2026-06-11|订单关注功能 V2 轮询优化
一、今日主要工作
- 围绕订单关注功能继续进行 V2 优化,重点解决 V1 定时轮询中逐订单调用完整订单交付查询接口导致的性能问题。
- 梳理 V1 架构中
OrderFollowScheduler依赖OrderDeliveryService.queryOrder()的问题,明确调度器只需要订单状态与预计配送时间,不应复用面向前端详情展示的重查询链路。 - 完成订单关注轮询链路重构,将原来的“逐订单查询完整订单详情”优化为“批量读取快照、批量查询当前状态、内存 HashMap 比对、批量写回快照”的轻量链路。
- 新增 productsys 批量查询 Mapper,用于一次性获取所有关注订单的产品行状态和预计配送时间,避免对每个订单重复执行完整交付查询。
- 处理 V2 初版遗漏的预计配送时间变更检测,并修复 MyBatis 返回
LocalDateTime后直接强转 String 导致的类型转换问题。 - 梳理并调整订单关注调度方式,从应用内部
@Scheduled/TaskScheduler方案演进为由外部平台统一调用 OpenAPI 扫描接口。 - 完成
/openapi/order-follow/scan单一触发入口封装,便于外部调度平台按固定频率调用,并统一接入失败重试、监控告警等能力。 - 完成订单到货、发货、预计配送时间变更、首次关注和无变化等核心场景测试验证,并沉淀《订单关注功能 V2 优化 — 项目文档》。
二、核心完成内容
1. 模块开发与功能实现
1.1 V1 轮询性能问题分析
今天首先对订单关注 V1 的实现进行了复盘。V1 版本中,调度器使用 @Scheduled(fixedDelay = 10min) 定时触发,每次执行时会查询所有活跃关注订单号,然后逐个订单调用 OrderDeliveryService.queryOrder()。
该方法本身是为用户前端订单详情查询设计的,会加载完整订单数据,包括订单头、产品行、SN、物流追踪、快递信息等。对于用户主动查询一次订单详情,这种设计是合理的;但对于后台定时任务来说,调度器实际只需要订单当前状态和预计配送时间,用完整详情查询来驱动轮询存在明显的性能浪费。
在 V1 中,如果存在 500 个关注订单,就需要执行 500 次完整订单交付查询。每次查询涉及多张表关联和物流数据处理,整体耗时可能达到 25–40 分钟。这个问题的根因是:后台状态检测复用了面向前端详情展示的重接口,导致接口职责不匹配。
1.2 V2 批量轮询链路重构
针对上述问题,今天完成了订单关注轮询链路的 V2 重构。新的设计不再逐订单调用 queryOrder(),而是将完整轮询拆成三个批量阶段:
Step 1:从 zhineng_ai 批量读取关注订单快照Step 2:从 productsys 批量查询当前订单状态和预计配送时间Step 3:在 Java 内存中使用 HashMap 进行状态比对,并批量写回快照V2 的核心变化是把原来的 O(N) 远程查询,改为固定次数的批量查询。无论当前有 10 个、100 个还是 500 个关注订单,主要数据库查询都可以控制在固定数量级。
整体流程如下:
OrderFollowScheduler.checkStatusChanges() → batchGetStatusSnapshot() → batchGetDeliveryTimeSnapshot() → batchSelectProductStates(orderNumbers) → Java HashMap 内存比对 → 根据变化类型触发企微推送 → batchUpdateStatusSnapshot()这种方式将“状态变化检测”从逐订单远程拉取,转为批量拉取后在内存中做集合对比,显著降低了 productsys 查询压力,也提升了调度任务执行效率。
1.3 新增 productsys 批量查询能力
今天新增 OrderFollowProductSysMapper,用于在 productsys 数据源中批量查询关注订单的产品行状态。
核心查询逻辑为:
order_product JOIN sfsys_parameter WHERE orderTid IN (...)通过该查询可以一次性拿到所有关注订单的:
- 订单号;
- 产品行状态名称;
- 预计配送时间;
- 产品行标识。
对于同一订单存在多条产品行的情况,V2 通过订单号聚合,将查询结果整理为 Map<orderNumber, currentStatus> 和 Map<orderNumber, currentDeliveryTime>。后续状态判断在 Java 内存中完成,避免重复查询数据库。
1.4 使用 HashMap 完成状态比对
今天将状态对比逻辑调整为基于 HashMap 的内存比对。
调度器会先拿到两个状态集合:
prevStatusMap:来自 portal_order_follow 快照字段currentStatusMap:来自 productsys 当前状态随后按订单号遍历并判断:
- 如果历史状态为空,说明是首次检测,只写入快照,不推送;
- 如果历史状态与当前状态一致,则跳过;
- 如果状态从生产中变为已发货,则发送发货通知;
- 如果状态变为已到货,则发送到货通知,并将关注状态更新为
COMPLETED; - 如果状态未变但预计配送时间变化,则发送配送时间变更通知。
这种方式本质上是在应用层完成不同数据源之间的轻量 Join。由于 zhineng_ai 与 productsys 不在同一个数据源中,使用 Java HashMap 做应用层对比,比强行依赖跨库 JOIN 更灵活,也更适合当前多数据源架构。
1.5 补齐预计配送时间变更检测
V2 初版主要关注订单状态变化,但遗漏了 V1 中已有的预计配送时间变更检测。今天补齐了这一逻辑:即使订单状态未变化,只要 last_est_delivery_time 与 productsys 当前 deliver_predict_date 不一致,也会触发预计配送时间变更通知,并更新快照。
同时,在实现过程中发现 productsys 中的 deliver_predict_date 字段通过 MyBatis 返回的是 LocalDateTime 类型,如果直接强转为 String,会抛出 ClassCastException。
处理方式是改为:
Object dtObj = row.get("deliverPredictDate");if (dtObj != null) { currentDeliveryTimeMap.put(tid, String.valueOf(dtObj));}这样可以兼容不同 JDBC 驱动返回的时间类型,避免因为类型转换导致整个轮询任务中断。
1.6 企微推送消息模板优化
今天也整理了 V2 中的三类企微推送模板:
- 到货通知:订单已送达仓库,提醒相关人员完成入库验收和系统确认;
- 发货通知:订单已完成发货,提醒用户关注物流状态;
- 订单变更通知:预计发货时间发生变化,展示变更前后时间,并提示如有疑问可联系合同履行复核。
这些消息模板相比单纯展示状态变化更加清晰,有利于用户理解通知原因和后续操作。
2. 接口设计与文档沉淀
2.1 调度方式从内部任务调整为外部平台触发
今天还重点梳理并调整了订单关注任务的调度方式。
最初 V1 使用 @Scheduled 写死轮询间隔。V2 初版尝试通过 TaskScheduler 做动态调度,支持通过接口修改轮询间隔。但进一步分析后,发现公司已有外部统一调度平台,可以提供定时 HTTP 调用、失败重试、监控告警等能力。继续在业务应用内部维护调度器,会造成职责重复。
因此,最终方案调整为:应用不再负责调度策略,只保留业务扫描能力;外部平台负责按固定频率触发扫描。
本次最终保留的接口为:
GET /openapi/order-follow/scan外部平台只需要定时调用该接口,应用内部完成完整的批量查询、状态比对、企微推送和快照写回。
2.2 精简内部调度组件
基于外部平台统一调度的方案,今天对内部调度相关代码进行了精简:
- 删除内部动态调度管理类;
- 删除应用启动后自动注册任务的逻辑;
- 保留
OrderFollowScheduler.checkStatusChanges()作为业务扫描核心方法; - 新增或保留
OrderFollowOpenApiController,对外暴露单一/scan入口; - 在安全配置中放行
/openapi/**,便于内网调度平台调用。
这种方式让系统职责更加清晰:
- 外部平台负责什么时候执行、执行失败如何重试、如何监控;
- 业务应用负责被调用时完成订单关注状态检测与推送。
2.3 技术文档沉淀
今天同步沉淀了《订单关注功能 V2 优化 — 项目文档》,文档中记录了:
- V1 架构问题分析;
- V2 核心设计原则;
- 三次 DB 调用的批量查询方案;
- 批量读取快照、批量查 productsys、内存比对、批量写回的完整流程;
- 预计配送时间变更检测逻辑;
- 内部调度到外部平台触发的架构演进;
- V1 与 V2 的性能对比;
- 测试验证场景;
- 相关提交记录。
该文档可以作为后续代码 Review、合并 main、测试验收和线上问题排查的参考材料。
3. 测试验证与问题处理
3.1 本地测试验证
今天对订单关注 V2 进行了本地 dev 环境验证。测试方式主要是通过修改 productsys 测试库中的订单状态和预计配送时间来模拟真实状态变化,然后调用:
GET /openapi/order-follow/scan触发一次完整扫描。
验证场景包括:
- 订单从生产中变为已到货;
- 订单从生产中变为已发货;
- 订单状态不变但预计配送时间发生变化;
- 首次关注订单,历史快照为空;
- 订单状态无变化。
测试结果表明:
- 到货场景能够触发到货通知,并将关注状态更新为
COMPLETED; - 发货场景能够触发发货通知,并更新状态快照;
- 配送时间变更场景能够触发订单变更通知,并写回最新配送时间;
- 首次关注只写入快照,不误推送;
- 无变化订单会被跳过,不重复推送。
3.2 LocalDateTime 类型转换问题修复
测试过程中发现,MyBatis 查询 productsys 的 deliver_predict_date 后返回 LocalDateTime 类型,V2 初版代码直接强转 String,导致 ClassCastException,使 Step 2 批量查询失败。
修复后改用 String.valueOf(obj) 做安全转换,保证无论 JDBC 返回 LocalDateTime、Date 还是 String,都能稳定转成用于比较的字符串。
3.3 dev 数据快照问题处理
由于 dev 环境的 productsys 数据是历史快照,无法等待真实订单状态变化,因此今天采用测试环境数据更新方式模拟状态变化。例如,将订单产品行状态更新为已到货或已发货,或修改预计配送时间,然后触发 /scan 验证状态检测逻辑。
测试完成后需要注意还原测试数据,避免影响后续测试。
三、今日工作产出
- 完成订单关注 V2 轮询链路优化,将逐订单完整查询改为批量查询 + 内存比对。
- 新增 productsys 批量状态查询能力,避免调度器重复调用
OrderDeliveryService.queryOrder()。 - 完成关注订单状态快照批量读取、当前状态批量查询、HashMap 内存比对和批量写回逻辑梳理与实现。
- 补齐预计配送时间变更检测,支持状态未变但预计发货时间变化时触发企微推送。
- 修复 MyBatis 查询时间字段返回
LocalDateTime后强转 String 导致的类型转换异常。 - 梳理并优化企微推送模板,覆盖到货、发货、预计配送时间变更三类场景。
- 将调度方式从应用内部
@Scheduled/TaskScheduler调整为外部平台统一 HTTP 调用。 - 封装
/openapi/order-follow/scan单一触发接口,便于外部调度平台定时调用。 - 精简内部调度管理逻辑,删除不再需要的动态调度组件,保留核心业务扫描方法。
- 完成本地 dev 测试,覆盖到货、发货、配送日期变更、首次关注和无变化等场景。
- 沉淀《订单关注功能 V2 优化 — 项目文档》,记录架构问题、优化方案、实现细节、性能对比和测试结果。
四、遇到的问题与解决情况
1. V1 逐订单查询导致轮询性能较差
- 问题:V1 每个关注订单都调用一次完整订单交付查询接口,查询链路重,订单量大时耗时明显。
- 处理:新增批量查询链路,通过一次 productsys 批量查询获取所有关注订单状态。
- 结果:将 productsys 查询从 N 次降低为 1 次,显著降低轮询开销。
2. 前端详情接口与后台轮询任务职责不匹配
- 问题:
OrderDeliveryService.queryOrder()面向前端详情展示,调度器只需要订单状态,复用该方法导致数据加载过重。 - 处理:为后台轮询任务设计独立轻量查询链路,避免依赖前端详情接口。
- 结果:调度器职责更清晰,状态检测链路更轻量。
3. 配送日期变更检测在 V2 初版中被遗漏
- 问题:V2 初版重点处理订单状态变化,但没有覆盖预计配送时间变更。
- 处理:在批量查询和快照比对中同时维护预计配送时间,并新增配送时间变化分支。
- 结果:状态不变但预计配送时间变化时,也可以触发企微通知。
4. 时间字段类型转换异常
- 问题:MyBatis 返回的
deliver_predict_date是LocalDateTime,直接强转 String 会抛出异常。 - 处理:改用
String.valueOf(obj)进行安全转换。 - 结果:批量查询和配送时间比对链路恢复稳定。
5. 应用内部调度与外部调度平台职责重复
- 问题:应用内部维护
@Scheduled或动态调度,会与外部统一调度平台能力重复。 - 处理:删除内部调度管理逻辑,改为提供
/openapi/order-follow/scan给外部平台定时调用。 - 结果:职责划分更清晰,调度频率、失败重试和监控告警由外部平台统一负责。
五、明日计划
- 将订单关注 V2 优化代码继续整理,准备合并至 main 分支。
- 与测试或相关同事确认外部调度平台的调用频率、失败重试策略和监控告警配置。
- 在 UAT 环境验证
/openapi/order-follow/scan是否可以被外部平台正常调用。 - 继续观察 V2 批量轮询在更多关注订单数据下的执行耗时和日志表现。
- 检查到货、发货和配送时间变更通知在真实数据下的消息内容是否符合业务预期。
- 继续补充异常场景处理,例如 productsys 查询失败、部分订单无产品行、企微推送失败、快照写回失败等情况。
- 根据测试反馈进一步完善订单关注模块的日志和可观测性。
六、总结
今日主要围绕订单关注功能 V2 优化展开工作,重点解决 V1 轮询任务逐订单调用完整交付查询导致的性能问题。通过新增 productsys 批量状态查询、批量读取关注快照、Java HashMap 内存比对和批量写回快照,订单关注状态检测链路从重查询模式调整为轻量批处理模式。同时,今天将调度策略从应用内部管理调整为外部平台统一触发,并封装 /openapi/order-follow/scan 单一入口,提升了系统职责清晰度和后续运维可控性。整体来看,今天的工作不仅优化了订单关注模块的性能,也推动了该模块从“功能可用”进一步向“可运维、可扩展、可交接”的方向演进。