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;
|
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<DispatchOrderMapper, DispatchOrder> 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<DispatchOrder> selectDispatchOrderList(DispatchOrder dispatchOrder)
|
{
|
return dispatchOrderMapper.selectDispatchOrderList(dispatchOrder);
|
}
|
/**
|
* 查询调度单列表
|
*
|
* @param dispatchOrder 调度单
|
* @return 调度单
|
*/
|
@DataSource(DataSourceType.CWSJ)
|
@Override
|
public List<DispatchOrder> 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<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;
|
}
|
|
|
|
/**
|
* 新增调度单
|
*
|
* @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<DispatchOrder> 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<DispatchOrder> 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<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);
|
}
|
|
|
|
|
}
|