2026-06-10|供应风险预警推送完善实现与验证
一、今日主要工作
- 按照前一天梳理的《供应风险预警推送完善》开发计划,完成供应风险预警精准推送后端逻辑的代码实施。
- 将原有“按订单号匹配销售”的推送链路调整为“按产品型号匹配报价单 / 订单,再提取销售人员并创建门户通知”的新链路。
- 新增 SCM 数据源下的供应风险查询 Mapper,完成未下单报价单查询和已下单未发货订单查询两类 SQL 实现。
- 改造
PortalSupplyRiskService中预警创建后的推送路由,支持SUPPLY_RISK、EOL、ORDERED_RISK三类预警类型分别走不同查询逻辑。 - 实现 seller 字段解析、产品型号关键词拆分、销售用户去重和通知创建逻辑,去除原先对
orderId和 sales 角色过滤的依赖。 - 完成供应风险预警推送链路的本地功能测试、边界测试和回归测试,共执行 46 个测试用例,核心功能链路通过验证。
- 在生产数据验证过程中发现
fcst_price_check_order报价单同步表已停用,当前实现代码已完成,但上线需要等待 CPQ 提供可替代的报价单查询接口。 - 沉淀实现产出文档,记录需求背景、数据源探索、代码改动、测试结果、当前阻塞和后续 CPQ 接口替换方案。
二、核心完成内容
1. 模块开发与功能实现
今天的主要工作不再停留在方案设计阶段,而是基于前一天的开发计划进行了实际编码实现。核心目标是修复供应风险预警在无订单号场景下无法推送的问题,并将推送对象从“订单处理人”调整为“产品型号相关销售”。
原有链路中,供应链交付组创建预警后,系统会调用 resolveOrderSalesUserIds(orderId):
创建预警 → resolveOrderSalesUserIds(orderId) → Oracle cux_om_option_tl.processor → SCM sys_user 匹配 → sales 角色过滤 → portal_supply_risk_notification该链路在有订单号时可以做到按订单精准推送,但当预警不关联具体订单号时会返回空列表,导致预警创建成功但没有任何销售收到通知。今天的实现将主链路改为:
创建预警 → 读取 warningType 和 productModels → 按产品型号匹配报价单 / 订单 → 从 seller 字段提取销售工号 → 匹配 sys_user.user_id → 去重后写入 portal_supply_risk_notification本次实现保持 API 入口不变,仍使用:
POST /api/portal/supply-risk/alertsController 和数据库主表结构暂不调整,主要改造集中在查询 Mapper 和 Service 层。这样可以在不大规模影响前端和接口契约的前提下,先完成后端推送链路增强。
2. 新增 SupplyRiskQueryMapper 查询能力
今天新增了 SupplyRiskQueryMapper.java,放在 com.sangfor.pangugong.mapper 包下,并使用 @ScmMapper 绑定 SCM 数据源,保证查询能够访问 fcst_price_check_order 和 oms_order_data 两张 SCM 业务表。
该 Mapper 主要提供两个查询方法:
-
selectUnorderedQuotesByProductName(keyword)
用于SUPPLY_RISK和EOL场景,按产品名称关键词查询未下单报价单。 -
selectUnshippedOrdersByProductName(keyword)
用于ORDERED_RISK场景,按产品名称关键词查询已下单但未完全发货的订单。
其中,未下单报价单查询逻辑为:
product_name LIKE keyword + 报价单审批状态满足要求 + 申请时间在指定窗口内 + seller 不为空 + 报价单号不为空 + NOT EXISTS oms_order_data.costApprovalNo已下单未发货查询逻辑为:
product_name LIKE keyword + fcst_price_check_order.price_approval_order_number = oms_order_data.costApprovalNo + seller 不为空 + orderState != 30其中 orderState=30 表示已完成或已关闭,因此 orderState != 30 用于表示未完全发货或仍在履行中的订单。
3. 改造 PortalSupplyRiskService 推送路由
今天重点修改了 PortalSupplyRiskService.createAndPublish() 方法中的推送逻辑。
旧逻辑:
List<String> salesIds = resolveOrderSalesUserIds(req.getOrderId());新逻辑:
List<String> salesIds;if ("ORDERED_RISK".equals(a.getWarningType())) { salesIds = resolveSalesForOrderedRisk(models);} else { salesIds = resolveSalesForUnorderedQuotes(models);}其中:
SUPPLY_RISK:按产品型号匹配未下单报价单,提取报价单 seller;EOL:与供应风险共用未下单报价单链路;ORDERED_RISK:按产品型号匹配已下单但未完全发货的订单,提取订单关联报价单的 seller。
改造后,供应风险预警的推送入口从 orderId 转为 productModels,即用户填写的产品型号。orderId 字段保留兼容旧前端,但后端不再依赖它做推送目标解析。
4. 实现 seller 到系统用户的解析逻辑
报价单中的 seller 字段格式为“姓名 + 工号”,例如:
刘敏79574王帅66320今天实现了 resolveSellerToSysUserId() 方法,负责将 seller 字段转换为系统用户 ID。
核心处理步骤为:
- 对 seller 字符串去空格;
- 提取尾部连续数字作为工号;
- 优先通过
ScmSysUserLookupService.findActiveByCode(code)精确匹配; - 如果精确匹配失败,再使用
sys_user.code LIKE '%工号'做后缀回退匹配; - 匹配失败时记录日志并跳过,不影响其他销售推送。
这里没有固定写死 5 位工号,而是提取尾部连续数字,主要是为了兼容生产环境中可能存在的不同工号位数。同时,LIKE 后缀匹配可以兼容 066320 与 66320 这类前导零差异。
5. 实现产品型号关键词拆分
由于供应链交付组在创建预警时可能一次输入多个产品型号,今天新增了 splitProductKeywords() 方法,用于将 productModels 拆分为多个查询关键词。
当前支持以下分隔方式:
- 换行;
- 英文逗号;
- 中文逗号;
- 英文分号;
- 中文分号;
- 多种分隔符混合输入。
例如:
AC-1000S5000-24PEDS-1210或:
AC-1000,S5000-24P;EDS-1210都会被拆分为多个产品型号关键词,分别查询报价单或订单,并最终对销售用户进行去重。
6. 删除旧推送方法并保留兼容字段
本次实现中删除或停止使用了两类旧逻辑:
resolveOrderSalesUserIds(String orderId):旧的订单号到 Oracle processor 再到销售用户的解析链路;listSalesSysUserIds():旧的全量 sales 推送方法。
同时,SupplyRiskAlertCreateRequest.orderId 字段没有直接删除,而是标记为 @Deprecated,用于兼容旧前端请求结构。后续前端适配完成后,可以再进一步清理该字段。
此外,本次通知创建时不再过滤 sales 角色。原因是销售人员可能尚未开通助手角色,如果通知创建时强制过滤 sales 角色,后续补充角色后仍无法看到历史预警。当前设计是先将通知写入 portal_supply_risk_notification,用户后续获得角色并登录后,可以通过自身 recipientSysUserId 查询到历史通知。
7. 测试验证与问题处理
今天完成了供应风险预警推送完善的本地测试验证。测试环境为 dev 环境,应用运行在 localhost:5170。
由于 dev 环境中的 fcst_price_check_order 数据为历史快照,最新数据停留在 2023 年,因此为验证代码链路,测试时临时将查询条件适配为:
INTERVAL 5 YEAR并按 dev 数据中的审批状态值调整条件,保证本地能够查到测试数据。
本次共执行 46 个测试用例,其中:
- 43 个通过;
- 1 个性能用例未达预期;
- 2 个因 dev 环境数据无法触发而跳过;
- 核心功能测试 40 个全部通过。
测试覆盖范围包括:
- supply 角色创建预警权限校验;
- 非 supply 角色创建预警拦截;
- 必填字段校验;
- 三种
warningType路由; - 默认 warningType 处理;
- 多产品型号分隔符解析;
SUPPLY_RISK与EOL共用链路验证;ORDERED_RISK与供应风险链路差异验证;- 无匹配型号返回
pushCount=0; - seller 匹配与用户去重;
- 通知创建、查看、已读和反馈流程;
- 经理看板、详情、接收人和关闭流程;
- 角色补充后历史通知可见性;
- 交付查询、订单关注、角色管理和登录等回归场景。
关键验证结果包括:
SUPPLY_RISK输入AC-1000可匹配并推送到 546 名销售;ORDERED_RISK输入AC-1000匹配到 82 名相关销售;VPN-1000在供应风险与已下单风险两类链路下推送人数存在差异,符合链路预期;- 无匹配型号时返回
pushCount=0,预警仍创建但不推送; - 通知去重结果正常;
- seller 字段可正确映射到系统用户,例如
霍高飞30793能映射到对应sys_user.user_id。
8. 性能与数据源问题定位
测试过程中发现,dev 环境下单次创建预警耗时约 35 秒。主要原因是 LIKE '%keyword%' 在 fcst_price_check_order 大表上进行模糊匹配,同时 dev 测试为了覆盖历史数据临时放宽到 5 年时间窗口,扫描数据量较大。
更关键的是,在生产验证阶段发现:fcst_price_check_order 报价单同步表已经停用,数据停留在约 2 年前,无法作为后续正式上线的数据源。
因此,今天虽然完成了代码实现和测试验证,但该功能不能直接依赖 fcst_price_check_order 上线。后续需要推动 CPQ 系统提供新的报价单查询接口,用于替代当前已作废的数据表。
9. CPQ 接口替换方案沉淀
当前项目中已经接入了一个 CPQ 接口:
POST /CPQ/api/quote/open/quotation/item_code该接口可以按报价单号查询物料编码,但不能满足本次供应风险预警推送的完整需求,因为缺少以下能力:
- 按产品名称查询报价单列表;
- 返回报价单审批状态;
- 返回报价单销售人员;
- 返回报价单申请时间;
- 支持按时间窗口过滤近期报价单。
后续如果 CPQ 提供按产品查询报价单的接口,替换方案可以保持 Service 层大部分逻辑不变,只需要将 SupplyRiskQueryMapper 中基于 fcst_price_check_order 的 SQL 查询替换为 CPQ API 调用,再结合 oms_order_data.costApprovalNo 校验订单状态。
也就是说,今天实现的 seller 解析、产品型号拆分、去重、通知创建和 warningType 路由逻辑仍然可以复用,主要需要替换的是报价单数据来源。
10. 接口设计与文档沉淀
今天同步补充了《供应风险预警推送完善 — 实现产出文档》,对本次开发实施情况进行了整理,内容包括:
- 业务背景与现有痛点;
- 三种预警类型的推送对象;
- 数据源探索过程;
- 报价单与订单关联关系验证;
SupplyRiskQueryMapper新增查询;PortalSupplyRiskService改造点;- seller 字段解析方案;
- 产品型号多分隔符解析;
- 测试环境准备与测试结果;
- 性能瓶颈;
- 当前阻塞问题;
- 后续 CPQ 接口替换方案。
该文档可以作为后续继续与 CPQ 或相关 IT 团队沟通接口能力、评估上线条件和进行二次改造的依据。
三、今日工作产出
- 完成供应风险预警推送完善后端代码实现,将推送入口从
orderId调整为productModels。 - 新增
SupplyRiskQueryMapper.java,使用 SCM 数据源查询报价单和订单数据。 - 实现
selectUnorderedQuotesByProductName(),用于SUPPLY_RISK和EOL场景匹配未下单报价单销售。 - 实现
selectUnshippedOrdersByProductName(),用于ORDERED_RISK场景匹配已下单未完全发货订单销售。 - 改造
PortalSupplyRiskService.createAndPublish(),根据warningType路由到不同销售解析链路。 - 新增
resolveSalesForUnorderedQuotes()、resolveSalesForOrderedRisk()、resolveSellerToSysUserId()、extractTrailingDigits()、splitProductKeywords()等核心方法。 - 删除或停止使用
resolveOrderSalesUserIds()和listSalesSysUserIds()旧逻辑。 - 将
SupplyRiskAlertCreateRequest.orderId标记为@Deprecated,保留旧前端兼容性。 - 完成本地测试验证,共执行 46 个测试用例,核心功能测试通过。
- 定位性能瓶颈主要来自
LIKE '%keyword%'大表模糊匹配和 dev 环境 5 年窗口扫描。 - 发现生产环境
fcst_price_check_order同步表已停用,明确当前功能需等待 CPQ 接口补齐后再上线。 - 沉淀《供应风险预警推送完善 — 实现产出文档》,记录实现细节、测试结果、阻塞点和后续替换方案。
四、遇到的问题与解决情况
1. 旧推送逻辑在无订单号场景下无法推送
- 问题:旧精准推送依赖
orderId,当供应风险预警不关联具体订单时,resolveOrderSalesUserIds()返回空列表,导致没有任何销售收到通知。 - 处理:将推送入口改为
productModels,按产品型号查询报价单或订单,再从 seller 字段提取销售。 - 结果:供应风险、产品退市和已下单风险三类预警都可以基于产品型号进行销售匹配。
2. 报价单与订单数据关系需要重新确认
- 问题:需要确认产品型号、报价单、订单、销售之间的真实关联链路,避免使用错误表或低覆盖表。
- 处理:对比
ivsc_order_info、oms_order_data和fcst_price_check_order,并通过字段注释、项目代码和 CPQ 接口交叉验证costApprovalNo与报价单号关系。 - 结果:确定使用 SCM 数据源中的
fcst_price_check_order和oms_order_data构建查询链路。
3. seller 字段需要转换为系统用户 ID
- 问题:报价单中的 seller 字段是“姓名 + 工号”文本,不是系统用户 ID,不能直接写入通知表。
- 处理:实现尾部连续数字提取算法,优先按工号精确匹配
sys_user.code,失败后按工号后缀模糊匹配。 - 结果:seller 可以转换为
sys_user.user_id,用于创建portal_supply_risk_notification。
4. dev 数据快照过期影响测试
- 问题:dev 环境
fcst_price_check_order最新数据停留在 2023 年,按生产条件中的 3 个月窗口无法查到数据。 - 处理:本地测试阶段临时放宽时间窗口,并按 dev 数据中的状态值适配查询条件。
- 结果:完成本地功能链路验证,但正式上线仍需按生产数据源重新验证。
5. 查询性能存在瓶颈
- 问题:
LIKE '%keyword%'在大表上进行模糊匹配,dev 环境单次创建预警约 35 秒。 - 处理:定位瓶颈来自大表模糊查询和测试窗口过大,并记录为后续优化项。
- 结果:当前逻辑可以验证功能正确性,但正式上线需要结合真实 CPQ 接口和索引方案重新评估性能。
6. 生产报价单同步表已停用
- 问题:生产验证发现
fcst_price_check_order表数据已停在约 2 年前,无法作为正式线上数据源。 - 处理:保留当前代码实现和测试结果,将数据源问题记录为阻塞;整理 CPQ 接口替换方案。
- 结果:当前功能实现完成但暂不适合上线,后续需与 CPQ 团队协作补齐按产品查询报价单接口。
五、明日计划
- 继续与相关业务或 IT 团队确认 CPQ 当前可提供的报价单查询接口能力。
- 明确 CPQ 是否支持按产品名称、物料编码或销售型号查询报价单列表。
- 确认 CPQ 接口是否能返回 seller、审批状态、申请时间、报价单号等关键字段。
- 评估将
SupplyRiskQueryMapper替换为 CPQ API 调用的代码改造范围。 - 根据 CPQ 接口能力重新调整供应风险、产品退市、已下单风险三类链路的数据来源。
- 继续完善性能评估方案,避免上线后出现大表模糊查询导致接口耗时过长的问题。
- 与前端确认后续是否需要移除
orderId输入框,并增加warningType与产品型号输入的适配交互。 - 保留当前本地实现作为逻辑验证版本,待 CPQ 数据源确认后进行二次改造和联调。
六、总结
今天主要围绕供应风险预警推送完善进行了实际开发实施。基于前一天确定的开发计划,我完成了 SCM 查询 Mapper、新的 warningType 推送路由、seller 解析、产品型号拆分、通知创建和旧逻辑替换等后端改造,并通过本地测试验证了核心功能链路。测试过程中也发现了两个重要问题:一是基于 LIKE '%keyword%' 的大表查询存在性能压力,二是生产环境中的 fcst_price_check_order 报价单同步表已经停用。整体来看,今天完成了预警精准推送新逻辑的代码验证和实现沉淀,同时也明确了后续真正上线前必须依赖 CPQ 接口补齐报价单数据源的问题。