feat: update cms case create

This commit is contained in:
2026-06-15 17:39:20 +08:00
parent 8a40fde923
commit 2bb9ff50d8
2 changed files with 194 additions and 13 deletions
+74 -9
View File
@@ -12,6 +12,7 @@
"""
import logging
from django.db import transaction
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse, inline_serializer
from rest_framework import viewsets, filters, status, serializers as drf_serializers
@@ -26,10 +27,13 @@ from apps.cms.permissions import (
)
from apps.user.models import Institution, Department
from apps.user.throttling import PdfParseUserThrottle
from .models import CaseBase
from .models import CaseBase, ScoringRule
from .serializers import CaseBaseListSerializer
from .services import case_importer
from .services.case_writer import create_case_full, build_full_response
from .services import case_importer, scoring_rule_generator
from .services.case_writer import (
create_case_full, update_case_full, build_full_response, build_scoring_rule_input,
validate_scoring_rules, SCORING_RULE_FIELDS,
)
audit = logging.getLogger('audit')
@@ -84,14 +88,50 @@ class CmsCaseViewSet(viewsets.ModelViewSet):
# ── CMS-CASE-3 表单新增(草稿)/ CMS-CASE-1 列表 ──────────────────────
def create(self, request, *args, **kwargs):
"""新增病例(草稿)。评分规则非必填——评分规则在「发布」时由 AI 生成并落库。"""
self._require_editor()
case, n_rules, n_items = create_case_full(request.data, request.user, institution=self._resolve_institution())
case, n_rules, n_items = create_case_full(
request.data, request.user,
institution=self._resolve_institution(),
require_scoring_rules=False,
)
audit.info('CMS_CASE_CREATE case_id=%s by=%s inst=%s scoring_rules=%d exam_items=%d',
case.id, request.user.id, case.institution_id, n_rules, n_items)
return Response(build_full_response(case), status=status.HTTP_201_CREATED)
# list 用默认实现(默认管理器已排除软删;医院管理员审核用 ?publish_status=1 取待审核)
# ── CMS-CASE-8 编辑草稿(再次保存)──────────────────────────────────
@extend_schema(
summary='CMS-CASE-8 编辑草稿(再次保存)',
description='对已保存的草稿病例,编辑录入后的表单(表单/PDF/AI 三种录入数据均在表单中)'
'再次保存,整体更新草稿内容。仅草稿(publish_status=0)可编辑。',
request=inline_serializer('CmsCaseSaveDraftRequest', fields={
'title': drf_serializers.CharField(required=False),
'case_type': drf_serializers.ChoiceField(choices=['traditional', 'teaching'], required=False,
help_text='只读校验,不支持修改;传入须与原病例一致'),
'department_name': drf_serializers.CharField(required=False, help_text='科室名称(解析为 department_id'),
'traditional': drf_serializers.DictField(required=False),
'teaching': drf_serializers.DictField(required=False),
'exam_items': drf_serializers.ListField(
child=drf_serializers.DictField(), required=False,
help_text='检查项(传入即整表替换;同一病例 item_code 不可重复)'),
'scoring_rules': drf_serializers.ListField(
child=drf_serializers.DictField(), required=False,
help_text='评分规则(一般发布时自动生成;如传入则整表替换)'),
}),
responses={200: OpenApiResponse(description='完整病例结构(同 CMS-CASE-7')},
tags=['CMS-病例'],
)
@action(detail=True, methods=['post'], url_path='save-draft')
def save_draft(self, request, pk=None):
"""编辑录入后的表单再次保存:更新草稿内容(主表+子表+检查项,评分规则可选)。"""
self._require_editor()
case = self.get_object()
case = update_case_full(case, request.data, request.user)
audit.info('CMS_CASE_SAVE_DRAFT case_id=%s by=%s', case.id, request.user.id)
return Response(build_full_response(case))
# ── CMS-CASE-7 / CMS-AUDIT-2 病例查看(完整结构)─────────────────────
@extend_schema(summary='CMS-CASE-7 病例查看(完整结构)', tags=['CMS-病例'])
@action(detail=True, methods=['get'])
@@ -218,12 +258,37 @@ class CmsCaseViewSet(viewsets.ModelViewSet):
@extend_schema(summary='CMS-AUDIT-3 发布(正常 → 已发布)', tags=['CMS-病例审核'])
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
"""医院管理员审核发布:正常(1) → 已发布(2)。发布后对本院移动端医学生可见。超管不做审核。"""
"""审核发布:正常(1) → 已发布(2)。
发布前先由 AI 生成该病例评分规则并落库;评分规则生成或入库失败则不发布
(评分规则落库与发布状态变更在同一事务内,保证「无评分规则不发布」)。
发布后对本院移动端医学生可见。
"""
self._require_publisher()
case = self.get_object()
if case.publish_status != 1:
raise AppError('CASE_NOT_PUBLISHABLE', '仅「正常」状态病例可发布', status_code=400)
case.publish_status = 2 # 已发布
case.save(update_fields=['publish_status', 'updated_at'])
audit.info('CMS_CASE_PUBLISH case_id=%s by=%s', case.id, request.user.id)
return Response({'message': '已发布', 'id': case.id, 'publish_status': case.publish_status})
# 1) AI 生成评分规则(慢 / 可能失败,放在事务外,失败则不触碰数据库)
gen = scoring_rule_generator.generate(build_scoring_rule_input(case))
rules = gen['scoring_rules']
validate_scoring_rules(rules)
# 2) 评分规则落库 + 发布状态变更,同一事务原子完成
with transaction.atomic():
case.scoring_rules.all().delete() # 幂等:清掉历史规则后重新写入
ScoringRule.objects.bulk_create([
ScoringRule(case=case, **{k: v for k, v in r.items() if k in SCORING_RULE_FIELDS})
for r in rules
])
case.publish_status = 2 # 已发布
case.save(update_fields=['publish_status', 'updated_at'])
audit.info('CMS_CASE_PUBLISH case_id=%s by=%s scoring_rules=%d prompt_version=%s',
case.id, request.user.id, len(rules), gen.get('prompt_version'))
return Response({
'message': '已发布',
'id': case.id,
'publish_status': case.publish_status,
'scoring_rules_generated': len(rules),
})