File size: 7,013 Bytes
b4f2238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
替换词 Admin 配置

Requirements: 6.1, 3.6
"""

from typing import Any, ClassVar

from django.contrib import admin
from django.db.models import QuerySet
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _

from apps.documents.models import Placeholder


def _get_placeholder_usage_service() -> Any:
    from apps.documents.services.placeholders.placeholder_usage_service import PlaceholderUsageService

    return PlaceholderUsageService()


def _get_code_placeholder_catalog_service() -> Any:
    from apps.documents.services.code_placeholders.catalog_service import CodePlaceholderCatalogService

    return CodePlaceholderCatalogService()


def _get_placeholder_admin_service() -> Any:
    from ..services import PlaceholderAdminService

    return PlaceholderAdminService()


class PlaceholderUsageFilter(admin.SimpleListFilter):
    title = _("用途")
    parameter_name: str = "usage"

    def lookups(self, request: Any, model_admin: Any) -> Any:
        return (
            ("contract", _("合同文件")),
            ("case", _("案件文件")),
            ("both", _("合同+案件")),
            ("unused", _("未使用")),
        )

    def queryset(self, request: Any, queryset: Any) -> Any:
        value = self.value()
        if not value:
            return queryset

        usage_map = getattr(self, "_usage_map_cache", None)
        if usage_map is None:
            usage_map = _get_placeholder_usage_service().get_usage_map()
            self._usage_map_cache = usage_map

        return _get_placeholder_admin_service().filter_by_usage(queryset, value, usage_map)


class PlaceholderAdmin(admin.ModelAdmin):
    """
    替换词管理

    提供替换词的 CRUD 操作,支持搜索和过滤.
    """

    list_display: tuple[Any, ...] = (
        "key",
        "usage_display",
        "example_value_display",
        "is_active",
    )

    list_filter: tuple[Any, ...] = (
        "is_active",
        PlaceholderUsageFilter,
    )

    search_fields: tuple[Any, ...] = (
        "key",
        "display_name",
        "description",
    )

    ordering: tuple[Any, ...] = ("key",)

    fieldsets: tuple[Any, ...] = (
        (None, {"fields": ("key", "display_name")}),
        (_("示例和说明"), {"fields": ("example_value", "description")}),
        (_("状态"), {"fields": ("is_active",)}),
    )

    actions: ClassVar = ["activate_placeholders", "deactivate_placeholders"]

    def has_add_permission(self, request: Any) -> Any:
        return False

    def _catalog_cache(self) -> Any:
        if not hasattr(self, "_cached_code_placeholder_catalog"):
            catalog = _get_code_placeholder_catalog_service()
            definitions = {d.key: d for d in catalog.list_definitions()}
            self._cached_code_placeholder_catalog = definitions
        return self._cached_code_placeholder_catalog

    def _usage_map_cache(self, request: Any) -> Any:
        if request is not None and getattr(request, "_placeholder_usage_map_cached", None) is not None:
            return request._placeholder_usage_map_cached
        usage_map = _get_placeholder_usage_service().get_usage_map()
        if request is not None:
            request._placeholder_usage_map_cached = usage_map
        self._usage_map_for_changelist = usage_map
        return usage_map

    def _ensure_code_placeholders(self, request: Any) -> None:
        if getattr(request, "_code_placeholders_synced", False):
            return
        definitions = self._catalog_cache()
        service = _get_placeholder_admin_service()
        service.ensure_code_placeholders(definitions)
        request._code_placeholders_synced = True

    def get_queryset(self, request: Any) -> QuerySet[Any]:
        self._ensure_code_placeholders(request)
        self._usage_map_cache(request)
        qs = super().get_queryset(request)
        code_keys = list(self._catalog_cache().keys())
        service = _get_placeholder_admin_service()
        return service.get_filtered_queryset(qs, code_keys)

    @admin.display(description=_("用途"))
    def usage_display(self, obj: Any) -> Any:
        usage_map = getattr(self, "_usage_map_for_changelist", None)
        if usage_map is None:
            usage_map = _get_placeholder_usage_service().get_usage_map()
        types = usage_map.get(obj.key, set()) or set()
        parts: list[Any] = []
        if "contract" in types:
            parts.append(str(_("合同文件")))
        if "case" in types:
            parts.append(str(_("案件文件")))
        if not parts:
            return format_html('<span style="color: #999;">{}</span>', "-")
        return " / ".join(parts)

    @admin.display(description=_("来源服务"))
    def code_service_display(self, obj: Any) -> Any:
        definition = self._catalog_cache().get(obj.key)
        return definition.source if definition else ""

    @admin.display(description=_("分类"))
    def code_category_display(self, obj: Any) -> Any:
        definition = self._catalog_cache().get(obj.key)
        return definition.category if definition else ""

    @admin.display(description=_("示例值"))
    def example_value_display(self, obj: Any) -> Any:
        """显示示例值"""
        if obj.example_value:
            # 截断过长的示例值
            value = obj.example_value
            if len(value) > 50:
                value = value[:50] + "..."
            return format_html(
                '<span title="{}" style="color: #666; font-style: italic;">{}</span>', obj.example_value, value
            )
        return format_html('<span style="color: #999;">{}</span>', "-")

    @admin.action(description=_("启用选中的替换词"))
    def activate_placeholders(self, request: Any, queryset: Any) -> None:
        """批量启用替换词"""
        updated = queryset.filter(is_active=False).update(is_active=True)
        self.message_user(request, _("已启用 %(count)d 个替换词") % {"count": updated})

    @admin.action(description=_("禁用选中的替换词"))
    def deactivate_placeholders(self, request: Any, queryset: Any) -> None:
        """批量禁用替换词"""
        updated = queryset.filter(is_active=True).update(is_active=False)
        self.message_user(request, _("已禁用 %(count)d 个替换词") % {"count": updated})

    @admin.action(description=_("复制选中的替换词"))
    def duplicate_placeholders(self, request: Any, queryset: QuerySet[Any]) -> None:
        """复制替换词"""
        service = _get_placeholder_admin_service()
        count: int = 0
        for placeholder in queryset:
            service.duplicate_placeholder(placeholder)
            count += 1

        self.message_user(request, _("已复制 %(count)d 个替换词") % {"count": count})

    def get_readonly_fields(self, request: Any, obj: Any = None) -> Any:
        """编辑时 key 字段只读"""
        if obj:  # 编辑现有对象
            return ("key",)
        return ()