from django.db import models from apps.common.models import BaseModel from apps.user.models import User from apps.case.models import CaseBase # ─── 只读镜像(fastapi 属主)──────────────────────────────────────────────────── # 训练相关表(training_record / training_session / training_submission / # user_learning_profiles 等)的 schema 属主是 fastapi 服务。Django 侧一律 managed=False、 # 只读接入,仅供 CMS 查询训练记录/统计,不写。 # ⚠️ 下方字段为当前最佳镜像,正式接入前应以 `python manage.py inspectdb` 对真实库反向校准 # (真实表清单见《项目架构设计.md》第二节;注意真实库中没有 training_score_detail)。 class TrainingRecord(BaseModel): """训练记录表(只读,fastapi 属主,managed=False)""" TRAINING_MODE_CHOICES = [ ('novice', '新手'), ('practice', '练习'), ('exam', '考试'), ] EVALUATION_LEVEL_CHOICES = [ ('excellent', '优秀'), ('good', '良好'), ('average', '一般'), ('poor', '较差'), ] STATUS_CHOICES = [ ('in_progress', '进行中'), ('completed', '已完成'), ('aborted', '已中断'), ] id = models.BigAutoField(primary_key=True) user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='training_records', verbose_name='用户' ) case = models.ForeignKey( CaseBase, on_delete=models.CASCADE, related_name='training_records', verbose_name='病例' ) training_mode = models.CharField('训练模式', max_length=50, choices=TRAINING_MODE_CHOICES) case_type = models.CharField('病例类型', max_length=30, blank=True) teacher = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='supervised_records', verbose_name='带教老师' ) start_time = models.DateTimeField('开始时间', auto_now_add=True) end_time = models.DateTimeField('结束时间', null=True, blank=True) duration_seconds = models.IntegerField('训练时长', null=True, blank=True) total_score = models.DecimalField('总分', max_digits=5, decimal_places=2, null=True, blank=True) ai_score = models.DecimalField('AI评分', max_digits=5, decimal_places=2, null=True, blank=True) teacher_score = models.DecimalField('教师评分', max_digits=5, decimal_places=2, null=True, blank=True) evaluation_level = models.CharField('评价等级', max_length=20, choices=EVALUATION_LEVEL_CHOICES, blank=True) status = models.CharField('状态', max_length=30, choices=STATUS_CHOICES, default='in_progress') feedback = models.TextField('总评', blank=True) thinking_chain = models.TextField('临床推理链', blank=True) diagnosis_path = models.TextField('诊断路径', blank=True) wrong_points = models.JSONField('错误知识点', default=list, blank=True) missed_questions = models.JSONField('漏问项', default=list, blank=True) recommendation_result = models.JSONField('AI推荐', default=dict, blank=True) ai_feedback_structured = models.JSONField('AI结构化反馈', default=dict, blank=True) osce_station_score = models.JSONField('OSCE各站点成绩', default=dict, blank=True) interruption_count = models.IntegerField('中断次数', default=0) emotion_analysis = models.JSONField('情绪分析', default=dict, blank=True) prompt_version = models.CharField('Prompt版本', max_length=50, blank=True) rag_context_version = models.CharField('知识上下文版本', max_length=50, blank=True) class Meta: managed = False db_table = 'training_record' verbose_name = '训练记录' verbose_name_plural = '训练记录' def __str__(self): return f"{self.user.username} - {self.case.title}" class TrainingScoreDetail(BaseModel): """评分明细表(只读占位,managed=False;真实 fastapi 库无此表,接入时以真实 schema 为准)""" id = models.BigAutoField(primary_key=True) record = models.ForeignKey( TrainingRecord, on_delete=models.CASCADE, related_name='score_details', verbose_name='训练记录' ) rule = models.ForeignKey( 'case.ScoringRule', on_delete=models.CASCADE, null=True, blank=True, verbose_name='评分规则' ) dimension = models.CharField('评分维度', max_length=50) score = models.DecimalField('分数', max_digits=5, decimal_places=2) deducted_reason = models.TextField('扣分原因', blank=True) evidence_message_ids = models.JSONField('对应对话证据', default=list, blank=True) ai_confidence = models.DecimalField('AI评分置信度', max_digits=5, decimal_places=2, null=True, blank=True) comment = models.TextField('评语', blank=True) class Meta: managed = False db_table = 'training_score_detail' verbose_name = '评分明细' verbose_name_plural = '评分明细' def __str__(self): return f"{self.record} - {self.dimension}: {self.score}"