From a0f604ac605a5d03ff96c4dfaaa37b0dbfddd154 Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Wed, 1 Apr 2026 09:52:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=8E=A5=E5=8F=A3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/SampleOrderScannerJob.java | 27 ++++ .../domain/scheduler/WebDavScannerJob.java | 9 ++ .../handler/ErpUpdateWaybillHandler.java | 12 +- .../handler/SfCreateOrderHandler.java | 20 ++- .../handler/SfDownloadWaybillHandler.java | 3 + .../handler/SfUploadResourceHandler.java | 3 + .../{ => handler}/ErpUpdateFeeHandler.java | 2 +- src/main/resources/application-test.yml | 118 ++++++++++++++++++ src/main/resources/application.yml | 3 +- 9 files changed, 189 insertions(+), 8 deletions(-) rename src/main/java/com/project/receive/strategy/{ => handler}/ErpUpdateFeeHandler.java (97%) create mode 100644 src/main/resources/application-test.yml diff --git a/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java b/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java index 588d49a..aaf6905 100644 --- a/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java +++ b/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java @@ -1,6 +1,7 @@ package com.project.logistics.domain.scheduler; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import com.project.logistics.config.LogisticsScannerProperties; import com.project.logistics.config.LogisticsSampleScannerProperties; // 注入新配置类 @@ -114,6 +115,32 @@ public class SampleOrderScannerJob { if (logisticsOrderService.existsByOrderNo(orderNo)) { return false; } + String transportMethod = u9Data.getStr(SampleOrderInfoFieldEnum.transportMethod.name()); + if (StrUtil.isBlank(transportMethod) || !transportMethod.contains("顺丰")) { + // 需要终止任务,匹配字段失败,失败原因:运输方式及货场名称不为顺丰 + log.warn("样品单号 {} 匹配字段失败:运输方式及货场名称不为顺丰 {} ", orderNo , transportMethod); + return false; + } + + // 2. 【新增校验】:实际发货时间必须是当天 + // 从 JSON 中获取实际发货时间字段 (DescFlexField_PubDescSeg19) + String actualSendTimeStr = u9Data.getStr(SampleOrderInfoFieldEnum.actualSendTime.name()); + + if (StrUtil.isBlank(actualSendTimeStr)) { + log.warn("样品单号 {} 实际发货时间为空,跳过处理", orderNo); + return false; + } + + // 获取今天的日期字符串 (格式: yyyy-MM-dd) + String today = cn.hutool.core.date.DateUtil.today(); + // 截取 U9 时间字符串的前 10 位进行比较 (防止 U9 返回的是 yyyy-MM-dd HH:mm:ss) + String actualSendDate = actualSendTimeStr.trim().substring(0, 10); + + if (!today.equals(actualSendDate)) { + log.info("样品单号 {} 实际发货时间 [{}] 不是当天 [{}], 不执行自动下单", + orderNo, actualSendDate, today); + return false; + } // 持久化订单:跳过上传资源步骤,直接设置为 RESOURCE_CREATED LogisticsOrderEntity order = new LogisticsOrderEntity(); diff --git a/src/main/java/com/project/logistics/domain/scheduler/WebDavScannerJob.java b/src/main/java/com/project/logistics/domain/scheduler/WebDavScannerJob.java index 06be444..c8f3d18 100644 --- a/src/main/java/com/project/logistics/domain/scheduler/WebDavScannerJob.java +++ b/src/main/java/com/project/logistics/domain/scheduler/WebDavScannerJob.java @@ -7,6 +7,7 @@ import com.github.sardine.DavResource; import com.project.logistics.config.LogisticsScannerProperties; import com.project.logistics.config.WebDavProperties; +import com.project.logistics.domain.enums.ShipmentOrderInfoFieldEnum; import com.project.logistics.domain.service.WebDavService; import com.project.logistics.domain.service.base.ApiRetryTaskService; import com.project.logistics.domain.service.base.ErpService; @@ -171,11 +172,19 @@ public class WebDavScannerJob { // 2. 跨库查询 U9 (无事务环境,完美适配专线查询) JSONObject u9Data = erpService.getShipmentOrderInfo(orderNo); + if (u9Data == null || u9Data.isEmpty()) { log.warn("单号 {} 在 U9 数据库中未找到数据", orderNo); return false; } + String transportMethod = u9Data.getStr(ShipmentOrderInfoFieldEnum.transportMethod.name()); + if (StrUtil.isBlank(transportMethod) || !transportMethod.contains("顺丰")) { + // 需要终止任务,匹配字段失败,失败原因:运输方式及货场名称不为顺丰 + log.warn("常规出货单号 {} 匹配字段失败:运输方式及货场名称不为顺丰 {} ", orderNo , transportMethod); + return false; + } + // 3. 调用 Service 存入数据库 (内部带 @Transactional) // 直接传入拼接好的 fullFilePath logisticsOrderService.createOrderAndTask(orderNo, u9Data, fullFilePath); diff --git a/src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java b/src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java index a92c28a..dcd09d7 100644 --- a/src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java +++ b/src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java @@ -38,9 +38,19 @@ public class ErpUpdateWaybillHandler implements ApiTaskHandler { if (StrUtil.isBlank(waybillNo)) { throw new RuntimeException("本地订单缺少顺丰运单号,无法回写 ERP"); } + String logInfo = String.format("Order: %s, Waybill: %s, Resource: %s", + order.getOrderNo(), order.getSfWaybillNo(), order.getResourceCode()); + task.setRequestData(logInfo); // 2. 调用 U9 专线执行更新 - erpService.updateWaybillToErp(order.getOrderNo(), waybillNo , order.getResourceCode() , order.getOrderType()); + try { + erpService.updateWaybillToErp(order.getOrderNo(), order.getSfWaybillNo(), + order.getResourceCode(), order.getOrderType()); + task.setResponseData("ERP Update Success"); + } catch (Exception e) { + task.setResponseData("ERP Update Failed: " + e.getMessage()); + throw e; + } // 3. 更新本地订单状态机 order.setOrderStatus(OrderStatusEnum.ERP_WAYBILL_UPDATED.getCode()); diff --git a/src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java b/src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java index 6abf71a..05d1178 100644 --- a/src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java +++ b/src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java @@ -1,7 +1,6 @@ package com.project.logistics.domain.strategy.handler; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -100,7 +99,7 @@ public class SfCreateOrderHandler implements ApiTaskHandler { sender.put("contactType", SfContactTypeEnum.SENDER.getCode()); // 寄付取业务的名字,到付取固定 sender.put("contact", SfPayMethodEnum.SENDER_PAY.equals(senderPayByU9Desc) ? - u9Data.getString(ShipmentOrderInfoFieldEnum.payer.name()) : + u9Data.getString(ShipmentOrderInfoFieldEnum.salesman.name()) : fixedRuleProperties.getSenderPayContact()); sender.put("mobile", fixedRuleProperties.getSenderMobile()); sender.put("address", fixedRuleProperties.getSenderAddress()); @@ -109,7 +108,7 @@ public class SfCreateOrderHandler implements ApiTaskHandler { // 收件方 (从 U9 快照中动态获取) JSONObject receiver = new JSONObject(); receiver.put("contactType", SfContactTypeEnum.RECIPIENT.getCode()); - receiver.put("contact", u9Data.getString(ShipmentOrderInfoFieldEnum.recipientContact.name())); // 假设 U9 字段名是这个 + receiver.put("contact", u9Data.getString(ShipmentOrderInfoFieldEnum.recipientContact.name())); receiver.put("mobile", u9Data.getString(ShipmentOrderInfoFieldEnum.recipientMobile.name())); receiver.put("address", u9Data.getString(ShipmentOrderInfoFieldEnum.recipientAddress.name())); contactInfoList.add(receiver); @@ -134,8 +133,19 @@ public class SfCreateOrderHandler implements ApiTaskHandler { msgData.put("serviceList", serviceList); } - // 3. 发起下单请求 - String result = sfApiService.callSfApi("EXP_RECE_CREATE_ORDER", msgData, null); + task.setRequestData(JSON.toJSONString(msgData)); + + + // 发起下单请求 + String result = ""; + try { + result = sfApiService.callSfApi("EXP_RECE_CREATE_ORDER", msgData, null); + // 【记录响应报文】 + task.setResponseData(result); + } catch (Exception e) { + task.setResponseData("HTTP请求异常: " + e.getMessage()); + throw e; + } // 4. 解析结果 (使用 JsonPath) try { diff --git a/src/main/java/com/project/logistics/domain/strategy/handler/SfDownloadWaybillHandler.java b/src/main/java/com/project/logistics/domain/strategy/handler/SfDownloadWaybillHandler.java index 6d43dff..0cb326b 100644 --- a/src/main/java/com/project/logistics/domain/strategy/handler/SfDownloadWaybillHandler.java +++ b/src/main/java/com/project/logistics/domain/strategy/handler/SfDownloadWaybillHandler.java @@ -1,5 +1,6 @@ package com.project.logistics.domain.strategy.handler; +import com.alibaba.fastjson.JSON; import com.project.logistics.config.WebDavProperties; import com.project.logistics.domain.entity.ApiRetryTaskEntity; import com.project.logistics.domain.entity.LogisticsOrderEntity; @@ -43,9 +44,11 @@ public class SfDownloadWaybillHandler implements ApiTaskHandler { @Transactional(rollbackFor = Exception.class) public void handle(ApiRetryTaskEntity task, LogisticsOrderEntity order) throws Exception { log.info(">>> 开始获取顺丰面单,单号: {}", order.getOrderNo()); + task.setRequestData("WaybillNo: " + order.getSfWaybillNo()); // 1. 调用顺丰云打印接口,获取 PDF 的临时下载链接 Map waybillPrintFile = sfApiService.getWaybillPrintFile(order.getSfWaybillNo()); + task.setResponseData(JSON.toJSONString(waybillPrintFile)); // 2. 通过 URL 下载 PDF 二进制流 byte[] pdfBytes = sfApiService.downloadWaybillPdfWithAuth(waybillPrintFile.get("url") , diff --git a/src/main/java/com/project/logistics/domain/strategy/handler/SfUploadResourceHandler.java b/src/main/java/com/project/logistics/domain/strategy/handler/SfUploadResourceHandler.java index 4c941ff..c865b9e 100644 --- a/src/main/java/com/project/logistics/domain/strategy/handler/SfUploadResourceHandler.java +++ b/src/main/java/com/project/logistics/domain/strategy/handler/SfUploadResourceHandler.java @@ -43,6 +43,9 @@ public class SfUploadResourceHandler implements ApiTaskHandler { // 1. 下载并上传(之前的三步走流程) byte[] pdfBytes = webDavService.downloadFile(order.getOriginalPdfPath()); String finalResult = sfApiService.uploadResourceFlow(pdfBytes, order.getOrderNo() + ".pdf"); + // 记录报文 + task.setRequestData("File size: " + pdfBytes.length + " bytes"); // 文件太大不存Data + task.setResponseData(finalResult); try { // --- 第一步:校验外层状态 --- String apiCode = JsonPath.read(finalResult, "$.apiResultCode"); diff --git a/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java b/src/main/java/com/project/receive/strategy/handler/ErpUpdateFeeHandler.java similarity index 97% rename from src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java rename to src/main/java/com/project/receive/strategy/handler/ErpUpdateFeeHandler.java index 2bae756..fd8e623 100644 --- a/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java +++ b/src/main/java/com/project/receive/strategy/handler/ErpUpdateFeeHandler.java @@ -1,4 +1,4 @@ -package com.project.receive.strategy; +package com.project.receive.strategy.handler; import com.project.logistics.domain.entity.ApiRetryTaskEntity; import com.project.logistics.domain.entity.LogisticsOrderEntity; diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..3254eb7 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,118 @@ +server: + port: 9088 +spring: + main: + # 允许 Bean 覆盖,解决 dynamic-datasource 与 JPA 的初始化冲突 + allow-bean-definition-overriding: true + datasource: + url: jdbc:mysql://8.129.84.155:3306/auto_logistics?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false + username: logistics_admin + password: Itc@123456 + driver-class-name: com.mysql.cj.jdbc.Driver + + # dynamic: + # primary: master + # datasource: + # master: + # driverClassName: com.mysql.cj.jdbc.Driver + # password: Itc@123456 + # url: jdbc:mysql://8.129.84.155:3306/auto_logistics?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false + # username: logistics_admin + + data: + redis: + host: 8.129.84.155 + port: 6379 + password: 123456 + database: 6 + timeout: 5000ms + lettuce: + pool: + max-active: 8 + max-idle: 30 + max-wait: 10000 + min-idle: 10 + jpa: + hibernate: + # 确保是 update + ddl-auto: update + # 显式指定数据库平台 + database-platform: org.hibernate.dialect.MySQL8Dialect + show-sql: true + # 关键:告诉 Hibernate 自动扫描实体类 + open-in-view: true + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + # 显式指定命名策略,防止大小写或下划线解析错误 + physical_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +sf: + api: + partnerId: Y847O1KA + secret: ZXmoWOQdSd2UTBmSP6Kv3VW9Q4N5dJqz + tokenUrl: https://sfapi-sbox.sf-express.com/oauth2/accessToken + baseUrl: https://sfapi-sbox.sf-express.com/std/service + channelCode: MCS-CAS-API-BOX +mybatis-plus: + configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境下打印SQL + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + map-underscore-to-camel-case: true # 开启驼峰命名 + global-config: + db-config: + id-type: assign_id # 使用雪花算法生成本地订单主键ID +webdav: + url: "http://8.129.84.155:8881" + username: "admin" + password: "123456" +logistics: + scanner: + enabled: true + # 每 10 分钟唤醒一次 (系统开销微乎其微) + cron: "0 0/10 * * * ?" + windows: + # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 + - name: "上午11点班次" + startTime: "11:00" + endTime: "11:10" + intervalMinutes: 60 + # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 + - name: "下午至傍晚波次" + startTime: "15:00" + endTime: "20:00" + intervalMinutes: 20 + sample-scanner: + enabled: true + # 每 10 分钟唤醒一次 (系统开销微乎其微) + cron: "0 0/10 * * * ?" + windows: + # 这个窗口设置10分钟有效期,配合10分钟一次的唤醒,只会跑一次 + - name: "上午11点班次" + startTime: "11:00" + endTime: "11:10" + intervalMinutes: 60 + # 这个窗口在15-20点之间,每隔20分钟会真正跑一次逻辑 + - name: "下午至傍晚波次" + startTime: "15:00" + endTime: "20:00" + intervalMinutes: 20 +logging: + level: + # 强制打印 Hibernate 初始化过程 + org.hibernate.SQL: debug + org.hibernate.orm.deprecation: error + org.hibernate.tool.schema: debug + # 看看 Spring 到底有没有加载 JPA + org.springframework.orm.jpa: debug +u9-source: + url: jdbc:sqlserver://192.168.4.202:1433;databaseName=20241030;encrypt=false;trustServerCertificate=true + username: sa + password: 'Liujun1928374650' + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +fixed-rule: + senderPayContact: '王盛荣' + senderMobile: '13480155048' + senderAddress: '广东省广州市番禺区石碁镇南荔东路56号' + monthlyCard: '7551234567' +scheduled-task: + owner: test \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 58e62d7..adfb414 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -55,7 +55,8 @@ sf: channelCode: MCS-CAS-API-BOX mybatis-plus: configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境下打印SQL +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境下打印SQL + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl map-underscore-to-camel-case: true # 开启驼峰命名 global-config: db-config: