Browse Source

调接口逻辑

master
luoweijian 3 weeks ago
parent
commit
e1b659a529
  1. 31
      src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java
  2. 13
      src/main/java/com/project/logistics/controller/WenDavScammerController.java
  3. 28
      src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java
  4. 3
      src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java
  5. 160
      src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java
  6. 9
      src/main/java/com/project/logistics/domain/service/base/ErpService.java
  7. 123
      src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java
  8. 1
      src/main/java/com/project/logistics/domain/service/base/impl/LogisticsOrderServiceImpl.java
  9. 2
      src/main/java/com/project/logistics/domain/strategy/handler/ErpUpdateWaybillHandler.java
  10. 16
      src/main/java/com/project/receive/domain/service/ReceiveService.java
  11. 3
      src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java
  12. 19
      src/main/resources/application.yml

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

@ -0,0 +1,31 @@
package com.project.logistics.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Data
@Component
@ConfigurationProperties(prefix = "logistics.sample-scanner")
public class LogisticsSampleScannerProperties {
private boolean enabled = true;
private String goLiveTime = "2026-04-01";
/**
* 默认每 10 分钟唤醒一次检查10分钟唤醒一次开销极低且能满足20分钟及以上的间隔
*/
private String cron = "0 0/10 * * * ?";
private List<LogisticsScannerProperties.ScanWindow> windows = new ArrayList<>();
@Data
public static class ScanWindow {
private String name; // 描述:如 "上午班次"
private String startTime; // "11:00"
private String endTime; // "12:00"
private int intervalMinutes; // 最小执行间隔,如 20
}
}

13
src/main/java/com/project/logistics/controller/WenDavScammerController.java

@ -2,6 +2,7 @@ package com.project.logistics.controller;
import com.project.base.domain.result.Result; import com.project.base.domain.result.Result;
import com.project.logistics.domain.scheduler.SampleOrderScannerJob;
import com.project.logistics.domain.scheduler.WebDavScannerJob; import com.project.logistics.domain.scheduler.WebDavScannerJob;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -15,9 +16,19 @@ public class WenDavScammerController {
@Autowired @Autowired
private WebDavScannerJob webDavScannerJob; private WebDavScannerJob webDavScannerJob;
@Autowired
private SampleOrderScannerJob sampleOrderScannerJob;
@GetMapping("/doScan") @GetMapping("/doScan")
public Result<String> receiveOrderState() { public Result<String> doScan() {
webDavScannerJob.doScanWork(); webDavScannerJob.doScanWork();
return Result.success("扫描成功"); return Result.success("扫描成功");
} }
@GetMapping("/doSampleScan")
public Result<String> doSampleScan() {
sampleOrderScannerJob.doScanWork();
return Result.success("扫描成功");
}
} }

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

@ -0,0 +1,28 @@
package com.project.logistics.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum SampleOrderInfoFieldEnum {
orderNo("DocNo" , "出货单号") ,
actualSendTime("DescFlexField_PubDescSeg19" , "实际发货时间"),
salesman("DescFlexField_PrivateDescSeg7" , "CRM业务员") ,
transportMethod("DescFlexField_PrivateDescSeg11" , "运输方式及货场名称") ,
recipientContact("DescFlexField_PubDescSeg15" , "收货人") ,
recipientMobile("DescFlexField_PubDescSeg16" , "收货人联系电话") ,
recipientAddress("DescFlexField_PubDescSeg14" , "收货地址") ,
payer("DescFlexField_PubDescSeg20" , "运费承担") ,
waybillNo("DescFlexField_PrivateDescSeg20" , "货运单号") ,
expressType("DescFlexField_PubDescSeg25" , "寄付方式") ,
fee("DescFlexField_PrivateDescSeg21" , "运费") ,
quantity("DescFlexField_PrivateDescSeg13" , "件数") ,
;
private final String fieldName;
private final String desc;
}

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

