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

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.constant.RedisConstants;
import com.yd.common.enums.*;
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.api.async.ApiExpectedFortuneAsyncService;
import com.yd.csf.api.dto.*;
import com.yd.csf.api.service.ApiBasicLawCalculateService;
import com.yd.csf.api.service.ApiCommissionConditionService;
import com.yd.csf.api.service.ApiExpectedFortuneLogService;
import com.yd.csf.api.service.ApiExpectedFortuneService;
import com.yd.csf.service.component.DictCacheService;
import com.yd.csf.feign.request.expectedfortune.*;
import com.yd.csf.feign.response.expectedfortune.ApiExpectedFortunePageResponse;
import com.yd.csf.feign.response.expectedfortune.ApiGenerateExpectedFortuneResponse;
import com.yd.csf.service.dto.CommissionRuleBindingDto;
import com.yd.csf.service.dto.QueryPolicyAndBrokerDto;
import com.yd.csf.service.dto.UserGradeDto;
import com.yd.csf.service.enums.CurrencyEnum;
import com.yd.csf.service.enums.FortuneStatusEnum;
import com.yd.csf.service.helper.FeignResultHelper;
import com.yd.csf.service.model.*;
import com.yd.csf.service.service.*;
import com.yd.csf.service.vo.ExpectedFortuneExportDTO;
import com.yd.csf.service.vo.ExpectedFortuneStatisticsVO;
import com.yd.csf.service.vo.PayableReportVO;
import com.yd.product.feign.client.announcementcommissionratio.ApiAnnouncementCommissionRatioFeignClient;
import com.yd.product.feign.request.announcementcommissionratio.ApiAnnouncementCommissionRatioListRequest;
import com.yd.product.feign.response.announcementcommissionratio.ApiAnnouncementCommissionRatioListResponse;
import com.yd.user.feign.client.clientuser.ApiClientUserFeignClient;
import com.yd.user.feign.client.sysdict.ApiSysDictFeignClient;
import com.yd.user.feign.response.sysdict.GetDictItemListByDictTypeResponse;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import static com.ibm.icu.impl.ValidIdentifiers.Datatype.currency;

@Slf4j
@Service
public class ApiExpectedFortuneServiceImpl implements ApiExpectedFortuneService {
    @Autowired
    private PolicyService policyService;
    @Autowired
    private ApiClientUserFeignClient apiClientUserFeignClient;
    @Autowired
    private ICommissionRuleBindingService iCommissionRuleBindingService;
    @Autowired
    private ApiBasicLawCalculateService apiBasicLawCalculateService;
    @Autowired
    private IExpectedFortuneService iExpectedFortuneService;
    @Autowired
    private ApplicationContext applicationContext;

    private ApiExpectedFortuneService getSelf() {
        return applicationContext.getBean(ApiExpectedFortuneService.class);
    }

    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private DictCacheService dictCacheService;
    @Autowired
    private ApiExpectedFortuneLogService apiExpectedFortuneLogService;
    @Autowired
    private PolicyBrokerService policyBrokerService;
    @Autowired
    private ApiSysDictFeignClient apiSysDictFeignClient;
    @Resource
    private PolicyFollowService policyFollowService;
    @Resource
    private ApiExchangeRateFeignClient apiExchangeRateFeignClient;
    @Resource
    private CommissionService commissionService;
    @Resource
    private ApiAnnouncementCommissionRatioFeignClient apiAnnouncementCommissionRatioFeignClient;
    @Resource
    private FeignResultHelper feignResultHelper;
    @Resource
    private ApiAnnouncementCommissionRatioFeignClient ratioFeignClient;
    @Resource
    private FortuneService fortuneService;

    @Autowired
    private ApiCommissionConditionService apiCommissionConditionService;
    @Autowired
    private IAgentAccumulatedFycService iAgentAccumulatedFycService;

    @Lazy
    @Resource
    private ApiExpectedFortuneAsyncService apiExpectedFortuneAsyncService;

