package com.ruoyi.cwgl.service.impl; import java.lang.reflect.Method; 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.ruoyi.common.annotation.Excel; 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 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.annotation.DataSource; import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.core.service.BaseService; import com.ruoyi.cwgl.mapper.DispatchOrderMapper; import com.ruoyi.cwgl.domain.DispatchOrder; import com.ruoyi.cwgl.service.IDispatchOrderService; import com.ruoyi.common.core.text.Convert; /** * 调度单Service业务层处理 * * @author ruoyi * @date 2025-07-29 */ @Service @Transactional(rollbackFor = Exception.class) public class DispatchOrderServiceImpl extends BaseService implements IDispatchOrderService { protected final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private DispatchOrderMapper dispatchOrderMapper; @Autowired private RedisCache redisCache; /** * 查询调度单 * * @param id 调度单ID * @return 调度单 */ @DataSource(DataSourceType.SLAVE) @Override public DispatchOrder selectDispatchOrderById(Integer id) { return dispatchOrderMapper.selectDispatchOrderById(id); } /** * 查询调度单 记录数 * * @param dispatchOrder 调度单 * @return 调度单集合 */ @DataSource(DataSourceType.SLAVE) @Override public int selectDispatchOrderCount(DispatchOrder dispatchOrder) { return dispatchOrderMapper.selectDispatchOrderCount(dispatchOrder); } /** * 查询调度单列表 * * @param dispatchOrder 调度单 * @return 调度单 */ @DataSource(DataSourceType.SLAVE) @Override public List selectDispatchOrderList(DispatchOrder dispatchOrder) { return dispatchOrderMapper.selectDispatchOrderList(dispatchOrder); } /** * 查询调度单列表 * * @param dispatchOrder 调度单 * @return 调度单 */ @DataSource(DataSourceType.CWSJ) @Override public List selectDispatchOrderList2(DispatchOrder dispatchOrder) { return dispatchOrderMapper.selectDispatchOrderList2(dispatchOrder); } /** * 查询调度单列表 异步 导出 * * @param dispatchOrder 调度单 * @param exportKey 导出功能的唯一标识 * @return 调度单集合 */ @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 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 dataList = new ArrayList<>(); //导出表格对象 ExcelUtil excelUtil = new ExcelUtil<>(DispatchOrderVo.class); excelUtil.initialize("台账信息", null, Excel.Type.EXPORT); List exportList;//导出的数组 int pageNum =1,i = 1; boolean have=true; //region循环分页获取数据 while (have) { Page page = PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE); List 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 structureData(List list,int i) { List exportList = new ArrayList<>(); Set dispatchNos = list.stream().map(DispatchOrder::getDispatchNo).collect(Collectors.toSet()); Map> 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> feeMap = initFeeMap(); Map totalMap = initTotalMap(); List 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> initFeeMap() { Map> map = new HashMap<>(); for (String key : Arrays.asList("customer_CNY", "customer_HKD", "supplier_CNY", "supplier_HKD")) { Map subMap = new EnumMap<>(FeeTypeEnums.class); for (FeeTypeEnums feeType : FeeTypeEnums.values()) { subMap.put(feeType, BigDecimal.ZERO); } map.put(key, subMap); } return map; } private Map initTotalMap() { Map 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> map, String key, FeeTypeEnums feeType, BigDecimal amount) { if (amount == null) return; Map subMap = map.get(key); subMap.put(feeType, subMap.get(feeType).add(amount)); } private void addTotal(Map totalMap, String key, BigDecimal amount) { if (amount == null) return; totalMap.put(key, totalMap.get(key).add(amount)); } private void setFeeToVo(DispatchOrderVo vo, Map> map, Map 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 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 selectDispatchOrderItems( Set nos) { // 使用Map来临时存储分组结果 List vos = new ArrayList<>(); List dispatchOrderItems=dispatchOrderMapper.selectDispatchOrderItemByNos(nos); for (DispatchOrderItem dispatchOrderItem : dispatchOrderItems) { HashSet 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; } /** * 新增调度单 * * @param dispatchOrder 调度单 * @return 结果 */ @Override public int insertDispatchOrder(DispatchOrder dispatchOrder) { dispatchOrder.setCreateTime(DateUtils.getNowDate()); return dispatchOrderMapper.insertDispatchOrder(dispatchOrder); } /** * 新增调度单[批量] * * @param dispatchOrders 调度单 * @return 结果 */ @Override public int insertDispatchOrderBatch(List dispatchOrders) { int rows = dispatchOrderMapper.insertDispatchOrderBatch(dispatchOrders); return rows; } /** * 修改调度单 * * @param dispatchOrder 调度单 * @return 结果 */ @Override public int updateDispatchOrder(DispatchOrder dispatchOrder) { dispatchOrder.setUpdateTime(DateUtils.getNowDate()); return dispatchOrderMapper.updateDispatchOrder(dispatchOrder); } /** * 修改调度单[批量] * * @param dispatchOrders 调度单 * @return 结果 */ @Override public int updateDispatchOrderBatch(List dispatchOrders){ return dispatchOrderMapper.updateDispatchOrderBatch(dispatchOrders); } /** * 删除调度单对象 * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteDispatchOrderByIds(String ids) { return deleteDispatchOrderByIds(Convert.toIntArray(ids)); } /** * 删除调度单对象 * * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteDispatchOrderByIds(Integer[] ids) { return dispatchOrderMapper.deleteDispatchOrderByIds(ids); } /** * 删除调度单信息 * * @param id 调度单ID * @return 结果 */ @Override public int deleteDispatchOrderById(Integer id) { return dispatchOrderMapper.deleteDispatchOrderById(id); } @DataSource(DataSourceType.CWSJ) @Override public List selectDispatchOrderItem(String no) { // 使用Map来临时存储分组结果 Map summaryMap = new HashMap<>(); List dispatchOrderItems=dispatchOrderMapper.selectDispatchOrderItemByNo(no); Set 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 settableFeeItemSet) { if (feeItem == null || settableFeeItemSet == null) { return 0; } if (settableFeeItemSet.contains(feeItem)) { return 1; } return 0; } @DataSource(DataSourceType.CWSJ) @Override public List selectDispatchOrderAttachment(String no) { return dispatchOrderMapper.selectDispatchOrderAttachment(no); } }