""" 文书模板 Admin 服务 处理Admin层的复杂业务逻辑 Requirements: 3.1, 3.2, 3.3 """ import logging from typing import Any from django.utils.html import format_html, format_html_join from django.utils.translation import gettext_lazy as _ from apps.core.exceptions import NotFoundError from apps.core.utils.path import Path from apps.documents.models.choices import DocumentTemplateType, LegalStatusMatchMode logger = logging.getLogger(__name__) class DocumentTemplateAdminService: """文书模板Admin服务""" def __init__(self, template_service: Any | None = None) -> None: self._template_service = template_service @property def template_service(self) -> Any: """延迟加载模板服务""" if self._template_service is None: from .template_service import DocumentTemplateService self._template_service = DocumentTemplateService() return self._template_service def get_form_initial_values(self, instance: Any, existing_files: list[tuple[str, str]]) -> dict[str, Any]: """ 获取表单初始值 Args: instance: 模板实例 existing_files: 已有文件列表 [(path, display_name), ...] Returns: 初始值字典 """ initial = { "template_type": instance.template_type, "contract_sub_type": instance.contract_sub_type or "", "case_sub_type": instance.case_sub_type or "", "contract_types_field": instance.contract_types or [], "case_types_field": instance.case_types or [], "case_stage_field": "", "existing_file": "", "file_path": "", "legal_statuses_field": instance.legal_statuses or [], "legal_status_match_mode": instance.legal_status_match_mode or LegalStatusMatchMode.ANY, } case_stages = instance.case_stages or [] initial["case_stage_field"] = case_stages[0] if case_stages else "" if instance.file_path: current_path = instance.file_path for path, _ in existing_files: if path == current_path: initial["existing_file"] = path initial["file_path"] = "" break elif instance.file: current_file_name = instance.file.name for path, _ in existing_files: if path == current_file_name: initial["existing_file"] = path break return initial def validate_file_sources( self, existing_file: str, uploaded_file: Any, file_path: str, instance: Any, is_editing: bool ) -> dict[str, Any]: """ 验证文件来源 Args: existing_file: 从模板库选择的文件 uploaded_file: 上传的文件 file_path: 手动输入的路径 instance: 模板实例 is_editing: 是否是编辑模式 Returns: 验证结果字典,包含 is_valid, error, cleaned_data """ result = { "is_valid": True, "error": None, "cleaned_data": {"existing_file": existing_file, "file": uploaded_file, "file_path": file_path}, } if is_editing: _original_file = instance.file original_file_path = instance.file_path file_modified = ( bool(existing_file) or bool(uploaded_file) or (file_path and file_path.strip() != original_file_path) ) if not file_modified: result["skip_file_validation"] = True return result sources = sum([bool(existing_file), bool(uploaded_file), bool(file_path and file_path.strip())]) if sources > 1: result["is_valid"] = False result["error"] = _("只能选择一种文件来源:从模板库选择、上传新文件、或手动输入路径") return result if existing_file: result["cleaned_data"]["file_path"] = existing_file result["cleaned_data"]["file"] = None result["cleaned_data"]["existing_file"] = "" has_file_source = bool(existing_file) or bool(uploaded_file) or bool(file_path and file_path.strip()) has_existing_file = is_editing and (instance.file or instance.file_path) if not has_file_source and (not has_existing_file): result["is_valid"] = False result["error"] = _("必须选择一种文件来源") return result def validate_template_type( self, template_type: str, contract_sub_type: str, case_sub_type: str, is_editing: bool, original_template_type: str | None, ) -> dict[str, Any]: """ 验证模板类型 Args: template_type: 模板类型 contract_sub_type: 合同子类型 Returns: 验证结果字典 """ result = { "is_valid": True, "errors": {}, "contract_sub_type": contract_sub_type, "case_sub_type": case_sub_type, } if template_type == DocumentTemplateType.CONTRACT: if not contract_sub_type: result["is_valid"] = False result["errors"]["contract_sub_type"] = _("选择合同文书模板时,必须选择合同子类型") result["case_sub_type"] = None elif template_type == DocumentTemplateType.CASE: result["contract_sub_type"] = None should_require_case_sub_type = not is_editing or ( original_template_type is not None and original_template_type != DocumentTemplateType.CASE ) if should_require_case_sub_type and (not case_sub_type): result["is_valid"] = False result["errors"]["case_sub_type"] = _("选择案件文书模板时,必须选择案件文件子类型") return result def prepare_save_data( self, template_type: str, contract_sub_type: str, case_sub_type: str, contract_types_field: list[str], case_types_field: list[str], case_stage_field: str, file: Any, file_path: str, legal_statuses_field: list[str] | None = None, legal_status_match_mode: str | None = None, ) -> dict[str, Any]: """ 准备保存数据 Args: template_type: 模板类型 contract_sub_type: 合同子类型 case_sub_type: 案件子类型 contract_types_field: 合同类型列表 case_types_field: 案件类型列表 case_stage_field: 案件阶段 file: 上传的文件 file_path: 文件路径 legal_statuses_field: 诉讼地位列表 legal_status_match_mode: 诉讼地位匹配模式 Returns: 准备好的数据字典 """ data = { "template_type": template_type, "contract_sub_type": contract_sub_type if template_type == DocumentTemplateType.CONTRACT else None, "case_sub_type": case_sub_type if template_type == DocumentTemplateType.CASE else None, "contract_types": [], "case_types": [], "case_stages": [], "legal_statuses": [], "legal_status_match_mode": LegalStatusMatchMode.ANY, "file": file, "file_path": file_path, } if template_type == DocumentTemplateType.CONTRACT: data["contract_types"] = contract_types_field or [] elif template_type == DocumentTemplateType.CASE: data["case_types"] = case_types_field or [] data["case_stages"] = [case_stage_field] if case_stage_field else [] data["legal_statuses"] = legal_statuses_field or [] data["legal_status_match_mode"] = legal_status_match_mode or LegalStatusMatchMode.ANY else: data["contract_types"] = contract_types_field or [] data["case_types"] = case_types_field or [] data["case_stages"] = [case_stage_field] if case_stage_field else [] if file: data["file_path"] = "" elif file_path: data["file"] = "" return data def render_placeholders_table(self, placeholders: list[str], undefined: set[str]) -> str: """渲染占位符表格HTML""" if not placeholders: return str(_("未找到占位符")) rows = [] for placeholder in placeholders: if placeholder in undefined: status = format_html('{}', "⚠️ 未定义") row_style = "background: #ffebee;" else: status = format_html('{}', "✓ 已定义") row_style = "" rows.append( format_html( '
| 占位符 | ' '状态 | ' "
|---|
' "⚠️ 发现 {} 个未定义的占位符:
" '请在"替换词管理"中注册这些占位符.
' "