From b85be2dfdffd3d1848376cf1b9e9df3df290b690 Mon Sep 17 00:00:00 2001 From: yueli Date: Tue, 22 Sep 2026 13:13:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(track):=20=E6=96=B0=E5=A2=9E=20resolve=5Ft?= =?UTF-8?q?rack=5Froute=EF=BC=8C=E6=8C=89=E5=85=AC=E5=8F=B8=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=20Track=20=E5=AE=9E=E4=BE=8B=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolve_track_route(company_name, channel) 支持 api / inbound / outbound 三个通道。未命中一律回落到扁平变量,**绝不返回空**——漏配一个 key 就 静默断链,比配置写错更难排查。 非空但不在路由表内的值(含 get_current_company_filter() 的 '__NO_COMPANY__' 哨兵、拼写异常的公司名)会打 WARNING 暴露出来,但不 阻断业务——这类值属于主数据问题,交给业务确认,代码不猜。 notify_track() 相应扩展:新增 channel 参数(不传则按 payload['event'] 推断 inbound/outbound),url 仍为最高优先级(显式指定时不做公司路由)。 实测各分支:IRIS/LICA 各通道命中正确;None/空串/未知公司/缺通道/空路由表 全部按预期回落。 --- .../app/services/track_webhook_service.py | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/inventory-backend/app/services/track_webhook_service.py b/inventory-backend/app/services/track_webhook_service.py index 4703697..ccd214c 100644 --- a/inventory-backend/app/services/track_webhook_service.py +++ b/inventory-backend/app/services/track_webhook_service.py @@ -37,6 +37,54 @@ def _post_to_track(payload, url, api_key): logger.error("[TrackWebhook] 发送失败 url=%s, err=%s", url, e) +# 各通道未命中路由表时回落到哪个扁平配置变量(保持改造前的单实例行为) +_TRACK_ROUTE_FALLBACK = { + 'api': 'TRACK_API_URL', + 'inbound': 'TRACK_WEBHOOK_URL', + 'outbound': 'TRACK_OUTBOUND_WEBHOOK_URL', +} + + +def resolve_track_route(company_name, channel): + """按公司名解析 Track 实例地址。 + + channel: 'api' | 'inbound' | 'outbound' + + ⚠️ 未命中必须**回落到扁平变量**,不能返回空 —— 漏配一个 key 就静默断链, + 比配置写错更难排查。 + """ + routes = current_app.config.get('TRACK_ROUTES') or {} + + if company_name and company_name in routes: + url = ((routes.get(company_name) or {}).get(channel) or '').strip() + if url: + return url + # 公司在表里但该通道没配 —— 这是配置漏项,务必暴露出来 + logger.warning( + "[TrackRoute] 公司 %s 的 %s 通道未配置,回落到全局默认地址", + company_name, channel, + ) + elif company_name: + # 非空但不在路由表内:可能是拼写异常的公司名,也可能是 + # get_current_company_filter() 的 '__NO_COMPANY__' 哨兵(用户 JWT 无公司)。 + # 不猜、不阻断,只暴露给业务确认。 + logger.warning( + "[TrackRoute] 公司 '%s' 不在 TRACK_ROUTES 中,回落到全局默认地址(channel=%s)。" + "若该值不是合法部门名,请业务确认主数据", + company_name, channel, + ) + + fallback_key = _TRACK_ROUTE_FALLBACK.get(channel) + if not fallback_key: + return '' + return (current_app.config.get(fallback_key) or '').strip() + + +def _channel_from_event(event): + """从事件名推断路由通道;缺省按入库处理。""" + return 'outbound' if 'outbound' in (event or '').lower() else 'inbound' + + def get_current_operator(): """从 JWT 中安全获取当前操作人姓名(失败返回空字符串,不抛异常)""" try: @@ -50,7 +98,7 @@ def get_current_operator(): return '' -def notify_track(payload, url=None): +def notify_track(payload, url=None, channel=None): """ 异步通知 Track 系统业务事件(入库/出库等)。 @@ -58,10 +106,15 @@ def notify_track(payload, url=None): - 未配置 URL / Key 为空 -> 静默跳过 - 网络异常 / 超时 -> 仅记录日志 - :param url: 指定 Track webhook 地址;不传则用默认 TRACK_WEBHOOK_URL(入库) + :param url: 显式指定 Track webhook 地址,优先级最高(指定后不再做公司路由) + :param channel: 路由通道 'inbound' | 'outbound';不传则按 payload['event'] 推断。 + 仅在不传 url 时生效——实例由 payload['company_name'] 决定。 """ try: - url = (url or current_app.config.get('TRACK_WEBHOOK_URL') or '').strip() + if not url: + channel = channel or _channel_from_event(payload.get('event')) + url = resolve_track_route(payload.get('company_name'), channel) + url = (url or '').strip() api_key = (current_app.config.get('TRACK_WEBHOOK_KEY') or '').strip() if not url: logger.info("[TrackWebhook] 未配置 webhook URL,跳过通知")