package com.cool.store.service.store.impl;

import cn.hutool.core.collection.CollStreamUtil;
import com.alibaba.excel.converters.bigdecimal.BigDecimalNumberConverter;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.RedisConstant;
import com.cool.store.dao.FranchiseFeeDAO;
import com.cool.store.dao.LineInfoDAO;
import com.cool.store.dao.ShopInfoDAO;
import com.cool.store.dao.ShopStageInfoDAO;
import com.cool.store.dao.fees.WalletAllocationConfigDAO;
import com.cool.store.dao.fees.WalletPayInfoDAO;
import com.cool.store.dao.order.PreAllocationRecordDAO;
import com.cool.store.dao.order.StoreOrderDAO;
import com.cool.store.dao.order.StoreOrderOptionSnapshotDAO;
import com.cool.store.dao.wallet.WalletTradeDAO;
import com.cool.store.dto.fees.ExpenseTypeAmountDTO;
import com.cool.store.dto.fees.WalletAllocationDTO;
import com.cool.store.dto.wallet.AccountInfoDTO;
import com.cool.store.dto.wallet.AccountTransferDTO;
import com.cool.store.dto.wallet.BatchTransferDTO;
import com.cool.store.dto.wallet.TradeRecordDTO;
import com.cool.store.entity.FranchiseFeeDO;
import com.cool.store.entity.LineInfoDO;
import com.cool.store.entity.ShopInfoDO;
import com.cool.store.entity.ShopStageInfoDO;
import com.cool.store.entity.fees.ShopAllocationInfoDO;
import com.cool.store.entity.fees.WalletAllocationConfigDO;
import com.cool.store.entity.fees.WalletPayInfoDO;
import com.cool.store.entity.order.PreAllocationRecordDO;
import com.cool.store.entity.order.StoreOrderDO;
import com.cool.store.entity.wallet.WalletTradeDO;
import com.cool.store.enums.ClaimStatusEnum;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.FranchiseBrandEnum;
import com.cool.store.enums.XGJCollectionStatusEnum;
import com.cool.store.enums.fees.AllocationPayStatusEnum;
import com.cool.store.enums.fees.PayTypeEnum;
import com.cool.store.enums.fees.WalletFeeItemEnum;
import com.cool.store.enums.order.StoreOrderStatusEnum;
import com.cool.store.enums.point.ShopSubStageEnum;
import com.cool.store.enums.point.ShopSubStageStatusEnum;
import com.cool.store.enums.wallet.TradeTypeEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.FranchiseFeeMapper;
import com.cool.store.request.store.PreAllocationAddRequest;
import com.cool.store.request.store.PreAllocationEditRequest;
import com.cool.store.request.store.PreAllocationSaveRequest;
import com.cool.store.request.store.TransRequest;
import com.cool.store.request.wallet.*;
import com.cool.store.request.xgj.PushFranchiseFeeRequest;
import com.cool.store.request.xgj.ReceiptRequest;
import com.cool.store.service.PushService;
import com.cool.store.service.dict.impl.DictService;
import com.cool.store.service.fees.WalletPayInfoService;
import com.cool.store.service.order.MiniStoreOrderService;
import com.cool.store.service.store.PreAllocationRecordService;
import com.cool.store.service.wallet.WalletApiService;
import com.cool.store.utils.*;
import com.cool.store.vo.order.MiniStoreOrderDetailVO;
import com.cool.store.vo.order.PreAllocationRecordVO;
import com.sun.xml.bind.v2.TODO;
import groovy.util.logging.Slf4j;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.cool.store.enums.order.StoreOrderStatusEnum.PAY_FAIL;
import static com.cool.store.enums.point.ShopSubStageStatusEnum.*;
import static com.cool.store.enums.wallet.WalletTradeModuleEnum.*;

/**
 * @Auther zx_szh
 * @Date 2026/2/4 15:56
 * @Version 1.0
 */
@lombok.extern.slf4j.Slf4j
@Service
@RequiredArgsConstructor
@Slf4j
public class PreAllocationRecordServiceImpl implements PreAllocationRecordService {

    private final PreAllocationRecordDAO dao;
    private final RedisUtil redisUtil;
    private final DictService dictService;
    private final StoreOrderDAO storeOrderDAO;
    private final ShopStageInfoDAO shopStageInfoDAO;
    private final WalletPayInfoDAO walletPayInfoDAO;
    private final WalletAllocationConfigDAO walletAllocationConfigDAO;
    private final StoreOrderOptionSnapshotDAO storeOrderOptionSnapshotDAO;
    private final ShopInfoDAO shopInfoDAO;
    private final WalletApiService walletApiService;
    private final LineInfoDAO lineInfoDAO;
    private final WalletTradeDAO walletTradeDAO;
    private final PreAllocationRecordDAO preAllocationRecordDAO;
    private final WalletPayInfoService walletPayInfoService;
    private final PushService pushService;
    private final FranchiseFeeDAO franchiseFeeDAO;
    private final FranchiseFeeMapper franchiseFeeMapper;


