187 lines
7.3 KiB
Python
187 lines
7.3 KiB
Python
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from django.contrib.auth.base_user import BaseUserManager
|
|
|
|
from apps.common.models import BaseModel, SoftDeleteModel
|
|
|
|
|
|
class UserManager(BaseUserManager):
|
|
def get_queryset(self):
|
|
# 默认管理器只返回未删除(未停用)用户;停用用户无法登录/被列出
|
|
return super().get_queryset().filter(is_deleted=False)
|
|
|
|
def create_user(self, username, password=None, **extra_fields):
|
|
if not username:
|
|
raise ValueError('用户名不能为空')
|
|
user = self.model(username=username, **extra_fields)
|
|
user.set_password(password)
|
|
user.save(using=self._db)
|
|
return user
|
|
|
|
def create_superuser(self, username, password=None, **extra_fields):
|
|
extra_fields.setdefault('is_staff', True)
|
|
extra_fields.setdefault('is_superuser', True)
|
|
extra_fields.setdefault('status', 1)
|
|
return self.create_user(username, password, **extra_fields)
|
|
|
|
|
|
class User(AbstractBaseUser, PermissionsMixin, BaseModel):
|
|
"""用户表"""
|
|
GENDER_CHOICES = [
|
|
(0, '未知'),
|
|
(1, '男'),
|
|
(2, '女'),
|
|
]
|
|
STATUS_CHOICES = [
|
|
(0, '禁用'),
|
|
(1, '正常'),
|
|
]
|
|
|
|
id = models.BigAutoField(primary_key=True)
|
|
username = models.CharField('用户名', max_length=50, unique=True)
|
|
password = models.CharField('密码', max_length=255)
|
|
real_name = models.CharField('真实姓名', max_length=50, blank=True)
|
|
phone = models.CharField('手机号', max_length=20, unique=True, blank=True)
|
|
avatar = models.CharField('头像', max_length=255, blank=True)
|
|
gender = models.SmallIntegerField('性别', choices=GENDER_CHOICES, default=0)
|
|
role_type = models.CharField('主角色', max_length=30, blank=True)
|
|
institution = models.ForeignKey(
|
|
'user.Institution', on_delete=models.SET_NULL,
|
|
null=True, blank=True, verbose_name='所属机构'
|
|
)
|
|
department = models.ForeignKey(
|
|
'user.Department', on_delete=models.SET_NULL,
|
|
null=True, blank=True, verbose_name='所属科室'
|
|
)
|
|
title_name = models.CharField('职称', max_length=50, blank=True)
|
|
practice_years = models.CharField('执业年限', max_length=20, blank=True)
|
|
major = models.CharField('专业', max_length=100, blank=True)
|
|
training_stage = models.CharField('培训阶段', max_length=50, blank=True)
|
|
learning_target = models.CharField('学习目标', max_length=255, blank=True)
|
|
competency_profile = models.JSONField('能力画像', default=dict, blank=True)
|
|
weak_dimensions = models.JSONField('薄弱项', default=list, blank=True)
|
|
strong_dimensions = models.JSONField('优势项', default=list, blank=True)
|
|
ai_preference = models.JSONField('AI训练偏好', default=dict, blank=True)
|
|
total_training_count = models.IntegerField('总训练次数', default=0)
|
|
total_case_count = models.IntegerField('完成病例数', default=0)
|
|
current_level = models.CharField('当前能力等级', max_length=30, blank=True)
|
|
status = models.SmallIntegerField('状态', choices=STATUS_CHOICES, default=1)
|
|
last_login_time = models.DateTimeField('最后登录', null=True, blank=True)
|
|
|
|
# Django required fields
|
|
is_staff = models.BooleanField('staff status', default=False)
|
|
is_active = models.BooleanField('active', default=True)
|
|
date_joined = models.DateTimeField('date joined', default=timezone.now)
|
|
|
|
# 软删除(停用 = 逻辑删除)
|
|
is_deleted = models.BooleanField('是否删除', default=False, db_index=True)
|
|
deleted_at = models.DateTimeField('删除时间', null=True, blank=True)
|
|
|
|
objects = UserManager() # 默认:仅未删除
|
|
all_objects = BaseUserManager() # 含已删除(管理/恢复用)
|
|
|
|
USERNAME_FIELD = 'username'
|
|
REQUIRED_FIELDS = []
|
|
|
|
class Meta:
|
|
db_table = 'user'
|
|
verbose_name = '用户'
|
|
verbose_name_plural = '用户'
|
|
|
|
def __str__(self):
|
|
return self.username
|
|
|
|
def delete(self, using=None, keep_parents=False):
|
|
"""停用 = 逻辑删除(不物理删除,避免级联丢数据)。"""
|
|
self.is_deleted = True
|
|
self.deleted_at = timezone.now()
|
|
self.save(using=using, update_fields=['is_deleted', 'deleted_at', 'updated_at'])
|
|
|
|
def hard_delete(self, using=None, keep_parents=False):
|
|
super().delete(using=using, keep_parents=keep_parents)
|
|
|
|
|
|
class Role(BaseModel):
|
|
"""角色表"""
|
|
id = models.BigAutoField(primary_key=True)
|
|
role_code = models.CharField('角色编码', max_length=50, unique=True)
|
|
role_name = models.CharField('角色名称', max_length=50)
|
|
|
|
class Meta:
|
|
db_table = 'role'
|
|
verbose_name = '角色'
|
|
verbose_name_plural = '角色'
|
|
|
|
def __str__(self):
|
|
return self.role_name
|
|
|
|
|
|
class TeacherStudentRelation(SoftDeleteModel):
|
|
"""师生关系表"""
|
|
STATUS_CHOICES = [
|
|
(0, '已结束'),
|
|
(1, '进行中'),
|
|
]
|
|
|
|
id = models.BigAutoField(primary_key=True)
|
|
teacher = models.ForeignKey(
|
|
User, on_delete=models.CASCADE,
|
|
related_name='teacher_relations', verbose_name='带教老师'
|
|
)
|
|
student = models.ForeignKey(
|
|
User, on_delete=models.CASCADE,
|
|
related_name='student_relations', verbose_name='学员'
|
|
)
|
|
relation_type = models.CharField('关系类型', max_length=30, blank=True)
|
|
start_time = models.DateTimeField('开始时间', null=True, blank=True)
|
|
end_time = models.DateTimeField('结束时间', null=True, blank=True)
|
|
status = models.SmallIntegerField('状态', choices=STATUS_CHOICES, default=1)
|
|
|
|
class Meta:
|
|
db_table = 'teacher_student_relation'
|
|
verbose_name = '师生关系'
|
|
verbose_name_plural = '师生关系'
|
|
|
|
def __str__(self):
|
|
return f"{self.teacher.real_name or self.teacher.username} -> {self.student.real_name or self.student.username}"
|
|
|
|
|
|
class Institution(SoftDeleteModel):
|
|
"""医疗机构表"""
|
|
|
|
id = models.BigAutoField(primary_key=True)
|
|
code = models.CharField('机构编码', max_length=100, unique=True)
|
|
name = models.CharField('名称', max_length=255)
|
|
type = models.CharField('类型', max_length=30, default='hospital', blank=True)
|
|
level = models.CharField('等级', max_length=30, blank=True)
|
|
province = models.CharField('省份', max_length=50, blank=True)
|
|
city = models.CharField('城市', max_length=50, blank=True)
|
|
banner_url = models.CharField(
|
|
'机构Banner图', max_length=500, blank=True,
|
|
help_text='机构专属图片:可为静态相对路径(如 institutions/xxx.png)或完整 http(s) URL'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'institution'
|
|
verbose_name = '机构'
|
|
verbose_name_plural = '机构'
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Department(SoftDeleteModel):
|
|
"""科室表(全局分类表,与机构无关;仅超级管理员维护,用于给病例分类)"""
|
|
id = models.BigAutoField(primary_key=True)
|
|
name = models.CharField('科室名称', max_length=100)
|
|
category = models.CharField('科室分类', max_length=50, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'department'
|
|
verbose_name = '科室'
|
|
verbose_name_plural = '科室'
|
|
|
|
def __str__(self):
|
|
return self.name
|