feat(regulatory): 新增法规核查模型与工作流通用字段
This commit is contained in:
@@ -261,8 +261,13 @@ class WorkflowNodeRun(models.Model):
|
||||
batch = models.ForeignKey(
|
||||
FileSummaryBatch,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="node_runs",
|
||||
)
|
||||
workflow_type = models.CharField(max_length=40, blank=True, default="file_summary")
|
||||
workflow_batch_id = models.PositiveBigIntegerField(null=True, blank=True)
|
||||
node_group = models.CharField(max_length=40, blank=True, default="file_summary")
|
||||
node_code = models.CharField(max_length=40)
|
||||
node_name = models.CharField(max_length=80)
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
|
||||
@@ -278,6 +283,10 @@ class WorkflowNodeRun(models.Model):
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "status"], name="idx_ra_node_batch_status"),
|
||||
models.Index(
|
||||
fields=["workflow_type", "workflow_batch_id"],
|
||||
name="idx_ra_node_workflow",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -287,8 +296,19 @@ class WorkflowEvent(models.Model):
|
||||
batch = models.ForeignKey(
|
||||
FileSummaryBatch,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="events",
|
||||
)
|
||||
workflow_type = models.CharField(max_length=40, blank=True, default="file_summary")
|
||||
workflow_batch_id = models.PositiveBigIntegerField(null=True, blank=True)
|
||||
conversation = models.ForeignKey(
|
||||
Conversation,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="workflow_events",
|
||||
)
|
||||
event_type = models.CharField(max_length=40)
|
||||
payload = models.JSONField(default=dict)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
@@ -299,6 +319,10 @@ class WorkflowEvent(models.Model):
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "id"], name="idx_ra_event_batch_id"),
|
||||
models.Index(fields=["batch", "created_at"], name="idx_ra_event_batch_created"),
|
||||
models.Index(
|
||||
fields=["workflow_type", "workflow_batch_id", "id"],
|
||||
name="idx_ra_event_workflow_id",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -308,6 +332,7 @@ class ExportedSummaryFile(models.Model):
|
||||
class ExportType(models.TextChoices):
|
||||
MARKDOWN = "markdown", "Markdown"
|
||||
EXCEL = "excel", "Excel"
|
||||
JSON = "json", "JSON"
|
||||
|
||||
class Status(models.TextChoices):
|
||||
SUCCESS = "success", "成功"
|
||||
@@ -318,6 +343,9 @@ class ExportedSummaryFile(models.Model):
|
||||
on_delete=models.CASCADE,
|
||||
related_name="exports",
|
||||
)
|
||||
workflow_type = models.CharField(max_length=40, blank=True, default="file_summary")
|
||||
workflow_batch_id = models.PositiveBigIntegerField(null=True, blank=True)
|
||||
export_category = models.CharField(max_length=40, blank=True, default="summary")
|
||||
export_type = models.CharField(max_length=20, choices=ExportType.choices)
|
||||
file_name = models.CharField(max_length=255)
|
||||
storage_path = models.CharField(max_length=500)
|
||||
@@ -331,4 +359,210 @@ class ExportedSummaryFile(models.Model):
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "export_type"], name="idx_ra_export_batch_type"),
|
||||
models.Index(fields=["batch", "created_at"], name="idx_ra_export_batch_created"),
|
||||
models.Index(
|
||||
fields=["workflow_type", "workflow_batch_id"],
|
||||
name="idx_ra_export_workflow",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class RegulatoryRuleVersion(models.Model):
|
||||
"""Tracks the local regulatory rule YAML and its matching RAG index."""
|
||||
|
||||
class Status(models.TextChoices):
|
||||
ACTIVE = "active", "启用"
|
||||
OUTDATED = "outdated", "待更新"
|
||||
DISABLED = "disabled", "停用"
|
||||
|
||||
code = models.CharField(max_length=80, unique=True)
|
||||
name = models.CharField(max_length=160)
|
||||
yaml_path = models.CharField(max_length=500)
|
||||
yaml_hash = models.CharField(max_length=128)
|
||||
rag_collection = models.CharField(max_length=120, blank=True, default="")
|
||||
rag_index_version = models.CharField(max_length=80, blank=True, default="")
|
||||
rag_index_hash = models.CharField(max_length=128, blank=True, default="")
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.ACTIVE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ra_regulatory_rule_version"
|
||||
ordering = ["-updated_at", "-id"]
|
||||
indexes = [
|
||||
models.Index(fields=["code", "status"], name="idx_ra_rule_code_status"),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.code
|
||||
|
||||
|
||||
class RegulatoryReviewBatch(models.Model):
|
||||
"""Tracks one NMPA regulatory review workflow run."""
|
||||
|
||||
class Status(models.TextChoices):
|
||||
PENDING = "pending", "待执行"
|
||||
RUNNING = "running", "执行中"
|
||||
SUCCESS = "success", "成功"
|
||||
FAILED = "failed", "失败"
|
||||
|
||||
conversation = models.ForeignKey(
|
||||
Conversation,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="regulatory_review_batches",
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="review_regulatory_batches",
|
||||
)
|
||||
trigger_message = models.ForeignKey(
|
||||
Message,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="triggered_regulatory_batches",
|
||||
)
|
||||
source_summary_batch = models.ForeignKey(
|
||||
FileSummaryBatch,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="regulatory_review_batches",
|
||||
)
|
||||
rule_version = models.ForeignKey(
|
||||
RegulatoryRuleVersion,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="review_batches",
|
||||
)
|
||||
batch_no = models.CharField(max_length=64, unique=True)
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
|
||||
risk_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)
|
||||
|
||||
class Meta:
|
||||
db_table = "ra_regulatory_review_batch"
|
||||
ordering = ["-created_at", "-id"]
|
||||
indexes = [
|
||||
models.Index(fields=["conversation", "created_at"], name="idx_ra_rr_batch_conv"),
|
||||
models.Index(fields=["user", "created_at"], name="idx_ra_rr_batch_user"),
|
||||
models.Index(fields=["status", "created_at"], name="idx_ra_rr_batch_status"),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.batch_no
|
||||
|
||||
|
||||
class RegulatoryIssue(models.Model):
|
||||
"""Stores one regulatory finding after risk consolidation."""
|
||||
|
||||
class Severity(models.TextChoices):
|
||||
BLOCKING = "blocking", "阻断项"
|
||||
HIGH = "high", "高风险"
|
||||
MEDIUM = "medium", "中风险"
|
||||
LOW = "low", "低风险"
|
||||
INFO = "info", "提示"
|
||||
|
||||
class Category(models.TextChoices):
|
||||
COMPLETENESS = "completeness", "完整性"
|
||||
STRUCTURE = "structure", "章节"
|
||||
CONSISTENCY = "consistency", "一致性"
|
||||
RAG = "rag", "法规依据"
|
||||
|
||||
class Status(models.TextChoices):
|
||||
OPEN = "open", "待处理"
|
||||
RESOLVED = "resolved", "已整改"
|
||||
ACCEPTED = "accepted", "已接受"
|
||||
|
||||
batch = models.ForeignKey(
|
||||
RegulatoryReviewBatch,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="issues",
|
||||
)
|
||||
rule_code = models.CharField(max_length=120, blank=True, default="")
|
||||
category = models.CharField(max_length=40, choices=Category.choices)
|
||||
severity = models.CharField(max_length=20, choices=Severity.choices)
|
||||
title = models.CharField(max_length=255)
|
||||
detail = models.TextField(blank=True, default="")
|
||||
suggestion = models.TextField(blank=True, default="")
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.OPEN)
|
||||
evidence = models.JSONField(default=dict, blank=True)
|
||||
citations = models.JSONField(default=list, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ra_regulatory_issue"
|
||||
ordering = ["severity", "id"]
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "severity"], name="idx_ra_rr_issue_severity"),
|
||||
models.Index(fields=["batch", "category"], name="idx_ra_rr_issue_category"),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.title
|
||||
|
||||
|
||||
class RegulatoryArtifact(models.Model):
|
||||
"""Stores regulatory review intermediate and exported artifacts."""
|
||||
|
||||
class ArtifactType(models.TextChoices):
|
||||
MARKDOWN = "markdown", "Markdown"
|
||||
EXCEL = "excel", "Excel"
|
||||
JSON = "json", "JSON"
|
||||
TEXT = "text", "文本"
|
||||
|
||||
batch = models.ForeignKey(
|
||||
RegulatoryReviewBatch,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="artifacts",
|
||||
)
|
||||
artifact_type = models.CharField(max_length=20, choices=ArtifactType.choices)
|
||||
name = models.CharField(max_length=160)
|
||||
storage_path = models.CharField(max_length=500)
|
||||
content_hash = models.CharField(max_length=128, blank=True, default="")
|
||||
metadata = models.JSONField(default=dict, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ra_regulatory_artifact"
|
||||
ordering = ["-created_at", "-id"]
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "artifact_type"], name="idx_ra_rr_artifact_type"),
|
||||
]
|
||||
|
||||
|
||||
class RegulatoryNotificationRecord(models.Model):
|
||||
"""Stores mock notification records for future Feishu integration."""
|
||||
|
||||
class Channel(models.TextChoices):
|
||||
MOCK = "mock", "模拟"
|
||||
FEISHU = "feishu", "飞书"
|
||||
|
||||
class Status(models.TextChoices):
|
||||
PENDING = "pending", "待发送"
|
||||
SENT = "sent", "已发送"
|
||||
FAILED = "failed", "失败"
|
||||
|
||||
batch = models.ForeignKey(
|
||||
RegulatoryReviewBatch,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="notifications",
|
||||
)
|
||||
channel = models.CharField(max_length=20, choices=Channel.choices, default=Channel.MOCK)
|
||||
target = models.CharField(max_length=160, blank=True, default="")
|
||||
payload = models.JSONField(default=dict, blank=True)
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
|
||||
error_message = models.TextField(blank=True, default="")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
sent_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "ra_regulatory_notification_record"
|
||||
ordering = ["-created_at", "-id"]
|
||||
indexes = [
|
||||
models.Index(fields=["batch", "status"], name="idx_ra_rr_notify_status"),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user