fc / apps /documents /services /placeholders /context_builder.py
jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
7.79 kB
"""Business logic services."""
from __future__ import annotations
import logging
from typing import Any
from django.utils.translation import gettext_lazy as _
from apps.core.exceptions import ValidationException
from .fallback import (
PLACEHOLDER_FALLBACK_VALUE,
ensure_required_placeholders,
get_service_placeholder_keys,
normalize_service_result,
)
from .registry import PlaceholderRegistry
from .types import PlaceholderContextData
logger = logging.getLogger(__name__)
class EnhancedContextBuilder:
"""增强的上下文构建器"""
def __init__(self, registry: PlaceholderRegistry | None = None) -> None:
"""
初始化上下文构建器
Args:
registry: 占位符注册表实例,如果为 None 则使用默认实例
"""
self.registry = registry or PlaceholderRegistry()
def build_context(
self, context_data: PlaceholderContextData, required_placeholders: list[str] | None = None
) -> dict[str, Any]:
"""
构建完整的替换词上下文
Args:
context_data: 原始数据(contract, clients 等)
required_placeholders: 可选,仅生成指定的占位符
Returns:
完整的占位符上下文字典
"""
if not context_data:
logger.warning("上下文数据为空")
return {}
context_data = self._normalize_context_data(context_data)
context: dict[str, Any] = {}
services = self._get_relevant_services(required_placeholders)
for service in services:
service_keys = get_service_placeholder_keys(service)
try:
service_result = service.generate(context_data)
normalized_result = normalize_service_result(service_result, expected_keys=service_keys)
if normalized_result:
context.update(normalized_result)
logger.debug("服务 %s 生成了 %s 个占位符", service.name, len(normalized_result))
except Exception as e:
logger.error(
"占位符服务执行失败: %s",
service.name,
extra={
"service_name": service.name,
"error": str(e),
"context_keys": list(context_data.keys()),
},
exc_info=True,
)
context.update(dict.fromkeys(service_keys, PLACEHOLDER_FALLBACK_VALUE))
# 继续执行其他服务,不中断整个流程
continue
if context_data.get("supplementary_agreement"):
key_map = {
"补充协议委托人信息": "委托人信息",
"补充协议委托人签名盖章信息": "委托人签名盖章信息",
"补充协议委托人主体信息条款": "委托人主体信息条款",
"补充协议委托人数量": "委托人数量",
"补充协议对方当事人主体信息条款": "对方当事人主体信息条款",
}
for old_key, new_key in key_map.items():
if old_key in context:
context[new_key] = context[old_key]
final_context = ensure_required_placeholders(context, required_placeholders)
logger.info("上下文构建完成,生成了 %s 个占位符", len(final_context))
return final_context
def _normalize_context_data(self, context_data: PlaceholderContextData) -> PlaceholderContextData:
"""标准化上下文,对常见缺失键进行兜底补全."""
normalized: dict[str, Any] = dict(context_data)
if normalized.get("case_id") is None:
case_obj = normalized.get("case")
case_id = getattr(case_obj, "id", None)
if case_id is not None:
normalized["case_id"] = case_id
return normalized
def build_contract_context(self, contract_id: int) -> dict[str, Any]:
"""
为合同构建上下文(便捷方法)
Args:
contract_id: 合同 ID
Returns:
完整的占位符上下文字典
Raises:
ValidationException: 合同不存在或数据无效
"""
try:
# 验证合同存在性
# Requirements: 3.2
from apps.documents.services.infrastructure.wiring import get_contract_service
contract_service = get_contract_service()
contract_dto = contract_service.get_contract_internal(contract_id)
if not contract_dto:
raise ValidationException(
message=_("合同不存在"),
code="CONTRACT_NOT_FOUND",
errors={"contract_id": f"ID 为 {contract_id} 的合同不存在"},
)
contract = contract_service.get_contract_model_internal(contract_id)
if not contract:
raise ValidationException(
message=_("合同不存在"),
code="CONTRACT_NOT_FOUND",
errors={"contract_id": f"ID 为 {contract_id} 的合同不存在"},
)
# 构建上下文数据
context_data: PlaceholderContextData = {"contract": contract, "contract_id": contract_id}
return self.build_context(context_data)
except Exception as e:
if isinstance(e, ValidationException):
raise
logger.error("构建合同上下文失败: %s", e, extra={"contract_id": contract_id}, exc_info=True)
raise ValidationException(
message=_("构建合同上下文失败"),
code="CONTEXT_BUILD_ERROR",
errors={"contract_id": f"合同 {contract_id} 上下文构建失败: {e!s}"},
) from e
def _get_relevant_services(self, required_placeholders: list[str] | None = None) -> Any:
"""
获取相关的占位符服务
Args: required_placeholders: 需要的占位符键列表
Returns:
相关的服务实例列表
"""
if not required_placeholders:
# 如果没有指定占位符,返回所有服务
return self.registry.get_all_services()
# 根据占位符键查找相关服务
relevant_services: list[Any] = []
for placeholder_key in required_placeholders:
service = self.registry.get_service_for_placeholder(placeholder_key)
if service and service not in relevant_services:
relevant_services.append(service)
return relevant_services
def get_available_placeholders(self) -> dict[str, list[str]]:
"""
获取所有可用的占位符按分类分组
Returns:
按分类分组的占位符字典
"""
result: dict[str, Any] = {}
services = self.registry.get_all_services()
for service in services:
category = service.category
if category not in result:
result[category] = []
result[category].extend(service.get_placeholder_keys())
return result
def validate_placeholders(self, placeholder_keys: list[str]) -> dict[str, bool]:
"""
验证占位符键是否可用
Args:
placeholder_keys: 要验证的占位符键列表
Returns:
占位符键的可用性字典
"""
result: dict[str, Any] = {}
for key in placeholder_keys:
service = self.registry.get_service_for_placeholder(key)
result[key] = service is not None
return result