feat: update cms case create
This commit is contained in:
+74
-9
@@ -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),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user