Browse Source

调接口逻辑

master
luoweijian 3 weeks ago
parent
commit
a0f604ac60
  1. 27
      src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java
  2. 9
      src/main/java/com/project/logistics/domain/scheduler/WebDavScannerJob.java
  3. 12
      src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java
  4. 20
      src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java
  5. 3
      src/main/java/com/project/logistics/domain/strategy/handler/SfDownloadWaybillHandler.java
  6. 3
      src/main/java/com/project/logistics/domain/strategy/handler/SfUploadResourceHandler.java
  7. 2
      src/main/java/com/project/receive/strategy/handler/ErpUpdateFeeHandler.java
  8. 118
      src/main/resources/application-test.yml
  9. 3
      src/main/resources/application.yml

27
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();

9
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);

12
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());

20
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 {

3
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<String, String> waybillPrintFile = sfApiService.getWaybillPrintFile(order.getSfWaybillNo());
task.setResponseData(JSON.toJSONString(waybillPrintFile));
// 2. 通过 URL 下载 PDF 二进制流
byte[] pdfBytes = sfApiService.downloadWaybillPdfWithAuth(waybillPrintFile.get("url") ,

3
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");

2
src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java → 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;

118
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

3
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:

Loading…
Cancel
Save