diff --git a/aibox_sophon_adapter/routes.py b/aibox_sophon_adapter/routes.py index a86f7c8..37322fa 100644 --- a/aibox_sophon_adapter/routes.py +++ b/aibox_sophon_adapter/routes.py @@ -12,7 +12,8 @@ import json import uuid import logging -from fastapi import APIRouter, UploadFile, File, Form, Request +from fastapi import APIRouter, UploadFile, File, Form, Request, Body, HTTPException +from pydantic import ValidationError from aiofile import AIOFile from .models.video_models import VideoUploadResponse, VideoUploadResult @@ -99,16 +100,18 @@ async def video_upload( description="接收盒子告警报文(JSON),所有字段已在 OpenAPI 中声明,未知字段自动保留", response_model=AlarmReportResponse, ) -async def alarm_report(body: AlarmReportRequest) -> AlarmReportResponse: +async def alarm_report( + request: Request, + payload: dict = Body(..., description="告警报文 JSON 对象"), +) -> AlarmReportResponse: """ 告警上报接口 盒子检测到违规事件后,先上传视频再上报 JSON 告警报文。 - 请求体所有字段已在 Pydantic 模型中声明(含默认值), - model_config extra=allow 确保盒子固件升级新增字段不会丢失。 + 使用手动 Pydantic 验证以便在验证失败时记录完整请求体,便于排查。 业务处理: - 1. Pydantic 验证基础字段类型 + 1. 手动 Pydantic 验证基础字段类型(验证失败时记录原始 JSON) 2. model_dump() 获取完整 dict(含额外字段) 3. 提取关键索引字段存入 alarm_record 表 4. 完整报文 JSON 字符串存入 alarm_data @@ -116,6 +119,17 @@ async def alarm_report(body: AlarmReportRequest) -> AlarmReportResponse: 返回格式:{"Result": {"Code": 0, "Desc": ""}} 仅当 HTTP 200 + Code 非 0 时,盒子认为上报失败。 """ + # 手动 Pydantic 验证,失败时记录完整原始请求体 + try: + body = AlarmReportRequest.model_validate(payload) + except ValidationError as e: + logger.error( + f"[AlarmReport] Pydantic 验证失败:\n" + f" 错误: {e.errors()}\n" + f" 请求体: {json.dumps(payload, ensure_ascii=False)[:3000]}" + ) + raise HTTPException(status_code=422, detail=e.errors()) + # 获取完整报文(含 extra 字段,排除 None 值以生成整洁 JSON) full_data = body.model_dump(exclude_none=True)