Browse Source

bug修改

master
luoweijian 2 weeks ago
parent
commit
7050144ff9
  1. 2
      src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java
  2. 127
      src/main/java/com/project/logistics/controller/ApiRetryTaskController.java
  3. 4
      src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java
  4. 4
      src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java
  5. 2
      src/main/java/com/project/logistics/domain/scheduler/ApiRetryJob.java
  6. 4
      src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java
  7. 22
      src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java
  8. 2
      src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java
  9. 12
      src/main/java/com/project/receive/controller/FaviconController.java
  10. 113
      src/main/resources/application-prod.yml

2
src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java

@ -13,7 +13,7 @@ import java.util.List;
public class LogisticsSampleScannerProperties { public class LogisticsSampleScannerProperties {
private boolean enabled = true; private boolean enabled = true;
private String goLiveTime = "2026-04-01"; private String goLiveTime = "2026-05-19";
/** /**
* 默认每 10 分钟唤醒一次检查10分钟唤醒一次开销极低且能满足20分钟及以上的间隔 * 默认每 10 分钟唤醒一次检查10分钟唤醒一次开销极低且能满足20分钟及以上的间隔
*/ */

127
src/main/java/com/project/logistics/controller/ApiRetryTaskController.java

@ -0,0 +1,127 @@
package com.project.logistics.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.project.logistics.domain.entity.ApiRetryTaskEntity;
import com.project.logistics.domain.entity.LogisticsOrderEntity;
import com.project.logistics.domain.service.base.ApiRetryTaskService;
import com.project.logistics.domain.service.base.LogisticsOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/api/task")
public class ApiRetryTaskController {
@Autowired
private ApiRetryTaskService apiRetryTaskService;
@Autowired
private LogisticsOrderService logisticsOrderService;
@GetMapping("/{orderNo}")
public Map<String, Object> getTaskDetail(@PathVariable String orderNo) throws Exception {
// 1. 查订单信息
LogisticsOrderEntity order = logisticsOrderService.getByOrderNo(orderNo);
if (order == null) {
return Map.of("code", 404, "msg", "订单不存在: " + orderNo);
}
// 2. 查该订单所有任务(按创建时间正序,组成时间线)
List<ApiRetryTaskEntity> tasks = apiRetryTaskService.lambdaQuery()
.eq(ApiRetryTaskEntity::getOrderNo, orderNo)
.orderByAsc(ApiRetryTaskEntity::getCreateTime)
.list();
// 3. 构时间线
List<Map<String, Object>> timeline = tasks.stream().map(task -> {
Map<String, Object> item = new LinkedHashMap<>();
item.put("actionCode", task.getActionCode());
item.put("status", task.getTaskStatus());
item.put("retryCount", task.getRetryCount() + "/" + task.getMaxRetries());
item.put("createTime", formatTime(task.getCreateTime()));
item.put("executeTime", formatTime(task.getUpdateTime()));
item.put("nextExecuteTime", formatTime(task.getNextExecuteTime()));
item.put("errorMessage", task.getErrorMessage());
// 请求/响应报文太长的只显示前1000字符
if (StrUtil.isNotBlank(task.getRequestData())) {
String req = task.getRequestData();
item.put("requestData", req.length() > 1000 ? req.substring(0, 1000) + "..." : req);
}
if (StrUtil.isNotBlank(task.getResponseData())) {
String resp = task.getResponseData();
item.put("responseData", resp.length() > 1000 ? resp.substring(0, 1000) + "..." : resp);
}
return item;
}).toList();
// 4. 汇总
Map<String, Object> orderInfo = new LinkedHashMap<>();
orderInfo.put("orderNo", order.getOrderNo());
orderInfo.put("orderType", order.getOrderType());
orderInfo.put("orderStatus", order.getOrderStatus());
orderInfo.put("sfWaybillNo", order.getSfWaybillNo());
orderInfo.put("resourceCode", order.getResourceCode());
orderInfo.put("createTime", formatTime(order.getCreateTime()));
orderInfo.put("orderInfo", StrUtil.isNotBlank(order.getOrderInfo()) ?
(order.getOrderInfo().length() > 500 ? order.getOrderInfo().substring(0, 500) + "..." : order.getOrderInfo()) : null);
return Map.of(
"code", 200,
"order", orderInfo,
"tasks", timeline,
"taskCount", tasks.size()
);
}
/**
* 可选根据actionCode查看所有失败的任务用于全局排查
*/
@GetMapping("/failed")
public Map<String, Object> listFailedTasks(
@RequestParam(required = false) String actionCode,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
var query = apiRetryTaskService.lambdaQuery()
.in(ApiRetryTaskEntity::getTaskStatus, List.of("FAILED", "MAX_RETRY_FAILED"));
if (StrUtil.isNotBlank(actionCode)) {
query.eq(ApiRetryTaskEntity::getActionCode, actionCode);
}
long total = query.count();
List<ApiRetryTaskEntity> tasks = query
.orderByDesc(ApiRetryTaskEntity::getUpdateTime).list();
List<Map<String, Object>> list = tasks.stream().map(task -> {
Map<String, Object> item = new LinkedHashMap<>();
item.put("orderNo", task.getOrderNo());
item.put("actionCode", task.getActionCode());
item.put("status", task.getTaskStatus());
item.put("retryCount", task.getRetryCount());
item.put("errorMessage", task.getErrorMessage());
item.put("updateTime", formatTime(task.getUpdateTime()));
return item;
}).toList();
//
return Map.of(
"code", 200,
"data", list,
"total", total,
"page", page,
"size", size
);
}
private String formatTime(Date date) {
return date == null ? null : DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
}
}

4
src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java

@ -16,9 +16,11 @@ public enum SampleOrderInfoFieldEnum {
recipientAddress("DescFlexField_PubDescSeg14" , "收货地址") , recipientAddress("DescFlexField_PubDescSeg14" , "收货地址") ,
payer("DescFlexField_PubDescSeg20" , "运费承担") , payer("DescFlexField_PubDescSeg20" , "运费承担") ,
waybillNo("DescFlexField_PrivateDescSeg20" , "货运单号") , waybillNo("DescFlexField_PrivateDescSeg20" , "货运单号") ,
expressType("DescFlexField_PrivateDescSeg25" , "寄付方式") , expressType("DescFlexField_PrivateDescSeg26" , "寄付方式") ,
fee("DescFlexField_PrivateDescSeg21" , "运费") , fee("DescFlexField_PrivateDescSeg21" , "运费") ,
quantity("DescFlexField_PrivateDescSeg13" , "件数") , quantity("DescFlexField_PrivateDescSeg13" , "件数") ,
freightPhone("DescFlexField_PrivateDescSeg23" , "货场电话") ,
createdOn("CreatedOn" , "创建人") ,
; ;

4
src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java

@ -19,6 +19,10 @@ public enum ShipmentOrderInfoFieldEnum {
fee("DescFlexField_PrivateDescSeg3" , "运费") , fee("DescFlexField_PrivateDescSeg3" , "运费") ,
resourceCode("DescFlexField_PrivateDescSeg18" , "资源编码"), resourceCode("DescFlexField_PrivateDescSeg18" , "资源编码"),
quantity("DescFlexField_PrivateDescSeg2" , "件数") , quantity("DescFlexField_PrivateDescSeg2" , "件数") ,
freightPhone("DescFlexField_PrivateDescSeg13" , "货场电话"),
createdOn("CreatedOn" , "创建人") ,
; ;
private final String fieldName; private final String fieldName;

2
src/main/java/com/project/logistics/domain/scheduler/ApiRetryJob.java

@ -116,7 +116,7 @@ public class ApiRetryJob {
task.setRetryCount(currentRetry); task.setRetryCount(currentRetry);
task.setErrorMessage(e.getMessage()); task.setErrorMessage(e.getMessage());
if (currentRetry >= task.getMaxRetries()) { if (currentRetry > task.getMaxRetries()) {
// 超过最大重试次数,标记为死信,需人工排查 // 超过最大重试次数,标记为死信,需人工排查
task.setTaskStatus(TaskStatusEnum.MAX_RETRY_FAILED.getCode()); task.setTaskStatus(TaskStatusEnum.MAX_RETRY_FAILED.getCode());
log.error("!!!任务已达最大重试次数,彻底失败,需人工介入!!!"); log.error("!!!任务已达最大重试次数,彻底失败,需人工介入!!!");

4
src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java

@ -92,6 +92,10 @@ public class SampleOrderScannerJob {
for (JSONObject u9Data : sampleOrders) { for (JSONObject u9Data : sampleOrders) {
String orderNo = u9Data.getStr("orderNo"); String orderNo = u9Data.getStr("orderNo");
// if (!StrUtil.equalsAny(orderNo , "YP-GD11-20260518-015" , "YP-S2913-20260519-010")) {
// log.error("样品单 [{}] 跳过处理", orderNo);
// continue;
// }
try { try {
if (processSampleImport(orderNo, u9Data)) { if (processSampleImport(orderNo, u9Data)) {
successCount++; successCount++;

22
src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java

@ -44,7 +44,9 @@ public class ErpServiceImpl implements ErpService {
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientAddress.getFieldName() , ShipmentOrderInfoFieldEnum.recipientAddress.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientAddress.getFieldName() , ShipmentOrderInfoFieldEnum.recipientAddress.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.payer.getFieldName() , ShipmentOrderInfoFieldEnum.payer.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.payer.getFieldName() , ShipmentOrderInfoFieldEnum.payer.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() , ShipmentOrderInfoFieldEnum.waybillNo.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() , ShipmentOrderInfoFieldEnum.waybillNo.name()) +
String.format("%s as %s" , ShipmentOrderInfoFieldEnum.expressType.getFieldName() , ShipmentOrderInfoFieldEnum.expressType.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.expressType.getFieldName() , ShipmentOrderInfoFieldEnum.expressType.name()) +
String.format("%s as %s" , ShipmentOrderInfoFieldEnum.createdOn.getFieldName() , ShipmentOrderInfoFieldEnum.createdOn.name()) +
" FROM SM_Ship WITH(NOLOCK) WHERE DocNo = ?"; " FROM SM_Ship WITH(NOLOCK) WHERE DocNo = ?";
try { try {
@ -75,7 +77,8 @@ public class ErpServiceImpl implements ErpService {
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.waybillNo.getFieldName() , SampleOrderInfoFieldEnum.waybillNo.name()) + String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.waybillNo.getFieldName() , SampleOrderInfoFieldEnum.waybillNo.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.expressType.getFieldName() , SampleOrderInfoFieldEnum.expressType.name()) + String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.expressType.getFieldName() , SampleOrderInfoFieldEnum.expressType.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.fee.getFieldName() , SampleOrderInfoFieldEnum.fee.name()) + String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.fee.getFieldName() , SampleOrderInfoFieldEnum.fee.name()) +
String.format(" a.%s as %s " , SampleOrderInfoFieldEnum.quantity.getFieldName() , SampleOrderInfoFieldEnum.quantity.name()) + String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.quantity.getFieldName() , SampleOrderInfoFieldEnum.quantity.name()) +
String.format(" a.%s as %s " , SampleOrderInfoFieldEnum.createdOn.getFieldName() , SampleOrderInfoFieldEnum.createdOn.name()) +
"FROM InvDoc_LendTrans a WITH(NOLOCK) " + "FROM InvDoc_LendTrans a WITH(NOLOCK) " +
"WHERE a.Org = '1002011064787026' " + // 指定组织 ID "WHERE a.Org = '1002011064787026' " + // 指定组织 ID
" AND a.Status = 0 " + // 状态为开立/待处理 " AND a.Status = 0 " + // 状态为开立/待处理
@ -129,19 +132,22 @@ public class ErpServiceImpl implements ErpService {
// --- 情况 A: 常规出货单 (使用 SM_Ship) --- // --- 情况 A: 常规出货单 (使用 SM_Ship) ---
log.info(">>> [U9回写费用] 正在更新【常规出货单】: {}, 金额: {}", orderNo, formattedAmount); log.info(">>> [U9回写费用] 正在更新【常规出货单】: {}, 金额: {}", orderNo, formattedAmount);
sql = String.format("UPDATE SM_Ship SET %s = ?, %s = ? WHERE DocNo = ?" , sql = String.format("UPDATE SM_Ship SET %s = ?, %s = ?,%s = ? WHERE DocNo = ?" ,
ShipmentOrderInfoFieldEnum.quantity.getFieldName() , ShipmentOrderInfoFieldEnum.quantity.getFieldName() ,
ShipmentOrderInfoFieldEnum.fee.getFieldName()); ShipmentOrderInfoFieldEnum.fee.getFieldName() ,
rows = u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo); ShipmentOrderInfoFieldEnum.freightPhone.getFieldName());
rows = u9JdbcTemplate.update(sql, qty, formattedAmount , "95338", orderNo);
} else { } else {
// --- 情况 B: 样品单/借出单 (使用 InvDoc_LendTrans) --- // --- 情况 B: 样品单/借出单 (使用 InvDoc_LendTrans) ---
log.info(">>> [U9回写费用] 正在更新【样品单】: {}, 金额: {}", orderNo, formattedAmount); log.info(">>> [U9回写费用] 正在更新【样品单】: {}, 金额: {}", orderNo, formattedAmount);
sql = String.format("UPDATE InvDoc_LendTrans SET %s = ?, %s = ? WHERE DocNo = ?" , sql = String.format("UPDATE InvDoc_LendTrans SET %s = ?, %s = ? , %s = ? WHERE DocNo = ?" ,
SampleOrderInfoFieldEnum.quantity.getFieldName() , SampleOrderInfoFieldEnum.quantity.getFieldName() ,
SampleOrderInfoFieldEnum.fee.getFieldName()); SampleOrderInfoFieldEnum.fee.getFieldName() ,
rows = u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo); SampleOrderInfoFieldEnum.freightPhone.getFieldName()
);
rows = u9JdbcTemplate.update(sql, qty, formattedAmount, "95338" , orderNo);
} }
if (rows == 0) { if (rows == 0) {

2
src/main/java/com/project/logistics/domain/strategy/handler/SfCreateOrderHandler.java

@ -118,6 +118,8 @@ public class SfCreateOrderHandler implements ApiTaskHandler {
cargoDetails.add(item); cargoDetails.add(item);
msgData.put("cargoDetails", cargoDetails); msgData.put("cargoDetails", cargoDetails);
msgData.put("remark" , u9Data.getString(ShipmentOrderInfoFieldEnum.createdOn.name()));
// 2.1 构造收寄双方信息 (根据你之前 SELECT * 看到的 U9 字段名来取值) // 2.1 构造收寄双方信息 (根据你之前 SELECT * 看到的 U9 字段名来取值)
JSONArray contactInfoList = new JSONArray(); JSONArray contactInfoList = new JSONArray();

12
src/main/java/com/project/receive/controller/FaviconController.java

@ -0,0 +1,12 @@
package com.project.receive.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FaviconController {
@GetMapping("favicon.ico")
void returnNoFavicon() {
// 映射此路径但不做任何操作,默认返回 200 或 204
}
}

113
src/main/resources/application-prod.yml

@ -0,0 +1,113 @@
server:
port: 9088
spring:
main:
# 允许 Bean 覆盖,解决 dynamic-datasource 与 JPA 的初始化冲突
allow-bean-definition-overriding: true
datasource:
url: jdbc:mysql://192.168.5.31:3306/auto_logistics?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false
username: root
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: 192.168.5.31
port: 6379
password: Itc@123456
database: 5
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: DgNS7ANgynXYcnhvu4moAe4rCAHkWvk9
tokenUrl: https://bspgw.sf-express.com/oauth2/accessToken
baseUrl: https://bspgw.sf-express.com/std/service
channelCode: INC-VMOS-CORE
podSecret: b4fb275c23204056
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://192.168.5.31:8081"
username: "admin"
password: "123456"
logistics:
scanner:
enabled: true
# 允许在 10点、15-20点的 第 8 分和第 28 分唤醒
# 唤醒时间点:10:08, 10:28, 15:08, 15:28, 16:08, 16:28 ... 20:28
cron: "0 2,32 10-19 * * ?"
windows:
- name: "日间半小时波次"
# 10:00 开始,确保 10:02 第一波顺利执行
startTime: "10:00"
# 【关键】设置为 19:15,这样 19:02 会执行,但 19:32 因为超过了 19:15 会被拦截
endTime: "19:15"
# 设置为 25,确保 02 分和 32 分(30分钟间隔)能通过频率检查
intervalMinutes: 25
sample-scanner:
enabled: true
cron: "0 2,32 10-19 * * ?"
windows:
- name: "日间半小时波次"
# 10:00 开始,确保 10:02 第一波顺利执行
startTime: "10:00"
# 【关键】设置为 19:15,这样 19:02 会执行,但 19:32 因为超过了 19:15 会被拦截
endTime: "19:15"
# 设置为 25,确保 02 分和 32 分(30分钟间隔)能通过频率检查
intervalMinutes: 25
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.206;databaseName=DataCenter;encrypt=false;trustServerCertificate=true
username: sa
password: 'Hello2018.world'
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
fixed-rule:
senderPayContact: '王盛荣'
senderMobile: '18998809792'
senderAddress: '广东省广州市番禺区石碁镇南荔东路56号'
monthlyCard: '0200467987'
scheduled-task:
owner: prod
Loading…
Cancel
Save