package com.ruoyi.cwgl.service.impl; import java.io.File; import java.io.OutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.enums.SystemDataNoEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.file.DownloadExportUtil; import com.ruoyi.common.utils.file.DownloadExportUtil.ExprotStatus; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.cwgl.domain.*; import com.ruoyi.cwgl.domain.vo.ReceivableFeeStatisticsVo; import com.ruoyi.cwgl.service.*; import com.ruoyi.system.domain.SysConfig; import com.ruoyi.system.mapper.SysConfigMapper; import com.ruoyi.system.service.ISystemDataNoService; import org.springframework.beans.factory.annotation.Autowired; 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.common.config.RuoYiConfig; import com.ruoyi.common.exception.UtilException; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.cwgl.mapper.ReceivableFeeManagementMapper; import com.ruoyi.cwgl.domain.vo.ReceivableBillCreateVo; import com.ruoyi.common.core.text.Convert; import javax.annotation.Resource; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.streaming.SXSSFWorkbook; /** * 应收费用管理Service业务层处理 * * @author ruoyi * @date 2025-12-17 */ @Service @Transactional(rollbackFor = Exception.class) public class ReceivableFeeManagementServiceImpl extends BaseService implements IReceivableFeeManagementService { protected final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private ReceivableFeeManagementMapper receivableFeeManagementMapper; @Autowired private IReceivableFeeDetailService receivableFeeDetailService; @Resource private SysConfigMapper sysConfigMapper; @Autowired private IReceivableBillManagementService receivableBillManagementService; @Autowired ISystemDataNoService systemDataNoService; @Autowired private IReceivableFeeManagementLogService logService; @Autowired private RedisCache redisCache; @Autowired private ITmsCustomerInfoService tmsCustomerInfoService; /** * 查询应收费用管理 * * @param id 应收费用管理ID * @return 应收费用管理 */ @DataSource(DataSourceType.SLAVE) @Override public ReceivableFeeManagement selectReceivableFeeManagementById(Integer id) { ReceivableFeeManagement receivableFeeManagement = receivableFeeManagementMapper.selectReceivableFeeManagementById(id); if (receivableFeeManagement != null) { // 查询对应的费用明细 ReceivableFeeDetail detail = new ReceivableFeeDetail(); detail.setReceivableFeeId(id); List detailList = receivableFeeDetailService.selectReceivableFeeDetailList(detail); receivableFeeManagement.setReceivableFeeDetailList(detailList); } return receivableFeeManagement; } /** * 查询应收费用管理 记录数 * * @param receivableFeeManagement 应收费用管理 * @return 应收费用管理集合 */ @DataSource(DataSourceType.SLAVE) @Override public int selectReceivableFeeManagementCount(ReceivableFeeManagement receivableFeeManagement) { return receivableFeeManagementMapper.selectReceivableFeeManagementCount(receivableFeeManagement); } /** * 查询应收费用管理列表 * * @param receivableFeeManagement 应收费用管理 * @return 应收费用管理 */ @DataSource(DataSourceType.SLAVE) @Override public List selectReceivableFeeManagementList(ReceivableFeeManagement receivableFeeManagement) { return receivableFeeManagementMapper.selectReceivableFeeManagementList(receivableFeeManagement); } /** * 查询应收费用管理列表 异步 导出 * * @param receivableFeeManagement 应收费用管理 * @param exportKey 导出功能的唯一标识 * @return 应收费用管理集合 */ @DataSource(DataSourceType.SLAVE) @Async @Override public void export(ReceivableFeeManagement receivableFeeManagement,String exportKey) { super.export(ReceivableFeeManagement.class,exportKey,"receivableFeeManagementData",(pageNum)->{ PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE); return selectReceivableFeeManagementList(receivableFeeManagement); }); } /** * 新增应收费用管理 * * @param receivableFeeManagement 应收费用管理 * @return 结果 */ @Override public int insertReceivableFeeManagement(ReceivableFeeManagement receivableFeeManagement) { receivableFeeManagement.setCreateTime(DateUtils.getNowDate()); String noByKey = systemDataNoService.getNoByKey(SystemDataNoEnum.YS); receivableFeeManagement.setSystemNo(noByKey); // 计算应收金额字符串描述 if (receivableFeeManagement.getReceivableFeeDetailList() != null && !receivableFeeManagement.getReceivableFeeDetailList().isEmpty()) { String receivableAmountStr = calculateReceivableAmountStr(receivableFeeManagement.getReceivableFeeDetailList()); receivableFeeManagement.setReceivableAmountStr(receivableAmountStr); } receivableFeeManagement.setCreateBy(SecurityUtils.getUsername()); // 保存主表信息 int result = receivableFeeManagementMapper.insertReceivableFeeManagement(receivableFeeManagement); // 获取主表ID Integer receivableFeeId = receivableFeeManagement.getId(); // 批量保存明细列表 if (receivableFeeManagement.getReceivableFeeDetailList() != null && !receivableFeeManagement.getReceivableFeeDetailList().isEmpty()) { // 设置每个明细的应收费用管理ID receivableFeeManagement.getReceivableFeeDetailList().forEach(detail -> { detail.setReceivableFeeId(receivableFeeId); detail.setCreateTime(DateUtils.getNowDate()); // 设置创建时间 }); // 调用批量插入方法 receivableFeeDetailService.insertReceivableFeeDetailBatch(receivableFeeManagement.getReceivableFeeDetailList()); } // 记录操作日志 if (result > 0) { ReceivableFeeManagementLog log = new ReceivableFeeManagementLog(); log.setReceivableFeeId(receivableFeeId); log.setOperator(SecurityUtils.getUsername()); log.setOperationTime(DateUtils.getNowDate()); log.setOperationDesc("新增应收费用管理记录,系统编号:" + receivableFeeManagement.getSystemNo()); log.setCreateTime(DateUtils.getNowDate()); logService.insertReceivableFeeManagementLog(log); } return result; } /** * 修改应收费用管理 * * @param receivableFeeManagement 应收费用管理 * @return 结果 */ @Override public int updateReceivableFeeManagement(ReceivableFeeManagement receivableFeeManagement) { receivableFeeManagement.setUpdateTime(DateUtils.getNowDate()); // 计算应收金额字符串描述 if (receivableFeeManagement.getReceivableFeeDetailList() != null && !receivableFeeManagement.getReceivableFeeDetailList().isEmpty()) { String receivableAmountStr = calculateReceivableAmountStr(receivableFeeManagement.getReceivableFeeDetailList()); receivableFeeManagement.setReceivableAmountStr(receivableAmountStr); } // 1. 更新主表信息 int result = receivableFeeManagementMapper.updateReceivableFeeManagement(receivableFeeManagement); // 2. 获取主表ID Integer receivableFeeId = receivableFeeManagement.getId(); // 3. 删除该应收费用下的所有现有明细 receivableFeeDetailService.deleteReceivableFeeDetailByReceivableFeeId(receivableFeeId); // 4. 批量保存新的明细列表 if (receivableFeeManagement.getReceivableFeeDetailList() != null && !receivableFeeManagement.getReceivableFeeDetailList().isEmpty()) { // 设置每个明细的应收费用管理ID receivableFeeManagement.getReceivableFeeDetailList().forEach(detail -> { detail.setReceivableFeeId(receivableFeeId); detail.setCreateTime(DateUtils.getNowDate()); // 设置更新时间 }); // 调用批量插入方法 receivableFeeDetailService.insertReceivableFeeDetailBatch(receivableFeeManagement.getReceivableFeeDetailList()); } // 记录操作日志 if (result > 0) { ReceivableFeeManagementLog log = new ReceivableFeeManagementLog(); log.setReceivableFeeId(receivableFeeId); log.setOperator(SecurityUtils.getUsername()); log.setOperationTime(DateUtils.getNowDate()); log.setOperationDesc("修改应收费用管理记录,系统编号:" + receivableFeeManagement.getSystemNo()); log.setCreateTime(DateUtils.getNowDate()); logService.insertReceivableFeeManagementLog(log); } return result; } /** * 作废应收费用管理记录 * * @param id 应收费用管理ID * @return 结果 */ @Override public int voidReceivableFeeManagement(Integer id) { // 查询费用管理记录 ReceivableFeeManagement feeManagement = receivableFeeManagementMapper.selectReceivableFeeManagementById(id); if (feeManagement == null) { throw new ServiceException("费用管理记录不存在"); } // 判断状态是否为0(待生成账单),只有状态为0才能作废 if (!"0".equals(feeManagement.getStatus())) { throw new ServiceException("只有状态为待生成账单的记录才能作废"); } // 设置状态为作废(假设用"2"表示作废状态) feeManagement.setStatus("2"); feeManagement.setUpdateTime(DateUtils.getNowDate()); // 更新记录 int result = receivableFeeManagementMapper.updateReceivableFeeManagement(feeManagement); // 记录操作日志 if (result > 0) { ReceivableFeeManagementLog log = new ReceivableFeeManagementLog(); log.setReceivableFeeId(id); log.setOperator(SecurityUtils.getUsername()); log.setOperationTime(DateUtils.getNowDate()); log.setOperationDesc("作废应收费用管理记录,系统编号:" + feeManagement.getSystemNo()); log.setCreateTime(DateUtils.getNowDate()); logService.insertReceivableFeeManagementLog(log); } return result; } /** * 新增应收费用管理[批量] * * @param receivableFeeManagements 应收费用管理 * @return 结果 */ @Override public int insertReceivableFeeManagementBatch(List receivableFeeManagements) { int rows = receivableFeeManagementMapper.insertReceivableFeeManagementBatch(receivableFeeManagements); return rows; } /** * 修改应收费用管理[批量] * * @param receivableFeeManagements 应收费用管理 * @return 结果 */ @Override public int updateReceivableFeeManagementBatch(List receivableFeeManagements){ return receivableFeeManagementMapper.updateReceivableFeeManagementBatch(receivableFeeManagements); } /** * 删除应收费用管理对象 * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteReceivableFeeManagementByIds(String ids) { return deleteReceivableFeeManagementByIds(Convert.toIntArray(ids)); } /** * 删除应收费用管理对象 * * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteReceivableFeeManagementByIds(Integer[] ids) { return receivableFeeManagementMapper.deleteReceivableFeeManagementByIds(ids); } /** * 删除应收费用管理信息 * * @param id 应收费用管理ID * @return 结果 */ @Override public int deleteReceivableFeeManagementById(Integer id) { return receivableFeeManagementMapper.deleteReceivableFeeManagementById(id); } /** * 根据应收费用ID数组查询统计信息和明细 * * @param ids 应收费用ID数组 * @return 包含统计信息和明细的结果 */ @DataSource(DataSourceType.SLAVE) @Override public ReceivableFeeStatisticsVo getReceivableFeeStatistics(Integer[] ids) { // 查询应收费用主表记录 List receivableFeeList = receivableFeeManagementMapper.selectReceivableFeeManagementByIds(ids); // 检查所有记录是否属于同一个客户 if (!receivableFeeList.isEmpty()) { Integer firstCustomerId = receivableFeeList.get(0).getCustomerId(); boolean allSameCustomer = receivableFeeList.stream() .allMatch(item -> Objects.equals(item.getCustomerId(), firstCustomerId)); if (!allSameCustomer) { throw new ServiceException("所选记录包含不同客户的数据,无法进行统计"); } } // 计算单据数量 int documentCount = receivableFeeList.size(); SysConfig sysConfig = sysConfigMapper.selectConfig(new SysConfig() {{ setConfigKey("sys.hk.rmb.rate"); }}); // 计算总应收金额(按币种分别计算) BigDecimal totalAmountRmb = BigDecimal.ZERO; BigDecimal totalAmountHkd = BigDecimal.ZERO; // 获取所有应收费用明细,按币种汇总 if (!receivableFeeList.isEmpty()) { // 获取所有应收费用ID Integer[] feeIds = receivableFeeList.stream() .map(ReceivableFeeManagement::getId) .toArray(Integer[]::new); // 查询所有明细记录 List allDetails = receivableFeeDetailService.selectReceivableFeeDetailByReceivableFeeIds(feeIds); // 按币种汇总金额 for (ReceivableFeeDetail detail : allDetails) { if (detail.getBillingAmount() != null && detail.getCurrency() != null) { if ("RMB".equals(detail.getCurrency())) { totalAmountRmb = totalAmountRmb.add(detail.getBillingAmount()); } else if ("HKD".equals(detail.getCurrency())) { totalAmountHkd = totalAmountHkd.add(detail.getBillingAmount()); } } } } BigDecimal exchangeRate = new BigDecimal(sysConfig.getConfigValue()); // 计算人民币总金额(人民币金额 + 港币换算成人民币) BigDecimal totalAmountRmbWithConversion = totalAmountRmb.add( totalAmountHkd.multiply(exchangeRate).setScale(2, RoundingMode.HALF_UP) ); // 计算港币总金额(港币金额 + 人民币换算成港币) BigDecimal totalAmountHkdWithConversion = totalAmountHkd.add( totalAmountRmb.divide(exchangeRate, 2, RoundingMode.HALF_UP) ); // 组装返回结果 ReceivableFeeStatisticsVo result = new ReceivableFeeStatisticsVo(); result.setDocumentCount(documentCount); result.setRate(exchangeRate); result.setTotalReceivableAmount(totalAmountRmbWithConversion); // 默认使用人民币总金额 result.setTotalAmountRmb(totalAmountRmbWithConversion); result.setTotalAmountHkd(totalAmountHkdWithConversion); result.setIds(ids); return result; } /** * 根据统计数据创建应收账单 * * @param billCreateVo 包含统计数据和账单类型的请求对象 * @return 创建的应收账单ID */ @Override public Integer createReceivableBill(ReceivableBillCreateVo billCreateVo) { // 1. 准备数据 ReceivableFeeStatisticsVo statisticsVo = billCreateVo.getStatisticsData(); // 检查费用管理表的状态,只有状态为0才能创建账单 Integer[] feeIds = statisticsVo.getIds(); if (feeIds != null && feeIds.length > 0) { for (Integer feeId : feeIds) { ReceivableFeeManagement feeManagement = receivableFeeManagementMapper.selectReceivableFeeManagementById(feeId); if (feeManagement != null && !"0".equals(feeManagement.getStatus())) { throw new ServiceException("费用记录ID为" + feeId + "的状态不是待生成账单,无法创建账单"); } } } Integer billType = billCreateVo.getBillType(); // 2. 创建应收账单主记录 ReceivableBillManagement billManagement = new ReceivableBillManagement(); billManagement.setDocumentCount(statisticsVo.getDocumentCount()); billManagement.setExchangeRate(statisticsVo.getRate()); billManagement.setStatus("0"); // 默认草稿状态 billManagement.setCreateTime(DateUtils.getNowDate()); billManagement.setBillName(billCreateVo.getBillName()); billManagement.setCustomerName(billCreateVo.getCustomerName()); billManagement.setCreateBy(SecurityUtils.getUsername()); billManagement.setIsInternalSettlement(billCreateVo.getIsInternalSettlement()); billManagement.setInternalSettlementUnit(billCreateVo.getInternalSettlementUnit()); // 3. 根据账单类型设置币种和总金额 if (billType == 0) { // 人民币账单 billManagement.setCurrency("RMB"); billManagement.setTotalAmount(statisticsVo.getTotalAmountRmb()); } else if (billType == 1) { // 港币账单 billManagement.setCurrency("HKD"); billManagement.setTotalAmount(statisticsVo.getTotalAmountHkd()); } else { throw new IllegalArgumentException("无效的账单类型:" + billType); } billManagement.setPendingAmount(billManagement.getTotalAmount()); String noByKey = systemDataNoService.getNoByKey(SystemDataNoEnum.YSZD); billManagement.setSystemNo(noByKey); // 4. 保存主账单记录 int i = receivableBillManagementService.insertReceivableBillManagement(billManagement); // 5. 更新应收费用主表的关联账单编号 if (statisticsVo.getIds() != null && statisticsVo.getIds().length > 0) { // 获取生成的账单系统编号 String billSystemNo = billManagement.getSystemNo(); // 批量更新应收费用主表的关联账单编号 for (Integer feeId : statisticsVo.getIds()) { ReceivableFeeManagement feeManagement = new ReceivableFeeManagement(); feeManagement.setId(feeId); feeManagement.setRelatedBillNo(billSystemNo); feeManagement.setStatus("1"); receivableFeeManagementMapper.updateReceivableFeeManagement(feeManagement); // 记录操作日志 ReceivableFeeManagementLog log = new ReceivableFeeManagementLog(); log.setReceivableFeeId(feeId); log.setOperator(SecurityUtils.getUsername()); log.setOperationTime(DateUtils.getNowDate()); log.setOperationDesc("生成应收账单,账单编号:" + billSystemNo); log.setCreateTime(DateUtils.getNowDate()); logService.insertReceivableFeeManagementLog(log); } // 调用批量更新方法 } return i; } /** * 根据关联账单编号批量更新应收费用管理记录 * 清除关联账单编号并将状态改为0(待生成账单) * * @param relatedBillNo 关联账单编号 * @return 结果 */ @Override public int updateReceivableFeeManagementByRelatedBillNo(String relatedBillNo) { return receivableFeeManagementMapper.updateReceivableFeeManagementByRelatedBillNo(relatedBillNo); } /** * 计算应收金额字符串描述 * 根据明细列表按币种汇总金额,格式如:"200港币100人民币" * * @param detailList 应收费用明细列表 * @return 应收金额字符串描述 */ private String calculateReceivableAmountStr(List detailList) { if (detailList == null || detailList.isEmpty()) { return ""; } // 按币种汇总金额 Map currencyAmountMap = new HashMap<>(); for (ReceivableFeeDetail detail : detailList) { String currency = detail.getCurrency(); BigDecimal billingAmount = detail.getActualAmount(); if (currency != null && billingAmount != null) { currencyAmountMap.merge(currency, billingAmount, BigDecimal::add); } } // 构建字符串描述 StringBuilder sb = new StringBuilder(); for (Map.Entry entry : currencyAmountMap.entrySet()) { if (sb.length() > 0) { sb.append(" "); } sb.append(entry.getValue().stripTrailingZeros().toPlainString()); // 根据币种显示对应的货币名称 String currency = entry.getKey(); if ("RMB".equals(currency)) { sb.append("人民币"); } else if ("HKD".equals(currency)) { sb.append("港币"); } else if ("USD".equals(currency)) { sb.append("美元"); } else { sb.append(currency); } } return sb.toString(); } /** * 导入应收费用管理数据(包含明细数据) * * @param receivableFeeList 应收费用管理数据列表(包含明细数据) * @param operName 操作用户 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public String importReceivableFee(List receivableFeeList, String operName) { if (receivableFeeList == null || receivableFeeList.isEmpty()) { throw new ServiceException("导入应收费用数据不能为空!"); } int successNum = 0; int failureNum = 0; StringBuilder successMsg = new StringBuilder(); StringBuilder failureMsg = new StringBuilder(); // 获取当前日期 Date now = DateUtils.getNowDate(); for (ReceivableFeeManagement receivableFee : receivableFeeList) { try { // 自动生成系统编号(和新增功能一样) String noByKey = systemDataNoService.getNoByKey(SystemDataNoEnum.YS); receivableFee.setSystemNo(noByKey); // 设置创建时间和创建人 receivableFee.setCreateTime(now); receivableFee.setCreateBy(operName); // 根据客户名称查询客户ID if (receivableFee.getCustomerName() != null && !receivableFee.getCustomerName().isEmpty()) { TmsCustomerInfo customerQuery = new TmsCustomerInfo(); customerQuery.setCustomerFullName(receivableFee.getCustomerName()); List customerList = tmsCustomerInfoService.selectTmsCustomerInfoList(customerQuery); if (customerList != null && !customerList.isEmpty()) { // 取第一个匹配的客户 TmsCustomerInfo customer = customerList.get(0); receivableFee.setCustomerId(customer.getId()); } else { // 如果没有找到匹配的客户,可以记录日志或抛出异常 logger.warn("未找到客户名称为 '{}' 的客户信息,customerId将设置为null", receivableFee.getCustomerName()); } } // 计算应收金额字符串描述 if (receivableFee.getReceivableFeeDetailList() != null && !receivableFee.getReceivableFeeDetailList().isEmpty()) { String receivableAmountStr = calculateReceivableAmountStr(receivableFee.getReceivableFeeDetailList()); receivableFee.setReceivableAmountStr(receivableAmountStr); } // 设置默认状态为待生成账单(0) if (receivableFee.getStatus() == null || receivableFee.getStatus().isEmpty()) { receivableFee.setStatus("0"); } // 插入主表数据 receivableFeeManagementMapper.insertReceivableFeeManagement(receivableFee); // 处理明细数据(如果有) if (receivableFee.getReceivableFeeDetailList() != null && !receivableFee.getReceivableFeeDetailList().isEmpty()) { for (ReceivableFeeDetail detail : receivableFee.getReceivableFeeDetailList()) { detail.setReceivableFeeId(receivableFee.getId()); detail.setCreateBy(operName); detail.setCreateTime(now); receivableFeeDetailService.insertReceivableFeeDetail(detail); } } successNum++; successMsg.append("
" + successNum + ".应收费用 " + receivableFee.getSystemNo() + " 导入成功"); } catch (Exception e) { failureNum++; String msg = "
" + failureNum + ".应收费用 " + receivableFee.getSystemNo() + " 导入失败:"; failureMsg.append(msg + e.getMessage()); logger.error(msg, e); } } if (failureNum > 0) { failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); throw new ServiceException(failureMsg.toString()); } else { successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); } return successMsg.toString(); } /** * 下载导入模板(包含Sheet1主表和Sheet2明细表) * * @param exportKey 导出功能的唯一标识 */ @DataSource(DataSourceType.SLAVE) @Async @Override public void importTemplate(String exportKey) { String fileName = ExcelUtil.encodeFileName("应收费用导入模板"); // 设置当前任务为"下载中"状态 DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, ExprotStatus.XZZ.getStatus()); try { // 创建空列表用于生成模板(只需要表头) List mainList = new ArrayList<>(); List detailList = new ArrayList<>(); // 使用我们新实现的exportMultiSheetData方法创建双sheet模板 exportMultiSheetData( ReceivableFeeManagement.class, ReceivableFeeDetail.class, fileName, "应收费用主表", "应收费用明细", mainList, detailList ); // 设置下载完成状态 DownloadExportUtil.setDownloadFile(redisCache, exportKey, fileName); logger.info("导入模板导出完成: {}, file: {}", exportKey, fileName); } catch (Exception e) { logger.error("导入模板导出失败: {}, error: {}", exportKey, e.getMessage(), e); DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, ExprotStatus.XZYC.getStatus()); // 设置失败状态 throw e; } } // 删除不再需要的方法 /** * 获取文件的绝对路径 */ private File getAbsoluteFile(String fileName) { String downloadPath = RuoYiConfig.getDownloadPath(); return new File(downloadPath + fileName); } }