    /**
     * 生成预计发佣
     *
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<ApiGenerateExpectedFortuneResponse> generate(ApiGenerateExpectedFortuneRequest request) {
        //查询当前保单号是否正在执行预计发佣的缓存，有值就说明正在执行，无值说明执行完毕或者没有执行。
        String value = redisUtil.getCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
        if (StringUtil.isNotBlank(value)) {
            //有值正在执行，提示
            return Result.fail("生成预计发佣正在执行中，无需再次执行");
        } else {
            //无值设置值，表示正在执行
            redisUtil.setCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo(), request.getPolicyNo());
        }
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(request.getPolicyNo());
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        //根据保单号查询保单和转介人列表信息
        List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList = policyService.queryPolicyBrokerList(request.getPolicyNo());
        if (CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
            throw new BusinessException("保单和转介人信息不存在");
        }

        //查询所有绑定基本法的业务员(客户端用户表用户), 计算业务员绑定的所有基本法对应的计算值, 新增积分明细和发佣记录
        List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
        //根据租户ID和项目ID查询客户端用户ID列表
        Result<List<String>> result = apiClientUserFeignClient.clientUserBizIdList(TenantEnum.YD.getTenantBizId(), ProjectEnum.CSF_MINI_PROGRAM.getProjectBizId());
        if (!CollectionUtils.isEmpty(result.getData())) {
            commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
                    .targetIdList(result.getData())
                    .build());
        }
        if (CollectionUtils.isEmpty(commissionRuleBindingList)) {
            throw new BusinessException("绑定基本法数据不存在");
        }

        //查询产品发佣配置，获取结算币种
        QueryPolicyAndBrokerDto queryPolicyAndBrokerDto = queryPolicyAndBrokerDtoList.get(0);
        ApiAnnouncementCommissionRatioListRequest announcementRatioListRequest = new ApiAnnouncementCommissionRatioListRequest();
        announcementRatioListRequest.setProductLaunchBizId(queryPolicyAndBrokerDto.getPlanBizId());
        announcementRatioListRequest.setEffectiveDate(Date.from(queryPolicyAndBrokerDto.getEffectiveDate().atStartOfDay(ZoneId.systemDefault()).toInstant()));
        announcementRatioListRequest.setPaymentTerm(String.valueOf(queryPolicyAndBrokerDto.getPaymentTerm()));

        List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList;
        try {
            announcementRatioList = feignResultHelper.extractListData(
                    apiAnnouncementCommissionRatioFeignClient.list(announcementRatioListRequest),
                    ApiAnnouncementCommissionRatioListResponse.class
            );
        } catch (Exception e) {
            log.info("查询结算币种->保单号:{}，入参request：{}", request.getPolicyNo(), JSON.toJSONString(announcementRatioListRequest));

            throw new BusinessException("未查询到产品发佣配置，查询条件：" + JSON.toJSONString(announcementRatioListRequest));
        }

        //遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
        // 通过代理对象调用
        getSelf().execute(queryPolicyAndBrokerDtoList, commissionRuleBindingList, request.getPolicyNo(), announcementRatioList);

        return Result.success(null, "生成预计发佣正在处理....，稍后查看预计发佣列表");
    }

    /**
     * 同步生成预计发佣
     *
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<ApiGenerateExpectedFortuneResponse> generateSync(ApiGenerateExpectedFortuneRequest request) {
        //查询当前保单号是否正在执行预计发佣的缓存，有值就说明正在执行，无值说明执行完毕或者没有执行。
        String value = redisUtil.getCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
        if (StringUtil.isNotBlank(value)) {
            //有值正在执行，提示
            return Result.fail("生成预计发佣正在执行中，无需再次执行");
        } else {
            //无值设置值，表示正在执行
            redisUtil.setCacheObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo(), request.getPolicyNo());
        }
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(request.getPolicyNo());
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        //根据保单号查询保单和转介人列表信息
        List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList = policyService.queryPolicyBrokerList(request.getPolicyNo());
        log.info("根据保单号查询保单和转介人列表信息======>,{}",JSON.toJSONString(queryPolicyAndBrokerDtoList));
        if (CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
            throw new BusinessException("保单和转介人信息不存在");
        }

//        //查询所有绑定基本法的业务员(客户端用户表用户), 计算业务员绑定的所有基本法对应的计算值, 新增积分明细和发佣记录
//        List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
//        //根据租户ID和项目ID查询客户端用户ID列表
//        Result<List<String>> result = apiClientUserFeignClient.clientUserBizIdList(TenantEnum.YD.getTenantBizId(), ProjectEnum.CSF_MINI_PROGRAM.getProjectBizId());
//        if (!CollectionUtils.isEmpty(result.getData())) {
//            commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
//                    .targetIdList(result.getData())
//                    .build());
//        }
//        if (CollectionUtils.isEmpty(commissionRuleBindingList)) {
//            throw new BusinessException("绑定基本法数据不存在");
//        }

        Integer paymentTerm = 0;
        List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList = new ArrayList<>();
        Result<List<ApiAnnouncementCommissionRatioListResponse>> listResult = apiCommissionConditionService.getCommissionRatioList(request.getPolicyNo(),null);
        if (listResult != null && !CollectionUtils.isEmpty(listResult.getData())) {
            announcementRatioList = listResult.getData();
            //取 endPeriod 的最大值作为总期数
            paymentTerm = listResult.getData().stream()
                    .map(item -> {
                        try {
                            return Integer.parseInt(item.getEndPeriod());
                        } catch (NumberFormatException e) {
                            log.warn("endPeriod 格式错误: {}", item.getEndPeriod());
                            return 0;
                        }
                    })
                    .max(Integer::compareTo)
                    .orElse(0);
        }
        log.info("查询产品发佣配置，获取结算币种==========>，{}",JSON.toJSONString(announcementRatioList));
        //查询产品发佣配置，获取结算币种
//        QueryPolicyAndBrokerDto queryPolicyAndBrokerDto = queryPolicyAndBrokerDtoList.get(0);
//        ApiAnnouncementCommissionRatioListRequest announcementRatioListRequest = new ApiAnnouncementCommissionRatioListRequest();
//        announcementRatioListRequest.setProductLaunchBizId(queryPolicyAndBrokerDto.getPlanBizId());
//        announcementRatioListRequest.setEffectiveDate(Date.from(queryPolicyAndBrokerDto.getEffectiveDate().atStartOfDay(ZoneId.systemDefault()).toInstant()));
//        announcementRatioListRequest.setPaymentTerm(String.valueOf(queryPolicyAndBrokerDto.getPaymentTerm()));
//
//        HttpServletRequest request1 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        String token = request1.getHeader("Authorization");
//        log.info("HttpServletRequest token==============,{}",token);
//        List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList;
//        try {
//            announcementRatioList = feignResultHelper.extractListData(
//                    apiAnnouncementCommissionRatioFeignClient.list(announcementRatioListRequest),
//                    ApiAnnouncementCommissionRatioListResponse.class
//            );
//        } catch (Exception e) {
//            log.info("查询结算币种->保单号:{}，入参request：{}", request.getPolicyNo(), JSON.toJSONString(announcementRatioListRequest));
//            throw new BusinessException("未查询到产品发佣配置，查询条件：" + JSON.toJSONString(announcementRatioListRequest));
//        }



//        Integer paymentTerm = 0;
//        PolicyFollow policyFollow = policyFollowService.queryOneByPolicyNo(request.getPolicyNo());
//        if (policyFollow != null) {
//            ApiAnnouncementCommissionRatioListRequest ratioListRequest = new ApiAnnouncementCommissionRatioListRequest();
//            ratioListRequest.setEffectiveDate(policyFollow.getEffectiveDate());
//            ratioListRequest.setPaymentTerm(policyFollow.getIssueNumber().toString()); // 保留原有过滤条件（可根据业务决定是否需要）
//            ratioListRequest.setProductLaunchBizId(policyFollow.getProductLaunchBizId());
//            Result<List<ApiAnnouncementCommissionRatioListResponse>> listResult = ratioFeignClient.list(ratioListRequest);
//            if (listResult != null && !CollectionUtils.isEmpty(listResult.getData())) {
//                // 取 endPeriod 的最大值作为总期数
//                paymentTerm = listResult.getData().stream()
//                        .map(item -> {
//                            try {
//                                return Integer.parseInt(item.getEndPeriod()); // 将字符串转为整数
//                            } catch (NumberFormatException e) {
//                                // 若转换失败，记录日志并返回0，避免影响最大值计算
//                                log.warn("endPeriod 格式错误: {}", item.getEndPeriod());
//                                return 0;
//                            }
//                        })
//                        .max(Integer::compareTo)
//                        .orElse(0); // 若无有效值，默认0
//            }
//        }






        //遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表（同步执行）
        executeSync(queryPolicyAndBrokerDtoList, request.getPolicyNo(), announcementRatioList,paymentTerm);

        return Result.success(null, "生成预计发佣成功");
    }

    /**
     * 生成预计发佣（包含日志记录和Redis处理）
     * 用于内部服务调用，会自动保存日志和异常时销毁Redis缓存
     *
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<ApiGenerateExpectedFortuneResponse> generateWithLogAndRedis(ApiGenerateExpectedFortuneRequest request) {
        try {
            Result<ApiGenerateExpectedFortuneResponse> result = generate(request);
            // 保存预计发佣日志记录（成功）
            apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                    .errorMsg(result.getMsg())
                    .policyNo(request.getPolicyNo())
                    .status(2)
                    .build());
            return result;
        } catch (Exception e) {
            // 抛出异常，销毁redis缓存
            try {
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
            } catch (Exception redisEx) {
                log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
            }
            // 保存预计发佣日志记录（失败）
            try {
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(request.getPolicyNo())
                        .status(1)
                        .build());
            } catch (Exception logEx) {
                log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
            }
            // 重新抛出原始异常，确保上层事务能够回滚
            throw e;
        }
    }

    /**
     * 同步生成预计发佣（包含日志记录和Redis处理）
     * 用于需要同步等待完成的场景
     *
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<ApiGenerateExpectedFortuneResponse> generateSyncWithLogAndRedis(ApiGenerateExpectedFortuneRequest request) {
        try {
            Result<ApiGenerateExpectedFortuneResponse> result = generateSync(request);
            // 保存预计发佣日志记录（成功）
            apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                    .errorMsg(result.getMsg())
                    .policyNo(request.getPolicyNo())
                    .status(2)
                    .build());
            return result;
        } catch (Exception e) {
            // 抛出异常，销毁redis缓存
            try {
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + request.getPolicyNo());
            } catch (Exception redisEx) {
                log.error("删除Redis缓存失败: {}", redisEx.getMessage(), redisEx);
            }
            // 保存预计发佣日志记录（失败）
            try {
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(request.getPolicyNo())
                        .status(1)
                        .build());
            } catch (Exception logEx) {
                log.error("保存预计发佣日志失败: {}", logEx.getMessage(), logEx);
            }
            // 重新抛出原始异常，确保上层事务能够回滚
            throw e;
        }
    }

    /**
     * 异步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
     *
     * @param queryPolicyAndBrokerDtoList
     * @param commissionRuleBindingList
     * @return
     */
    @Override
    @Async("commonAsyncExecutor")
    @Transactional(rollbackFor = Exception.class)
    public Result execute(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
                          List<CommissionRuleBinding> commissionRuleBindingList,
                          String policyNo,
                          List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList) {

        // 使用编程式事务，确保异步方法内的事务一致性
        return transactionTemplate.execute(status -> {
            try {
                for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
                    Integer paymentTerm = brokerDto.getPaymentTerm();
                    if (Objects.isNull(paymentTerm)) {
                        throw new BusinessException("保单的供款年期不存在");
                    }

                    for (int i = 1; i <= paymentTerm; i++) {
                        executeBilling(ExecuteBillingDto.builder()
                                .name(brokerDto.getBrokerName())
                                .policyAndBrokerDto(brokerDto)
                                .issueNumber(i)
                                .build());

                        for (CommissionRuleBinding binding : commissionRuleBindingList) {
                            executeReward(ExecuteBillingDto.builder()
                                    .clientUserBizId(binding.getTargetId())
                                    .name(binding.getTargetName())
                                    .policyAndBrokerDto(brokerDto)
                                    .issueNumber(i)
                                    .build());
                        }
                    }
                }
                //批量设置应付款编号、发佣类型、默认结算汇率、港币金额
                String policyCurrency = queryPolicyAndBrokerDtoList.get(0).getCurrency();
                updatePayableNoAndHkdAmountBatch(policyNo, policyCurrency, announcementRatioList);

                //执行成功完毕，也要销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);

                //保存预计发佣日志记录
                //成功日志
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg("")
                        .policyNo(policyNo)
                        .status(0)
                        .build());
                return Result.success();
            } catch (Exception e) {
                //抛出异常，销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
                status.setRollbackOnly(); // 标记回滚
                log.error("异步执行预计发佣失败，已回滚所有操作", e);
                //保存预计发佣日志记录
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(policyNo)
                        .status(1)
                        .build());
                throw new BusinessException("异步处理失败: " + e.getMessage());
            }
        });
    }

    @Override
    public Result executeSync(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList, List<CommissionRuleBinding> commissionRuleBindingList, String policyNo) {
        return null;
    }

    /**
     * 同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表
     *
     * @param queryPolicyAndBrokerDtoList
     * @param policyNo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result executeSync(List<QueryPolicyAndBrokerDto> queryPolicyAndBrokerDtoList,
                              String policyNo, List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList, Integer paymentTerm) {
        log.info("同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表->入参queryPolicyAndBrokerDtoList：{}", JSON.toJSONString(queryPolicyAndBrokerDtoList));
        log.info("同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表->入参policyNo：{}", policyNo);

        HttpServletRequest request1 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = request1.getHeader("Authorization");
        log.info("同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表 token==============,{}", token);
        // 使用编程式事务，确保方法内的事务一致性
        return transactionTemplate.execute(status -> {
            try {
                HttpServletRequest request2 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                String token2 = request2.getHeader("Authorization");
                log.info("使用编程式事务，确保方法内的事务一致性 token==============,{}", token2);

                //保单币种->港币的汇率
                BigDecimal exchangeRate = BigDecimal.ZERO;
                if (!CollectionUtils.isEmpty(queryPolicyAndBrokerDtoList)) {
                    QueryPolicyAndBrokerDto queryPolicyAndBrokerDto = queryPolicyAndBrokerDtoList.get(0);
                    exchangeRate = queryExchangeRateByFeign(queryPolicyAndBrokerDto.getCurrency(), "HKD");
                }
                log.info("保单币种->港币的汇率=>,{}",exchangeRate);

                //基本法计算值收集列表
                List<AlgorithmCollectResDto> collectResDtos = new ArrayList<>();
                for (QueryPolicyAndBrokerDto brokerDto : queryPolicyAndBrokerDtoList) {
//                    Integer paymentTerm = brokerDto.getPaymentTerm();
                    if (paymentTerm == null || paymentTerm == 0) {
                        throw new BusinessException("保单的供款年期不存在");
                    }

                    // 冷静期结束日期 Date 转换为 LocalDateTime
                    LocalDateTime baseThawingTime = brokerDto.getCoolingOffEndDate()
                            .toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDateTime();

                    List<CommissionRuleBinding> commissionRuleBindingList = brokerRelUserIdList(brokerDto);
                    log.info("同步处理-> 遍历保单转介人列表信息 -> 调用基本法算出预计发佣列表->commissionRuleBindingList：{}", JSON.toJSONString(commissionRuleBindingList));
                    for (int i = 1; i <= paymentTerm; i++) {
                        Result<List<AlgorithmResDto>> result = executeBilling(ExecuteBillingDto.builder()
                                .name(brokerDto.getBrokerName())
                                .policyAndBrokerDto(brokerDto)
                                .issueNumber(i)
                                .paymentTerm(paymentTerm)
                                .brokerRatio(brokerDto.getBrokerRatio())
                                .build());
                        //收集计算销售佣金的转介人
                        AlgorithmCollectResDto resDto = new AlgorithmCollectResDto();
                        resDto.setClientUserBizId(brokerDto.getBrokerBizId());
                        resDto.setClientUserName(brokerDto.getBrokerName());
                        //发佣期数
                        resDto.setFortunePeriod(i);
                        // 根据期数增加年份（第一年 +0，第二年 +1，以此类推）
                        LocalDateTime actualThawingTime = baseThawingTime.plusYears(resDto.getFortunePeriod() - 1);
                        //积分解冻时间（受发佣期数影响，第一年就是冷静期结束日期，第二年冷静期结束日期+1年，以此类推）
                        resDto.setThawingTime(actualThawingTime);
                        //介绍费占比
                        resDto.setBrokerRatio(brokerDto.getBrokerRatio());
                        resDto.setAlgorithmResDtoList(result.getData());
                        collectResDtos.add(resDto);

                        //续年度只发销售佣金，其他奖项均不发放
                        if (!CollectionUtils.isEmpty(commissionRuleBindingList) && i == 1) {
                            for (CommissionRuleBinding binding : commissionRuleBindingList) {
                                Result<List<AlgorithmResDto>> result1 = executeReward(ExecuteBillingDto.builder()
                                        .clientUserBizId(binding.getTargetId())
                                        .name(binding.getTargetName())
                                        .policyAndBrokerDto(brokerDto)
                                        .issueNumber(i)
                                        .paymentTerm(paymentTerm)
                                        .brokerRatio(brokerDto.getBrokerRatio())
                                        .build());
                                //收集计算非销售佣金的转介人
                                AlgorithmCollectResDto resDto1 = new AlgorithmCollectResDto();
                                resDto1.setClientUserBizId(binding.getTargetId());
                                resDto1.setClientUserName(binding.getTargetName());
                                //发佣期数
                                resDto1.setFortunePeriod(i);
                                //积分解冻时间（受发佣期数影响，第一年就是冷静期结束日期，第二年冷静期结束日期+1年，以此类推）
                                resDto1.setThawingTime(actualThawingTime);
                                resDto1.setAlgorithmResDtoList(result1.getData());
                                //介绍费占比
                                resDto1.setBrokerRatio(brokerDto.getBrokerRatio());
                                collectResDtos.add(resDto1);

                            }
                        }
                    }
                }

                //批量设置应付款编号、默认结算汇率、港币金额
                String policyCurrency = queryPolicyAndBrokerDtoList.get(0).getCurrency();
                updatePayableNoAndHkdAmountBatch(policyNo, policyCurrency, announcementRatioList);

                //执行成功完毕，也要销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);

                //保存预计发佣日志记录
                //成功日志
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg("")
                        .policyNo(policyNo)
                        .status(0)
                        .build());
                return Result.success();
            } catch (Exception e) {
                //抛出异常，销毁redis缓存
                redisUtil.deleteObject(RedisConstants.EXPECTED_FORTUNE + policyNo);
                status.setRollbackOnly(); // 标记回滚
                log.error("同步执行预计发佣失败，已回滚所有操作", e);
                //保存预计发佣日志记录
                apiExpectedFortuneLogService.saveExpectedFortuneLog(ApiExpectedFortuneLogDto.builder()
                        .errorMsg(e.getMessage())
                        .policyNo(policyNo)
                        .status(1)
                        .build());
                throw new BusinessException("同步处理失败: " + e.getMessage());
            }
        });
    }

    /**
     * 查询和当前转介人相关关系（推荐，一级管理，二级管理，辅导）的客户端用户ID列表数据
     *
     * @param brokerDto
     * @return
     */
    public List<CommissionRuleBinding> brokerRelUserIdList(QueryPolicyAndBrokerDto brokerDto) {
        List<CommissionRuleBinding> commissionRuleBindingList = new ArrayList<>();
        List<String> userIdList = policyBrokerService.selectRelatedBrokerBizIds(brokerDto.getBrokerBizId());
        if (!CollectionUtils.isEmpty(userIdList)) {
            commissionRuleBindingList = iCommissionRuleBindingService.queryList(CommissionRuleBindingDto.builder()
                    .targetIdList(userIdList)
                    .build());
        }
        return commissionRuleBindingList;
    }

    /**
     * 批量更新应付款编号、发佣类型、默认结算汇率、港币金额
     */
    private void updatePayableNoAndHkdAmountBatch(String policyNo, String policyCurrency,
                                                   List<ApiAnnouncementCommissionRatioListResponse> announcementRatioList) {
        //查询当前保单号的所有预计发佣记录
        List<ExpectedFortune> expectedFortuneList = iExpectedFortuneService.queryList(policyNo);

        //查询redis缓存的字典列表信息
        List<GetDictItemListByDictTypeResponse> dictTypeResponses = dictCacheService.getDictList("csf_fortune_type");

        log.info("------------------------开始计算默认结算汇率、港币金额----------------------");

        //计算汇率（只在announcementRatioList非空时）
        String currency = null;
        BigDecimal originalToHkdRate = null;
        BigDecimal hkdToPayoutRate = null;
        if (!CollectionUtils.isEmpty(announcementRatioList)) {
            ApiAnnouncementCommissionRatioListResponse announcementRatio = announcementRatioList.get(0);
            currency = announcementRatio.getCurrency();
            log.info("结算币种（发放币种）：{}", currency);

            originalToHkdRate = queryExchangeRateByFeign(policyCurrency, "HKD");
            log.info("结算汇率（原币种 -> 港币汇率）：{}", originalToHkdRate);

            hkdToPayoutRate = queryExchangeRateByFeign("HKD", currency);
            log.info("港币 -> 发放币种汇率：{}", hkdToPayoutRate);
        }

        List<ExpectedFortune> updateList = new ArrayList<>();
        for (ExpectedFortune expectedFortune : expectedFortuneList) {
            ExpectedFortune updateObj = new ExpectedFortune();
            updateObj.setId(expectedFortune.getId());

            //设置发佣类型
            updateObj.setFortuneType(GetDictItemListByDictTypeResponse.getItemValue(dictTypeResponses,
                    "csf_fortune_type", expectedFortune.getFortuneName()));
            //设置应付款编号
            updateObj.setPayableNo(iExpectedFortuneService.getPayableNo(updateObj.getFortuneType(), policyNo, expectedFortune.getId()));

            //设置默认结算汇率、港币金额
            if (!CollectionUtils.isEmpty(announcementRatioList)) {
                //原币种
                updateObj.setOriginalCurrency(policyCurrency);
                //原币种金额
                BigDecimal ruleAmount = expectedFortune.getRuleAmount();
                updateObj.setOriginalAmount(ruleAmount);
                //原币种 -> 港币汇率
                updateObj.setOriginalToHkdRate(originalToHkdRate);
                //保单币种
                updateObj.setRuleCurrency(policyCurrency);
                //默认保单币种 -> 港币汇率
                updateObj.setDefaultExchangeRate(originalToHkdRate);
                //应发港币金额（估）（标准发佣金额 * 默认保单币种 -> 港币汇率）
                BigDecimal hkdAmount = ruleAmount.multiply(originalToHkdRate);
                updateObj.setHkdAmount(hkdAmount);
                //发放币种
                updateObj.setPayoutCurrency(currency);
                //发放币种金额
                updateObj.setPayoutAmount(hkdAmount.multiply(hkdToPayoutRate));
                //港币 -> 发放币种汇率
                updateObj.setHkdToPayoutRate(hkdToPayoutRate);
                //默认值
                updateObj.setPaidRuleAmount(BigDecimal.ZERO);
                updateObj.setUnpaidRuleAmount(ruleAmount);
                updateObj.setUnpaidRuleAmountHkd(hkdAmount);
                updateObj.setPaidAmount(BigDecimal.ZERO);
                updateObj.setPaidRatio(BigDecimal.ZERO);
                updateObj.setUnpaidAmount(expectedFortune.getHkdAmount());
                updateObj.setUnpaidRatio(BigDecimal.valueOf(100));
            }

            updateList.add(updateObj);
        }
        iExpectedFortuneService.updateBatchById(updateList);
    }

    /**
     * 执行 - 奖励（非销售佣金基本法）
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result executeReward(ExecuteBillingDto dto) {
        QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
        //构造销售佣金基本法项目的顺序下标值执行
        List<Integer> executionOrderList = new ArrayList<>();
        executionOrderList.add(1);
        log.info("执行获得积分用户的非销售佣金基本法项目->入参：{}", JSON.toJSONString(dto));
        //执行获得积分用户的非销售佣金基本法项目
        Result<List<AlgorithmResDto>> result = apiBasicLawCalculateService.policyBrokerAlgorithm(AlgorithmDto.builder()
                .brokerBizId(brokerDto.getBrokerBizId())
                .clientUserBizId(dto.getClientUserBizId())
                .sqlTemplateParamDto(SqlTemplateParamDto.builder()
                        .brokerBizId(brokerDto.getBrokerBizId())
                        .clientUserBizId(dto.getClientUserBizId())
                        .policyNo(brokerDto.getPolicyNo())
                        .productBizId(brokerDto.getPlanBizId())
                        .productCode(brokerDto.getProductCode())
                        .term(brokerDto.getPaymentTerm())
                        .issueNumber(dto.getIssueNumber())
                        .build())
                .executionOrderList(executionOrderList)
                .isNegateExecutionOrderList(true)
                .build());
        log.info("执行获得积分用户的非销售佣金基本法项目->出参：{}", JSON.toJSONString(result));

        //生成保单预计发佣表记录 (非销售佣金基本法)
        generateExpectedFortune(GenerateExpectedFortuneDto.builder()
                //获得积分的用户绑定的基本法列表对应计算值
                .algorithmResDtoList(result.getData())
                //保单发佣批次ID
                //获得积分的用户姓名
                .broker(dto.getName())
                //获得积分的用户业务ID
                .brokerBizId(dto.getClientUserBizId())
                //发佣币种
                .currency(brokerDto.getCurrency())
                //发佣期数
                .fortunePeriod(dto.getIssueNumber())
                //发佣总期数
//                .fortuneTotalPeriod(brokerDto.getPaymentTerm())
                .fortuneTotalPeriod(dto.getPaymentTerm())
                //发佣类型 TODO
//                .fortuneType()
                //发佣日期 保单生效日期 + 3个月
                .payoutDate(calculateExpectedPayoutDate(brokerDto.getEffectiveDate(), dto.getIssueNumber()))
                //保单号
                .policyNo(brokerDto.getPolicyNo())
                //佣金发放状态 0=待出账 1=可出账 2=已出账
                .status(FortuneStatusEnum.WAIT.getItemValue())
                //期交保费
                .paymentPremium(brokerDto.getPaymentPremium())
                //所属团队
                .team(brokerDto.getTeam())
                //所属团队业务ID
                .teamBizId(brokerDto.getTeamBizId())
                //产品业务id
                .productLaunchBizId(brokerDto.getPlanBizId())
                //保险公司业务id
                .insuranceCompanyBizId(brokerDto.getInsuranceCompanyBizId())
                .brokerRatio(dto.getBrokerRatio())
                .source(2)
                .amountSourceId(brokerDto.getBrokerBizId())
                .amountSourceName(brokerDto.getBrokerName())
                .build());

        return Result.success();
    }

    private static LocalDate calculateExpectedPayoutDate(LocalDate effectiveDate, Integer issueNumber) {
        return effectiveDate.plusMonths(2).plusYears(issueNumber - 1);
    }

    /**
     * 执行 - 销售佣金（销售佣金基本法）
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result executeBilling(ExecuteBillingDto dto) {
        log.info("执行 - 销售佣金（销售佣金基本法）——>入参：{}", JSON.toJSONString(dto));
        QueryPolicyAndBrokerDto brokerDto = dto.getPolicyAndBrokerDto();
        //构造销售佣金基本法项目的顺序下标值执行
        List<Integer> executionOrderList = new ArrayList<>();
        executionOrderList.add(1);

        //保单出账先执行销售佣金基本法项目，入完到积分明细表里后，再执行保单绑定人的其他基本法项目
        Result<List<AlgorithmResDto>> result = apiBasicLawCalculateService.policyBrokerAlgorithm(AlgorithmDto.builder()
                .issueNumber(dto.getIssueNumber())
                .brokerBizId(brokerDto.getBrokerBizId())
                .clientUserBizId(brokerDto.getBrokerBizId())
                .sqlTemplateParamDto(SqlTemplateParamDto.builder()
                        .brokerBizId(brokerDto.getBrokerBizId())
                        .policyNo(brokerDto.getPolicyNo())
                        .productBizId(brokerDto.getPlanBizId())
                        .productCode(brokerDto.getProductCode())
                        .term(brokerDto.getPaymentTerm())
                        .issueNumber(dto.getIssueNumber())
                        .build())
                .executionOrderList(executionOrderList)
                .isNegateExecutionOrderList(false)
                .build());

        log.info("执行 - 销售佣金（销售佣金基本法）——>brokerDto：{}", JSON.toJSONString(brokerDto));
        log.info("执行 - 销售佣金（销售佣金基本法）——>policyBrokerAlgorithm——>result：{}", JSON.toJSONString(result));
        //生成保单预计发佣表记录 (销售佣金基本法)
        generateExpectedFortune(GenerateExpectedFortuneDto.builder()
                //转介人绑定的基本法列表对应计算值
                .algorithmResDtoList(result.getData())
                //保单发佣批次ID
                //获得积分的转介人（业务员）姓名
                .broker(dto.getName())
                //获得积分的转介人（业务员）业务ID
                .brokerBizId(brokerDto.getBrokerBizId())
                //发佣币种
                .currency(brokerDto.getCurrency())
                //发佣期数
                .fortunePeriod(dto.getIssueNumber())
                //发佣总期数
//                .fortuneTotalPeriod(brokerDto.getPaymentTerm())
                .fortuneTotalPeriod(dto.getPaymentTerm())
                //发佣类型 TODO
//                .fortuneType()
                //发佣日期
                .payoutDate(calculateExpectedPayoutDate(brokerDto.getEffectiveDate(), dto.getIssueNumber()))
                //保单号
                .policyNo(brokerDto.getPolicyNo())
                //佣金发放状态 0=待出账 1=可出账 2=已出账
                .status(FortuneStatusEnum.WAIT.getItemValue())
                //期交保费
                .paymentPremium(brokerDto.getPaymentPremium())
                //所属团队
                .team(brokerDto.getTeam())
                //所属团队业务ID
                .teamBizId(brokerDto.getTeamBizId())
                //产品业务id
                .productLaunchBizId(brokerDto.getPlanBizId())
                //保险公司业务id
                .insuranceCompanyBizId(brokerDto.getInsuranceCompanyBizId())
                .brokerRatio(dto.getBrokerRatio())
                .source(1)
                .build());

        return Result.success();
    }

    /**
     * 生成保单预计发佣表记录
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result generateExpectedFortune(GenerateExpectedFortuneDto fortuneDto) {
        log.info("生成保单预计发佣表记录——>入参：{}", JSON.toJSONString(fortuneDto));
        List<ExpectedFortune> fortuneList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fortuneDto.getAlgorithmResDtoList())) {
            //获得积分业务员绑定的基本法列表对应计算值结果集合
            for (AlgorithmResDto algorithmResDto : fortuneDto.getAlgorithmResDtoList()) {
                if (fortuneDto.getSource() == 2 && (Objects.isNull(algorithmResDto.getCalculatedValue())
                        || algorithmResDto.getCalculatedValue().compareTo(BigDecimal.ZERO) == 0)) {
                    //2-非销售佣金并且计算金额为0，走这里
                    //判空判0
                    continue;
                }
                log.info("生成保单预计发佣表记录——>入参algorithmResDto：{}", JSON.toJSONString(algorithmResDto));
//                PolicyBroker policyBroker = policyBrokerService.queryOne(QueryPolicyBrokerDto.builder()
//                        .policyNo(fortuneDto.getPolicyNo())
//                        .brokerBizId(fortuneDto.getBrokerBizId())
//                        .build());
                ExpectedFortune fortune = new ExpectedFortune();
                //发佣金额 -> 计算值 - sqlAlgorithmResultDtoList集合里面的计算值和值
//                fortune.setAmount(algorithmResDto.getCalculatedValue());
                //保单发佣批次ID
//                fortune.setBatchBizId(fortuneDto.getBatchBizId());
                fortune.setFortuneBizType("R");
                fortune.setIsPart(0);
                fortune.setPolicyCurrency(fortuneDto.getCurrency());
                fortune.setPremium(fortuneDto.getPaymentPremium());
                //转介人姓名 (获得积分的业务员)
                fortune.setBroker(fortuneDto.getBroker());
                //转介人业务ID (获得积分的业务员)
                fortune.setBrokerBizId(fortuneDto.getBrokerBizId());
                //所属团队
                fortune.setTeam(fortuneDto.getTeam());
                //所属团队业务ID
                fortune.setTeamBizId(fortuneDto.getTeamBizId());
                //产品业务id
                fortune.setProductLaunchBizId(fortuneDto.getProductLaunchBizId());
                //保险公司业务id
                fortune.setInsuranceCompanyBizId(fortuneDto.getInsuranceCompanyBizId());
                //出账项目名称 -> 基本法项目名称
                fortune.setFortuneName(algorithmResDto.getItemName());
                //保单预计发佣业务id
                fortune.setExpectedFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_EXPECTED_FORTUNE.getCode()));
                //基本法项目配置表唯一业务ID
                fortune.setRuleItemBizId(algorithmResDto.getRuleItemBizId());
                //保单币种
                fortune.setPolicyCurrency(fortuneDto.getCurrency());
                //发佣期数
                fortune.setFortunePeriod(fortuneDto.getFortunePeriod());
                //发佣总期数
                fortune.setFortuneTotalPeriod(fortuneDto.getFortuneTotalPeriod());
                //保单号
                fortune.setPolicyNo(fortuneDto.getPolicyNo());
                //预计发佣日期
                fortune.setPayoutDate(fortuneDto.getPayoutDate());
                fortune.setAmountSourceId(fortuneDto.getAmountSourceId());
                fortune.setAmountSourceName(fortuneDto.getAmountSourceName());

                // 标准发佣金额 -> 计算值
                BigDecimal standardAmount = algorithmResDto.getCalculatedValue();
                fortune.setStandardAmount(standardAmount);

                // 转介人介绍费占比
                String brokerRatioStr = StringUtils.isNotBlank(fortuneDto.getBrokerRatio()) ? fortuneDto.getBrokerRatio() : "100";
                fortune.setBrokerRatio(brokerRatioStr);

                try {
                    // 将百分比字符串转换为小数
                    BigDecimal ratio = new BigDecimal(brokerRatioStr);
                    // 除以100
                    BigDecimal decimalRatio = ratio.divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);

                    // 计算实际预计发佣金额 = 标准发佣金额 × decimalRatio
                    BigDecimal actualAmount = standardAmount.multiply(decimalRatio);
                    fortune.setRuleAmount(actualAmount);

                } catch (NumberFormatException e) {
                    // 如果比例不是有效的数字，使用标准金额
                    fortune.setRuleAmount(standardAmount);
                } catch (Exception e) {
                    // 其他异常处理
                    log.error("计算发佣金额异常: {}", brokerRatioStr, e);
                    fortune.setRuleAmount(standardAmount);
                }

                // 创建人
                fortune.setCreatorName("系统生成");

                fortuneList.add(fortune);
            }
        }

        log.info("生成保单预计发佣表记录——>出参fortuneList：{}", JSON.toJSONString(fortuneList));
        if (!CollectionUtils.isEmpty(fortuneList)) {
            iExpectedFortuneService.saveOrUpdateBatch(fortuneList);
        }

        return Result.success();
    }

    @Override
    public Result<ApiExpectedFortunePageResponseVO> list(ApiExpectedFortunePageRequest request) {
        // 1. 参数校验
        if (request == null) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "查询参数不能为空");
        }
        if (request.getPageNo() == null || request.getPageNo() < 1) {
            request.setPageNo(1);
        }
        if (request.getPageSize() == null || request.getPageSize() < 1) {
            request.setPageSize(10);
        }

        long startTime = System.currentTimeMillis();

        try {
            // 2. 构建查询条件
            QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);

            // 移除 QueryWrapper 中的排序，避免在 UNION ALL 的子查询中出现 ORDER BY
            queryWrapper.getExpression().getOrderBy().clear();

            // 3. 查询分页列表（UNION ALL 查询，包含预计发佣和实际发佣）
            IPage<ApiExpectedFortunePageResponse> iPage = iExpectedFortuneService.queryListNew(
                    request.getPageNo(),
                    request.getPageSize(),
                    queryWrapper
            );

            // 4. 查询统计数据（使用 SQL 聚合，不加载明细）
            ExpectedFortuneStatisticsVO statisticsVO = iExpectedFortuneService.queryListStatistics(queryWrapper);

            // 计算已出账比例
            BigDecimal totalAmount = statisticsVO.getTotalExpectedAmount();
            BigDecimal totalPaidAmount = statisticsVO.getTotalPaidAmount();
            BigDecimal divided = BigDecimal.ZERO;
            if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
                divided = totalPaidAmount.divide(totalAmount, 4, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100));
            }
            statisticsVO.setPaidAmountRatio(divided);

            // 5. 组装返回结果
            ApiExpectedFortunePageResponseVO response = new ApiExpectedFortunePageResponseVO();
            response.setStatisticsVO(statisticsVO);
            response.setPage(iExpectedFortuneService.getVOPage(iPage));  // 字段填充、调整数据顺序

            log.info("查询应付款管理列表完成, 耗时: {}ms, 页码: {}, 页大小: {}, 总记录数: {}",
                    System.currentTimeMillis() - startTime,
                    request.getPageNo(),
                    request.getPageSize(),
                    iPage.getTotal());

            return Result.success(response);
        } catch (Exception e) {
            log.error("查询应付款管理列表失败", e);
            return Result.fail(ResultCode.FAIL.getCode(), "查询失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(List<ExpectedFortuneAddRequest> fortuneAddRequestList) {
        if (CollectionUtils.isEmpty(fortuneAddRequestList)) {
            return true;
        }
        // 校验参数
        validateAdd(fortuneAddRequestList);

        // 根据保单号、期数、项目类型查询已存在的记录
        validIsExist(fortuneAddRequestList);

        // 查询保单信息
        Set<String> policyNoSet = fortuneAddRequestList.stream()
                .map(ExpectedFortuneAddRequest::getPolicyNo)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());

        Map<String, Policy> policyMap = policyService.queryPolicyMap(policyNoSet);

        // 当前登录用户
        AuthUserDto authUserDto = SecurityUtil.getCurrentLoginUser();
        // 当前时间
        LocalDateTime now = LocalDateTime.now();

        // 查询最新一条有 payableNo 记录
//        int currentSeq = iExpectedFortuneService.getPayableNoCurrentSeq();

        List<ExpectedFortune> fortuneList = new ArrayList<>();
        for (ExpectedFortuneAddRequest expectedFortuneDto : fortuneAddRequestList) {
            ExpectedFortune expectedFortune = new ExpectedFortune();
            BeanUtil.copyProperties(expectedFortuneDto, expectedFortune, "payoutDate", "actualPayoutDate");

            // 设置创建人、更新人
            expectedFortune.setCreatorId(authUserDto.getUserBizId());
            expectedFortune.setCreatorName(authUserDto.getRealName());
            expectedFortune.setUpdaterId(authUserDto.getUserBizId());
            expectedFortune.setCreateTime(now);
            expectedFortune.setUpdateTime(now);
            // 是否拆分
            expectedFortune.setIsPart(0);

            if ("R".equals(expectedFortuneDto.getFortuneBizType())) {
                Policy policy = policyMap.get(expectedFortuneDto.getPolicyNo());
                if (ObjectUtils.isEmpty(policy)) {
                    throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "保单号为" + expectedFortuneDto.getPolicyNo() + "的保单不存在");
                }

                // 设置关联字段
                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(queryByDict(expectedFortuneDto.getFortuneType()));
            // 预计出账年月
            if (StringUtils.isNotBlank(expectedFortuneDto.getPayoutDate())) {
                expectedFortune.setPayoutDate(LocalDate.parse(expectedFortuneDto.getPayoutDate() + "-01"));
            }
            // 实际出账年月
            if (StringUtils.isNotBlank(expectedFortuneDto.getActualPayoutDate())) {
                expectedFortune.setActualPayoutDate(LocalDate.parse(expectedFortuneDto.getActualPayoutDate() + "-01"));
            }
            // 转介人比例默认100%
            expectedFortune.setBrokerRatio("100");

            // 已出帐金额、待出帐金额、已出帐比例、待出帐比例
            expectedFortune.setPaidAmount(BigDecimal.ZERO);
            expectedFortune.setUnpaidAmount(expectedFortune.getHkdAmount());
            expectedFortune.setPaidRatio(BigDecimal.ZERO);
            expectedFortune.setUnpaidRatio(BigDecimal.valueOf(100));
            fortuneList.add(expectedFortune);
        }
        iExpectedFortuneService.saveOrUpdateBatch(fortuneList);

        // 更新应付款编号
        List<ExpectedFortune> updateList = new ArrayList<>();
        for (ExpectedFortune expected : fortuneList) {
            // 生成应付款编号
            String payableNo = iExpectedFortuneService.getPayableNo(expected.getFortuneType(), expected.getPolicyNo(), expected.getId());

            ExpectedFortune updateExpected = new ExpectedFortune();
            updateExpected.setId(expected.getId());
            updateExpected.setPayableNo(payableNo);
            updateList.add(updateExpected);
        }
        iExpectedFortuneService.updateBatchById(updateList);

        return true;
    }

    private void validIsExist(List<ExpectedFortuneAddRequest> fortuneAddRequestList) {

        Set<String> policyNoSet = new HashSet<>();
        Set<Integer> fortunePeriodSet = new HashSet<>();
        Set<String> fortuneBizTypeSet = new HashSet<>();
        for (ExpectedFortuneAddRequest request : fortuneAddRequestList) {
            policyNoSet.add(request.getPolicyNo());
            fortunePeriodSet.add(request.getFortunePeriod());
            fortuneBizTypeSet.add(request.getFortuneBizType());
        }

        List<ExpectedFortune> existingRecords = iExpectedFortuneService.lambdaQuery()
                .in(ExpectedFortune::getPolicyNo, policyNoSet)
                .in(ExpectedFortune::getFortunePeriod, fortunePeriodSet)
                .in(ExpectedFortune::getFortuneBizType, fortuneBizTypeSet)
                .list();

        // 转换为 Map
        Map<String, ExpectedFortune> existingRecordsMap = new HashMap<>();
        for (ExpectedFortune item : existingRecords) {
            String key = String.format("%s_%s_%s", item.getPolicyNo(), item.getFortunePeriod(), item.getFortuneBizType());
            existingRecordsMap.put(key, item);
        }

        // 校验是否存在
        for (ExpectedFortuneAddRequest request : fortuneAddRequestList) {
            String key = String.format("%s_%s_%s", request.getPolicyNo(), request.getFortunePeriod(), request.getFortuneType());
            if (existingRecordsMap.containsKey(key)) {
                throw new BusinessException(ResultCode.PARAM_CHECK_ERROR.getCode(), "当前期数已存在: " + request.getFortuneName());
            }
        }
    }

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

    /**
     * 查询币对的默认汇率
     *
     * @param sourceCurrency   源币种
     * @param targetCurrency   目标币种
     * @return 汇率
     */
    private BigDecimal queryExchangeRateByFeign(String sourceCurrency, String targetCurrency) {
        HttpServletRequest request2 = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token2 = request2.getHeader("Authorization");
        log.info("查询币对的默认汇率 token==============,{}", token2);
        if (sourceCurrency.equalsIgnoreCase(targetCurrency)) {
            return BigDecimal.ONE;
        }
        // 调用Feign客户端查询汇率
        Result<BigDecimal> result = apiExchangeRateFeignClient.getExchangeRate(sourceCurrency, targetCurrency, "");
        if (result != null && result.getData() != null) {
            return result.getData();
        } else {
            throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "查询币对的默认汇率失败, 币种：" + sourceCurrency + " -> " + targetCurrency);
        }
    }

    private void validateAdd(List<ExpectedFortuneAddRequest> fortuneAddRequestList) {
        for (ExpectedFortuneAddRequest expectedFortuneDto : fortuneAddRequestList) {
            if (Objects.isNull(expectedFortuneDto.getOriginalAmount())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "原币种金额不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getOriginalCurrency())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "原币种不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getOriginalToHkdRate())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "原币种→港币汇率不能为空");
            }

            if (Objects.isNull(expectedFortuneDto.getRuleAmount())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "保单币种金额不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getRuleCurrency())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "保单币种不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getDefaultExchangeRate())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "预计出账汇率不能为空");
            }
            if (Objects.isNull(expectedFortuneDto.getHkdAmount())) {
                throw new BusinessException(ResultCode.PARAMS_ERROR.getCode(), "预计出账金额不能为空");
            }

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exportExpectedFortune(ApiExpectedFortunePageRequest request, HttpServletResponse response) {
        // 查询数据（复用 /list 的查询逻辑，UNION ALL 双表 + processData 排序 + 枚举补充）
        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);
        // 移除 QueryWrapper 中的排序，避免在 UNION ALL 的子查询中出现 ORDER BY
        queryWrapper.getExpression().getOrderBy().clear();
        List<ApiExpectedFortunePageResponse> expectedFortuneVOList = iExpectedFortuneService.queryListAll(queryWrapper);
        // 转换为导出DTO
        List<ExpectedFortuneExportDTO> exportDataList = expectedFortuneVOList.stream()
                .map(ExpectedFortuneExportDTO::convertToExportDTO)
                .collect(Collectors.toList());

        // 使用EasyExcel导出
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExpectedFortuneExportDTO.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("应收款数据").build();
            excelWriter.write(exportDataList, writeSheet);
        } catch (Exception e) {
            throw new RuntimeException("导出应收款数据失败", e);
        }
    }

    @Override
    public Result<PayableReportResponse> payableReport(ApiExpectedFortunePageRequest request) {
        // 1. 参数校验
        if (request == null) {
            return Result.fail(ResultCode.PARAMS_ERROR.getCode(), "查询参数不能为空");
        }
        if (request.getPageNo() == null || request.getPageNo() < 1) {
            request.setPageNo(1);
        }
        if (request.getPageSize() == null || request.getPageSize() < 1) {
            request.setPageSize(10);
        }

        // 2. 构建查询条件（复用 getQueryWrapper，与 /list 共用同一套条件）
        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);

        // 3. 移除 QueryWrapper 中的排序，避免在 UNION ALL 的子查询中出现 ORDER BY
        queryWrapper.getExpression().getOrderBy().clear();

        // 4. 查询统计数据（直接传递 QueryWrapper，在 SQL 中聚合计算，不再加载明细到内存）
        ExpectedFortuneStatisticsVO statisticsVO = iExpectedFortuneService.queryListStatistics(queryWrapper);

        // 计算待出账金额和已出账比例
        BigDecimal totalAmount = statisticsVO.getTotalExpectedAmount();
        BigDecimal totalPaidAmount = statisticsVO.getTotalPaidAmount();
        statisticsVO.setTotalUnpaidAmount(totalAmount.subtract(totalPaidAmount));
        BigDecimal divided = BigDecimal.ZERO;
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            divided = totalPaidAmount.divide(totalAmount, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        }
        statisticsVO.setPaidAmountRatio(divided);

        // 5. 应付款报表分页查询（按保单号和期数维度统计，直接传递 QueryWrapper）
        Page<PayableReportVO> reportPage = new Page<>(request.getPageNo(), request.getPageSize());
        IPage<PayableReportVO> payableReportPage = iExpectedFortuneService.payableReportPage(reportPage, queryWrapper);

        // 6. 数据补充（转介人等级、保单持有人、产品名称等信息）
        payableReportPage = convertPayableReportVO(payableReportPage);

        // 7. 组装返回结果
        PayableReportResponse response = new PayableReportResponse();
        response.setStatisticsVO(statisticsVO);
        response.setPage(payableReportPage);
        return Result.success(response);
    }

    public IPage<PayableReportVO> convertPayableReportVO(IPage<PayableReportVO> payableReportPage) {
        if (!CollectionUtils.isEmpty(payableReportPage.getRecords())) {
            // 查询转介人等级
            List<String> brokerBizIdList = payableReportPage.getRecords().stream().map(PayableReportVO::getBrokerBizId).collect(Collectors.toList());
            Map<String, UserGradeDto> userGradeMap = iAgentAccumulatedFycService.queryUserGradeMap(brokerBizIdList);

            // 查询新单跟进
            List<String> policyNoList = payableReportPage.getRecords().stream().map(PayableReportVO::getPolicyNo).collect(Collectors.toList());
            Map<String, PolicyFollow> policyFollowMap = policyFollowService.queryPolicyFollowMap(policyNoList);

            List<PayableReportVO> voList = payableReportPage.getRecords().stream().map(vo -> {
                PolicyFollow policyFollow = policyFollowMap.get(vo.getPolicyNo());
                UserGradeDto userGradeDto = userGradeMap.get(vo.getBrokerBizId());

                vo.setCurrency(CurrencyEnum.toLabel(vo.getCurrency()));
                if (userGradeDto != null) {
                    vo.setBrokerGradeName(userGradeDto.getGradeName());
                }
                if (policyFollow != null) {
                    vo.setProductName(policyFollow.getProductName());
                    vo.setProductLaunchBizId(policyFollow.getProductLaunchBizId());
                    vo.setPolicyHolder(policyFollow.getPolicyHolder());
                    vo.setPolicyHolderEn(policyFollow.getPolicyHolderEn());
                }
                return vo;
            }).collect(Collectors.toList());
            payableReportPage.setRecords(voList);
        }
        return payableReportPage;
    }

    /**
     * 分页查询 - 预计发佣
     *
     * @param request
     * @return
     */
    @Override
    public Result<IPage<ApiExpectedFortunePageResponse>> page(ApiExpectedFortunePageRequest request) {
        Page<ExpectedFortune> page = new Page<>(request.getPageNo(), request.getPageSize());

        QueryWrapper<ExpectedFortune> queryWrapper = this.getQueryWrapper(request);
        IPage<ExpectedFortune> iPage = iExpectedFortuneService.page(page, queryWrapper);

        return Result.success(iPage.convert(ApiExpectedFortuneServiceImpl::transform));
    }

    public static ApiExpectedFortunePageResponse transform(ExpectedFortune expectedFortune) {
        ApiExpectedFortunePageResponse response = new ApiExpectedFortunePageResponse();
        BeanUtil.copyProperties(expectedFortune, response);
        return response;
    }

    @Override
    public QueryWrapper<ExpectedFortune> getQueryWrapper(ApiExpectedFortunePageRequest request) {
        QueryWrapper<ExpectedFortune> queryWrapper = new QueryWrapper<>();
        if (request == null) {
            return queryWrapper;
        }

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

        // 获取参数
        String payableNo = request.getPayableNo();
        String policyNo = request.getPolicyNo();
        LocalDate payoutDateStart = request.getPayoutDateStart();
        LocalDate payoutDateEnd = request.getPayoutDateEnd();
        List<String> statusList = request.getStatusList();
        Integer fortunePeriod = request.getFortunePeriod();
        String fortuneType = request.getFortuneType();
        String team = request.getTeam();
        List<String> teamBizIdList = request.getTeamBizIdList();
        List<String> insuranceCompanyBizIdList = request.getInsuranceCompanyBizIdList();
        List<String> productLaunchBizIdList = request.getProductLaunchBizIdList();
        List<String> brokerBizIdList = request.getBrokerBizIdList();
        List<String> signerBizIdList = request.getSignerBizIdList();
        String fortuneBizType = request.getFortuneBizType();
        String policyHolder = request.getPolicyHolder();

        // 按保单号模糊查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(payableNo), "payable_no", payableNo);
        queryWrapper.like(StringUtils.isNotBlank(policyNo), "policy_no", policyNo);
        queryWrapper.ge(ObjectUtils.isNotEmpty(payoutDateStart), "payout_date", payoutDateStart);
        queryWrapper.le(ObjectUtils.isNotEmpty(payoutDateEnd), "payout_date", payoutDateEnd);
        queryWrapper.in(ObjectUtils.isNotEmpty(statusList), "status", statusList);
        queryWrapper.eq(ObjectUtils.isNotEmpty(fortunePeriod), "fortune_period", fortunePeriod);
        queryWrapper.eq(StringUtils.isNotBlank(fortuneType), "fortune_type", fortuneType);
        queryWrapper.like(StringUtils.isNotBlank(team), "team", team);
        queryWrapper.in(ObjectUtils.isNotEmpty(teamBizIdList), "team_biz_id", teamBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(insuranceCompanyBizIdList), "insurance_company_biz_id", insuranceCompanyBizIdList);
        queryWrapper.in(ObjectUtils.isNotEmpty(productLaunchBizIdList), "product_launch_biz_id", productLaunchBizIdList);
        // 根据转介人(主)查询
        if (!CollectionUtils.isEmpty(brokerBizIdList)) {
            // 构建带引号的参数列表
            String brokerIds = brokerBizIdList.stream()
                    .map(id -> "'" + id + "'")
                    .collect(Collectors.joining(","));
            queryWrapper.apply("EXISTS (SELECT 1 FROM policy_broker pb WHERE pb.policy_no = expected_fortune.policy_no AND pb.broker_biz_id in (" + brokerIds + "))");
        }
        queryWrapper.in(ObjectUtils.isNotEmpty(signerBizIdList), "signer_biz_id", signerBizIdList);
        queryWrapper.eq(StringUtils.isNotBlank(fortuneBizType), "fortune_biz_type", fortuneBizType);
        // 根据投保人名称查询
        if (StringUtils.isNotBlank(policyHolder)) {
            queryWrapper.apply("EXISTS (SELECT 1 FROM policy_follow f WHERE f.policy_no = expected_fortune.policy_no AND (f.policy_holder like CONCAT('%', {0}, '%') or f.policy_holder_en like CONCAT('%', {0}, '%')))",
                    policyHolder);
        }

        // 按id升序排序
        queryWrapper.orderByAsc("id");
        return queryWrapper;
    }

    /**
     * 查询保单是否生成过预计发佣
     *
     * @param policyNo
     * @return
     */
    @Override
    public Result<Boolean> isGenerate(String policyNo) {
        List<ExpectedFortune> list = iExpectedFortuneService.queryList(policyNo);
        if (!CollectionUtils.isEmpty(list)) {
            throw new BusinessException("当前保单已经生成过预计发佣数据，无需再次生成");
        }
        return Result.success();
    }

    /**
     * 根据保单号批量删除预计发佣数据
     *
     * @param policyNo
     * @return
     */
    @Override
    public Result batchDelByPolicyNo(String policyNo) {
        iExpectedFortuneService.batchDelByPolicyNo(policyNo);
        return Result.success();
    }

    @Override
    public ExpectedFortuneStatisticsVO getStatistics(List<Long> expectedFortuneIds) {
        if (CollectionUtils.isEmpty(expectedFortuneIds)) {
            ExpectedFortuneStatisticsVO statisticsVO = new ExpectedFortuneStatisticsVO();
            statisticsVO.setTotalExpectedAmount(BigDecimal.ZERO);
            statisticsVO.setTotalPaidAmount(BigDecimal.ZERO);
            statisticsVO.setTotalUnpaidAmount(BigDecimal.ZERO);
            statisticsVO.setPaidAmountRatio(BigDecimal.ZERO);
            statisticsVO.setTotalPolicyCount(0);
            return statisticsVO;
        }
        // 自定义统计数据
        ExpectedFortuneStatisticsVO statisticsVO = iExpectedFortuneService.getStatistics(expectedFortuneIds);

        BigDecimal totalAmount = statisticsVO.getTotalExpectedAmount();
        BigDecimal totalPaidAmount = statisticsVO.getTotalPaidAmount();
        // 计算待出账金额
        statisticsVO.setTotalUnpaidAmount(totalAmount.subtract(totalPaidAmount));
        // 计算已出账比例（已出账金额/预计发佣金额）
        BigDecimal divided = BigDecimal.ZERO;
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
            divided = totalPaidAmount.divide(totalAmount, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        }
        statisticsVO.setPaidAmountRatio(divided);

        return statisticsVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(ExpectedFortuneUpdateRequest request) {
        String expectedFortuneBizId = request.getExpectedFortuneBizId();
        ExpectedFortune expectedFortune = iExpectedFortuneService.getOne(new QueryWrapper<ExpectedFortune>().eq("expected_fortune_biz_id", expectedFortuneBizId));
        if (expectedFortune == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), "预计发佣数据不存在");
        }
        if (expectedFortune.getPaidAmount().compareTo(BigDecimal.ZERO) > 0) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "当前应付款已有出账金额，不能修改");
        }

        // 根据保单号、期数查询关联 fortune 数据，重新计算：已出帐金额、待出帐金额、已出帐比例、待出帐比例
        List<Fortune> fortuneList = fortuneService.lambdaQuery()
                .eq(Fortune::getPolicyNo, request.getPolicyNo())
                .eq(Fortune::getFortunePeriod, request.getFortunePeriod())
                .list();
        BigDecimal paidAmount = BigDecimal.ZERO;
        BigDecimal unpaidAmount = request.getHkdAmount();
        BigDecimal paidRatio = BigDecimal.ZERO;
        BigDecimal unpaidRatio = BigDecimal.valueOf(100);
        if (!CollectionUtils.isEmpty(fortuneList)) {
            // 计算已出账金额
            for (Fortune fortune : fortuneList) {
                if (FortuneStatusEnum.SENT.getItemValue().equals(fortune.getStatus())) {
                    paidAmount = paidAmount.add(fortune.getCurrentPaymentHkdAmount());
                }
            }
            // 计算待出账金额
            unpaidAmount = request.getHkdAmount().subtract(paidAmount);
            // 计算已出账比例（已出账金额/预计发佣金额）
            paidRatio = paidAmount.divide(request.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
            // 计算待出账比例（待出账金额/预计发佣金额）
            unpaidRatio = unpaidAmount.divide(request.getHkdAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        }

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

        ExpectedFortune updateExpectedFortune = new ExpectedFortune();
        BeanUtils.copyProperties(request, updateExpectedFortune, "payoutDate", "actualPayoutDate");
        updateExpectedFortune.setId(expectedFortune.getId());

        // 设置 policy 关联字段
        if (!expectedFortune.getPolicyNo().equals(updateExpectedFortune.getPolicyNo())) {
            // 查询保单数据
            Policy policy = null;
            if (StringUtils.isNotBlank(request.getPolicyNo())) {
                policy = policyService.getOne(new QueryWrapper<Policy>().eq("policy_no", request.getPolicyNo()));
            }
            if (policy != null) {
                updateExpectedFortune.setInsuranceCompanyBizId(policy.getInsuranceCompanyBizId());
                updateExpectedFortune.setProductLaunchBizId(policy.getProductLaunchBizId());
                updateExpectedFortune.setPremium(policy.getPaymentPremium());
                updateExpectedFortune.setPolicyCurrency(policy.getCurrency());
            }
        }

        // 处理出账年月字段
        if (StringUtils.isNotBlank(request.getPayoutDate())) {
            updateExpectedFortune.setPayoutDate(LocalDate.parse(request.getPayoutDate() + "-01"));
        }
        // 处理实际出账年月字段
        if (StringUtils.isNotBlank(request.getActualPayoutDate())) {
            updateExpectedFortune.setActualPayoutDate(LocalDate.parse(request.getActualPayoutDate() + "-01"));
        }

        // 已出帐金额、待出帐金额、已出帐比例、待出帐比例
        updateExpectedFortune.setPaidAmount(paidAmount);
        updateExpectedFortune.setUnpaidAmount(unpaidAmount);
        updateExpectedFortune.setPaidRatio(paidRatio);
        updateExpectedFortune.setUnpaidRatio(unpaidRatio);

        updateExpectedFortune.setUpdaterId(loginUserId);
        updateExpectedFortune.setUpdateTime(LocalDateTime.now());
        iExpectedFortuneService.updateById(updateExpectedFortune);

        return true;
    }
}
