From e1b659a5292e4f21f272cb84785208e028e2de8c Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Tue, 31 Mar 2026 19:20:21 +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 --- .../LogisticsSampleScannerProperties.java | 31 ++++ .../controller/WenDavScammerController.java | 13 +- .../enums/SampleOrderInfoFieldEnum.java | 28 +++ .../enums/ShipmentOrderInfoFieldEnum.java | 3 +- .../scheduler/SampleOrderScannerJob.java | 160 ++++++++++++++++++ .../domain/service/base/ErpService.java | 9 +- .../service/base/impl/ErpServiceImpl.java | 123 +++++++++----- .../base/impl/LogisticsOrderServiceImpl.java | 1 + .../handler/ErpUpdateWaybillHandler.java | 2 +- .../domain/service/ReceiveService.java | 16 +- .../receive/strategy/ErpUpdateFeeHandler.java | 3 +- src/main/resources/application.yml | 19 ++- 12 files changed, 355 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java create mode 100644 src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java create mode 100644 src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java diff --git a/src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java b/src/main/java/com/project/logistics/config/LogisticsSampleScannerProperties.java new file mode 100644 index 0000000..e4a22f1 --- /dev/null +++ b/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 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 + } +} diff --git a/src/main/java/com/project/logistics/controller/WenDavScammerController.java b/src/main/java/com/project/logistics/controller/WenDavScammerController.java index 08bbf09..b35ceb5 100644 --- a/src/main/java/com/project/logistics/controller/WenDavScammerController.java +++ b/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.logistics.domain.scheduler.SampleOrderScannerJob; import com.project.logistics.domain.scheduler.WebDavScannerJob; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -15,9 +16,19 @@ public class WenDavScammerController { @Autowired private WebDavScannerJob webDavScannerJob; + @Autowired + private SampleOrderScannerJob sampleOrderScannerJob; + + @GetMapping("/doScan") - public Result receiveOrderState() { + public Result doScan() { webDavScannerJob.doScanWork(); return Result.success("扫描成功"); } + + @GetMapping("/doSampleScan") + public Result doSampleScan() { + sampleOrderScannerJob.doScanWork(); + return Result.success("扫描成功"); + } } diff --git a/src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java b/src/main/java/com/project/logistics/domain/enums/SampleOrderInfoFieldEnum.java new file mode 100644 index 0000000..144e1ff --- /dev/null +++ b/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; +} diff --git a/src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java b/src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java index 64fb533..3217bd8 100644 --- a/src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java +++ b/src/main/java/com/project/logistics/domain/enums/ShipmentOrderInfoFieldEnum.java @@ -10,7 +10,6 @@ public enum ShipmentOrderInfoFieldEnum { orderNo("DocNo", "出货单号"), salesman("DescFlexField_PrivateDescSeg15", "CRM业务员") , transportMethod("DescFlexField_PrivateDescSeg4" , "运输方式及货场名称") , - piece("DescFlexField_PrivateDescSeg2" , "物流件数") , recipientContact("DescFlexField_PrivateDescSeg8" , "收货人") , recipientMobile("DescFlexField_PrivateDescSeg5" , "收货人联系电话") , recipientAddress("DescFlexField_PrivateDescSeg7" , "收货地址") , @@ -19,7 +18,7 @@ public enum ShipmentOrderInfoFieldEnum { expressType("DescFlexField_PrivateDescSeg20","寄付方式") , fee("DescFlexField_PrivateDescSeg3" , "运费") , resourceCode("DescFlexField_PrivateDescSeg18" , "资源编码"), - quantity("SM_Ship.DescFlexField_PrivateDescSeg2" , "件数") , + quantity("DescFlexField_PrivateDescSeg2" , "件数") , ; private final String fieldName; diff --git a/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java b/src/main/java/com/project/logistics/domain/scheduler/SampleOrderScannerJob.java new file mode 100644 index 0000000..588d49a --- /dev/null +++ b/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 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/project/logistics/domain/service/base/ErpService.java b/src/main/java/com/project/logistics/domain/service/base/ErpService.java index ebb99fa..dd84863 100644 --- a/src/main/java/com/project/logistics/domain/service/base/ErpService.java +++ b/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 java.math.BigDecimal; +import java.util.List; public interface ErpService { @@ -12,9 +13,11 @@ public interface ErpService { /** * 回写运单号到 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 listPendingSampleOrders(); } diff --git a/src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java b/src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java index 12bb7bc..a7f348a 100644 --- a/src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java +++ b/src/main/java/com/project/logistics/domain/service/base/impl/ErpServiceImpl.java @@ -1,6 +1,9 @@ package com.project.logistics.domain.service.base.impl; 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.service.base.ErpService; import lombok.extern.slf4j.Slf4j; @@ -10,7 +13,9 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Service @Slf4j @@ -21,6 +26,10 @@ public class ErpServiceImpl implements ErpService { @Qualifier("u9JdbcTemplate") private JdbcTemplate u9JdbcTemplate; + + @Autowired + private LogisticsSampleScannerProperties sampleProperties; // 注入配置类 + @Override public JSONObject getShipmentOrderInfo(String orderNo) throws Exception { 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.salesman.getFieldName() , ShipmentOrderInfoFieldEnum.salesman.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.recipientMobile.getFieldName() , ShipmentOrderInfoFieldEnum.recipientMobile.name()) + String.format("%s as %s," , ShipmentOrderInfoFieldEnum.recipientAddress.getFieldName() , ShipmentOrderInfoFieldEnum.recipientAddress.name()) + @@ -51,56 +59,93 @@ public class ErpServiceImpl implements ErpService { } @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); - - // SQL 说明:根据 U9 实际情况,更新扩展字段(如 DescFlexField_Pub_1)和同步状态 - String sql = String.format("UPDATE SM_Ship SET %s = ?,%s = ? WHERE DocNo = ?" , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() , - ShipmentOrderInfoFieldEnum.resourceCode.getFieldName()); - - try { - int rows = u9JdbcTemplate.update(sql, waybillNo , resourceCode , orderNo); - - if (rows > 0) { - log.info(">>> [U9回写成功] 单号: {}", orderNo); - } else { - // 如果更新了 0 行,说明 U9 里的单号不存在,这在重试中可能是致命的 - throw new RuntimeException("U9 中未找到对应的出货单据,回写失败"); - } - } catch (Exception e) { - log.error(">>> [U9回写异常]: {}", e.getMessage()); - throw e; // 抛出异常,触发 ApiRetryJob 的重试机制 + String sql; + int rows; + + if (OrderTypeEnum.SHIPMENT.getCode().equals(orderType)) { + // --- 情况 A: 常规出货单 --- + log.info(">>> [U9回写] 正在更新【常规出货单】: {}", orderNo); + // 假设字段:Pub_1存运单号,Pub_2存资源编码 + sql = String.format("UPDATE SM_Ship SET %s = ?,%s = ? WHERE DocNo = ?" , ShipmentOrderInfoFieldEnum.waybillNo.getFieldName() , + ShipmentOrderInfoFieldEnum.resourceCode.getFieldName()); + rows = u9JdbcTemplate.update(sql, waybillNo , resourceCode , orderNo); + + } else { + log.info(">>> [U9回写] 正在更新【样品单】: {}", orderNo); + sql = String.format("UPDATE InvDoc_LendTrans SET %s = ? WHERE DocNo = ?" , SampleOrderInfoFieldEnum.waybillNo.getFieldName()); + rows = u9JdbcTemplate.update(sql, waybillNo , orderNo); } - } - /** - * 通用的状态回写方法 (揽收、签收) - */ - @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) { - throw new RuntimeException("U9单据不存在,回写状态失败"); + throw new RuntimeException("U9 中未找到对应的单据 [" + orderNo + "],回写失败"); } + log.info(">>> [U9回写成功] 类型: {}, 单号: {}", orderType, orderNo); } + @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% 是两位小数 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 = ?" , - ShipmentOrderInfoFieldEnum.quantity.getFieldName() , - ShipmentOrderInfoFieldEnum.fee.getFieldName()); + log.info(">>> [U9费用回写成功] 类型: {}, 单号: {}", orderType, orderNo); + } - // 执行更新 - u9JdbcTemplate.update(sql, qty, formattedAmount, orderNo); + @Override + public List 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> list = u9JdbcTemplate.queryForList(sql , goLiveTime); + return list.stream().map(JSONObject::new).collect(Collectors.toList()); } } diff --git a/src/main/java/com/project/logistics/domain/service/base/impl/LogisticsOrderServiceImpl.java b/src/main/java/com/project/logistics/domain/service/base/impl/LogisticsOrderServiceImpl.java index b7fb27e..3a52529 100644 --- a/src/main/java/com/project/logistics/domain/service/base/impl/LogisticsOrderServiceImpl.java +++ b/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.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.project.logistics.domain.enums.OrderTypeEnum; import java.util.Date; import java.util.Objects; 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 1cc46a4..a92c28a 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 @@ -40,7 +40,7 @@ public class ErpUpdateWaybillHandler implements ApiTaskHandler { } // 2. 调用 U9 专线执行更新 - erpService.updateWaybillToErp(order.getOrderNo(), waybillNo , order.getResourceCode()); + erpService.updateWaybillToErp(order.getOrderNo(), waybillNo , order.getResourceCode() , order.getOrderType()); // 3. 更新本地订单状态机 order.setOrderStatus(OrderStatusEnum.ERP_WAYBILL_UPDATED.getCode()); diff --git a/src/main/java/com/project/receive/domain/service/ReceiveService.java b/src/main/java/com/project/receive/domain/service/ReceiveService.java index ce629f5..f434600 100644 --- a/src/main/java/com/project/receive/domain/service/ReceiveService.java +++ b/src/main/java/com/project/receive/domain/service/ReceiveService.java @@ -107,15 +107,23 @@ public class ReceiveService { order.setSfCurrentStateCode(opCode); // 根据核心节点跳转 order_status 状态机 - if ("50".equals(opCode)) { + if (StrUtil.equals(SfRouteOpCodeEnum.PICKED_UP.name() , opCode)) { // 顺丰已揽收 order.setOrderStatus(OrderStatusEnum.PICKED_UP.getCode()); log.info(">>> 单号 {} 状态机流转至: [PICKED_UP]", orderNo); } - else if ("80".equals(opCode)) { - // 客户已签收 + else if (StrUtil.equals(SfRouteOpCodeEnum.DELIVERED.name() , opCode)) { + // 1. 更新为已签收 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); + } } diff --git a/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java b/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java index f75b1cf..2bae756 100644 --- a/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java +++ b/src/main/java/com/project/receive/strategy/ErpUpdateFeeHandler.java @@ -46,7 +46,8 @@ public class ErpUpdateFeeHandler implements ApiTaskHandler { erpService.updateFeeToErp( order.getOrderNo(), feeLog.getItemCount(), - feeLog.getTotalFeeAmt() + feeLog.getTotalFeeAmt(), + order.getOrderType() ); // 3. 更新费用日志的同步状态 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3c66a8c..58e62d7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,7 +5,7 @@ spring: # 允许 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 + 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 password: Itc@123456 driver-class-name: com.mysql.cj.jdbc.Driver @@ -80,6 +80,21 @@ logistics: 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 初始化过程 @@ -99,4 +114,4 @@ fixed-rule: senderAddress: '广东省广州市番禺区石碁镇南荔东路56号' monthlyCard: '7551234567' scheduled-task: - owner: local \ No newline at end of file + owner: test \ No newline at end of file