@ -10,7 +10,6 @@ public enum ShipmentOrderInfoFieldEnum {
orderNo("DocNo", "出货单号"), orderNo("DocNo", "出货单号"),
salesman("DescFlexField_PrivateDescSeg15", "CRM业务员") , salesman("DescFlexField_PrivateDescSeg15", "CRM业务员") ,
transportMethod("DescFlexField_PrivateDescSeg4" , "运输方式及货场名称") , transportMethod("DescFlexField_PrivateDescSeg4" , "运输方式及货场名称") ,
piece("DescFlexField_PrivateDescSeg2" , "物流件数") ,
recipientContact("DescFlexField_PrivateDescSeg8" , "收货人") , recipientContact("DescFlexField_PrivateDescSeg8" , "收货人") ,
recipientMobile("DescFlexField_PrivateDescSeg5" , "收货人联系电话") , recipientMobile("DescFlexField_PrivateDescSeg5" , "收货人联系电话") ,
recipientAddress("DescFlexField_PrivateDescSeg7" , "收货地址") , recipientAddress("DescFlexField_PrivateDescSeg7" , "收货地址") ,
@ -19,7 +18,7 @@ public enum ShipmentOrderInfoFieldEnum {
expressType("DescFlexField_PrivateDescSeg20","寄付方式") , expressType("DescFlexField_PrivateDescSeg20","寄付方式") ,
fee("DescFlexField_PrivateDescSeg3" , "运费") , fee("DescFlexField_PrivateDescSeg3" , "运费") ,
resourceCode("DescFlexField_PrivateDescSeg18" , "资源编码"), resourceCode("DescFlexField_PrivateDescSeg18" , "资源编码"),
quantity("SM_Ship.DescFlexField_PrivateDescSeg2" , "件数") , quantity("DescFlexField_PrivateDescSeg2" , "件数") ,
; ;
private final String fieldName; private final String fieldName;

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

@ -0,0 +1,160 @@
package com.project.logistics.domain.scheduler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import com.project.logistics.config.LogisticsScannerProperties;
import com.project.logistics.config.LogisticsSampleScannerProperties; // 注入新配置类
import com.project.logistics.domain.entity.ApiRetryTaskEntity;
import com.project.logistics.domain.entity.LogisticsOrderEntity;
import com.project.logistics.domain.enums.*;
import com.project.logistics.domain.service.base.ApiRetryTaskService;
import com.project.logistics.domain.service.base.ErpService;
import com.project.logistics.domain.service.base.LogisticsOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class SampleOrderScannerJob {
@Autowired
private LogisticsSampleScannerProperties sampleProperties; // 使用样品单专有配置
@Autowired
private StringRedisTemplate redisTemplate;
// 独立的 Redis 控制 key
private static final String REDIS_LAST_SUCCESS_KEY = "auto_logistics:sample_scanner:last_success_ts";
@Autowired
private LogisticsOrderService logisticsOrderService;
@Autowired
private ApiRetryTaskService apiRetryTaskService;
@Autowired
private ErpService erpService;
/**
* 读取样品单专有的 Cron 配置进行唤醒
*/
@Scheduled(cron = "${logistics.sample-scanner.cron:0 0/10 * * * ?}")
public void execute() {
if (!sampleProperties.isEnabled()) return;
LocalTime now = LocalTime.now();
// 使用样品单专有的时间窗口配置
LogisticsScannerProperties.ScanWindow currentWindow = findMatchedWindow(now);
if (currentWindow == null) {
log.debug("当前时刻 {} 不在样品单业务窗口内", now);
return;
}
if (checkInterval(currentWindow)) {
log.info(">>> 命中样品单业务窗口 [{}], 开始扫描 U9 待下单数据...", currentWindow.getName());
boolean success = doScanWork();
if (success) {
redisTemplate.opsForValue().set(REDIS_LAST_SUCCESS_KEY,
String.valueOf(System.currentTimeMillis()), 24, TimeUnit.HOURS);
}
}
}
/**
* 核心扫库逻辑
*/
public boolean doScanWork() {
try {
// 1. 调用 ErpService 查询符合“样品单”条件的数据
List<JSONObject> sampleOrders = erpService.listPendingSampleOrders();
if (CollUtil.isEmpty(sampleOrders)) {
log.info("U9 样品单库中暂无待处理数据");
return true;
}
log.info("发现 {} 个样品单需要同步下单...", sampleOrders.size());
int successCount = 0;
for (JSONObject u9Data : sampleOrders) {
String orderNo = u9Data.getStr("orderNo");
try {
if (processSampleImport(orderNo, u9Data)) {
successCount++;
}
} catch (Exception e) {
log.error("样品单 [{}] 导入失败: {}", orderNo, e.getMessage());
}
}
log.info(">>> 样品单同步结束。总共获取 {} 条,成功启动自动化流程 {} 条", sampleOrders.size(), successCount);
return true;
} catch (Exception e) {
log.error("样品单扫库任务异常", e);
return false;
}
}
@Transactional(rollbackFor = Exception.class)
public boolean processSampleImport(String orderNo, JSONObject u9Data) throws Exception {
if (logisticsOrderService.existsByOrderNo(orderNo)) {
return false;
}
// 持久化订单:跳过上传资源步骤,直接设置为 RESOURCE_CREATED
LogisticsOrderEntity order = new LogisticsOrderEntity();
order.setOrderNo(orderNo);
order.setOrderType(OrderTypeEnum.SAMPLE.getCode());
order.setOrderStatus(OrderStatusEnum.RESOURCE_CREATED.getCode());
order.setOrderInfo(u9Data.toString());
order.setOriginalPdfPath(null);
logisticsOrderService.save(order);
// 创建异步下单任务
ApiRetryTaskEntity task = new ApiRetryTaskEntity();
task.setOrderNo(orderNo);
task.setActionCode(RetryActionEnum.SF_CREATE_ORDER.getCode());
task.setTaskStatus(TaskStatusEnum.PENDING.getCode());
task.setRetryCount(0);
task.setMaxRetries(5);
task.setNextExecuteTime(new Date());
apiRetryTaskService.save(task);
log.info(">>> 样品单 {} 导入成功,已开启下单链条", orderNo);
return true;
}
// --- 私有工具方法 ---
private boolean checkInterval(LogisticsScannerProperties.ScanWindow window) {
String lastTs = redisTemplate.opsForValue().get(REDIS_LAST_SUCCESS_KEY);
if (lastTs == null) return true;
long diffMinutes = (System.currentTimeMillis() - Long.parseLong(lastTs)) / (1000 * 60);
return diffMinutes >= window.getIntervalMinutes();
}
private LogisticsScannerProperties.ScanWindow findMatchedWindow(LocalTime now) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm");
// 关键:遍历的是样品单专有的 windows 集合
for (LogisticsScannerProperties.ScanWindow window : sampleProperties.getWindows()) {
LocalTime start = LocalTime.parse(window.getStartTime(), dtf);
LocalTime end = LocalTime.parse(window.getEndTime(), dtf);
if (!now.isBefore(start) && !now.isAfter(end)) return window;
}
return null;
}
}

9
src/main/java/com/project/logistics/domain/service/base/ErpService.java

@ -3,6 +3,7 @@ package com.project.logistics.domain.service.base;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List;
public interface ErpService { public interface ErpService {
@ -12,9 +13,11 @@ public interface ErpService {
/** /**
* 回写运单号到 U9 * 回写运单号到 U9
*/ */
void updateWaybillToErp(String orderNo, String waybillNo , String resourceCode) throws Exception; void updateWaybillToErp(String orderNo, String waybillNo , String resourceCode, String orderType) throws Exception;
void updateStatusToErp(String orderNo, String statusDesc) throws Exception;
void updateFeeToErp(String orderNo, Integer qty, BigDecimal amount) throws Exception; void updateFeeToErp(String orderNo, Integer qty, BigDecimal amount , String orderType) throws Exception;
List<JSONObject> listPendingSampleOrders();
} }

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

@ -1,6 +1,9 @@
package com.project.logistics.domain.service.base.impl; package com.project.logistics.domain.service.base.impl;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import com.project.logistics.config.LogisticsSampleScannerProperties;
import com.project.logistics.domain.enums.OrderTypeEnum;
import com.project.logistics.domain.enums.SampleOrderInfoFieldEnum;
import com.project.logistics.domain.enums.ShipmentOrderInfoFieldEnum; import com.project.logistics.domain.enums.ShipmentOrderInfoFieldEnum;
import com.project.logistics.domain.service.base.ErpService; import com.project.logistics.domain.service.base.ErpService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -10,7 +13,9 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@Service @Service
@Slf4j @Slf4j
@ -21,6 +26,10 @@ public class ErpServiceImpl implements ErpService {
@Qualifier("u9JdbcTemplate") @Qualifier("u9JdbcTemplate")
private JdbcTemplate u9JdbcTemplate; private JdbcTemplate u9JdbcTemplate;
@Autowired
private LogisticsSampleScannerProperties sampleProperties; // 注入配置类
@Override @Override
public JSONObject getShipmentOrderInfo(String orderNo) throws Exception { public JSONObject getShipmentOrderInfo(String orderNo) throws Exception {
log.info(">>> 正在通过专属 U9 管道查询单据: {}", orderNo); log.info(">>> 正在通过专属 U9 管道查询单据: {}", orderNo);
@ -30,7 +39,6 @@ public class ErpServiceImpl implements ErpService {
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.orderNo.getFieldName() , ShipmentOrderInfoFieldEnum.orderNo.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.orderNo.getFieldName() , ShipmentOrderInfoFieldEnum.orderNo.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.salesman.getFieldName() , ShipmentOrderInfoFieldEnum.salesman.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.salesman.getFieldName() , ShipmentOrderInfoFieldEnum.salesman.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.transportMethod.getFieldName() , ShipmentOrderInfoFieldEnum.transportMethod.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.transportMethod.getFieldName() , ShipmentOrderInfoFieldEnum.transportMethod.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.piece.getFieldName() , ShipmentOrderInfoFieldEnum.piece.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientContact.getFieldName() , ShipmentOrderInfoFieldEnum.recipientContact.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientContact.getFieldName() , ShipmentOrderInfoFieldEnum.recipientContact.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientMobile.getFieldName() , ShipmentOrderInfoFieldEnum.recipientMobile.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientMobile.getFieldName() , ShipmentOrderInfoFieldEnum.recipientMobile.name()) +
String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientAddress.getFieldName() , ShipmentOrderInfoFieldEnum.recipientAddress.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientAddress.getFieldName() , ShipmentOrderInfoFieldEnum.recipientAddress.name()) +
@ -51,56 +59,93 @@ public class ErpServiceImpl implements ErpService {
} }
@Override @Override
public void updateWaybillToErp(String orderNo, String waybillNo, String resourceCode) throws Exception { public void updateWaybillToErp(String orderNo, String waybillNo, String resourceCode, String orderType) throws Exception {
log.info(">>> [U9回写] 准备将运单号 {} 写入 U9 单据 {}", waybillNo, orderNo); log.info(">>> [U9回写] 准备将运单号 {} 写入 U9 单据 {}", waybillNo, orderNo);
String sql;
// SQL 说明:根据 U9 实际情况,更新扩展字段(如 DescFlexField_Pub_1)和同步状态 int rows;
String sql = String.format("UPDATE SM_Ship SET %s = ?,%s = ? WHERE DocNo = ?" , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() ,
ShipmentOrderInfoFieldEnum.resourceCode.getFieldName()); if (OrderTypeEnum.SHIPMENT.getCode().equals(orderType)) {
// --- 情况 A: 常规出货单 ---
try { log.info(">>> [U9回写] 正在更新【常规出货单】: {}", orderNo);
int rows = u9JdbcTemplate.update(sql, waybillNo , resourceCode , orderNo); // 假设字段:Pub_1存运单号,Pub_2存资源编码
sql = String.format("UPDATE SM_Ship SET %s = ?,%s = ? WHERE DocNo = ?" , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() ,
if (rows > 0) { ShipmentOrderInfoFieldEnum.resourceCode.getFieldName());
log.info(">>> [U9回写成功] 单号: {}", orderNo); rows = u9JdbcTemplate.update(sql, waybillNo , resourceCode , orderNo);
} else {
// 如果更新了 0 行,说明 U9 里的单号不存在,这在重试中可能是致命的 } else {
throw new RuntimeException("U9 中未找到对应的出货单据,回写失败"); log.info(">>> [U9回写] 正在更新【样品单】: {}", orderNo);
} sql = String.format("UPDATE InvDoc_LendTrans SET %s = ? WHERE DocNo = ?" , SampleOrderInfoFieldEnum.waybillNo.getFieldName());
} catch (Exception e) { rows = u9JdbcTemplate.update(sql, waybillNo , orderNo);
log.error(">>> [U9回写异常]: {}", e.getMessage());
throw e; // 抛出异常,触发 ApiRetryJob 的重试机制
} }
}
/**
* 通用的状态回写方法 (揽收签收)
*/
@Override
public void updateStatusToErp(String orderNo, String statusDesc) throws Exception {
log.info(">>> [U9回写状态] 单号: {}, 目标状态: {}", orderNo, statusDesc);
// 假设 U9 的状态字段是 StatusDesc,或者是自定义字段 DescFlexField_Pub_2
String sql = "UPDATE SM_Ship SET DescFlexField_Pub_2 = ? WHERE DocNo = ?";
int rows = u9JdbcTemplate.update(sql, statusDesc, orderNo);
if (rows == 0) { if (rows == 0) {
throw new RuntimeException("U9单据不存在,回写状态失败"); throw new RuntimeException("U9 中未找到对应的单据 [" + orderNo + "],回写失败");
} }
log.info(">>> [U9回写成功] 类型: {}, 单号: {}", orderType, orderNo);
} }
@Override @Override
public void updateFeeToErp(String orderNo, Integer qty, BigDecimal amount) throws Exception { public void updateFeeToErp(String orderNo, Integer qty, BigDecimal amount, String orderType) throws Exception {
// 财务安全:确保回写的金额 100% 是两位小数 // 财务安全:确保回写的金额 100% 是两位小数
BigDecimal formattedAmount = amount.setScale(2, java.math.RoundingMode.HALF_UP); BigDecimal formattedAmount = amount.setScale(2, java.math.RoundingMode.HALF_UP);
String sql;
int rows;
if (OrderTypeEnum.SHIPMENT.getCode().equals(orderType)) {
// --- 情况 A: 常规出货单 (使用 SM_Ship) ---
log.info(">>> [U9回写费用] 正在更新【常规出货单】: {}, 金额: {}", orderNo, formattedAmount);
sql = String.format("UPDATE SM_Ship SET %s = ?, %s = ? WHERE DocNo = ?" ,
ShipmentOrderInfoFieldEnum.quantity.getFieldName() ,
ShipmentOrderInfoFieldEnum.fee.getFieldName());
rows = u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo);
} else {
// --- 情况 B: 样品单/借出单 (使用 InvDoc_LendTrans) ---
log.info(">>> [U9回写费用] 正在更新【样品单】: {}, 金额: {}", orderNo, formattedAmount);
sql = String.format("UPDATE InvDoc_LendTrans SET %s = ?, %s = ? WHERE DocNo = ?" ,
SampleOrderInfoFieldEnum.quantity.getFieldName() ,
SampleOrderInfoFieldEnum.fee.getFieldName());
rows = u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo);
}
log.info(">>> [U9回写费用] 单号: {}, 最终金额: {}", orderNo, formattedAmount); if (rows == 0) {
throw new RuntimeException("U9 中未找到对应的单据 [" + orderNo + "],费用回写失败");
}
String sql = String.format("UPDATE SM_Ship SET %s = ?, %s = ? WHERE DocNo = ?" , log.info(">>> [U9费用回写成功] 类型: {}, 单号: {}", orderType, orderNo);
ShipmentOrderInfoFieldEnum.quantity.getFieldName() , }
ShipmentOrderInfoFieldEnum.fee.getFieldName());
// 执行更新 @Override
u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo); public List<JSONObject> listPendingSampleOrders() {
log.info(">>> 正在从 U9 查询待处理的【样品单】...");
// SQL 逻辑:查询类型为样品单、且顺丰单号为空、且审核通过的数据
String goLiveTime = sampleProperties.getGoLiveTime();
// 具体条件请根据 U9 实际字段修改
String sql = "SELECT DISTINCT" +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.orderNo.getFieldName() , SampleOrderInfoFieldEnum.orderNo.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.actualSendTime.getFieldName() , SampleOrderInfoFieldEnum.actualSendTime.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.salesman.getFieldName() , SampleOrderInfoFieldEnum.salesman.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.transportMethod.getFieldName() , SampleOrderInfoFieldEnum.transportMethod.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.recipientContact.getFieldName() , SampleOrderInfoFieldEnum.recipientContact.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.recipientMobile.getFieldName() , SampleOrderInfoFieldEnum.recipientMobile.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.recipientAddress.getFieldName() , SampleOrderInfoFieldEnum.recipientAddress.name()) +
String.format(" a.%s as %s , " , SampleOrderInfoFieldEnum.payer.getFieldName() , SampleOrderInfoFieldEnum.payer.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.fee.getFieldName() , SampleOrderInfoFieldEnum.fee.name()) +
String.format(" a.%s as %s " , SampleOrderInfoFieldEnum.quantity.getFieldName() , SampleOrderInfoFieldEnum.quantity.name()) +
"FROM InvDoc_LendTrans a WITH(NOLOCK) " +
"LEFT JOIN InvDoc_LendLine b WITH(NOLOCK) ON b.LendTrans = a.ID " +
"LEFT JOIN Base_Organization_Trl c WITH(NOLOCK) ON b.Org = c.ID " +
"WHERE c.ID = '1002011064787026' " + // 指定组织 ID
" AND a.Status = 0 " + // 状态为开立/待处理
" AND a.DescFlexField_PubDescSeg19 >= ? ";
List<Map<String, Object>> list = u9JdbcTemplate.queryForList(sql , goLiveTime);
return list.stream().map(JSONObject::new).collect(Collectors.toList());
} }
} }

