feat(regulatory-info-package): 实现材料包生成工作流
This commit is contained in:
127
review_agent/regulatory_info_package/views.py
Normal file
127
review_agent/regulatory_info_package/views.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import json
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.conf import settings
|
||||
from django.http import Http404, JsonResponse
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from review_agent.models import ExportedSummaryFile, RegulatoryInfoPackageBatch, WorkflowNodeRun
|
||||
from review_agent.regulatory_info_package.constants import WORKFLOW_TYPE
|
||||
from review_agent.regulatory_info_package.services.input_select import select_instruction_input
|
||||
from review_agent.regulatory_info_package.workflow import (
|
||||
create_regulatory_info_package_batch,
|
||||
start_regulatory_info_package_workflow,
|
||||
)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def health(request):
|
||||
return JsonResponse({"workflow_type": WORKFLOW_TYPE, "status": "available"})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def start(request):
|
||||
try:
|
||||
payload = json.loads(request.body.decode("utf-8") or "{}")
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({"error": "JSON 格式错误。"}, status=400)
|
||||
from review_agent.models import Conversation
|
||||
|
||||
conversation = Conversation.objects.filter(pk=payload.get("conversation_id"), user=request.user).first()
|
||||
if not conversation:
|
||||
raise Http404("对话不存在。")
|
||||
selection = select_instruction_input(conversation, str(payload.get("message") or ""))
|
||||
if selection.status != "selected":
|
||||
return JsonResponse(
|
||||
{"status": selection.status, "message": selection.message, "candidates": selection.candidates},
|
||||
status=400,
|
||||
)
|
||||
batch = create_regulatory_info_package_batch(
|
||||
conversation=conversation,
|
||||
user=request.user,
|
||||
source_attachment=selection.attachment,
|
||||
source_summary_batch=selection.source_summary_batch,
|
||||
source_summary_item_id=selection.source_summary_item_id,
|
||||
source_file_name=selection.file_name,
|
||||
source_storage_path=selection.storage_path,
|
||||
)
|
||||
start_regulatory_info_package_workflow(batch, async_run=getattr(settings, "REGULATORY_INFO_PACKAGE_ASYNC", True))
|
||||
return JsonResponse({"batch_id": batch.pk, "workflow_type": WORKFLOW_TYPE, "status": batch.status})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def batch_status(request, batch_id: int):
|
||||
batch = RegulatoryInfoPackageBatch.objects.filter(
|
||||
pk=batch_id,
|
||||
conversation__user=request.user,
|
||||
is_deleted=False,
|
||||
).first()
|
||||
if not batch:
|
||||
raise Http404("材料包批次不存在。")
|
||||
exports = ExportedSummaryFile.objects.filter(
|
||||
workflow_type=WORKFLOW_TYPE,
|
||||
workflow_batch_id=batch.pk,
|
||||
).order_by("-export_type", "id")
|
||||
sorted_exports = sorted(exports, key=lambda item: 0 if item.export_type == ExportedSummaryFile.ExportType.ZIP else 1)
|
||||
return JsonResponse(
|
||||
{
|
||||
"batch": {
|
||||
"id": batch.pk,
|
||||
"workflow_type": WORKFLOW_TYPE,
|
||||
"batch_no": batch.batch_no,
|
||||
"status": batch.status,
|
||||
"product_name": batch.product_name,
|
||||
"risk_summary_text": _risk_summary_text(batch),
|
||||
"error_message": batch.error_message,
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"node_code": node.node_code,
|
||||
"node_name": node.node_name,
|
||||
"status": node.status,
|
||||
"progress": node.progress,
|
||||
"message": node.message,
|
||||
}
|
||||
for node in WorkflowNodeRun.objects.filter(
|
||||
workflow_type=WORKFLOW_TYPE,
|
||||
workflow_batch_id=batch.pk,
|
||||
).order_by("id")
|
||||
],
|
||||
"exports": [
|
||||
{
|
||||
"id": export.pk,
|
||||
"export_type": export.export_type,
|
||||
"export_category": export.export_category,
|
||||
"file_name": export.file_name,
|
||||
"download_url": f"/api/review-agent/file-summary/exports/{export.pk}/download/",
|
||||
}
|
||||
for export in sorted_exports
|
||||
],
|
||||
"failed_files": [item for item in batch.generated_files if item.get("status") == "failed"],
|
||||
"notifications": [
|
||||
{
|
||||
"id": item.pk,
|
||||
"channel": item.channel,
|
||||
"send_status": item.send_status,
|
||||
"status_label": "通知已记录" if item.send_status == "success" else item.send_status,
|
||||
"error_message": item.error_message,
|
||||
}
|
||||
for item in batch.notifications.filter(is_deleted=False).order_by("-created_at", "-id")
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _risk_summary_text(batch: RegulatoryInfoPackageBatch) -> str:
|
||||
parts = []
|
||||
if batch.missing_fields:
|
||||
parts.append(f"缺失字段 {len(batch.missing_fields)}")
|
||||
if batch.llm_only_fields:
|
||||
parts.append(f"LLM-only {len(batch.llm_only_fields)}")
|
||||
if batch.conflict_fields:
|
||||
parts.append(f"冲突字段 {len(batch.conflict_fields)}")
|
||||
if batch.risk_notes:
|
||||
parts.append(f"提示 {len(batch.risk_notes)}")
|
||||
return " · ".join(parts)
|
||||
Reference in New Issue
Block a user