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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
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.constant.RedisConstants;
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.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;
        }
        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.getCurrency()));

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

                // 收集所有不同的fortune项目名称
                Set<String> allFortuneNames = fortuneList.stream()
                        .map(Fortune::getFortuneName)
                        .filter(StringUtils::isNotBlank)
                        .collect(Collectors.toSet());

                // 处理每个分组的数据
                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);
                    }

                    // 计算出账总额
                    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);

//                // 使用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.setExchangeRate(expectedFortune.getDefaultExchangeRate());
            fortune.setHkdAmount(fortuneAddRequest.getAmount().multiply(expectedFortune.getDefaultExchangeRate()));
            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
            fortune.setPayoutDate(expectedFortune.getPayoutDate());
        } else {
            fortune.setExchangeRate(BigDecimal.ONE);
            fortune.setHkdAmount(fortuneAddRequest.getAmount());
            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());
        }

        // 生成发佣业务ID
        fortune.setFortuneBizId(RandomStringGenerator.generateBizId16(CommonEnum.UID_TYPE_FORTUNE.getCode()));
        fortune.setCurrentPaymentAmount(fortuneAddRequest.getAmount());
        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.FAIL.getCode(), "出账类型不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getAmount())) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "出账金额不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getCurrency())) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "出账币种不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getFortuneType())) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "出账项目不能为空");
        }
        if (ObjectUtils.isEmpty(fortuneAddRequest.getBrokerBizId())) {
            throw new BusinessException(ResultCode.FAIL.getCode(), "转介人不能为空");
        }
        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(), "关联保单应付款，佣金期数不能为空");
            }
        }
    }

    private String queryByDict(String fortuneType) {
        //查询redis缓存的字典列表信息
        List<GetDictItemListByDictTypeResponse> dictTypeResponses = redisUtil.getCacheObject(RedisConstants.DICT_LIST);
        String fortuneName = GetDictItemListByDictTypeResponse.getItemLabel(dictTypeResponses,
                "csf_fortune_type", fortuneType);
        if (ObjectUtils.isNotEmpty(fortuneName)) {
            return fortuneName;
        }
        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.setExchangeRate(queryExchangeRateByFeign(fortuneAddRequest.getCurrency(), "HKD"));
            fortune.setHkdAmount(fortuneAddRequest.getAmount().multiply(fortune.getExchangeRate()));
            fortune.setCurrentPaymentHkdAmount(fortune.getHkdAmount());

            if ("R".equals(fortuneAddRequest.getFortuneBizType())) {
                if (policyMap.get(fortuneAddRequest.getPolicyNo()) == null) {
                    throw new BusinessException(ResultCode.NULL_ERROR.getCode(), fortuneAddRequest.getPolicyNo() + " 保单号不存在");
                }
                ExpectedFortune expectedFortune = expectedFortuneMap.get(fortuneAddRequest.getPolicyNo() + "_" + fortuneAddRequest.getFortunePeriod() + "_" + fortuneAddRequest.getFortuneType());
                if (expectedFortune == null) {
                    throw new BusinessException(ResultCode.NULL_ERROR.getCode(), fortuneAddRequest.getFortuneName() + " 预计出账不存在");
                }
                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("未找到当前预计发佣对应的来佣");
                }
                // 更新预计出账的数据
                processExpectedFortune(expectedFortune, fortune, expectedFortuneUpdateList);
            }

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

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

        return true;
    }

    private void processExpectedFortune(ExpectedFortune expectedFortune, Fortune fortune, List<ExpectedFortune> expectedFortuneUpdateList) {
        // 出账币种
        String currency = fortune.getCurrency();
        // 港币出账金额
        BigDecimal hkdAmount = fortune.getHkdAmount();

        ExpectedFortune updateObj = new ExpectedFortune();
        updateObj.setId(expectedFortune.getId());
        updateObj.setHkdAmount(expectedFortune.getHkdAmount().add(hkdAmount));
        updateObj.setUnpaidAmount(hkdAmount.add(expectedFortune.getUnpaidAmount()));
        updateObj.setUnpaidRatio(updateObj.getUnpaidAmount().divide(updateObj.getHkdAmount(), 4, RoundingMode.HALF_UP));

        // 根据预计出账币种计算 expectedFortune.getAmount() 字段
        BigDecimal originalAmount = fortune.getAmount();  // 原始币种金额
        BigDecimal exchangeRate = queryExchangeRateByFeign(currency, expectedFortune.getCurrency());
        BigDecimal amount = originalAmount.multiply(exchangeRate);// 本次新增出账金额

        updateObj.setAmount(amount.add(expectedFortune.getAmount()));

        expectedFortuneUpdateList.add(updateObj);
    }

}




