Download apps/client/services/client_mutation_service.py from jun2024/fc: direct link, hf CLI and curl.
- Browser
- Download file 8.27 kB
-
https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/client/services/client_mutation_service.py
- Command line
-
hf download hf://spaces/jun2024/fc/apps/client/services/client_mutation_service.py
-
curl -L -o client_mutation_service.py https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/client/services/client_mutation_service.py
8.27 kB
| """当事人写操作服务。""" | |
| from __future__ import annotations | |
| import logging | |
| from typing import TYPE_CHECKING, Any, cast | |
| from django.db import transaction | |
| from django.utils.translation import gettext_lazy as _ | |
| from apps.client.models import Client | |
| from apps.core.exceptions import ForbiddenError, ValidationException | |
| if TYPE_CHECKING: | |
| from ninja.files import UploadedFile | |
| from apps.client.workflows import ClientDeletionWorkflow | |
| from .client_access_policy import ClientAccessPolicy | |
| from .client_identity_doc_service import ClientIdentityDocService | |
| from .client_query_service import ClientQueryService | |
| logger = logging.getLogger("apps.client") | |
| class ClientMutationService: | |
| _VALID_CLIENT_TYPES: list[str] = [Client.NATURAL, Client.LEGAL, Client.NON_LEGAL_ORG] | |
| _UPDATABLE_FIELDS: set[str] = { | |
| "name", | |
| "phone", | |
| "address", | |
| "client_type", | |
| "id_number", | |
| "legal_representative", | |
| "legal_representative_id_number", | |
| "is_our_client", | |
| } | |
| def __init__( | |
| self, | |
| access_policy: ClientAccessPolicy | None = None, | |
| query_service: ClientQueryService | None = None, | |
| deletion_workflow: ClientDeletionWorkflow | None = None, | |
| identity_doc_service: ClientIdentityDocService | None = None, | |
| ) -> None: | |
| self._access_policy = access_policy | |
| self._query_service = query_service | |
| self._deletion_workflow = deletion_workflow | |
| self._identity_doc_service = identity_doc_service | |
| def access_policy(self) -> ClientAccessPolicy: | |
| if self._access_policy is None: | |
| from .client_access_policy import ClientAccessPolicy | |
| self._access_policy = ClientAccessPolicy() | |
| return self._access_policy | |
| def query_service(self) -> ClientQueryService: | |
| if self._query_service is None: | |
| from .client_query_service import ClientQueryService | |
| self._query_service = ClientQueryService() | |
| return self._query_service | |
| def identity_doc_service(self) -> ClientIdentityDocService: | |
| if self._identity_doc_service is None: | |
| from .client_identity_doc_service import ClientIdentityDocService | |
| self._identity_doc_service = ClientIdentityDocService() | |
| return self._identity_doc_service | |
| def deletion_workflow(self) -> ClientDeletionWorkflow: | |
| if self._deletion_workflow is None: | |
| from apps.client.workflows import ClientDeletionWorkflow | |
| self._deletion_workflow = ClientDeletionWorkflow() | |
| return self._deletion_workflow | |
| def create_client(self, *, data: dict[str, Any], user: Any | None = None) -> Client: | |
| try: | |
| self.access_policy.ensure_can_create_client(user) | |
| except ForbiddenError: | |
| logger.warning( | |
| "用户尝试创建客户但权限不足", | |
| extra={"user_id": getattr(user, "id", None), "action": "create_client"}, | |
| ) | |
| raise | |
| self._validate_create_data(data) | |
| client = Client.objects.create(**data) | |
| # prefetch identity_docs 避免 schema 序列化时 N+1 | |
| client = Client.objects.prefetch_related("identity_docs").get(pk=client.pk) | |
| logger.info( | |
| "客户创建成功", | |
| extra={"client_id": cast(int, client.pk), "user_id": getattr(user, "id", None), "action": "create_client"}, | |
| ) | |
| return client | |
| def update_client(self, *, client_id: int, data: dict[str, Any], user: Any | None = None) -> Client: | |
| try: | |
| self.access_policy.ensure_can_update_client(user) | |
| except ForbiddenError: | |
| logger.warning( | |
| "用户尝试更新客户但权限不足", | |
| extra={"user_id": getattr(user, "id", None), "client_id": client_id, "action": "update_client"}, | |
| ) | |
| raise | |
| client = self.query_service.get_client(client_id=client_id, user=user) | |
| self._validate_update_data(client, data) | |
| updated_fields = [] | |
| for key, value in data.items(): | |
| if key in self._UPDATABLE_FIELDS: | |
| setattr(client, key, value) | |
| updated_fields.append(key) | |
| if updated_fields: | |
| client.save(update_fields=updated_fields) | |
| logger.info( | |
| "客户更新成功", | |
| extra={"client_id": cast(int, client.pk), "user_id": getattr(user, "id", None), "action": "update_client"}, | |
| ) | |
| return client | |
| def delete_client(self, *, client_id: int, user: Any | None = None) -> None: | |
| try: | |
| self.access_policy.ensure_can_delete_client(user) | |
| except ForbiddenError: | |
| logger.warning( | |
| "用户尝试删除客户但权限不足", | |
| extra={"user_id": getattr(user, "id", None), "client_id": client_id, "action": "delete_client"}, | |
| ) | |
| raise | |
| client = self.query_service.get_client(client_id=client_id, user=user) | |
| file_paths = self.deletion_workflow.collect_client_file_paths(client_id=cast(int, client.pk)) | |
| client.delete() | |
| self.deletion_workflow.cleanup_files_on_commit(file_paths=file_paths) | |
| logger.info( | |
| "客户删除成功", | |
| extra={"client_id": client_id, "user_id": getattr(user, "id", None), "action": "delete_client"}, | |
| ) | |
| def create_client_with_docs( | |
| self, | |
| *, | |
| data: dict[str, Any], | |
| doc_types: list[str], | |
| files: list[UploadedFile], | |
| user: Any | None = None, | |
| ) -> Client: | |
| """创建客户并上传证件文档(事务内完成)。""" | |
| client = self.create_client(data=data, user=user) | |
| for doc_type, file in zip(doc_types, files, strict=True): | |
| self.identity_doc_service.add_identity_doc_from_upload( | |
| client_id=client.id, | |
| doc_type=doc_type, | |
| uploaded_file=file, | |
| user=user, | |
| ) | |
| return Client.objects.prefetch_related("identity_docs").get(pk=client.pk) | |
| def _validate_create_data(self, data: dict[str, Any]) -> None: | |
| if not data.get("name"): | |
| raise ValidationException( | |
| message=_("客户名称不能为空"), code="INVALID_NAME", errors={"name": _("客户名称不能为空")} | |
| ) | |
| if data.get("client_type") not in self._VALID_CLIENT_TYPES: | |
| raise ValidationException( | |
| message=_("无效的客户类型"), | |
| code="INVALID_CLIENT_TYPE", | |
| errors={"client_type": _("无效的客户类型")}, | |
| ) | |
| if data.get("client_type") == Client.LEGAL and not data.get("legal_representative"): | |
| raise ValidationException( | |
| message=_("法人客户必须填写法定代表人"), | |
| code="MISSING_LEGAL_REPRESENTATIVE", | |
| errors={"legal_representative": _("法人客户必须填写法定代表人")}, | |
| ) | |
| def _validate_update_data(self, client: Client, data: dict[str, Any]) -> None: | |
| if "name" in data and not data["name"]: | |
| raise ValidationException( | |
| message=_("客户名称不能为空"), code="INVALID_NAME", errors={"name": _("客户名称不能为空")} | |
| ) | |
| if "client_type" in data and data["client_type"] not in self._VALID_CLIENT_TYPES: | |
| raise ValidationException( | |
| message=_("无效的客户类型"), | |
| code="INVALID_CLIENT_TYPE", | |
| errors={"client_type": _("无效的客户类型")}, | |
| ) | |
| client_type = data.get("client_type", client.client_type) | |
| legal_rep = data.get("legal_representative", client.legal_representative) | |
| if client_type == Client.LEGAL and not legal_rep: | |
| raise ValidationException( | |
| message=_("法人客户必须填写法定代表人"), | |
| code="MISSING_LEGAL_REPRESENTATIVE", | |
| errors={"legal_representative": _("法人客户必须填写法定代表人")}, | |
| ) | |