进行了 GVSDSDK 迁移,可能存在诸多问题
This commit is contained in:
@@ -92,27 +92,34 @@ class RoleManager:
|
||||
return User.objects.filter(UserUUID__in=user_uuids)
|
||||
|
||||
@staticmethod
|
||||
def AssignPermission(RoleUUID, PermUUID):
|
||||
"""为角色分配权限"""
|
||||
def AssignPermission(RoleUUID, PermUUID, CompanyUUID):
|
||||
"""为角色分配权限(指定公司)"""
|
||||
rp = RolePermission(
|
||||
RolePermUUID=uuid.uuid4().bytes,
|
||||
RoleUUID=RoleUUID,
|
||||
PermUUID=PermUUID,
|
||||
CompanyUUID=CompanyUUID,
|
||||
CreateTime=datetime.datetime.utcnow(),
|
||||
)
|
||||
rp.save()
|
||||
return rp
|
||||
|
||||
@staticmethod
|
||||
def RemovePermission(RoleUUID, PermUUID):
|
||||
def RemovePermission(RoleUUID, PermUUID, CompanyUUID=None):
|
||||
"""移除角色的指定权限"""
|
||||
deleted, _ = RolePermission.objects.filter(RoleUUID=RoleUUID, PermUUID=PermUUID).delete()
|
||||
qs = RolePermission.objects.filter(RoleUUID=RoleUUID, PermUUID=PermUUID)
|
||||
if CompanyUUID is not None:
|
||||
qs = qs.filter(CompanyUUID=CompanyUUID)
|
||||
deleted, _ = qs.delete()
|
||||
return deleted > 0
|
||||
|
||||
@staticmethod
|
||||
def GetPermissionsForRole(RoleUUID):
|
||||
"""获取角色的所有权限"""
|
||||
perm_uuids = RolePermission.objects.filter(RoleUUID=RoleUUID).values_list('PermUUID', flat=True)
|
||||
def GetPermissionsForRole(RoleUUID, CompanyUUID=None):
|
||||
"""获取角色的所有权限(可按公司过滤)"""
|
||||
qs = RolePermission.objects.filter(RoleUUID=RoleUUID)
|
||||
if CompanyUUID is not None:
|
||||
qs = qs.filter(CompanyUUID=CompanyUUID)
|
||||
perm_uuids = qs.values_list('PermUUID', flat=True)
|
||||
return Permission.objects.filter(PermUUID__in=perm_uuids, PermStatus=1)
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ def PermissionVerifier(permission, attributes, user=None, request=None):
|
||||
"""权限验证器——混合 ABAC + RBAC 检查
|
||||
|
||||
RBAC 仅检查当前角色(CurrentRoleUUID Cookie 指定的角色)的权限,
|
||||
通过 RolePermission → Permission 链路查询。
|
||||
通过 RolePermission → Permission 链路查询,按当前公司过滤。
|
||||
超级管理员/员工直接放行。
|
||||
"""
|
||||
if not permission:
|
||||
@@ -63,6 +63,7 @@ def PermissionVerifier(permission, attributes, user=None, request=None):
|
||||
return True
|
||||
|
||||
current_role = getattr(request, 'current_role', None) if request else None
|
||||
current_company = getattr(request, 'current_company_uuid', None) if request else None
|
||||
|
||||
for attr in attributes:
|
||||
# ABAC 检查
|
||||
@@ -73,13 +74,16 @@ def PermissionVerifier(permission, attributes, user=None, request=None):
|
||||
if abac_match:
|
||||
continue
|
||||
|
||||
# RBAC 检查:仅检查当前角色
|
||||
# RBAC 检查:仅检查当前角色,按公司过滤权限
|
||||
if current_role and isinstance(current_role, Role):
|
||||
if current_role.RoleStatus != 1:
|
||||
return False
|
||||
perm_uuids = RolePermission.objects.filter(
|
||||
rp_qs = RolePermission.objects.filter(
|
||||
RoleUUID=current_role.RoleUUID
|
||||
).values_list('PermUUID', flat=True)
|
||||
)
|
||||
if current_company:
|
||||
rp_qs = rp_qs.filter(CompanyUUID=current_company)
|
||||
perm_uuids = rp_qs.values_list('PermUUID', flat=True)
|
||||
rbac_result = Permission.objects.filter(
|
||||
PermUUID__in=perm_uuids,
|
||||
PermCode=attr,
|
||||
@@ -94,9 +98,12 @@ def PermissionVerifier(permission, attributes, user=None, request=None):
|
||||
RoleUUID__in=user_role_uuids,
|
||||
RoleStatus=1,
|
||||
).values_list('RoleUUID', flat=True)
|
||||
perm_uuids = RolePermission.objects.filter(
|
||||
rp_qs = RolePermission.objects.filter(
|
||||
RoleUUID__in=active_role_uuids
|
||||
).values_list('PermUUID', flat=True)
|
||||
)
|
||||
if current_company:
|
||||
rp_qs = rp_qs.filter(CompanyUUID=current_company)
|
||||
perm_uuids = rp_qs.values_list('PermUUID', flat=True)
|
||||
rbac_result = Permission.objects.filter(
|
||||
PermUUID__in=perm_uuids,
|
||||
PermCode=attr,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Generated by Django 6.0.5 on 2026-06-13 11:43
|
||||
# Generated by Django 4.2.27 on 2026-06-17 17:07
|
||||
|
||||
import datetime
|
||||
import gvsdsdk.models
|
||||
from django.db import migrations, models
|
||||
import gvsdsdk.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -16,9 +16,9 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='AccountActivation',
|
||||
fields=[
|
||||
('AccountActivationCode', gvsdsdk.models.FixedBinaryField(binary_length=6, db_column='AccountActivationCode', max_length=6, primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('AccountActivationCode', gvsdsdk.models.FixedBinaryField(binary_length=6, db_column='AccountActivationCode', max_length=6, primary_key=True, serialize=False, verbose_name='激活码')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', verbose_name='用户UUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '账户激活',
|
||||
@@ -30,13 +30,13 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='EphemeralToken',
|
||||
fields=[
|
||||
('ELTUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ELTUUID', primary_key=True, serialize=False)),
|
||||
('ELTUserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ELTUserUUID', db_index=True)),
|
||||
('ELTCreateTime', models.DateTimeField(db_column='ELTCreateTime', default=datetime.datetime.utcnow)),
|
||||
('ELTEndTime', models.DateTimeField(db_column='ELTEndTime', default=gvsdsdk.models._elt_end_time_default)),
|
||||
('ELTType', models.SmallIntegerField(db_column='ELTType', default=0)),
|
||||
('ELTAllowIP', models.JSONField(db_column='ELTAllowIP', default=list)),
|
||||
('ELTStatus', models.SmallIntegerField(db_column='ELTStatus', db_index=True, default=1)),
|
||||
('ELTUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ELTUUID', primary_key=True, serialize=False, verbose_name='令牌UUID')),
|
||||
('ELTUserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ELTUserUUID', db_index=True, verbose_name='用户UUID')),
|
||||
('ELTCreateTime', models.DateTimeField(db_column='ELTCreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
('ELTEndTime', models.DateTimeField(db_column='ELTEndTime', default=gvsdsdk.models._EltEndTimeDefault, verbose_name='过期时间')),
|
||||
('ELTType', models.SmallIntegerField(db_column='ELTType', default=0, verbose_name='令牌类型')),
|
||||
('ELTAllowIP', models.JSONField(db_column='ELTAllowIP', default=list, verbose_name='允许IP')),
|
||||
('ELTStatus', models.SmallIntegerField(db_column='ELTStatus', db_index=True, default=1, verbose_name='状态')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '临时令牌',
|
||||
@@ -48,8 +48,8 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='Password',
|
||||
fields=[
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False)),
|
||||
('Password', models.CharField(db_column='Password', max_length=1024)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False, verbose_name='用户UUID')),
|
||||
('Password', models.CharField(db_column='Password', max_length=1024, verbose_name='密码哈希')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '密码',
|
||||
@@ -61,12 +61,14 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='Permission',
|
||||
fields=[
|
||||
('PermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PermUUID', primary_key=True, serialize=False)),
|
||||
('PermName', models.CharField(db_column='PermName', max_length=128)),
|
||||
('PermCode', models.CharField(db_column='PermCode', max_length=128)),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID')),
|
||||
('PermStatus', models.SmallIntegerField(db_column='PermStatus', default=1)),
|
||||
('PermDesc', models.CharField(db_column='PermDesc', default='', max_length=512)),
|
||||
('PermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PermUUID', primary_key=True, serialize=False, verbose_name='权限UUID')),
|
||||
('TenantUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='TenantUUID', verbose_name='租户UUID')),
|
||||
('PermName', models.CharField(db_column='PermName', max_length=128, verbose_name='权限名称')),
|
||||
('PermCode', models.CharField(db_column='PermCode', max_length=128, verbose_name='权限键')),
|
||||
('PermCategory', models.CharField(db_column='PermCategory', default='', max_length=64, verbose_name='权限分类')),
|
||||
('PermStatus', models.SmallIntegerField(db_column='PermStatus', default=1, verbose_name='是否启用')),
|
||||
('PermDesc', models.CharField(db_column='PermDesc', default='', max_length=512, verbose_name='描述')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '权限',
|
||||
@@ -75,16 +77,72 @@ class Migration(migrations.Migration):
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Position',
|
||||
fields=[
|
||||
('PositionUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PositionUUID', primary_key=True, serialize=False, verbose_name='职务UUID')),
|
||||
('TenantUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='TenantUUID', verbose_name='租户UUID')),
|
||||
('PositionName', models.CharField(db_column='PositionName', max_length=128, verbose_name='职务名称')),
|
||||
('PositionCode', models.CharField(db_column='PositionCode', default='', max_length=64, verbose_name='职务编码')),
|
||||
('PositionLevel', models.SmallIntegerField(db_column='PositionLevel', default=0, verbose_name='职务级别')),
|
||||
('PositionStatus', models.SmallIntegerField(db_column='PositionStatus', default=1, verbose_name='状态')),
|
||||
('PositionDesc', models.CharField(db_column='PositionDesc', default='', max_length=512, verbose_name='描述')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
('UpdateTime', models.DateTimeField(db_column='UpdateTime', default=datetime.datetime.utcnow, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '人事职务标签',
|
||||
'verbose_name_plural': '人事职务标签',
|
||||
'db_table': 'Position',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PositionTemplate',
|
||||
fields=[
|
||||
('PositionTemplateUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PositionTemplateUUID', primary_key=True, serialize=False, verbose_name='岗位模板UUID')),
|
||||
('TenantUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='TenantUUID', verbose_name='租户UUID')),
|
||||
('TemplateName', models.CharField(db_column='TemplateName', max_length=128, verbose_name='模板名称')),
|
||||
('TemplateCode', models.CharField(db_column='TemplateCode', default='', max_length=64, verbose_name='模板编码')),
|
||||
('TemplateStatus', models.SmallIntegerField(db_column='TemplateStatus', default=1, verbose_name='模板状态')),
|
||||
('TemplateDesc', models.CharField(db_column='TemplateDesc', default='', max_length=512, verbose_name='模板描述')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
('UpdateTime', models.DateTimeField(db_column='UpdateTime', default=datetime.datetime.utcnow, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '岗位模板',
|
||||
'verbose_name_plural': '岗位模板',
|
||||
'db_table': 'PositionTemplate',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PositionTemplatePermission',
|
||||
fields=[
|
||||
('PTPermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PTPermUUID', primary_key=True, serialize=False, verbose_name='关联UUID')),
|
||||
('PositionTemplateUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PositionTemplateUUID', verbose_name='岗位模板UUID')),
|
||||
('PermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PermUUID', verbose_name='权限UUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '岗位模板权限关联',
|
||||
'verbose_name_plural': '岗位模板权限关联',
|
||||
'db_table': 'PositionTemplatePermission',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Role',
|
||||
fields=[
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID', primary_key=True, serialize=False)),
|
||||
('CompanyUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='CompanyUUID')),
|
||||
('DeptUUID', gvsdsdk.models.UUIDField(binary_length=16, blank=True, db_column='DeptUUID', null=True)),
|
||||
('RoleName', models.CharField(db_column='RoleName', max_length=128)),
|
||||
('RoleType', models.SmallIntegerField(db_column='RoleType', default=2)),
|
||||
('RoleStatus', models.SmallIntegerField(db_column='RoleStatus', default=1)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID', primary_key=True, serialize=False, verbose_name='角色UUID')),
|
||||
('TenantUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='TenantUUID', verbose_name='租户UUID')),
|
||||
('RoleName', models.CharField(db_column='RoleName', max_length=128, verbose_name='角色名称')),
|
||||
('RoleType', models.SmallIntegerField(db_column='RoleType', default=2, verbose_name='角色类型')),
|
||||
('RoleStatus', models.SmallIntegerField(db_column='RoleStatus', default=1, verbose_name='角色状态')),
|
||||
('AssignScope', models.SmallIntegerField(db_column='AssignScope', default=0, verbose_name='分配可见范围')),
|
||||
('AssignScopeUUID', gvsdsdk.models.UUIDField(binary_length=16, blank=True, db_column='AssignScopeUUID', null=True, verbose_name='分配范围UUID')),
|
||||
('RoleDesc', models.CharField(db_column='RoleDesc', default='', max_length=512, verbose_name='角色描述')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色',
|
||||
@@ -94,55 +152,69 @@ class Migration(migrations.Migration):
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RoleDept',
|
||||
name='RolePermission',
|
||||
fields=[
|
||||
('RoleDeptUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleDeptUUID', primary_key=True, serialize=False)),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID')),
|
||||
('DeptUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='DeptUUID')),
|
||||
('IsMainDept', models.SmallIntegerField(db_column='IsMainDept', default=1)),
|
||||
('RolePermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RolePermUUID', primary_key=True, serialize=False, verbose_name='关联UUID')),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID', verbose_name='角色UUID')),
|
||||
('PermUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PermUUID', verbose_name='权限UUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色部门关联',
|
||||
'verbose_name_plural': '角色部门关联',
|
||||
'db_table': 'RoleDept',
|
||||
'verbose_name': '角色权限关联',
|
||||
'verbose_name_plural': '角色权限关联',
|
||||
'db_table': 'RolePermission',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
name='RoleShop',
|
||||
fields=[
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False)),
|
||||
('UserName', models.CharField(db_column='UserName', max_length=256, unique=True)),
|
||||
('UserPassword', models.CharField(db_column='UserPassword', default='', max_length=60)),
|
||||
('UserPositionStatus', models.SmallIntegerField(db_column='UserPositionStatus', default=0)),
|
||||
('UserPersonalSignature', models.CharField(db_column='UserPersonalSignature', default='', max_length=1024)),
|
||||
('UserEmail', models.CharField(db_column='UserEmail', default='', max_length=256)),
|
||||
('UserEmailVerify', models.SmallIntegerField(db_column='UserEmailVerify', default=0)),
|
||||
('UserAccountLicense', models.SmallIntegerField(db_column='UserAccountLicense', default=1)),
|
||||
('UserGender', models.SmallIntegerField(db_column='UserGender', default=-1)),
|
||||
('UserTele', models.CharField(blank=True, db_column='UserTele', max_length=16, null=True)),
|
||||
('UserCreateTime', models.DateTimeField(db_column='UserCreateTime', default=datetime.datetime.utcnow)),
|
||||
('UserLastLoginDate', models.DateTimeField(db_column='UserLastLoginDate', default=datetime.datetime.utcnow)),
|
||||
('IsStaff', models.BooleanField(db_column='IsStaff', default=False)),
|
||||
('IsSuperuser', models.BooleanField(db_column='IsSuperuser', default=False)),
|
||||
('RoleShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleShopUUID', primary_key=True, serialize=False, verbose_name='关联UUID')),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID', verbose_name='角色UUID')),
|
||||
('ShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ShopUUID', verbose_name='商铺UUID')),
|
||||
('IsMainShop', models.SmallIntegerField(db_column='IsMainShop', default=0, verbose_name='主商铺')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
'db_table': 'User',
|
||||
'verbose_name': '角色商铺关联',
|
||||
'verbose_name_plural': '角色商铺关联',
|
||||
'db_table': 'RoleShop',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Shop',
|
||||
fields=[
|
||||
('ShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ShopUUID', primary_key=True, serialize=False, verbose_name='商铺UUID')),
|
||||
('CompanyUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='CompanyUUID', verbose_name='公司UUID')),
|
||||
('ShopName', models.CharField(db_column='ShopName', max_length=128, verbose_name='商铺名称')),
|
||||
('ShopCode', models.CharField(db_column='ShopCode', default='', max_length=64, verbose_name='商铺编码')),
|
||||
('ShopType', models.SmallIntegerField(db_column='ShopType', default=0, verbose_name='商铺类型')),
|
||||
('ShopStatus', models.SmallIntegerField(db_column='ShopStatus', default=1, verbose_name='商铺状态')),
|
||||
('ShopAddress', models.CharField(db_column='ShopAddress', default='', max_length=512, verbose_name='商铺地址')),
|
||||
('ShopContact', models.CharField(db_column='ShopContact', default='', max_length=128, verbose_name='联系人')),
|
||||
('ShopPhone', models.CharField(db_column='ShopPhone', default='', max_length=32, verbose_name='联系电话')),
|
||||
('ShopDesc', models.CharField(db_column='ShopDesc', default='', max_length=512, verbose_name='描述')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
('UpdateTime', models.DateTimeField(db_column='UpdateTime', default=datetime.datetime.utcnow, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商铺',
|
||||
'verbose_name_plural': '商铺',
|
||||
'db_table': 'Shop',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User2FA',
|
||||
fields=[
|
||||
('User2FAUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='User2FAUUID', primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', db_index=True)),
|
||||
('SecretKey', models.CharField(db_column='SecretKey', max_length=64)),
|
||||
('IsEnabled', models.SmallIntegerField(db_column='IsEnabled', default=0)),
|
||||
('Verified', models.SmallIntegerField(db_column='Verified', default=0)),
|
||||
('LastUsedAt', models.DateTimeField(blank=True, db_column='LastUsedAt', null=True)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('User2FAUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='User2FAUUID', primary_key=True, serialize=False, verbose_name='2FA UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', db_index=True, verbose_name='用户UUID')),
|
||||
('SecretKey', models.CharField(db_column='SecretKey', max_length=64, verbose_name='密钥')),
|
||||
('IsEnabled', models.SmallIntegerField(db_column='IsEnabled', default=0, verbose_name='是否启用')),
|
||||
('Verified', models.SmallIntegerField(db_column='Verified', default=0, verbose_name='已验证')),
|
||||
('LastUsedAt', models.DateTimeField(blank=True, db_column='LastUsedAt', null=True, verbose_name='最后使用')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '双因素认证',
|
||||
@@ -154,9 +226,9 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='UserABAC',
|
||||
fields=[
|
||||
('UserABACUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserABACUUID', primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID')),
|
||||
('HierarchicalPermissionIdentifiers', models.CharField(db_column='HierarchicalPermissionIdentifiers', max_length=256)),
|
||||
('UserABACUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserABACUUID', primary_key=True, serialize=False, verbose_name='ABAC UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', verbose_name='用户UUID')),
|
||||
('HierarchicalPermissionIdentifiers', models.CharField(db_column='HierarchicalPermissionIdentifiers', max_length=256, verbose_name='层级权限标识')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户ABAC',
|
||||
@@ -168,10 +240,10 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='UserAvatar',
|
||||
fields=[
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False)),
|
||||
('UserAvatar', models.BinaryField(db_column='UserAvatar', editable=True)),
|
||||
('AvatarUploadTime', models.DateTimeField(db_column='AvatarUploadTime', default=datetime.datetime.utcnow)),
|
||||
('AvatarFileType', models.CharField(db_column='AvatarFileType', default='png', max_length=16)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False, verbose_name='用户UUID')),
|
||||
('UserAvatar', models.BinaryField(db_column='UserAvatar', editable=True, verbose_name='头像数据')),
|
||||
('AvatarUploadTime', models.DateTimeField(db_column='AvatarUploadTime', default=datetime.datetime.utcnow, verbose_name='上传时间')),
|
||||
('AvatarFileType', models.CharField(db_column='AvatarFileType', default='png', max_length=16, verbose_name='文件类型')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户头像',
|
||||
@@ -180,16 +252,33 @@ class Migration(migrations.Migration):
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserDept',
|
||||
fields=[
|
||||
('UserDeptUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserDeptUUID', primary_key=True, serialize=False, verbose_name='关联UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', verbose_name='用户UUID')),
|
||||
('DeptUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='DeptUUID', verbose_name='部门UUID')),
|
||||
('PositionUUID', gvsdsdk.models.UUIDField(binary_length=16, blank=True, db_column='PositionUUID', null=True, verbose_name='职务标签UUID')),
|
||||
('IsMainDept', models.SmallIntegerField(db_column='IsMainDept', default=1, verbose_name='是否主部门')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '员工部门关联',
|
||||
'verbose_name_plural': '员工部门关联',
|
||||
'db_table': 'UserDept',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserLoginLog',
|
||||
fields=[
|
||||
('LogUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='LogUUID', primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', db_index=True)),
|
||||
('LoginIP', models.CharField(db_column='LoginIP', max_length=39)),
|
||||
('LoginPlatform', models.CharField(db_column='LoginPlatform', max_length=32)),
|
||||
('LoginStatus', models.SmallIntegerField(db_column='LoginStatus')),
|
||||
('LoginTime', models.DateTimeField(db_column='LoginTime', default=datetime.datetime.utcnow)),
|
||||
('UserAgent', models.CharField(db_column='UserAgent', default='', max_length=512)),
|
||||
('LogUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='LogUUID', primary_key=True, serialize=False, verbose_name='日志UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', db_index=True, verbose_name='用户UUID')),
|
||||
('LoginIP', models.CharField(db_column='LoginIP', max_length=39, verbose_name='登录IP')),
|
||||
('LoginPlatform', models.CharField(db_column='LoginPlatform', max_length=32, verbose_name='登录平台')),
|
||||
('LoginStatus', models.SmallIntegerField(db_column='LoginStatus', verbose_name='登录状态')),
|
||||
('LoginTime', models.DateTimeField(db_column='LoginTime', default=datetime.datetime.utcnow, verbose_name='登录时间')),
|
||||
('UserAgent', models.CharField(db_column='UserAgent', default='', max_length=512, verbose_name='用户代理')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '登录日志',
|
||||
@@ -201,10 +290,10 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='UserPending2FA',
|
||||
fields=[
|
||||
('PendingUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PendingUUID', primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', unique=True)),
|
||||
('SecretKey', models.CharField(db_column='SecretKey', max_length=64)),
|
||||
('ExpireAt', models.DateTimeField(db_column='ExpireAt')),
|
||||
('PendingUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PendingUUID', primary_key=True, serialize=False, verbose_name='待确认UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', unique=True, verbose_name='用户UUID')),
|
||||
('SecretKey', models.CharField(db_column='SecretKey', max_length=64, verbose_name='密钥')),
|
||||
('ExpireAt', models.DateTimeField(db_column='ExpireAt', verbose_name='过期时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '2FA待确认',
|
||||
@@ -216,10 +305,11 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='UserRole',
|
||||
fields=[
|
||||
('UserRoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserRoleUUID', primary_key=True, serialize=False)),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID')),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('UserRoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserRoleUUID', primary_key=True, serialize=False, verbose_name='关联UUID')),
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', verbose_name='用户UUID')),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID', verbose_name='角色UUID')),
|
||||
('CompanyUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='CompanyUUID', verbose_name='公司UUID')),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户角色关联',
|
||||
@@ -228,4 +318,37 @@ class Migration(migrations.Migration):
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False, verbose_name='用户UUID')),
|
||||
('UserName', models.CharField(db_column='UserName', max_length=256, unique=True, verbose_name='用户名')),
|
||||
('UserPassword', models.CharField(db_column='UserPassword', default='', max_length=60, verbose_name='密码')),
|
||||
('UserPositionStatus', models.SmallIntegerField(db_column='UserPositionStatus', default=0, verbose_name='职位状态')),
|
||||
('UserPersonalSignature', models.CharField(db_column='UserPersonalSignature', default='', max_length=1024, verbose_name='个人签名')),
|
||||
('UserEmail', models.CharField(db_column='UserEmail', default='', max_length=256, verbose_name='邮箱')),
|
||||
('UserEmailVerify', models.SmallIntegerField(db_column='UserEmailVerify', default=0, verbose_name='邮箱验证')),
|
||||
('UserAccountLicense', models.SmallIntegerField(db_column='UserAccountLicense', default=1, verbose_name='账户许可')),
|
||||
('UserGender', models.SmallIntegerField(db_column='UserGender', default=-1, verbose_name='性别')),
|
||||
('UserTele', models.CharField(blank=True, db_column='UserTele', max_length=16, null=True, verbose_name='电话')),
|
||||
('UserCreateTime', models.DateTimeField(db_column='UserCreateTime', default=datetime.datetime.utcnow, verbose_name='创建时间')),
|
||||
('UserLastLoginDate', models.DateTimeField(db_column='UserLastLoginDate', default=datetime.datetime.utcnow, verbose_name='最后登录')),
|
||||
('UserUID', models.CharField(blank=True, db_column='UserUID', max_length=7, null=True, unique=True, verbose_name='用户业务ID')),
|
||||
('OpenID', models.CharField(blank=True, db_column='OpenID', max_length=64, null=True, unique=True, verbose_name='微信OpenID')),
|
||||
('UnionID', models.CharField(blank=True, db_column='UnionID', max_length=64, null=True, verbose_name='微信UnionID')),
|
||||
('Avatar', models.CharField(db_column='Avatar', default='a_long/morentouxiang.jpg', max_length=500, verbose_name='头像')),
|
||||
('Phone', models.CharField(blank=True, db_column='Phone', max_length=11, null=True, verbose_name='手机号')),
|
||||
('PhoneVerified', models.BooleanField(db_column='PhoneVerified', default=False, verbose_name='手机号是否已认证')),
|
||||
('IP', models.CharField(blank=True, db_column='IP', max_length=50, null=True, verbose_name='来访用户IP')),
|
||||
('IsStaff', models.BooleanField(db_column='IsStaff', default=False, verbose_name='是否员工')),
|
||||
('IsSuperuser', models.BooleanField(db_column='IsSuperuser', default=False, verbose_name='是否超级用户')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
'db_table': 'User',
|
||||
'managed': True,
|
||||
'indexes': [models.Index(fields=['UserUID'], name='User_UserUID_a656ea_idx'), models.Index(fields=['OpenID'], name='User_OpenID_2a920d_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# Generated by Django 6.0.5 on 2026-06-13 12:29
|
||||
|
||||
import datetime
|
||||
import gvsdsdk.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gvsdsdk', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Position',
|
||||
fields=[
|
||||
('PositionUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PositionUUID', primary_key=True, serialize=False)),
|
||||
('CompanyUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='CompanyUUID')),
|
||||
('PositionName', models.CharField(db_column='PositionName', max_length=128)),
|
||||
('PositionCode', models.CharField(db_column='PositionCode', default='', max_length=64)),
|
||||
('PositionLevel', models.SmallIntegerField(db_column='PositionLevel', default=0)),
|
||||
('PositionStatus', models.SmallIntegerField(db_column='PositionStatus', default=1)),
|
||||
('PositionDesc', models.CharField(db_column='PositionDesc', default='', max_length=512)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('UpdateTime', models.DateTimeField(db_column='UpdateTime', default=datetime.datetime.utcnow)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '职务',
|
||||
'verbose_name_plural': '职务',
|
||||
'db_table': 'Position',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RolePosition',
|
||||
fields=[
|
||||
('RolePositionUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RolePositionUUID', primary_key=True, serialize=False)),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID')),
|
||||
('PositionUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='PositionUUID')),
|
||||
('IsMainPosition', models.SmallIntegerField(db_column='IsMainPosition', default=0)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色职务关联',
|
||||
'verbose_name_plural': '角色职务关联',
|
||||
'db_table': 'RolePosition',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RoleShop',
|
||||
fields=[
|
||||
('RoleShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleShopUUID', primary_key=True, serialize=False)),
|
||||
('RoleUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='RoleUUID')),
|
||||
('ShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ShopUUID')),
|
||||
('IsMainShop', models.SmallIntegerField(db_column='IsMainShop', default=0)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '角色商铺关联',
|
||||
'verbose_name_plural': '角色商铺关联',
|
||||
'db_table': 'RoleShop',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Shop',
|
||||
fields=[
|
||||
('ShopUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='ShopUUID', primary_key=True, serialize=False)),
|
||||
('CompanyUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='CompanyUUID')),
|
||||
('ShopName', models.CharField(db_column='ShopName', max_length=128)),
|
||||
('ShopCode', models.CharField(db_column='ShopCode', default='', max_length=64)),
|
||||
('ShopType', models.SmallIntegerField(db_column='ShopType', default=0)),
|
||||
('ShopStatus', models.SmallIntegerField(db_column='ShopStatus', default=1)),
|
||||
('ShopAddress', models.CharField(db_column='ShopAddress', default='', max_length=512)),
|
||||
('ShopContact', models.CharField(db_column='ShopContact', default='', max_length=128)),
|
||||
('ShopPhone', models.CharField(db_column='ShopPhone', default='', max_length=32)),
|
||||
('ShopDesc', models.CharField(db_column='ShopDesc', default='', max_length=512)),
|
||||
('CreateTime', models.DateTimeField(db_column='CreateTime', default=datetime.datetime.utcnow)),
|
||||
('UpdateTime', models.DateTimeField(db_column='UpdateTime', default=datetime.datetime.utcnow)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商铺',
|
||||
'verbose_name_plural': '商铺',
|
||||
'db_table': 'Shop',
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -186,17 +186,33 @@ class User(QModel):
|
||||
UserTele = models.CharField(max_length=16, null=True, blank=True, db_column='UserTele', verbose_name='电话')
|
||||
UserCreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='UserCreateTime', verbose_name='创建时间')
|
||||
UserLastLoginDate = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='UserLastLoginDate', verbose_name='最后登录')
|
||||
# === 本地业务字段(去远程化后新增) ===
|
||||
UserUID = models.CharField(max_length=7, unique=True, db_column='UserUID', verbose_name='用户业务ID', null=True, blank=True)
|
||||
OpenID = models.CharField(max_length=64, unique=True, db_column='OpenID', verbose_name='微信OpenID', null=True, blank=True)
|
||||
UnionID = models.CharField(max_length=64, db_column='UnionID', verbose_name='微信UnionID', null=True, blank=True)
|
||||
Avatar = models.CharField(max_length=500, default='a_long/morentouxiang.jpg', db_column='Avatar', verbose_name='头像')
|
||||
Phone = models.CharField(max_length=11, db_column='Phone', verbose_name='手机号', null=True, blank=True)
|
||||
PhoneVerified = models.BooleanField(default=False, db_column='PhoneVerified', verbose_name='手机号是否已认证')
|
||||
IP = models.CharField(max_length=50, db_column='IP', verbose_name='来访用户IP', null=True, blank=True)
|
||||
IsStaff = models.BooleanField(default=False, db_column='IsStaff', verbose_name='是否员工')
|
||||
IsSuperuser = models.BooleanField(default=False, db_column='IsSuperuser', verbose_name='是否超级用户')
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
# Django auth 框架必需
|
||||
USERNAME_FIELD = 'UserName'
|
||||
REQUIRED_FIELDS = ['UserUID']
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True # 将由 QModelBase.__new__ 根据 GVSDSDK_MANAGE_TABLES 动态设置
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'User'
|
||||
verbose_name = '用户'
|
||||
verbose_name_plural = '用户'
|
||||
indexes = [
|
||||
models.Index(fields=['UserUID']),
|
||||
models.Index(fields=['OpenID']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.UserName
|
||||
@@ -304,6 +320,66 @@ class User(QModel):
|
||||
def is_authenticated(self):
|
||||
return self.IsAuthenticated
|
||||
|
||||
@property
|
||||
def yonghuid(self):
|
||||
"""兼容旧代码的 yonghuid 属性"""
|
||||
return self.UserUID
|
||||
|
||||
@property
|
||||
def openid(self):
|
||||
"""兼容旧代码的 openid 属性"""
|
||||
return self.OpenID
|
||||
|
||||
@property
|
||||
def user_type(self):
|
||||
"""兼容旧代码的 user_type 属性 — 通过 UserRole 推断(带实例缓存)"""
|
||||
# 实例缓存:同一请求内只查一次
|
||||
if hasattr(self, '_cached_user_type'):
|
||||
return self._cached_user_type
|
||||
try:
|
||||
# 单次查询获取所有角色名
|
||||
role_uuids = UserRole.objects.filter(
|
||||
UserUUID=self.UserUUID
|
||||
).values_list('RoleUUID', flat=True)
|
||||
role_names = set(
|
||||
Role.objects.filter(RoleUUID__in=role_uuids)
|
||||
.values_list('RoleName', flat=True)
|
||||
)
|
||||
# 优先级:管理员 > 客服 > 负责人 > 组长 > 审核官 > 商家 > 打手
|
||||
for rn in role_names:
|
||||
if '管理员' in rn or 'admin' in rn.lower():
|
||||
self._cached_user_type = 'admin'
|
||||
return 'admin'
|
||||
for rn in role_names:
|
||||
if '客服' in rn or 'kefu' in rn.lower():
|
||||
self._cached_user_type = 'kefu'
|
||||
return 'kefu'
|
||||
for rn in role_names:
|
||||
if '负责人' in rn or '管事' in rn or 'guanshi' in rn.lower():
|
||||
self._cached_user_type = 'guanshi'
|
||||
return 'guanshi'
|
||||
for rn in role_names:
|
||||
if '组长' in rn or 'zuzhang' in rn.lower():
|
||||
self._cached_user_type = 'zuzhang'
|
||||
return 'zuzhang'
|
||||
for rn in role_names:
|
||||
if '审核' in rn or 'shenheguan' in rn.lower():
|
||||
self._cached_user_type = 'shenheguan'
|
||||
return 'shenheguan'
|
||||
for rn in role_names:
|
||||
if '商家' in rn or 'shangjia' in rn.lower():
|
||||
self._cached_user_type = 'shop'
|
||||
return 'shop'
|
||||
for rn in role_names:
|
||||
if '打手' in rn or 'dashou' in rn.lower():
|
||||
self._cached_user_type = 'dashou'
|
||||
return 'dashou'
|
||||
self._cached_user_type = 'normal'
|
||||
return 'normal'
|
||||
except Exception:
|
||||
self._cached_user_type = 'normal'
|
||||
return 'normal'
|
||||
|
||||
@property
|
||||
def pk(self):
|
||||
return self.PK
|
||||
@@ -318,6 +394,73 @@ class User(QModel):
|
||||
def check_password(self, raw_password):
|
||||
return self.CheckPassword(raw_password)
|
||||
|
||||
@property
|
||||
def dashou_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserDashou
|
||||
return UserDashou.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def shop_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserShangjia
|
||||
return UserShangjia.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def guanshi_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserGuanshi
|
||||
return UserGuanshi.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def boss_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserBoss
|
||||
return UserBoss.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def admin_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import AdminProfile
|
||||
return AdminProfile.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def kefu_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserKefu
|
||||
return UserKefu.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def zuzhang_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserZuzhang
|
||||
return UserZuzhang.objects.filter(user=self).first()
|
||||
|
||||
@property
|
||||
def shenheguan_profile(self):
|
||||
"""兼容旧代码"""
|
||||
from users.models import UserShenheguan
|
||||
return UserShenheguan.objects.filter(user=self).first()
|
||||
|
||||
def has_role(self, role_code):
|
||||
"""检查用户是否拥有指定角色(支持角色名或旧 user_type 值)"""
|
||||
# 角色名映射:旧 user_type → 角色名
|
||||
ROLE_CODE_MAP = {
|
||||
'admin': '管理员', 'kefu': '客服', 'guanshi': '负责人',
|
||||
'zuzhang': '组长', 'shenheguan': '审核官', 'shop': '商家',
|
||||
'dashou': '打手', 'normal': '消费者',
|
||||
}
|
||||
target = ROLE_CODE_MAP.get(role_code, role_code)
|
||||
try:
|
||||
role_uuids = UserRole.objects.filter(
|
||||
UserUUID=self.UserUUID
|
||||
).values_list('RoleUUID', flat=True)
|
||||
return Role.objects.filter(
|
||||
RoleUUID__in=role_uuids, RoleName=target
|
||||
).exists()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
# === 用户头像表 ===
|
||||
|
||||
@@ -330,7 +473,7 @@ class UserAvatar(QModel):
|
||||
AvatarFileType = models.CharField(max_length=16, default='png', db_column='AvatarFileType', verbose_name='文件类型')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserAvatar'
|
||||
verbose_name = '用户头像'
|
||||
@@ -351,7 +494,7 @@ class User2FA(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'User2FA'
|
||||
verbose_name = '双因素认证'
|
||||
@@ -369,7 +512,7 @@ class UserPending2FA(QModel):
|
||||
ExpireAt = models.DateTimeField(db_column='ExpireAt', verbose_name='过期时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserPending2FA'
|
||||
verbose_name = '2FA待确认'
|
||||
@@ -396,7 +539,7 @@ class EphemeralToken(QModel):
|
||||
ELTStatus = models.SmallIntegerField(default=1, db_index=True, db_column='ELTStatus', verbose_name='状态')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'EphemeralToken'
|
||||
verbose_name = '临时令牌'
|
||||
@@ -425,7 +568,7 @@ class UserLoginLog(QModel):
|
||||
UserAgent = models.CharField(max_length=512, default='', db_column='UserAgent', verbose_name='用户代理')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserLoginLog'
|
||||
verbose_name = '登录日志'
|
||||
@@ -442,7 +585,7 @@ class AccountActivation(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'AccountActivation'
|
||||
verbose_name = '账户激活'
|
||||
@@ -472,7 +615,7 @@ class Role(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'Role'
|
||||
verbose_name = '角色'
|
||||
@@ -516,7 +659,7 @@ class Permission(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'Permission'
|
||||
verbose_name = '权限'
|
||||
@@ -555,7 +698,7 @@ class UserRole(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserRole'
|
||||
verbose_name = '用户角色关联'
|
||||
@@ -577,7 +720,7 @@ class RolePermission(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'RolePermission'
|
||||
verbose_name = '角色权限关联'
|
||||
@@ -594,7 +737,7 @@ class UserABAC(QModel):
|
||||
HierarchicalPermissionIdentifiers = models.CharField(max_length=256, db_column='HierarchicalPermissionIdentifiers', verbose_name='层级权限标识')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserABAC'
|
||||
verbose_name = '用户ABAC'
|
||||
@@ -610,7 +753,7 @@ class Password(QModel):
|
||||
Password = models.CharField(max_length=1024, db_column='Password', verbose_name='密码哈希')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'Password'
|
||||
verbose_name = '密码'
|
||||
@@ -639,7 +782,7 @@ class Shop(QModel):
|
||||
UpdateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='UpdateTime', verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'Shop'
|
||||
verbose_name = '商铺'
|
||||
@@ -671,7 +814,7 @@ class RoleShop(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'RoleShop'
|
||||
verbose_name = '角色商铺关联'
|
||||
@@ -700,7 +843,7 @@ class PositionTemplate(QModel):
|
||||
UpdateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='UpdateTime', verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'PositionTemplate'
|
||||
verbose_name = '岗位模板'
|
||||
@@ -750,7 +893,7 @@ class PositionTemplatePermission(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'PositionTemplatePermission'
|
||||
verbose_name = '岗位模板权限关联'
|
||||
@@ -778,7 +921,7 @@ class Position(QModel):
|
||||
UpdateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='UpdateTime', verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'Position'
|
||||
verbose_name = '人事职务标签'
|
||||
@@ -806,7 +949,7 @@ class UserDept(QModel):
|
||||
CreateTime = models.DateTimeField(default=_datetime.datetime.utcnow, db_column='CreateTime', verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
managed = True
|
||||
app_label = 'gvsdsdk'
|
||||
db_table = 'UserDept'
|
||||
verbose_name = '员工部门关联'
|
||||
|
||||
532
gvsdsdk/msync/orm_handler.py
Normal file
532
gvsdsdk/msync/orm_handler.py
Normal file
@@ -0,0 +1,532 @@
|
||||
"""MSYNC ORM 中转处理器
|
||||
|
||||
子服务器端注册 Socket.IO 事件处理器,接收主服务器发来的 ORM 查询请求,
|
||||
在本地数据库执行 SQL 并返回结果。
|
||||
|
||||
用法(子服务器端):
|
||||
from gvsdsdk.msync.orm_handler import ORMRelayHandler
|
||||
|
||||
handler = ORMRelayHandler()
|
||||
handler.Register(sio_client) # 注册到 MSyncSocketClient 实例
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ORMRelayHandler:
|
||||
"""ORM 中转处理器——在子服务器端执行本地 SQL 查询
|
||||
|
||||
通过 MSYNC Socket.IO 事件协议,主服务器将 ORM 请求转发给子服务器,
|
||||
子服务器在本地数据库执行查询后返回结果。
|
||||
"""
|
||||
|
||||
def Register(self, sio_client):
|
||||
"""将 ORM 事件处理器注册到 MSyncSocketClient 实例
|
||||
|
||||
Args:
|
||||
sio_client: MSyncSocketClient 实例,需要有 _sio 属性
|
||||
"""
|
||||
sio = sio_client._sio
|
||||
|
||||
@sio.on('msync:orm:tables', namespace='/msync')
|
||||
def on_orm_tables(data):
|
||||
return self.HandleTables(data)
|
||||
|
||||
@sio.on('msync:orm:columns', namespace='/msync')
|
||||
def on_orm_columns(data):
|
||||
return self.HandleColumns(data)
|
||||
|
||||
@sio.on('msync:orm:list', namespace='/msync')
|
||||
def on_orm_list(data):
|
||||
return self.HandleList(data)
|
||||
|
||||
@sio.on('msync:orm:add', namespace='/msync')
|
||||
def on_orm_add(data):
|
||||
return self.HandleAdd(data)
|
||||
|
||||
@sio.on('msync:orm:modify', namespace='/msync')
|
||||
def on_orm_modify(data):
|
||||
return self.HandleModify(data)
|
||||
|
||||
@sio.on('msync:orm:delete', namespace='/msync')
|
||||
def on_orm_delete(data):
|
||||
return self.HandleDelete(data)
|
||||
|
||||
logger.info('[ORM] ORM 中转处理器已注册')
|
||||
|
||||
def _GetConnection(self):
|
||||
"""获取本地数据库连接(Django default)"""
|
||||
from django.db import connections
|
||||
conn = connections['default']
|
||||
conn.ensure_connection()
|
||||
return conn
|
||||
|
||||
# ─── 事件处理 ──────────────────────────────────────────────
|
||||
|
||||
def HandleTables(self, data):
|
||||
"""列出本地数据库中所有用户表"""
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
tables = self._ListTables(cursor)
|
||||
return {'code': 0, 'msg': 'success', 'data': tables}
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleTables 异常: {e}')
|
||||
return {'code': 1, 'msg': f'查询失败: {e}'}
|
||||
|
||||
def HandleColumns(self, data):
|
||||
"""列出指定表的列信息"""
|
||||
table_name = (data or {}).get('Table', '')
|
||||
if not table_name:
|
||||
return {'code': 1, 'msg': '缺少 Table 参数'}
|
||||
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
columns = self._ListColumns(cursor, table_name)
|
||||
return {'code': 0, 'msg': 'success', 'data': columns}
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleColumns 异常: {e}')
|
||||
return {'code': 1, 'msg': f'查询失败: {e}'}
|
||||
|
||||
def HandleList(self, data):
|
||||
"""分页查询表数据"""
|
||||
table_name = (data or {}).get('Table', '')
|
||||
if not table_name:
|
||||
return {'code': 1, 'msg': '缺少 Table 参数'}
|
||||
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
result = self._QueryTable(cursor, table_name, data or {})
|
||||
if result.get('error'):
|
||||
return {'code': 1, 'msg': result['error'], 'data': None}
|
||||
return {'code': 0, 'msg': 'success', 'data': result}
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleList 异常: {e}')
|
||||
return {'code': 1, 'msg': f'查询失败: {e}'}
|
||||
|
||||
def HandleAdd(self, data):
|
||||
"""新增记录"""
|
||||
table_name = (data or {}).get('Table', '')
|
||||
record = (data or {}).get('Record', {})
|
||||
if not table_name:
|
||||
return {'code': 1, 'msg': '缺少 Table 参数'}
|
||||
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
result = self._AddRecord(cursor, table_name, record)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleAdd 异常: {e}')
|
||||
return {'code': 1, 'msg': f'新增失败: {e}'}
|
||||
|
||||
def HandleModify(self, data):
|
||||
"""修改记录"""
|
||||
table_name = (data or {}).get('Table', '')
|
||||
record = (data or {}).get('Record', {})
|
||||
if not table_name:
|
||||
return {'code': 1, 'msg': '缺少 Table 参数'}
|
||||
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
result = self._ModifyRecord(cursor, table_name, record)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleModify 异常: {e}')
|
||||
return {'code': 1, 'msg': f'修改失败: {e}'}
|
||||
|
||||
def HandleDelete(self, data):
|
||||
"""删除记录"""
|
||||
table_name = (data or {}).get('Table', '')
|
||||
record = (data or {}).get('Record', {})
|
||||
if not table_name:
|
||||
return {'code': 1, 'msg': '缺少 Table 参数'}
|
||||
|
||||
try:
|
||||
conn = self._GetConnection()
|
||||
except Exception as e:
|
||||
return {'code': 1, 'msg': f'数据库连接失败: {e}'}
|
||||
|
||||
try:
|
||||
with conn.cursor() as cursor:
|
||||
result = self._DeleteRecord(cursor, table_name, record)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] HandleDelete 异常: {e}')
|
||||
return {'code': 1, 'msg': f'删除失败: {e}'}
|
||||
|
||||
# ─── SQL 执行 ──────────────────────────────────────────────
|
||||
|
||||
def _ListTables(self, cursor):
|
||||
"""列出所有用户表,含 i18n 标题"""
|
||||
cursor.execute(
|
||||
"SELECT TABLE_NAME, TABLE_ROWS, TABLE_COMMENT FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' "
|
||||
"ORDER BY TABLE_NAME"
|
||||
)
|
||||
|
||||
from django.apps import apps
|
||||
# 构建表名→中文标题映射
|
||||
model_verbose = {}
|
||||
for model in apps.get_models():
|
||||
meta = model._meta
|
||||
# 优先使用 table_title(自定义 Meta 属性),其次 verbose_name
|
||||
table_title = getattr(meta, 'table_title', None)
|
||||
title = str(table_title) if table_title else (str(meta.verbose_name) if meta.verbose_name else '')
|
||||
if title:
|
||||
model_verbose[meta.object_name] = title
|
||||
model_verbose[meta.db_table] = title
|
||||
|
||||
result = []
|
||||
for row in cursor.fetchall():
|
||||
table_name, table_rows, table_comment = row
|
||||
# 优先级:TABLE_COMMENT > table_title/verbose_name > 表名
|
||||
title = table_comment or model_verbose.get(table_name) or table_name
|
||||
result.append({'name': table_name, 'title': title, 'count': table_rows or 0})
|
||||
return result
|
||||
|
||||
def _ListColumns(self, cursor, table_name):
|
||||
"""列出指定表的列信息"""
|
||||
cursor.execute(
|
||||
"SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA, COLUMN_TYPE, COLUMN_COMMENT "
|
||||
"FROM information_schema.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
|
||||
"ORDER BY ORDINAL_POSITION",
|
||||
[table_name]
|
||||
)
|
||||
|
||||
model_verbose = self._FindModelVerboseNames(table_name)
|
||||
logger.info(f'[ORM] _ListColumns table={table_name} model_verbose={model_verbose}')
|
||||
|
||||
columns = []
|
||||
for row in cursor.fetchall():
|
||||
col_name, data_type, is_nullable, col_key, extra, col_type, col_comment = row
|
||||
is_pk = col_key == 'PRI'
|
||||
is_auto = 'auto_increment' in (extra or '').lower()
|
||||
|
||||
if data_type in ('int', 'bigint', 'tinyint', 'smallint', 'mediumint'):
|
||||
ftype = 'number'
|
||||
elif data_type in ('float', 'double', 'decimal'):
|
||||
ftype = 'number'
|
||||
elif data_type in ('datetime', 'timestamp'):
|
||||
ftype = 'datetime'
|
||||
elif data_type in ('date',):
|
||||
ftype = 'date'
|
||||
elif data_type in ('time',):
|
||||
ftype = 'time'
|
||||
elif data_type in ('json',):
|
||||
ftype = 'textarea'
|
||||
elif data_type in ('text', 'mediumtext', 'longtext', 'tinytext'):
|
||||
ftype = 'textarea'
|
||||
elif data_type in ('binary', 'varbinary', 'blob'):
|
||||
ftype = 'uuid'
|
||||
else:
|
||||
ftype = 'text'
|
||||
|
||||
title = col_comment or model_verbose.get(col_name) or col_name
|
||||
|
||||
columns.append({
|
||||
'field': col_name,
|
||||
'title': title,
|
||||
'type': ftype,
|
||||
'editable': not is_pk and not is_auto,
|
||||
'sortable': True,
|
||||
'filterable': True,
|
||||
'primaryKey': is_pk,
|
||||
'hideInForm': is_pk,
|
||||
'required': is_nullable == 'NO' and not is_auto,
|
||||
'dbType': col_type,
|
||||
})
|
||||
return columns
|
||||
|
||||
def _QueryTable(self, cursor, table_name, params):
|
||||
"""分页查询表数据"""
|
||||
cursor.execute(
|
||||
"SELECT COUNT(*) FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s",
|
||||
[table_name]
|
||||
)
|
||||
if cursor.fetchone()[0] == 0:
|
||||
return {'records': [], 'total': 0, 'error': f'表不存在: {table_name}'}
|
||||
|
||||
col_info = self._GetTableColumns(cursor, table_name)
|
||||
pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}
|
||||
|
||||
where_parts = []
|
||||
where_values = []
|
||||
for pk_name in pk_cols:
|
||||
pk_val = params.get(pk_name, '')
|
||||
if pk_val:
|
||||
where_parts.append(f'`{pk_name}` = %s')
|
||||
where_values.append(self._ConvertValueForDB(pk_val, pk_cols[pk_name]))
|
||||
|
||||
keyword = params.get('keyword', '')
|
||||
if not where_parts and keyword:
|
||||
cursor.execute(
|
||||
"SELECT COLUMN_NAME FROM information_schema.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
|
||||
"AND DATA_TYPE IN ('varchar', 'char', 'text', 'mediumtext', 'longtext')",
|
||||
[table_name]
|
||||
)
|
||||
text_cols = [row[0] for row in cursor.fetchall()]
|
||||
if text_cols:
|
||||
conditions = [f'`{c}` LIKE %s' for c in text_cols]
|
||||
or_clause = ' OR '.join(conditions)
|
||||
where_parts.append(f'({or_clause})')
|
||||
where_values.extend([f'%{keyword}%'] * len(text_cols))
|
||||
|
||||
where_clause = (' WHERE ' + ' AND '.join(where_parts)) if where_parts else ''
|
||||
|
||||
if where_clause:
|
||||
cursor.execute(f'SELECT COUNT(*) FROM `{table_name}`{where_clause}', where_values)
|
||||
total = cursor.fetchone()[0]
|
||||
else:
|
||||
cursor.execute(f'SELECT COUNT(*) FROM `{table_name}`')
|
||||
total = cursor.fetchone()[0]
|
||||
|
||||
try:
|
||||
page = max(int(params.get('PageIndex', 1)), 1)
|
||||
except (ValueError, TypeError):
|
||||
page = 1
|
||||
try:
|
||||
page_size = min(max(int(params.get('PageSize', 20)), 1), 200)
|
||||
except (ValueError, TypeError):
|
||||
page_size = 20
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
cursor.execute(
|
||||
f'SELECT * FROM `{table_name}`{where_clause} ORDER BY 1 LIMIT %s OFFSET %s',
|
||||
where_values + [page_size, offset]
|
||||
)
|
||||
desc = [d[0] for d in cursor.description]
|
||||
records = []
|
||||
for row in cursor.fetchall():
|
||||
record = {}
|
||||
for i, val in enumerate(row):
|
||||
val = self._SerializeValue(val)
|
||||
record[desc[i]] = val
|
||||
records.append(record)
|
||||
|
||||
return {'records': records, 'total': total, 'page': page, 'PageSize': page_size}
|
||||
|
||||
def _AddRecord(self, cursor, table_name, record):
|
||||
"""新增记录"""
|
||||
if not self._ValidateTableName(cursor, table_name):
|
||||
return {'code': 1, 'msg': f'表 {table_name} 不存在'}
|
||||
|
||||
col_info = self._GetTableColumns(cursor, table_name)
|
||||
|
||||
fields = []
|
||||
values = []
|
||||
for key, val in record.items():
|
||||
if key not in col_info:
|
||||
continue
|
||||
if col_info[key]['is_auto']:
|
||||
continue
|
||||
fields.append(f'`{key}`')
|
||||
values.append(self._ConvertValueForDB(val, col_info[key]))
|
||||
|
||||
if not fields:
|
||||
return {'code': 1, 'msg': '没有可插入的字段'}
|
||||
|
||||
placeholders = ', '.join(['%s'] * len(fields))
|
||||
sql = f'INSERT INTO `{table_name}` ({", ".join(fields)}) VALUES ({placeholders})'
|
||||
cursor.execute(sql, values)
|
||||
return {'code': 0, 'msg': '新增成功'}
|
||||
|
||||
def _ModifyRecord(self, cursor, table_name, record):
|
||||
"""修改记录"""
|
||||
if not self._ValidateTableName(cursor, table_name):
|
||||
return {'code': 1, 'msg': f'表 {table_name} 不存在'}
|
||||
|
||||
col_info = self._GetTableColumns(cursor, table_name)
|
||||
pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}
|
||||
|
||||
if not pk_cols:
|
||||
return {'code': 1, 'msg': '表没有主键,无法定位记录'}
|
||||
|
||||
set_parts = []
|
||||
set_values = []
|
||||
for key, val in record.items():
|
||||
if key not in col_info or key in pk_cols:
|
||||
continue
|
||||
set_parts.append(f'`{key}` = %s')
|
||||
set_values.append(self._ConvertValueForDB(val, col_info[key]))
|
||||
|
||||
if not set_parts:
|
||||
return {'code': 0, 'msg': '没有需要更新的字段'}
|
||||
|
||||
where_parts = []
|
||||
where_values = []
|
||||
for pk_name in pk_cols:
|
||||
if pk_name not in record:
|
||||
return {'code': 1, 'msg': f'缺少主键字段: {pk_name}'}
|
||||
where_parts.append(f'`{pk_name}` = %s')
|
||||
where_values.append(self._ConvertValueForDB(record[pk_name], pk_cols[pk_name]))
|
||||
|
||||
sql = f'UPDATE `{table_name}` SET {", ".join(set_parts)} WHERE {" AND ".join(where_parts)}'
|
||||
cursor.execute(sql, set_values + where_values)
|
||||
|
||||
if cursor.rowcount == 0:
|
||||
return {'code': 1, 'msg': '记录不存在或未变更'}
|
||||
return {'code': 0, 'msg': '修改成功'}
|
||||
|
||||
def _DeleteRecord(self, cursor, table_name, record):
|
||||
"""删除记录"""
|
||||
if not self._ValidateTableName(cursor, table_name):
|
||||
return {'code': 1, 'msg': f'表 {table_name} 不存在'}
|
||||
|
||||
col_info = self._GetTableColumns(cursor, table_name)
|
||||
pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}
|
||||
|
||||
if not pk_cols:
|
||||
return {'code': 1, 'msg': '表没有主键,无法定位记录'}
|
||||
|
||||
where_parts = []
|
||||
where_values = []
|
||||
for pk_name in pk_cols:
|
||||
if pk_name not in record:
|
||||
return {'code': 1, 'msg': f'缺少主键字段: {pk_name}'}
|
||||
where_parts.append(f'`{pk_name}` = %s')
|
||||
where_values.append(self._ConvertValueForDB(record[pk_name], pk_cols[pk_name]))
|
||||
|
||||
sql = f'DELETE FROM `{table_name}` WHERE {" AND ".join(where_parts)}'
|
||||
cursor.execute(sql, where_values)
|
||||
|
||||
if cursor.rowcount == 0:
|
||||
return {'code': 1, 'msg': '记录不存在'}
|
||||
return {'code': 0, 'msg': '删除成功'}
|
||||
|
||||
# ─── 工具方法 ──────────────────────────────────────────────
|
||||
|
||||
def _ValidateTableName(self, cursor, table_name):
|
||||
"""验证表名存在"""
|
||||
cursor.execute(
|
||||
"SELECT COUNT(*) FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s",
|
||||
[table_name]
|
||||
)
|
||||
return cursor.fetchone()[0] > 0
|
||||
|
||||
def _GetTableColumns(self, cursor, table_name):
|
||||
"""获取表的列信息"""
|
||||
cursor.execute(
|
||||
"SELECT COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, COLUMN_KEY, EXTRA, IS_NULLABLE "
|
||||
"FROM information_schema.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
|
||||
"ORDER BY ORDINAL_POSITION",
|
||||
[table_name]
|
||||
)
|
||||
cols = {}
|
||||
for row in cursor.fetchall():
|
||||
col_name, data_type, column_type, col_key, extra, is_nullable = row
|
||||
cols[col_name] = {
|
||||
'type': data_type,
|
||||
'column_type': column_type or '',
|
||||
'is_pk': col_key == 'PRI',
|
||||
'is_auto': 'auto_increment' in (extra or '').lower(),
|
||||
'is_nullable': is_nullable == 'YES',
|
||||
}
|
||||
return cols
|
||||
|
||||
def _ConvertValueForDB(self, value, col_info):
|
||||
"""将前端传来的值转换为数据库可接受的类型"""
|
||||
if value is None or value == '':
|
||||
if col_info['is_nullable']:
|
||||
return None
|
||||
return ''
|
||||
|
||||
data_type = col_info['type']
|
||||
|
||||
if data_type in ('int', 'bigint', 'tinyint', 'smallint', 'mediumint'):
|
||||
return int(value)
|
||||
elif data_type in ('float', 'double', 'decimal'):
|
||||
return float(value)
|
||||
elif data_type in ('binary', 'varbinary'):
|
||||
import re
|
||||
hex_str = re.sub(r'[^0-9a-fA-F]', '', str(value))
|
||||
if len(hex_str) == 32:
|
||||
return uuid.UUID(hex_str).bytes
|
||||
return value
|
||||
|
||||
return value
|
||||
|
||||
def _SerializeValue(self, val):
|
||||
"""将数据库值序列化为 JSON 安全格式"""
|
||||
if isinstance(val, bytes):
|
||||
if len(val) == 16:
|
||||
try:
|
||||
return str(uuid.UUID(bytes=val))
|
||||
except ValueError:
|
||||
return val.hex()
|
||||
return val.hex()
|
||||
elif isinstance(val, datetime.datetime):
|
||||
return val.strftime('%Y-%m-%d %H:%M:%S')
|
||||
elif isinstance(val, datetime.date):
|
||||
return val.isoformat()
|
||||
elif isinstance(val, datetime.time):
|
||||
return val.strftime('%H:%M:%S')
|
||||
elif isinstance(val, (bytearray, memoryview)):
|
||||
return bytes(val).hex()
|
||||
return val
|
||||
|
||||
def _FindModelVerboseNames(self, table_name):
|
||||
"""从 Django 模型中获取字段的 verbose_name"""
|
||||
from django.apps import apps
|
||||
verbose_map = {}
|
||||
try:
|
||||
for model in apps.get_models():
|
||||
if model._meta.db_table == table_name:
|
||||
logger.info(f'[ORM] _FindModelVerboseNames matched model={model.__name__} db_table={model._meta.db_table}')
|
||||
for f in model._meta.get_fields():
|
||||
# 获取数据库列名
|
||||
col_name = None
|
||||
if hasattr(f, 'column') and f.column:
|
||||
col_name = f.column
|
||||
elif hasattr(f, 'db_column') and f.db_column:
|
||||
col_name = f.db_column
|
||||
elif hasattr(f, 'attname'):
|
||||
col_name = f.attname
|
||||
|
||||
if not col_name:
|
||||
continue
|
||||
|
||||
vn = getattr(f, 'verbose_name', None)
|
||||
if vn and str(vn) != col_name:
|
||||
verbose_map[col_name] = str(vn)
|
||||
logger.debug(f'[ORM] {col_name} -> {str(vn)}')
|
||||
break
|
||||
else:
|
||||
# 没有匹配的模型
|
||||
available = [m._meta.db_table for m in apps.get_models()]
|
||||
logger.warning(f'[ORM] _FindModelVerboseNames: 未找到 db_table={table_name} 的模型, 可用表: {available[:20]}...')
|
||||
except Exception as e:
|
||||
logger.error(f'[ORM] _FindModelVerboseNames 异常: {e}')
|
||||
return verbose_map
|
||||
Reference in New Issue
Block a user