models.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. class AcademicProfile(models.Model):
  59. """学术信息扩展模型 (与用户一对一关联)"""
  60. class AcademicRole(models.TextChoices):
  61. UNDERGRAD = 'UG', _('本科生')
  62. MASTER = 'MS', _('硕士生')
  63. PHD = 'PhD', _('博士生')
  64. POSTDOC = 'PD', _('博士后')
  65. FACULTY = 'FAC', _('教职工')
  66. RESEARCHER = 'RES', _('研究员')
  67. user = models.OneToOneField(
  68. Users,
  69. on_delete=models.CASCADE,
  70. primary_key=True,
  71. related_name='user_academic_profile',
  72. verbose_name='关联用户'
  73. )
  74. # 学术身份
  75. role = models.CharField(
  76. max_length=10,
  77. choices=AcademicRole.choices,
  78. default=AcademicRole.MASTER,
  79. verbose_name='学术身份'
  80. )
  81. # 年级信息
  82. enrollment_year = models.PositiveIntegerField(
  83. verbose_name='入学年份',
  84. help_text='格式:YYYY(如2023)'
  85. )
  86. graduation_year = models.PositiveIntegerField(
  87. null=True,
  88. blank=True,
  89. verbose_name='预计毕业年份'
  90. )
  91. # 学术单位
  92. department = models.CharField(max_length=100, verbose_name='院系/研究所')
  93. major = models.CharField(max_length=100, verbose_name='专业方向')
  94. # 研究标签
  95. research_tags = models.CharField(
  96. max_length=255,
  97. blank=True,
  98. verbose_name='研究方向',
  99. help_text='用逗号分隔多个方向(如:人工智能,教育技术)'
  100. )
  101. skill_tags = models.CharField(
  102. max_length=255,
  103. blank=True,
  104. verbose_name='技能标签',
  105. help_text='用逗号分隔多个技能(如:Python,数据分析)'
  106. )
  107. class Meta:
  108. db_table = 'user_academic_profile'
  109. verbose_name = '学术档案'
  110. verbose_name_plural = "学术档案"
  111. def __str__(self):
  112. return f"{self.user.name}的学术档案"
  113. class ResearchGroup(models.Model):
  114. """教研小组模型"""
  115. group_id = models.AutoField(primary_key=True, verbose_name="小组ID")
  116. name = models.CharField(max_length=100, unique=True, verbose_name="小组名称")
  117. description = models.TextField(blank=True, verbose_name="小组描述")
  118. created_by = models.ForeignKey(
  119. Users,
  120. on_delete=models.SET_NULL,
  121. null=True,
  122. related_name='created_groups',
  123. verbose_name="创建人"
  124. )
  125. created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
  126. is_active = models.BooleanField(default=True, verbose_name="是否活跃")
  127. class Meta:
  128. db_table = 'user_research_group'
  129. verbose_name = '教研小组'
  130. verbose_name_plural = "教研小组"
  131. ordering = ['-created_at']
  132. def __str__(self):
  133. return f"{self.name}(ID:{self.group_id})"
  134. class GroupMembership(models.Model):
  135. """小组成员关联模型"""
  136. class MemberRole(models.TextChoices):
  137. LEADER = 'LEAD', _('组长')
  138. DEPUTY = 'DEP', _('副组长')
  139. CORE = 'CORE', _('核心成员')
  140. MEMBER = 'MEM', _('普通成员')
  141. ADVISOR = 'ADV', _('指导老师')
  142. OBSERVER = 'OBS', _('观察员')
  143. class MemberStatus(models.TextChoices):
  144. ACTIVE = 'ACT', _('活跃')
  145. LEAVE = 'LV', _('请假')
  146. INACTIVE = 'INAC', _('不活跃')
  147. GRADUATED = 'GRAD', _('已毕业')
  148. TRANSFERRED = 'TRF', _('已转组')
  149. user = models.ForeignKey(
  150. Users,
  151. on_delete=models.CASCADE,
  152. related_name='group_memberships',
  153. verbose_name="成员"
  154. )
  155. group = models.ForeignKey(
  156. ResearchGroup,
  157. on_delete=models.CASCADE,
  158. related_name='members',
  159. verbose_name="所属小组"
  160. )
  161. role = models.CharField(
  162. max_length=20,
  163. choices=MemberRole.choices,
  164. default=MemberRole.MEMBER,
  165. verbose_name="小组角色"
  166. )
  167. status = models.CharField(
  168. max_length=10,
  169. choices=MemberStatus.choices,
  170. default=MemberStatus.ACTIVE,
  171. verbose_name="在组状态"
  172. )
  173. joined_at = models.DateTimeField(auto_now_add=True, verbose_name="加入时间")
  174. left_at = models.DateTimeField(null=True, blank=True, verbose_name="离开时间")
  175. custom_permissions = models.JSONField(
  176. null=True,
  177. blank=True,
  178. default=dict,
  179. verbose_name="特殊权限",
  180. help_text="JSON格式存储额外权限"
  181. )
  182. class Meta:
  183. db_table = 'user_group_membership'
  184. verbose_name = '小组成员'
  185. verbose_name_plural = "小组成员"
  186. unique_together = ('user', 'group') # 确保同一用户不会重复加入同一小组
  187. ordering = ['-joined_at']
  188. def save(self, *args, **kwargs):
  189. """自动更新离开时间"""
  190. if self.status in [self.MemberStatus.GRADUATED, self.MemberStatus.TRANSFERRED] and not self.left_at:
  191. self.left_at = timezone.now()
  192. super().save(*args, **kwargs)
  193. def __str__(self):
  194. return f"{self.user.name}在{self.group.name}({self.get_role_display()})"