该文件提供隐患识别相关的接口,包括:
所有接口均需要 Token 认证。
路由前缀:/apiv1(以 routers/__init__.py 中注册为准)
/apiv1/hazard — 隐患识别功能说明: 接收一张图片的 OSS URL,执行以下完整流程:
RecognitionRecord 记录是否需要认证: 是
请求方式: POST
请求体(JSON): | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | image_url | string | 是 | — | 图片的 OSS URL(可能是加密的代理 URL) | | scene_type | string | 否 | "" | 场景类型(如 "隧道施工"、"桥梁施工") | | user_name | string | 否 | "" | 用户名(用于水印,不传则使用 user.account) | | user_account | string | 否 | "" | 用户账号(用于请求体传递,实际水印使用 user.account) |
成功响应字段: | 字段 | 类型 | 说明 | |------|------|------| | data.record_id | int | 识别记录 ID | | data.hazard_count | int | 检测到的隐患数量 | | data.hazards | array | 隐患详情列表,每项含 bbox, label, confidence | | data.result_image_url | string | 标注后的结果图片 OSS URL | | data.original_image_url | string | 原始图片 URL(与请求中的一致) |
测试用例:
// 请求
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"image_url": "encrypted_oss_url_of_construction_site_image",
"scene_type": "隧道施工",
"user_name": "张三",
"user_account": "zhangsan"
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "识别成功",
"data": {
"record_id": 1,
"hazard_count": 3,
"hazards": [
{
"bbox": [100, 150, 300, 400],
"label": "未佩戴安全帽",
"confidence": 0.92
},
{
"bbox": [500, 200, 650, 350],
"label": "临边防护缺失",
"confidence": 0.85
},
{
"bbox": [200, 400, 400, 550],
"label": "材料堆放不规范",
"confidence": 0.78
}
],
"result_image_url": "https://oss.example.com/hazard_detection/123/1700000000.jpg",
"original_image_url": "encrypted_oss_url_of_construction_site_image"
}
}
// 请求
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"image_url": "encrypted_oss_url_of_safe_image",
"scene_type": "桥梁施工"
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "识别成功",
"data": {
"record_id": 2,
"hazard_count": 0,
"hazards": [],
"result_image_url": "https://oss.example.com/hazard_detection/123/1700000001.jpg",
"original_image_url": "encrypted_oss_url_of_safe_image"
}
}
// 请求
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"image_url": "https://invalid-url.example.com/nonexistent.jpg",
"scene_type": "隧道施工"
}
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "图片下载失败: <httpx 错误信息>"
}
// 请求(YOLO 服务不可用时)
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"image_url": "valid_encrypted_oss_url",
"scene_type": "隧道施工"
}
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "处理失败: <YOLO 服务错误信息>"
}
// 请求
POST /apiv1/hazard
// 预期响应 (HTTP 401)
{
"statusCode": 401,
"msg": "未提供认证Token"
}
// 请求
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"scene_type": "隧道施工"
}
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["body", "image_url"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
// 请求
POST /apiv1/hazard
token: <有效Token>
Content-Type: application/json
{
"image_url": "valid_encrypted_oss_url",
"scene_type": "隧道施工"
}
// 预期响应 (HTTP 200)
// 水印使用 token 中用户的 account 字段
{
"statusCode": 200,
"msg": "识别成功",
"data": {
"record_id": 3,
"hazard_count": 1,
"hazards": [...],
"result_image_url": "https://oss.example.com/hazard_detection/123/1700000002.jpg",
"original_image_url": "valid_encrypted_oss_url"
}
}
/apiv1/save_step — 保存识别步骤功能说明: 更新指定隐患识别记录的当前步骤编号。用于前端多步骤流程中保存用户当前进度。
是否需要认证: 是
请求方式: POST
请求体(JSON): | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | record_id | int | 是 | 识别记录 ID | | current_step | int | 是 | 当前步骤编号 |
业务逻辑:
record_id + user_id(当前登录用户)双重条件查找 RecognitionRecordcurrent_step 和 updated_at 字段测试用例:
// 请求
POST /apiv1/save_step
token: <有效Token>
Content-Type: application/json
{
"record_id": 1,
"current_step": 2
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "保存成功",
"data": {
"record_id": 1,
"current_step": 2
}
}
// 请求
POST /apiv1/save_step
token: <有效Token>
Content-Type: application/json
{
"record_id": 1,
"current_step": 5
}
// 预期响应 (HTTP 200)
{
"statusCode": 200,
"msg": "保存成功",
"data": {
"record_id": 1,
"current_step": 5
}
}
// 请求
POST /apiv1/save_step
token: <有效Token>
Content-Type: application/json
{
"record_id": 99999,
"current_step": 2
}
// 预期响应 (HTTP 200, 业务码 404)
{
"statusCode": 404,
"msg": "记录不存在"
}
// 请求(用用户 A 的 Token 修改用户 B 的记录)
POST /apiv1/save_step
token: <用户A的Token>
Content-Type: application/json
{
"record_id": 5,
"current_step": 3
}
// 预期响应 (HTTP 200, 业务码 404)
// 因为 user_id 不匹配,查询结果为空
{
"statusCode": 404,
"msg": "记录不存在"
}
// 请求
POST /apiv1/save_step
// 预期响应 (HTTP 401)
{
"statusCode": 401,
"msg": "未提供认证Token"
}
// 请求
POST /apiv1/save_step
token: <有效Token>
Content-Type: application/json
{
"record_id": 1
}
// 预期响应 (HTTP 422)
{
"detail": [
{
"loc": ["body", "current_step"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
// 预期响应 (HTTP 200, 业务码 500)
{
"statusCode": 500,
"msg": "保存失败: <具体错误信息>"
}
注意:此时代码会执行
db.rollback()回滚事务。
_draw_boxes_and_watermark(image_bytes, hazards, user_name, user_account) — 绘制标注和水印功能: 在图片上绘制 YOLO 检测的边界框和 45 度角平铺水印。
参数:
| 参数 | 类型 | 说明 |
|------|------|------|
| image_bytes | bytes | 原始图片二进制数据 |
| hazards | list | YOLO 检测结果列表,每项含 bbox(4 元素列表)、label、confidence |
| user_name | string | 用户名(水印文本之一) |
| user_account | string | 用户账号(水印文本之一) |
返回: bytes — 处理后的 JPEG 图片二进制数据
处理逻辑:
该函数为内部函数,不直接暴露为 API 接口。
| 依赖项 | 说明 |
|---|---|
database.get_db |
SQLAlchemy 数据库会话 |
models.scene.RecognitionRecord |
识别记录模型(字段:id, user_id, scene_type, original_image_url, recognition_image_url, hazard_count, hazard_details, current_step, created_at, updated_at, is_deleted) |
services.yolo_service |
YOLO 目标检测服务,提供 detect_hazards(url, scene_type) 方法 |
services.oss_service |
OSS 存储服务,提供 upload_bytes(data, filename) 方法 |
utils.crypto.decrypt_url |
URL 解密工具函数 |
PIL (Pillow) |
图片处理库 |
httpx |
异步 HTTP 客户端,用于下载图片 |
request.state.user |
从中间件注入的用户信息(含 user_id, account 等) |
hazard 接口的 URL 解密有容错处理:decrypt_url 失败时直接使用原始 URL,不会抛出异常。save_step 接口通过 record_id + user_id 双重过滤实现了用户间的数据隔离——用户只能修改自己的记录。_draw_boxes_and_watermark 异常),返回原始未标注图片,不会导致整个接口失败。hazard_details 字段以 JSON 字符串形式存储在数据库中(json.dumps,ensure_ascii=False)。hazard_detection/{user_id}/{timestamp}.jpg。