models.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. from django.db import models
  2. from django.utils.translation import gettext_lazy as _
  3. from django.utils import timezone
  4. from django.contrib.auth.models import User
  5. class Users(models.Model):
  6. """核心用户模型 (继承现有结构)"""
  7. class UserRole(models.TextChoices):
  8. ADMIN = 'ADM', _('管理员')
  9. USER = 'USR', _('普通用户')
  10. DEVELOPER = 'DEV', _('开发者')
  11. user_name = models.OneToOneField(
  12. User,
  13. on_delete=models.CASCADE,
  14. primary_key=False,
  15. related_name='user_profile',
  16. verbose_name='关联用户'
  17. )
  18. user_id = models.AutoField(primary_key=True, verbose_name="用户ID")
  19. name = models.CharField(max_length=80, verbose_name='姓名')
  20. openid = models.CharField(max_length=100, unique=True, verbose_name='OPENID')
  21. appid = models.CharField(max_length=100, verbose_name='APPID')
  22. roles = models.CharField(
  23. max_length=10,
  24. choices=UserRole.choices,
  25. default=UserRole.USER,
  26. verbose_name='角色'
  27. )
  28. # 权限系统
  29. vip = models.PositiveIntegerField(default=1, verbose_name='VIP等级')
  30. vip_time = models.DateTimeField(auto_now_add=True, verbose_name='VIP生效时间')
  31. # 账户状态
  32. is_delete = models.BooleanField(default=False, verbose_name='删除标记')
  33. developer = models.BooleanField(default=False, verbose_name='开发者标记')
  34. # 安全信息
  35. t_code = models.CharField(max_length=100, unique=True, verbose_name='交易验证码')
  36. ip = models.GenericIPAddressField(verbose_name='注册IP')
  37. # 基本信息
  38. avatar = models.ImageField(upload_to='avatars/', blank=True, null=True,default='/static/img/user.jpg', verbose_name='头像')
  39. email = models.EmailField(unique=False, null=True, blank=True, default='email@email.com', verbose_name='邮箱')
  40. phone = models.CharField(max_length=20, null=True, blank=True, verbose_name='手机号')
  41. address = models.CharField(max_length=200, null=True, blank=True, verbose_name='地址')
  42. # 注册信息
  43. create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  44. update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
  45. class Meta:
  46. db_table = 'user_profile'
  47. verbose_name = '用户档案'
  48. verbose_name_plural = "用户档案"
  49. ordering = ['-create_time']
  50. def __str__(self):
  51. return f"{self.name}(ID:{self.user_id})"
  52. # 如果变更名称,同步修改User表中的username
  53. def save(self, *args, **kwargs):
  54. if self.name != self.user_name.username:
  55. self.user_name.username = self.name
  56. self.user_name.save()
  57. super().save(*args, **kwargs)
  58. def is_admin(self):
  59. return self.roles == self.UserRole.ADMIN
  60. class AcademicProfile(models.Model):
  61. """学术信息扩展模型 (与用户一对一关联)"""
  62. class AcademicRole(models.TextChoices):
  63. UNDERGRAD = 'UG', _('本科生')
  64. MASTER = 'MS', _('硕士生')
  65. PHD = 'PhD', _('博士生')
  66. POSTDOC = 'PD', _('博士后')
  67. FACULTY = 'FAC', _('教职工')
  68. RESEARCHER = 'RES', _('研究员')
  69. NOROLE = 'NOR', _('未指定')
  70. user = models.OneToOneField(
  71. Users,
  72. on_delete=models.CASCADE,
  73. primary_key=True,
  74. related_name='user_academic_profile',
  75. verbose_name='关联用户'
  76. )
  77. # 学术身份
  78. role = models.CharField(
  79. max_length=10,
  80. choices=AcademicRole.choices,
  81. default=AcademicRole.NOROLE,
  82. verbose_name='学术身份'
  83. )
  84. # 年级信息
  85. enrollment_year = models.PositiveIntegerField(
  86. null=True,
  87. blank=True,
  88. verbose_name='入学年份',
  89. help_text='格式:YYYY(如2023)'
  90. )
  91. graduation_year = models.PositiveIntegerField(
  92. null=True,
  93. blank=True,
  94. verbose_name='预计毕业年份'
  95. )
  96. # 学术单位
  97. department = models.CharField(max_length=100, verbose_name='院系/研究所',default='UESTC')
  98. major = models.CharField(max_length=100, verbose_name='专业方向',default='机械')
  99. # 研究标签
  100. research_tags = models.CharField(
  101. max_length=255,
  102. default='',
  103. null=True,
  104. blank=True,
  105. verbose_name='研究方向',
  106. help_text='用逗号分隔多个方向(如:人工智能,教育技术)'
  107. )
  108. skill_tags = models.CharField(
  109. max_length=255,
  110. default='',
  111. null=True,
  112. blank=True,
  113. verbose_name='技能标签',
  114. help_text='用逗号分隔多个技能(如:Python,数据分析)'
  115. )
  116. class Meta:
  117. db_table = 'user_academic_profile'
  118. verbose_name = '学术档案'
  119. verbose_name_plural = "学术档案"
  120. def __str__(self):
  121. return f"{self.user.name}的学术档案"
  122. class ResearchGroup(models.Model):
  123. """教研小组模型"""
  124. group_id = models.AutoField(primary_key=True, verbose_name="小组ID")
  125. name = models.CharField(max_length=100, unique=True, verbose_name="小组名称")
  126. description = models.TextField(blank=True, verbose_name="小组描述")
  127. created_by = models.ForeignKey(
  128. Users,
  129. on_delete=models.SET_NULL,
  130. null=True,
  131. related_name='created_groups',
  132. verbose_name="创建人"
  133. )
  134. created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
  135. is_active = models.BooleanField(default=True, verbose_name="是否活跃")
  136. class Meta:
  137. db_table = 'user_research_group'
  138. verbose_name = '教研小组'
  139. verbose_name_plural = "教研小组"
  140. ordering = ['-created_at']
  141. def __str__(self):
  142. return f"{self.name}(ID:{self.group_id})"
  143. class GroupMembership(models.Model):
  144. """小组成员关联模型"""
  145. class MemberRole(models.TextChoices):
  146. LEADER = 'LEAD', _('组长')
  147. DEPUTY = 'DEP', _('副组长')
  148. CORE = 'CORE', _('核心成员')
  149. MEMBER = 'MEM', _('普通成员')
  150. ADVISOR = 'ADV', _('指导老师')
  151. OBSERVER = 'OBS', _('观察员')
  152. class MemberStatus(models.TextChoices):
  153. ACTIVE = 'ACT', _('活跃')
  154. LEAVE = 'LV', _('请假')
  155. INACTIVE = 'INAC', _('不活跃')
  156. GRADUATED = 'GRAD', _('已毕业')
  157. TRANSFERRED = 'TRF', _('已转组')
  158. user = models.ForeignKey(
  159. Users,
  160. on_delete=models.CASCADE,
  161. related_name='group_memberships',
  162. verbose_name="成员"
  163. )
  164. group = models.ForeignKey(
  165. ResearchGroup,
  166. on_delete=models.CASCADE,
  167. related_name='members',
  168. verbose_name="所属小组"
  169. )
  170. role = models.CharField(
  171. max_length=20,
  172. choices=MemberRole.choices,
  173. default=MemberRole.MEMBER,
  174. verbose_name="小组角色"
  175. )
  176. status = models.CharField(
  177. max_length=10,
  178. choices=MemberStatus.choices,
  179. default=MemberStatus.ACTIVE,
  180. verbose_name="在组状态"
  181. )
  182. joined_at = models.DateTimeField(auto_now_add=True, verbose_name="加入时间")
  183. left_at = models.DateTimeField(null=True, blank=True, verbose_name="离开时间")
  184. custom_permissions = models.JSONField(
  185. null=True,
  186. blank=True,
  187. default=dict,
  188. verbose_name="特殊权限",
  189. help_text="JSON格式存储额外权限"
  190. )
  191. class Meta:
  192. db_table = 'user_group_membership'
  193. verbose_name = '小组成员'
  194. verbose_name_plural = "小组成员"
  195. unique_together = ('user', 'group') # 确保同一用户不会重复加入同一小组
  196. ordering = ['-joined_at']
  197. def save(self, *args, **kwargs):
  198. """自动更新离开时间"""
  199. if self.status in [self.MemberStatus.GRADUATED, self.MemberStatus.TRANSFERRED] and not self.left_at:
  200. self.left_at = timezone.now()
  201. super().save(*args, **kwargs)
  202. def __str__(self):
  203. return f"{self.user.name}在{self.group.name}({self.get_role_display()})"