From 318cef274e8784f652772e3efb1b3febd7f43d65 Mon Sep 17 00:00:00 2001 From: van Date: Tue, 24 Mar 2026 15:36:52 +0800 Subject: [PATCH] 1 --- .../system/JDOrderListController.java | 62 +++++- .../jarvis/domain/GroupRebateExcelUpload.java | 28 +++ .../mapper/GroupRebateExcelUploadMapper.java | 14 ++ .../IGroupRebateExcelUploadService.java | 17 ++ .../impl/GroupRebateExcelImportService.java | 178 ++++++++++++++++-- .../GroupRebateExcelUploadServiceImpl.java | 34 ++++ .../jarvis/GroupRebateExcelUploadMapper.xml | 62 ++++++ sql/jd_group_rebate_excel_upload.sql | 17 ++ 8 files changed, 387 insertions(+), 25 deletions(-) create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/GroupRebateExcelUpload.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/GroupRebateExcelUploadMapper.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IGroupRebateExcelUploadService.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelUploadServiceImpl.java create mode 100644 ruoyi-system/src/main/resources/mapper/jarvis/GroupRebateExcelUploadMapper.xml create mode 100644 sql/jd_group_rebate_excel_upload.sql diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java index beadd14..a14055a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/JDOrderListController.java @@ -1,14 +1,20 @@ package com.ruoyi.web.controller.system; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.ruoyi.jarvis.domain.GroupRebateExcelUpload; import com.ruoyi.jarvis.domain.OrderRows; +import com.ruoyi.jarvis.service.IGroupRebateExcelUploadService; import com.ruoyi.jarvis.service.impl.GroupRebateExcelImportService; import com.ruoyi.jarvis.service.IOrderRowsService; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUtils; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.annotation.Log; @@ -37,14 +43,17 @@ public class JDOrderListController extends BaseController private final IOrderRowsService orderRowsService; private final IInstructionService instructionService; private final GroupRebateExcelImportService groupRebateExcelImportService; + private final IGroupRebateExcelUploadService groupRebateExcelUploadService; public JDOrderListController(IJDOrderService jdOrderService, IOrderRowsService orderRowsService, IInstructionService instructionService, - GroupRebateExcelImportService groupRebateExcelImportService) { + GroupRebateExcelImportService groupRebateExcelImportService, + IGroupRebateExcelUploadService groupRebateExcelUploadService) { this.jdOrderService = jdOrderService; this.orderRowsService = orderRowsService; this.instructionService = instructionService; this.groupRebateExcelImportService = groupRebateExcelImportService; + this.groupRebateExcelUploadService = groupRebateExcelUploadService; } /** @@ -140,10 +149,7 @@ public class JDOrderListController extends BaseController } /** - * 导出JD订单列表 - */ - /** - * 导入跟团返现类 Excel:按「单号/订单号」匹配系统订单,将「是否返现」「总共返现」等写入后返备注(可多次导入累加) + * 导入跟团返现类 Excel:按「单号/订单号」匹配系统订单,将「是否返现」「总共返现」等写入后返备注(可多次导入累加);文件落盘并记上传记录。 */ @Log(title = "JD订单后返表导入", businessType = BusinessType.IMPORT) @PostMapping("/importGroupRebateExcel") @@ -151,15 +157,55 @@ public class JDOrderListController extends BaseController @RequestParam(value = "documentTitle", required = false) String documentTitle) { try { Map data = groupRebateExcelImportService.importExcel(file, documentTitle); - if (Boolean.FALSE.equals(data.get("success"))) { - return AjaxResult.error(String.valueOf(data.get("message"))); - } return AjaxResult.success(data); } catch (Exception e) { return AjaxResult.error("导入失败: " + e.getMessage()); } } + /** + * 后返表上传记录(分页) + */ + @GetMapping("/groupRebateUpload/list") + public TableDataInfo groupRebateUploadList(GroupRebateExcelUpload query, HttpServletRequest request) { + startPage(); + String beginTimeStr = request.getParameter("beginTime"); + String endTimeStr = request.getParameter("endTime"); + if (beginTimeStr != null && !beginTimeStr.isEmpty()) { + query.getParams().put("beginTime", beginTimeStr); + } + if (endTimeStr != null && !endTimeStr.isEmpty()) { + query.getParams().put("endTime", endTimeStr); + } + List list = groupRebateExcelUploadService.selectGroupRebateExcelUploadList(query); + return getDataTable(list); + } + + /** + * 重新下载已上传的后返表原件(与通用 download 一致,使用 POST + blob) + */ + @Log(title = "后返表上传记录下载", businessType = BusinessType.EXPORT) + @PostMapping("/groupRebateUpload/download/{id}") + public void downloadGroupRebateUpload(@PathVariable("id") Long id, HttpServletResponse response) throws Exception { + GroupRebateExcelUpload rec = groupRebateExcelUploadService.selectGroupRebateExcelUploadById(id); + if (rec == null || StringUtils.isEmpty(rec.getFilePath())) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; + } + File file = GroupRebateExcelImportService.resolveDiskFile(rec.getFilePath()); + if (!file.isFile()) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; + } + String downloadName = StringUtils.isNotEmpty(rec.getOriginalFilename()) ? rec.getOriginalFilename() : file.getName(); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(file.getAbsolutePath(), response.getOutputStream()); + } + + /** + * 导出JD订单列表 + */ @Log(title = "JD订单", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, JDOrder jdOrder) throws IOException diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/GroupRebateExcelUpload.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/GroupRebateExcelUpload.java new file mode 100644 index 0000000..6a405fb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/domain/GroupRebateExcelUpload.java @@ -0,0 +1,28 @@ +package com.ruoyi.jarvis.domain; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 后返/跟团返现 Excel 上传记录 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GroupRebateExcelUpload extends BaseEntity { + + private static final long serialVersionUID = 1L; + + private Long id; + private String documentTitle; + private String originalFilename; + /** 若依资源路径,如 /profile/upload/group-rebate-excel/... */ + private String filePath; + private Long fileSize; + /** 1 解析失败或未处理 2 已成功解析并写订单 */ + private Integer importStatus; + private Integer dataRows; + private Integer updatedOrders; + private Integer notFoundCount; + private String resultDetailJson; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/GroupRebateExcelUploadMapper.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/GroupRebateExcelUploadMapper.java new file mode 100644 index 0000000..34fa2c8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/GroupRebateExcelUploadMapper.java @@ -0,0 +1,14 @@ +package com.ruoyi.jarvis.mapper; + +import com.ruoyi.jarvis.domain.GroupRebateExcelUpload; + +import java.util.List; + +public interface GroupRebateExcelUploadMapper { + + int insertGroupRebateExcelUpload(GroupRebateExcelUpload row); + + GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id); + + List selectGroupRebateExcelUploadList(GroupRebateExcelUpload query); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IGroupRebateExcelUploadService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IGroupRebateExcelUploadService.java new file mode 100644 index 0000000..06c6c04 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IGroupRebateExcelUploadService.java @@ -0,0 +1,17 @@ +package com.ruoyi.jarvis.service; + +import com.ruoyi.jarvis.domain.GroupRebateExcelUpload; + +import java.util.List; + +public interface IGroupRebateExcelUploadService { + + /** + * 独立事务写入上传记录(导入主流程回滚时仍保留审计记录) + */ + void saveRecordInNewTransaction(GroupRebateExcelUpload row); + + GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id); + + List selectGroupRebateExcelUploadList(GroupRebateExcelUpload query); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelImportService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelImportService.java index 704da9a..36219c6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelImportService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelImportService.java @@ -1,9 +1,17 @@ package com.ruoyi.jarvis.service.impl; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.jarvis.domain.GroupRebateExcelUpload; import com.ruoyi.jarvis.domain.JDOrder; import com.ruoyi.jarvis.domain.dto.RebateRemarkItem; import com.ruoyi.jarvis.mapper.JDOrderMapper; +import com.ruoyi.jarvis.service.IGroupRebateExcelUploadService; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DateUtil; @@ -13,11 +21,15 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.io.File; +import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; @@ -30,7 +42,7 @@ import java.util.Set; import java.util.regex.Pattern; /** - * 解析跟团返现类 Excel(表头含单号、是否返现、返现金额等),写入订单后返备注 JSON。 + * 解析跟团返现类 Excel(表头含单号、是否返现、返现金额等),写入订单后返备注 JSON;并保存上传文件与数据库记录。 */ @Service public class GroupRebateExcelImportService { @@ -39,6 +51,8 @@ public class GroupRebateExcelImportService { private static final DataFormatter DATA_FORMATTER = new DataFormatter(); + private static final String[] EXCEL_EXT = { "xls", "xlsx" }; + private static final Pattern ABNORMAL_HINT = Pattern.compile( ".*(异常|待补|待返|下次|驳回|未返|不返|暂缓|缺|补表|重做|暂无|无$|-).*", Pattern.CASE_INSENSITIVE); @@ -48,29 +62,73 @@ public class GroupRebateExcelImportService { @Resource private JDOrderMapper jdOrderMapper; - @Transactional(rollbackFor = Exception.class) + @Resource + private IGroupRebateExcelUploadService groupRebateExcelUploadService; + + private GroupRebateExcelImportService self; + + @Autowired + public void setSelf(@Lazy GroupRebateExcelImportService self) { + this.self = self; + } + + /** + * 先落盘上传文件,再解析写订单;无论成败均写入上传记录(独立事务)。 + */ public Map importExcel(MultipartFile file, String documentTitle) throws Exception { - Map result = new HashMap<>(); + String originalFilename = file != null ? file.getOriginalFilename() : null; + long fileSize = file != null ? file.getSize() : 0L; + String title = resolveDocumentTitle(file, documentTitle); + String webPath = ""; + if (file == null || file.isEmpty()) { - result.put("success", false); - result.put("message", "请选择文件"); - return result; - } - String title = documentTitle != null ? documentTitle.trim() : ""; - if (title.isEmpty()) { - String name = file.getOriginalFilename(); - if (name != null && name.contains(".")) { - title = name.substring(0, name.lastIndexOf('.')); - } else { - title = name != null ? name : "未命名文档"; - } + Map empty = new HashMap<>(); + empty.put("success", false); + empty.put("message", "请选择文件"); + empty.put("documentTitle", title); + persistUploadRecord(originalFilename, webPath, fileSize, title, empty); + return empty; } - try (InputStream is = file.getInputStream(); Workbook wb = WorkbookFactory.create(is)) { + try { + String baseDir = RuoYiConfig.getUploadPath() + File.separator + "group-rebate-excel"; + webPath = FileUploadUtils.upload(baseDir, file, EXCEL_EXT); + } catch (Exception e) { + log.warn("保存后返Excel失败", e); + Map fail = new HashMap<>(); + fail.put("success", false); + fail.put("message", "保存上传文件失败: " + e.getMessage()); + fail.put("documentTitle", title); + persistUploadRecord(originalFilename, webPath, fileSize, title, fail); + return fail; + } + + File diskFile = resolveDiskFile(webPath); + Map result; + try { + result = self.processWorkbookTransactional(diskFile, title); + } catch (Exception e) { + log.error("后返表导入处理异常", e); + result = new HashMap<>(); + result.put("success", false); + result.put("message", "导入处理异常: " + e.getMessage()); + result.put("documentTitle", title); + } + result.put("savedFilePath", webPath); + Long recordId = persistUploadRecord(originalFilename, webPath, fileSize, title, result); + result.put("uploadRecordId", recordId); + return result; + } + + @Transactional(rollbackFor = Exception.class) + public Map processWorkbookTransactional(File diskFile, String title) throws Exception { + Map result = new HashMap<>(); + try (InputStream is = new FileInputStream(diskFile); Workbook wb = WorkbookFactory.create(is)) { Sheet sheet = wb.getNumberOfSheets() > 0 ? wb.getSheetAt(0) : null; if (sheet == null) { result.put("success", false); result.put("message", "工作簿无工作表"); + result.put("documentTitle", title); return result; } @@ -78,6 +136,7 @@ public class GroupRebateExcelImportService { if (headerRowIndex < 0) { result.put("success", false); result.put("message", "未找到表头(需包含「单号」或「订单号」列,以及「是否返现」或「总共返现」相关列)"); + result.put("documentTitle", title); return result; } @@ -86,11 +145,13 @@ public class GroupRebateExcelImportService { if (hm.orderCol < 0) { result.put("success", false); result.put("message", "未识别订单号列(表头需含「单号」或「订单号」,不含「第三方」)"); + result.put("documentTitle", title); return result; } if (hm.whetherRebateCol < 0 && hm.totalCashbackCol < 0) { result.put("success", false); result.put("message", "未识别「是否返现」或返现金额列(如「总共返现」)"); + result.put("documentTitle", title); return result; } @@ -154,6 +215,90 @@ public class GroupRebateExcelImportService { } } + private Long persistUploadRecord(String originalFilename, String webPath, long fileSize, + String title, Map procResult) { + GroupRebateExcelUpload row = new GroupRebateExcelUpload(); + row.setOriginalFilename(originalFilename); + row.setFilePath(StringUtils.isNotEmpty(webPath) ? webPath : ""); + row.setFileSize(fileSize); + row.setDocumentTitle(title); + try { + row.setCreateBy(SecurityUtils.getUsername()); + } catch (Exception e) { + row.setCreateBy(""); + } + boolean ok = Boolean.TRUE.equals(procResult.get("success")); + row.setImportStatus(ok ? 2 : 1); + row.setDataRows(asInt(procResult.get("dataRows"))); + row.setUpdatedOrders(asInt(procResult.get("updatedOrders"))); + @SuppressWarnings("unchecked") + List nf = (List) procResult.get("notFoundOrderNos"); + row.setNotFoundCount(nf != null ? nf.size() : null); + + JSONObject detail = new JSONObject(); + detail.put("message", procResult.get("message")); + detail.put("documentTitle", procResult.get("documentTitle")); + if (procResult.containsKey("notFoundOrderNos")) { + detail.put("notFoundOrderNos", procResult.get("notFoundOrderNos")); + } + if (procResult.containsKey("errors")) { + detail.put("errors", procResult.get("errors")); + } + if (procResult.containsKey("savedFilePath")) { + detail.put("savedFilePath", procResult.get("savedFilePath")); + } + row.setResultDetailJson(detail.toJSONString()); + + try { + groupRebateExcelUploadService.saveRecordInNewTransaction(row); + return row.getId(); + } catch (Exception e) { + log.error("写入后返上传记录失败", e); + return null; + } + } + + private static Integer asInt(Object o) { + if (o == null) { + return null; + } + if (o instanceof Number) { + return ((Number) o).intValue(); + } + try { + return Integer.parseInt(o.toString()); + } catch (NumberFormatException e) { + return null; + } + } + + private static String resolveDocumentTitle(MultipartFile file, String documentTitle) { + String t = documentTitle != null ? documentTitle.trim() : ""; + if (!t.isEmpty()) { + return t; + } + if (file == null) { + return "未命名文档"; + } + String name = file.getOriginalFilename(); + if (name != null && name.contains(".")) { + return name.substring(0, name.lastIndexOf('.')); + } + return name != null ? name : "未命名文档"; + } + + /** 将 /profile/... 转为本地 File */ + public static File resolveDiskFile(String webPath) { + if (StringUtils.isEmpty(webPath)) { + throw new IllegalArgumentException("file path empty"); + } + String sub = StringUtils.substringAfter(webPath, Constants.RESOURCE_PREFIX); + if (sub.startsWith("/")) { + sub = sub.substring(1); + } + return new File(RuoYiConfig.getProfile(), sub); + } + private void appendRebateRemark(JDOrder order, String documentTitle, String whetherRebate, String rebateAmount) { List list = new ArrayList<>(); String existing = order.getRebateRemarkJson(); @@ -342,7 +487,6 @@ public class GroupRebateExcelImportService { int orderCol = -1; int whetherRebateCol = -1; int totalCashbackCol = -1; - /** 无「总共返现」时的备选:含「返现」「金额」的列 */ int fallbackAmountCol = -1; } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelUploadServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelUploadServiceImpl.java new file mode 100644 index 0000000..cd6a0d6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/GroupRebateExcelUploadServiceImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.jarvis.service.impl; + +import com.ruoyi.jarvis.domain.GroupRebateExcelUpload; +import com.ruoyi.jarvis.mapper.GroupRebateExcelUploadMapper; +import com.ruoyi.jarvis.service.IGroupRebateExcelUploadService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class GroupRebateExcelUploadServiceImpl implements IGroupRebateExcelUploadService { + + @Resource + private GroupRebateExcelUploadMapper groupRebateExcelUploadMapper; + + @Override + @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) + public void saveRecordInNewTransaction(GroupRebateExcelUpload row) { + groupRebateExcelUploadMapper.insertGroupRebateExcelUpload(row); + } + + @Override + public GroupRebateExcelUpload selectGroupRebateExcelUploadById(Long id) { + return groupRebateExcelUploadMapper.selectGroupRebateExcelUploadById(id); + } + + @Override + public List selectGroupRebateExcelUploadList(GroupRebateExcelUpload query) { + return groupRebateExcelUploadMapper.selectGroupRebateExcelUploadList(query); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/jarvis/GroupRebateExcelUploadMapper.xml b/ruoyi-system/src/main/resources/mapper/jarvis/GroupRebateExcelUploadMapper.xml new file mode 100644 index 0000000..4bed946 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/jarvis/GroupRebateExcelUploadMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + select id, document_title, original_filename, file_path, file_size, import_status, + data_rows, updated_orders, not_found_count, result_detail_json, create_by, create_time + from jd_group_rebate_excel_upload + + + + insert into jd_group_rebate_excel_upload ( + document_title, original_filename, file_path, file_size, import_status, + data_rows, updated_orders, not_found_count, result_detail_json, create_by, create_time + ) values ( + #{documentTitle}, #{originalFilename}, #{filePath}, #{fileSize}, #{importStatus}, + #{dataRows}, #{updatedOrders}, #{notFoundCount}, #{resultDetailJson}, #{createBy}, now() + ) + + + + + + diff --git a/sql/jd_group_rebate_excel_upload.sql b/sql/jd_group_rebate_excel_upload.sql new file mode 100644 index 0000000..1ad1f11 --- /dev/null +++ b/sql/jd_group_rebate_excel_upload.sql @@ -0,0 +1,17 @@ +-- 跟团返现 / 后返 Excel 上传记录(含服务器存档路径,可再次下载) +CREATE TABLE IF NOT EXISTS jd_group_rebate_excel_upload ( + id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', + document_title VARCHAR(255) DEFAULT NULL COMMENT '文档标题(如跟团+返现 260316)', + original_filename VARCHAR(255) DEFAULT NULL COMMENT '用户原始文件名', + file_path VARCHAR(512) NOT NULL DEFAULT '' COMMENT '服务器存储相对路径(若依 /profile/upload/...)', + file_size BIGINT DEFAULT NULL COMMENT '字节数', + import_status TINYINT NOT NULL DEFAULT 1 COMMENT '1 未成功处理表 2 已解析并写入订单', + data_rows INT DEFAULT NULL COMMENT '有效数据行数', + updated_orders INT DEFAULT NULL COMMENT '匹配并更新订单数', + not_found_count INT DEFAULT NULL COMMENT '未找到单号条数', + result_detail_json LONGTEXT COMMENT '未匹配单号、错误等详情JSON', + create_by VARCHAR(64) DEFAULT NULL, + create_time DATETIME DEFAULT NULL, + PRIMARY KEY (id), + KEY idx_jd_gr_upload_time (create_time) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后返表Excel上传记录';