File size: 6,087 Bytes
f8b03c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | """
客户 API 层
只负责请求/响应处理,不包含业务逻辑
"""
from __future__ import annotations
from typing import Any, cast
from django.utils.translation import gettext_lazy as _
from ninja import File, Router, Status
from ninja.files import UploadedFile
from pydantic import BaseModel
from apps.client.schemas import ClientIn, ClientOut, ClientUpdateIn, OACredentialCheckOut
from apps.client.services.text_parser import parse_client_text as _parse_client
from apps.client.services.text_parser import parse_multiple_clients_text as _parse_multi
from apps.core.dto.request_context import extract_request_context
from apps.core.exceptions import ValidationException
from apps.core.utils.id_card_utils import IdCardUtils
class ParseTextRequest(BaseModel):
text: str
parse_multiple: bool = False
class IdCardValidateRequest(BaseModel):
id_number: str
class IdCardValidateResponse(BaseModel):
valid: bool
message: str
router = Router(tags=["Client"])
def _get_query_facade() -> Any:
"""工厂函数:创建 ClientQueryFacade 实例"""
from apps.client.services.client_query_facade import ClientQueryFacade
return ClientQueryFacade()
def _get_mutation_service() -> Any:
"""工厂函数:创建 ClientMutationService 实例"""
from apps.client.services.client_mutation_service import ClientMutationService
return ClientMutationService()
@router.get("/clients", response=list[ClientOut])
def list_clients(
request: Any,
page: int = 1,
page_size: int | None = None,
client_type: str | None = None,
is_our_client: bool | None = None,
search: str | None = None,
) -> list[ClientOut]:
"""获取客户列表"""
facade = _get_query_facade()
user = getattr(request, "auth", None) or extract_request_context(request).user
clients = facade.list_clients(
page=page,
page_size=page_size or 20,
client_type=client_type,
is_our_client=is_our_client,
search=search,
user=user,
)
return list(clients)
@router.post("/clients/parse-text")
def parse_client_text(request: Any, payload: ParseTextRequest) -> dict[str, Any]:
"""解析客户文本信息"""
if payload.parse_multiple:
results = [c for c in _parse_multi(payload.text) if c.get("name")]
return {"success": True, "clients": results}
else:
result = _parse_client(payload.text)
if result.get("name"):
return {"success": True, "client": result, "parse_method": "regex"}
else:
return {"success": False, "error": _("未能解析出客户信息")}
@router.get("/parse-text")
def parse_text_get(request: Any, text: str = "") -> dict[str, Any]:
"""解析客户文本(GET 方式)。"""
return cast(dict[str, Any], _parse_client(text))
@router.post("/clients/validate-id-card", response=IdCardValidateResponse)
def validate_id_card(request: Any, payload: IdCardValidateRequest) -> IdCardValidateResponse:
"""校验身份证号码是否合法"""
result = IdCardUtils.validate_id_card(payload.id_number)
return IdCardValidateResponse(valid=bool(result["valid"]), message=str(result["message"]))
@router.get("/clients/check-oa-credential", response=OACredentialCheckOut)
def check_oa_credential(request: Any) -> OACredentialCheckOut:
"""检查当前用户是否有金诚同达OA凭证。"""
from django.db.models import Q
from apps.organization.models import AccountCredential
lawyer_id = getattr(request.user, "id", None)
if lawyer_id is None:
return OACredentialCheckOut(has_credential=False)
credential = AccountCredential.objects.filter(
Q(account__icontains="jtn.com") | Q(url__icontains="jtn.com"),
lawyer_id=lawyer_id,
).exists()
return OACredentialCheckOut(has_credential=credential)
@router.get("/clients/{client_id}", response=ClientOut)
def get_client(request: Any, client_id: int) -> Any:
"""获取单个客户"""
facade = _get_query_facade()
user = getattr(request, "auth", None) or extract_request_context(request).user
return facade.get_client(client_id=client_id, user=user)
@router.post("/clients", response=ClientOut)
def create_client(request: Any, payload: ClientIn) -> Any:
"""创建客户"""
service = _get_mutation_service()
user = getattr(request, "auth", None) or extract_request_context(request).user
return service.create_client(data=payload.model_dump(), user=user)
@router.post("/clients-with-docs", response=ClientOut)
def create_client_with_docs(
request: Any,
payload: ClientIn,
doc_types: list[str],
files: list[UploadedFile] = File(...),
) -> Any:
"""创建客户并上传文档"""
if doc_types and files and len(doc_types) != len(files):
raise ValidationException(
message=_("证件类型数量与文件数量不一致"),
code="DOC_FILES_MISMATCH",
errors={"doc_types": _("doc_types 与 files 长度必须一致")},
)
mutation_service = _get_mutation_service()
user = getattr(request, "auth", None) or extract_request_context(request).user
return mutation_service.create_client_with_docs(
data=payload.model_dump(),
doc_types=doc_types,
files=files,
user=user,
)
@router.put("/clients/{client_id}", response=ClientOut)
def update_client(request: Any, client_id: int, payload: ClientUpdateIn) -> Any:
"""更新客户"""
service = _get_mutation_service()
data = payload.model_dump(exclude_unset=True)
user = getattr(request, "auth", None) or extract_request_context(request).user
return service.update_client(client_id=client_id, data=data, user=user)
@router.delete("/clients/{client_id}", response={204: None})
def delete_client(request: Any, client_id: int) -> Any:
"""删除客户"""
service = _get_mutation_service()
user = getattr(request, "auth", None) or extract_request_context(request).user
service.delete_client(client_id=client_id, user=user)
return Status(204, None)
|