From 367639ae1bbf2af441058b13617938e12cd2dfc0 Mon Sep 17 00:00:00 2001 From: ljw <706814450@qq.com> Date: Thu, 22 Jan 2026 17:01:55 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E9=87=91=E6=B5=81=E6=B0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- njzscloud-svr/pom.xml | 12 ++ .../dispose/common/utils/FileUtil.java | 133 ++++++++++++++++++ .../controller/MoneyFlowController.java | 17 ++- .../finance/mapper/MoneyFlowMapper.java | 14 +- .../pojo/param/SearchMoneyFlowParam.java | 62 ++++++++ .../finance/pojo/result/MoneyFlowResult.java | 115 +++++++++++++++ .../finance/service/MoneyFlowService.java | 60 +++++++- .../mapper/finance/MoneyFlowMapper.xml | 44 ++++++ 8 files changed, 451 insertions(+), 6 deletions(-) create mode 100644 njzscloud-svr/src/main/java/com/njzscloud/dispose/common/utils/FileUtil.java create mode 100644 njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/param/SearchMoneyFlowParam.java create mode 100644 njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/result/MoneyFlowResult.java create mode 100644 njzscloud-svr/src/main/resources/mapper/finance/MoneyFlowMapper.xml diff --git a/njzscloud-svr/pom.xml b/njzscloud-svr/pom.xml index f71e53e..16702cd 100644 --- a/njzscloud-svr/pom.xml +++ b/njzscloud-svr/pom.xml @@ -89,6 +89,18 @@ hibernate-validator + + cn.hutool + hutool-all + 5.8.35 + + + + org.apache.poi + poi-ooxml + 5.2.5 + + diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/common/utils/FileUtil.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/common/utils/FileUtil.java new file mode 100644 index 0000000..19aebec --- /dev/null +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/common/utils/FileUtil.java @@ -0,0 +1,133 @@ +package com.njzscloud.dispose.common.utils; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.xssf.streaming.SXSSFSheet; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * File工具类,扩展 + * + * @author ljw + * @date 2505-10-13 + */ +@Slf4j +public class FileUtil extends cn.hutool.core.io.FileUtil { + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *
+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + + /** + * 导出excel,支持自定义文件名 + */ + public static void downloadExcel(List> list, HttpServletResponse response, String fileName) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 处理数据以防止CSV注入 + List> sanitizedList = list.parallelStream().map(map -> { + Map sanitizedMap = new LinkedHashMap<>(); + map.forEach((key, value) -> { + if (value instanceof String) { + String strValue = (String) value; + // 检查并处理以特殊字符开头的值 + if (strValue.startsWith("=") || strValue.startsWith("+") || strValue.startsWith("-") || strValue.startsWith("@")) { + strValue = "'" + strValue; // 添加单引号前缀 + } + sanitizedMap.put(key, strValue); + } else { + sanitizedMap.put(key, value); + } + }); + return sanitizedMap; + }).collect(Collectors.toList()); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(sanitizedList, true); + SXSSFSheet sheet = (SXSSFSheet) writer.getSheet(); + + // 手动设置列宽,确保标题和数据内容都能完整显示 + /*int columnCount = sheet.getRow(0).getLastCellNum(); + for (int i = 0; i < columnCount; i++) { + String headerText = ""; + int maxContentLength = 0; + + try { + // 获取标题文本 + if (sheet.getRow(0) != null && sheet.getRow(0).getCell(i) != null) { + headerText = sheet.getRow(0).getCell(i).getStringCellValue(); + } + + // 遍历所有数据行,找到最长的内容 + for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) { + if (sheet.getRow(rowIndex) != null && sheet.getRow(rowIndex).getCell(i) != null) { + String cellValue = sheet.getRow(rowIndex).getCell(i).getStringCellValue(); + if (cellValue != null) { + maxContentLength = Math.max(maxContentLength, cellValue.length()); + } + } + } + } catch (Exception e) { + // 忽略获取单元格值的异常 + } + + // 根据标题和内容的最大长度计算合适的列宽 + int headerLength = headerText.length(); + int contentLength = maxContentLength; + int maxLength = Math.max(headerLength, contentLength); + + // 设置列宽:每个字符约2个字符宽度,最小8个字符,最大50个字符 + int calculatedWidth = Math.max(Math.min(maxLength * 2, 50), 8) * 256; + int currentWidth = sheet.getColumnWidth(i); + + // 设置列宽,确保标题和内容都能完整显示 + sheet.setColumnWidth(i, Math.max(calculatedWidth, currentWidth)); + }*/ + + // 尝试自动调整列宽 + try { + sheet.trackAllColumnsForAutoSizing(); + writer.autoSizeColumnAll(); + } catch (Exception e) { + log.warn("自动调整列宽失败,使用手动设置的列宽", e); + } + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //设置文件名,支持中文文件名 + try { + String encodedFileName = java.net.URLEncoder.encode(fileName, "UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); + } catch (Exception e) { + // 如果编码失败,使用默认文件名 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + } + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + +} diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/controller/MoneyFlowController.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/controller/MoneyFlowController.java index 0cb0c6a..1ade0ab 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/controller/MoneyFlowController.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/controller/MoneyFlowController.java @@ -4,11 +4,14 @@ import com.njzscloud.common.core.utils.R; import com.njzscloud.common.mp.support.PageParam; import com.njzscloud.common.mp.support.PageResult; import com.njzscloud.dispose.finance.pojo.entity.MoneyFlowEntity; +import com.njzscloud.dispose.finance.pojo.param.SearchMoneyFlowParam; +import com.njzscloud.dispose.finance.pojo.result.MoneyFlowResult; import com.njzscloud.dispose.finance.service.MoneyFlowService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; - +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.List; /** @@ -62,8 +65,16 @@ public class MoneyFlowController { * 分页查询 */ @GetMapping("/paging") - public R> paging(PageParam pageParam, MoneyFlowEntity moneyFlowEntity) { - return R.success(moneyFlowService.paging(pageParam, moneyFlowEntity)); + public R> paging(PageParam pageParam, SearchMoneyFlowParam flowParam) { + return R.success(moneyFlowService.paging(pageParam, flowParam)); + } + + /** + * 导出资金流水 + */ + @GetMapping(value = "/export") + public void export(HttpServletResponse response, SearchMoneyFlowParam flowParam) throws IOException { + moneyFlowService.export(response, flowParam); } } diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/mapper/MoneyFlowMapper.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/mapper/MoneyFlowMapper.java index 81fdff0..8c7b446 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/mapper/MoneyFlowMapper.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/mapper/MoneyFlowMapper.java @@ -1,14 +1,26 @@ package com.njzscloud.dispose.finance.mapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.njzscloud.dispose.finance.pojo.entity.MoneyFlowEntity; +import com.njzscloud.dispose.finance.pojo.result.MoneyFlowResult; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /** * 资金流水 + * * @author ljw */ @Mapper public interface MoneyFlowMapper extends BaseMapper { -} + IPage paging(Page page, @Param("ew") QueryWrapper ew); + + List paging(@Param("ew") QueryWrapper ew); + +} diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/param/SearchMoneyFlowParam.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/param/SearchMoneyFlowParam.java new file mode 100644 index 0000000..3d1d845 --- /dev/null +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/param/SearchMoneyFlowParam.java @@ -0,0 +1,62 @@ +package com.njzscloud.dispose.finance.pojo.param; + +import com.njzscloud.dispose.finance.constant.AccountType; +import com.njzscloud.dispose.finance.constant.MoneyChangeCategory; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 资金流水 + * + * @author ljw + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class SearchMoneyFlowParam { + + /** + * 订单 Id + */ + private Long orderId; + + /** + * 订单编号 + */ + private String orderSn; + + /** + * 车次 + */ + private Integer trainNum; + + /** + * 资金账户 Id;fin_money_account.id + */ + private Long moneyAccountId; + + /** + * 变动类型;字典代码:money_change_category + */ + private MoneyChangeCategory moneyChangeCategory; + + /** + * 账户名称(客户姓名或组织名称) + */ + private String accountName; + + /** + * 账户类型;GeRen-->个人、QiYe-->企业 + */ + private AccountType accountType; + + private LocalDateTime startTime; + + private LocalDateTime endTime; +} + diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/result/MoneyFlowResult.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/result/MoneyFlowResult.java new file mode 100644 index 0000000..32b3861 --- /dev/null +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/pojo/result/MoneyFlowResult.java @@ -0,0 +1,115 @@ +package com.njzscloud.dispose.finance.pojo.result; + +import com.njzscloud.dispose.finance.constant.AccountType; +import com.njzscloud.dispose.finance.constant.MoneyChangeCategory; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 资金流水结果类 + * + * @author ljw + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class MoneyFlowResult { + + /** + * 主键 + */ + private Long id; + + /** + * 订单 Id + */ + private Long orderId; + + /** + * 订单编号 + */ + private String orderSn; + + /** + * 车次 + */ + private Integer trainNum; + + /** + * 资金账户 Id + */ + private Long moneyAccountId; + + /** + * 变动前余额 + */ + private BigDecimal beforeBalance; + + /** + * 变动金额;有正负 + */ + private BigDecimal delta; + + /** + * 变动后余额 + */ + private BigDecimal afterBalance; + + /** + * 变动类型;字典代码:money_change_category + */ + private MoneyChangeCategory moneyChangeCategory; + + /** + * 备注 + */ + private String memo; + + /** + * 附件地址 + */ + private List fileUrl; + + /** + * 创建人 Id + */ + private Long creatorId; + + /** + * 修改人 Id + */ + private Long modifierId; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 修改时间 + */ + private LocalDateTime modifyTime; + + /** + * 是否删除 + */ + private Boolean deleted; + + /** + * 账户名称(客户姓名或组织名称) + */ + private String accountName; + + /** + * 账户类型;GeRen-->个人、QiYe-->企业 + */ + private AccountType accountType; + +} diff --git a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/service/MoneyFlowService.java b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/service/MoneyFlowService.java index 6234792..61924da 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/service/MoneyFlowService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/dispose/finance/service/MoneyFlowService.java @@ -1,17 +1,27 @@ package com.njzscloud.dispose.finance.service; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.njzscloud.common.mp.support.PageParam; import com.njzscloud.common.mp.support.PageResult; +import com.njzscloud.dispose.common.utils.FileUtil; import com.njzscloud.dispose.finance.mapper.MoneyFlowMapper; import com.njzscloud.dispose.finance.pojo.entity.MoneyFlowEntity; +import com.njzscloud.dispose.finance.pojo.param.SearchMoneyFlowParam; +import com.njzscloud.dispose.finance.pojo.result.MoneyFlowResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * 资金流水 @@ -52,10 +62,56 @@ public class MoneyFlowService extends ServiceImpl buildQueryWrapper(SearchMoneyFlowParam param) { + return Wrappers.query() + .eq("fmf.deleted", Boolean.FALSE) + .eq(param.getOrderId() != null, "fmf.order_id", param.getOrderId()) + .like(StrUtil.isNotBlank(param.getOrderSn()), "fmf.order_sn", param.getOrderSn()) + .eq(param.getTrainNum() != null, "fmf.train_num", param.getTrainNum()) + .eq(param.getMoneyAccountId() != null, "fmf.money_account_id", param.getMoneyAccountId()) + .eq(param.getMoneyChangeCategory() != null, "fmf.money_change_category", param.getMoneyChangeCategory()) + .and(StrUtil.isNotBlank(param.getAccountName()), wrapper -> + wrapper.like("cc.customer_name", param.getAccountName()) + .or() + .like("co.org_name", param.getAccountName()) + ) + .eq(param.getAccountType() != null, "fma.account_type", param.getAccountType()) + .ge(param.getStartTime() != null, "fmf.create_time", param.getStartTime()) + .le(param.getEndTime() != null, "fmf.create_time", param.getEndTime()); + } + /** * 分页查询 */ - public PageResult paging(PageParam pageParam, MoneyFlowEntity moneyFlowEntity) { - return PageResult.of(this.page(pageParam.toPage(), Wrappers.query(moneyFlowEntity))); + public PageResult paging(PageParam pageParam, SearchMoneyFlowParam param) { + return PageResult.of(baseMapper.paging(pageParam.toPage(), buildQueryWrapper(param))); + } + + /** + * 导出 + */ + public void export(HttpServletResponse response, SearchMoneyFlowParam param) throws IOException { + List exportList = baseMapper.paging(buildQueryWrapper(param)); + List> downList = new ArrayList<>(); + int i = 1; + for (MoneyFlowResult result : exportList) { + Map map = new LinkedHashMap<>(); + map.put("编号", i); + map.put("账户名称", result.getAccountName()); + map.put("账户类型", result.getAccountType().getTxt()); + map.put("订单号", result.getOrderSn()); + map.put("车次", result.getTrainNum()); + map.put("变动类型", result.getMoneyChangeCategory().getTxt()); + map.put("变动前金额", result.getBeforeBalance()); + map.put("变动金额", result.getDelta()); + map.put("变动后金额", result.getAfterBalance()); + map.put("备注", result.getMemo()); + downList.add(map); + i++; + } + FileUtil.downloadExcel(downList, response, "资金变动明细.xlsx"); } } diff --git a/njzscloud-svr/src/main/resources/mapper/finance/MoneyFlowMapper.xml b/njzscloud-svr/src/main/resources/mapper/finance/MoneyFlowMapper.xml new file mode 100644 index 0000000..18a0825 --- /dev/null +++ b/njzscloud-svr/src/main/resources/mapper/finance/MoneyFlowMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + SELECT fmf.id, + fmf.order_id, + fmf.order_sn, + fmf.train_num, + fmf.money_account_id, + fmf.before_balance, + fmf.delta, + fmf.after_balance, + fmf.money_change_category, + fmf.memo, + fmf.file_url, + fmf.creator_id, + fmf.modifier_id, + fmf.create_time, + fmf.modify_time, + fmf.deleted, + CASE fma.account_type + WHEN 'GeRen' THEN cc.customer_name + WHEN 'QiYe' THEN co.org_name + ELSE NULL + END AS account_name, + fma.account_type + FROM fin_money_flow fmf + LEFT JOIN fin_money_account fma ON fma.id = fmf.money_account_id + LEFT JOIN cst_customer cc ON cc.id = fma.customer_id + LEFT JOIN cst_org co ON co.id = fma.org_id + + + +