10 changed files with 281 additions and 11 deletions
@ -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"); |
||||
|
} |
||||
|
} |
||||
@ -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
|
||||
|
} |
||||
|
} |
||||
@ -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…
Reference in new issue