File size: 5,593 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
"""
外部模板模型

本模块定义外部模板 (ExternalTemplate) 数据模型,
用于存储法院、破产管理人等机构提供的 Word 模板文件及其元数据.
"""

from __future__ import annotations

import logging
from typing import ClassVar

from django.db import models
from django.utils.translation import gettext_lazy as _

from .choices import FillType, TemplateStatus

logger: logging.Logger = logging.getLogger(__name__)


class ExternalTemplate(models.Model):
    """
    外部模板

    存储法院、破产管理人、仲裁委员会等机构提供的 Word 模板文件.
    通过结构指纹 (SHA-256) 去重, 支持 LLM 字段映射分析.
    数据隔离基于 law_firm (律所级别共享).

    Requirements: 1.1, 1.3, 1.4, 1.5, 1.6, 3.1, 3.4, 8.1, 8.2,
                  9.1, 9.2, 11.4, 11.5, 12.1, 13.1, 13.2, 13.3
    """

    id: int
    name = models.CharField(
        max_length=255,
        verbose_name=_("模板名称"),
    )
    source_name = models.CharField(
        max_length=255,
        blank=True,
        default="",
        verbose_name=_("来源机构"),
        help_text=_("法院名称或其他机构名称"),
    )
    file_path = models.CharField(
        max_length=500,
        verbose_name=_("文件路径"),
        help_text=_("相对于 MEDIA_ROOT 的存储路径"),
    )
    original_filename = models.CharField(
        max_length=255,
        verbose_name=_("原始文件名"),
    )
    file_size = models.PositiveIntegerField(
        verbose_name=_("文件大小(字节)"),
    )
    structure_fingerprint = models.CharField(
        max_length=64,
        blank=True,
        default="",
        db_index=True,
        verbose_name=_("结构指纹"),
        help_text=_("基于模板 XML 结构计算的 SHA-256 哈希值"),
    )
    structure_json = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_("提取的结构JSON"),
    )
    status = models.CharField(
        max_length=20,
        choices=TemplateStatus.choices,
        default=TemplateStatus.UPLOADED,
        verbose_name=_("状态"),
    )
    mapping_source = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_("映射来源模板"),
        help_text=_("结构指纹匹配时复用映射的原始模板"),
    )
    version = models.PositiveIntegerField(
        default=1,
        verbose_name=_("版本号"),
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_("是否活跃"),
    )
    uploaded_by = models.ForeignKey(
        "organization.Lawyer",
        on_delete=models.SET_NULL,
        null=True,
        verbose_name=_("上传者"),
        related_name="uploaded_external_templates",
    )
    law_firm = models.ForeignKey(
        "organization.LawFirm",
        on_delete=models.CASCADE,
        verbose_name=_("所属律所"),
        related_name="external_templates",
    )
    status_changed_at = models.DateTimeField(
        auto_now=True,
        verbose_name=_("状态变更时间"),
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_("创建时间"),
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name=_("更新时间"),
    )

    class Meta:
        app_label = "documents"
        verbose_name = _("外部模板")
        verbose_name_plural = _("外部模板")
        ordering: ClassVar = ["-updated_at"]
        indexes: ClassVar = [
            models.Index(fields=["law_firm", "source_name"]),
            models.Index(fields=["law_firm", "is_active"]),
            models.Index(fields=["structure_fingerprint"]),
            models.Index(fields=["status"]),
        ]

    def __str__(self) -> str:
        return self.name


class ExternalTemplateFieldMapping(models.Model):
    """
    外部模板字段映射

    存储 LLM 分析生成的字段映射关系, 将模板中的可填充位置
    通过语义标签(semantic_label)标识, 填充时由 LLM 根据案件数据自动取值。

    Requirements: 4.3, 4.4, 4.5, 5.1, 5.2, 5.3, 5.5, 12.1
    """

    id: int
    template = models.ForeignKey(
        ExternalTemplate,
        on_delete=models.CASCADE,
        related_name="field_mappings",
        verbose_name=_("关联模板"),
    )
    position_locator = models.JSONField(
        verbose_name=_("位置定位器"),
    )
    position_description = models.CharField(
        max_length=500,
        blank=True,
        default="",
        verbose_name=_("位置描述"),
    )
    semantic_label = models.CharField(
        max_length=255,
        verbose_name=_("语义标签"),
    )
    fill_type = models.CharField(
        max_length=30,
        choices=FillType.choices,
        default=FillType.TEXT,
        verbose_name=_("填充类型"),
    )
    sort_order = models.PositiveIntegerField(
        default=0,
        verbose_name=_("排序"),
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name=_("创建时间"),
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name=_("更新时间"),
    )

    class Meta:
        app_label = "documents"
        verbose_name = _("字段映射")
        verbose_name_plural = _("字段映射")
        ordering: ClassVar = ["sort_order", "id"]
        indexes: ClassVar = [
            models.Index(fields=["template", "sort_order"]),
        ]

    def __str__(self) -> str:
        return f"{self.template.name} - {self.semantic_label}"