feat(regulatory-info-package): 增加材料包数据模型

This commit is contained in:
2026-06-10 19:49:25 +08:00
parent a060c23ba7
commit f0286264e2
3 changed files with 592 additions and 1 deletions

View File

@@ -280,7 +280,11 @@ class WorkflowNodeRun(models.Model):
class Meta:
db_table = "ra_workflow_node_run"
constraints = [
models.UniqueConstraint(fields=["batch", "node_code"], name="uq_ra_node_batch_code")
models.UniqueConstraint(fields=["batch", "node_code"], name="uq_ra_node_batch_code"),
models.UniqueConstraint(
fields=["workflow_type", "workflow_batch_id", "node_code"],
name="uq_ra_node_workflow_batch_code",
),
]
indexes = [
models.Index(fields=["batch", "status"], name="idx_ra_node_batch_status"),
@@ -336,6 +340,7 @@ class ExportedSummaryFile(models.Model):
JSON = "json", "JSON"
WORD = "word", "Word"
PDF = "pdf", "PDF"
ZIP = "zip", "ZIP"
class Status(models.TextChoices):
SUCCESS = "success", "成功"
@@ -345,6 +350,8 @@ class ExportedSummaryFile(models.Model):
FileSummaryBatch,
on_delete=models.CASCADE,
related_name="exports",
null=True,
blank=True,
)
workflow_type = models.CharField(max_length=40, blank=True, default="file_summary")
workflow_batch_id = models.PositiveBigIntegerField(null=True, blank=True)
@@ -524,6 +531,87 @@ class ApplicationFormFillBatch(models.Model):
return self.batch_no
class RegulatoryInfoPackageBatch(models.Model):
"""Tracks one Chapter 1 regulatory information package workflow run."""
class Status(models.TextChoices):
PENDING = "pending", "待执行"
RUNNING = "running", "执行中"
WAITING_USER = "waiting_user", "等待用户"
SUCCESS = "success", "成功"
PARTIAL_SUCCESS = "partial_success", "部分成功"
FAILED = "failed", "失败"
CANCELLED = "cancelled", "已取消"
conversation = models.ForeignKey(
Conversation,
on_delete=models.CASCADE,
related_name="regulatory_info_package_batches",
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="review_regulatory_info_package_batches",
)
trigger_message = models.ForeignKey(
Message,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="triggered_regulatory_info_package_batches",
)
source_attachment = models.ForeignKey(
FileAttachment,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="regulatory_info_package_batches",
)
source_summary_batch = models.ForeignKey(
FileSummaryBatch,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="regulatory_info_package_batches",
)
source_summary_item_id = models.PositiveBigIntegerField(null=True, blank=True)
batch_no = models.CharField(max_length=64, unique=True)
status = models.CharField(max_length=30, choices=Status.choices, default=Status.PENDING)
source_file_name = models.CharField(max_length=255, blank=True, default="")
source_storage_path = models.CharField(max_length=500, blank=True, default="")
product_name = models.CharField(max_length=200, blank=True, default="")
output_zip_name = models.CharField(max_length=255, blank=True, default="第1章 监管信息(预生成版).zip")
generated_files = models.JSONField(default=list, blank=True)
missing_fields = models.JSONField(default=list, blank=True)
llm_only_fields = models.JSONField(default=list, blank=True)
conflict_fields = models.JSONField(default=list, blank=True)
risk_notes = models.JSONField(default=list, blank=True)
template_config_version = models.CharField(max_length=80, blank=True, default="")
template_config_hash = models.CharField(max_length=128, blank=True, default="")
adapter_summary = models.JSONField(default=dict, blank=True)
work_dir = models.CharField(max_length=500, blank=True, default="")
error_message = models.TextField(blank=True, default="")
created_at = models.DateTimeField(auto_now_add=True)
started_at = models.DateTimeField(null=True, blank=True)
finished_at = models.DateTimeField(null=True, blank=True)
archived_at = models.DateTimeField(null=True, blank=True)
is_deleted = models.BooleanField(default=False)
class Meta:
db_table = "ra_regulatory_info_package_batch"
ordering = ["-created_at", "-id"]
indexes = [
models.Index(fields=["conversation", "status"], name="idx_ra_rip_batch_conv_status"),
models.Index(fields=["user", "created_at"], name="idx_ra_rip_batch_user_created"),
models.Index(fields=["source_attachment"], name="idx_ra_rip_batch_attachment"),
models.Index(fields=["source_summary_batch"], name="idx_ra_rip_batch_summary"),
models.Index(fields=["created_at"], name="idx_ra_rip_batch_created"),
]
def __str__(self) -> str:
return self.batch_no
class RegulatoryReviewBatch(models.Model):
"""Tracks one NMPA regulatory review workflow run."""
@@ -745,6 +833,54 @@ class ApplicationFormFillArtifact(models.Model):
]
class RegulatoryInfoPackageArtifact(models.Model):
"""Stores regulatory information package intermediate and generated files."""
class ArtifactType(models.TextChoices):
TEMPLATE_COPY = "template_copy", "模板副本"
INSTRUCTION_EXTRACT = "instruction_extract", "说明书抽取结果"
FIELD_EXTRACT_RESULT = "field_extract_result", "字段抽取结果"
MERGED_FIELDS = "merged_fields", "合并字段"
GENERATED_DOCUMENT = "generated_document", "生成文件"
TRACEABILITY = "traceability", "追溯清单"
ZIP_PACKAGE = "zip_package", "ZIP包"
NOTIFICATION_RECORD = "notification_record", "通知记录"
class FileFormat(models.TextChoices):
JSON = "json", "JSON"
EXCEL = "excel", "Excel"
DOCX = "docx", "DOCX"
DOC = "doc", "DOC"
ZIP = "zip", "ZIP"
MARKDOWN = "markdown", "Markdown"
batch = models.ForeignKey(
RegulatoryInfoPackageBatch,
on_delete=models.CASCADE,
related_name="artifacts",
)
artifact_type = models.CharField(max_length=60, choices=ArtifactType.choices)
file_format = models.CharField(max_length=20, choices=FileFormat.choices)
name = models.CharField(max_length=160)
file_name = models.CharField(max_length=255)
storage_path = models.CharField(max_length=500)
file_size = models.BigIntegerField(default=0)
content_hash = models.CharField(max_length=128, blank=True, default="")
metadata = models.JSONField(default=dict, blank=True)
created_by_node = models.CharField(max_length=60, blank=True, default="")
created_at = models.DateTimeField(auto_now_add=True)
is_deleted = models.BooleanField(default=False)
class Meta:
db_table = "ra_regulatory_info_package_artifact"
ordering = ["-created_at", "-id"]
indexes = [
models.Index(fields=["batch", "artifact_type"], name="idx_ra_rip_artifact_batch_type"),
models.Index(fields=["file_format"], name="idx_ra_rip_artifact_format"),
models.Index(fields=["created_at"], name="idx_ra_rip_artifact_created"),
]
class ApplicationFormFillNotificationRecord(models.Model):
"""Stores mock/Feishu notification records for application-form auto-fill."""
@@ -795,6 +931,55 @@ class ApplicationFormFillNotificationRecord(models.Model):
]
class RegulatoryInfoPackageNotificationRecord(models.Model):
"""Stores mock/Feishu notification records for regulatory info packages."""
class Channel(models.TextChoices):
FEISHU_CLI = "feishu_cli", "飞书 CLI"
FEISHU_API = "feishu_api", "飞书 API"
MOCK = "mock", "模拟"
class SendStatus(models.TextChoices):
PENDING = "pending", "待发送"
SUCCESS = "success", "成功"
FAILED = "failed", "失败"
batch = models.ForeignKey(
RegulatoryInfoPackageBatch,
on_delete=models.CASCADE,
related_name="notifications",
)
recipient = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="regulatory_info_package_notifications",
)
channel = models.CharField(max_length=30, choices=Channel.choices, default=Channel.MOCK)
export_ids = models.JSONField(default=list, blank=True)
message_summary = models.TextField(blank=True, default="")
send_status = models.CharField(
max_length=20,
choices=SendStatus.choices,
default=SendStatus.PENDING,
)
retry_count = models.PositiveIntegerField(default=0)
external_message_id = models.CharField(max_length=120, blank=True, default="")
error_message = models.TextField(blank=True, default="")
sent_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_deleted = models.BooleanField(default=False)
class Meta:
db_table = "ra_regulatory_info_package_notification_record"
ordering = ["-created_at", "-id"]
indexes = [
models.Index(fields=["batch", "created_at"], name="idx_ra_rip_notify_batch"),
models.Index(fields=["recipient", "send_status"], name="idx_ra_rip_notify_recipient"),
models.Index(fields=["send_status", "retry_count"], name="idx_ra_rip_notify_status"),
]
class FeishuUserMapping(models.Model):
"""Maps a system user to Feishu identifiers maintained by Admin."""