feat(regulatory): 完善复核前端入口
This commit is contained in:
@@ -79,6 +79,27 @@ def confirm_conditions(request, batch_id: int):
|
|||||||
progress=100,
|
progress=100,
|
||||||
message="适用条件已确认",
|
message="适用条件已确认",
|
||||||
)
|
)
|
||||||
|
record_event(
|
||||||
|
batch,
|
||||||
|
"condition_confirmed",
|
||||||
|
{"conditions": batch.condition_json["confirmed_conditions"], "resume_from": "rule_scope"},
|
||||||
|
)
|
||||||
|
start_regulatory_review_workflow(
|
||||||
|
batch,
|
||||||
|
async_run=getattr(settings, "REGULATORY_REVIEW_ASYNC", True),
|
||||||
|
)
|
||||||
|
batch.refresh_from_db()
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"batch": {
|
||||||
|
"id": batch.pk,
|
||||||
|
"workflow_type": "regulatory_review",
|
||||||
|
"batch_no": batch.batch_no,
|
||||||
|
"status": batch.status,
|
||||||
|
"condition_json": batch.condition_json,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
@@ -160,27 +181,6 @@ def review_issues(request, batch_id: int):
|
|||||||
return JsonResponse({"error": "file_summary_batch_id 不存在或未成功。"}, status=400)
|
return JsonResponse({"error": "file_summary_batch_id 不存在或未成功。"}, status=400)
|
||||||
record = review_missing_issues(batch=batch, issue_ids=[int(item) for item in issue_ids], file_summary_batch=summary_batch)
|
record = review_missing_issues(batch=batch, issue_ids=[int(item) for item in issue_ids], file_summary_batch=summary_batch)
|
||||||
return JsonResponse({"review_record": record})
|
return JsonResponse({"review_record": record})
|
||||||
record_event(
|
|
||||||
batch,
|
|
||||||
"condition_confirmed",
|
|
||||||
{"conditions": batch.condition_json["confirmed_conditions"], "resume_from": "rule_scope"},
|
|
||||||
)
|
|
||||||
start_regulatory_review_workflow(
|
|
||||||
batch,
|
|
||||||
async_run=getattr(settings, "REGULATORY_REVIEW_ASYNC", True),
|
|
||||||
)
|
|
||||||
batch.refresh_from_db()
|
|
||||||
return JsonResponse(
|
|
||||||
{
|
|
||||||
"batch": {
|
|
||||||
"id": batch.pk,
|
|
||||||
"workflow_type": "regulatory_review",
|
|
||||||
"batch_no": batch.batch_no,
|
|
||||||
"status": batch.status,
|
|
||||||
"condition_json": batch.condition_json,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _format_risk_summary(risk_summary: dict) -> str:
|
def _format_risk_summary(risk_summary: dict) -> str:
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ def build_workflow_cards(conversation: Conversation) -> list[dict[str, object]]:
|
|||||||
"risk_label": _format_risk_label(batch.risk_summary or {}),
|
"risk_label": _format_risk_label(batch.risk_summary or {}),
|
||||||
"condition_json": batch.condition_json or {},
|
"condition_json": batch.condition_json or {},
|
||||||
"condition_candidates": (batch.condition_json or {}).get("candidates") or {},
|
"condition_candidates": (batch.condition_json or {}).get("candidates") or {},
|
||||||
|
"notification_count": batch.notifications.count(),
|
||||||
|
"review_record_count": batch.artifacts.filter(metadata__artifact="review_record").count(),
|
||||||
"created_at": batch.created_at,
|
"created_at": batch.created_at,
|
||||||
"nodes": list(
|
"nodes": list(
|
||||||
WorkflowNodeRun.objects.filter(
|
WorkflowNodeRun.objects.filter(
|
||||||
|
|||||||
@@ -850,6 +850,28 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bindRectificationActionButtons() {
|
||||||
|
document.querySelectorAll("[data-rectification-action]").forEach(function (button) {
|
||||||
|
if (button.dataset.bound === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
button.dataset.bound = "true";
|
||||||
|
button.addEventListener("click", function () {
|
||||||
|
if (!promptInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var action = button.getAttribute("data-rectification-action");
|
||||||
|
var batchNo = button.getAttribute("data-batch-no") || "";
|
||||||
|
if (action === "full-review") {
|
||||||
|
promptInput.value = "请基于新的文件汇总批次,对法规核查批次 " + batchNo + " 发起整包复核,并先确认使用哪个补充批次。";
|
||||||
|
} else {
|
||||||
|
promptInput.value = "请对法规核查批次 " + batchNo + " 的缺失项发起复核,并先确认 issue_ids 和补充文件汇总批次。";
|
||||||
|
}
|
||||||
|
promptInput.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function streamChat(event) {
|
async function streamChat(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!composer || !promptInput || !sendButton || !chatStage) {
|
if (!composer || !promptInput || !sendButton || !chatStage) {
|
||||||
@@ -1010,6 +1032,7 @@
|
|||||||
refreshWorkflowBatchCarousel(0);
|
refreshWorkflowBatchCarousel(0);
|
||||||
bindWorkflowBatchCarouselControls();
|
bindWorkflowBatchCarouselControls();
|
||||||
bindConditionConfirmForms();
|
bindConditionConfirmForms();
|
||||||
|
bindRectificationActionButtons();
|
||||||
refreshRunningWorkflowCards();
|
refreshRunningWorkflowCards();
|
||||||
|
|
||||||
if (chatScroll) {
|
if (chatScroll) {
|
||||||
|
|||||||
@@ -237,6 +237,23 @@
|
|||||||
{% if batch.risk_label %}
|
{% if batch.risk_label %}
|
||||||
<p class="workflow-risk-summary">{{ batch.risk_label }}</p>
|
<p class="workflow-risk-summary">{{ batch.risk_label }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if batch.workflow_type == "regulatory_review" %}
|
||||||
|
<div class="workflow-card-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-rectification-action="full-review"
|
||||||
|
data-batch-no="{{ batch.batch_no }}"
|
||||||
|
>整包复核</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-rectification-action="issue-review"
|
||||||
|
data-batch-no="{{ batch.batch_no }}"
|
||||||
|
>缺失项复核</button>
|
||||||
|
</div>
|
||||||
|
<p class="workflow-record-summary">
|
||||||
|
通知 {{ batch.notification_count|default:0 }} · 复核记录 {{ batch.review_record_count|default:0 }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
{% if batch.error_message %}
|
{% if batch.error_message %}
|
||||||
<p class="workflow-error">{{ batch.error_message }}</p>
|
<p class="workflow-error">{{ batch.error_message }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from django.urls import reverse
|
|||||||
from review_agent.models import (
|
from review_agent.models import (
|
||||||
Conversation,
|
Conversation,
|
||||||
FileSummaryBatch,
|
FileSummaryBatch,
|
||||||
|
RegulatoryArtifact,
|
||||||
|
RegulatoryNotificationRecord,
|
||||||
RegulatoryReviewBatch,
|
RegulatoryReviewBatch,
|
||||||
WorkflowNodeRun,
|
WorkflowNodeRun,
|
||||||
)
|
)
|
||||||
@@ -102,6 +104,49 @@ def test_workspace_renders_condition_confirmation_form(client, django_user_model
|
|||||||
assert "甲胎蛋白检测试剂盒" in content
|
assert "甲胎蛋白检测试剂盒" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_workspace_renders_rectification_actions_and_summaries(client, tmp_path, django_user_model):
|
||||||
|
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||||
|
conversation = Conversation.objects.create(user=user, title="会话")
|
||||||
|
summary = FileSummaryBatch.objects.create(
|
||||||
|
conversation=conversation,
|
||||||
|
user=user,
|
||||||
|
batch_no="FS-OK",
|
||||||
|
status=FileSummaryBatch.Status.SUCCESS,
|
||||||
|
)
|
||||||
|
regulatory = RegulatoryReviewBatch.objects.create(
|
||||||
|
conversation=conversation,
|
||||||
|
user=user,
|
||||||
|
source_summary_batch=summary,
|
||||||
|
batch_no="RR-RECTIFY",
|
||||||
|
status=RegulatoryReviewBatch.Status.SUCCESS,
|
||||||
|
)
|
||||||
|
record_path = tmp_path / "review_record.json"
|
||||||
|
record_path.write_text('{"items":[{"status":"review_passed"}]}', encoding="utf-8")
|
||||||
|
RegulatoryArtifact.objects.create(
|
||||||
|
batch=regulatory,
|
||||||
|
artifact_type=RegulatoryArtifact.ArtifactType.JSON,
|
||||||
|
name="review_record.json",
|
||||||
|
storage_path=str(record_path),
|
||||||
|
metadata={"artifact": "review_record"},
|
||||||
|
)
|
||||||
|
RegulatoryNotificationRecord.objects.create(
|
||||||
|
batch=regulatory,
|
||||||
|
channel=RegulatoryNotificationRecord.Channel.MOCK,
|
||||||
|
target="法规整改负责人",
|
||||||
|
status=RegulatoryNotificationRecord.Status.SENT,
|
||||||
|
payload={"title": "缺少申请表"},
|
||||||
|
)
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.get(f"{reverse('home')}?conversation={conversation.pk}")
|
||||||
|
|
||||||
|
content = response.content.decode("utf-8")
|
||||||
|
assert "data-rectification-action=\"full-review\"" in content
|
||||||
|
assert "data-rectification-action=\"issue-review\"" in content
|
||||||
|
assert "通知 1" in content
|
||||||
|
assert "复核记录 1" in content
|
||||||
|
|
||||||
|
|
||||||
def test_frontend_selects_status_url_by_workflow_type():
|
def test_frontend_selects_status_url_by_workflow_type():
|
||||||
script = open("static/js/app.js", encoding="utf-8").read()
|
script = open("static/js/app.js", encoding="utf-8").read()
|
||||||
|
|
||||||
@@ -110,3 +155,5 @@ def test_frontend_selects_status_url_by_workflow_type():
|
|||||||
assert "statusUrlForWorkflow" in script
|
assert "statusUrlForWorkflow" in script
|
||||||
assert "bindConditionConfirmForms" in script
|
assert "bindConditionConfirmForms" in script
|
||||||
assert "data-condition-confirm-form" in script
|
assert "data-condition-confirm-form" in script
|
||||||
|
assert "bindRectificationActionButtons" in script
|
||||||
|
assert "data-rectification-action" in script
|
||||||
|
|||||||
Reference in New Issue
Block a user