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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:20:30 +08:00

172 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
安保工单开放接口客户端
对接外部工单系统,支持:
- 创建工单POST /open-api/ops/security/order/create
- 自动结单POST /open-api/ops/security/order/auto-complete
- SHA256 签名认证
"""
import hashlib
import json
import time
import uuid
from typing import Optional
import httpx
from app.utils.logger import logger
class WorkOrderClient:
"""安保工单 API 客户端(单例)"""
def __init__(self):
self._enabled = False
self._base_url = ""
self._app_id = ""
self._app_secret = ""
self._timeout = 10
def init(self, config):
"""初始化工单配置"""
self._enabled = config.enabled and bool(config.base_url) and bool(config.app_secret)
self._base_url = config.base_url.rstrip("/")
self._app_id = config.app_id
self._app_secret = config.app_secret
self._timeout = getattr(config, "timeout", 10)
if self._enabled:
logger.info(f"工单客户端已启用: base_url={self._base_url}")
else:
logger.info("工单客户端未启用")
@property
def enabled(self) -> bool:
return self._enabled
def _sign(self, body_json: str, nonce: str, timestamp: str) -> str:
"""
SHA256 签名
签名算法SHA256(body_json + "appId=" + appId + "&nonce=" + nonce + "&timestamp=" + timestamp + appSecret)
"""
raw = (
body_json
+ "appId=" + self._app_id
+ "&nonce=" + nonce
+ "&timestamp=" + timestamp
+ self._app_secret
)
return hashlib.sha256(raw.encode("utf-8")).hexdigest()
def _build_headers(self, body_json: str) -> dict:
"""构造请求头(含签名)"""
nonce = uuid.uuid4().hex[:16]
timestamp = str(int(time.time() * 1000))
sign = self._sign(body_json, nonce, timestamp)
return {
"Content-Type": "application/json",
"appId": self._app_id,
"nonce": nonce,
"timestamp": timestamp,
"sign": sign,
}
async def create_order(
self,
title: str,
area_id: int,
alarm_id: str,
alarm_type: str,
) -> Optional[str]:
"""
创建安保工单
Returns:
orderId 字符串,失败返回 None
"""
if not self._enabled:
logger.debug("工单客户端未启用,跳过创建")
return None
body = {
"title": title,
"areaId": area_id,
"alarmId": alarm_id,
"alarmType": alarm_type,
}
body_json = json.dumps(body, ensure_ascii=False, separators=(",", ":"))
try:
headers = self._build_headers(body_json)
url = f"{self._base_url}/open-api/ops/security/order/create"
async with httpx.AsyncClient(timeout=self._timeout) as client:
resp = await client.post(url, content=body_json, headers=headers)
data = resp.json()
if data.get("code") != 0:
logger.error(f"创建工单失败: {data}")
return None
# 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
except Exception as e:
logger.error(f"创建工单异常: {e}")
return None
async def auto_complete_order(
self,
order_id: str,
remark: str = "",
) -> bool:
"""
自动结单
Returns:
是否成功
"""
if not self._enabled:
return False
body = {
"orderId": int(order_id) if order_id else 0,
"remark": remark,
}
body_json = json.dumps(body, ensure_ascii=False, separators=(",", ":"))
try:
headers = self._build_headers(body_json)
url = f"{self._base_url}/open-api/ops/security/order/auto-complete"
async with httpx.AsyncClient(timeout=self._timeout) as client:
resp = await client.post(url, content=body_json, headers=headers)
data = resp.json()
if data.get("code") != 0:
logger.error(f"自动结单失败: orderId={order_id}, resp={data}")
return False
logger.info(f"工单已自动结单: orderId={order_id}")
return True
except Exception as e:
logger.error(f"自动结单异常: orderId={order_id}, error={e}")
return False
# 全局单例
_work_order_client: Optional[WorkOrderClient] = None
def get_work_order_client() -> WorkOrderClient:
global _work_order_client
if _work_order_client is None:
_work_order_client = WorkOrderClient()
return _work_order_client