wujianwei
2025-08-14 22976afc564c4b5b911026c6540ad48db43e2166
service/src/main/java/com/ruoyi/cwgl/service/impl/DispatchOrderServiceImpl.java
@@ -1,9 +1,44 @@
package com.ruoyi.cwgl.service.impl;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.PageUtil;
import com.github.pagehelper.Page;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import javax.annotation.Resource;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.file.DownloadExportUtil;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.cwgl.domain.vo.DispatchOrderVo;
import com.ruoyi.cwgl.enums.FeeTypeEnums;
import com.ruoyi.cwgl.utils.MultiPagePdfWithImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.ruoyi.cwgl.domain.DispatchOrderItem;
import com.ruoyi.cwgl.domain.vo.DispatchOrderAttachmentVo;
import com.ruoyi.cwgl.domain.vo.DispatchOrderItemVo;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async;
@@ -33,7 +68,8 @@
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private DispatchOrderMapper dispatchOrderMapper;
    @Autowired
    private RedisCache redisCache;
    /**
     * 查询调度单
@@ -73,6 +109,18 @@
    {
        return dispatchOrderMapper.selectDispatchOrderList(dispatchOrder);
    }
    /**
     * 查询调度单列表
     *
     * @param dispatchOrder 调度单
     * @return 调度单
     */
    @DataSource(DataSourceType.CWSJ)
    @Override
    public List<DispatchOrder> selectDispatchOrderList2(DispatchOrder dispatchOrder)
    {
        return dispatchOrderMapper.selectDispatchOrderList2(dispatchOrder);
    }
    /**
     * 查询调度单列表 异步 导出
@@ -81,16 +129,203 @@
     * @param exportKey 导出功能的唯一标识
     * @return 调度单集合
     */
    @DataSource(DataSourceType.SLAVE)
    @DataSource(DataSourceType.CWSJ)
    @Async
    @Override
    public void export(DispatchOrder dispatchOrder,String exportKey) {
        super.export(DispatchOrder.class,exportKey,"dispatchOrderData",(pageNum)->{
            PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE);
            return selectDispatchOrderList(dispatchOrder);
            return selectDispatchOrderList2(dispatchOrder);
        });
    }
    @DataSource(DataSourceType.CWSJ)
    @Async
    @Override
    public void export2(DispatchOrder dispatchOrder,String exportKey) {
        //查询并导出数据文件
        String fileName =selectListExport(dispatchOrder,  "台账信息");
        //设置文件缓存
        DownloadExportUtil.setDownloadFile(redisCache, exportKey, fileName);
    }
    @DataSource(DataSourceType.CWSJ)
    public String selectListExport(DispatchOrder dispatchOrder, String  fileName)
    {
         fileName =ExcelUtil.encodeFileName(fileName);
        //数据集合
        List<DispatchOrder> dataList = new ArrayList<>();
        //导出表格对象
        ExcelUtil<DispatchOrderVo> excelUtil = new ExcelUtil<>(DispatchOrderVo.class);
        excelUtil.initialize("台账信息", null, Excel.Type.EXPORT);
        List<DispatchOrderVo> exportList;//导出的数组
        int pageNum =1,i = 1;
        boolean have=true;
        //region循环分页获取数据
        while (have) {
            Page<Object> page = PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE);
            List<DispatchOrder> dispatchOrders = selectDispatchOrderList2(dispatchOrder);
            if (dispatchOrders != null && !dispatchOrders.isEmpty()) {
                // 导出当前页的数据
                exportList=structureData(dispatchOrders,i);
                excelUtil.exportExcel(exportList);
                pageNum++;
            } else {
                // 没有数据时退出
                have = false;
            }
        }
        excelUtil.finishExport(fileName);
        return fileName;
    }
    /**
     * 封装数据
     * @param list
     * @return
     */
    @DataSource(DataSourceType.CWSJ)
    private List<DispatchOrderVo> structureData(List<DispatchOrder> list,int i) {
        List<DispatchOrderVo> exportList = new ArrayList<>();
        Set<String> dispatchNos = list.stream().map(DispatchOrder::getDispatchNo).collect(Collectors.toSet());
        Map<String, List<DispatchOrderItemVo>> itemMap = selectDispatchOrderItems(dispatchNos)
                .stream().collect(Collectors.groupingBy(DispatchOrderItemVo::getDispatchNo));
        for (DispatchOrder order : list) {
            try {
                DispatchOrderVo vo = BeanUtil.copyProperties(order, DispatchOrderVo.class);
                vo.setId(i++);
                // 初始化费用结构:Map<角色_币种, Map<费用类型, 金额>>
                Map<String, Map<FeeTypeEnums, BigDecimal>> feeMap = initFeeMap();
                Map<String, BigDecimal> totalMap = initTotalMap();
                List<DispatchOrderItemVo> items = itemMap.get(order.getDispatchNo());
                if (items != null) {
                    for (DispatchOrderItemVo item : items) {
                        if (!StringUtils.isNotEmpty(item.getFeeItem()) || item.getIsSettlement() == 0) continue;
                        FeeTypeEnums feeType = FeeTypeEnums.from(item.getFeeItem());
                        String actualCurrency = Optional.ofNullable(item.getActualFeeCurrency()).orElse("CNY");
                        String receivableCurrency = Optional.ofNullable(item.getReceivableFeeCurrency()).orElse("CNY");
                        addFee(feeMap, "customer_" + actualCurrency, feeType, item.getActualFee());
                        addTotal(totalMap, "customer_" + actualCurrency, item.getActualFee());
                        addFee(feeMap, "supplier_" + receivableCurrency, feeType, item.getReceivableFee());
                        addTotal(totalMap, "supplier_" + receivableCurrency, item.getReceivableFee());
                    }
                }
                // 设置 VO 字段
                setFeeToVo(vo, feeMap, totalMap);
                exportList.add(vo);
            } catch (Exception e) {
                logger.error("数据处理失败 id:{}", order, e);
            }
        }
        return exportList;
    }
    private Map<String, Map<FeeTypeEnums, BigDecimal>> initFeeMap() {
        Map<String, Map<FeeTypeEnums, BigDecimal>> map = new HashMap<>();
        for (String key : Arrays.asList("customer_CNY", "customer_HKD", "supplier_CNY", "supplier_HKD")) {
            Map<FeeTypeEnums, BigDecimal> subMap = new EnumMap<>(FeeTypeEnums.class);
            for (FeeTypeEnums feeType : FeeTypeEnums.values()) {
                subMap.put(feeType, BigDecimal.ZERO);
            }
            map.put(key, subMap);
        }
        return map;
    }
    private Map<String, BigDecimal> initTotalMap() {
        Map<String, BigDecimal> totalMap = new HashMap<>();
        for (String key : Arrays.asList("customer_CNY", "customer_HKD", "supplier_CNY", "supplier_HKD")) {
            totalMap.put(key, BigDecimal.ZERO);
        }
        return totalMap;
    }
    private void addFee(Map<String, Map<FeeTypeEnums, BigDecimal>> map, String key, FeeTypeEnums feeType, BigDecimal amount) {
        if (amount == null) return;
        Map<FeeTypeEnums, BigDecimal> subMap = map.get(key);
        subMap.put(feeType, subMap.get(feeType).add(amount));
    }
    private void addTotal(Map<String, BigDecimal> totalMap, String key, BigDecimal amount) {
        if (amount == null) return;
        totalMap.put(key, totalMap.get(key).add(amount));
    }
    private void setFeeToVo(DispatchOrderVo vo,
                            Map<String, Map<FeeTypeEnums, BigDecimal>> map,
                            Map<String, BigDecimal> totalMap) {
        for (String role : Arrays.asList("Customer", "Supplier")) {
            for (String currency : Arrays.asList("Rmb", "Hkd")) {
                String mapKey = role.toLowerCase() + "_" + ("Rmb".equals(currency) ? "CNY" : "HKD");
                Map<FeeTypeEnums, BigDecimal> feeTypeMap = map.get(mapKey);
                for (FeeTypeEnums feeType : FeeTypeEnums.values()) {
                    String methodName = "set" + role + feeType.getFieldSuffix() + currency;
                    invokeSetter(vo, methodName, feeTypeMap.get(feeType));
                }
                // 设置 total 字段
                String totalMethod = "set" + role + "Total" + currency;
                invokeSetter(vo, totalMethod, totalMap.get(mapKey));
            }
        }
    }
    private void invokeSetter(Object obj, String methodName, BigDecimal value) {
        try {
            Method method = obj.getClass().getMethod(methodName, BigDecimal.class);
            method.invoke(obj, value);
        } catch (Exception e) {
            logger.warn("调用方法失败: {}", methodName, e);
        }
    }
    @DataSource(DataSourceType.CWSJ)
    public List<DispatchOrderItemVo> selectDispatchOrderItems( Set<String> nos) {
        // 使用Map来临时存储分组结果
        List<DispatchOrderItemVo> vos = new ArrayList<>();
        List<DispatchOrderItem> dispatchOrderItems=dispatchOrderMapper.selectDispatchOrderItemByNos(nos);
        for (DispatchOrderItem dispatchOrderItem : dispatchOrderItems) {
            HashSet<String> settableFeeItemSet = new HashSet<>(
                    Arrays.asList(Optional.ofNullable(dispatchOrderItem.getSettableFeeItems()).orElse("").split(",")));
            // 判断是否可结算
            String feeItem = Optional.ofNullable(dispatchOrderItem.getFeeItem()).orElse("");
            int isSettable = settableFeeItemSet.contains(feeItem)?1:0;
            // 如果Map中不存在该费用类型,则创建新条目
                DispatchOrderItemVo summary = new DispatchOrderItemVo();
                summary.setDispatchNo(dispatchOrderItem.getDispatchNo());
                summary.setFeeItem(feeItem);
                summary.setReceivableFee(BigDecimal.ZERO);
                summary.setActualFee(BigDecimal.ZERO);
                summary.setIsSettlement(isSettable);
            if ("R".equals(dispatchOrderItem.getBillType())) { // 应收
                summary.setReceivableFee(dispatchOrderItem.getSettleFee());
                summary.setReceivableFeeCurrency(dispatchOrderItem.getCurrencyType());
            } else if ("P".equals(dispatchOrderItem.getBillType())) { // 应付
                summary.setActualFee(dispatchOrderItem.getSettleFee());
                summary.setActualFeeCurrency(dispatchOrderItem.getCurrencyType());
            }
            vos.add( summary);
        }
        return vos;
    }
    /**
@@ -179,4 +414,88 @@
    {
        return dispatchOrderMapper.deleteDispatchOrderById(id);
    }
    @DataSource(DataSourceType.CWSJ)
    @Override
    public List<DispatchOrderItemVo> selectDispatchOrderItem(String no) {
        // 使用Map来临时存储分组结果
        Map<String, DispatchOrderItemVo> summaryMap = new HashMap<>();
        List<DispatchOrderItem> dispatchOrderItems=dispatchOrderMapper.selectDispatchOrderItemByNo(no);
        Set<String> settableFeeItemSet = null;
        for (DispatchOrderItem dispatchOrderItem : dispatchOrderItems) {
            String settableFeeItems = dispatchOrderItem.getSettableFeeItems();
            if (settableFeeItemSet==null) {
                settableFeeItemSet = new HashSet<>(
                        Arrays.asList(settableFeeItems.split(",")));
            }
            // 判断是否可结算
            String feeItem = dispatchOrderItem.getFeeItem();
            int isSettable = isFeeItemSettable(feeItem, settableFeeItemSet);
            // 如果Map中不存在该费用类型,则创建新条目
            if (!summaryMap.containsKey(feeItem)) {
                DispatchOrderItemVo summary = new DispatchOrderItemVo();
                summary.setFeeItem(feeItem);
                summary.setReceivableFee(BigDecimal.ZERO);
                summary.setActualFee(BigDecimal.ZERO);
                summary.setIsSettlement(isSettable);
                summaryMap.put(feeItem, summary);
            }
            // 根据账单类型累加金额
            DispatchOrderItemVo summary = summaryMap.get(feeItem);
            if ("R".equals(dispatchOrderItem.getBillType())) { // 应收
                summary.setReceivableFee(summary.getReceivableFee().add(dispatchOrderItem.getSettleFee()));
                summary.setReceivableFeeCurrency(dispatchOrderItem.getCurrencyType());
            } else if ("P".equals(dispatchOrderItem.getBillType())) { // 应付
                summary.setActualFee(summary.getActualFee().add(dispatchOrderItem.getSettleFee()));
                summary.setActualFeeCurrency(dispatchOrderItem.getCurrencyType());
            }
        }
           // 转换为List并过滤、排序
        return summaryMap.values().stream()
                // 过滤掉应收应付都为0的记录
                .filter(dto -> dto.getReceivableFee().compareTo(BigDecimal.ZERO) != 0
                        || dto.getActualFee().compareTo(BigDecimal.ZERO) != 0)
                // 排序:可结算的在前,不可结算的在后;然后按总金额降序
                .sorted(Comparator.comparing(DispatchOrderItemVo::getIsSettlement).reversed()
                        .thenComparing(dto -> dto.getReceivableFee().add(dto.getActualFee()),
                                Comparator.reverseOrder()))
                .collect(Collectors.toList());
    }
    /**
     * 判断费用类型是否可结算
     */
    private static int isFeeItemSettable(String feeItem, Set<String> settableFeeItemSet) {
        if (feeItem == null || settableFeeItemSet == null) {
            return 0;
        }
            if (settableFeeItemSet.contains(feeItem)) {
                return 1;
            }
        return 0;
    }
    @DataSource(DataSourceType.CWSJ)
    @Override
    public List<DispatchOrderAttachmentVo> selectDispatchOrderAttachment(String no) {
        return dispatchOrderMapper.selectDispatchOrderAttachment(no);
    }
    @DataSource(DataSourceType.CWSJ)
    @Override
    public String  downAttachment(String no) throws Exception {
        List<DispatchOrderAttachmentVo> dispatchOrderAttachmentVos = selectDispatchOrderAttachment(no);
        return MultiPagePdfWithImageUtils.createPdf(dispatchOrderAttachmentVos, no);
    }
}