修改路径,从src放到根目录
This commit is contained in:
417
api/routes/face_features.py
Normal file
417
api/routes/face_features.py
Normal file
@@ -0,0 +1,417 @@
|
||||
"""
|
||||
人脸特征API路由
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query, BackgroundTasks
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from schemas.face_feature import (
|
||||
FaceFeatureCreate,
|
||||
FaceFeatureUpdate,
|
||||
FaceFeatureQuery,
|
||||
FaceFeatureResponse,
|
||||
FaceFeatureListResponse,
|
||||
FaceFeatureStatsResponse,
|
||||
BatchFaceFeatureCreate,
|
||||
FeatureStatus
|
||||
)
|
||||
from api.dependencies import (
|
||||
get_face_feature_service,
|
||||
get_face_feature_by_id
|
||||
)
|
||||
from services.face_feature_service import FaceFeatureService
|
||||
from api.errors import (
|
||||
FaceFeatureProcessingError,
|
||||
FeatureNotFoundError,
|
||||
DuplicateFeatureError
|
||||
)
|
||||
from config import settings
|
||||
|
||||
# 创建路由器
|
||||
router = APIRouter(
|
||||
prefix="/face-features",
|
||||
tags=["人脸特征管理"],
|
||||
responses={
|
||||
404: {"description": "资源不存在"},
|
||||
400: {"description": "请求参数错误"},
|
||||
500: {"description": "服务器内部错误"}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/",
|
||||
response_model=FaceFeatureResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="创建人脸特征记录",
|
||||
description="创建新的人脸特征记录"
|
||||
)
|
||||
async def create_face_feature(
|
||||
feature_data: FaceFeatureCreate,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
创建人脸特征记录
|
||||
|
||||
- **person_id**: 人员ID (必须,大于0)
|
||||
- **feature_type**: 特征类型 (可选,大于等于0)
|
||||
- **pic_id**: 图片ID (可选)
|
||||
- **status**: 计算状态 (默认: NOT_STARTED)
|
||||
- **feature_data**: 特征数据 (可选,二进制)
|
||||
"""
|
||||
try:
|
||||
return service.create_feature(feature_data)
|
||||
except ValueError as e:
|
||||
if "already exists" in str(e):
|
||||
# 解析错误信息中的person_id和feature_type
|
||||
raise DuplicateFeatureError(
|
||||
person_id=feature_data.person_id,
|
||||
feature_type=feature_data.feature_type or 0
|
||||
)
|
||||
raise FaceFeatureProcessingError(detail=str(e))
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{feature_id}",
|
||||
response_model=FaceFeatureResponse,
|
||||
summary="获取人脸特征记录",
|
||||
description="根据ID获取人脸特征记录"
|
||||
)
|
||||
async def get_face_feature(
|
||||
feature: FaceFeatureResponse = Depends(get_face_feature_by_id)
|
||||
):
|
||||
"""
|
||||
根据ID获取人脸特征记录
|
||||
|
||||
- **feature_id**: 特征记录ID (路径参数)
|
||||
"""
|
||||
return feature
|
||||
|
||||
|
||||
@router.get(
|
||||
"/",
|
||||
response_model=FaceFeatureListResponse,
|
||||
summary="查询人脸特征记录",
|
||||
description="查询人脸特征记录列表,支持分页和过滤"
|
||||
)
|
||||
async def list_face_features(
|
||||
person_id: Optional[int] = Query(None, description="人员ID", gt=0),
|
||||
feature_type: Optional[int] = Query(None, description="特征类型", ge=0),
|
||||
status: Optional[FeatureStatus] = Query(None, description="计算状态"),
|
||||
start_date: Optional[datetime] = Query(None, description="开始时间"),
|
||||
end_date: Optional[datetime] = Query(None, description="结束时间"),
|
||||
has_feature_data: Optional[bool] = Query(None, description="是否有特征数据"),
|
||||
page: int = Query(1, description="页码", ge=1),
|
||||
page_size: int = Query(20, description="每页数量", ge=1, le=100),
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
查询人脸特征记录
|
||||
|
||||
- **person_id**: 按人员ID过滤 (可选)
|
||||
- **feature_type**: 按特征类型过滤 (可选)
|
||||
- **status**: 按计算状态过滤 (可选)
|
||||
- **start_date**: 开始时间过滤 (可选)
|
||||
- **end_date**: 结束时间过滤 (可选)
|
||||
- **has_feature_data**: 是否有特征数据过滤 (可选)
|
||||
- **page**: 页码 (默认: 1)
|
||||
- **page_size**: 每页数量 (默认: 20, 最大: 100)
|
||||
"""
|
||||
# 构建查询参数
|
||||
query = FaceFeatureQuery(
|
||||
person_id=person_id,
|
||||
feature_type=feature_type,
|
||||
status=status,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
has_feature_data=has_feature_data
|
||||
)
|
||||
|
||||
return service.query_features(
|
||||
query=query,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
order_by="created_time",
|
||||
desc_order=True
|
||||
)
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{feature_id}",
|
||||
response_model=FaceFeatureResponse,
|
||||
summary="更新人脸特征记录",
|
||||
description="更新指定ID的人脸特征记录"
|
||||
)
|
||||
async def update_face_feature(
|
||||
feature_id: int,
|
||||
update_data: FaceFeatureUpdate,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
更新人脸特征记录
|
||||
|
||||
- **feature_id**: 特征记录ID (路径参数)
|
||||
- **update_data**: 更新数据 (请求体)
|
||||
"""
|
||||
try:
|
||||
result = service.update_feature(feature_id, update_data)
|
||||
if not result:
|
||||
raise FeatureNotFoundError(feature_id)
|
||||
return result
|
||||
except ValueError as e:
|
||||
raise FaceFeatureProcessingError(detail=str(e), feature_id=feature_id)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{feature_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
summary="删除人脸特征记录",
|
||||
description="删除指定ID的人脸特征记录"
|
||||
)
|
||||
async def delete_face_feature(
|
||||
feature_id: int,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
删除人脸特征记录
|
||||
|
||||
- **feature_id**: 特征记录ID (路径参数)
|
||||
"""
|
||||
success = service.delete_feature(feature_id)
|
||||
if not success:
|
||||
raise FeatureNotFoundError(feature_id)
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
content=None
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{feature_id}/start-processing",
|
||||
response_model=FaceFeatureResponse,
|
||||
summary="开始处理人脸特征",
|
||||
description="开始计算指定ID的人脸特征值"
|
||||
)
|
||||
async def start_face_feature_processing(
|
||||
feature_id: int,
|
||||
background_tasks: BackgroundTasks,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
开始处理人脸特征计算
|
||||
|
||||
- **feature_id**: 特征记录ID (路径参数)
|
||||
|
||||
注意:这是一个异步处理接口,会立即返回开始状态,
|
||||
实际特征计算可能在后台进行。
|
||||
"""
|
||||
try:
|
||||
# 先获取特征记录
|
||||
feature = service.get_feature(feature_id)
|
||||
if not feature:
|
||||
raise FeatureNotFoundError(feature_id)
|
||||
|
||||
# 检查是否可以开始处理
|
||||
if feature.status != FeatureStatus.NOT_STARTED:
|
||||
raise FaceFeatureProcessingError(
|
||||
detail=f"特征记录状态为 {feature.status_name},无法开始处理",
|
||||
feature_id=feature_id
|
||||
)
|
||||
|
||||
# 开始处理
|
||||
success = service.start_processing(feature_id)
|
||||
if not success:
|
||||
raise FaceFeatureProcessingError(
|
||||
detail="开始处理失败",
|
||||
feature_id=feature_id
|
||||
)
|
||||
|
||||
# 异步任务:模拟特征计算过程
|
||||
# 在实际应用中,这里应该调用实际的特征计算服务
|
||||
background_tasks.add_task(
|
||||
simulate_feature_processing,
|
||||
feature_id=feature_id,
|
||||
service=service
|
||||
)
|
||||
|
||||
# 返回更新后的特征记录
|
||||
return service.get_feature(feature_id)
|
||||
|
||||
except ValueError as e:
|
||||
raise FaceFeatureProcessingError(detail=str(e), feature_id=feature_id)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{feature_id}/finish-processing",
|
||||
response_model=FaceFeatureResponse,
|
||||
summary="完成人脸特征处理",
|
||||
description="完成指定ID的人脸特征值计算"
|
||||
)
|
||||
async def finish_face_feature_processing(
|
||||
feature_id: int,
|
||||
success: bool = Query(True, description="是否成功完成"),
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
完成人脸特征计算
|
||||
|
||||
- **feature_id**: 特征记录ID (路径参数)
|
||||
- **success**: 是否成功完成 (查询参数,默认: true)
|
||||
"""
|
||||
try:
|
||||
# 检查特征记录
|
||||
feature = service.get_feature(feature_id)
|
||||
if not feature:
|
||||
raise FeatureNotFoundError(feature_id)
|
||||
|
||||
# 检查是否可以完成处理
|
||||
if feature.status != FeatureStatus.PROCESSING:
|
||||
raise FaceFeatureProcessingError(
|
||||
detail=f"特征记录状态为 {feature.status_name},无法完成处理",
|
||||
feature_id=feature_id
|
||||
)
|
||||
|
||||
# 完成处理
|
||||
finish_success = service.finish_processing(feature_id, success)
|
||||
if not finish_success:
|
||||
raise FaceFeatureProcessingError(
|
||||
detail="完成处理失败",
|
||||
feature_id=feature_id
|
||||
)
|
||||
|
||||
return service.get_feature(feature_id)
|
||||
|
||||
except ValueError as e:
|
||||
raise FaceFeatureProcessingError(detail=str(e), feature_id=feature_id)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/batch",
|
||||
response_model=List[FaceFeatureResponse],
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="批量创建人脸特征记录",
|
||||
description="批量创建多个人脸特征记录"
|
||||
)
|
||||
async def batch_create_face_features(
|
||||
batch_data: BatchFaceFeatureCreate,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
批量创建人脸特征记录
|
||||
|
||||
- **items**: 特征记录列表 (必须,1-1000条)
|
||||
"""
|
||||
try:
|
||||
return service.create_features_batch(batch_data)
|
||||
except ValueError as e:
|
||||
raise FaceFeatureProcessingError(detail=str(e))
|
||||
|
||||
|
||||
@router.get(
|
||||
"/person/{person_id}",
|
||||
response_model=List[FaceFeatureResponse],
|
||||
summary="获取人员的人脸特征记录",
|
||||
description="根据人员ID获取所有相关的人脸特征记录"
|
||||
)
|
||||
async def get_face_features_by_person(
|
||||
person_id: int,
|
||||
limit: int = Query(100, description="返回数量限制", ge=1, le=1000),
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
获取人员的人脸特征记录
|
||||
|
||||
- **person_id**: 人员ID (路径参数)
|
||||
- **limit**: 返回数量限制 (查询参数,默认: 100, 最大: 1000)
|
||||
"""
|
||||
return service.list_features_by_person(person_id, limit)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/stats/summary",
|
||||
response_model=FaceFeatureStatsResponse,
|
||||
summary="获取特征记录统计信息",
|
||||
description="获取人脸特征记录的统计摘要"
|
||||
)
|
||||
async def get_face_features_stats(
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
获取特征记录统计信息
|
||||
"""
|
||||
return service.get_statistics()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/person/{person_id}/stats",
|
||||
summary="获取人员特征统计信息",
|
||||
description="获取指定人员的特征记录统计信息"
|
||||
)
|
||||
async def get_person_face_features_stats(
|
||||
person_id: int,
|
||||
service: FaceFeatureService = Depends(get_face_feature_service)
|
||||
):
|
||||
"""
|
||||
获取人员特征统计信息
|
||||
|
||||
- **person_id**: 人员ID (路径参数)
|
||||
"""
|
||||
try:
|
||||
stats = service.get_person_statistics(person_id)
|
||||
return {
|
||||
"person_id": person_id,
|
||||
"total_features": stats["total_features"],
|
||||
"status_summary": stats["status_summary"],
|
||||
"feature_types": stats["feature_types"],
|
||||
"avg_processing_time": stats["avg_processing_time"],
|
||||
"successful_count": stats["successful_count"]
|
||||
}
|
||||
except Exception as e:
|
||||
raise FaceFeatureProcessingError(detail=str(e))
|
||||
|
||||
|
||||
async def simulate_feature_processing(
|
||||
feature_id: int,
|
||||
service: FaceFeatureService
|
||||
):
|
||||
"""
|
||||
模拟人脸特征计算过程
|
||||
|
||||
在实际应用中,这里应该调用实际的特征计算算法
|
||||
例如:使用InsightFace、OpenCV等库进行人脸特征提取
|
||||
|
||||
Args:
|
||||
feature_id: 特征记录ID
|
||||
service: 人脸特征服务
|
||||
"""
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
try:
|
||||
# 模拟计算延迟 (3-10秒)
|
||||
delay = random.uniform(3, 10)
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
# 模拟成功或失败 (90%成功率)
|
||||
success = random.random() < 0.9
|
||||
|
||||
# 完成处理
|
||||
service.finish_processing(feature_id, success)
|
||||
|
||||
# 如果成功,添加模拟的特征数据
|
||||
if success:
|
||||
# 生成模拟的512维特征向量 (float32)
|
||||
import numpy as np
|
||||
feature_data = np.random.randn(512).astype(np.float32).tobytes()
|
||||
service.update_feature_data(feature_id, feature_data)
|
||||
|
||||
except Exception as e:
|
||||
# 如果发生异常,标记为失败
|
||||
service.finish_processing(feature_id, False)
|
||||
# 记录日志
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"特征计算失败 (ID: {feature_id}): {e}")
|
||||
Reference in New Issue
Block a user