diff --git a/a_long_dianjing/settings.py b/a_long_dianjing/settings.py index 8eda5ef..da67050 100644 --- a/a_long_dianjing/settings.py +++ b/a_long_dianjing/settings.py @@ -119,8 +119,8 @@ SIMPLE_JWT = { 'LEEWAY': 0, 'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', - 'USER_ID_FIELD': 'YonghuID', - 'USER_ID_CLAIM': 'YonghuID', + 'USER_ID_FIELD': 'yonghuid', + 'USER_ID_CLAIM': 'yonghuid', 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'TOKEN_TYPE_CLAIM': 'token_type', @@ -132,10 +132,7 @@ AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', ] -AUTH_USER_MODEL = 'gvsdsdk.User' - -# gvsdsdk 去远程化:所有 gvsdsdk 表存储在本地数据库 -GVSDSDK_MANAGE_TABLES = True +AUTH_USER_MODEL = 'users.UserMain' # ==================== DRF 配置 ==================== REST_FRAMEWORK = { diff --git a/gvsdsdk/models.py b/gvsdsdk/models.py index efd9834..310a117 100644 --- a/gvsdsdk/models.py +++ b/gvsdsdk/models.py @@ -172,16 +172,11 @@ class UserManager(BaseUserManager): # === 用户表 === class User(QModel): - """用户表——系统核心身份模型 - - 去远程化后存储在本地数据库,作为 AUTH_USER_MODEL。 - 在 SAAS 主库字段基础上增加本地业务字段(YonghuID/UserAvatar/UserType)。 - 敏感数据(OpenID/支付信息)存放在 users.UserSensitiveData 表。 - """ + """用户表——系统核心身份模型(映射 SAAS 主库 User 表)""" UserUUID = UUIDField(primary_key=True, db_column='UserUUID', verbose_name='用户UUID') UserName = models.CharField(max_length=256, unique=True, db_column='UserName', verbose_name='用户名') - UserPassword = models.CharField(max_length=200, default='', db_column='UserPassword', verbose_name='密码') + UserPassword = models.CharField(max_length=60, default='', db_column='UserPassword', verbose_name='密码') UserPositionStatus = models.SmallIntegerField(default=0, db_column='UserPositionStatus', verbose_name='职位状态') UserPersonalSignature = models.CharField(max_length=1024, default='', db_column='UserPersonalSignature', verbose_name='个人签名') UserEmail = models.CharField(max_length=256, default='', db_column='UserEmail', verbose_name='邮箱') @@ -194,261 +189,17 @@ class User(QModel): IsStaff = models.BooleanField(default=False, db_column='IsStaff', verbose_name='是否员工') IsSuperuser = models.BooleanField(default=False, db_column='IsSuperuser', verbose_name='是否超级用户') - # === 本地业务字段(去远程化新增) === - YonghuID = models.CharField(max_length=7, unique=True, null=True, blank=True, db_index=True, db_column='YonghuID', verbose_name='用户业务ID') - UserAvatar = models.CharField(max_length=500, null=True, blank=True, default='a_long/morentouxiang.jpg', db_column='UserAvatar', verbose_name='头像') - UserType = models.CharField( - max_length=10, - choices=[ - ('normal', '普通用户'), - ('dashou', '打手'), - ('shop', '商家'), - ('guanshi', '管事'), - ('admin', '管理员'), - ('kefu', '客服'), - ], - default='normal', db_column='UserType', verbose_name='用户类型' - ) - objects = UserManager() - # Django auth 框架要求 - USERNAME_FIELD = 'YonghuID' - REQUIRED_FIELDS = ['UserName'] - class Meta: - managed = True + managed = False app_label = 'gvsdsdk' db_table = 'User' verbose_name = '用户' verbose_name_plural = '用户' def __str__(self): - return self.YonghuID or self.UserName - - # === 兼容属性(snake_case → PascalCase 映射,保持现有代码兼容) === - - @property - def yonghuid(self): - """兼容 UserMain.yonghuid""" - return self.YonghuID - - @yonghuid.setter - def yonghuid(self, value): - self.YonghuID = value - - @property - def avatar(self): - """兼容 UserMain.avatar""" - return self.UserAvatar - - @avatar.setter - def avatar(self, value): - self.UserAvatar = value - - @property - def user_type(self): - """兼容 UserMain.user_type""" - return self.UserType - - @user_type.setter - def user_type(self, value): - self.UserType = value - - @property - def phone(self): - """兼容 UserMain.phone""" - return self.UserTele - - @phone.setter - def phone(self, value): - self.UserTele = value - - @property - def openid(self): - """兼容 UserMain.openid —— 从 UserSensitiveData 延迟加载""" - if hasattr(self, '_openid_cache'): - return self._openid_cache - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - self._openid_cache = sd.OpenID if sd else None - except Exception: - self._openid_cache = None - return self._openid_cache - - @openid.setter - def openid(self, value): - """设置 openid —— 写入 UserSensitiveData""" - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'OpenID': value or ''} - ) - if not created: - sd.OpenID = value or '' - sd.save(update_fields=['OpenID']) - self._openid_cache = value - - @property - def unionid(self): - """兼容 UserMain.unionid""" - if hasattr(self, '_unionid_cache'): - return self._unionid_cache - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - self._unionid_cache = sd.UnionID if sd else None - except Exception: - self._unionid_cache = None - return self._unionid_cache - - @unionid.setter - def unionid(self, value): - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'UnionID': value or ''} - ) - if not created: - sd.UnionID = value or '' - sd.save(update_fields=['UnionID']) - - @property - def password(self): - """兼容 UserMain.password —— 从 UserSensitiveData 加载""" - if hasattr(self, '_password_cache'): - return self._password_cache - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - self._password_cache = sd.Password if sd else '' - except Exception: - self._password_cache = '' - return self._password_cache - - @password.setter - def password(self, value): - """设置 password —— 写入 UserSensitiveData""" - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'Password': value or ''} - ) - if not created: - sd.Password = value or '' - sd.save(update_fields=['Password']) - self._password_cache = value - - @property - def zhifu(self): - """兼容 UserMain.zhifu""" - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - return sd.Zhifu if sd else None - except Exception: - return None - - @zhifu.setter - def zhifu(self, value): - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'Zhifu': value or ''} - ) - if not created: - sd.Zhifu = value or '' - sd.save(update_fields=['Zhifu']) - - @property - def skzhanghao(self): - """兼容 UserMain.skzhanghao""" - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - return sd.SKZhanghao if sd else None - except Exception: - return None - - @skzhanghao.setter - def skzhanghao(self, value): - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'SKZhanghao': value or ''} - ) - if not created: - sd.SKZhanghao = value or '' - sd.save(update_fields=['SKZhanghao']) - - @property - def ip(self): - """兼容 UserMain.ip""" - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - return sd.IP if sd else None - except Exception: - return None - - @ip.setter - def ip(self, value): - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'IP': value or ''} - ) - if not created: - sd.IP = value or '' - sd.save(update_fields=['IP']) - - @property - def shoujihao_renzheng(self): - """兼容 UserMain.shoujihao_renzheng""" - try: - from users.models import UserSensitiveData - sd = UserSensitiveData.objects.filter(UserUUID=self.UserUUID).first() - return sd.PhoneVerified if sd else False - except Exception: - return False - - @shoujihao_renzheng.setter - def shoujihao_renzheng(self, value): - from users.models import UserSensitiveData - sd, created = UserSensitiveData.objects.get_or_create( - UserUUID=self.UserUUID, - defaults={'PhoneVerified': value} - ) - if not created: - sd.PhoneVerified = value - sd.save(update_fields=['PhoneVerified']) - - @property - def last_login_time(self): - """兼容 UserMain.last_login_time""" - return self.UserLastLoginDate - - @last_login_time.setter - def last_login_time(self, value): - self.UserLastLoginDate = value - - @property - def create_time(self): - """兼容 UserMain.create_time""" - return self.UserCreateTime - - @property - def id(self): - """兼容 UserMain.id —— 返回 YonghuID(业务主键)""" - return self.YonghuID - - @property - def pk(self): - """Django 内部使用 —— 返回 UserUUID""" - return self.UserUUID - - # === gvsdsdk 原生方法 === + return self.UserName @property def UUIDStr(self): @@ -503,15 +254,31 @@ class User(QModel): """获取电话,空则返回默认文本""" return self.UserTele or '未填写' + def GetCompanies(self): + """获取用户所属公司列表""" + # Company 模型不在 gvsdsdk 中,此处返回空 QuerySet + # 如需使用,请在子服务器中自行实现 + return [] + def GetRoles(self): - """获取用户角色列表""" + """获取用户角色列表(需在同一 database 连接下使用)""" user_role_uuids = UserRole.objects.filter( UserUUID=self.UserUUID ).values_list('RoleUUID', flat=True) return Role.objects.filter(RoleUUID__in=user_role_uuids) + def GetDepartments(self): + """获取用户部门列表""" + # Department 模型不在 gvsdsdk 中,此处返回空列表 + # 如需使用,请在子服务器中自行实现 + return [] + def HasPermission(self, PermCode): - """检查用户是否拥有指定权限""" + """检查用户是否拥有指定权限(需在同一 database 连接下使用) + + 通过 UserRole → RolePermission → Permission 链路查询, + 汇总所有角色的操作权限后判断。 + """ user_role_uuids = UserRole.objects.filter( UserUUID=self.UserUUID ).values_list('RoleUUID', flat=True) @@ -524,7 +291,7 @@ class User(QModel): PermStatus=1, ).exists() - # Django auth 框架兼容(蛇形命名) + # Django auth 框架兼容(内部使用蛇形命名) @property def is_active(self): return self.IsActive @@ -537,6 +304,14 @@ class User(QModel): def is_authenticated(self): return self.IsAuthenticated + @property + def pk(self): + return self.PK + + @property + def id(self): + return self.ID + def set_password(self, raw_password): self.SetPassword(raw_password) diff --git a/shop/models.py b/shop/models.py index b1df7e1..d3b8572 100644 --- a/shop/models.py +++ b/shop/models.py @@ -148,8 +148,8 @@ class DianpuShouzhiMeiriTongji(QModel): class YonghuDianpuBangding(QModel): id = models.AutoField(primary_key=True, verbose_name='绑定自增ID') yonghu = models.ForeignKey( - 'gvsdsdk.User', - to_field='YonghuID', + 'users.UserMain', # 🔥 完整路径 + to_field='yonghuid', on_delete=models.CASCADE, db_index=True, verbose_name='用户ID' diff --git a/users/models.py b/users/models.py index f406f77..f65d7bb 100644 --- a/users/models.py +++ b/users/models.py @@ -1,51 +1,72 @@ # models.py from django.db import models +from django.contrib.auth.models import AbstractBaseUser from gvsdsdk.model_base import QModel -# ==================== 用户敏感数据表(独立于 gvsdsdk.User) ==================== -class UserSensitiveData(QModel): - """用户敏感数据表——存储 OpenID、支付信息等敏感数据 +# ==================== 用户主表 ==================== +class UserMain(AbstractBaseUser, QModel): + # ID字段 + id = models.AutoField(primary_key=True, verbose_name='主键ID') + yonghuid = models.CharField(max_length=7, unique=True, db_index=True, verbose_name='用户ID') - 与 gvsdsdk.User 一对一关联,不继承 User 表。 - 所有敏感字段仅在此表中存储,实现数据隔离。 - """ - UserUUID = models.OneToOneField( - 'gvsdsdk.User', - on_delete=models.CASCADE, - db_column='UserUUID', - related_name='sensitive_data', - verbose_name='关联用户UUID' + # 微信相关字段 + openid = models.CharField(max_length=64, unique=True, db_index=True, verbose_name='微信OpenID') + unionid = models.CharField(max_length=64, null=True, blank=True, db_index=True, verbose_name='微信UnionID') + + # 基础信息字段 + #tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='商品图片URL') # 用CharField + avatar = models.CharField(max_length=500, null=True, blank=True,default='a_long/morentouxiang.jpg', verbose_name='头像') # 用CharField + phone = models.CharField(max_length=11, blank=True, null=True, verbose_name='手机号') + shoujihao_renzheng = models.BooleanField(default=False, verbose_name='手机号是否已认证') + password = models.CharField(max_length=200, null=True, blank=True, verbose_name='密码') + zhifu = models.CharField(max_length=500, null=True, blank=True, verbose_name='收款码图片') # 用CharField + skzhanghao = models.CharField(max_length=11, blank=True, null=True, verbose_name='收款账号') + ip = models.CharField(max_length=50, blank=True, null=True, verbose_name='来访用户ip') + last_login_time = models.DateTimeField(null=True, blank=True, verbose_name='最后登录时间') + + + # 用户类型字段 + user_type = models.CharField( + max_length=10, + choices=[ + ('normal', '普通用户'), + ('dashou', '打手'), + ('shop', '商家'), + ('guanshi', '管事'), + ('admin', '管理员') + ], + default='normal', + verbose_name='用户类型' ) - # 微信相关 - OpenID = models.CharField(max_length=64, unique=True, null=True, blank=True, db_index=True, verbose_name='微信OpenID') - UnionID = models.CharField(max_length=64, null=True, blank=True, db_index=True, verbose_name='微信UnionID') - - # 认证相关 - Password = models.CharField(max_length=200, null=True, blank=True, verbose_name='本地密码') - PhoneVerified = models.BooleanField(default=False, verbose_name='手机号是否已认证') - - # 支付相关 - Zhifu = models.CharField(max_length=500, null=True, blank=True, verbose_name='收款码图片') - SKZhanghao = models.CharField(max_length=11, blank=True, null=True, verbose_name='收款账号') - - # 安全相关 - IP = models.CharField(max_length=50, blank=True, null=True, verbose_name='来访用户IP') + # 状态和时间字段 + #is_active = models.BooleanField(default=True, verbose_name='是否激活') + #last_login = models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间') create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') + # 邀请码字段 + #yaoqingma = models.CharField(max_length=20, blank=True, null=True, verbose_name='邀请码') + + # 只需要添加这3行 + USERNAME_FIELD = 'openid' # 用openid作为用户名 + REQUIRED_FIELDS = ['yonghuid'] # 必填字段 + class Meta: - db_table = 'user_sensitive_data' - verbose_name = '用户敏感数据' + db_table = 'user_main' + verbose_name = '用户主表' verbose_name_plural = verbose_name indexes = [ - models.Index(fields=['OpenID']), - models.Index(fields=['UnionID']), + models.Index(fields=['yonghuid']), + models.Index(fields=['openid']), + #models.Index(fields=['phone']), + models.Index(fields=['user_type']), + #models.Index(fields=['create_time']), ] def __str__(self): - return f'敏感数据-{self.UserUUID}' + return f'{self.yonghuid}' @@ -69,7 +90,7 @@ class OfficialAccountUser(QModel): # ==================== 老板扩展表 ==================== class UserBoss(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='boss_profile', + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='boss_profile', verbose_name='关联用户') nickname = models.CharField(max_length=50, verbose_name='老板昵称') zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='消费总额') @@ -86,7 +107,7 @@ class UserBoss(QModel): # ==================== 打手扩展表 ==================== class UserDashou(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='dashou_profile', + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='dashou_profile', verbose_name='关联用户') nicheng = models.CharField(max_length=50, null=True, verbose_name='打手昵称') chenghao = models.CharField(max_length=50, null=True,verbose_name='打手称号') @@ -130,7 +151,7 @@ class UserDashou(QModel): # ==================== 商家扩展表 ==================== class UserShangjia(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='shop_profile', + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='shop_profile', verbose_name='关联用户') nicheng = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='商家昵称') zhuangtai = models.IntegerField(choices=[(0, '待审核'), (1, '正常'), (2, '禁用')], default=1, @@ -159,7 +180,7 @@ class UserShangjia(QModel): # ==================== 管事扩展表 ==================== class UserGuanshi(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='guanshi_profile', + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='guanshi_profile', verbose_name='关联用户') yaoqingren = models.CharField(max_length=7,null=True, db_index=True, verbose_name='邀请人ID') yaoqingma = models.CharField(max_length=20,null=True,db_index=True, verbose_name='邀请码') @@ -255,7 +276,7 @@ class UserZuzhang(QModel): class UserShenheguan(QModel): """审核官扩展表(与UserMain一对一关联)""" - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='shenheguan_profile', verbose_name='关联用户', db_index=True) + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='shenheguan_profile', verbose_name='关联用户', db_index=True) zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态') shenhe_zhuangtai = models.IntegerField(choices=[(0, '空闲'), (1, '审核中')], default=0, verbose_name='审核官状态') is_renzheng = models.BooleanField(default=False, verbose_name='是否认证') @@ -275,7 +296,7 @@ class UserShenheguan(QModel): # ==================== 管理员扩展表 ==================== class AdminProfile(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='admin_profile', + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='admin_profile', verbose_name='关联用户') password = models.CharField(max_length=255, verbose_name='密码') wechat = models.CharField(max_length=50, blank=True, verbose_name='管理员微信') @@ -290,7 +311,7 @@ class AdminProfile(QModel): class UserKefu(QModel): - user = models.OneToOneField('gvsdsdk.User', on_delete=models.CASCADE, related_name='kefu_profile', verbose_name='关联用户') + user = models.OneToOneField(UserMain, on_delete=models.CASCADE, related_name='kefu_profile', verbose_name='关联用户') nicheng = models.CharField(max_length=50, verbose_name='客服昵称') erjimima = models.CharField(max_length=128, verbose_name='二级密码') zhuangtai = models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='客服状态') @@ -577,9 +598,3 @@ class RankingRecord(QModel): return f"{self.tongji_date} {zhouqi_str}榜 {shenfen_map.get(self.shenfen)} 第{self.paiming}名" -# ==================== 兼容层 ==================== -# UserMain 已废弃,所有用户数据迁移到 gvsdsdk.User -# 此别名保持现有代码 from users.models import UserMain 可用 -from gvsdsdk.models import User as UserMain - -