package com.yd.csf.service.service.impl;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

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.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yd.auth.core.dto.AuthUserDto;
import com.yd.auth.core.utils.SecurityUtil;
import com.yd.common.enums.ResultCode;
import com.yd.common.exception.BusinessException;
import com.yd.common.utils.RandomStringGenerator;
import com.yd.csf.service.dto.*;
import com.yd.csf.service.enums.FortuneAccountStatusEnum;
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.FortuneAccountMapper;
import com.yd.csf.service.utils.BusinessNoUtils;
import com.yd.csf.service.vo.FortuneAccountVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author Zhang Jianan
* @description 针对表【fortune_account(出账记录表)】的数据库操作Service实现
* @createDate 2025-09-23 15:45:32
*/
@Service
public class FortuneAccountServiceImpl extends ServiceImpl<FortuneAccountMapper, FortuneAccount>
    implements FortuneAccountService{

    @Resource
    private FortuneService fortuneService;

    @Resource
    private FortuneAccountEditRecordService fortuneAccountEditRecordService;

    @Resource
    private IExpectedFortuneService expectedFortuneService;

    @Resource
    private PolicyBrokerService policyBrokerService;

    private final Gson GSON = new Gson();

    @Override
    public QueryWrapper<FortuneAccount> getQueryWrapper(FortuneAccountQueryRequest fortuneAccountQueryRequest) {
        QueryWrapper<FortuneAccount> queryWrapper = new QueryWrapper<>();
        if (fortuneAccountQueryRequest == null) {
            return queryWrapper;
        }
        List<String> statusList = fortuneAccountQueryRequest.getStatusList();
        if (CollectionUtils.isEmpty(statusList)) {
            statusList = Collections.singletonList("6");
        }
        List<String> brokerBizIdList = fortuneAccountQueryRequest.getBrokerBizIdList();
        List<String> teamBizIdList = fortuneAccountQueryRequest.getTeamBizIdList();
        queryWrapper.in(CollectionUtils.isNotEmpty(brokerBizIdList), "broker_biz_id", brokerBizIdList);
        queryWrapper.in(CollectionUtils.isNotEmpty(teamBizIdList), "team_biz_id", teamBizIdList);

        queryWrapper.eq(StringUtils.isNotBlank(fortuneAccountQueryRequest.getBroker()), "broker", fortuneAccountQueryRequest.getBroker());
        queryWrapper.eq(StringUtils.isNotBlank(fortuneAccountQueryRequest.getTeam()), "team", fortuneAccountQueryRequest.getTeam());
        queryWrapper.in(CollectionUtils.isNotEmpty(statusList), "status", statusList);
//        queryWrapper.ge(fortuneAccountQueryRequest.getAccountDateStart() != null, "fortune_account_date", fortuneAccountQueryRequest.getAccountDateStart());
//        queryWrapper.le(fortuneAccountQueryRequest.getAccountDateEnd() != null, "fortune_account_date", fortuneAccountQueryRequest.getAccountDateEnd());
        Date start = parseStartDate(fortuneAccountQueryRequest.getAccountDateStart());
        Date end = parseEndDate(fortuneAccountQueryRequest.getAccountDateEnd());
        queryWrapper.ge(start != null, "fortune_account_date", start);
        queryWrapper.le(end != null, "fortune_account_date", end);
        queryWrapper.like(StringUtils.isNotBlank(fortuneAccountQueryRequest.getBusinessNo()),"business_no",fortuneAccountQueryRequest.getBusinessNo());
        // 默认排序
        queryWrapper.orderByDesc("fortune_account_date");
        return queryWrapper;
    }

    private Date parseStartDate(String dateStr) {
        if (StringUtils.isBlank(dateStr)) return null;
        if (dateStr.length() == 7) { // yyyy-MM
            return Date.from(YearMonth.parse(dateStr).atDay(1)
                    .atStartOfDay(ZoneId.systemDefault()).toInstant());
        }
        return Date.from(LocalDate.parse(dateStr).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    private Date parseEndDate(String dateStr) {
        if (StringUtils.isBlank(dateStr)) return null;
        if (dateStr.length() == 7) { // yyyy-MM
            return Date.from(YearMonth.parse(dateStr).atEndOfMonth()
                    .atStartOfDay(ZoneId.systemDefault()).toInstant());
        }
        return Date.from(LocalDate.parse(dateStr).plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    @Override
    public Page<FortuneAccountVO> getFortuneAccountVOPage(Page<FortuneAccount> fortunePage) {
        List<FortuneAccount> fortuneAccountList = fortunePage.getRecords();
        Page<FortuneAccountVO> fortuneAccountVOPage = new Page<>(fortunePage.getCurrent(), fortunePage.getSize(), fortunePage.getTotal());

        if (CollectionUtils.isEmpty(fortuneAccountList)) {
            return fortuneAccountVOPage;
        }

        List<FortuneAccountVO> fortuneAccountVOList = new ArrayList<>();
        for (FortuneAccount fortuneAccount : fortuneAccountList) {
            FortuneAccountVO fortuneAccountVO = FortuneAccountVO.objToVo(fortuneAccount);
            fortuneAccountVO.setFortuneList(GSON.fromJson(fortuneAccount.getContent(), new TypeToken<List<Map<String, Object>>>(){}.getType()));
            fortuneAccountVO.setFortuneAccountMonth(fortuneAccount.getFortuneAccountDate());
            fortuneAccountVOList.add(fortuneAccountVO);
        }

        fortuneAccountVOPage.setRecords(fortuneAccountVOList);
        return fortuneAccountVOPage;
    }

    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public void saveFortuneAccount(List<FortuneAccountExportDTO> accountExportDTOList) {
        if (CollectionUtils.isEmpty(accountExportDTOList)) {
            return;
        }
        // 当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        Long loginUserId = currentLoginUser.getId();

        // 查询转介人的内部编号列表信息
        List<String> brokerBizIdList = accountExportDTOList.stream().map(FortuneAccountExportDTO::getBrokerBizId).collect(Collectors.toList());
        Map<String, String> map = policyBrokerService.queryInternalCodeMap(brokerBizIdList);
        List<FortuneAccount> fortuneAccountList = new ArrayList<>();
        Date currentDate = new Date();

        Map<String, Integer> prefixSerialCache = new HashMap<>();

        for (FortuneAccountExportDTO accountExportDTO : accountExportDTOList) {
            FortuneAccount fortuneAccount = new FortuneAccount();
            fortuneAccount.setFortuneAccountBizId(RandomStringGenerator.generateBizId16("fortune_account"));
            String internalCode = map.get(accountExportDTO.getBrokerBizId());

            String billingYearMonth = accountExportDTO.getActualPayoutDate() != null
                    ? accountExportDTO.getActualPayoutDate().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMM"))
                    : "";

            String prefix = BusinessNoUtils.getPrefix(billingYearMonth, internalCode);
            // 业务编号生成
            String businessNo = generateBusinessNoWithCache(prefix, billingYearMonth, internalCode, prefixSerialCache);

            fortuneAccount.setBusinessNo(businessNo);
            fortuneAccount.setBroker(accountExportDTO.getBroker());
            fortuneAccount.setBrokerBizId(accountExportDTO.getBrokerBizId());
            fortuneAccount.setTeam(accountExportDTO.getTeam());
            fortuneAccount.setTeamBizId(accountExportDTO.getTeamBizId());
            fortuneAccount.setPayoutCurrency(accountExportDTO.getPayoutCurrency());
            fortuneAccount.setPayoutAmount(accountExportDTO.getPayoutAmount());
            fortuneAccount.setHkdAmount(accountExportDTO.getHkdAmount());
            fortuneAccount.setFortuneAccountDate(this.getActualPayoutDate(accountExportDTO.getActualPayoutDate()));
            // 出账状态默认待出账
            fortuneAccount.setStatus(FortuneStatusEnum.CHECKED.getItemValue());

            // 转换为List<Map<String, Object>>
            Map<String, BigDecimal> fortuneAmounts = accountExportDTO.getFortuneAmounts();
            List<Map<String, Object>> fortuneList = new ArrayList<>();
            for (Map.Entry<String, BigDecimal> entry : fortuneAmounts.entrySet()) {
                Map<String, Object> fortune = new HashMap<>();
                fortune.put("fortuneName", entry.getKey());
                fortune.put("amount", entry.getValue());
                fortuneList.add(fortune);
            }
            fortuneAccount.setContent(GSON.toJson(fortuneList));
            fortuneAccount.setFortuneList(accountExportDTO.getFortuneList());
            fortuneAccount.setCreatorId(loginUserId.toString());
            fortuneAccount.setUpdaterId(loginUserId.toString());
            fortuneAccount.setCreateTime(currentDate);
            fortuneAccount.setUpdateTime(currentDate);

            fortuneAccountList.add(fortuneAccount);
        }
        // 1. 保存薪资记录
        this.saveBatch(fortuneAccountList);

        // 处理关联发佣记录
        List<Fortune> updateFortuneList = new ArrayList<>();
        // 处理关联预计发佣记录
        Set<String> expectedFortuneBizIdList = new HashSet<>();
        for (FortuneAccount fortuneAccount : fortuneAccountList) {
            String fortuneAccountBizId = fortuneAccount.getFortuneAccountBizId();
            List<Fortune> fortuneList = fortuneAccount.getFortuneList();
            if (CollectionUtils.isNotEmpty(fortuneList)) {
                for (Fortune item : fortuneList) {
                    Fortune updateFortune = new Fortune();
                    updateFortune.setId(item.getId());
                    updateFortune.setExpectedFortuneBizId(item.getExpectedFortuneBizId());
                    updateFortune.setRuleAmount(item.getRuleAmount());
                    updateFortune.setFortuneAccountBizId(fortuneAccountBizId);
                    updateFortune.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
                    updateFortune.setReconciliationOperator(currentLoginUser.getRealName());

                    updateFortuneList.add(updateFortune);
                    // 处理关联预计发佣记录
                    expectedFortuneBizIdList.add(item.getExpectedFortuneBizId());
                }
            }
        }

        // 2. 更新预计发佣记录的出账状态
        Map<String, ExpectedFortune> expectedFortuneMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(expectedFortuneBizIdList)) {

            // 查询预计发佣记录
            List<ExpectedFortune> expectedFortuneList = expectedFortuneService.list(new QueryWrapper<ExpectedFortune>()
                    .in("expected_fortune_biz_id", expectedFortuneBizIdList));

            // 转为 Map
            expectedFortuneMap = expectedFortuneList.stream()
                    .collect(Collectors.toMap(ExpectedFortune::getExpectedFortuneBizId, Function.identity()));

            List<ExpectedFortune> updateExpectedFortuneList = new ArrayList<>();
            for (ExpectedFortune expectedFortune : expectedFortuneList) {
                // 如果状态是 待出账，才修改
                if (FortuneStatusEnum.WAIT.getItemValue().equals(expectedFortune.getStatus())) {
                    ExpectedFortune updateExpectedFortune = new ExpectedFortune();
                    updateExpectedFortune.setId(expectedFortune.getId());
                    updateExpectedFortune.setStatus(FortuneStatusEnum.CHECKED.getItemValue());
                    updateExpectedFortuneList.add(updateExpectedFortune);
                }
            }
            if (CollectionUtils.isNotEmpty(updateExpectedFortuneList)) {
                expectedFortuneService.updateBatchById(updateExpectedFortuneList);
            }
        }

        // 3. 计算本次出账比例
        for (Fortune f : updateFortuneList) {
            ExpectedFortune ef = expectedFortuneMap.get(f.getExpectedFortuneBizId());
            if (ef != null) {
                BigDecimal currentPaymentRatio = BigDecimal.ZERO;
                if (BigDecimal.ZERO.compareTo(ef.getRuleAmount()) != 0) {
                    currentPaymentRatio = f.getRuleAmount()
                            .divide(ef.getRuleAmount(), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                }
                f.setCurrentPaymentRatio(currentPaymentRatio);
            }
        }
        fortuneService.updateBatchById(updateFortuneList);
    }

    /**
     * 带本地缓存的业务编号生成（解决同一批次内相同 prefix 的流水号重复问题）
     * @param prefix           业务编号前缀（出账年月 + 转介人内部编号后6位）
     * @param billingYearMonth 出账年月
     * @param internalCode     转介人内部编号
     * @param prefixSerialCache 本地缓存（Map<prefix, 当前已使用的最大流水号数值>）
     * @return 完整的业务编号
     */
    private String generateBusinessNoWithCache(String prefix, String billingYearMonth,
                                               String internalCode, Map<String, Integer> prefixSerialCache) {
        // 从缓存中获取当前 prefix 已分配的最大流水号，若不存在则查询数据库
        Integer currentMaxSerial = prefixSerialCache.get(prefix);
        if (currentMaxSerial == null) {
            String maxSerialFromDb = getMaxSerialNoByPrefix(prefix); // 返回如 "000000"
            currentMaxSerial = Integer.parseInt(maxSerialFromDb);
        }
        int nextSerial = currentMaxSerial + 1;
        String serialPart = String.format("%06d", nextSerial);
        String businessNo = BusinessNoUtils.generate(billingYearMonth, internalCode, serialPart);
        // 更新缓存
        prefixSerialCache.put(prefix, nextSerial);
        return businessNo;
    }

    /**
     * 根据前缀查询当前最大流水号（后6位）
     * @param prefix 业务编号前缀（出账年月+转介人内部编号后6位，共12位）
     * @return 最大流水号（6位字符串），如果没有记录则返回 "000000"
     */
    private String getMaxSerialNoByPrefix(String prefix) {
        // 查询 business_no 以指定前缀开头的记录，按业务编号降序取第一条
        LambdaQueryWrapper<FortuneAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.likeRight(FortuneAccount::getBusinessNo, prefix)
                .orderByDesc(FortuneAccount::getBusinessNo)
                .last("limit 1");
        FortuneAccount account = this.getOne(wrapper, false);
        if (account == null) {
            return "000000";
        }
        String businessNo = account.getBusinessNo();
        // 取最后6位作为流水号
        return businessNo.substring(businessNo.length() - 6);
    }

    private Date getActualPayoutDate(LocalDate actualPayoutDate) {
        if (actualPayoutDate == null) {
            return null;
        }
        return Date.from(actualPayoutDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    @Override
    public FortuneAccount getByFortuneAccountBizId(String fortuneAccountBizId) {
        return this.getOne(new QueryWrapper<FortuneAccount>().eq("fortune_account_biz_id", fortuneAccountBizId));
    }

    @Override
    public Map<String, Object> addFortuneAccount(FortuneAccountAddRequest fortuneAccountAddRequest) {
        // 当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        Long loginUserId = currentLoginUser.getId();

        FortuneAccount fortuneAccount = new FortuneAccount();
        BeanUtils.copyProperties(fortuneAccountAddRequest, fortuneAccount);

        fortuneAccount.setFortuneAccountBizId(RandomStringGenerator.generateBizId16("fortune_account"));
        fortuneAccount.setRemark(fortuneAccountAddRequest.getRemark());
        fortuneAccount.setCreatorId(loginUserId.toString());
        fortuneAccount.setUpdaterId(loginUserId.toString());
        fortuneAccount.setCreateTime(new Date());
        fortuneAccount.setUpdateTime(new Date());

        this.save(fortuneAccount);
        return Collections.singletonMap("fortuneAccountBizId", fortuneAccount.getFortuneAccountBizId());
    }

    @Override
    public Boolean updateFortuneAccount(FortuneAccountUpdateRequest fortuneAccountUpdateRequest) {
        // 当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        Long loginUserId = currentLoginUser.getId();

        String fortuneAccountBizId = fortuneAccountUpdateRequest.getFortuneAccountBizId();

        FortuneAccount fortuneAccount = this.getByFortuneAccountBizId(fortuneAccountBizId);
        if (fortuneAccount == null) {
            throw new BusinessException(ResultCode.NULL_ERROR.getCode(), ResultCode.NULL_ERROR.getMessage());
        }
        // 保存修改记录
        saveUpdateFortuneAccountRecord(fortuneAccount, fortuneAccountUpdateRequest);

        BeanUtils.copyProperties(fortuneAccountUpdateRequest, fortuneAccount, "fortuneAccountBizId");
        fortuneAccount.setUpdaterId(loginUserId.toString());
        fortuneAccount.setUpdateTime(new Date());

        this.updateById(fortuneAccount);

//        // 更新关联 expected_fortune、fortune的出账日期
//        if (fortuneAccountUpdateRequest.getFortuneAccountDate() != null) {
//            List<Fortune> fortuneList = fortuneService.lambdaQuery().eq(Fortune::getFortuneAccountBizId, fortuneAccountBizId).list();
//            if (CollectionUtils.isNotEmpty(fortuneList)) {
//                Set<String> expectedBizIdSet = fortuneList.stream().map(Fortune::getExpectedFortuneBizId).collect(Collectors.toSet());
//                // 查询 expected_fortune 列表
//                List<ExpectedFortune> expectedList = expectedFortuneService.lambdaQuery()
//                        .in(ExpectedFortune::getExpectedFortuneBizId, expectedBizIdSet)
//                        .list();
//
//            }
//        }
        return true;
    }

    private void saveUpdateFortuneAccountRecord(FortuneAccount fortuneAccount, FortuneAccountUpdateRequest fortuneAccountUpdateRequest) {
        FortuneAccountEditRecord fortuneAccountEditRecord = new FortuneAccountEditRecord();
        // 薪资记录业务id
        fortuneAccountEditRecord.setFortuneAccountBizId(fortuneAccount.getFortuneAccountBizId());
        fortuneAccountEditRecord.setIsDeleted(0);
        fortuneAccountEditRecord.setCreateTime(new Date());
        // 当前登录用户
        AuthUserDto currentLoginUser = SecurityUtil.getCurrentLoginUser();
        Long loginUserId = currentLoginUser.getId();

        fortuneAccountEditRecord.setUserBizId(loginUserId.toString());
        fortuneAccountEditRecord.setUserName(currentLoginUser.getUsername());

        // 构建修改内容
        StringBuilder editContentBuilder = new StringBuilder();
        editContentBuilder.append("修改了薪资记录:");
        editContentBuilder.append("转介人:").append(fortuneAccountUpdateRequest.getBroker()).append(";");
        editContentBuilder.append("团队:").append(fortuneAccountUpdateRequest.getTeam()).append(";");
        editContentBuilder.append("出账金额:").append(fortuneAccountUpdateRequest.getAmount()).append(";");
        editContentBuilder.append("出账币种:").append(fortuneAccountUpdateRequest.getCurrency()).append(";");
        if (StringUtils.isNotBlank(fortuneAccountUpdateRequest.getRemark())) {
            editContentBuilder.append("备注:").append(fortuneAccountUpdateRequest.getRemark()).append(";");
        }

        fortuneAccountEditRecord.setEditContent(editContentBuilder.toString());
        fortuneAccountEditRecord.setRemark(fortuneAccountUpdateRequest.getRemark());

        fortuneAccountEditRecordService.save(fortuneAccountEditRecord);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean completeFortuneAccount(CompleteFortuneAccountRequest req) {
        /* 1. 参数校验 & 查询 --------------------------------------------------*/
        List<String> accountBizIds = req.getFortuneAccountBizIdList();
        if (CollectionUtils.isEmpty(accountBizIds)) {
            throw new BusinessException("出账清单不能为空");
        }
        List<FortuneAccount> accountList = this.lambdaQuery()
                .in(FortuneAccount::getFortuneAccountBizId, accountBizIds)
                .list();

        Long loginUserId = SecurityUtil.getCurrentLoginUser().getId();

        /* 2. 更新 FortuneAccount 状态为已出账 ----------------------------------*/
        accountList.forEach(a -> {
            a.setStatus(FortuneStatusEnum.SENT.getItemValue());
            a.setUpdaterId(loginUserId.toString());
            a.setUpdateTime(new Date());
        });
        this.updateBatchById(accountList);

        /* 3. 取出关联的所有 fortune 并一次性更新为已出账 ------------------------*/
        List<String> accountBizIdList = accountList.stream()
                .map(FortuneAccount::getFortuneAccountBizId).collect(Collectors.toList());

        List<Fortune> fortunes = fortuneService.lambdaQuery()
                .in(Fortune::getFortuneAccountBizId, accountBizIdList)
                .eq(Fortune::getStatus, FortuneStatusEnum.CHECKED.getItemValue())
                .list();
        if (CollectionUtils.isNotEmpty(fortunes)) {
            List<Fortune> fortuneUpdateList = new ArrayList<>();
            for (Fortune f : fortunes) {
                Fortune fortune = new Fortune();
                fortune.setId(f.getId());
                fortune.setStatus(FortuneStatusEnum.SENT.getItemValue());
                fortune.setUpdaterId(loginUserId.toString());
                fortune.setUpdateTime(new Date());

                fortuneUpdateList.add(fortune);
            }
            fortuneService.updateBatchById(fortuneUpdateList);
        }

        /* 4. 按 expected_fortune 维度重新汇总已出账金额 --------------------------*/
        // 4.1 收集所有涉及的 expectedFortuneBizId
        Set<String> expectedIds = fortunes.stream()
                .map(Fortune::getExpectedFortuneBizId)
                .collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(expectedIds)) {
            return true; // 没有对应预计出账，直接结束
        }

        // 4.2 先锁再算
        List<ExpectedFortune> expectedList = expectedFortuneService.lambdaQuery()
                .in(ExpectedFortune::getExpectedFortuneBizId, expectedIds)
                .last("FOR UPDATE")
                .list();

        // 4.3 全量汇总：把同一 expected_fortune 下所有 fortune 的 current_payment_ratio 求和
        Map<String, List<Fortune>> fortuneMap = fortuneService.lambdaQuery()
                .in(Fortune::getExpectedFortuneBizId, expectedIds)
                .eq(Fortune::getStatus, FortuneStatusEnum.SENT.getItemValue())
                .list()
                .stream()
                .collect(Collectors.groupingBy(Fortune::getExpectedFortuneBizId));

        Map<String, BigDecimal> paidRatioMap = new HashMap<>();
        Map<String, BigDecimal> paidMap = new HashMap<>();
        Map<String, BigDecimal> paidRuleMap = new HashMap<>();
        Map<String, BigDecimal> hkdAmountMap = new HashMap<>();

        Set<String> keySet = fortuneMap.keySet();
        for (String expectedFortuneBizId : keySet) {
            List<Fortune> list = fortuneMap.get(expectedFortuneBizId);
            BigDecimal paidRatio = BigDecimal.ZERO;
            BigDecimal paid = BigDecimal.ZERO;
            BigDecimal paidRule = BigDecimal.ZERO;
            BigDecimal hkdAmount = BigDecimal.ZERO;
            for (Fortune f : list) {
                paidRatio = paidRatio.add(f.getCurrentPaymentRatio());
                paid = paid.add(f.getCurrentPaymentHkdAmount());
                paidRule = paidRule.add(f.getRuleAmount());
                hkdAmount = hkdAmount.add(f.getCurrentPaymentHkdAmount());
            }
            paidRatioMap.put(expectedFortuneBizId, paidRatio);
            paidMap.put(expectedFortuneBizId, paid);
            paidRuleMap.put(expectedFortuneBizId, paidRule);
            hkdAmountMap.put(expectedFortuneBizId, hkdAmount);
        }

        // 4.4 反写 expected_fortune
        for (ExpectedFortune ef : expectedList) {
            // 累积已发放保单记账金额
            BigDecimal paidRule = paidRuleMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
            BigDecimal unpaidRule = ef.getRuleAmount().subtract(paidRule);

            // 剩余发放折合港币金额
            BigDecimal unpaidRuleAmountHkd = unpaidRule.multiply(ef.getDefaultExchangeRate());

            // 应发港币金额（估）
            BigDecimal hkdAmount = hkdAmountMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
            BigDecimal expectedHKD = hkdAmount.add(unpaidRuleAmountHkd);
            // 发放币种金额 = 港币 × 港币兑发放币种汇率
            BigDecimal expectedPayout = expectedHKD.multiply(ef.getHkdToPayoutRate());

            // 累计出账港币金额
            BigDecimal paid = paidMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);

            // 累积已发放比例 = 累积：已发放保单记账金额/应发保单记账金额
            BigDecimal totalPaidRatio = paidRatioMap.getOrDefault(ef.getExpectedFortuneBizId(), BigDecimal.ZERO);
            BigDecimal unpaidRatio = BigDecimal.valueOf(100).subtract(totalPaidRatio);

            String newStatus;
            if (unpaidRatio.compareTo(BigDecimal.ZERO) == 0) {
                newStatus = FortuneStatusEnum.SENT.getItemValue();        // 全部完成
            } else if (totalPaidRatio.compareTo(BigDecimal.ZERO) > 0) {
                newStatus = FortuneStatusEnum.PARTIAL_SENT.getItemValue(); // 部分完成
            } else {
                newStatus = ef.getStatus();                               // 无变化
            }

            expectedFortuneService.lambdaUpdate()
                    .set(ExpectedFortune::getPaidRuleAmount, paidRule)
                    .set(ExpectedFortune::getUnpaidRuleAmount, unpaidRule)
                    .set(ExpectedFortune::getUnpaidRuleAmountHkd, unpaidRuleAmountHkd)
                    .set(ExpectedFortune::getHkdAmount, expectedHKD)
                    .set(ExpectedFortune::getPayoutAmount, expectedPayout)
                    .set(ExpectedFortune::getPaidAmount, paid)
                    .set(ExpectedFortune::getUnpaidAmount, unpaidRuleAmountHkd)
                    .set(ExpectedFortune::getPaidRatio, totalPaidRatio)
                    .set(ExpectedFortune::getUnpaidRatio, unpaidRatio)
                    .set(ExpectedFortune::getStatus, newStatus)
                    .set(ExpectedFortune::getUpdaterId, loginUserId.toString())
                    .set(ExpectedFortune::getUpdateTime, LocalDateTime.now())
                    .eq(ExpectedFortune::getId, ef.getId())
                    .update();
        }

        return true;
    }

    @Override
    public FortuneAccountStatisticsVO getFortuneAccountStatistics(List<Long> fortuneAccountIdList) {
        if (CollectionUtils.isEmpty(fortuneAccountIdList)) {
            FortuneAccountStatisticsVO vo = new FortuneAccountStatisticsVO();
            vo.setTotalAmount(BigDecimal.ZERO);
            vo.setBrokerCount(0L);
            return vo;
        }

        // 统计总金额和总人数
        List<FortuneAccount> fortuneAccountList = this.baseMapper.selectBatchIds(fortuneAccountIdList);

        BigDecimal totalAmount = fortuneAccountList.stream()
                .map(FortuneAccount::getHkdAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        int brokerCount = fortuneAccountList.stream()
                .map(FortuneAccount::getBroker)
                .collect(Collectors.toSet()).size();

        FortuneAccountStatisticsVO vo = new FortuneAccountStatisticsVO();
        vo.setTotalAmount(totalAmount);
        vo.setBrokerCount(brokerCount);
        return vo;
    }

    /**
     * 查询单个对象
     * @param fortuneAccountBizId
     * @return
     */
    @Override
    public FortuneAccount queryOne(String fortuneAccountBizId) {
        return this.baseMapper.selectOne(new LambdaQueryWrapper<FortuneAccount>()
                .eq(FortuneAccount::getFortuneAccountBizId,fortuneAccountBizId)
                .last(" limit 1 ")
        );
    }

    /**
     * 查询出账记录列表
     * @param brokerBizId 转介人唯一业务ID
     * @param month       出账年月，例 202605
     * @param status      出账状态
     * @return
     */
    @Override
    public List<FortuneAccount> queryList(String brokerBizId,
                                          String month,
                                          String status) {
        LambdaQueryWrapper<FortuneAccount> wrapper = new LambdaQueryWrapper<>();

        if (StringUtils.isNotBlank(brokerBizId)) {
            wrapper.eq(FortuneAccount::getBrokerBizId, brokerBizId);
        }

        // 出账年月范围过滤（转换为当月第一天 ~ 当月最后一天）
        if (StringUtils.isNotBlank(month)) {
            YearMonth yearMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyyMM"));
            LocalDate startDate = yearMonth.atDay(1);
            LocalDate endDate = yearMonth.atEndOfMonth();
            wrapper.ge(FortuneAccount::getFortuneAccountDate, startDate)
                    .le(FortuneAccount::getFortuneAccountDate, endDate);
        }

        if (StringUtils.isNotBlank(status)) {
            wrapper.eq(FortuneAccount::getStatus, status);
        }

        wrapper.orderByDesc(FortuneAccount::getFortuneAccountDate);

        // 执行查询
        return list(wrapper);
    }

    @Override
    public List<FortuneAccount> queryList(List<Map.Entry<String, String>> pairs, String status) {
        LambdaQueryWrapper<FortuneAccount> wrapper = new LambdaQueryWrapper<>();

        if (CollectionUtils.isNotEmpty(pairs)) {
            wrapper.and(wq -> {
                for (int i = 0; i < pairs.size(); i++) {
                    Map.Entry<String, String> pair = pairs.get(i);
                    String brokerBizId = pair.getKey();
                    String month = pair.getValue();
                    if (StringUtils.isBlank(brokerBizId) || StringUtils.isBlank(month)) {
                        continue;
                    }
                    YearMonth yearMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyyMM"));
                    LocalDate startDate = yearMonth.atDay(1);
                    LocalDate endDate = yearMonth.atEndOfMonth();

                    if (i == 0) {
                        wq.eq(FortuneAccount::getBrokerBizId, brokerBizId)
                                .ge(FortuneAccount::getFortuneAccountDate, startDate)
                                .le(FortuneAccount::getFortuneAccountDate, endDate);
                    } else {
                        wq.or()
                                .eq(FortuneAccount::getBrokerBizId, brokerBizId)
                                .ge(FortuneAccount::getFortuneAccountDate, startDate)
                                .le(FortuneAccount::getFortuneAccountDate, endDate);
                    }
                }
            });
        }

        if (StringUtils.isNotBlank(status)) {
            wrapper.eq(FortuneAccount::getStatus, status);
        }
        wrapper.orderByDesc(FortuneAccount::getFortuneAccountDate);

        return list(wrapper);
    }

}




