- 半成品/成品入库弹窗顶部新增'扫码入库'按钮,打开扫码面板(扫码枪/摄像头) - 扫码 Track 身份证后自动填充物料信息与序列号,不再二次搜索 - 后端新增 track_query_service(httpx 调 Track lookup)、track-lookup 接口 - 入库成功后 notify_track 通知 Track 闭环
79 lines
2.7 KiB
Python
79 lines
2.7 KiB
Python
# app/services/track_webhook_service.py
|
||
"""
|
||
Track 系统 Webhook 通知服务(发送侧)
|
||
|
||
真实扫码入库成功后,通过后台线程 + httpx 异步 POST 通知 Track 系统。
|
||
容错策略:
|
||
- 未配置 TRACK_WEBHOOK_URL 时静默跳过,不通知。
|
||
- 全包裹 try-except,任何异常(连接拒绝/超时/网络错误)仅记录 logger.error,
|
||
绝不抛出异常阻断入库主业务流程。
|
||
"""
|
||
import logging
|
||
import threading
|
||
|
||
import httpx
|
||
from flask import current_app
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 请求超时(秒):后台线程,宁可超时失败也不能拖住业务
|
||
_WEBHOOK_TIMEOUT = 5.0
|
||
|
||
|
||
def _post_to_track(payload, url, api_key):
|
||
"""后台线程中执行的真正 POST 请求(含详细日志)"""
|
||
headers = {
|
||
'Content-Type': 'application/json',
|
||
# ★ 鉴权字段名必须为 X-API-Key,与 Track 接收端严格对齐
|
||
'X-API-Key': api_key,
|
||
}
|
||
try:
|
||
resp = httpx.post(url, json=payload, headers=headers, timeout=_WEBHOOK_TIMEOUT)
|
||
logger.info(
|
||
"[TrackWebhook] event=%s source_table=%s status=%s",
|
||
payload.get('event'), payload.get('source_table'), resp.status_code,
|
||
)
|
||
except Exception as e:
|
||
logger.error("[TrackWebhook] 发送失败 url=%s, err=%s", url, e)
|
||
|
||
|
||
def get_current_operator():
|
||
"""从 JWT 中安全获取当前操作人姓名(失败返回空字符串,不抛异常)"""
|
||
try:
|
||
from flask_jwt_extended import get_jwt
|
||
claims = get_jwt()
|
||
if not claims:
|
||
return ''
|
||
# 优先显示名(如"张三"),回退到账号ID
|
||
return claims.get('display_name') or claims.get('username') or ''
|
||
except Exception:
|
||
return ''
|
||
|
||
|
||
def notify_track(payload, url=None):
|
||
"""
|
||
异步通知 Track 系统业务事件(入库/出库等)。
|
||
|
||
调用方在 db.session.commit() 成功后调用,本函数绝不抛出异常:
|
||
- 未配置 URL / Key 为空 -> 静默跳过
|
||
- 网络异常 / 超时 -> 仅记录日志
|
||
|
||
:param url: 指定 Track webhook 地址;不传则用默认 TRACK_WEBHOOK_URL(入库)
|
||
"""
|
||
try:
|
||
url = (url or current_app.config.get('TRACK_WEBHOOK_URL') or '').strip()
|
||
api_key = (current_app.config.get('TRACK_WEBHOOK_KEY') or '').strip()
|
||
if not url:
|
||
logger.info("[TrackWebhook] 未配置 webhook URL,跳过通知")
|
||
return
|
||
|
||
thread = threading.Thread(
|
||
target=_post_to_track,
|
||
args=(payload, url, api_key),
|
||
daemon=True, # 守护线程:不阻塞进程退出
|
||
name='track_webhook_notify',
|
||
)
|
||
thread.start()
|
||
except Exception as e:
|
||
logger.error("[TrackWebhook] 通知调用失败(不影响业务): %s", e)
|