diff --git a/apps/case/schemas/case_full.json b/apps/case/schemas/case_full.json index 14438ed..874966b 100644 --- a/apps/case/schemas/case_full.json +++ b/apps/case/schemas/case_full.json @@ -16,7 +16,7 @@ }, "difficulty": { "type": "string", - "enum": ["easy", "medium", "hard", ""] + "enum": ["低", "中", "高", ""] }, "chief_complaint": { "type": "string" }, "description": { "type": "string" }, diff --git a/apps/user/cms.py b/apps/user/cms.py index a0e24c4..be353c8 100644 --- a/apps/user/cms.py +++ b/apps/user/cms.py @@ -7,6 +7,7 @@ import re from django.db import IntegrityError +from django.db.models import Prefetch from rest_framework import viewsets, filters, status from rest_framework.decorators import action from rest_framework.parsers import MultiPartParser, FormParser @@ -52,6 +53,12 @@ class CmsUserSerializer(serializers.ModelSerializer): """读取(列表 / 详情)。""" institution_name = serializers.CharField(source='institution.name', read_only=True, default=None) role_label = serializers.SerializerMethodField() + # 师生关系(仅取进行中 status=1): + # - 医生:students = 名下学生 [{id, real_name, phone}],teachers 为 None; + # - 学生:teachers = 带教医生 [{id, real_name, phone}],students 为 None; + # - 其它角色:两者均为 None。 + students = serializers.SerializerMethodField() + teachers = serializers.SerializerMethodField() class Meta: model = User @@ -59,11 +66,33 @@ class CmsUserSerializer(serializers.ModelSerializer): 'id', 'username', 'real_name', 'phone', 'role_type', 'role_label', 'institution', 'institution_name', 'gender', 'title_name', 'major', 'training_stage', 'status', 'created_at', + 'students', 'teachers', ] def get_role_label(self, obj): return ROLE_LABEL_BY_CODE.get(obj.role_type, obj.role_type) + def get_students(self, obj): + if obj.role_type != 'doctor': + return None + # 优先使用 viewset 预取(prefetch)的关系,避免 N+1 + rels = obj.teacher_relations.all() if 'teacher_relations' in getattr(obj, '_prefetched_objects_cache', {}) \ + else TeacherStudentRelation.objects.filter(teacher=obj, status=1).select_related('student') + return [ + {'id': r.student_id, 'real_name': r.student.real_name, 'phone': r.student.phone} + for r in rels if r.status == 1 + ] + + def get_teachers(self, obj): + if obj.role_type != 'student': + return None + rels = obj.student_relations.all() if 'student_relations' in getattr(obj, '_prefetched_objects_cache', {}) \ + else TeacherStudentRelation.objects.filter(student=obj, status=1).select_related('teacher') + return [ + {'id': r.teacher_id, 'real_name': r.teacher.real_name, 'phone': r.teacher.phone} + for r in rels if r.status == 1 + ] + class CmsUserWriteSerializer(serializers.ModelSerializer): """新增 / 编辑(role 必填)。""" @@ -169,7 +198,17 @@ class CmsUserViewSet(viewsets.ModelViewSet): http_method_names = ['get', 'post', 'head', 'options'] def get_queryset(self): - qs = User.objects.select_related('institution', 'department').all().order_by('-created_at') + # 预取师生关系(仅进行中 status=1)+ 对端用户,列表输出师生关系时避免 N+1 + qs = ( + User.objects.select_related('institution', 'department') + .prefetch_related( + Prefetch('teacher_relations', + queryset=TeacherStudentRelation.objects.filter(status=1).select_related('student')), + Prefetch('student_relations', + queryset=TeacherStudentRelation.objects.filter(status=1).select_related('teacher')), + ) + .all().order_by('-created_at') + ) user = self.request.user if is_super(user): return qs diff --git a/prompts/case_teaching_full.md b/prompts/case_teaching_full.md index da60246..b3e89dc 100644 --- a/prompts/case_teaching_full.md +++ b/prompts/case_teaching_full.md @@ -14,7 +14,7 @@ { "title": "病例标题", "case_type": "teaching", - "difficulty": "easy|medium|hard", + "difficulty": "低|中|高", "chief_complaint": "主诉", "description": "病例简介/摘要", "patient_age": 45, @@ -42,7 +42,7 @@ # 规则 1. `case_type` 固定为 `"teaching"`。 -2. `difficulty` 根据病例复杂度判断:简单常见病 → easy,需鉴别诊断 → medium,多系统/罕见病 → hard。 +2. `difficulty` 根据病例复杂度判断:简单常见病 → 低,需鉴别诊断 → 中,多系统/罕见病 → 高。 3. `patient_age` 为整数,无法判断填 `null`。 4. `patient_gender` 仅 `"male"` 或 `"female"`,无法判断填空字符串。 5. 标签类字段(symptom_tags、disease_tags 等)至少各提取 1 个,从原文推断。 diff --git a/prompts/case_traditional_full.md b/prompts/case_traditional_full.md index cc95c38..dadcac4 100644 --- a/prompts/case_traditional_full.md +++ b/prompts/case_traditional_full.md @@ -14,7 +14,7 @@ { "title": "病例标题", "case_type": "traditional", - "difficulty": "easy|medium|hard", + "difficulty": "低|中|高", "chief_complaint": "主诉", "description": "病例简介/摘要", "patient_age": 45, @@ -54,7 +54,7 @@ # 规则 1. `case_type` 固定为 `"traditional"`。 -2. `difficulty` 根据病例复杂度判断:简单常见病 → easy,需鉴别诊断 → medium,多系统/罕见病 → hard。 +2. `difficulty` 根据病例复杂度判断:简单常见病 → 低,需鉴别诊断 → 中,多系统/罕见病 → 高。 3. `patient_age` 为整数,无法判断填 `null`。 4. `patient_gender` 仅 `"male"` 或 `"female"`,无法判断填空字符串。 5. 标签类字段(symptom_tags、disease_tags 等)至少各提取 1 个,从原文推断。