# CbCaseResultServiceImpl.java

    package cloud.wconnect.finance.service.chargeback.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.ApproveStatusEnum;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.util.*;
    import cloud.wconnect.finance.cons.S3BucketFunctionCons;
    import cloud.wconnect.finance.cons.chargeback.*;
    import cloud.wconnect.finance.entity.chargeback.CbCase;
    import cloud.wconnect.finance.entity.chargeback.CbCaseAction;
    import cloud.wconnect.finance.entity.chargeback.CbCaseResult;
    import cloud.wconnect.finance.mapper.chargeback.CbCaseResultMapper;
    import cloud.wconnect.finance.pojo.dto.chargeback.SaveCaseResultDTO;
    import cloud.wconnect.finance.pojo.dto.chargeback.SaveResultFinalDateDTO;
    import cloud.wconnect.finance.service.chargeback.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.concurrent.Executor;
    import java.util.stream.Collectors;

    /**
     * <p>
     *  服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-08-22
     */
    @Service
    @DS("finance")
    public class CbCaseResultServiceImpl extends ServiceImpl<CbCaseResultMapper, CbCaseResult> implements ICbCaseResultService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private ICbCaseResultLogService cbCaseResultLogService;
        @Lazy
        @Autowired
        private ICbAdjService cbAdjService;
        @Lazy
        @Autowired
        private ICbCaseService cbCaseService;
        @Autowired
        private ICbCaseActionService cbCaseActionService;
        @Value("${aws-s3.region}")
        private String region;
        @Value("#{${aws-s3.function-bucket}}")
        private Map<String, Map<String, Object>> bucketMap;
        @Autowired
        private Executor executor;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 case result
         *
         * @param caseIds case id
         * @param results result
         * @return result
         * @author yyt
         * @date 2023/8/28
         */
        @Override
        public List<CbCaseResult> listByCaseIds(List<Long> caseIds, List<Integer> results) {
            return list(new QueryWrapper<CbCaseResult>().lambda().in(CbCaseResult::getCaseId, caseIds)
                    .in(Objects.nonNull(results), CbCaseResult::getResult, results));
        }

        /**
         * 获取 case result
         *
         * @param caseId case id
         * @return result
         * @author yyt
         * @date 2023/9/6
         */
        @Override
        public CbCaseResult getCaseResult(Long caseId) {
            return getOne(new QueryWrapper<CbCaseResult>().lambda().eq(CbCaseResult::getCaseId, caseId));
        }

        /**
         * 保存 case result
         *
         * @param cs                case
         * @param saveCaseResultDTO result 信息
         * @param userId            操作人
         * @author yyt
         * @date 2023/9/6
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(CbCase cs, SaveCaseResultDTO saveCaseResultDTO, int userId) {
            // 校验阶段是 portal / patch / final
            Integer stage = cs.getStage();
            if (!Lists.newArrayList(StageCons.PORTAL, StageCons.PATCH, StageCons.FINAL).contains(stage)) {
                throw new BadRequestException(CaseErrorCons.OPERATION_RESULT_FAIL_STAGE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The stage is the portal, patch, or final stage before operation can be carried out.");
            }

            CbCase updateCase = new CbCase();

            long now = DateTimeUtils.nowTimestamp();
            Long caseId = cs.getId();
            CbCaseResult caseResult = getCaseResult(caseId);
            if (Objects.isNull(caseResult)) {
                caseResult = beanMapper.map(saveCaseResultDTO, CbCaseResult.class);
                caseResult.setPropertyId(cs.getPropertyId()).setCaseId(caseId).setCreatedBy(userId).setCreatedTime(now)
                        .setUpdatedBy(userId).setUpdatedTime(now);
                save(caseResult);

                // 增加操作日志
                cbCaseResultLogService.save(cs, saveCaseResultDTO.getResult(), userId);

                updateCase.setStatus(getCaseStatus(saveCaseResultDTO.getResult()));
                updateCase.setStatusSetBy(userId);
                updateCase.setStatusSetTime(now);
            } else {
                // 如果变更 result，则检查是否是 portal 阶段，如果不是，则不允许修改
                if (!saveCaseResultDTO.getResult().equals(caseResult.getResult()) && cs.getStage() != StageCons.PORTAL
                        && cs.getApproveStatus() != ApproveStatusEnum.PENDING.getStatus()) {
                    throw new BadRequestException(CaseErrorCons.OPERATION_RESULT_FAIL_CHANGE_RESULT,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "the case stage is not portal, can not change result");
                }

                // 如果变更 result，需重新走 portal 阶段审批
                if (!saveCaseResultDTO.getResult().equals(caseResult.getResult())) {
                    updateCase.setStatus(getCaseStatus(saveCaseResultDTO.getResult()));
                    updateCase.setStatusSetBy(userId);
                    updateCase.setStatusSetTime(now);
                } else {
                    if (cs.getStage() == StageCons.PORTAL) {
                        updateCase.setStatus(getCaseStatus(saveCaseResultDTO.getResult()));
                        updateCase.setStatusSetTime(now);
                    }
                }

                CbCaseResult updateResult = beanMapper.map(saveCaseResultDTO, CbCaseResult.class);
                updateResult.setId(caseResult.getId());
                updateResult.setUpdatedBy(userId).setUpdatedTime(now);
                updateById(updateResult);

                if (!saveCaseResultDTO.getResult().equals(caseResult.getResult())) {
                    // 增加操作日志
                    cbCaseResultLogService.save(cs, saveCaseResultDTO.getResult(), userId);
                }

                CbCaseResult finalCaseResult = caseResult;
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    //修改前的s3Key集合
                    List<String> updateBeforeS3Keys = AttachmentUtil.getKeysByAbbrAttachmentArrayJson(finalCaseResult.getAttachments(), objectMapper);
                    //修改后的s3Key集合
                    List<String> updateAfterS3Keys = AttachmentUtil.getKeysByAbbrAttachmentArrayJson(saveCaseResultDTO.getAttachments(), objectMapper);
                    //修改前的 last 阶段填写的 s3Key集合
                    updateBeforeS3Keys.addAll(AttachmentUtil.getKeysByAbbrAttachmentArrayJson(finalCaseResult.getAttachmentsLastStage(), objectMapper));
                    //修改后的 last 阶段填写的 s3Key集合
                    updateAfterS3Keys.addAll(AttachmentUtil.getKeysByAbbrAttachmentArrayJson(saveCaseResultDTO.getAttachmentsLastStage(), objectMapper));
                    //删除的附件 = 修改前的附件集合 - 修改后的附件集合
                    List<String> delS3Keys = Lists.newArrayList(updateBeforeS3Keys);
                    delS3Keys.removeAll(updateAfterS3Keys);
                    S3Utils.batchDeleteFile(region,bucketMap.get(S3BucketFunctionCons.CHARGEBACK),delS3Keys);
                });
            }

            if (saveCaseResultDTO.getResult() == CaseResultEnum.WIN.getCode()) {
                // 处理 删除 adj
                cbAdjService.handleDeleteAdj(cs);
            }

            updateCase.setResult(saveCaseResultDTO.getResult());
            updateCase.setResultFinalDate(saveCaseResultDTO.getResultFinalDate());
            if (saveCaseResultDTO.getResult() == CaseResultEnum.PROCESSED.getCode()) {
                updateCase.setHandleCase(true);
            }
            updateCase.setApproveStatus(ApproveStatusEnum.PENDING.getStatus());
            updateCase.setId(caseId);
            cbCaseService.updateById(updateCase);
        }

        /**
         * 根据选择 result 返回 case 状态
         *
         * @param result result
         * @return case 状态
         * @author yyt
         * @date 2023/9/7
         */
        private Integer getCaseStatus(Integer result) {
            if (result == CaseResultEnum.WIN.getCode()) {
                return CaseStatusEnum.WIN.getCode();
            } else if (result == CaseResultEnum.LOSS.getCode()) {
                return CaseStatusEnum.LOSS.getCode();
            } else if (result == CaseResultEnum.FROZEN.getCode()) {
                return CaseStatusEnum.FROZEN.getCode();
            } else if (result == CaseResultEnum.PROCESSED.getCode()) {
                return CaseStatusEnum.PROCESSED.getCode();
            }

            throw new BadRequestException(CommonErrorI18nConstant.DATA_INVALID,
                    i18nMessageUtils.getErrorParamMapNotNeedTranslate("result"),
                    BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "result error");
        }

        /**
         * action 为 accept 时 case 填写 Bank Receiving/Write-off Date
         *
         * @param cs                     case
         * @param saveResultFinalDateDTO Bank Receiving/Write-off Date
         * @param userId                 操作人
         * @author yyt
         * @date 2023/9/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void chooseResultForAccept(CbCase cs, SaveResultFinalDateDTO saveResultFinalDateDTO, int userId) {
            Long caseId = cs.getId();
            long now = DateTimeUtils.nowTimestamp();
            Long resultFinalDate = saveResultFinalDateDTO.getResultFinalDate();
            String comment = saveResultFinalDateDTO.getComment();
            String attachments = saveResultFinalDateDTO.getAttachments();

            // 满足以下三种条件才可以操作
            // 1、阶段是 final，
            // 2、hotel operation 阶段选择 accept
            // 3、overdue 阶段选择 accept
            Map<Integer, Integer> actionByStage = cbCaseActionService.list(Lists.newArrayList(caseId),
                            Lists.newArrayList(StageCons.HOTEL_OPERATION, StageCons.OVERDUE))
                    .stream().collect(Collectors.toMap(CbCaseAction::getStage, CbCaseAction::getAction));
            Integer hotelOperationStageChooseAction = actionByStage.get(StageCons.HOTEL_OPERATION);
            Integer overdueStageChooseAction = actionByStage.get(StageCons.OVERDUE);
            if (cs.getStage() != StageCons.FINAL
                    || (hotelOperationStageChooseAction != null && hotelOperationStageChooseAction != CaseActionEnum.ACCEPT.getCode())
                    || (overdueStageChooseAction != null && overdueStageChooseAction != CaseActionEnum.ACCEPT.getCode())) {
                throw new BadRequestException(CaseErrorCons.OPERATION_BANK_RECEIVING_DATE_FAIL,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The stage must be final and the action chosen must be accepted before it can be operated.");
            }

            CbCaseResult caseResult = getCaseResult(caseId);
            if (Objects.isNull(caseResult)) {
                caseResult = new CbCaseResult();
                caseResult.setPropertyId(cs.getPropertyId()).setCaseId(caseId).setResult(BaseConstant.ZERO)
                        .setResultDate(BaseConstant.LONG_ZERO).setResultFinalDate(resultFinalDate)
                        .setNoWithdrawal(false).setAttachments(StringUtils.EMPTY)
                        .setCommentLastStage(comment).setAttachmentsLastStage(attachments)
                        .setCreatedBy(userId)
                        .setCreatedTime(now);
                save(caseResult);
            } else {
                CbCaseResult updateCaseResult = new CbCaseResult();
                updateCaseResult.setId(caseResult.getId());
                updateCaseResult.setResultFinalDate(resultFinalDate);
                updateCaseResult.setCommentLastStage(comment);
                updateCaseResult.setAttachmentsLastStage(attachments);
                updateCaseResult.setUpdatedBy(userId);
                updateCaseResult.setUpdatedTime(now);
                updateById(updateCaseResult);
            }

            CbCase updateCase = new CbCase();
            updateCase.setId(caseId);
            updateCase.setResultFinalDate(resultFinalDate);
            cbCaseService.updateById(updateCase);
        }

        /**
         * 清空 result
         *
         * @param caseId case id
         * @author yyt
         * @date 2023/9/7
         */
        @Override
        public void clearResult(Long caseId) {
            CbCaseResult cbCaseResult = getOne(new QueryWrapper<CbCaseResult>().lambda().eq(CbCaseResult::getCaseId, caseId));
            if(Objects.nonNull(cbCaseResult)){
                removeById(cbCaseResult.getId());
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    //删除附件
                    List<String> delS3Keys = new ArrayList<>();
                    delS3Keys.addAll(AttachmentUtil.getKeysByAbbrAttachmentArrayJson(cbCaseResult.getAttachments(), objectMapper));
                    delS3Keys.addAll(AttachmentUtil.getKeysByAbbrAttachmentArrayJson(cbCaseResult.getAttachmentsLastStage(), objectMapper));
                    S3Utils.batchDeleteFile(region, bucketMap.get(S3BucketFunctionCons.CHARGEBACK), delS3Keys);
                });
            }

            cbCaseResultLogService.clearLog(caseId);

            CbCase updateCase = new CbCase();
            updateCase.setId(caseId);
            updateCase.setResult(0);
            updateCase.setResultFinalDate(0L);
            cbCaseService.updateById(updateCase);
        }

        /**
         * 清空 last 阶段填写的信息
         *
         * @param caseId case id
         * @author yyt
         * @date 2023/9/7
         */
        @Override
        public void clearResultForLastStage(Long caseId) {
            CbCaseResult cbCaseResult = getOne(new QueryWrapper<CbCaseResult>().lambda().eq(CbCaseResult::getCaseId, caseId));
            if (Objects.nonNull(cbCaseResult)) {
                if (cbCaseResult.getResult() == BaseConstant.ZERO) {
                    removeById(cbCaseResult.getId());
                } else {
                    CbCaseResult updateCaseResult = new CbCaseResult();
                    updateCaseResult.setId(cbCaseResult.getId());
                    updateCaseResult.setCommentLastStage(StringUtils.EMPTY);
                    updateCaseResult.setAttachmentsLastStage(StringUtils.EMPTY);
                    updateCaseResult.setResultFinalDate(BaseConstant.LONG_ZERO);
                    updateCaseResult.setNoWithdrawal(false);
                    updateById(updateCaseResult);
                }
                TransactionUtils.afterCommitAsyncExecute(executor, () -> {
                    //删除附件
                    List<String> delS3Keys = AttachmentUtil.getKeysByAbbrAttachmentArrayJson(cbCaseResult.getAttachmentsLastStage(), objectMapper);
                    S3Utils.batchDeleteFile(region, bucketMap.get(S3BucketFunctionCons.CHARGEBACK), delS3Keys);
                });
            }

            CbCase updateCase = new CbCase();
            updateCase.setId(caseId);
            updateCase.setResultFinalDate(0L);
            cbCaseService.updateById(updateCase);
        }

    }