package com.yd.csf.service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.base.feign.client.exchangerate.ApiExchangeRateFeignClient;
import com.yd.common.enums.CommonEnum;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.common.result.Result;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.common.utils.RedisUtil;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.FortuneStatusEnum;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.csf.service.dao.FortuneMapper;
import com.yd.csf.service.vo.FortuneVO;
import com.yd.user.feign.client.sysdict.ApiSysDictFeignClient;
import com.yd.user.feign.response.sysdict.GetDictItemListByDictTypeResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Zhang Jianan
 * @description 针对表【fortune(保单发佣表)】的数据库操作Service实现
 * @createDate 2025-09-22 13:22:08
 */
@Service
public class FortuneServiceImpl extends ServiceImpl<FortuneMapper, Fortune>
        implements FortuneService {

    @Resource
    private FortuneAccountService fortuneAccountService;
    @Resource
    private CommissionService commissionService;
    @Resource
    private CommissionExpectedService commissionExpectedService;
    @Resource
    private IExpectedFortuneService expectedFortuneService;
    @Resource
    private PolicyService policyService;
    @Resource
    private ApiSysDictFeignClient apiSysDictFeignClient;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private ApiExchangeRateFeignClient apiExchangeRateFeignClient;

    @Override
    public Wrapper<Fortune> getQueryWrapper(FortuneQueryRequest fortuneQueryRequest) {
        QueryWrapper<Fortune> queryWrapper = new QueryWrapper<>();
        if (fortuneQueryRequest == null) {
            return queryWrapper;
        }

        // 默认查询条件 是否分期出账 1-是 0-否 2-拆分的原纪录
        queryWrapper.in("is_part", 0, 1);

        // 获取参数
        String reconciliationYearMonth = fortuneQueryRequest.getReconciliationYearMonth();
        List<String> statusList = fortuneQueryRequest.getStatusList();
        String policyNo = fortuneQueryRequest.getPolicyNo();
        List<String> insuranceCompanyBizIdList = fortuneQueryRequest.getInsuranceCompanyBizIdList();
        List<String> productLaunchBizIdList = fortuneQueryRequest.getProductLaunchBizIdList();
        LocalDate payoutDateStart = fortuneQueryRequest.getPayoutDateStart();
        LocalDate payoutDateEnd = fortuneQueryRequest.getPayoutDateEnd();
        // 入账状态
        List<String> commissionStatusList = fortuneQueryRequest.getCommissionStatusList();

        queryWrapper.eq(StringUtils.isNotBlank(reconciliationYearMonth), "reconciliation_year_month", reconciliationYearMonth);
        queryWrapper.in(CollUtil.isNotEmpty(statusList), "status", statusList);
        queryWrapper.eq(StringUtils.isNotBlank(policyNo), "policy_no", policyNo);
        queryWrapper.in(CollUtil.isNotEmpty(insuranceCompanyBizIdList), "insurance_company_biz_id", insuranceCompanyBizIdList);
        queryWrapper.in(CollUtil.isNotEmpty(productLaunchBizIdList), "product_launch_biz_id", productLaunchBizIdList);

        queryWrapper.gt(ObjectUtils.isNotEmpty(payoutDateStart), "payout_date", payoutDateStart);
        queryWrapper.lt(ObjectUtils.isNotEmpty(payoutDateEnd), "payout_date", payoutDateEnd);

        // 入账状态, 关联查询 commission_expected 表
        if (CollUtil.isNotEmpty(commissionStatusList)) {
            queryWrapper.apply("EXISTS (SELECT 1 FROM commission_expected ce WHERE ce.commission_expected_biz_id = fortune.commission_expected_biz_id AND ce.status IN ({0}))",
                    Joiner.on(",").join(commissionStatusList));
        }

        queryWrapper.orderByAsc("id");
        return queryWrapper;
    }

    @Override
    public Page<FortuneVO> getFortuneVOPage(Page<Fortune> fortunePage) {
        List<Fortune> fortuneList = fortunePage.getRecords();
        Page<FortuneVO> fortuneVOPage = new Page<>(fortunePage.getCurrent(), fortunePage.getSize(), fortunePage.getTotal());
        if (CollUtil.isEmpty(fortuneList)) {
            return fortuneVOPage;
        }

        List<FortuneVO> fortuneVOList = new ArrayList<>();
        Set<String> expectedFortuneBizIdSet = new HashSet<>();
        Set<String> policyNoSet = new HashSet<>();
        for (Fortune fortune : fortuneList) {
            FortuneVO fortuneVO = FortuneVO.objToVo(fortune);
            fortuneVOList.add(fortuneVO);
            expectedFortuneBizIdSet.add(fortune.getExpectedFortuneBizId());
            policyNoSet.add(fortune.getPolicyNo());
        }

        Set<String> commissionExpectedBizIdSet = fortuneList.stream().map(Fortune::getCommissionExpectedBizId).collect(Collectors.toSet());
        // 1 关联查询来佣预期信息
        List<CommissionExpected> commissionExpectedList = commissionExpectedService.lambdaQuery()
                .in(CommissionExpected::getCommissionExpectedBizId, commissionExpectedBizIdSet)
                .select(CommissionExpected::getCommissionExpectedBizId, CommissionExpected::getPaidAmount, CommissionExpected::getPaidRatio)
                .list();
        Map<String, CommissionExpected> commissionExpectedMap = commissionExpectedList.stream().collect(Collectors.toMap(CommissionExpected::getCommissionExpectedBizId, a -> a, (oldValue, newValue) -> newValue));

        // 2.关联查询保单信息
        QueryWrapper<Policy> queryWrapper = new QueryWrapper<Policy>();
        queryWrapper.select("policy_no", "payment_premium", "product_name", "insurance_company", "reconciliation_company");
        queryWrapper.in("policy_no", policyNoSet);
        List<Policy> policyList = policyService.list(queryWrapper);
        Map<String, Policy> policyMap = policyList.stream().collect(Collectors.toMap(Policy::getPolicyNo, a -> a, (oldValue, newValue) -> newValue));

        fortuneVOList.forEach(fortuneVO -> {
            String commissionBizId = fortuneVO.getCommissionExpectedBizId();
            // 设置来佣金额和比例
            CommissionExpected commissionExpected = commissionExpectedMap.get(commissionBizId);
            if (commissionExpected != null) {
                fortuneVO.setCommissionPaidAmount(commissionExpected.getPaidAmount());
                fortuneVO.setCommissionPaidRatio(commissionExpected.getPaidRatio());
            }

            // 设置保单信息
            Policy policy = policyMap.get(fortuneVO.getPolicyNo());
            if (policy != null) {
                fortuneVO.setPremium(policy.getPaymentPremium());
                fortuneVO.setInsuranceCompany(policy.getInsuranceCompany());
                fortuneVO.setProductName(policy.getProductName());
            }
        });

        fortuneVOPage.setRecords(fortuneVOList);
        return fortuneVOPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateFortune(FortuneUpdateRequest fortuneUpdateRequest) {
        if (fortuneUpdateRequest.getStatus() == null) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账状态不能为空");
        }
        if (!StringUtils.equalsAny(fortuneUpdateRequest.getStatus(), FortuneStatusEnum.RESERVED.getItemValue(), FortuneStatusEnum.INVALID.getItemValue(), FortuneStatusEnum.CHECKED.getItemValue(), FortuneStatusEnum.CAN_SEND.getItemValue())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账状态只能为可出帐，待检核、保留、已失效、可出帐，检核完成");
        }

        String fortuneBizId = fortuneUpdateRequest.getFortuneBizId();
        // 查询出账数据
        Fortune fortune = this.getOne(new QueryWrapper<Fortune>().eq("fortune_biz_id", fortuneBizId));
        if (fortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "发佣数据不存在");
        }
        // 查询预计出账数据
        ExpectedFortune expectedFortune = expectedFortuneService.getOne(new QueryWrapper<ExpectedFortune>().eq("expected_fortune_biz_id", fortune.getExpectedFortuneBizId()));
        if (expectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "预计出账数据不存在");
        }
        BigDecimal currentPaymentHkdAmount = fortuneUpdateRequest.getCurrentPaymentHkdAmount();
        if (currentPaymentHkdAmount != null) {
            if (currentPaymentHkdAmount.compareTo(expectedFortune.getHkdAmount()) > 0) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账港币金额不能大于应付款港币金额");
            }
        }

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        // 检核人
        fortune.setReconciliationOperator(currentLoginUser.getUsername());

        // 如果传入了金额
        if (currentPaymentHkdAmount != null && currentPaymentHkdAmount.compareTo(BigDecimal.ZERO) != 0) {
            splitFortune(fortune, currentPaymentHkdAmount, expectedFortune, loginUserId, fortuneUpdateRequest);
        } else {
            // 如果未传入金额，全额发放，出账状态为已出帐
            totalFortune(fortune, expectedFortune, loginUserId, fortuneUpdateRequest);
        }

        // 更新 expected_fortune 出账状态
        expectedFortuneService.lambdaUpdate()
                .set(ExpectedFortune::getStatus, fortuneUpdateRequest.getStatus())
                .eq(ExpectedFortune::getId, expectedFortune.getId())
                .update();

        return true;
    }

    private void totalFortune(Fortune fortune, ExpectedFortune expectedFortune, String loginUserId, FortuneUpdateRequest fortuneUpdateRequest) {

        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();

        // 1. 更新fortune记录为全额发放状态
        fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
        fortune.setCurrentPaymentRatio(BigDecimal.valueOf(100));
        fortune.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
        fortune.setReconciliationOperator(currentLoginUser.getUsername());
        fortune.setUpdaterId(loginUserId);
        fortune.setUpdateTime(new Date());

        this.updateById(fortune);
    }

    /**
     * 保单币种对预计来佣的结算币种的默认汇率
     * @param policyCurrency 保单币种
     * @param currency 预计来佣的结算币种
     * @return 汇率
     */
    private BigDecimal queryExchangeRateByFeign(String policyCurrency, String currency) {
        if (policyCurrency.equalsIgnoreCase(currency)) {
            return BigDecimal.ONE;
        }
        // 调用Feign客户端查询汇率
        Result<BigDecimal> result = apiExchangeRateFeignClient.getExchangeRate(policyCurrency, currency, "");
        if (result != null && result.getData() != null) {
            return result.getData();
        }
        return BigDecimal.ONE;
    }

    /**
     * 拆分发放（按本次发放金额拆）
     */
    @Transactional(rollbackFor = Exception.class)
    public void splitFortune(Fortune main, BigDecimal currentPaymentHkdAmount, ExpectedFortune expectedFortune, String loginUserId, FortuneUpdateRequest fortuneUpdateRequest) {

        if (!main.getStatus().equals(FortuneStatusEnum.CAN_SEND.getItemValue()))
            throw new BusinessException("只有可出账记录才能修改金额");

        BigDecimal fullAmount = main.getHkdAmount();
        if (currentPaymentHkdAmount.compareTo(main.getCurrentPaymentHkdAmount()) > 0)
            throw new BusinessException("本次发放金额不能大于可出账金额");

        // 获取当前登录用户的ID
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();

        // 1. 更新主行 = 本次发放金额
        main.setCurrentPaymentAmount(null);
        main.setCurrentPaymentHkdAmount(currentPaymentHkdAmount);
        main.setCurrentPaymentRatio(currentPaymentHkdAmount
                .divide(expectedFortune.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
        main.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
        main.setReconciliationOperator(currentLoginUser.getUsername());
        main.setUpdaterId(loginUserId);
        main.setUpdateTime(new Date());

        this.updateById(main);

        // 2. 剩余金额生成新行
        BigDecimal leftAmount = fullAmount.subtract(currentPaymentHkdAmount);
        if (leftAmount.compareTo(BigDecimal.ZERO) > 0) {
            Fortune part = new Fortune();
            BeanUtils.copyProperties(main, part, "id", "fortuneBizId", "currentPaymentAmount", "status", "isPart", "payoutDate");
            part.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
            part.setCurrentPaymentAmount(null);
            part.setCurrentPaymentHkdAmount(leftAmount);
            part.setCurrentPaymentRatio(leftAmount
                    .divide(expectedFortune.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
            part.setStatus(FortuneStatusEnum.RESERVED.getItemValue());
            part.setIsPart(1);
            part.setPayoutDate(LocalDate.now().plusMonths(1)); // 次月
            part.setReconciliationOperator(currentLoginUser.getUsername());
            part.setCreatorId(loginUserId);
            part.setCreateTime(new Date());
            this.save(part);
        }
    }

    private BigDecimal calculateExpectedFortunePaidAmount(ExpectedFortune expectedFortune) {
        // 查询所有已出账的记录
        List<Fortune> paidList = this.list(new QueryWrapper<Fortune>()
                .eq("expected_fortune_biz_id", expectedFortune.getExpectedFortuneBizId())
                .eq("fortune_period", expectedFortune.getFortunePeriod())
                .eq("status", FortuneStatusEnum.CHECKED.getItemValue()));
        // 计算已出账金额
        return paidList.stream()
                .map(Fortune::getCurrentPaymentHkdAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Override
    public void downloadFortune(FortuneDownloadRequest fortuneDownloadRequest, HttpServletResponse response) throws IOException {
        List<String> fortuneBizIdList = fortuneDownloadRequest.getFortuneBizIdList();
        if (CollUtil.isNotEmpty(fortuneBizIdList)) {
            List<Fortune> fortuneList = this.list(new QueryWrapper<Fortune>().in("fortune_biz_id", fortuneBizIdList));
            if (CollUtil.isNotEmpty(fortuneList)) {
                // 转换为导出DTO
                List<FortuneExportDTO> exportDTOList = fortuneList.stream().map(fortune -> {
                    FortuneExportDTO exportDTO = new FortuneExportDTO();
                    BeanUtils.copyProperties(fortune, exportDTO);
                    return exportDTO;
                }).collect(Collectors.toList());

                // 设置响应头
                response.setContentType("application/vnd.ms-excel;charset=UTF-8");
                response.setCharacterEncoding("utf-8");
                String encode = URLEncoder.encode("保单发佣.xlsx", "UTF-8");
                response.setHeader("Content-Disposition", "attachment;filename=" + "fortune_" + System.currentTimeMillis() + ".xlsx");

                // 导出
                try {
                    EasyExcel.write(response.getOutputStream(), FortuneExportDTO.class).sheet("发佣").doWrite(exportDTOList);
                } catch (IOException e) {
                    log.error("导出保单发佣数据失败", e);
                    throw new BusinessException(ResultCode.FAIL.getCode(), "导出失败");
                }
            }
        }
    }

    @Override
    public void downloadAccount(FortuneDownloadRequest fortuneDownloadRequest, HttpServletResponse response) throws UnsupportedEncodingException {
        List<String> fortuneBizIdList = fortuneDownloadRequest.getFortuneBizIdList();
        if (CollUtil.isNotEmpty(fortuneBizIdList)) {
            List<Fortune> fortuneList = this.list(new QueryWrapper<Fortune>().in("fortune_biz_id", fortuneBizIdList));
            // 校验发佣记录状态
            StringBuilder validateMsg = new StringBuilder();
            for (Fortune fortune : fortuneList) {
                if (StringUtils.isNotBlank(fortune.getFortuneAccountBizId())) {
                    validateMsg.append(fortune.getPolicyNo()).append("-").append(fortune.getBroker()).append("已发薪资，不能重复生成薪资记录; ");
                }
                if (StringUtils.equals(fortune.getStatus(), FortuneStatusEnum.WAIT.getItemValue())) {
                    validateMsg.append(fortune.getPolicyNo()).append("-").append(fortune.getBroker()).append("待出账状态不能直接改为'可出账, 检核完成'，不能生成出账记录; ");
                }
                if (StringUtils.equals(fortune.getStatus(), FortuneStatusEnum.SENT.getItemValue())) {
                    validateMsg.append(fortune.getPolicyNo()).append("-").append(fortune.getBroker()).append("已完成出账，不能生成出账记录; ");
                }
            }
            if (StringUtils.isNotBlank(validateMsg.toString())) {
                throw new BusinessException(ResultCode.FAIL.getCode(), validateMsg.toString());
            }

            if (CollUtil.isNotEmpty(fortuneList)) {
                // 按人和发放币种分组
                Map<String, List<Fortune>> fortuneMap = fortuneList.stream().collect(Collectors.groupingBy(fortune -> fortune.getBroker() + "_" + fortune.getPayoutCurrency()));

                // 创建按人分组的导出DTO列表
                List<FortuneAccountExportDTO> accountExportDTOList = new ArrayList<>();

                // 设置出账年月（实）
                LocalDate actualPayoutDate = LocalDate.parse(fortuneDownloadRequest.getActualPayoutDate() + "-01");
                fortuneList.forEach(fortune -> fortune.setActualPayoutDate(actualPayoutDate));

                // 处理每个分组的数据
                for (Map.Entry<String, List<Fortune>> entry : fortuneMap.entrySet()) {
                    String broker = entry.getKey().split("_")[0];
                    String currency = entry.getKey().split("_")[1];
                    List<Fortune> brokerFortunes = entry.getValue();

                    FortuneAccountExportDTO accountDTO = new FortuneAccountExportDTO();
                    accountDTO.setBroker(broker);
                    accountDTO.setBrokerBizId(brokerFortunes.get(0).getBrokerBizId());
                    accountDTO.setFortuneList(brokerFortunes);

                    // 设置团队、币种（取第一个记录）
                    if (CollUtil.isNotEmpty(brokerFortunes)) {
                        accountDTO.setTeam(brokerFortunes.get(0).getTeam());
                        accountDTO.setTeamBizId(brokerFortunes.get(0).getTeamBizId());
                        accountDTO.setCurrency(currency);
                        accountDTO.setActualPayoutDate(brokerFortunes.get(0).getActualPayoutDate());
                    }

                    // 计算出账总额
                    BigDecimal totalAmount = brokerFortunes.stream()
                            .map(Fortune::getCurrentPaymentHkdAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    accountDTO.setAmount(totalAmount);

                    // 按fortune项目分组并累加金额
                    Map<String, BigDecimal> fortuneAmountMap = brokerFortunes.stream()
                            .collect(Collectors.groupingBy(
                                    Fortune::getFortuneName,
                                    Collectors.reducing(BigDecimal.ZERO, Fortune::getCurrentPaymentHkdAmount, BigDecimal::add)
                            ));

                    // 设置各个fortune项目的金额
                    fortuneAmountMap.forEach(accountDTO::addFortuneAmount);

                    accountExportDTOList.add(accountDTO);
                }

//                // 设置响应头
//                response.setContentType("application/vnd.ms-excel;charset=UTF-8");
//                response.setCharacterEncoding("utf-8");
//                String fileName = "fortune_account_" + System.currentTimeMillis() + ".xlsx";
//                response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
//
//                // 创建动态表头
//                List<List<String>> head = new ArrayList<>();
//                head.add(Collections.singletonList("转介人"));
//                head.add(Collections.singletonList("所属团队"));
//                head.add(Collections.singletonList("出账币种"));
//                head.add(Collections.singletonList("出账总额"));
//
//                // 添加所有fortune项目作为表头
//                for (String fortuneName : allFortuneNames) {
//                    head.add(Collections.singletonList(fortuneName + "金额"));
//                }
//
//                // 构建数据行
//                List<List<Object>> dataList = new ArrayList<>();
//                for (FortuneAccountExportDTO dto : accountExportDTOList) {
//                    List<Object> row = new ArrayList<>();
//                    row.add(dto.getBroker());
//                    row.add(dto.getTeam());
//                    row.add(dto.getCurrency());
//                    row.add(dto.getAmount());
//                    for (String fortuneName : allFortuneNames) {
//                        row.add(dto.getFortuneAmount(fortuneName));
//                    }
//                    dataList.add(row);
//                }

                // 写入数据库
                fortuneAccountService.saveFortuneAccount(accountExportDTOList, actualPayoutDate);

//                // 使用try-with-resources确保流正确关闭
//                try (ServletOutputStream outputStream = response.getOutputStream()) {
//                    // 写入 Excel
//                    EasyExcel.write(outputStream)
//                            .head(head)
//                            .sheet("出账清单")
//                            .doWrite(dataList);
//
//                    outputStream.flush();
//                } catch (Exception e) {
//                    log.error("导出失败", e);
//                    throw new BusinessException(ResultCode.FAIL.getCode(), "导出失败");
//                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateFortuneStatus(FortuneStatusUpdateRequest fortuneStatusUpdateRequest) {

        List<String> fortuneBizIdList = fortuneStatusUpdateRequest.getFortuneBizIdList();
        List<Fortune> fortuneList = this.list(new QueryWrapper<Fortune>().in("fortune_biz_id", fortuneBizIdList));

        // 获取当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();

        Date now = new Date();
        List<Fortune> updateList = new ArrayList<>();
        for (Fortune fortune : fortuneList) {
            Fortune updateFortune = new Fortune();
            updateFortune.setId(fortune.getId());
            updateFortune.setUpdaterId(loginUserId);
            updateFortune.setUpdateTime(now);
            updateFortune.setStatus(fortuneStatusUpdateRequest.getStatus());
            updateList.add(updateFortune);
        }

        return this.updateBatchById(updateList);
    }

    @Override
    public Fortune getByFortuneBizId(String fortuneBizId) {
        return this.getOne(new QueryWrapper<Fortune>().eq("fortune_biz_id", fortuneBizId));
    }

    @Override
    public Boolean addFortune(FortuneAddRequest fortuneAddRequest) {

        valiAddFortune(fortuneAddRequest);

        // 创建Fortune实体
        Fortune fortune = new Fortune();
        BeanUtil.copyProperties(fortuneAddRequest, fortune);

        if ("R".equals(fortuneAddRequest.getFortuneBizType())) {
            if (ObjectUtils.isEmpty(fortuneAddRequest.getPolicyNo())) {
                throw new BusinessException(ResultCode.FAIL.getCode(), "关联保单应付款，保单号不能为空");
            }
            if (ObjectUtils.isEmpty(fortuneAddRequest.getFortunePeriod())) {
                throw new BusinessException(ResultCode.FAIL.getCode(), "关联保单应付款，佣金期数不能为空");
            }

            ExpectedFortune expectedFortune = expectedFortuneService.lambdaQuery()
                    .eq(ExpectedFortune::getPolicyNo, fortuneAddRequest.getPolicyNo())
                    .eq(ExpectedFortune::getFortunePeriod, fortuneAddRequest.getFortunePeriod())
                    .eq(ExpectedFortune::getFortuneType, fortuneAddRequest.getFortuneType())
                    .one();

            if (expectedFortune == null) {
                throw new BusinessException(ResultCode.FAIL.getCode(), "未查询到当前项目对应的预计出账数据");
            }

            fortune.setExpectedFortuneBizId(expectedFortune.getExpectedFortuneBizId());
            fortune.setCommissionExpectedBizId(expectedFortune.getExpectedFortuneBizId());
            fortune.setPolicyCurrency(expectedFortune.getPolicyCurrency());

            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
            fortune.setPayoutDate(expectedFortune.getPayoutDate());
        } else {
            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
        }

        // 生成发佣业务ID
        fortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
        fortune.setCurrentPaymentAmount(fortuneAddRequest.getPayoutAmount());
        fortune.setFortuneName(queryByDict(fortuneAddRequest.getFortuneType()));
        fortune.setActualPayoutDate(fortuneAddRequest.getPayoutDate());
        fortune.setIsPart(0);
        fortune.setStatus(FortuneStatusEnum.CAN_SEND.getItemValue());

        return this.save(fortune);
    }

    private static void valiAddFortune(FortuneAddRequest fortuneAddRequest) {
        if (ObjectUtils.isEmpty(fortuneAddRequest.getFortuneBizType())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账类型不能为空");
        }

        if (ObjectUtils.isEmpty(fortuneAddRequest.getOriginalCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getOriginalAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getOriginalToHkdRate())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种→港币汇率不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getPayoutCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "发放币种不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getPayoutAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "发放币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getRuleCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getRuleAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getExchangeRate())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种→港币汇率不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getHkdAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "港币金额不能为空");
        }

        if (ObjectUtils.isEmpty(fortuneAddRequest.getFortuneType())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账项目不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getBrokerBizId())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "转介人不能为空");
        }
        if ("R".equals(fortuneAddRequest.getFortuneBizType())) {
            if (ObjectUtils.isEmpty(fortuneAddRequest.getPolicyNo())) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "关联保单应付款，保单号不能为空");
            }
            if (ObjectUtils.isEmpty(fortuneAddRequest.getFortunePeriod())) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "关联保单应付款，佣金期数不能为空");
            }
        }
    }

    private String queryByDict(String fortuneType) {
        Result<List<GetDictItemListByDictTypeResponse>> result = apiSysDictFeignClient.getDictItemListByDictType("csf_fortune_type");
        if (CollectionUtils.isNotEmpty(result.getData())) {
            for (GetDictItemListByDictTypeResponse dictItem : result.getData()) {
                if (StringUtils.equalsIgnoreCase(dictItem.getItemValue(), fortuneType)) {
                    return dictItem.getItemLabel();
                }
            }
        }
        return null;
    }

    /**
     * 查询单个对象
     *
     * @param fortuneBizId
     * @return
     */
    @Override
    public Fortune queryOne(String fortuneBizId) {
        return this.getOne(new LambdaQueryWrapper<Fortune>().eq(Fortune::getFortuneBizId, fortuneBizId));
    }

    @Override
    public FortuneStatisticsVO getFortuneStatistics(List<Long> fortuneIdList) {
        if (CollectionUtils.isEmpty(fortuneIdList)) {
            FortuneStatisticsVO fortuneStatisticsVO = new FortuneStatisticsVO();
            fortuneStatisticsVO.setTotalInAmount(BigDecimal.ZERO);
            fortuneStatisticsVO.setTotalPolicyCount(0L);
            fortuneStatisticsVO.setTotalPremium(BigDecimal.ZERO);
            fortuneStatisticsVO.setPendingOutAmount(BigDecimal.ZERO);
            fortuneStatisticsVO.setAvailableOutAmount(BigDecimal.ZERO);
            return fortuneStatisticsVO;
        }
        // 自定义查询
        List<FortuneStatisticsVO> fortuneStatisticsList = this.baseMapper.getFortuneStatistics(fortuneIdList);

        // 查询结果
        FortuneStatisticsVO fortuneStatistics;
        if (CollectionUtils.isNotEmpty(fortuneStatisticsList) && fortuneStatisticsList.size() > 1) {
            // 合并查询结果
            FortuneStatisticsVO noPolicyNoData = fortuneStatisticsList.get(0);
            FortuneStatisticsVO policyNoData = fortuneStatisticsList.get(1);
            // 合并查询结果
            fortuneStatistics = new FortuneStatisticsVO();
            fortuneStatistics.setTotalInAmount(policyNoData.getTotalInAmount());
            fortuneStatistics.setTotalPolicyCount(policyNoData.getTotalPolicyCount());
            fortuneStatistics.setTotalPremium(policyNoData.getTotalPremium());
            fortuneStatistics.setPendingOutAmount(noPolicyNoData.getPendingOutAmount().add(policyNoData.getPendingOutAmount()));
            fortuneStatistics.setAvailableOutAmount(noPolicyNoData.getAvailableOutAmount().add(policyNoData.getAvailableOutAmount()));
        } else {
            fortuneStatistics = fortuneStatisticsList.get(0);
        }
        if (fortuneStatistics.getAvailableOutAmount() == null) {
            fortuneStatistics.setAvailableOutAmount(BigDecimal.ZERO);
        }
        // 计算总保费、入账金额
        if (CollectionUtils.isNotEmpty(fortuneIdList)) {
            List<Fortune> fortuneList = this.listByIds(fortuneIdList);

            Map<String, BigDecimal> exchangeRateMap = new HashMap<>();
            for (Fortune fortune : fortuneList) {
                exchangeRateMap.put(fortune.getPolicyNo(), fortune.getExchangeRate());
            }

            Set<String> policyNoSet = fortuneList.stream().map(Fortune::getPolicyNo).collect(Collectors.toSet());
            // 查询总保费
            List<Policy> policyList = policyService.lambdaQuery().in(Policy::getPolicyNo, policyNoSet).list();
            BigDecimal totalPremium = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(policyList)) {
                for (Policy policy : policyList) {
                    BigDecimal exchangeRate = exchangeRateMap.getOrDefault(policy.getPolicyNo(), BigDecimal.ONE);
                    totalPremium = totalPremium.add(policy.getTotalPaymentPremium().multiply(exchangeRate));
                }
                fortuneStatistics.setTotalPremium(totalPremium);
            }
            // 查询总入账金额
            List<Commission> commissions = commissionService.lambdaQuery()
                    .in(Commission::getPolicyNo, policyNoSet)
                    .select(Commission::getHkdAmount)
                    .list();
            BigDecimal totalInAmount = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(commissions)) {
                for (Commission commission : commissions) {
                    totalInAmount = totalInAmount.add(commission.getHkdAmount());
                }
                fortuneStatistics.setTotalInAmount(totalInAmount);
            }
        }

        return fortuneStatistics;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addFortuneBatch(List<FortuneAddRequest> fortuneAddRequestList) {
        if (CollectionUtils.isEmpty(fortuneAddRequestList)) {
            return false;
        }

        Set<String> policyNoSet = fortuneAddRequestList.stream()
                .map(FortuneAddRequest::getPolicyNo)
                .filter(StringUtils::isNotBlank).collect(Collectors.toSet());

        // 查询保单号
        Map<String, Policy> policyMap = new HashMap<>();
        // 查询预计出账
        Map<String, ExpectedFortune> expectedFortuneMap = new HashMap<>();
        // 查询入账记录
        Map<String, Commission> commissionByPolicyPeriod = new HashMap<>();
        if (CollectionUtils.isNotEmpty(policyNoSet)) {
            // 查询保单号
            List<Policy> policyList = policyService.lambdaQuery().in(Policy::getPolicyNo, policyNoSet).list();
            policyMap = policyList.stream().collect(Collectors.toMap(Policy::getPolicyNo, Function.identity(), (oldValue, newValue) -> newValue));

            // 查询预计出账
            expectedFortuneMap = expectedFortuneService.lambdaQuery()
                    .in(ExpectedFortune::getPolicyNo, policyNoSet)
                    .list()
                    .stream()
                    .collect(Collectors.toMap(
                            i -> i.getPolicyNo() + "_" + i.getFortunePeriod() + "_" + i.getFortuneType(),
                            Function.identity(),
                            (oldValue, newValue) -> newValue)
                    );
            // 查询入账记录
            List<Commission> commissionList = commissionService.lambdaQuery().in(Commission::getPolicyNo, policyNoSet)
                    .list();
            if (CollectionUtils.isNotEmpty(commissionList)) {
                commissionByPolicyPeriod = commissionList.stream()
                        .collect(Collectors.toMap(
                                commission -> commissionService.buildPolicyPeriodKey(commission.getPolicyNo(), commission.getCommissionPeriod()),
                                Function.identity(),
                                (oldValue, newValue) -> newValue  // 遇到重复时使用新值
                        ));
            }
        }

        List<Fortune> fortuneList = new ArrayList<>();
        List<ExpectedFortune> expectedFortuneUpdateList = new ArrayList<>();
        for (FortuneAddRequest fortuneAddRequest : fortuneAddRequestList) {
            // 校验参数
            valiAddFortune(fortuneAddRequest);

            Fortune fortune = new Fortune();
            BeanUtil.copyProperties(fortuneAddRequest, fortune);

            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
            // 查询发佣类型名称
            String fortuneName = queryByDict(fortuneAddRequest.getFortuneType());
            fortune.setFortuneName(fortuneName);


            if ("R".equals(fortuneAddRequest.getFortuneBizType())) {
                Policy policy = policyMap.get(fortuneAddRequest.getPolicyNo());
                if (policy == null) {
                    throw new BusinessException(ResultCode.NULL_ERROR.getCode(), fortuneAddRequest.getPolicyNo() + " 保单号不存在");
                }
                ExpectedFortune expectedFortune = expectedFortuneMap.get(fortuneAddRequest.getPolicyNo() + "_" + fortuneAddRequest.getFortunePeriod() + "_" + fortuneAddRequest.getFortuneType());
                if (expectedFortune == null) {
                    // 同步新增预计出账
                    expectedFortune = createExpectedFortune(fortuneAddRequest, policy, fortuneName);
                    // 保存
                    expectedFortuneService.save(expectedFortune);
                }
                fortune.setExpectedFortuneBizId(expectedFortune.getExpectedFortuneBizId());
                fortune.setPolicyCurrency(expectedFortune.getPolicyCurrency());
                fortune.setCurrentPaymentRatio(BigDecimal.valueOf(100));
                fortune.setPayoutDate(expectedFortune.getPayoutDate());

                // 关联来佣业务ID
                String key = commissionService.buildPolicyPeriodKey(expectedFortune.getPolicyNo(), expectedFortune.getFortunePeriod());
                Commission matchedCommission = commissionByPolicyPeriod.get(key);
                if (matchedCommission != null) {
                    fortune.setCommissionBizId(matchedCommission.getCommissionBizId());
                    fortune.setCommissionExpectedBizId(matchedCommission.getCommissionExpectedBizId());
                } else {
                    fortune.setStatus(FortuneStatusEnum.MATCH_FAIL.getItemValue());
                    fortune.setRemark("未找到当前预计发佣对应的来佣");
                }

            }

            // 生成发佣业务ID
            fortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
            fortune.setCurrentPaymentAmount(fortuneAddRequest.getPayoutAmount());
            fortune.setActualPayoutDate(fortuneAddRequest.getPayoutDate());
            fortune.setIsPart(0);
            fortuneList.add(fortune);
        }
        this.saveBatch(fortuneList);

        if (CollectionUtils.isNotEmpty(expectedFortuneUpdateList)) {
            expectedFortuneService.updateBatchById(expectedFortuneUpdateList);
        }

        return true;
    }

    private ExpectedFortune createExpectedFortune(FortuneAddRequest fortuneAddRequest, Policy policy, String fortuneName) {

        // 应付款编号（序号递增）
        String payableNo = expectedFortuneService.getPayableNo(fortuneAddRequest.getFortuneType());

        // 创建 expectedFortune
        ExpectedFortune expectedFortune = new ExpectedFortune();
        BeanUtil.copyProperties(fortuneAddRequest, expectedFortune);

        // 设置 policy 关联字段
        expectedFortune.setInsuranceCompanyBizId(policy.getInsuranceCompanyBizId());
        expectedFortune.setProductLaunchBizId(policy.getProductLaunchBizId());
        expectedFortune.setPremium(policy.getPaymentPremium());
        expectedFortune.setPolicyCurrency(policy.getCurrency());

        // 预计发佣业务id
        expectedFortune.setExpectedFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_EXPECTED_FORTUNE.getCode()));
        // 预计发佣类型名称
        expectedFortune.setFortuneName(fortuneName);
        // 应付款编号
        expectedFortune.setPayableNo(payableNo);

        // 已出帐金额、待出帐金额、已出帐比例、待出帐比例
        expectedFortune.setPaidAmount(BigDecimal.ZERO);
        // 转介人比例默认100%
        expectedFortune.setBrokerRatio("100");
        expectedFortune.setUnpaidAmount(expectedFortune.getHkdAmount());
        expectedFortune.setPaidRatio(BigDecimal.ZERO);
        expectedFortune.setUnpaidRatio(BigDecimal.valueOf(100));

        return expectedFortune;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean splitFortune(FortuneSplitRequest fortuneSplitRequest) {
        // 1. 参数验证
        validSplitFortune(fortuneSplitRequest);

        // 2. 查询并验证原始记录
        Fortune originalFortune = this.getByFortuneBizId(fortuneSplitRequest.getFortuneBizId());
        if (originalFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "原出账记录不存在");
        }

        ExpectedFortune originalExpectedFortune = expectedFortuneService.getByBizId(originalFortune.getExpectedFortuneBizId());
        if (originalExpectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "对应的预计出账记录不存在");
        }

        if (FortuneStatusEnum.SENT.getItemValue().equals(originalFortune.getStatus())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "已完成出账状态的记录不能分期");
        }

        if (originalFortune.getIsPart() != null && originalFortune.getIsPart() == 1) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "该记录已是分期拆分记录，不能再次分期");
        }

        if (StringUtils.isNotBlank(originalFortune.getFortuneAccountBizId())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "该记录已有薪资发放，不能分期");
        }

        List<FortuneSplitDto> splitList = fortuneSplitRequest.getFortuneSplitDtoList();
        // 3. 金额验证
        BigDecimal totalSplitOriginalAmount = splitList.stream()
            .map(FortuneSplitDto::getOriginalAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalSplitRatio = splitList.stream()
            .map(FortuneSplitDto::getSplitRatio)
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal tolerance = new BigDecimal("0.01");
        BigDecimal originalDifference = originalFortune.getOriginalAmount().subtract(totalSplitOriginalAmount).abs();
        if (originalDifference.compareTo(tolerance) > 0) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
                String.format("分期原始金额总和(%s)与原记录原始金额(%s)不匹配", totalSplitOriginalAmount, originalFortune.getOriginalAmount()));
        }

        if (totalSplitRatio.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal ratioDifference = new BigDecimal("100").subtract(totalSplitRatio).abs();
            if (ratioDifference.compareTo(new BigDecimal("0.1")) > 0) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
                    String.format("分期比例总和(%s%%)应为100%%", totalSplitRatio));
            }
        }

        // 4. 生成新记录
        List<Fortune> newFortuneList = new ArrayList<>();
        List<ExpectedFortune> newExpectedFortuneList = new ArrayList<>();

        Date now = new Date();
        LocalDateTime localDateTime = LocalDateTime.now();

        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        String loginUserId = currentLoginUser.getId().toString();
        String username = currentLoginUser.getUsername();

        for (int i = 0; i < splitList.size(); i++) {
            FortuneSplitDto splitDto = splitList.get(i);

            // 4.1 生成新的 ExpectedFortune
            ExpectedFortune newExpectedFortune = new ExpectedFortune();

            BeanUtils.copyProperties(originalExpectedFortune, newExpectedFortune,
                "id", "expectedFortuneBizId", "amount", "hkdAmount",
                "paidAmount", "unpaidAmount", "paidRatio", "unpaidRatio",
                "status", "payoutDate", "actualPayoutDate", "remark");

            String newExpectedFortuneBizId = RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_EXPECTED_FORTUNE.getCode());
            newExpectedFortune.setExpectedFortuneBizId(newExpectedFortuneBizId);

            // 赋值分期出账信息
            BeanUtils.copyProperties(splitDto, newExpectedFortune);

            // 生成应付款编号
            String payableNo = originalExpectedFortune.getPayableNo() + "_" + (i + 1);
            newExpectedFortune.setPayableNo(payableNo);

            newExpectedFortune.setDefaultExchangeRate(splitDto.getExchangeRate());

            newExpectedFortune.setPaidAmount(BigDecimal.ZERO);
            newExpectedFortune.setUnpaidAmount(splitDto.getHkdAmount());
            newExpectedFortune.setPaidRatio(BigDecimal.ZERO);
            newExpectedFortune.setUnpaidRatio(BigDecimal.valueOf(100));

            newExpectedFortune.setStatus("0");

            newExpectedFortune.setPayoutDate(LocalDate.parse(splitDto.getPayoutYearMonth() + "-01"));

            String expectedRemark = StringUtils.isBlank(splitDto.getRemark()) ? "" : splitDto.getRemark();
            if (StringUtils.isNotBlank(originalExpectedFortune.getRemark())) {
                expectedRemark = StringUtils.isBlank(expectedRemark) ?
                        originalExpectedFortune.getRemark() :
                        originalExpectedFortune.getRemark() + "; " + expectedRemark;
            }
            newExpectedFortune.setRemark(expectedRemark);

            newExpectedFortune.setCreatorId(loginUserId);
            newExpectedFortune.setCreatorName(username);
            newExpectedFortune.setCreateTime(localDateTime);
            newExpectedFortune.setUpdaterId(loginUserId);
            newExpectedFortune.setUpdateTime(localDateTime);

            newExpectedFortuneList.add(newExpectedFortune);

            // 4.2 生成新的 Fortune
            Fortune newFortune = new Fortune();

            BeanUtils.copyProperties(originalFortune, newFortune,
                "id", "fortuneBizId", "expectedFortuneBizId",
                "amount", "hkdAmount", "currentPaymentAmount", "currentPaymentHkdAmount",
                "currentPaymentRatio", "status", "isPart",
                "payoutDate", "actualPayoutDate", "remark");

            newFortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));

            newFortune.setExpectedFortuneBizId(newExpectedFortuneBizId);

            // 赋值分期出账信息
            BeanUtils.copyProperties(splitDto, newFortune);
            // 特殊字段
            newFortune.setCurrentPaymentAmount(splitDto.getPayoutAmount());
            newFortune.setCurrentPaymentHkdAmount(splitDto.getHkdAmount());
            newFortune.setCurrentPaymentRatio(splitDto.getSplitRatio());

            newFortune.setStatus(FortuneStatusEnum.CAN_SEND.getItemValue());
            newFortune.setIsPart(1);

            newFortune.setPayoutDate(newExpectedFortune.getPayoutDate());

            String fortuneRemark = StringUtils.isBlank(splitDto.getRemark()) ? "" : splitDto.getRemark();
            if (StringUtils.isNotBlank(originalFortune.getRemark())) {
                fortuneRemark = StringUtils.isBlank(fortuneRemark) ?
                    originalFortune.getRemark() :
                    originalFortune.getRemark() + "; " + fortuneRemark;
            }
            newFortune.setRemark(fortuneRemark);

            newFortune.setReconciliationOperator(username);
            newFortune.setCreatorId(loginUserId);
            newFortune.setCreateTime(now);
            newFortune.setUpdaterId(loginUserId);
            newFortune.setUpdateTime(now);

            newFortuneList.add(newFortune);
        }

        // 5.1 设置原始 fortune记录 is_part 为 2，表示该记录已被分期
        this.lambdaUpdate()
                .set(Fortune::getIsPart, 2)
                .eq(Fortune::getId, originalFortune.getId())
                .update();

        // 5.2 设置原始 expected fortune记录 is_part 为 2，表示该记录已被分期
        expectedFortuneService.lambdaUpdate()
                .set(ExpectedFortune::getIsPart, 2)
                .eq(ExpectedFortune::getId, originalExpectedFortune.getId())
                .update();

        // 6. 批量保存新记录
        boolean expectedSaveSuccess = expectedFortuneService.saveBatch(newExpectedFortuneList);
        if (!expectedSaveSuccess) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "分期预计出账记录保存失败");
        }

        boolean fortuneSaveSuccess = this.saveBatch(newFortuneList);
        if (!fortuneSaveSuccess) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "分期出账记录保存失败");
        }

        // 7. 返回结果
        return true;
    }

    @Override
    public Boolean editActualPayoutDate(EditActualPayoutDateRequest editActualPayoutDateRequest) {
        String fortuneBizId = editActualPayoutDateRequest.getFortuneBizId();

        Fortune fortune = this.getByFortuneBizId(fortuneBizId);
        if (fortune == null) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账记录不存在");
        }

        ExpectedFortune originalExpectedFortune = expectedFortuneService.getByBizId(fortune.getExpectedFortuneBizId());
        if (originalExpectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "对应的预计出账记录不存在");
        }

        // 计算实际出账日期
        LocalDate actualPayoutDate = LocalDate.parse(editActualPayoutDateRequest.getActualPayoutDate() + "-01");

        // 更新实际出账日期
        this.lambdaUpdate()
                .set(Fortune::getActualPayoutDate, actualPayoutDate)
                .eq(Fortune::getId, fortune.getId())
                .update();

        expectedFortuneService.lambdaUpdate()
                .set(ExpectedFortune::getActualPayoutDate, actualPayoutDate)
                .eq(ExpectedFortune::getId, originalExpectedFortune.getId())
                .update();

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editExchangeRate(EditExchangeRateRequest editExchangeRateRequest) {

        validEditExchangeRate(editExchangeRateRequest);

        String fortuneBizId = editExchangeRateRequest.getFortuneBizId();

        Fortune fortune = this.getByFortuneBizId(fortuneBizId);
        if (fortune == null) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "出账记录不存在");
        }
        if (FortuneStatusEnum.SENT.getItemValue().equals(fortune.getStatus())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "已完成出账状态的记录不能修改结算汇率");
        }

        ExpectedFortune originalExpectedFortune = expectedFortuneService.getByBizId(fortune.getExpectedFortuneBizId());
        if (originalExpectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "对应的预计出账记录不存在");
        }

        // 获取入参
        BigDecimal ruleAmount = editExchangeRateRequest.getRuleAmount();
        String ruleCurrency = editExchangeRateRequest.getRuleCurrency();
        BigDecimal exchangeRate = editExchangeRateRequest.getExchangeRate();
        String originalCurrency = editExchangeRateRequest.getOriginalCurrency();
        BigDecimal originalAmount = editExchangeRateRequest.getOriginalAmount();
        BigDecimal originalToHkdRate = editExchangeRateRequest.getOriginalToHkdRate();
        String payoutCurrency = editExchangeRateRequest.getPayoutCurrency();
        BigDecimal payoutAmount = editExchangeRateRequest.getPayoutAmount();
        BigDecimal hkdToPayoutRate = editExchangeRateRequest.getHkdToPayoutRate();
        BigDecimal hkdAmount = editExchangeRateRequest.getHkdAmount();

        // 更新 fortune 的字段
        this.lambdaUpdate()
                .set(Fortune::getRuleAmount, ruleAmount)
                .set(Fortune::getRuleCurrency, ruleCurrency)
                .set(Fortune::getExchangeRate, exchangeRate)
                .set(Fortune::getOriginalCurrency, originalCurrency)
                .set(Fortune::getOriginalAmount, originalAmount)
                .set(Fortune::getOriginalToHkdRate, originalToHkdRate)
                .set(Fortune::getPayoutCurrency, payoutCurrency)
                .set(Fortune::getPayoutAmount, payoutAmount)
                .set(Fortune::getHkdToPayoutRate, hkdToPayoutRate)
                .set(Fortune::getHkdAmount, hkdAmount)
                .eq(Fortune::getId, fortune.getId())
                .update();

        // 更新 expected fortune 的字段
        expectedFortuneService.lambdaUpdate()
                .set(ExpectedFortune::getRuleAmount, ruleAmount)
                .set(ExpectedFortune::getRuleCurrency, ruleCurrency)
                .set(ExpectedFortune::getDefaultExchangeRate, exchangeRate)
                .set(ExpectedFortune::getOriginalCurrency, originalCurrency)
                .set(ExpectedFortune::getOriginalAmount, originalAmount)
                .set(ExpectedFortune::getOriginalToHkdRate, originalToHkdRate)
                .set(ExpectedFortune::getPayoutCurrency, payoutCurrency)
                .set(ExpectedFortune::getPayoutAmount, payoutAmount)
                .set(ExpectedFortune::getHkdToPayoutRate, hkdToPayoutRate)
                .set(ExpectedFortune::getHkdAmount, hkdAmount)
                .set(ExpectedFortune::getUnpaidAmount, hkdAmount)
                .eq(ExpectedFortune::getId, originalExpectedFortune.getId())
                .update();

        return true;
    }

    private void validEditExchangeRate(EditExchangeRateRequest editExchangeRateRequest) {
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getOriginalCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getOriginalAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getOriginalToHkdRate())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "原币种→港币汇率不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getPayoutCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "发放币种不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getPayoutAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "发放币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getRuleCurrency())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getRuleAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种金额不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getExchangeRate())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单币种→港币汇率不能为空");
        }
        if (ObjectUtils.isEmpty(editExchangeRateRequest.getHkdAmount())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "港币金额不能为空");
        }
    }

    private void validSplitFortune(FortuneSplitRequest fortuneSplitRequest) {
        if (fortuneSplitRequest == null) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "分期出账请求不能为空");
        }
        if (StringUtils.isBlank(fortuneSplitRequest.getFortuneBizId())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "保单出账业务ID不能为空");
        }
        if (CollectionUtils.isEmpty(fortuneSplitRequest.getFortuneSplitDtoList())) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "分期配置列表不能为空");
        }
        if (fortuneSplitRequest.getFortuneSplitDtoList().size() < 2) {
            throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "分期至少需要2条配置");
        }

        List<FortuneSplitDto> splitList = fortuneSplitRequest.getFortuneSplitDtoList();
        for (int i = 0; i < splitList.size(); i++) {
            FortuneSplitDto dto = splitList.get(i);
            if (dto.getHkdAmount() == null || dto.getHkdAmount().compareTo(BigDecimal.ZERO) <= 0) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
                    "第" + (i + 1) + "期：港币金额必须大于0");
            }
            if (dto.getExchangeRate() == null || dto.getExchangeRate().compareTo(BigDecimal.ZERO) <= 0) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
                    "第" + (i + 1) + "期：汇率必须大于0");
            }
            if (StringUtils.isBlank(dto.getPayoutYearMonth())) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(),
                    "第" + (i + 1) + "期：出账年月不能为空");
            }
        }
    }

}