1
src/main/java/com/project/logistics/domain/service/base/impl/LogisticsOrderServiceImpl.java

@ -14,6 +14,7 @@ import com.project.logistics.mapper.LogisticsOrderMapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.project.logistics.domain.enums.OrderTypeEnum;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;

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

@ -40,7 +40,7 @@ public class ErpUpdateWaybillHandler implements ApiTaskHandler {
} }
// 2. 调用 U9 专线执行更新 // 2. 调用 U9 专线执行更新
erpService.updateWaybillToErp(order.getOrderNo(), waybillNo , order.getResourceCode()); erpService.updateWaybillToErp(order.getOrderNo(), waybillNo , order.getResourceCode() , order.getOrderType());
// 3. 更新本地订单状态机 // 3. 更新本地订单状态机
order.setOrderStatus(OrderStatusEnum.ERP_WAYBILL_UPDATED.getCode()); order.setOrderStatus(OrderStatusEnum.ERP_WAYBILL_UPDATED.getCode());

16
src/main/java/com/project/receive/domain/service/ReceiveService.java

@ -107,15 +107,23 @@ public class ReceiveService {
order.setSfCurrentStateCode(opCode); order.setSfCurrentStateCode(opCode);
// 根据核心节点跳转 order_status 状态机 // 根据核心节点跳转 order_status 状态机
if ("50".equals(opCode)) { if (StrUtil.equals(SfRouteOpCodeEnum.PICKED_UP.name() , opCode)) {
// 顺丰已揽收 // 顺丰已揽收
order.setOrderStatus(OrderStatusEnum.PICKED_UP.getCode()); order.setOrderStatus(OrderStatusEnum.PICKED_UP.getCode());
log.info(">>> 单号 {} 状态机流转至: [PICKED_UP]", orderNo); log.info(">>> 单号 {} 状态机流转至: [PICKED_UP]", orderNo);
} }
else if ("80".equals(opCode)) { else if (StrUtil.equals(SfRouteOpCodeEnum.DELIVERED.name() , opCode)) {
// 客户已签收 // 1. 更新为已签收
order.setOrderStatus(OrderStatusEnum.DELIVERED.getCode()); order.setOrderStatus(OrderStatusEnum.DELIVERED.getCode());
log.info(">>> 单号 {} 状态机流转至: [DELIVERED]", orderNo);
// 2. 【新增逻辑】判断如果是样品单,流程直接到此为止
if (OrderTypeEnum.SAMPLE.getCode().equals(order.getOrderType())) {
log.info(">>> 样品单 {} 已签收,流程结束", orderNo);
order.setOrderStatus(OrderStatusEnum.FINISHED.getCode());
} else {
// 出货单则继续保持 DELIVERED,等待图片推送触发下载任务
log.info(">>> 出货单 {} 已签收,等待顺丰推送回单图片...", orderNo);
}
} }

3
src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java

@ -46,7 +46,8 @@ public class ErpUpdateFeeHandler implements ApiTaskHandler {
erpService.updateFeeToErp( erpService.updateFeeToErp(
order.getOrderNo(), order.getOrderNo(),
feeLog.getItemCount(), feeLog.getItemCount(),
feeLog.getTotalFeeAmt() feeLog.getTotalFeeAmt(),
order.getOrderType()
); );
// 3. 更新费用日志的同步状态 // 3. 更新费用日志的同步状态

19
src/main/resources/application.yml

@ -5,7 +5,7 @@ spring:
# 允许 Bean 覆盖,解决 dynamic-datasource 与 JPA 的初始化冲突 # 允许 Bean 覆盖,解决 dynamic-datasource 与 JPA 的初始化冲突
allow-bean-definition-overriding: true allow-bean-definition-overriding: true
datasource: 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 url: jdbc:mysql://8.129.84.155:3306/auto_logistics_dev?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useSSL=false
username: logistics_admin username: logistics_admin
password: Itc@123456 password: Itc@123456
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
@ -80,6 +80,21 @@ logistics:
startTime: "15:00" startTime: "15:00"
endTime: "20:00" endTime: "20:00"
intervalMinutes: 20 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: logging:
level: level:
# 强制打印 Hibernate 初始化过程 # 强制打印 Hibernate 初始化过程
@ -99,4 +114,4 @@ fixed-rule:
senderAddress: '广东省广州市番禺区石碁镇南荔东路56号' senderAddress: '广东省广州市番禺区石碁镇南荔东路56号'
monthlyCard: '7551234567' monthlyCard: '7551234567'
scheduled-task: scheduled-task:
owner: local owner: test
Loading…
Cancel
Save