package com.cool.store.service.tp.impl;

import cn.hutool.core.collection.CollStreamUtil;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.CommonConstants;
import com.cool.store.constants.RedisConstant;
import com.cool.store.context.CurrentUserHolder;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.EnterpriseUserDAO;
import com.cool.store.dao.StoreDao;
import com.cool.store.dao.tp.*;
import com.cool.store.dto.ImportOaOldShopDataErrorDTO;
import com.cool.store.dto.openPreparation.UserNameDTO;
import com.cool.store.dto.tp.TpPenaltyImportErrorDTO;
import com.cool.store.entity.ImportTaskDO;
import com.cool.store.entity.StoreDO;
import com.cool.store.entity.tp.TpApplyFormDO;
import com.cool.store.entity.tp.TpAuditRecordDO;
import com.cool.store.entity.tp.TpRuleDO;
import com.cool.store.entity.tp.TpScoreJournalDO;
import com.cool.store.enums.*;
import com.cool.store.enums.tp.TpFormStatusEnum;
import com.cool.store.enums.tp.TpFormTypeEnum;
import com.cool.store.enums.tp.TpPayStatusEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.ImportTaskMapper;
import com.cool.store.mapper.SysRoleMapper;
import com.cool.store.mapper.SysRoleMenuMapper;
import com.cool.store.mq.producer.SimpleMessageService;
import com.cool.store.oss.OssClientService;
import com.cool.store.request.tp.*;
import com.cool.store.response.AuditInfoResponse;
import com.cool.store.service.dict.impl.DictService;
import com.cool.store.service.tp.TpApplyService;
import com.cool.store.request.tp.TpPenaltyImportRowDTO;
import com.cool.store.utils.*;
import com.cool.store.utils.easyExcel.EasyExcelUtil;
import com.cool.store.utils.poi.DateUtils;
import com.cool.store.vo.tp.*;
import com.cool.store.vo.tp.mini.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.excel.EasyExcel;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.cool.store.utils.poi.DateUtils.SPECIAL_DATE_START_1;