    @Transactional
    @Override
    public Boolean saveBatch(PreAllocationSaveRequest req){
        String lockKey = "order:lock:" + req.getOrderId();
        String uuid = UUID.randomUUID().toString();
        if(!Boolean.TRUE.equals(redisUtil.tryLock(lockKey, uuid,30, TimeUnit.SECONDS))){
            throw new ServiceException(ErrorCodeEnum.ORDER_PAYING_CANT_ADJUST);
        }
        try{
            //查询订单
            StoreOrderDO storeOrder = storeOrderDAO.getById(req.getOrderId());
            //订单不存在
            if (Objects.isNull(storeOrder)){
                throw new ServiceException(ErrorCodeEnum.STORE_ORDER_NOT_FOUND);
            }
            BigDecimal totalAmount = storeOrder.getTotalAmount() == null ? BigDecimal.ZERO : storeOrder.getTotalAmount();

            List<PreAllocationRecordDO> existList = dao.queryPageByShopId(req.getShopId());
            Map<Long, PreAllocationRecordDO> existMap = existList.stream()
                    .filter(v -> v.getId() != null)
                    .collect(Collectors.toMap(PreAllocationRecordDO::getId, Function.identity(), (a, b) -> a));

            List<PreAllocationSaveRequest.PreAllocationItem> reqItems = req.getItems() == null ? Collections.emptyList() : req.getItems().stream().filter(item->BigDecimal.ZERO.compareTo(item.getPayAmount())<0).collect(Collectors.toList());
            Set<Long> reqIdSet = reqItems.stream()
                    .filter(Objects::nonNull)
                    .map(PreAllocationSaveRequest.PreAllocationItem::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            List<PreAllocationRecordDO> insertList = new ArrayList<>();
            List<PreAllocationRecordDO> updateList = new ArrayList<>();
            BigDecimal reqTouchedAmount = BigDecimal.ZERO;

            for (PreAllocationSaveRequest.PreAllocationItem item : reqItems) {
                if (item == null) {
                    continue;
                }
                if (BigDecimal.ZERO.compareTo(item.getPayAmount())>=0){
                    throw new ServiceException(ErrorCodeEnum.AMOUNT_GEN_ZERO);
                }
                Long id = item.getId();
                if (id == null) {
                    insertList.add(PreAllocationRecordDO.builder()
                            .orderId(req.getOrderId())
                            .shopId(req.getShopId())
                            .expenseType(item.getExpenseType())
                            .payeeName(item.getPayeeName())
                            .payeeCode(item.getPayeeCode())
                            .payAmount(item.getPayAmount())
                            .allocationStatus(AllocationPayStatusEnum.UNPAID.getStatus())
                            .build());
                    reqTouchedAmount = reqTouchedAmount.add(item.getPayAmount() == null ? BigDecimal.ZERO : item.getPayAmount());
                    continue;
                }

                PreAllocationRecordDO exist = existMap.get(id);
                if (exist == null) {
                    throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
                }
                if (!Objects.equals(exist.getShopId(), req.getShopId()) || !Objects.equals(exist.getOrderId(), req.getOrderId())) {
                    throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
                }

                //已支付
                if (Objects.equals(exist.getAllocationStatus(), 1)) {
                    reqTouchedAmount = reqTouchedAmount.add(exist.getPayAmount() == null ? BigDecimal.ZERO : exist.getPayAmount());
                    continue;
                }

                exist.setExpenseType(item.getExpenseType());
                exist.setPayeeName(item.getPayeeName());
                exist.setPayeeCode(item.getPayeeCode());
                exist.setPayAmount(item.getPayAmount());
                updateList.add(exist);
                reqTouchedAmount = reqTouchedAmount.add(item.getPayAmount() == null ? BigDecimal.ZERO : item.getPayAmount());
            }

            List<PreAllocationRecordDO> removedList = existList.stream()
                    .filter(v -> v != null && v.getId() != null && !reqIdSet.contains(v.getId()))
                    .collect(Collectors.toList());

            BigDecimal paidRemovedAmount = removedList.stream()
                    .filter(v -> Objects.equals(v.getAllocationStatus(), AllocationPayStatusEnum.PAID.getStatus()))
                    .map(v -> v.getPayAmount() == null ? BigDecimal.ZERO : v.getPayAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal finalAmount = reqTouchedAmount.add(paidRemovedAmount);
            if (finalAmount.compareTo(totalAmount) != 0) {
                throw new ServiceException(ErrorCodeEnum.FEE_NOT_CONSISTENT);
            }

            if (CollectionUtils.isNotEmpty(updateList)) {
                dao.batchUpdateEditableFields(updateList);
            }
            if (CollectionUtils.isNotEmpty(insertList)) {
                dao.insertBatch(insertList);
            }

            List<Long> removeIds = removedList.stream()
                    .filter(v -> !Objects.equals(v.getAllocationStatus(), AllocationPayStatusEnum.PAID.getStatus()))
                    .map(PreAllocationRecordDO::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(removeIds)) {
                dao.deleteByIdsNotPaid(removeIds);
            }

            //分账
            batchTrans(req,storeOrder);
            return Boolean.TRUE;
        }finally{
            redisUtil.unlock(lockKey);
        }
    }


    @Transactional
    public void batchTrans(PreAllocationSaveRequest req,StoreOrderDO storeOrder){
        log.info("batchTrans:{}", JSONObject.toJSONString(req));
        LineInfoDO lineInfo = lineInfoDAO.getLineInfo(storeOrder.getLineId());
        //分账
        List<String> walletFeeItemBySubStage = WalletFeeItemEnum.getWalletFeeItemBySubStage(req.getSubStage());
        List<PreAllocationRecordDO> newAllocationList = dao.queryPageByShopId(req.getShopId());
        //待分账数据
        List<PreAllocationRecordDO> list = newAllocationList.stream().
                filter(x -> x.getExpenseType() != null && walletFeeItemBySubStage.contains(x.getExpenseType()) && x.getAllocationStatus() == AllocationPayStatusEnum.UNPAID.getStatus()
                ).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(list)){
            ShopInfoDO shopInfo = shopInfoDAO.getShopInfo(storeOrder.getShopId());
            BatchTransferRequest batchTransferRequest = new BatchTransferRequest();
            batchTransferRequest.setOutStoreId(shopInfo.getStoreId());
            batchTransferRequest.setVerifyPassword(1);
            batchTransferRequest.setWalletType(2);

            List<BatchTransferRequest.TransDataRequest> transArray = new ArrayList<>();
            //
            BigDecimal currentAmount = new BigDecimal("0");
            for (PreAllocationRecordDO record : list) {
                BatchTransferRequest.TransDataRequest transData = new BatchTransferRequest.TransDataRequest();

                // payNo 必须唯一且失败重试要沿用第一次的 payNo
                String payNo = record.getPayNo();
                if (StringUtils.isBlank(payNo)) {
                    payNo = GenerateNoUtil.generateMillsNoWithRandom();
                    record.setPayNo(payNo);
                }
                transData.setReqNo(payNo);

                // 根据费用类型获取费用科目ID
                WalletFeeItemEnum feeItem = WalletFeeItemEnum.getByExpenseType(record.getExpenseType());
                if (feeItem == null) {
                    throw new ServiceException(ErrorCodeEnum.NONSUPPORT_EXPENSE_TYPE);
                }
                transData.setFeeItemId(feeItem.getFeeItemId());
                transData.setCompanyCode(record.getPayeeCode());
                transData.setAmount(record.getPayAmount().toString());

                currentAmount = currentAmount.add(record.getPayAmount());

                transArray.add(transData);
            }

            if (StringUtil.isNotEmpty(shopInfo.getStoreId())){
                List<AccountInfoDTO> accountInfo = walletApiService.getAccountInfo(new OutStoreIdRequest(shopInfo.getStoreId()));
                if (CollectionUtils.isNotEmpty(accountInfo)){
                    AccountInfoDTO accountInfoDTO = accountInfo.get(0);
                    String totalAmount = accountInfoDTO.getTotalAmount();
                    BigDecimal total = new BigDecimal(totalAmount);
                    //校验本次缴纳金额
                    if (total.compareTo(currentAmount)<0) {
                        throw new ServiceException(ErrorCodeEnum.NOT_ENOUGH_BALANCE);
                    }
                    //首次缴款 校验总金额
                    log.info("batchTrans total:{},orderAmount",total,storeOrder.getTotalAmount());
                    if (req.getSubStage()==ShopSubStageEnum.SHOP_STAGE_28.getShopSubStage()){
                        if (total.compareTo(storeOrder.getTotalAmount())<0) {
                            throw new ServiceException(ErrorCodeEnum.NOT_ENOUGH_BALANCE);
                        }
                    }

                }
            }

            batchTransferRequest.setTransArray(transArray);
            Map<String, PreAllocationRecordDO> map = list.stream().collect(Collectors.toMap(PreAllocationRecordDO::getPayNo, data -> data));
            //// 调用第三方前先固化 payNo（失败重试需要复用）
            preAllocationRecordDAO.updateBatch(list);
            BatchTransferDTO batchTransferResult;
            try {
                batchTransferResult = walletApiService.batchTransfer(batchTransferRequest);
            } catch (Exception e) {
                throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR.getCode(), "支付失败：" + e.getMessage());
            }
            // 11. 处理分账结果
            if (batchTransferResult == null || batchTransferResult.getTradeStatus() == null) {
                throw new ServiceException(ErrorCodeEnum.THIRD_API_ERROR.getCode(), "支付失败：分账接口返回异常");
            }

            List<WalletPayInfoDO> walletPayInfoList = new ArrayList<>();
            List<WalletTradeDO> walletTradeList = new ArrayList<>();
            List<PreAllocationRecordDO> updatedRecords = new ArrayList<>();

            //批量支付状态 1.成功 2.失败 3.处理中（网商银行有处理中）
            Integer tradeStatus = batchTransferResult.getTradeStatus();

            // 订单状态
            Integer orderStatus = null;

            // 按批次结果全成/全败
            Integer allocationStatus = tradeStatus;
            if (tradeStatus != null && tradeStatus == 1) {
                orderStatus = StoreOrderStatusEnum.PART_OF_WAIT_PAY.getCode();
            }

            // 无论成功失败，都需要落库，并更新预分账状态
            if (batchTransferResult.getTransArray() != null) {
                for (int i = 0; i < batchTransferResult.getTransArray().size() && i < list.size(); i++) {
                    BatchTransferDTO.TransDataRequest result = batchTransferResult.getTransArray().get(i);
                    PreAllocationRecordDO record = map.get(result.getReqNo());
                    if (record == null) {
                        continue;
                    }

                    record.setAllocationStatus(allocationStatus);
                    updatedRecords.add(record);

                    WalletPayInfoDO walletPayInfo = new WalletPayInfoDO();
                    walletPayInfo.setShopId(storeOrder.getShopId());
                    walletPayInfo.setModule(STANDARD_STORE.getModule());
                    walletPayInfo.setPayNo(result.getReqNo());
                    walletPayInfo.setPayUserName(lineInfo.getUsername());
                    walletPayInfo.setPayAmount(new BigDecimal(result.getAmount()));
                    walletPayInfo.setPayStatus(tradeStatus);
                    walletPayInfo.setPayTime(new Date());
                    walletPayInfo.setClaimStatus(ClaimStatusEnum.TO_BE_CLAIMED.getCode());
                    walletPayInfo.setExpenseTypes(record.getExpenseType());
                    walletPayInfo.setCreateTime(new Date());
                    walletPayInfo.setTradeId(result.getTradeId());
                    walletPayInfo.setBatchCode(batchTransferResult.getBatchCode());
                    walletPayInfoList.add(walletPayInfo);

                    WalletTradeDO walletTrade = new WalletTradeDO();
                    walletTrade.setModule(STANDARD_STORE.getModule());
                    walletTrade.setPayNo(result.getReqNo());
                    walletTrade.setTradeId(result.getTradeId());
                    walletTrade.setType(1);
                    walletTrade.setPayUserName(lineInfo.getUsername());
                    walletTrade.setPayAmount(new BigDecimal(result.getAmount()));
                    walletTrade.setPayStatus(tradeStatus);
                    walletTrade.setPayTime(new Date());
                    walletTrade.setBatchCode(batchTransferResult.getBatchCode());
                    walletTrade.setCreateTime(new Date());
                    walletTradeList.add(walletTrade);
                }
            }

            // 13. 保存钱包支付信息/交易单：payNo 唯一索引，存在则更新，不存在则插入（批量处理）
            List<String> payNoList = walletPayInfoList.stream()
                    .map(WalletPayInfoDO::getPayNo)
                    .filter(StringUtils::isNotBlank)
                    .distinct()
                    .collect(Collectors.toList());

            Set<String> existPayNoSet = new HashSet<>();
            if (CollectionUtils.isNotEmpty(payNoList)) {
                List<WalletPayInfoDO> existList = walletPayInfoDAO.getByPayNoList(payNoList);
                if (CollectionUtils.isNotEmpty(existList)) {
                    existPayNoSet = existList.stream().map(WalletPayInfoDO::getPayNo).collect(Collectors.toSet());
                }
            }

            List<WalletPayInfoDO> walletPayInfoInsertList = new ArrayList<>();
            List<WalletPayInfoDO> walletPayInfoUpdateList = new ArrayList<>();
            for (WalletPayInfoDO v : walletPayInfoList) {
                if (v == null || StringUtils.isBlank(v.getPayNo())) {
                    continue;
                }
                if (existPayNoSet.contains(v.getPayNo())) {
                    walletPayInfoUpdateList.add(v);
                } else {
                    walletPayInfoInsertList.add(v);
                }
            }

            if (CollectionUtils.isNotEmpty(walletPayInfoInsertList)) {
                walletPayInfoDAO.insertBatch(walletPayInfoInsertList);
            }
            if (CollectionUtils.isNotEmpty(walletPayInfoUpdateList)) {
                walletPayInfoDAO.updateBatchByPayNo(walletPayInfoUpdateList);
            }

            // WalletTrade：同样按 payNo 分批新增/更新
            List<WalletTradeDO> walletTradeInsertList = new ArrayList<>();
            List<WalletTradeDO> walletTradeUpdateList = new ArrayList<>();
            for (WalletTradeDO v : walletTradeList) {
                if (v == null || StringUtils.isBlank(v.getPayNo())) {
                    continue;
                }
                if (existPayNoSet.contains(v.getPayNo())) {
                    walletTradeUpdateList.add(v);
                } else {
                    walletTradeInsertList.add(v);
                }
            }

            if (CollectionUtils.isNotEmpty(walletTradeInsertList)) {
                walletTradeDAO.insertBatch(walletTradeInsertList);
            }
            if (CollectionUtils.isNotEmpty(walletTradeUpdateList)) {
                walletTradeDAO.updateBatchByPayNo(walletTradeUpdateList);
            }

            // 更新预分账明细
            if (CollectionUtils.isNotEmpty(updatedRecords)) {
                preAllocationRecordDAO.updateBatch(updatedRecords);
            }

            StoreOrderDO updateOrder = StoreOrderDO.builder()
                    .id(storeOrder.getId())
                    .status(orderStatus)
                    .updateTime(new Date())
                    .build();

            if (batchTransferResult.getTradeStatus()==1) {
                List<WalletPayInfoDO> walletPayInfoDOS = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(walletPayInfoInsertList)){
                    walletPayInfoDOS.addAll(walletPayInfoInsertList);
                }
                if (CollectionUtils.isNotEmpty(walletPayInfoUpdateList)){
                    walletPayInfoDOS.addAll(walletPayInfoUpdateList);
                }
                this.pushReceiptRequest(walletPayInfoUpdateList,shopInfo,storeOrder);

                //对账中
                shopStageInfoDAO.batchUpdateShopStageStatus(storeOrder.getShopId(),Arrays.asList(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_284_5));

                //当前缴纳金额 = 本次缴纳前已缴纳金额+本次缴纳金额
                BigDecimal paidAmount = storeOrder.getPaidAmount().add(currentAmount);
                BigDecimal unpaidAmount = storeOrder.getTotalAmount().subtract(paidAmount);
                //支付成功 修改金额，支付中 回调的时候习惯
                updateOrder.setPaidAmount(paidAmount);
                updateOrder.setUnpaidAmount(unpaidAmount);
                updateOrder.setPayTime(new Date());
                if (unpaidAmount.compareTo(new BigDecimal(0))==0){
                    //剩余缴纳金额是0 缴费完成
                    updateOrder.setStatus( StoreOrderStatusEnum.PAID.getCode());
                }
            }
            storeOrderDAO.updateSelective(updateOrder);
        }
    }

    private void pushReceiptRequest(List<WalletPayInfoDO> payInfoList, ShopInfoDO shopInfoDO,StoreOrderDO storeOrder) {
        if (CollectionUtils.isEmpty(payInfoList)) {
            return;
        }
        FranchiseFeeDO franchiseFeeDO = franchiseFeeDAO.selectByShopId(shopInfoDO.getId());
        Integer feeId = null;
        if (Objects.isNull(franchiseFeeDO)){
            feeId = this.pushStandardStoreFee(storeOrder);
        }else {
            feeId = franchiseFeeDO.getId().intValue();
        }
        for (WalletPayInfoDO payInfo : payInfoList) {
            ReceiptRequest receiptRequest = new ReceiptRequest(shopInfoDO.getId(), feeId, payInfo, Integer.valueOf(shopInfoDO.getFranchiseBrand()));
            try {
                pushService.pushReceiptToXGJ(receiptRequest);
            } catch (Exception e) {
                log.error("钱包账单推送新管家失败, walletPayInfoId:{}", payInfo.getId(), e);
            }
        }
    }

    @Override
    @Transactional
    public void queryAndUpdateBatchTransferStatus(String batchCode) {
        BatchTransferDTO batchTransferDTO = walletApiService.queryBatchTransfer(new BatchTransferQueryRequest(batchCode));
        // 根据批次号更新交易单
        walletTradeDAO.updatePayStatusByBatchCode(batchCode, batchTransferDTO.getTradeStatus());
        // 根据批次号更新业务单
        List<WalletPayInfoDO> payInfoList = walletPayInfoDAO.getByBatchCode(batchCode);
        if (CollectionUtils.isEmpty(payInfoList)) {
            log.error("查询并更新批量转账状态异常，业务单为空");
            return;
        }
        walletPayInfoDAO.updatePayStatusByBatchCode(batchCode, batchTransferDTO.getTradeStatus());
        // 更新分账信息
        Long shopId = payInfoList.get(0).getShopId();
        List<String> payNoList = batchTransferDTO.getTransArray().stream().map(BatchTransferDTO.TransDataRequest::getReqNo).collect(Collectors.toList());
        //分账状态调整
        preAllocationRecordDAO.updateByPayNoList(payNoList,batchTransferDTO.getTradeStatus());
        // 校验并流转流程
        if (AllocationPayStatusEnum.PAID.getStatus().equals(batchTransferDTO.getTradeStatus())) {
            ShopInfoDO shopInfo = shopInfoDAO.getShopInfo(shopId);
            // 校验并流转流程
            shopStageInfoDAO.updateShopStageInfo(shopId, ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_284_5);
            //订单状态更新

            StoreOrderDO storeOrder = storeOrderDAO.getLatestOrderByShopId(shopId);
            //部分支付
            StoreOrderDO updateOrder = StoreOrderDO.builder()
                    .id(storeOrder.getId())
                    .status(StoreOrderStatusEnum.PART_OF_WAIT_PAY.getCode())
                    .updateTime(new Date())
                    .build();
            //当前缴纳金额 = 本次缴纳前已缴纳金额+本次缴纳金额
            BigDecimal currentAmount = sumAmount(payInfoList);
            BigDecimal paidAmount = storeOrder.getPaidAmount().add(currentAmount);
            BigDecimal unpaidAmount = storeOrder.getTotalAmount().subtract(paidAmount);
            //支付成功 修改金额，支付中 回调的时候习惯
            updateOrder.setPaidAmount(paidAmount);
            updateOrder.setUnpaidAmount(unpaidAmount);
            updateOrder.setPayTime(new Date());
            if (unpaidAmount.compareTo(new BigDecimal(0))==0){
                //剩余缴纳金额是0 缴费完成
                updateOrder.setStatus( StoreOrderStatusEnum.PAID.getCode());
            }
            storeOrderDAO.updateSelective(updateOrder);
            // 推送缴费单数据到新管家
            if (CollectionUtils.isNotEmpty(payInfoList)) {
                pushReceiptRequest(payInfoList, shopInfo,storeOrder);
            }

        }

    }

    @Override
    public List<PreAllocationRecordVO> queryByShop(Long shopId){
        List<PreAllocationRecordDO> list = dao.queryPageByShopId(shopId);
        if (CollectionUtils.isEmpty(list)) {
            StoreOrderDO storeOrder = storeOrderDAO.getLatestOrderByShopId(shopId);
            if (storeOrder == null) {
                return new ArrayList<>();
            }

            // 获取基础费用类型配置
            List<String> baseExpenseTypeList = WalletFeeItemEnum.getBaseExpenseType().stream()
                    .map(WalletFeeItemEnum::getExpenseType)
                    .collect(Collectors.toList());

            // 获取费用配置映射
            Map<String, WalletAllocationDTO> allocationConfigMap = walletAllocationConfigDAO
                    .selectByExpenseTypeList(FranchiseBrandEnum.ZXJP.getCode(), null)
                    .stream()
                    .collect(Collectors.toMap(WalletAllocationDTO::getExpenseType, Function.identity(),(existing, replacement) -> existing));

            // 获取订单实际费用明细
            List<ExpenseTypeAmountDTO> actualExpenseList = storeOrderOptionSnapshotDAO
                    .getExpenseTypeAmountByOrderId(storeOrder.getId());

            // 构建已存在的费用类型集合
            Set<String> processedExpenseTypes = new HashSet<>();
            // 构建结果集
            List<PreAllocationRecordVO> result = new ArrayList<>();

            // 处理实际费用明细
            for (ExpenseTypeAmountDTO expense : actualExpenseList) {
                String expenseType = expense.getExpenseType();
                BigDecimal payAmount;

                if (processedExpenseTypes.contains(expenseType)) {
                    // 重复的费用类型，金额设为0
                    payAmount = BigDecimal.ZERO;
                } else {
                    payAmount = expense.getTotalAmount();
                    processedExpenseTypes.add(expenseType);
                }

                PreAllocationRecordVO record = buildPreAllocationRecord(
                        storeOrder, shopId, expenseType, payAmount, allocationConfigMap);
                result.add(record);
            }

            // 处理配置中但订单中没有的费用
            for (String expenseType : baseExpenseTypeList) {

                // 如果已经处理过，金额设为0（继续添加）
                if (processedExpenseTypes.contains(expenseType)) {
                    PreAllocationRecordVO record = buildPreAllocationRecord(
                            storeOrder, shopId, expenseType, BigDecimal.ZERO, allocationConfigMap);
                    result.add(record);
                    continue;
                }

                // 未处理过的，从订单中获取金额
                BigDecimal amount = getAmountFromOrderByExpenseType(storeOrder, expenseType);
                PreAllocationRecordVO record = buildPreAllocationRecord(
                        storeOrder, shopId, expenseType, amount, allocationConfigMap);
                result.add(record);
                processedExpenseTypes.add(expenseType);
            }
            return result;
        }
        List<String> payNoList = list.stream().filter(x-> StringUtil.isNotEmpty(x.getPayNo())).map(PreAllocationRecordDO::getPayNo).collect(Collectors.toList());
        Map<String, WalletPayInfoDO> map =  new HashMap<>();
        if (CollectionUtils.isNotEmpty(payNoList)){
            List<WalletPayInfoDO> wallet = walletPayInfoDAO.getByPayNoList(payNoList);
            map = wallet.stream().collect(Collectors.toMap(WalletPayInfoDO::getPayNo, data -> data));
        }

        List<PreAllocationRecordVO> preAllocationRecordVOS = BeanUtil.copyToList(list, PreAllocationRecordVO.class);
        for (PreAllocationRecordVO preAllocationRecordVO : preAllocationRecordVOS) {
            WalletPayInfoDO walletPayInfoDO = map.get(preAllocationRecordVO.getPayNo());
            if (!Objects.isNull(walletPayInfoDO)){
                preAllocationRecordVO.setClaimStatus(walletPayInfoDO.getClaimStatus());
            }
        }
        dictService.fillDictField(preAllocationRecordVOS);
        return preAllocationRecordVOS;
    }




    /**
     * 构建预分配记录
     */
    private PreAllocationRecordVO buildPreAllocationRecord(
            StoreOrderDO storeOrder, Long shopId, String expenseType,
            BigDecimal payAmount, Map<String, WalletAllocationDTO> allocationConfigMap) {

        PreAllocationRecordVO record = new PreAllocationRecordVO();
        record.setShopId(shopId);
        record.setOrderId(storeOrder.getId());
        record.setExpenseType(expenseType);
        record.setExpenseTypeName(WalletFeeItemEnum.getByExpenseTypeName(expenseType));
        record.setPayAmount(payAmount);
        record.setClaimStatus(ClaimStatusEnum.TO_BE_CLAIMED.getCode());
        record.setAllocationStatus(AllocationPayStatusEnum.UNPAID.getStatus());

        // 设置收款方信息
        WalletAllocationDTO allocation = allocationConfigMap.get(expenseType);
        if (allocation != null) {
            record.setPayeeName(allocation.getPayeeName());
            record.setPayeeCode(allocation.getPayeeCode());
        }
        dictService.fillDictField(record);

        return record;
    }

    /**
     * 根据费用类型从订单中获取金额
     */
    private BigDecimal getAmountFromOrderByExpenseType(StoreOrderDO storeOrder, String expenseType) {
        if (WalletFeeItemEnum.FRANCHISE_FEE.getExpenseType().equals(expenseType)) {
            return storeOrder.getFranchiseFee();
        }
        if (WalletFeeItemEnum.MANAGEMENT_FEE.getExpenseType().equals(expenseType)) {
            return storeOrder.getManagementFee();
        }
        if (WalletFeeItemEnum.BRAND_USAGE_FEE.getExpenseType().equals(expenseType)) {
            return storeOrder.getBrandUsageFee();
        }
        if (WalletFeeItemEnum.DESIGN_FEE_GROUP.getExpenseType().equals(expenseType)) {
            return storeOrder.getDesignFee();
        }
        if (WalletFeeItemEnum.EARNEST_MONEY.getExpenseType().equals(expenseType)) {
            return storeOrder.getContractDeposit();
        }
        if (WalletFeeItemEnum.DEVICE_EARNEST_MONEY.getExpenseType().equals(expenseType)) {
            return storeOrder.getDeviceDeposit();
        }
        return BigDecimal.ZERO;
    }

    public BigDecimal sumAmount(List<WalletPayInfoDO> list) {
        if (list == null || list.isEmpty()) {
            return BigDecimal.ZERO;
        }

        // 方法1：使用 map 和 reduce
        return list.stream()
                .map(WalletPayInfoDO::getPayAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Override
    public List<PreAllocationRecordDO> listDOByShop(Long shopId) {
        return dao.queryPageByShopId(shopId);
    }


    @Override
    public Integer pushStandardStoreFee(StoreOrderDO order) {
        FranchiseFeeDO franchiseFeeDO = new  FranchiseFeeDO();
        franchiseFeeDO.setXgjCollectionStatus(XGJCollectionStatusEnum.WAIT_PAY.getCode());
        franchiseFeeDO.setCreateTime(new Date());
        franchiseFeeDO.setShopId(order.getShopId());
        //加盟费
        franchiseFeeDO.setYearFranchiseFee(order.getFranchiseFee().stripTrailingZeros().toPlainString());
        //保证金
        franchiseFeeDO.setLoanMargin(order.getContractDeposit().stripTrailingZeros().toPlainString());
        //品牌使用费
        franchiseFeeDO.setFirstYearFee(order.getBrandUsageFee().stripTrailingZeros().toPlainString());
        //管理费
        franchiseFeeDO.setFirstYearManageFee(order.getManagementFee().stripTrailingZeros().toPlainString());
        //设计费
        franchiseFeeDO.setPerformanceBond(order.getDesignFee().stripTrailingZeros().toPlainString());
        franchiseFeeDO.setDiscountReason("标准AI店优惠");
        //系统使用费
        franchiseFeeDO.setCashierFee(order.getSystemUsageFee().stripTrailingZeros().toPlainString());
        franchiseFeeDO.setPayType(PayTypeEnum.PING_AN.getType());
        franchiseFeeMapper.insertSelective(franchiseFeeDO);
        ShopInfoDO shopInfoDO = shopInfoDAO.getShopInfo(order.getShopId());
        LineInfoDO lineInfo = lineInfoDAO.getLineInfo(shopInfoDO.getLineId());
        PushFranchiseFeeRequest feeRequest = new PushFranchiseFeeRequest(shopInfoDO.getId(), lineInfo.getUsername(), franchiseFeeDO,Integer.valueOf(shopInfoDO.getFranchiseBrand()));
        feeRequest.setJoinBrand(Integer.valueOf(shopInfoDO.getFranchiseBrand()));
        pushService.pushFranchiseFeeToXGJ(feeRequest);
        return franchiseFeeDO.getId().intValue();
    }

    @Override
    public Boolean trans(TransRequest transRequest) {
        String lockKey = MessageFormat.format(RedisConstant.PRE_ALLOCATION,transRequest.getId());
        String uuid = UUID.randomUUID().toString();
        if(!Boolean.TRUE.equals(redisUtil.tryLock(lockKey, uuid,10, TimeUnit.MINUTES))){
            throw new ServiceException(ErrorCodeEnum.TRANSFER_ING);
        }
        PreAllocationRecordDO record = preAllocationRecordDAO.getById(transRequest.getId());
        if (record==null){
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }
        ShopInfoDO shopInfo = shopInfoDAO.getShopInfo(record.getShopId());
        OutStoreIdRequest outStoreIdRequest = new OutStoreIdRequest();
        outStoreIdRequest.setOutStoreId(shopInfo.getStoreId());
        List<AccountInfoDTO> accountInfoList = walletApiService.getAccountInfo(outStoreIdRequest);
        if (CollectionUtils.isEmpty(accountInfoList)){
            throw new ServiceException(ErrorCodeEnum.NOT_EXIST_WANG_SHANG_ACCOUNT);
        }
        //校验金额
        AccountInfoDTO accountInfoDTO = accountInfoList.get(0);
        String totalAmount = accountInfoDTO.getTotalAmount();
        Boolean amountSupport = BigDecimalUtils.greaterThanOrEqual(new BigDecimal(totalAmount), record.getPayAmount());
        if (!amountSupport){
            throw new ServiceException(ErrorCodeEnum.WALLET_BALANCE_INSUFFICIENT);
        }
        AccountTransferDTO accountTransferDTO ;
        try {
            accountTransferDTO =  walletPayInfoService.accountPay(record.getExpenseType(), record.getPayAmount(),
                    record.getPayeeCode(), accountInfoDTO.getAccountNo(), record.getPayNo(), transRequest.getRemark());
        }catch (Exception e){
            //分账失败 释放锁
            redisUtil.unlock(lockKey);
            log.error("confirmSplitOrder:{}",e.getMessage());
            throw new ServiceException(ErrorCodeEnum.ERROR_MESSAGE,e.getMessage());
        }
        record.setAllocationStatus(AllocationPayStatusEnum.PAYING.getStatus());
        record.setRemark(transRequest.getRemark());
        //先改为分账中
        preAllocationRecordDAO.updateByPrimaryKeySelective(record);
        WalletTradeDO walletTradeDO = new WalletTradeDO();
        walletTradeDO.setPayStatus(accountTransferDTO.getTradeStatus());
        walletTradeDO.setModule(TRANSFER.getModule());
        walletTradeDO.setRemark(transRequest.getRemark());
        walletTradeDO.setPayAmount(new BigDecimal(accountTransferDTO.getAmount()));
        walletTradeDO.setPayTime(new Date());
        walletTradeDO.setPayNo(record.getPayNo());
        walletTradeDO.setTradeId(String.valueOf(accountTransferDTO.getTradeId()));
        walletTradeDO.setType(TradeTypeEnum.ADD_BY_HAND.getStatus());
        walletTradeDAO.insertSelective(walletTradeDO);
        return null;
    }

    @Override
    public Boolean transStatusRefresh(WalletTradeDO walletTradeDO) {
        BillDetailRequest request = new BillDetailRequest();
        request.setTradeId(Long.valueOf(walletTradeDO.getTradeId()));
        TradeRecordDTO billDetail = walletApiService.getBillDetail(request);
        walletTradeDO.setPayStatus(billDetail.getTradeStatus());
        walletTradeDAO.updateByPrimaryKeySelective(walletTradeDO);
        PreAllocationRecordDO preAllocationRecordDO = preAllocationRecordDAO.queryPageByPayNo(walletTradeDO.getPayNo());
        preAllocationRecordDO.setAllocationStatus(billDetail.getTradeStatus());
        preAllocationRecordDO.setUpdateTime(new Date());
        preAllocationRecordDAO.updateByPrimaryKeySelective(preAllocationRecordDO);
        if (AllocationPayStatusEnum.PAID.getStatus().equals(walletTradeDO.getPayStatus())) {
            ShopInfoDO shopInfo = shopInfoDAO.getShopInfo(preAllocationRecordDO.getShopId());
            //订单状态更新
            StoreOrderDO storeOrder = storeOrderDAO.getById(preAllocationRecordDO.getOrderId());
            //部分支付
            StoreOrderDO updateOrder = StoreOrderDO.builder()
                    .id(storeOrder.getId())
                    .status(StoreOrderStatusEnum.PART_OF_WAIT_PAY.getCode())
                    .updateTime(new Date())
                    .build();
            //当前缴纳金额 = 本次缴纳前已缴纳金额+本次缴纳金额
            BigDecimal currentAmount = new BigDecimal(billDetail.getAmount());
            BigDecimal paidAmount = storeOrder.getPaidAmount().add(currentAmount);
            BigDecimal unpaidAmount = storeOrder.getTotalAmount().subtract(paidAmount);
            //支付成功 修改金额，支付中 回调的时候习惯
            updateOrder.setPaidAmount(paidAmount);
            updateOrder.setUnpaidAmount(unpaidAmount);
            updateOrder.setPayTime(new Date());
            if (BigDecimalUtils.equals(unpaidAmount,new BigDecimal(0))){
                //剩余缴纳金额是0 缴费完成
                updateOrder.setStatus( StoreOrderStatusEnum.PAID.getCode());
                //阶段状态变为完成
                shopStageInfoDAO.updateShopStageInfo(storeOrder.getShopId(), SHOP_SUB_STAGE_STATUS_285);
            }
            storeOrderDAO.updateSelective(updateOrder);
            //清除缓存
            String lockKey = MessageFormat.format(RedisConstant.PRE_ALLOCATION,preAllocationRecordDO.getId());
            redisUtil.unlock(lockKey);
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean add(PreAllocationAddRequest request) {
        if (request == null) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }

        PreAllocationRecordDO record = PreAllocationRecordDO.builder()
                .orderId(request.getOrderId())
                .shopId(request.getShopId())
                .expenseType(request.getExpenseType())
                .payeeName(request.getPayeeName())
                .payeeCode(request.getPayeeCode())
                .payAmount(request.getPayAmount())
                .payNo(GenerateNoUtil.generateMillsNoWithRandom())
                .remark(request.getRemark())
                .deleted(0)
                .allocationStatus(AllocationPayStatusEnum.UNPAID.getStatus())
                .createTime(new Date())
                .updateTime(new Date())
                .build();

        return dao.insertSelective(record);
    }

    @Override
    public Boolean edit(PreAllocationEditRequest request) {
        if (request == null || request.getId() == null) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }

        PreAllocationRecordDO exist = dao.getById(request.getId());
        if (exist == null) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }

        PreAllocationRecordDO record = PreAllocationRecordDO.builder()
                .id(request.getId())
                .expenseType(request.getExpenseType())
                .payeeName(request.getPayeeName())
                .payeeCode(request.getPayeeCode())
                .payAmount(request.getPayAmount())
                .remark(request.getRemark())
                .updateTime(new Date())
                .build();

        return dao.updateByPrimaryKeySelective(record);
    }

    @Override
    public Boolean delete(Long id) {
        if (id == null) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }
        return dao.softDeleteById(id);
    }


}
