fc / apps /client /api /clientidentitydoc_api.py
jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
7.03 kB
import logging
from typing import Any
from django.utils.translation import gettext_lazy as _
from ninja import File, Router, Schema
from ninja.files import UploadedFile
from apps.client.schemas import IdentityDocDetailOut, IdentityRecognizeOut
from apps.client.services.wiring import get_task_service_port
from apps.core.infrastructure.throttling import rate_limit_from_settings
logger = logging.getLogger(__name__)
router = Router()
def _get_identity_doc_service() -> Any:
"""工厂函数:创建 ClientIdentityDocService 实例"""
from apps.client.services import ClientIdentityDocService
return ClientIdentityDocService()
def _get_identity_extraction_service() -> Any:
"""工厂函数:创建 IdentityExtractionService 实例"""
from apps.client.services.identity_extraction.extraction_service import IdentityExtractionService
return IdentityExtractionService()
def _get_id_card_merge_service() -> Any:
"""工厂函数:创建 IdCardMergeService 实例"""
from apps.client.services.id_card_merge import IdCardMergeService
return IdCardMergeService()
@router.post("/identity-doc/recognize", response=IdentityRecognizeOut)
def recognize_identity_doc(
request: Any,
file: UploadedFile = File(...),
doc_type: str = "auto",
enable_ollama: bool = False,
) -> IdentityRecognizeOut:
"""识别证件信息"""
image_bytes = file.read()
service = _get_identity_extraction_service()
normalized_doc_type = (doc_type or "").strip() or "auto"
logger.info(
"证件识别请求入参: doc_type=%s, enable_ollama=%s, filename=%s",
normalized_doc_type,
enable_ollama,
getattr(file, "name", ""),
)
result = service.safe_extract(
image_bytes,
normalized_doc_type,
enable_ollama=enable_ollama,
source_name=getattr(file, "name", None),
)
return IdentityRecognizeOut(
success=result["success"],
doc_type=result["doc_type"],
extracted_data=result["extracted_data"],
confidence=result["confidence"],
error=result.get("error"),
)
@router.post("/clients/{client_id}/identity-docs")
def add_identity_doc(
request: Any,
client_id: int,
doc_type: str,
file: UploadedFile = File(...),
) -> dict[str, Any]:
"""添加证件文档"""
service = _get_identity_doc_service()
identity_doc = service.add_identity_doc_from_upload(
client_id=client_id,
doc_type=doc_type,
uploaded_file=file,
user=getattr(request, "user", None),
)
return {"success": True, "doc_id": identity_doc.id, "message": _("证件文档添加成功")}
class MergeIdCardManualIn(Schema):
"""手动合并身份证请求体"""
front_image_path: str
back_image_path: str
front_corners: list[list[int]]
back_corners: list[list[int]]
@router.post("/identity-docs/merge-id-card")
def merge_id_card(
request: Any,
front_image: UploadedFile = File(...),
back_image: UploadedFile = File(...),
client_id: int | None = None,
) -> dict[str, Any]:
"""自动检测并合并身份证正反面为 PDF,可选保存到客户证件"""
service = _get_id_card_merge_service()
result: dict[str, Any] = service.merge_id_card_with_detection(front_image, back_image)
if result.get("success") and client_id:
doc_service = _get_identity_doc_service()
doc = doc_service.add_identity_doc(
client_id=client_id,
doc_type="id_card",
file_path=result["pdf_path"],
)
result["doc_id"] = doc.id
return result
@router.post("/identity-docs/merge-id-card-direct")
def merge_id_card_direct(
request: Any,
front_image: UploadedFile = File(...),
back_image: UploadedFile = File(...),
client_id: int | None = None,
) -> dict[str, Any]:
"""直接合并已裁剪的身份证正反面为 PDF(前端已完成裁剪),可选保存到客户证件"""
service = _get_id_card_merge_service()
result: dict[str, Any] = service.merge_id_card(front_image, back_image)
if result.get("success") and client_id:
doc_service = _get_identity_doc_service()
doc = doc_service.add_identity_doc(
client_id=client_id,
doc_type="id_card",
file_path=result["pdf_path"],
)
result["doc_id"] = doc.id
return result
@router.post("/identity-docs/merge-id-card-manual")
def merge_id_card_manual(
request: Any,
data: MergeIdCardManualIn,
) -> dict[str, Any]:
"""手动指定四角坐标合并身份证"""
service = _get_id_card_merge_service()
result: dict[str, Any] = service.merge_id_card_manual(
front_image_path=data.front_image_path,
back_image_path=data.back_image_path,
front_corners=data.front_corners,
back_corners=data.back_corners,
)
return result
@router.get("/identity-docs/{doc_id}", response=IdentityDocDetailOut)
def get_identity_doc(request: Any, doc_id: int) -> IdentityDocDetailOut:
"""
获取证件文档
Args:
request: HTTP 请求
doc_id: 证件文档 ID
Returns:
证件文档信息
"""
service = _get_identity_doc_service()
identity_doc = service.get_identity_doc(doc_id)
return IdentityDocDetailOut(
id=identity_doc.id,
client_id=identity_doc.client_id,
doc_type=identity_doc.doc_type,
file_path=identity_doc.file_path,
uploaded_at=identity_doc.uploaded_at,
media_url=identity_doc.media_url,
)
@router.delete("/identity-docs/{doc_id}")
def delete_identity_doc(request: Any, doc_id: int) -> dict[str, Any]:
"""
删除证件文档
Args:
request: HTTP 请求
doc_id: 证件文档 ID
Returns:
操作结果
"""
service = _get_identity_doc_service()
service.delete_identity_doc(doc_id=doc_id, user=getattr(request, "user", None))
return {"success": True, "message": _("证件文档删除成功")}
@router.post("/identity-doc/recognize/submit")
@rate_limit_from_settings("TASK", by_user=True)
def submit_recognize_task(
request: Any,
file: UploadedFile = File(...),
) -> dict[str, Any]:
"""提交证件识别异步任务"""
service = _get_identity_doc_service()
rel_path = service.save_uploaded_file_to_dir(file, rel_dir="client_docs/recognize")
task_service = get_task_service_port()
task_id: str = task_service.submit_task(
"apps.client.tasks.execute_identity_doc_recognition",
rel_path,
)
logger.info("证件识别任务已提交", extra={"task_id": task_id, "file_path": rel_path})
return {"task_id": task_id, "status": "pending"}
@router.get("/identity-doc/task/{task_id}")
def get_recognize_task_status(
request: Any,
task_id: str,
) -> dict[str, Any]:
"""查询证件识别任务状态"""
task_service = get_task_service_port()
result: dict[str, Any] = task_service.get_task_status(task_id)
return result