/**
 * <p>
 * 十二分制-申请单 服务实现类
 * </p>
 *
 * @author wangff
 * @since 2025/11/5
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TpApplyServiceImpl implements TpApplyService {

    private final TpRuleDAO tpRuleDAO;
    private final TpApplyFormDAO tpApplyFormDAO;
    private final StoreDao storeDao;
    private final TpAuditRecordDAO tpAuditRecordDAO;
    private final DictService dictService;
    private final TpScoreJournalDAO tpScoreJournalDAO;
    private final EnterpriseUserDAO enterpriseUserDAO;
    private final RedisUtilPool redisUtilPool;
    private final OssClientService ossClientService;
    private final ImportTaskMapper importTaskMapper;
    @Resource(name = "generalThreadPool")
    private ThreadPoolTaskExecutor executor;
    private final EasyExcelUtil easyExcelUtil;
    private final SysRoleMenuMapper sysRoleMenuMapper;
    private final SimpleMessageService simpleMessageService;

    @Value("${mybatis.configuration.variables.enterpriseId}")
    private String eid;
    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Override
    @Transactional
    public Boolean rewardApplySubmit(TpApplyRequest request) {
        // 1.每个门店一个月内相同加分项仅能申请一次
        // 2.门店分值=12时，所有加分项不允许申请
        boolean isDraft = CommonConstants.INDEX_ONE.equals(request.getIsDraft());
        if (!isDraft && tpApplyFormDAO.existPassRewardForm(request.getStoreId(), request.getRuleId())) {
            throw new ServiceException(ErrorCodeEnum.TP_MONTH_EXIST_APPLY);
        }
        if (!isDraft && tpApplyFormDAO.existsPendingApply(request.getStoreId(), request.getRuleId(), Collections.singletonList(TpFormTypeEnum.REWARD.getType()))) {
            throw new ServiceException(ErrorCodeEnum.TP_EXISTS_PENDING_APPLY);
        }
        BigDecimal score = storeDao.getStoreScore(request.getStoreId());
        if (!isDraft && BigDecimal.valueOf(12.0).compareTo(score.setScale(1, RoundingMode.DOWN)) == 0) {
            throw new ServiceException(ErrorCodeEnum.TP_SCORE_EQUAL_TWELVE);
        }
        TpApplyFormDO formDO = BeanUtil.toBean(request, TpApplyFormDO.class);
        if (Objects.isNull(request.getId())) {
            formDO.setApplyNo(TpHelper.generateApplyNo(TpFormTypeEnum.REWARD.getType()));
            formDO.setType(TpFormTypeEnum.REWARD.getType());
            formDO.setPayStatus(TpPayStatusEnum.NO_NEED_PAY.getStatus());
        }
        formDO.setStatus(isDraft ? TpFormStatusEnum.DRAFT.getStatus() : TpFormStatusEnum.PENDING.getStatus());
        fillRuleFields(formDO);
        tpApplyFormDAO.insertOrUpdate(formDO);
        // 第一次提交后添加审批记录
        if (!isDraft) {
            tpAuditRecordDAO.firstAddRecord(formDO.getId(), formDO.getType(), request.getApplyUserId(), request.getApplyUserName());
        }
        return true;
    }

    @Override
    public TpRewardApplyDetailVO rewardDetail(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.nonNull(formDO)) {
            TpRewardApplyDetailVO vo = BeanUtil.toBean(formDO, TpRewardApplyDetailVO.class);
            dictService.fillDictField(vo);
            StoreDO storeDO = storeDao.getByStoreId(vo.getStoreId());
            if (Objects.nonNull(storeDO)) {
                vo.setStoreNum(storeDO.getStoreNum());
                vo.setStoreName(storeDO.getStoreName());
            }
            return vo;
        }
        return null;
    }

    @Override
    public PageInfo<TpRewardApplyListVO> rewardPage(TpApplyQueryRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TpApplyFormDO> list = tpApplyFormDAO.getEffectiveList(request);
        PageInfo<TpApplyFormDO> page = new PageInfo<>(list);
        PageInfo<TpRewardApplyListVO> newPage = BeanUtil.toPage(page, TpRewardApplyListVO.class);
        fillListFields(newPage.getList());
        return newPage;
    }

    @Override
    @Transactional
    public Boolean penaltyApplySubmit(TpApplyRequest request) {
        TpApplyFormDO formDO = BeanUtil.toBean(request, TpApplyFormDO.class);
        // 如果存在审批中的申请，则不允许再次提交
        boolean isDraft = CommonConstants.INDEX_ONE.equals(request.getIsDraft());
        if (!isDraft && tpApplyFormDAO.existsPendingApply(request.getStoreId(), request.getRuleId(), Arrays.asList(TpFormTypeEnum.PENALTY.getType(), TpFormTypeEnum.WARNING.getType()))) {
            throw new ServiceException(ErrorCodeEnum.TP_EXISTS_PENDING_APPLY);
        }
        fillRuleFields(formDO);
        formDO.setStatus(isDraft ? TpFormStatusEnum.DRAFT.getStatus() : TpFormStatusEnum.INEFFECTIVE.getStatus());
        formDO.setApplyNo(Objects.isNull(request.getId()) ? TpHelper.generateApplyNo(formDO.getType()) : null);
        tpApplyFormDAO.insertOrUpdate(formDO);
        // 第一次提交后添加审批记录
        if (!isDraft) {
            tpAuditRecordDAO.firstAddRecord(formDO.getId(), formDO.getType(), request.getApplyUserId(), request.getApplyUserName());
            // 发送延时任务
            simpleMessageService.send(String.valueOf(formDO.getId()), RocketMqTagEnum.TP_PENALTY_APPEAL_OVERDUE, formDO.getAppealEndDate().getTime());
        }
        return true;
    }

    @Override
    public TpPenaltyApplyDetailVO penaltyDetail(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.nonNull(formDO)) {
            TpPenaltyApplyDetailVO vo = BeanUtil.toBean(formDO, TpPenaltyApplyDetailVO.class);
            dictService.fillDictField(vo);
            StoreDO storeDO = storeDao.getByStoreId(vo.getStoreId());
            if (Objects.nonNull(storeDO)) {
                vo.setStoreNum(storeDO.getStoreNum());
                vo.setStoreName(storeDO.getStoreName());
            }
            return vo;
        }
        return null;
    }

    @Override
    public PageInfo<TpPenaltyApplyListVO> penaltyPage(TpApplyQueryRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TpApplyFormDO> list = tpApplyFormDAO.getEffectiveList(request);
        PageInfo<TpApplyFormDO> page = new PageInfo<>(list);
        PageInfo<TpPenaltyApplyListVO> newPage = BeanUtil.toPage(page, TpPenaltyApplyListVO.class);
        fillListFields(newPage.getList());
        return newPage;
    }

    @Override
    @Transactional
    public Boolean rewardAudit(TpApplyAuditRequest request) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(request.getApplyId());
        if (Objects.isNull(formDO) || !TpFormTypeEnum.REWARD.getType().equals(formDO.getType())) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.PENDING.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_APPLY_AUDIT_COMPLETED);
        }
        if (CommonConstants.INDEX_ONE.equals(request.getAuditStatus()) && tpApplyFormDAO.existPassRewardForm(formDO.getStoreId(), formDO.getRuleId())) {
            throw new ServiceException(ErrorCodeEnum.TP_MONTH_EXIST_APPLY);
        }
        auditCommon(request, formDO);
        return true;
    }

    @Override
    @Transactional
    public Boolean penaltyAudit(TpApplyAuditRequest request) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(request.getApplyId());
        if (Objects.isNull(formDO) || !TpFormTypeEnum.isPenalty(formDO.getType())) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.PENDING.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_APPLY_AUDIT_COMPLETED);
        }
        auditCommon(request, formDO);
        return true;
    }

    @Override
    public Boolean appealApplySubmit(TpAppealApplyRequest request) {
        TpApplyFormDO formDO = BeanUtil.toBean(request, TpApplyFormDO.class);
        boolean isDraft = CommonConstants.INDEX_ONE.equals(request.getIsDraft());
        TpApplyFormDO penaltyFormDO = tpApplyFormDAO.getEffectiveById(request.getPenaltyId());
        if (Objects.isNull(penaltyFormDO)) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!isDraft) {
            TpApplyFormDO appealFormDO = tpApplyFormDAO.getAppealByPenaltyId(request.getPenaltyId());
            if (Objects.nonNull(appealFormDO) && CommonConstants.INDEX_ZERO.equals(appealFormDO.getIsDraft())) {
                throw new ServiceException(ErrorCodeEnum.TP_EXISTS_APPEAL_FORM);
            }
            // 已生效的无法复议
            if (TpFormStatusEnum.EFFECTIVE.getStatus().equals(penaltyFormDO.getStatus())) {
                throw new ServiceException(ErrorCodeEnum.TP_PENALTY_APPLY_EFFECTIVE);
            }
        }
        formDO.setApplyNo(Objects.isNull(request.getId()) ? TpHelper.generateAppealNo() : null);
        formDO.setRuleId(penaltyFormDO.getRuleId());
        formDO.setType(TpFormTypeEnum.APPEAL.getType());
        formDO.setStoreId(penaltyFormDO.getStoreId());
        formDO.setStatus(isDraft ? TpFormStatusEnum.DRAFT.getStatus() : TpFormStatusEnum.PENDING.getStatus());
        fillRuleFields(formDO);
        tpApplyFormDAO.insertOrUpdate(formDO);
        // 第一次提交后添加审批记录
        if (!isDraft) {
            tpAuditRecordDAO.firstAddRecord(formDO.getId(), formDO.getType(), request.getApplyUserId(), request.getApplyUserName());
        }
        return true;
    }

    @Override
    public TpAppealFormDetailVO appealDetail(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.nonNull(formDO)) {
            TpAppealFormDetailVO vo = BeanUtil.toBean(formDO, TpAppealFormDetailVO.class);
            dictService.fillDictField(vo);
            StoreDO storeDO = storeDao.getByStoreId(vo.getStoreId());
            if (Objects.nonNull(storeDO)) {
                vo.setStoreNum(storeDO.getStoreNum());
                vo.setStoreName(storeDO.getStoreName());
            }
            vo.setPenaltyDetail(penaltyDetail(vo.getPenaltyId()));
            return vo;
        }
        return null;
    }

    @Override
    public PageInfo<TpAppealListVO> appealPage(TpApplyQueryRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TpApplyFormDO> list = tpApplyFormDAO.getEffectiveList(request);
        PageInfo<TpApplyFormDO> page = new PageInfo<>(list);
        PageInfo<TpAppealListVO> newPage = BeanUtil.toPage(page, TpAppealListVO.class);
        List<Long> penaltyIds = CollStreamUtil.toList(newPage.getList(), TpAppealListVO::getPenaltyId);
        List<TpApplyFormDO> penaltyList = tpApplyFormDAO.getByIds(penaltyIds);
        Map<Long, String> penaltyNoMap = CollStreamUtil.toMap(penaltyList, TpApplyFormDO::getId, TpApplyFormDO::getApplyNo);
        newPage.getList().forEach(vo -> {
            vo.setPenaltyNo(penaltyNoMap.get(vo.getPenaltyId()));
        });
        fillListFields(newPage.getList());
        return newPage;
    }

    @Override
    @Transactional
    public Boolean appealAudit(TpApplyAuditRequest request) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(request.getApplyId());
        if (Objects.isNull(formDO) || !TpFormTypeEnum.APPEAL.getType().equals(formDO.getType())) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.PENDING.getStatus().equals(formDO.getStatus()) && !TpFormStatusEnum.INEFFECTIVE.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_FORM_ENABLE_APPEAL);
        }
        auditCommon(request, formDO);
        return true;
    }

    @Override
    @Transactional
    public Boolean deleteBatch(List<Long> applyIds) {
        tpApplyFormDAO.deleteByIds(applyIds);
        // 删除审批记录
        tpAuditRecordDAO.deleteByApplyIds(applyIds);
        return true;
    }

    @Override
    public List<AuditInfoResponse> getAuditRecordList(Long applyId) {
        List<TpAuditRecordDO> list = tpAuditRecordDAO.getAuditRecordList(applyId);
        List<AuditInfoResponse> result = new ArrayList<>();
        for (TpAuditRecordDO auditRecordDO : list) {
            AuditInfoResponse response = AuditInfoResponse.builder()
                    .type(getAuditInfoType(auditRecordDO))
                    .execute(auditRecordDO.getRecordType() - 1)
                    .status(CommonConstants.INDEX_ZERO.equals(auditRecordDO.getAuditStatus()) ? 0 : 1)
                    .createTime(auditRecordDO.getReceiveTaskTime())
                    .remark(auditRecordDO.getRemark())
                    .actualUserId(auditRecordDO.getHandlerUserId())
                    .build();
            if (CommonConstants.INDEX_ZERO.equals(auditRecordDO.getAuditStatus())) {
                // 待审批记录根据功能权限分解待审批人
                String menuId = redisUtilPool.hashGet(RedisConstant.TP_AUDIT_AUTH_MENU_ID, String.valueOf(auditRecordDO.getApplyType()));
                if (StringUtils.isNotBlank(menuId)) {
                    List<Long> roleIds = sysRoleMenuMapper.listSysRoleMenuIdByMenuId(PlatFormTypeEnum.PC.getCode(), Long.valueOf(menuId));
                    // 根据角色分解用户
                    if (CollectionUtils.isNotEmpty(roleIds)) {
                        List<UserNameDTO> users = sysRoleMapper.getPositionUsers(roleIds.stream().map(String::valueOf).collect(Collectors.toList()));
                        List<AuditInfoResponse.AuditUserInfoVO> auditUserList = CollStreamUtil.toList(users, v -> new AuditInfoResponse.AuditUserInfoVO(v.getUserId(), v.getName(), null));
                        response.setList(auditUserList);
                        result.add(response);
                    }
                }
            } else {
                List<AuditInfoResponse.AuditUserInfoVO> auditUserList = Collections.singletonList(
                        new AuditInfoResponse.AuditUserInfoVO(auditRecordDO.getHandlerUserId(), auditRecordDO.getHandlerUserName(), null)
                );
                response.setList(auditUserList);
                result.add(response);
            }
        }
        return result;
    }

    private Integer getAuditInfoType(TpAuditRecordDO auditRecordDO) {
        if (CommonConstants.INDEX_ONE.equals(auditRecordDO.getRecordType())) {
            return OperationLogAuditEnum.SUBMIT_AUDIT.getCode();
        } else {
            switch (auditRecordDO.getAuditStatus()) {
                case 0:
                    return OperationLogAuditEnum.WAIT_AUDIT.getCode();
                case 1:
                    return OperationLogAuditEnum.PASS.getCode();
                case 2:
                    return OperationLogAuditEnum.REFUSE.getCode();
                default:
                    return null;
            }
        }
    }

    @Override
    public PageInfo<MiniTpApplyListVO> getMiniApplyList(TpApplyQueryRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TpApplyFormDO> list = tpApplyFormDAO.getEffectiveList(request);
        PageInfo<TpApplyFormDO> page = new PageInfo<>(list);
        PageInfo<MiniTpApplyListVO> newPage = BeanUtil.toPage(page, MiniTpApplyListVO.class);
        List<MiniTpApplyListVO> applyList = newPage.getList();
        dictService.fillDictField(applyList);
        if (Boolean.TRUE.equals(request.getIsPenalty()) && CollectionUtils.isNotEmpty(applyList)) {
            List<Long> applyIds = CollStreamUtil.toList(applyList, MiniTpApplyListVO::getId);
            Set<Long> existAppealSet = tpApplyFormDAO.filterExistAppeal(applyIds);
            applyList.forEach(v -> v.setIsAppeal(existAppealSet.contains(v.getId()) ? 1 : 0));
        }
        return newPage;
    }

    @Override
    public MiniTpRewardApplyVO getMiniRewardApplyDetail(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        MiniTpRewardApplyVO vo = BeanUtil.toBean(formDO, MiniTpRewardApplyVO.class);
        dictService.fillDictField(vo);
        return vo;
    }

    @Override
    public MiniTpPenaltyApplyVO getMiniPenaltyApplyDetail(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.nonNull(formDO)) {
            MiniTpPenaltyApplyVO vo = BeanUtil.toBean(formDO, MiniTpPenaltyApplyVO.class);
            TpApplyFormDO appealForm = tpApplyFormDAO.getAppealByPenaltyId(formDO.getId());
            vo.setAppeal(BeanUtil.toBean(appealForm, MiniTpAppealVO.class));
            dictService.fillDictField(vo);
            return vo;
        }
        return null;
    }

    @Override
    public PageInfo<MiniTpRuleListVO> getMiniRulePage(TpRuleQueryRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TpRuleDO> list = tpRuleDAO.getEffectiveList(request);
        PageInfo<TpRuleDO> page = new PageInfo<>(list);
        PageInfo<MiniTpRuleListVO> newPage = BeanUtil.toPage(page, MiniTpRuleListVO.class);
        dictService.fillDictField(newPage.getList());
        return newPage;
    }

    @Override
    @Transactional
    public Boolean acceptPenalty(Long applyId) {
        // 校验申请单状态是否为审批通过
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.isNull(formDO)) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.PASS.getStatus().equals(formDO.getStatus()) && !TpFormStatusEnum.INEFFECTIVE.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_PENALTY_APPLY_UNABLE_ACCEPT);
        }
        tpApplyFormDAO.updateStatus(applyId, TpFormStatusEnum.EFFECTIVE);
        scoreChange(formDO);
        return true;
    }

    @Override
    @Transactional
    public void appealOverdue(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.nonNull(formDO) && (TpFormStatusEnum.PASS.getStatus().equals(formDO.getStatus()) || TpFormStatusEnum.INEFFECTIVE.getStatus().equals(formDO.getStatus()))) {
            tpApplyFormDAO.updateStatus(applyId, TpFormStatusEnum.EFFECTIVE);
            scoreChange(formDO);
        }
    }

    @Override
    public Boolean completePayment(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getEffectiveById(applyId);
        if (Objects.isNull(formDO)) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.EFFECTIVE.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_PENALTY_APPLY_INEFFECTIVE);
        }
        if (TpPayStatusEnum.NO_NEED_PAY.getStatus().equals(formDO.getPayStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_PENALTY_APPLY_NO_NEED_PAY);
        }
        tpApplyFormDAO.updatePayStatus(applyId, TpPayStatusEnum.PAID);
        return true;
    }

    @Override
    public Boolean withdrawAppeal(Long applyId) {
        TpApplyFormDO formDO = tpApplyFormDAO.getById(applyId);
        if (Objects.isNull(formDO) || !TpFormTypeEnum.APPEAL.getType().equals(formDO.getType())) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_APPLY_FORM);
        }
        if (!TpFormStatusEnum.PENDING.getStatus().equals(formDO.getStatus()) && !TpFormStatusEnum.DRAFT.getStatus().equals(formDO.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.TP_APPLY_AUDIT_COMPLETED);
        }
        tpApplyFormDAO.deleteByIds(Collections.singletonList(applyId));
        return true;
    }

    /**
     * 审批通用方法
     */
    @Transactional
    public void auditCommon(TpApplyAuditRequest request, TpApplyFormDO formDO) {
        Date now = new Date();
        // 修改申请单审批数据
        formDO.setStatus(CommonConstants.INDEX_ONE.equals(request.getAuditStatus()) ? TpFormStatusEnum.PASS.getStatus() : TpFormStatusEnum.REJECT.getStatus());
        formDO.setApproveTime(now);
        formDO.setApproveUserId(CurrentUserHolder.getUserId());
        tpApplyFormDAO.updateSelective(formDO);
        LoginUserInfo user = CurrentUserHolder.getUser();
        // 处理审批记录
        tpAuditRecordDAO.auditPendingRecordByApplyId(request.getApplyId(), formDO.getType(), user.getUserId(), user.getName(), request.getAuditStatus(), request.getRemark(), now);
        if (TpFormTypeEnum.APPEAL.getType().equals(formDO.getType())) {
            appealResolve(formDO);
        } else if (TpFormTypeEnum.REWARD.getType().equals(formDO.getType())) {
            // 加分审批通过后门店积分变动
            scoreChange(formDO);
        }
    }

    /**
     * 复议申请审批完成处理
     */
    @Transactional
    public void appealResolve(TpApplyFormDO formDO) {
        if (TpFormStatusEnum.PASS.getStatus().equals(formDO.getStatus())) {
            // 申诉通过后，处罚单失效
            tpApplyFormDAO.updateStatus(formDO.getPenaltyId(), TpFormStatusEnum.CANCEL);
        } else {
            // 惩处单直接生效
            acceptPenalty(formDO.getPenaltyId());
        }
    }

    /**
     * 门店积分变动
     */
    @Override
    public void scoreChange(TpApplyFormDO formDO) {
        if (TpFormStatusEnum.PASS.getStatus().equals(formDO.getStatus()) || TpFormStatusEnum.INEFFECTIVE.getStatus().equals(formDO.getStatus()) && TpFormTypeEnum.isPenalty(formDO.getType())) {
            BigDecimal occurBeforeScore = storeDao.getStoreScore(formDO.getStoreId());
            BigDecimal occurAfterScore = null;
            String occurScore = "";
            if (TpFormTypeEnum.REWARD.getType().equals(formDO.getType())) {
                // 加分
                if (CommonConstants.INDEX_ONE.equals(formDO.getIsFull())) {
                    occurAfterScore = BigDecimal.valueOf(12.0);
                    occurScore = "加满";
                } else {
                    occurAfterScore = occurBeforeScore.add(formDO.getScore());
                    occurScore = "+" + formDO.getScore().toString();
                }
            } else if (TpFormTypeEnum.PENALTY.getType().equals(formDO.getType())) {
                occurAfterScore = occurBeforeScore.subtract(formDO.getScore());
                occurScore = "-" + formDO.getScore().toString();
            }
            // 修改门店积分
            if (Objects.nonNull(occurAfterScore)) {
                // 限制积分在0-12之间
                if (occurAfterScore.compareTo(BigDecimal.valueOf(12)) > 0) {
                    occurAfterScore = BigDecimal.valueOf(12);
                }
                if (occurAfterScore.compareTo(BigDecimal.ZERO) < 0) {
                    occurAfterScore = BigDecimal.ZERO;
                }
                storeDao.updateStoreScore(formDO.getStoreId(), occurAfterScore);
                StoreDO storeDO = storeDao.getEffectiveByStoreId(formDO.getStoreId());
                if (Objects.isNull(storeDO)) {
                    throw new ServiceException(ErrorCodeEnum.STORE_NOT_FIND);
                }
                TpRuleDO tpRuleDO = tpRuleDAO.getById(formDO.getRuleId());
                // 添加积分流水
                tpScoreJournalDAO.insertSelective(new TpScoreJournalDO(storeDO, formDO, occurScore, occurBeforeScore, occurAfterScore, tpRuleDO.getRemark()));
            }
        }
    }

    @Override
    public TpStoreScoreVO getStoreScore(String storeId) {
        StoreDO storeDO = storeDao.getEffectiveByStoreId(storeId);
        BigDecimal score = storeDao.getStoreScore(storeId);
        return new TpStoreScoreVO(storeDO.getStoreName(), storeDO.getAvatar(), score);
    }

    @Override
    @Transactional
    public List<TpBatchAuditVO> batchAudit(TpBatchAuditRequest request) {
        List<TpBatchAuditVO> result = new ArrayList<>();
        TpFormTypeEnum type = TpFormTypeEnum.getByType(request.getType());
        if (Objects.isNull(type)) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_FORM_TYPE);
        }
        for (Long applyId : request.getApplyIds()) {
            boolean f = false;
            String failReason = null;
            try {
                switch (type) {
                    case REWARD:
                        f = rewardAudit(new TpApplyAuditRequest(applyId, request.getAuditStatus(), request.getRemark()));
                        break;
                    case PENALTY:
                    case WARNING:
                        f = penaltyAudit(new TpApplyAuditRequest(applyId, request.getAuditStatus(), request.getRemark()));
                        break;
                    case APPEAL:
                        f = appealAudit(new TpApplyAuditRequest(applyId, request.getAuditStatus(), request.getRemark()));
                        break;
                }
                if (!f) {
                    failReason = "未知错误，请联系管理员";
                }
            } catch (ServiceException e) {
                failReason = e.getErrorMessage();
            } catch (Exception e) {
                failReason = "未知错误，请联系管理员";
                log.error("申请单批量审批失败，applyId:{}", applyId, e);
            }
            result.add(new TpBatchAuditVO(applyId, f ? 1 : 2, failReason));
        }
        List<Long> applyIds = CollStreamUtil.toList(result, TpBatchAuditVO::getApplyId);
        List<TpApplyFormDO> forms = tpApplyFormDAO.getByIds(applyIds);
        Map<Long, String> applyNoMap = CollStreamUtil.toMap(forms, TpApplyFormDO::getId, TpApplyFormDO::getApplyNo);
        result.forEach(v -> v.setApplyNo(applyNoMap.get(v.getApplyId())));
        return result;
    }

    /**
     * 惩处申请单Excel导入（异步执行，立即返回）
     */
    @Override
    public Boolean penaltyImport(MultipartFile file) {
        final String lockKey = RedisConstant.TP_PENALTY_APPLY_IMPORT_LOCK;
        boolean lock = redisUtilPool.setNxExpire(lockKey, UUIDUtils.get32UUID(), 30 * 60 * 1000);
        if (!lock) {
            throw new ServiceException(ErrorCodeEnum.TP_EXIST_PENDING_IMPORT_TASK);
        }
        ImportTaskDO importTaskDO = new ImportTaskDO();
        importTaskDO.setFileName(file.getOriginalFilename());
        importTaskDO.setFileType(FileTypeEnum.TP_PENALTY_IMPORT.getFileType());
        importTaskDO.setIsImport(true);
        importTaskDO.setStatus(ImportTaskStatusEnum.PROGRESS.getCode());
        importTaskDO.setCreateUserId(CurrentUserHolder.getUserId());
        importTaskDO.setCreateName(CurrentUserHolder.getUser().getName());
        importTaskDO.setCreateTime(System.currentTimeMillis());
        importTaskMapper.insert(eid, importTaskDO);
        LoginUserInfo user = CurrentUserHolder.getUser();
        try {
            // 图片是额外读取文件操作，这里使用异步会导致临时文件被清理，因此改成字节数组传参
            byte[] excelBytes = file.getBytes();
            executor.execute(() -> importPenalty(excelBytes, importTaskDO, user));
        } catch (Exception e) {
            log.error("导入失败");
        }
        return true;
    }

    private void importPenalty(byte[] excelBytes, ImportTaskDO importTaskDO, LoginUserInfo user) {
        List<TpPenaltyImportErrorDTO> errorList = new ArrayList<>();
        AtomicInteger successNum = new AtomicInteger();
        AtomicInteger totalNum = new AtomicInteger();
        try {
            EasyExcel.read(new ByteArrayInputStream(excelBytes), TpPenaltyImportRowDTO.class,
                            new TpPenaltyImportListener(this, tpRuleDAO, storeDao, ossClientService, errorList, user, excelBytes, totalNum, successNum))
                    .sheet(0)
                    .headRowNumber(2)
                    .doRead();
        } catch (Exception e) {
            log.error("惩处申请单导入失败", e);
        } finally {
            try {
                final String lockKey = RedisConstant.TP_PENALTY_APPLY_IMPORT_LOCK;
                String value = redisUtilPool.getString(lockKey);
                if (StringUtils.isNotBlank(value)) {
                    redisUtilPool.delKey(lockKey);
                }
            } catch (Exception ex) {
                log.error("释放惩处申请导入锁异常", ex);
            }
        }
        importTaskDO.setStatus(totalNum.get() == successNum.get() ? ImportTaskStatusEnum.SUCCESS.getCode() : ImportTaskStatusEnum.ERROR.getCode());
        importTaskDO.setTotalNum(totalNum.get());
        importTaskDO.setSuccessNum(successNum.get());
        if (CollectionUtils.isNotEmpty(errorList)) {
            try {
                String url = easyExcelUtil.exportExcel(ImportOaOldShopDataErrorDTO.class, errorList, null,
                        FileTypeEnum.TP_PENALTY_ERROR_EXPORT.getDesc() + DateUtils.parseDateToStr(SPECIAL_DATE_START_1, new Date()),
                        FileTypeEnum.TP_PENALTY_ERROR_EXPORT.getDesc() + DateUtils.parseDateToStr(SPECIAL_DATE_START_1, new Date()));
                importTaskDO.setFileUrl(url);
            } catch (Exception e) {
                log.info("导出失败列表失败 errorList:{}", JSONObject.toJSONString(errorList));
            }
        }
        importTaskMapper.update(eid, importTaskDO);
    }

    /**
     * 填充规则相关字段
     */
    private void fillRuleFields(TpApplyFormDO formDO) {
        TpRuleDO ruleDO = tpRuleDAO.getById(formDO.getRuleId());
        if (Objects.isNull(ruleDO)) {
            throw new ServiceException(ErrorCodeEnum.TP_NOT_EXIST_RULE);
        }
        formDO.setRuleNo(ruleDO.getRuleNo());
        formDO.setProblemClassification(ruleDO.getProblemClassification());
        formDO.setProjectCategory(ruleDO.getProjectCategory());
        formDO.setProjectName(ruleDO.getProjectName());
        formDO.setIsFull(ruleDO.getIsFull());
        if (Objects.isNull(formDO.getScore())) {
            formDO.setScore(ruleDO.getScore());
        }
        if (Objects.isNull(formDO.getAmount())) {
            formDO.setAmount(ruleDO.getAmount());
        }
        // 扣分
        if (CommonConstants.INDEX_ZERO.equals(ruleDO.getType())) {
            if (Objects.isNull(formDO.getAppealEndDate())) {
                LocalDateTime appealEndDate = LocalDateTime.now().plusDays(ruleDO.getAppealDeadline());
                formDO.setAppealEndDate(Date.from(appealEndDate.atZone(ZoneId.systemDefault()).toInstant()));
            }
            if (Objects.isNull(formDO.getType())) {
                // 超过警告次数的为处罚单，否则警告单
                int penaltyCount = tpApplyFormDAO.getPenaltyCount(formDO.getStoreId(), formDO.getRuleId());
                Integer type = ruleDO.getWarningLimit().compareTo(penaltyCount) <= 0 ? TpFormTypeEnum.PENALTY.getType() : TpFormTypeEnum.WARNING.getType();
                formDO.setType(type);
                if (TpFormTypeEnum.WARNING.getType().equals(type)) {
                    formDO.setAmount(BigDecimal.ZERO);
                    formDO.setScore(BigDecimal.ZERO);
                }
            }
        }
        // 是否缴费
        formDO.setPayStatus(Objects.nonNull(ruleDO.getAmount()) && BigDecimal.ZERO.compareTo(ruleDO.getAmount()) < 0 ? TpPayStatusEnum.UNPAID.getStatus() : TpPayStatusEnum.NO_NEED_PAY.getStatus());
    }

    /**
     * 填充列表相关字段
     */
    private void fillListFields(List<? extends TpApplyListBaseVO> list) {
        if (CollectionUtils.isEmpty(list)) return;
        dictService.fillDictField(list);
        Set<String> storeIds = CollStreamUtil.toSet(list, TpApplyListBaseVO::getStoreId);
        Set<String> approveUserIds = CollStreamUtil.toSet(list, TpApplyListBaseVO::getApproveUserId);
        Map<String, StoreDO> storeMap = storeDao.getStoreMapByStoreIds(new ArrayList<>(storeIds));
        Map<String, String> userNameMap = enterpriseUserDAO.getUserNameMap(new ArrayList<>(approveUserIds));
        for (TpApplyListBaseVO vo : list) {
            StoreDO storeDO = storeMap.get(vo.getStoreId());
            if (Objects.nonNull(storeDO)) {
                vo.setStoreName(storeDO.getStoreName());
                vo.setStoreNum(storeDO.getStoreNum());
            }
            vo.setApproveUserName(userNameMap.get(vo.getApproveUserId()));
        }
    }

}
