fix: 注释工单对接代码,修复API响应解析和类型问题

- 工单创建/自动结单代码全部注释,待本地测试后启用
- 修复create_order响应解析:data直接是orderId,非嵌套对象
- 修复areaId类型:int(文档要求),非str
- 修复auto-complete orderId类型:int
- 两步卡片状态机和先到先得逻辑保留生效

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 18:20:30 +08:00
parent 6cbf89a38b
commit f7a69892f6
5 changed files with 71 additions and 70 deletions

View File

@@ -65,10 +65,10 @@ async def lifespan(app: FastAPI):
agent = get_agent_dispatcher()
agent.init(settings.agent)
# 初始化工单客户端
from app.services.work_order_client import get_work_order_client
wo_client = get_work_order_client()
wo_client.init(settings.work_order)
# 初始化工单客户端(暂未上线,待本地测试通过后启用)
# from app.services.work_order_client import get_work_order_client
# wo_client = get_work_order_client()
# wo_client.init(settings.work_order)
logger.info("AI 告警平台启动完成")

View File

@@ -89,50 +89,49 @@ async def edge_alarm_resolve(
if not success:
return YudaoResponse.error(404, "告警不存在")
# 如果之前不是终态(边缘端先到),触发自动结单 + 卡片更新
# 如果之前不是终态(边缘端先到),触发卡片更新
if not was_terminal:
asyncio.create_task(_resolve_work_order_and_card(resolve.alarm_id, resolve.resolve_type))
asyncio.create_task(_resolve_card_update(resolve.alarm_id, resolve.resolve_type))
return YudaoResponse.success(True)
async def _resolve_work_order_and_card(alarm_id: str, resolve_type: str):
"""边缘端 resolve 后异步处理:更新卡片 + 自动结单"""
async def _resolve_card_update(alarm_id: str, resolve_type: str):
"""边缘端 resolve 后异步处理:更新卡片(工单暂未上线)"""
try:
from app.services.work_order_client import get_work_order_client
from app.services.wechat_service import get_wechat_service
from app.models import get_session, AlarmEventExt
# 1. 自动结单
wo_client = get_work_order_client()
if wo_client.enabled:
db = get_session()
try:
ext = db.query(AlarmEventExt).filter(
AlarmEventExt.alarm_id == alarm_id,
AlarmEventExt.ext_type == "WORK_ORDER",
).first()
order_id = ext.ext_data.get("order_id", "") if ext and ext.ext_data else ""
finally:
db.close()
# 工单自动结单(暂未上线)
# from app.services.work_order_client import get_work_order_client
# from app.models import get_session, AlarmEventExt
# wo_client = get_work_order_client()
# if wo_client.enabled:
# db = get_session()
# try:
# ext = db.query(AlarmEventExt).filter(
# AlarmEventExt.alarm_id == alarm_id,
# AlarmEventExt.ext_type == "WORK_ORDER",
# ).first()
# order_id = ext.ext_data.get("order_id", "") if ext and ext.ext_data else ""
# finally:
# db.close()
# if order_id:
# remark_map = {
# "person_returned": "人员回岗自动关闭",
# "non_work_time": "非工作时间自动关闭",
# "intrusion_cleared": "入侵消失自动关闭",
# }
# remark = remark_map.get(resolve_type, f"边缘端自动结单: {resolve_type}")
# await wo_client.auto_complete_order(order_id, remark)
if order_id:
remark_map = {
"person_returned": "人员回岗自动关闭",
"non_work_time": "非工作时间自动关闭",
"intrusion_cleared": "入侵消失自动关闭",
}
remark = remark_map.get(resolve_type, f"边缘端自动结单: {resolve_type}")
await wo_client.auto_complete_order(order_id, remark)
# 2. 更新企微卡片到终态(如果有 response_code
# 更新企微卡片到终态(如果有 response_code
wechat = get_wechat_service()
if wechat.enabled:
response_code = wechat.get_response_code(alarm_id)
if response_code:
await wechat.update_alarm_card_terminal(
response_code=response_code,
user_ids=[], # 空列表时企微更新所有已收到卡片的用户
user_ids=[],
alarm_id=alarm_id,
action="auto_resolve",
)

View File

@@ -105,7 +105,7 @@ async def _process_card_button_click(msg: dict):
"""
try:
from app.services.wechat_service import get_wechat_service
from app.services.work_order_client import get_work_order_client
# from app.services.work_order_client import get_work_order_client # 工单暂未上线
user_id = msg.get("FromUserName", "")
event_key = msg.get("EventKey", "")
@@ -136,7 +136,7 @@ async def _process_card_button_click(msg: dict):
return
wechat = get_wechat_service()
wo_client = get_work_order_client()
# wo_client = get_work_order_client() # 工单暂未上线
service = get_alarm_event_service()
# ---- 第一步:确认接单 ----
@@ -181,10 +181,10 @@ async def _process_card_button_click(msg: dict):
operator_name=user_id,
)
# 自动结单
order_id = _get_order_id_for_alarm(alarm_id)
if order_id:
await wo_client.auto_complete_order(order_id, f"误报忽略 by {user_id}")
# 自动结单(工单暂未上线)
# order_id = _get_order_id_for_alarm(alarm_id)
# if order_id:
# await wo_client.auto_complete_order(order_id, f"误报忽略 by {user_id}")
# ---- 第二步:已处理完成 ----
elif action == "complete":
@@ -207,10 +207,10 @@ async def _process_card_button_click(msg: dict):
operator_name=user_id,
)
# 自动结单
order_id = _get_order_id_for_alarm(alarm_id)
if order_id:
await wo_client.auto_complete_order(order_id, f"已处理 by {user_id}")
# 自动结单(工单暂未上线)
# order_id = _get_order_id_for_alarm(alarm_id)
# if order_id:
# await wo_client.auto_complete_order(order_id, f"已处理 by {user_id}")
# ---- 第二步:标记误报 ----
elif action == "false":
@@ -233,10 +233,10 @@ async def _process_card_button_click(msg: dict):
operator_name=user_id,
)
# 自动结单
order_id = _get_order_id_for_alarm(alarm_id)
if order_id:
await wo_client.auto_complete_order(order_id, f"标记误报 by {user_id}")
# 自动结单(工单暂未上线)
# order_id = _get_order_id_for_alarm(alarm_id)
# if order_id:
# await wo_client.auto_complete_order(order_id, f"标记误报 by {user_id}")
except Exception as e:
logger.error(f"处理卡片按钮点击失败: {e}", exc_info=True)

View File

@@ -52,7 +52,7 @@ async def process_alarm_notification(alarm_data: Dict):
scene_id = alarm_data.get("scene_id", "")
# 查找区域名称用于 VLM prompt比 UUID 更有意义)
area_name_for_vlm, _ = _get_notify_persons(device_id, alarm_level)
area_name_for_vlm, _, _ = _get_notify_persons(device_id, alarm_level)
roi_name = area_name_for_vlm if area_name_for_vlm != "未知区域" else scene_id
# snapshot_url 可能是 COS object key需转为可访问的预签名URL
@@ -82,7 +82,7 @@ async def process_alarm_notification(alarm_data: Dict):
# ========== 2. 查表获取通知人员 ==========
description = vlm_result.get("description", "")
area_name = area_name_for_vlm
_, persons = _get_notify_persons(device_id, alarm_level)
_, area_id_int, persons = _get_notify_persons(device_id, alarm_level)
# 演示模式:数据库无人员时,使用配置的测试 userid
if not persons and settings.wechat.test_uids:
@@ -143,20 +143,20 @@ async def process_alarm_notification(alarm_data: Dict):
else:
logger.warning(f"个人卡片发送失败: {alarm_id}")
# ---- 4. 创建安保工单 ----
wo_client = get_work_order_client()
if wo_client.enabled:
type_name = ALARM_TYPE_NAMES.get(alarm_type, alarm_type)
level_name = ALARM_LEVEL_NAMES.get(alarm_level, "一般")
wo_title = f"{level_name}{type_name}告警 - {area_name}"
order_id = await wo_client.create_order(
title=wo_title,
area_id=area_name,
alarm_id=alarm_id,
alarm_type=alarm_type,
)
if order_id:
_save_order_id(alarm_id, order_id)
# ---- 4. 创建安保工单(暂未上线,待本地测试通过后启用) ----
# wo_client = get_work_order_client()
# if wo_client.enabled:
# type_name = ALARM_TYPE_NAMES.get(alarm_type, alarm_type)
# level_name = ALARM_LEVEL_NAMES.get(alarm_level, "一般")
# wo_title = f"【{level_name}】{type_name}告警 - {area_name}"
# order_id = await wo_client.create_order(
# title=wo_title,
# area_id=area_id_int,
# alarm_id=alarm_id,
# alarm_type=alarm_type,
# )
# if order_id:
# _save_order_id(alarm_id, order_id)
except Exception as e:
logger.error(f"告警通知处理失败: {alarm_id}, error={e}", exc_info=True)
@@ -231,7 +231,7 @@ def _get_notify_persons(camera_id: str, alarm_level: int) -> tuple:
根据摄像头ID查找通知人员
Returns:
(area_name, [{"person_name": ..., "wechat_uid": ..., "role": ...}])
(area_name, area_id, [{"person_name": ..., "wechat_uid": ..., "role": ...}])
"""
db = get_session()
try:
@@ -240,7 +240,7 @@ def _get_notify_persons(camera_id: str, alarm_level: int) -> tuple:
).first()
if not binding:
return ("未知区域", [])
return ("未知区域", 0, [])
area = db.query(NotifyArea).filter(
NotifyArea.area_id == binding.area_id,
@@ -248,6 +248,7 @@ def _get_notify_persons(camera_id: str, alarm_level: int) -> tuple:
).first()
area_name = area.area_name if area else "未知区域"
area_id = binding.area_id or 0
persons = db.query(AreaPersonBinding).filter(
AreaPersonBinding.area_id == binding.area_id,
@@ -264,11 +265,11 @@ def _get_notify_persons(camera_id: str, alarm_level: int) -> tuple:
for p in persons
]
return (area_name, result)
return (area_name, area_id, result)
except Exception as e:
logger.error(f"查询通知人员失败: {e}")
return ("未知区域", [])
return ("未知区域", 0, [])
finally:
db.close()

View File

@@ -77,7 +77,7 @@ class WorkOrderClient:
async def create_order(
self,
title: str,
area_id: str,
area_id: int,
alarm_id: str,
alarm_type: str,
) -> Optional[str]:
@@ -111,7 +111,8 @@ class WorkOrderClient:
logger.error(f"创建工单失败: {data}")
return None
order_id = data.get("data", {}).get("orderId", "")
# API 返回 {"code":0, "data": 1234567890} — data 直接是 orderId
order_id = str(data.get("data", ""))
logger.info(f"工单已创建: orderId={order_id}, alarmId={alarm_id}")
return order_id
@@ -134,7 +135,7 @@ class WorkOrderClient:
return False
body = {
"orderId": order_id,
"orderId": int(order_id) if order_id else 0,
"remark": remark,
}
body_json = json.dumps(body, ensure_ascii=False, separators=(",", ":"))