From 004012b59f7135cd7680d4bd16fc1495585cb5bc Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 17 Jun 2026 04:10:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=9B=9E=E9=80=80=E4=BA=86?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gvsdsdk/migrations/0001_initial.py | 282 ++++---------- gvsdsdk/model_base.py | 56 +++ migrate_to_gvsdsdk.py | 510 ------------------------ migrate_user_to_gvsdsdk.sql | 179 --------- shop/migrations/0001_initial.py | 255 +++++------- users/migrations/0001_initial.py | 606 ++++++++++------------------- users/migrations/0007_userkefu.py | 36 ++ users/models.py | 2 +- 8 files changed, 480 insertions(+), 1446 deletions(-) delete mode 100644 migrate_to_gvsdsdk.py delete mode 100644 migrate_user_to_gvsdsdk.sql create mode 100644 users/migrations/0007_userkefu.py diff --git a/gvsdsdk/migrations/0001_initial.py b/gvsdsdk/migrations/0001_initial.py index 0104da4..3a8544f 100644 --- a/gvsdsdk/migrations/0001_initial.py +++ b/gvsdsdk/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 4.2.27 on 2026-06-17 01:34 +# Generated by Django 6.0.5 on 2026-06-13 11:43 import datetime -from django.db import migrations, models import gvsdsdk.models +from django.db import migrations, models class Migration(migrations.Migration): @@ -13,40 +13,12 @@ class Migration(migrations.Migration): ] operations = [ - 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=200, 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='最后登录')), - ('IsStaff', models.BooleanField(db_column='IsStaff', default=False, verbose_name='是否员工')), - ('IsSuperuser', models.BooleanField(db_column='IsSuperuser', default=False, verbose_name='是否超级用户')), - ('YonghuID', models.CharField(blank=True, db_column='YonghuID', db_index=True, max_length=7, null=True, unique=True, verbose_name='用户业务ID')), - ('UserAvatar', models.CharField(blank=True, db_column='UserAvatar', default='a_long/morentouxiang.jpg', max_length=500, null=True, verbose_name='头像')), - ('UserType', models.CharField(choices=[('normal', '普通用户'), ('dashou', '打手'), ('shop', '商家'), ('guanshi', '管事'), ('admin', '管理员'), ('kefu', '客服')], db_column='UserType', default='normal', max_length=10, verbose_name='用户类型')), - ], - options={ - 'verbose_name': '用户', - 'verbose_name_plural': '用户', - 'db_table': 'User', - 'managed': True, - }, - ), migrations.CreateModel( name='AccountActivation', fields=[ - ('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='创建时间')), + ('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)), ], options={ 'verbose_name': '账户激活', @@ -58,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, 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='状态')), + ('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)), ], options={ 'verbose_name': '临时令牌', @@ -76,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, verbose_name='用户UUID')), - ('Password', models.CharField(db_column='Password', max_length=1024, verbose_name='密码哈希')), + ('UserUUID', gvsdsdk.models.UUIDField(binary_length=16, db_column='UserUUID', primary_key=True, serialize=False)), + ('Password', models.CharField(db_column='Password', max_length=1024)), ], options={ 'verbose_name': '密码', @@ -89,14 +61,12 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Permission', fields=[ - ('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='创建时间')), + ('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)), ], options={ 'verbose_name': '权限', @@ -105,72 +75,16 @@ 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, 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='创建时间')), + ('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)), ], options={ 'verbose_name': '角色', @@ -180,69 +94,55 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='RolePermission', + name='RoleDept', fields=[ - ('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='创建时间')), + ('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)), ], options={ - 'verbose_name': '角色权限关联', - 'verbose_name_plural': '角色权限关联', - 'db_table': 'RolePermission', + 'verbose_name': '角色部门关联', + 'verbose_name_plural': '角色部门关联', + 'db_table': 'RoleDept', 'managed': True, }, ), migrations.CreateModel( - name='RoleShop', + name='User', fields=[ - ('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='创建时间')), + ('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)), ], 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, 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', + 'verbose_name': '用户', + 'verbose_name_plural': '用户', + 'db_table': 'User', 'managed': True, }, ), migrations.CreateModel( name='User2FA', fields=[ - ('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='创建时间')), + ('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)), ], options={ 'verbose_name': '双因素认证', @@ -254,9 +154,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, 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='层级权限标识')), + ('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)), ], options={ 'verbose_name': '用户ABAC', @@ -268,10 +168,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, 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='文件类型')), + ('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)), ], options={ 'verbose_name': '用户头像', @@ -280,33 +180,16 @@ 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, 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='用户代理')), + ('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)), ], options={ 'verbose_name': '登录日志', @@ -318,10 +201,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, 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='过期时间')), + ('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')), ], options={ 'verbose_name': '2FA待确认', @@ -333,11 +216,10 @@ class Migration(migrations.Migration): migrations.CreateModel( name='UserRole', fields=[ - ('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='创建时间')), + ('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)), ], options={ 'verbose_name': '用户角色关联', diff --git a/gvsdsdk/model_base.py b/gvsdsdk/model_base.py index 190d52a..9170247 100644 --- a/gvsdsdk/model_base.py +++ b/gvsdsdk/model_base.py @@ -294,6 +294,62 @@ class _CastJsonExpression: return f'CastJsonExpr({self.field_name}[{self.key!r}]->{type_name})' +class ForeignKeyIdDescriptor: + """FK 字段描述符:类级别访问返回 FieldExpression,实例级别访问/设置委托给原始 Django 字段。""" + def __init__(self, fk_name): + self.fk_name = fk_name + self.id_attr = fk_name + '_id' + self._original_field = None # 延迟绑定原始 Django 字段 + + def _get_original_field(self, owner): + if self._original_field is None: + try: + self._original_field = owner._meta.get_field(self.fk_name) + except Exception: + pass + return self._original_field + + def __get__(self, obj, objtype=None): + if obj is None: + return FieldExpression(objtype, self.id_attr) + return getattr(obj, self.id_attr) + + def __set__(self, obj, value): + # 直接写入实例 __dict__,避免触发描述符递归 + obj.__dict__[self.id_attr] = value + + +class FieldExpressionDescriptor: + """普通字段描述符:类级别访问返回 FieldExpression,实例级别访问/设置委托给原始 Django 字段。""" + def __init__(self, field_name): + self.field_name = field_name + self._original_field = None # 延迟绑定原始 Django 字段 + + def _get_original_field(self, owner): + if self._original_field is None: + try: + self._original_field = owner._meta.get_field(self.field_name) + except Exception: + pass + return self._original_field + + def __get__(self, obj, objtype=None): + if obj is None: + return FieldExpression(objtype, self.field_name) + # 实例级别:从 __dict__ 获取,或让 Django 字段描述符处理 + if self.field_name in obj.__dict__: + return obj.__dict__[self.field_name] + # 委托给原始 Django 字段 + orig = self._get_original_field(objtype) + if orig is not None: + return orig.__get__(obj, objtype) + return getattr(obj, self.field_name) + + def __set__(self, obj, value): + # 直接写入实例 __dict__,避免触发描述符递归 + obj.__dict__[self.field_name] = value + + class QModelBase(models.base.ModelBase): def __new__(mcs, name, bases, namespace, **kwargs): # 不再强制设置 db_column —— Django 会自动处理 diff --git a/migrate_to_gvsdsdk.py b/migrate_to_gvsdsdk.py deleted file mode 100644 index c99dc2a..0000000 --- a/migrate_to_gvsdsdk.py +++ /dev/null @@ -1,510 +0,0 @@ -""" -gvsdsdk User 表迁移脚本 -将 user_main 数据迁移到 gvsdsdk.User + user_sensitive_data - -执行前确保已全量备份数据库! -分阶段执行,每阶段验证后才继续下一阶段。 -""" -import uuid -import sys -import MySQLdb - -# ==================== 配置 ==================== -DB_CONFIG = { - 'host': 'gvsds.com', - 'port': 50030, - 'user': 'root', - 'passwd': 'sajksh.sdfGH3YUge.wjkd+', - 'db': 'xaio_cheng_xu', - 'charset': 'utf8mb4', - 'connect_timeout': 10, -} - -# UUID 命名空间(固定值,确保可重现) -UUID_NAMESPACE = uuid.UUID('6e6d6163-0000-0000-0000-000000000000') - -# 旧 RBAC 表 → 重命名映射 -RBAC_RENAME_MAP = { - 'role': 'legacy_role', - 'user_role': 'legacy_user_role', - 'permission': 'legacy_permission', - 'role_permission': 'legacy_role_permission', - 'account_permission': 'legacy_account_permission', -} - -# 扩展表 + admin_profile(有 FK 约束指向 user_main.id) -EXT_TABLES_WITH_FK = [ - 'user_boss', 'user_dashou', 'user_guanshi', 'user_kefu', - 'user_shangjia', 'user_shenheguan', 'user_zuzhang', 'admin_profile', -] - - -def get_conn(): - return MySQLdb.connect(**DB_CONFIG) - - -def table_exists(cur, table_name): - cur.execute(""" - SELECT COUNT(*) FROM information_schema.TABLES - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s - """, (DB_CONFIG['db'], table_name)) - return cur.fetchone()[0] > 0 - - -def fk_constraint_exists(cur, table_name, ref_table): - cur.execute(""" - SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND REFERENCED_TABLE_NAME = %s - """, (DB_CONFIG['db'], table_name, ref_table)) - return [r[0] for r in cur.fetchall()] - - -def phase1_rename_rbac_tables(): - """Phase 1: 重命名旧 RBAC 表,避免与 gvsdsdk PascalCase 表名冲突""" - print('\n' + '='*60) - print('Phase 1: 重命名旧 RBAC 表') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - for old_name, new_name in RBAC_RENAME_MAP.items(): - if table_exists(cur, old_name) and not table_exists(cur, new_name): - sql = f'RENAME TABLE `{old_name}` TO `{new_name}`' - print(f' {old_name} -> {new_name}') - cur.execute(sql) - elif table_exists(cur, new_name): - print(f' {new_name} 已存在,跳过') - else: - print(f' {old_name} 不存在,跳过') - - conn.commit() - cur.close() - conn.close() - print('Phase 1 完成') - - -def phase2_create_tables(): - """Phase 2: 创建 User 表和 user_sensitive_data 表""" - print('\n' + '='*60) - print('Phase 2: 创建 User 表 + user_sensitive_data 表') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - # 2.1 创建 User 表 - if table_exists(cur, 'User'): - print(' User 表已存在,跳过创建') - else: - print(' 创建 User 表...') - cur.execute(""" - CREATE TABLE `User` ( - `UserUUID` BINARY(16) NOT NULL PRIMARY KEY, - `UserName` varchar(256) NOT NULL UNIQUE, - `UserPassword` varchar(200) NOT NULL DEFAULT '', - `UserPositionStatus` smallint NOT NULL DEFAULT 0, - `UserPersonalSignature` varchar(1024) NOT NULL DEFAULT '', - `UserEmail` varchar(256) NOT NULL DEFAULT '', - `UserEmailVerify` smallint NOT NULL DEFAULT 0, - `UserAccountLicense` smallint NOT NULL DEFAULT 1, - `UserGender` smallint NOT NULL DEFAULT -1, - `UserTele` varchar(16) NULL, - `UserCreateTime` datetime(6) NOT NULL, - `UserLastLoginDate` datetime(6) NOT NULL, - `IsStaff` tinyint(1) NOT NULL DEFAULT 0, - `IsSuperuser` tinyint(1) NOT NULL DEFAULT 0, - `YonghuID` varchar(7) NULL UNIQUE, - `UserAvatar` varchar(500) NULL DEFAULT 'a_long/morentouxiang.jpg', - `UserType` varchar(10) NOT NULL DEFAULT 'normal', - INDEX `User_UserUUID_0a9d8a_idx` (`UserUUID`), - INDEX `User_YonghuID_idx` (`YonghuID`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci - """) - print(' User 表创建完成') - - # 2.2 创建 user_sensitive_data 表 - if table_exists(cur, 'user_sensitive_data'): - print(' user_sensitive_data 表已存在,跳过创建') - else: - print(' 创建 user_sensitive_data 表...') - cur.execute(""" - CREATE TABLE `user_sensitive_data` ( - `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, - `UserUUID` BINARY(16) NOT NULL UNIQUE, - `OpenID` varchar(64) NULL UNIQUE, - `UnionID` varchar(64) NULL, - `Password` varchar(200) NULL, - `PhoneVerified` tinyint(1) NOT NULL DEFAULT 0, - `Zhifu` varchar(500) NULL, - `SKZhanghao` varchar(11) NULL, - `IP` varchar(50) NULL, - `create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - INDEX `user_sensitive_data_OpenID_idx` (`OpenID`), - INDEX `user_sensitive_data_UnionID_idx` (`UnionID`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci - """) - print(' user_sensitive_data 表创建完成') - - conn.commit() - cur.close() - conn.close() - print('Phase 2 完成') - - -def phase3_migrate_user_data(): - """Phase 3: 迁移 user_main 数据到 User + user_sensitive_data""" - print('\n' + '='*60) - print('Phase 3: 迁移 user_main 数据') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - # 检查 User 表是否已有数据 - cur.execute('SELECT COUNT(*) FROM `User`') - existing = cur.fetchone()[0] - if existing > 0: - print(f' User 表已有 {existing} 行数据,跳过迁移') - cur.close() - conn.close() - return - - # 获取所有 user_main 数据 - cur.execute(""" - SELECT id, yonghuid, openid, avatar, phone, password, zhifu, - skzhanghao, ip, last_login_time, user_type, create_time, - update_time, unionid, shoujihao_renzheng, last_login - FROM user_main - """) - rows = cur.fetchall() - print(f' 从 user_main 读取 {len(rows)} 行') - - # 生成 UUID 并批量插入 - user_data = [] - sensitive_data = [] - - for row in rows: - (id_, yonghuid, openid, avatar, phone, password, zhifu, - skzhanghao, ip, last_login_time, user_type, create_time, - update_time, unionid, shoujihao_renzheng, last_login) = row - - # 生成确定性 UUID - user_uuid = uuid.uuid5(UUID_NAMESPACE, yonghuid).bytes - - # User 表数据 - is_superuser = 1 if user_type == 'admin' else 0 - is_staff = 1 if user_type in ('admin', 'kefu') else 0 - user_name = yonghuid # 用 yonghuid 作为 UserName - avatar_val = avatar if avatar else 'a_long/morentouxiang.jpg' - last_login = last_login_time or last_login or create_time - - user_data.append(( - user_uuid, user_name, '', # UserPassword 留空 - 0, # UserPositionStatus - '', # UserPersonalSignature - '', # UserEmail - 0, # UserEmailVerify - 1, # UserAccountLicense - -1, # UserGender - phone, # UserTele - create_time, # UserCreateTime - last_login, # UserLastLoginDate - is_staff, is_superuser, - yonghuid, # YonghuID - avatar_val, # UserAvatar - user_type, # UserType - )) - - # user_sensitive_data 表数据 - sensitive_data.append(( - user_uuid, - openid if openid else None, - unionid if unionid else None, - password if password else None, - 1 if shoujihao_renzheng else 0, - zhifu if zhifu else None, - skzhanghao if skzhanghao else None, - ip if ip else None, - )) - - # 批量插入 User 表 - print(f' 插入 {len(user_data)} 行到 User 表...') - cur.executemany(""" - INSERT INTO `User` ( - UserUUID, UserName, UserPassword, UserPositionStatus, - UserPersonalSignature, UserEmail, UserEmailVerify, - UserAccountLicense, UserGender, UserTele, - UserCreateTime, UserLastLoginDate, IsStaff, IsSuperuser, - YonghuID, UserAvatar, UserType - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - """, user_data) - - # 批量插入 user_sensitive_data 表 - print(f' 插入 {len(sensitive_data)} 行到 user_sensitive_data 表...') - cur.executemany(""" - INSERT INTO user_sensitive_data ( - UserUUID, OpenID, UnionID, Password, PhoneVerified, - Zhifu, SKZhanghao, IP - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) - """, sensitive_data) - - conn.commit() - - # 验证 - cur.execute('SELECT COUNT(*) FROM `User`') - user_count = cur.fetchone()[0] - cur.execute('SELECT COUNT(*) FROM user_sensitive_data') - sd_count = cur.fetchone()[0] - print(f' 验证: User={user_count}, UserSensitiveData={sd_count}') - - if user_count != len(rows): - print(f' 警告: User 表行数({user_count}) != user_main 行数({len(rows)})') - - cur.close() - conn.close() - print('Phase 3 完成') - - -def phase4_alter_extension_tables(): - """Phase 4: 删除 FK 约束,修改扩展表 user_id 列类型 int → BINARY(16)""" - print('\n' + '='*60) - print('Phase 4: 修改扩展表 FK 列类型') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - for table in EXT_TABLES_WITH_FK: - print(f'\n 处理 {table}...') - - # 4.1 删除 FK 约束 - fk_names = fk_constraint_exists(cur, table, 'user_main') - for fk_name in fk_names: - print(f' 删除 FK: {fk_name}') - cur.execute(f'ALTER TABLE `{table}` DROP FOREIGN KEY `{fk_name}`') - - # 4.2 检查是否已有 new_user_id 列 - cur.execute(""" - SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = 'new_user_id' - """, (DB_CONFIG['db'], table)) - has_new = cur.fetchone()[0] > 0 - - if not has_new: - # 4.3 添加新 BINARY(16) 列 - print(f' 添加 new_user_id BINARY(16) 列') - cur.execute(f'ALTER TABLE `{table}` ADD COLUMN `new_user_id` BINARY(16) NULL') - - # 4.4 填充新列:通过 user_main.id → user_main.yonghuid → User.UserUUID - print(f' 填充 new_user_id...') - cur.execute(f""" - UPDATE `{table}` ext - JOIN user_main um ON ext.user_id = um.id - JOIN `User` u ON u.YonghuID = um.yonghuid COLLATE utf8mb4_general_ci - SET ext.new_user_id = u.UserUUID - """) - affected = cur.rowcount - print(f' 更新 {affected} 行') - - # 4.5 删除旧 user_id 列 - print(f' 删除旧 user_id 列') - cur.execute(f'ALTER TABLE `{table}` DROP COLUMN `user_id`') - - # 4.6 重命名 new_user_id → user_id - print(f' 重命名 new_user_id → user_id') - cur.execute(f'ALTER TABLE `{table}` CHANGE `new_user_id` `user_id` BINARY(16) NULL') - - # 4.7 添加索引 - print(f' 添加索引') - cur.execute(f'ALTER TABLE `{table}` ADD INDEX `{table}_user_id_idx` (`user_id`)') - - conn.commit() - - # 4.8 处理 django_admin_log(空表,直接改列类型) - print(f'\n 处理 django_admin_log...') - fk_names = fk_constraint_exists(cur, 'django_admin_log', 'user_main') - for fk_name in fk_names: - print(f' 删除 FK: {fk_name}') - cur.execute(f'ALTER TABLE `django_admin_log` DROP FOREIGN KEY `{fk_name}`') - - # 检查并修改 user_id 列类型 - cur.execute(""" - SELECT DATA_TYPE FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'django_admin_log' AND COLUMN_NAME = 'user_id' - """, (DB_CONFIG['db'],)) - col_type = cur.fetchone() - if col_type and col_type[0] != 'binary': - print(f' 修改 user_id 列类型: {col_type[0]} → BINARY(16)') - cur.execute('ALTER TABLE `django_admin_log` MODIFY COLUMN `user_id` BINARY(16) NULL') - else: - print(f' user_id 已是 BINARY 类型,跳过') - - conn.commit() - cur.close() - conn.close() - print('\nPhase 4 完成') - - -def phase5_update_migrations(): - """Phase 5: 更新 django_migrations 记录""" - print('\n' + '='*60) - print('Phase 5: 更新 django_migrations 记录') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - # 删除旧迁移记录 - apps = ['gvsdsdk', 'users', 'shop', 'backend', 'rank', 'orders', 'config', 'products'] - for app in apps: - cur.execute('DELETE FROM django_migrations WHERE app = %s', (app,)) - if cur.rowcount > 0: - print(f' 删除 {app} 旧迁移记录: {cur.rowcount} 条') - - # 插入新迁移记录 - from datetime import datetime - now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - new_migrations = [ - ('gvsdsdk', '0001_initial'), - ('users', '0001_initial'), - ('shop', '0001_initial'), - ('backend', '0001_initial'), - ('rank', '0001_initial'), - ('orders', '0001_initial'), - ('config', '0001_initial'), - ('products', '0001_initial'), - ('products', '0002_initial'), - ] - for app, name in new_migrations: - cur.execute( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - (app, name, now) - ) - print(f' 插入 {app}.{name}') - - conn.commit() - cur.close() - conn.close() - print('Phase 5 完成') - - -def phase6_verify(): - """Phase 6: 验证迁移结果""" - print('\n' + '='*60) - print('Phase 6: 验证迁移结果') - print('='*60) - - conn = get_conn() - cur = conn.cursor() - - # 1. User 表数据量 - cur.execute('SELECT COUNT(*) FROM `User`') - user_count = cur.fetchone()[0] - print(f' User 表: {user_count} 行') - - # 2. user_sensitive_data 数据量 - cur.execute('SELECT COUNT(*) FROM user_sensitive_data') - sd_count = cur.fetchone()[0] - print(f' user_sensitive_data: {sd_count} 行') - - # 3. user_main 对比 - cur.execute('SELECT COUNT(*) FROM user_main') - um_count = cur.fetchone()[0] - print(f' user_main: {um_count} 行 (原表保留)') - - # 4. 验证 User 表字段 - cur.execute('DESCRIBE `User`') - print(f'\n User 表结构:') - for c in cur.fetchall(): - print(f' {c[0]} | {c[1]}') - - # 5. 验证扩展表 user_id 列类型 - print(f'\n 扩展表 user_id 列类型:') - for table in EXT_TABLES_WITH_FK: - cur.execute(""" - SELECT DATA_TYPE FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = 'user_id' - """, (DB_CONFIG['db'], table)) - result = cur.fetchone() - print(f' {table}.user_id: {result[0] if result else "不存在"}') - - # 6. 验证扩展表数据完整性 - print(f'\n 扩展表数据完整性:') - for table in EXT_TABLES_WITH_FK: - cur.execute(f'SELECT COUNT(*) FROM `{table}`') - total = cur.fetchone()[0] - cur.execute(f'SELECT COUNT(*) FROM `{table}` WHERE user_id IS NULL') - null_count = cur.fetchone()[0] - print(f' {table}: total={total}, null_user_id={null_count}') - - # 7. 验证 RBAC 表重命名 - print(f'\n RBAC 表重命名验证:') - for old, new in RBAC_RENAME_MAP.items(): - cur.execute(""" - SELECT COUNT(*) FROM information_schema.TABLES - WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s - """, (DB_CONFIG['db'], new)) - exists = cur.fetchone()[0] > 0 - print(f' {new}: {"存在" if exists else "不存在"}') - - # 8. 验证 django_migrations - print(f'\n django_migrations:') - cur.execute("SELECT app, name FROM django_migrations WHERE app IN ('gvsdsdk','users','shop') ORDER BY app") - for r in cur.fetchall(): - print(f' {r[0]}.{r[1]}') - - # 9. 抽样验证数据一致性 - print(f'\n 抽样验证 (前5行):') - cur.execute(""" - SELECT u.YonghuID, u.UserType, u.UserTele, u.UserAvatar, - sd.OpenID, sd.Password, sd.PhoneVerified - FROM `User` u - LEFT JOIN user_sensitive_data sd ON u.UserUUID = sd.UserUUID - LIMIT 5 - """) - for r in cur.fetchall(): - print(f' YonghuID={r[0]}, Type={r[1]}, Tele={r[2]}, OpenID={r[4]}, Verified={r[6]}') - - cur.close() - conn.close() - print('\nPhase 6 验证完成') - - -def main(): - print('='*60) - print('gvsdsdk User 表迁移脚本') - print('='*60) - print(f'目标数据库: {DB_CONFIG["db"]} @ {DB_CONFIG["host"]}:{DB_CONFIG["port"]}') - - # 确认 - if '--yes' not in sys.argv: - print('\n警告: 此脚本将修改生产数据库!') - print('请确保已全量备份数据库。') - print('使用 --yes 参数跳过确认。') - response = input('输入 YES 确认执行: ') - if response != 'YES': - print('已取消') - return - - # 执行各阶段 - phase1_rename_rbac_tables() - phase2_create_tables() - phase3_migrate_user_data() - phase4_alter_extension_tables() - phase5_update_migrations() - phase6_verify() - - print('\n' + '='*60) - print('迁移完成!') - print('='*60) - print('\n后续步骤:') - print('1. 在服务器上部署新代码 (git pull)') - print('2. 重启 Django 服务') - print('3. 测试登录和核心功能') - print('4. 确认无误后 DROP TABLE user_main') - - -if __name__ == '__main__': - main() diff --git a/migrate_user_to_gvsdsdk.sql b/migrate_user_to_gvsdsdk.sql deleted file mode 100644 index 13722ef..0000000 --- a/migrate_user_to_gvsdsdk.sql +++ /dev/null @@ -1,179 +0,0 @@ --- ============================================================ --- 用户表架构迁移脚本 --- 从旧 user_main 表迁移数据到新 User + user_sensitive_data 表 --- --- 执行顺序: --- 1. 先执行 python manage.py migrate(创建新表) --- 2. 再执行本 SQL 脚本(迁移数据) --- 3. 最后删除旧表(确认无误后) --- ============================================================ - --- ============================================================ --- 第1步:从 user_main 迁移数据到 User 表 --- ============================================================ - --- User 表:核心身份字段 -INSERT INTO `User` ( - `UserUUID`, - `UserName`, - `UserPassword`, - `UserPositionStatus`, - `UserPersonalSignature`, - `UserEmail`, - `UserEmailVerify`, - `UserAccountLicense`, - `UserGender`, - `UserTele`, - `UserCreateTime`, - `UserLastLoginDate`, - `IsStaff`, - `IsSuperuser`, - `YonghuID`, - `UserAvatar`, - `UserType` -) -SELECT - UNHEX(REPLACE(UUID(), '-', '')), -- 生成 16 字节 UUID - COALESCE(yonghuid, openid), -- UserName = yonghuid 或 openid - COALESCE(password, ''), -- UserPassword - 0, -- UserPositionStatus - '', -- UserPersonalSignature - '', -- UserEmail - 0, -- UserEmailVerify - 1, -- UserAccountLicense (启用) - -1, -- UserGender - phone, -- UserTele - create_time, -- UserCreateTime - COALESCE(last_login_time, create_time), -- UserLastLoginDate - CASE WHEN user_type = 'admin' THEN 1 ELSE 0 END, -- IsStaff - CASE WHEN user_type = 'admin' THEN 1 ELSE 0 END, -- IsSuperuser - yonghuid, -- YonghuID - COALESCE(avatar, 'a_long/morentouxiang.jpg'), -- UserAvatar - user_type -- UserType -FROM `user_main` -WHERE yonghuid IS NOT NULL AND yonghuid != ''; - --- ============================================================ --- 第2步:迁移敏感数据到 user_sensitive_data 表 --- ============================================================ - -INSERT INTO `user_sensitive_data` ( - `UserUUID_id`, - `OpenID`, - `UnionID`, - `Password`, - `PhoneVerified`, - `Zhifu`, - `SKZhanghao`, - `IP`, - `create_time`, - `update_time` -) -SELECT - u.`UserUUID`, - um.openid, - um.unionid, - um.password, - um.shoujihao_renzheng, - um.zhifu, - um.skzhanghao, - um.ip, - um.create_time, - NOW() -FROM `user_main` um -JOIN `User` u ON u.`YonghuID` = um.yonghuid; - --- ============================================================ --- 第3步:更新扩展表的外键引用 --- 扩展表(user_dashou, user_shangjia 等)的 user_id 原来指向 user_main.id(自增整数) --- 现在需要改为指向 User.UserUUID(16字节二进制) --- ============================================================ - --- user_boss -UPDATE `user_boss` ub -JOIN `user_main` um ON um.id = ub.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET ub.user_id = u.`UserUUID`; - --- user_dashou -UPDATE `user_dashou` ud -JOIN `user_main` um ON um.id = ud.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET ud.user_id = u.`UserUUID`; - --- user_shangjia -UPDATE `user_shangjia` us -JOIN `user_main` um ON um.id = us.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET us.user_id = u.`UserUUID`; - --- user_guanshi -UPDATE `user_guanshi` ug -JOIN `user_main` um ON um.id = ug.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET ug.user_id = u.`UserUUID`; - --- user_zuzhang -UPDATE `user_zuzhang` uz -JOIN `user_main` um ON um.id = uz.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET uz.user_id = u.`UserUUID`; - --- user_shenheguan -UPDATE `user_shenheguan` ush -JOIN `user_main` um ON um.id = ush.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET ush.user_id = u.`UserUUID`; - --- admin_profile -UPDATE `admin_profile` ap -JOIN `user_main` um ON um.id = ap.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET ap.user_id = u.`UserUUID`; - --- user_kefu -UPDATE `user_kefu` uk -JOIN `user_main` um ON um.id = uk.user_id -JOIN `User` u ON u.`YonghuID` = um.yonghuid -SET uk.user_id = u.`UserUUID`; - --- ============================================================ --- 第4步:更新 yonghu_dianpu_bangding 表的 to_field 引用 --- 原来用 to_field='yonghuid' 关联 user_main.yonghuid --- 现在用 to_field='YonghuID' 关联 User.YonghuID --- 数据本身不需要变(yonghuid 值相同),但需要确认列名 --- ============================================================ - --- yonghu_dianpu_bangding 的 yonghu_id 列存储的是 yonghuid 字符串值 --- 新模型 to_field='YonghuID',列名变为 yonghu_id(Django 自动生成) --- 如果旧列名也是 yonghu_id,则数据不需要迁移 - --- ============================================================ --- 第5步:验证数据完整性 --- ============================================================ - --- 验证 User 表行数 = user_main 表行数 -SELECT 'User count' AS check_name, COUNT(*) AS cnt FROM `User` -UNION ALL -SELECT 'user_main count', COUNT(*) FROM `user_main` -UNION ALL -SELECT 'user_sensitive_data count', COUNT(*) FROM `user_sensitive_data` -UNION ALL -SELECT 'user_dashou count', COUNT(*) FROM `user_dashou` -UNION ALL -SELECT 'user_shangjia count', COUNT(*) FROM `user_shangjia`; - --- 验证所有扩展表的 user_id 都能匹配到 User 表 -SELECT 'orphan user_dashou' AS check_name, COUNT(*) AS cnt -FROM `user_dashou` ud LEFT JOIN `User` u ON u.`UserUUID` = ud.user_id -WHERE u.`UserUUID` IS NULL -UNION ALL -SELECT 'orphan user_shangjia', COUNT(*) -FROM `user_shangjia` us LEFT JOIN `User` u ON u.`UserUUID` = us.user_id -WHERE u.`UserUUID` IS NULL; - --- ============================================================ --- 第6步:确认无误后删除旧表(手动执行) --- ============================================================ --- DROP TABLE IF EXISTS `user_main`; --- 注意:删除前请确保所有数据已正确迁移! diff --git a/shop/migrations/0001_initial.py b/shop/migrations/0001_initial.py index 97656d0..797a874 100644 --- a/shop/migrations/0001_initial.py +++ b/shop/migrations/0001_initial.py @@ -1,163 +1,92 @@ -# Generated by Django 4.2.27 on 2026-06-17 01:32 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('products', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='DianpuMorenPeizhi', - fields=[ - ('id', models.PositiveSmallIntegerField(default=1, editable=False, primary_key=True, serialize=False)), - ('moren_choucheng_feilv', models.DecimalField(decimal_places=4, default=0.1, max_digits=5, verbose_name='默认订单抽成费率(小数,0.1000=10%)')), - ('moren_tixian_xiane', models.DecimalField(decimal_places=2, default=1000.0, max_digits=10, verbose_name='默认可提现限额')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '店铺默认配置', - 'verbose_name_plural': '店铺默认配置', - 'db_table': 'dianpu_moren_peizhi', - }, - ), - migrations.CreateModel( - name='DianpuShangpinShenheShezhi', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('kaiqi_shenhe', models.BooleanField(default=False, verbose_name='是否开启商品审核模式')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '商品审核模式配置', - 'verbose_name_plural': '商品审核模式配置', - 'db_table': 'dianpu_shangpin_shenhe_shezhi', - }, - ), - migrations.CreateModel( - name='YonghuPingzheng', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='自增主键ID')), - ('yonghu', models.CharField(db_index=True, max_length=7, unique=True, verbose_name='关联用户ID')), - ('zhanghao', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='登录账号')), - ('mima', models.CharField(max_length=255, verbose_name='密码')), - ('is_active', models.BooleanField(default=True, verbose_name='是否启用')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '用户认证凭证', - 'verbose_name_plural': '用户认证凭证', - 'db_table': 'yonghu_pingzheng', - }, - ), - migrations.CreateModel( - name='DianpuShouzhiMeiriTongji', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('dianpu_id', models.PositiveIntegerField(db_index=True, verbose_name='店铺ID')), - ('riqi', models.DateField(db_index=True, verbose_name='统计日期')), - ('nian', models.PositiveSmallIntegerField(verbose_name='年份')), - ('yue', models.PositiveSmallIntegerField(verbose_name='月份')), - ('ri', models.PositiveSmallIntegerField(verbose_name='日')), - ('xiadan_zongliang', models.PositiveIntegerField(default=0, verbose_name='下单总量')), - ('xiadan_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='下单总额')), - ('chengjiao_zongliang', models.PositiveIntegerField(default=0, verbose_name='成交总量')), - ('chengjiao_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='成交总额')), - ('tuikuan_zongliang', models.PositiveIntegerField(default=0, verbose_name='退款总量')), - ('tuikuan_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='退款总额')), - ('shouyi_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='收益总额')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('shangji_fenhong_cishu', models.PositiveIntegerField(default=0, verbose_name='作为上级获得分红次数')), - ('shangji_fenhong_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级获得分红总额')), - ('shangji_id', models.PositiveIntegerField(blank=True, null=True, verbose_name='当天对应的上级店铺ID(若无则为空)')), - ('gei_shangji_cishu', models.PositiveIntegerField(default=0, verbose_name='给上级分红次数')), - ('gei_shangji_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='给上级分红总额')), - ], - options={ - 'verbose_name': '店铺每日收支统计', - 'verbose_name_plural': '店铺每日收支统计', - 'db_table': 'dianpu_shouzhi_meiri_tongji', - 'indexes': [models.Index(fields=['dianpu_id', 'riqi'], name='dianpu_shou_dianpu__bffdb2_idx'), models.Index(fields=['nian', 'yue', 'ri'], name='dianpu_shou_nian_a0f415_idx')], - 'unique_together': {('dianpu_id', 'riqi')}, - }, - ), - migrations.CreateModel( - name='Dianpu', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='店铺自增ID')), - ('dianpu_mingcheng', models.CharField(max_length=50, verbose_name='店铺名称')), - ('dianpu_touxiang', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺头像URL')), - ('lianxi_dianhua', models.CharField(blank=True, max_length=20, null=True, verbose_name='联系电话')), - ('weixinhao', models.CharField(blank=True, max_length=50, null=True, verbose_name='微信号')), - ('zhuangtai', models.IntegerField(choices=[(0, '封禁'), (1, '正常')], default=1, verbose_name='店铺状态')), - ('bangding_yonghushu', models.PositiveIntegerField(default=0, verbose_name='绑定用户总数')), - ('erweima_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺二维码相对URL')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('kaiqi_shouyi_choucheng', models.BooleanField(default=False, verbose_name='是否开启店铺订单收益抽成')), - ('shouyi_choucheng_feilv', models.DecimalField(blank=True, decimal_places=4, default=0.0, max_digits=5, null=True, verbose_name='店铺订单收益抽成费率(小数,如0.1000=10%)')), - ('ketixian_yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='店铺可提现余额')), - ('zhengqu_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='店铺赚取总额')), - ('meiri_tixian_xiane', models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True, verbose_name='店铺每日提现限额')), - ('kaiqi_meiri_xiane', models.BooleanField(default=False, verbose_name='是否开启每日提现限额')), - ('yaoqing_dianpu_id', models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='邀请人店铺ID(上级)')), - ('fenhong_choucheng_feilv', models.DecimalField(blank=True, decimal_places=4, default=0.05, max_digits=5, null=True, verbose_name='上级分红抽成费率(默认5%)')), - ('shangji_fenhong_leiji', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级累计获得分红')), - ('shangji_ketixian_fenhong', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级可提现分红余额')), - ('pingzheng', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.yonghupingzheng', verbose_name='关联用户凭证')), - ], - options={ - 'verbose_name': '平台商家店铺', - 'verbose_name_plural': '平台商家店铺', - 'db_table': 'dianpu', - }, - ), - migrations.CreateModel( - name='YonghuDianpuBangding', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='绑定自增ID')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.dianpu', verbose_name='关联店铺ID')), - ('yonghu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='YonghuID', verbose_name='用户ID')), - ], - options={ - 'verbose_name': '用户店铺绑定关系', - 'verbose_name_plural': '用户店铺绑定关系', - 'db_table': 'yonghu_dianpu_bangding', - 'unique_together': {('yonghu', 'dianpu')}, - }, - ), - migrations.CreateModel( - name='ShangpinLeixingDianpu', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='类型自增ID')), - ('jieshao', models.CharField(max_length=45, verbose_name='类型介绍')), - ('tupian_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='类型图片URL')), - ('paixu', models.IntegerField(default=0, verbose_name='排序权重')), - ('shangjia_zhuangtai', models.BooleanField(default=True, verbose_name='是否上架')), - ('fengjin_zhuangtai', models.BooleanField(default=False, verbose_name='是否封禁')), - ('shenhe_zhuangtai', models.BooleanField(default=False, help_text='True=审核通过,False=未审核', verbose_name='平台审核状态')), - ('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.dianpu', verbose_name='所属店铺')), - ('gonggong_leixing', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.shangpinleixing', verbose_name='映射的公共商品类型')), - ], - options={ - 'verbose_name': '店铺商品类型映射', - 'verbose_name_plural': '店铺商品类型映射', - 'db_table': 'shangdian_shangpin_leixing', - 'indexes': [models.Index(fields=['dianpu'], name='shangdian_s_dianpu__100f93_idx'), models.Index(fields=['gonggong_leixing'], name='shangdian_s_gonggon_88a9a0_idx')], - }, - ), - ] +# Generated by Django 4.2.27 on 2026-04-21 12:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('products', '0010_shangpinleixing_is_cross_enabled'), + ] + + operations = [ + migrations.CreateModel( + name='YonghuPingzheng', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='自增主键ID')), + ('zhanghao', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='登录账号')), + ('mima', models.CharField(max_length=255, verbose_name='密码')), + ('is_active', models.BooleanField(default=True, verbose_name='是否启用')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('yonghu', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='yonghuid', verbose_name='关联用户ID')), + ], + options={ + 'verbose_name': '用户认证凭证', + 'verbose_name_plural': '用户认证凭证', + 'db_table': 'yonghu_pingzheng', + }, + ), + migrations.CreateModel( + name='Dianpu', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='店铺自增ID')), + ('dianpu_mingcheng', models.CharField(max_length=50, verbose_name='店铺名称')), + ('dianpu_touxiang', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺头像URL')), + ('lianxi_dianhua', models.CharField(blank=True, max_length=20, null=True, verbose_name='联系电话')), + ('weixinhao', models.CharField(blank=True, max_length=50, null=True, verbose_name='微信号')), + ('zhuangtai', models.IntegerField(choices=[(0, '封禁'), (1, '正常')], default=1, verbose_name='店铺状态')), + ('bangding_yonghushu', models.PositiveIntegerField(default=0, verbose_name='绑定用户总数')), + ('erweima_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺二维码相对URL')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('pingzheng', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.yonghupingzheng', verbose_name='关联用户凭证')), + ], + options={ + 'verbose_name': '平台商家店铺', + 'verbose_name_plural': '平台商家店铺', + 'db_table': 'dianpu', + }, + ), + migrations.CreateModel( + name='YonghuDianpuBangding', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='绑定自增ID')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.dianpu', verbose_name='关联店铺ID')), + ('yonghu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='yonghuid', verbose_name='用户ID')), + ], + options={ + 'verbose_name': '用户店铺绑定关系', + 'verbose_name_plural': '用户店铺绑定关系', + 'db_table': 'yonghu_dianpu_bangding', + 'unique_together': {('yonghu', 'dianpu')}, + }, + ), + migrations.CreateModel( + name='ShangpinLeixingDianpu', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='类型自增ID')), + ('jieshao', models.CharField(max_length=45, verbose_name='类型介绍')), + ('tupian_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='类型图片URL')), + ('paixu', models.IntegerField(default=0, verbose_name='排序权重')), + ('shangjia_zhuangtai', models.BooleanField(default=True, verbose_name='是否上架')), + ('fengjin_zhuangtai', models.BooleanField(default=False, verbose_name='是否封禁')), + ('shenhe_zhuangtai', models.BooleanField(default=False, help_text='True=审核通过,False=未审核', verbose_name='平台审核状态')), + ('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.dianpu', verbose_name='所属店铺')), + ('gonggong_leixing', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.shangpinleixing', verbose_name='映射的公共商品类型')), + ], + options={ + 'verbose_name': '店铺商品类型映射', + 'verbose_name_plural': '店铺商品类型映射', + 'db_table': 'shangdian_shangpin_leixing', + 'indexes': [models.Index(fields=['dianpu'], name='shangdian_s_dianpu__100f93_idx'), models.Index(fields=['gonggong_leixing'], name='shangdian_s_gonggon_88a9a0_idx')], + }, + ), + ] diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index 222d488..dbd1243 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -1,393 +1,213 @@ -# Generated by Django 4.2.27 on 2026-06-17 01:32 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='OfficialAccountUser', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('unionid', models.CharField(db_index=True, max_length=64, null=True, verbose_name='UnionID')), - ('official_openid', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='服务号OpenID')), - ('subscribe_time', models.DateTimeField(auto_now_add=True, verbose_name='关注时间')), - ('is_subscribed', models.BooleanField(default=True, verbose_name='是否关注')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '服务号用户', - 'verbose_name_plural': '服务号用户', - 'db_table': 'official_account_user', - }, - ), - migrations.CreateModel( - name='Xiugaijilu', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='记录ID')), - ('yonghuid', models.CharField(max_length=7, verbose_name='被修改用户ID')), - ('xiugaiid', models.CharField(max_length=30, verbose_name='修改人账号ID')), - ('leixing', models.IntegerField(verbose_name='被修改用户类型')), - ('xiugaitijiao', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手提现金额')), - ('xiugaitijiaoq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手提现金额')), - ('jifen', models.IntegerField(default=0, verbose_name='修改后的打手积分')), - ('yjifen', models.IntegerField(default=0, verbose_name='修改前的打手积分')), - ('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手押金')), - ('yyajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手押金')), - ('shangjiayue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的商家余额')), - ('shangjiayueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的商家余额')), - ('guanshiyue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的管事余额')), - ('guanshiyueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的管事余额')), - ('huiyuants', models.IntegerField(default=0, verbose_name='添加会员天数')), - ('huiyuan_id', models.CharField(blank=True, max_length=6, null=True, verbose_name='会员ID')), - ('qitashuoming', models.TextField(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='更新时间')), - ], - options={ - 'verbose_name': '修改记录表', - 'verbose_name_plural': '修改记录表', - 'db_table': 'xiugaijilu', - }, - ), - migrations.CreateModel( - name='TixianShenheJilu', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('shenhe_danhao', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='审核单号')), - ('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')), - ('leixing', models.PositiveSmallIntegerField(db_index=True, verbose_name='提现类型')), - ('zhuangtai', models.IntegerField(db_index=True, default=1, verbose_name='审核状态(1审核中2提现成功3提现失败4审核成功5审核失败6待收款)')), - ('shenqing_jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='申请提现金额(扣款额)')), - ('shouxufei', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='手续费')), - ('shijidaozhang', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='实际到账金额')), - ('feilv', models.DecimalField(decimal_places=4, default=0, max_digits=5, verbose_name='手续费率')), - ('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回/失败原因')), - ('bo_hui_ren_id', models.CharField(blank=True, db_index=True, max_length=11, null=True, verbose_name='驳回人用户ID')), - ('shenhe_ren_id', models.CharField(blank=True, db_index=True, max_length=11, null=True, verbose_name='审核通过操作人ID')), - ('tixianjilu_id', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='关联Tixianjilu.id')), - ('tixian_auto_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='最近一次打款单号(非唯一,重试收款会更新)')), - ('fail_reason', models.TextField(blank=True, null=True, verbose_name='微信打款失败原因')), - ('dakuan_mode', models.PositiveSmallIntegerField(db_index=True, default=1, verbose_name='打款方式(1手动2自动)')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '提现审核记录', - 'verbose_name_plural': '提现审核记录', - 'db_table': 'tixian_shenhe_jilu', - 'indexes': [models.Index(fields=['yonghuid', 'zhuangtai'], name='tixian_shen_yonghui_a782e9_idx'), models.Index(fields=['yonghuid', 'leixing'], name='tixian_shen_yonghui_bb44eb_idx'), models.Index(fields=['shenhe_danhao'], name='tixian_shen_shenhe__4a1ca7_idx')], - }, - ), - migrations.CreateModel( - name='Tixianjilu', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='提现记录ID')), - ('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='提现用户ID')), - ('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')), - ('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')), - ('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')), - ('leixing', models.PositiveIntegerField(blank=True, null=True, verbose_name='提现类型1:打手佣金2:管事分红')), - ('zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')), - ('skzhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')), - ('jine', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='提现金额')), - ('zhuangtai', models.IntegerField(blank=True, db_index=True, default=1, null=True, verbose_name='提现状态1:审核2:成功3:失败')), - ('fangshi', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='收款方式1:微信2:支付宝')), - ('shenheid', models.CharField(db_index=True, max_length=11, null=True, verbose_name='审核用户ID')), - ('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回理由')), - ('shenhe_jilu_id', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='关联提现审核记录ID(TixianShenheJilu.id)')), - ('shenhe_danhao', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联提现审核单号')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '提现记录表', - 'verbose_name_plural': '提现记录表', - 'db_table': 'tixianjilu', - 'indexes': [models.Index(fields=['yonghuid'], name='tixianjilu_yonghui_8083bb_idx'), models.Index(fields=['zhuangtai'], name='tixianjilu_zhuangt_839d8e_idx'), models.Index(fields=['shenheid'], name='tixianjilu_shenhei_470e0c_idx'), models.Index(fields=['fangshi'], name='tixianjilu_fangshi_a1e0bc_idx'), models.Index(fields=['shenhe_danhao'], name='tixianjilu_shenhe__7b3e7f_idx'), models.Index(fields=['shenhe_jilu_id'], name='tixianjilu_shenhe__d9355e_idx')], - }, - ), - migrations.CreateModel( - name='TixianAutoRecord', - fields=[ - ('id', models.BigAutoField(primary_key=True, serialize=False)), - ('tixian_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='提现订单号(自定义)')), - ('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')), - ('leixing', models.IntegerField(verbose_name='提现类型 1打手 2管事')), - ('jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='申请提现金额')), - ('shouxufei', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='手续费')), - ('shijidaozhang', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='实际到账金额')), - ('feilv', models.DecimalField(decimal_places=4, max_digits=5, verbose_name='费率')), - ('zhuangtai', models.IntegerField(choices=[(0, '处理中'), (1, '成功'), (2, '失败')], default=0, verbose_name='状态')), - ('wechat_transfer_no', models.CharField(blank=True, max_length=64, null=True, verbose_name='微信转账单号')), - ('wechat_package', models.TextField(blank=True, null=True, verbose_name='微信返回的package_info(JSON)')), - ('fail_reason', models.TextField(blank=True, null=True, verbose_name='失败原因')), - ('shenhe_danhao', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联审核单号')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ], - options={ - 'verbose_name': '自动提现记录', - 'verbose_name_plural': '自动提现记录', - 'db_table': 'tixian_auto_record', - 'indexes': [models.Index(fields=['tixian_id'], name='tixian_auto_tixian__273dac_idx'), models.Index(fields=['yonghuid'], name='tixian_auto_yonghui_61c5b6_idx'), models.Index(fields=['zhuangtai'], name='tixian_auto_zhuangt_24d9fa_idx')], - }, - ), - migrations.CreateModel( - name='RankingRecord', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID')), - ('zhouqi', models.IntegerField(choices=[(1, '日榜'), (2, '月榜')], verbose_name='周期类型')), - ('tongji_date', models.DateField(db_index=True, verbose_name='统计日期')), - ('shenfen', models.IntegerField(choices=[(2, '打手'), (3, '管事'), (4, '商家')], verbose_name='用户身份')), - ('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')), - ('paiming', models.IntegerField(verbose_name='排名')), - ('zhizhi', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='指标值')), - ('nicheng', models.CharField(max_length=50, null=True, verbose_name='用户昵称')), - ('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')), - ('chuangjian_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ], - options={ - 'verbose_name': '排行榜记录', - 'verbose_name_plural': '排行榜记录', - 'db_table': 'ranking_record', - 'indexes': [models.Index(fields=['zhouqi', 'tongji_date', 'shenfen', 'paiming'], name='ranking_rec_zhouqi_fc9f4d_idx'), models.Index(fields=['yonghuid', 'zhouqi'], name='ranking_rec_yonghui_e5be77_idx'), models.Index(fields=['tongji_date'], name='ranking_rec_tongji__820158_idx')], - 'unique_together': {('zhouqi', 'tongji_date', 'shenfen', 'paiming')}, - }, - ), - migrations.CreateModel( - name='UserZuzhang', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='组长邀请码')), - ('fenyong_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='分佣总额(元)')), - ('ketixian_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现金额(元)')), - ('yaoqing_zongshu', models.IntegerField(default=0, verbose_name='邀请总数')), - ('jinri_fenyong', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日分佣(元)')), - ('jinyue_fenyong', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今月分佣(元)')), - ('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='账号状态')), - ('haibao_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='推广海报相对URL')), - ('jinri_tixian', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日提现金额(元)')), - ('last_tixian_time', models.DateTimeField(blank=True, null=True, verbose_name='最后提现时间')), - ('ewai_tixian_xiane', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)')), - ('kaioi_ewai_tixian', models.BooleanField(default=False, verbose_name='开启额外提现')), - ('kaioi_ewai_fenhong', models.BooleanField(default=False, verbose_name='开启额外分红')), - ('ewai_fenhong_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外分红金额(元)')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='zuzhang_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '组长扩展表', - 'verbose_name_plural': '组长扩展表', - 'db_table': 'user_zuzhang', - 'indexes': [models.Index(fields=['user'], name='user_zuzhan_user_id_8064f2_idx'), models.Index(fields=['yaoqingma'], name='user_zuzhan_yaoqing_cba91d_idx')], - }, - ), - migrations.CreateModel( - name='UserShenheguan', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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='是否认证')), - ('shenhe_zongshu', models.IntegerField(default=0, verbose_name='审核总数')), - ('tongguo_zongshu', models.IntegerField(default=0, verbose_name='通过总数')), - ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')), - ('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='赚取总额')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shenheguan_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '审核官扩展表', - 'verbose_name_plural': '审核官扩展表', - 'db_table': 'user_shenheguan', - 'indexes': [models.Index(fields=['user'], name='user_shenhe_user_id_624f19_idx')], - }, - ), - migrations.CreateModel( - name='UserShangjia', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('nicheng', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='商家昵称')), - ('zhuangtai', models.IntegerField(choices=[(0, '待审核'), (1, '正常'), (2, '禁用')], default=1, verbose_name='商家状态')), - ('dianhua', models.CharField(max_length=20, null=True, verbose_name='商家电话')), - ('wechat', models.CharField(max_length=50, null=True, verbose_name='商家微信号')), - ('fabu', models.IntegerField(default=0, verbose_name='发布订单总数量')), - ('tuikuan', models.IntegerField(default=0, verbose_name='退款订单总量')), - ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家余额')), - ('chengjiao', models.IntegerField(default=0, verbose_name='商家成交订单总量')), - ('jinridingdan', models.IntegerField(default=0, verbose_name='商家今日订单总量')), - ('jinriliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今日流水')), - ('jinyuedingdan', models.IntegerField(default=0, verbose_name='商家今月订单总量')), - ('jinyueliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今月流水')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shop_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '商家扩展表', - 'verbose_name_plural': '商家扩展表', - 'db_table': 'user_shangjia', - 'indexes': [models.Index(fields=['user'], name='user_shangj_user_id_823c5c_idx'), models.Index(fields=['nicheng'], name='user_shangj_nicheng_6967bc_idx')], - }, - ), - migrations.CreateModel( - name='UserSensitiveData', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('OpenID', models.CharField(blank=True, db_index=True, max_length=64, null=True, unique=True, verbose_name='微信OpenID')), - ('UnionID', models.CharField(blank=True, db_index=True, max_length=64, null=True, verbose_name='微信UnionID')), - ('Password', models.CharField(blank=True, max_length=200, null=True, verbose_name='本地密码')), - ('PhoneVerified', models.BooleanField(default=False, verbose_name='手机号是否已认证')), - ('Zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')), - ('SKZhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')), - ('IP', models.CharField(blank=True, max_length=50, null=True, verbose_name='来访用户IP')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('UserUUID', models.OneToOneField(db_column='UserUUID', on_delete=django.db.models.deletion.CASCADE, related_name='sensitive_data', to=settings.AUTH_USER_MODEL, verbose_name='关联用户UUID')), - ], - options={ - 'verbose_name': '用户敏感数据', - 'verbose_name_plural': '用户敏感数据', - 'db_table': 'user_sensitive_data', - 'indexes': [models.Index(fields=['OpenID'], name='user_sensit_OpenID_41a0d1_idx'), models.Index(fields=['UnionID'], name='user_sensit_UnionID_bd018d_idx')], - }, - ), - migrations.CreateModel( - name='UserKefu', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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='客服状态')), - ('jinrichuli', models.IntegerField(default=0, verbose_name='今日处理订单数')), - ('jinyuechuli', models.IntegerField(default=0, verbose_name='本月处理订单数')), - ('zongchuli', models.IntegerField(default=0, verbose_name='累计处理订单数')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='kefu_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '客服扩展表', - 'verbose_name_plural': '客服扩展表', - 'db_table': 'user_kefu', - 'indexes': [models.Index(fields=['user'], name='user_kefu_user_id_5fe3a1_idx'), models.Index(fields=['zhuangtai'], name='user_kefu_zhuangt_317491_idx')], - }, - ), - migrations.CreateModel( - name='UserGuanshi', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('yaoqingren', models.CharField(db_index=True, max_length=7, null=True, verbose_name='邀请人ID')), - ('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='邀请码')), - ('invite_qrcode_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='邀请码二维码图片URL')), - ('dianhua', models.CharField(max_length=20, null=True, verbose_name='管事电话')), - ('wechat', models.CharField(max_length=50, null=True, verbose_name='管事微信号')), - ('yaogingshuliang', models.IntegerField(default=0, verbose_name='邀请打手总数')), - ('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='管事状态')), - ('jinrichongzhi', models.IntegerField(default=0, verbose_name='今日充值打手数量')), - ('jinyuechongzhi', models.IntegerField(default=0, verbose_name='今月充值打手数量')), - ('chongzhifenrun', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='充值分佣总额')), - ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('fenhong_yici', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='首次分红金额')), - ('fenghong_erci_enabled', models.BooleanField(default=False, verbose_name='开启二次分红')), - ('fenghong_erci', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='二次分红金额')), - ('last_tixian_date', models.DateField(blank=True, null=True, verbose_name='最近提现日期')), - ('jinritixian_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日已提现金额')), - ('kaioi_ewai_xiane', models.BooleanField(default=False, verbose_name='是否开启额外提现限额')), - ('ewai_xiane', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='guanshi_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '管事扩展表', - 'verbose_name_plural': '管事扩展表', - 'db_table': 'user_guanshi', - 'indexes': [models.Index(fields=['user'], name='user_guansh_user_id_cbadac_idx'), models.Index(fields=['yaoqingma'], name='user_guansh_yaoqing_21d1d4_idx')], - }, - ), - migrations.CreateModel( - name='UserDashou', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')), - ('chenghao', models.CharField(max_length=50, null=True, verbose_name='打手称号')), - ('zhuangtai', models.IntegerField(default=1, verbose_name='打手状态')), - ('zaixianzhuangtai', models.IntegerField(default=1, verbose_name='打手在线状态')), - ('zhanghaozhuangtai', models.IntegerField(default=1, verbose_name='打手账号状态')), - ('jieshao', models.TextField(null=True, verbose_name='打手介绍')), - ('jiedanzongliang', models.IntegerField(default=0, verbose_name='接单总量')), - ('chengjiaozongliang', models.IntegerField(default=0, verbose_name='成交单总量')), - ('tuikuanliang', models.IntegerField(default=0, verbose_name='退款订单总量')), - ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')), - ('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打单赚取总额')), - ('dianhua', models.CharField(max_length=20, null=True, verbose_name='打手电话')), - ('wechat', models.CharField(max_length=50, null=True, verbose_name='打手微信号')), - ('yaoqingren', models.CharField(db_index=True, max_length=7, null=True, verbose_name='邀请人ID')), - ('jinrijiedan', models.IntegerField(default=0, verbose_name='今日接单数量')), - ('jinrishouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日收益金额')), - ('jinyuejiedan', models.IntegerField(default=0, verbose_name='今月接单数量')), - ('jinyueshouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今月收益金额')), - ('jifen', models.IntegerField(default=0, verbose_name='打手积分')), - ('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打手押金')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('last_tixian_date', models.DateField(blank=True, null=True, verbose_name='最近提现日期')), - ('jinritixian_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日已提现金额')), - ('kaioi_ewai_xiane', models.BooleanField(default=False, verbose_name='是否开启额外提现限额')), - ('ewai_xiane', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='额外提现限额(元)')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashou_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '打手扩展表', - 'verbose_name_plural': '打手扩展表', - 'db_table': 'user_dashou', - 'indexes': [models.Index(fields=['user'], name='user_dashou_user_id_cf0410_idx'), models.Index(fields=['yaoqingren'], name='user_dashou_yaoqing_894658_idx')], - }, - ), - migrations.CreateModel( - name='UserBoss', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('nickname', models.CharField(max_length=50, verbose_name='老板昵称')), - ('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='消费总额')), - ('alldingdan', models.IntegerField(default=0, verbose_name='订单总数')), - ('alltui', models.IntegerField(default=0, verbose_name='退款总数')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='boss_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '老板扩展表', - 'verbose_name_plural': '老板扩展表', - 'db_table': 'user_boss', - 'indexes': [models.Index(fields=['user'], name='user_boss_user_id_4e81a4_idx')], - }, - ), - migrations.CreateModel( - name='AdminProfile', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=255, verbose_name='密码')), - ('wechat', models.CharField(blank=True, max_length=50, verbose_name='管理员微信')), - ('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='管理员邀请码')), - ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='admin_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), - ], - options={ - 'verbose_name': '管理员扩展表', - 'verbose_name_plural': '管理员扩展表', - 'db_table': 'admin_profile', - 'indexes': [models.Index(fields=['user'], name='admin_profi_user_id_7d067d_idx')], - }, - ), - ] +# Generated by Django 5.2.10 on 2026-01-14 19:59 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Xiugaijilu', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='记录ID')), + ('yonghuid', models.CharField(max_length=7, verbose_name='被修改用户ID')), + ('xiugaiid', models.CharField(max_length=30, verbose_name='修改人账号ID')), + ('leixing', models.IntegerField(verbose_name='被修改用户类型')), + ('xiugaitijiao', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手提现金额')), + ('xiugaitijiaoq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手提现金额')), + ('jifen', models.IntegerField(default=0, verbose_name='修改后的打手积分')), + ('yjifen', models.IntegerField(default=0, verbose_name='修改前的打手积分')), + ('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的打手押金')), + ('yyajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的打手押金')), + ('shangjiayue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的商家余额')), + ('shangjiayueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的商家余额')), + ('guanshiyue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改后的管事余额')), + ('guanshiyueq', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='修改前的管事余额')), + ('huiyuants', models.IntegerField(default=0, verbose_name='添加会员天数')), + ('huiyuan_id', models.CharField(blank=True, max_length=6, null=True, verbose_name='会员ID')), + ('qitashuoming', models.TextField(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='更新时间')), + ], + options={ + 'verbose_name': '修改记录表', + 'verbose_name_plural': '修改记录表', + 'db_table': 'xiugaijilu', + }, + ), + migrations.CreateModel( + name='UserMain', + fields=[ + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='主键ID')), + ('yonghuid', models.CharField(db_index=True, max_length=7, unique=True, verbose_name='用户ID')), + ('openid', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='微信OpenID')), + ('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')), + ('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')), + ('password', models.CharField(blank=True, max_length=200, null=True, verbose_name='密码')), + ('zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')), + ('skzhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')), + ('ip', models.CharField(blank=True, max_length=50, null=True, verbose_name='来访用户ip')), + ('last_login_time', models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间')), + ('user_type', models.CharField(choices=[('normal', '普通用户'), ('dashou', '打手'), ('shop', '商家'), ('guanshi', '管事'), ('admin', '管理员')], default='normal', max_length=10, verbose_name='用户类型')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'verbose_name': '用户主表', + 'verbose_name_plural': '用户主表', + 'db_table': 'user_main', + 'indexes': [models.Index(fields=['yonghuid'], name='user_main_yonghui_b8b2fe_idx'), models.Index(fields=['openid'], name='user_main_openid_c9edbe_idx'), models.Index(fields=['user_type'], name='user_main_user_ty_8515fd_idx')], + }, + ), + migrations.CreateModel( + name='Tixianjilu', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='提现记录ID')), + ('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='提现用户ID')), + ('avatar', models.CharField(blank=True, max_length=500, null=True, verbose_name='头像')), + ('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='手机号')), + ('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')), + ('leixing', models.PositiveIntegerField(blank=True, null=True, verbose_name='提现类型1:打手佣金2:管事分红')), + ('zhifu', models.CharField(blank=True, max_length=500, null=True, verbose_name='收款码图片')), + ('skzhanghao', models.CharField(blank=True, max_length=11, null=True, verbose_name='收款账号')), + ('jine', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='提现金额')), + ('zhuangtai', models.IntegerField(blank=True, db_index=True, default=1, null=True, verbose_name='提现状态1:审核2:成功3:失败')), + ('fangshi', models.IntegerField(blank=True, db_index=True, null=True, verbose_name='收款方式1:微信2:支付宝')), + ('shenheid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='审核用户ID')), + ('bhliyou', models.TextField(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='更新时间')), + ], + options={ + 'verbose_name': '提现记录表', + 'verbose_name_plural': '提现记录表', + 'db_table': 'tixianjilu', + 'indexes': [models.Index(fields=['yonghuid'], name='tixianjilu_yonghui_8083bb_idx'), models.Index(fields=['zhuangtai'], name='tixianjilu_zhuangt_839d8e_idx'), models.Index(fields=['shenheid'], name='tixianjilu_shenhei_470e0c_idx'), models.Index(fields=['fangshi'], name='tixianjilu_fangshi_a1e0bc_idx')], + }, + ), + migrations.CreateModel( + name='AdminProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=255, verbose_name='密码')), + ('wechat', models.CharField(blank=True, max_length=50, verbose_name='管理员微信')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='admin_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '管理员扩展表', + 'verbose_name_plural': '管理员扩展表', + 'db_table': 'admin_profile', + 'indexes': [models.Index(fields=['user'], name='admin_profi_user_id_7d067d_idx')], + }, + ), + migrations.CreateModel( + name='UserBoss', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nickname', models.CharField(max_length=50, verbose_name='老板昵称')), + ('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='消费总额')), + ('alldingdan', models.IntegerField(default=0, verbose_name='订单总数')), + ('alltui', models.IntegerField(default=0, verbose_name='退款总数')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='boss_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '老板扩展表', + 'verbose_name_plural': '老板扩展表', + 'db_table': 'user_boss', + 'indexes': [models.Index(fields=['user'], name='user_boss_user_id_4e81a4_idx')], + }, + ), + migrations.CreateModel( + name='UserDashou', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nicheng', models.CharField(max_length=50, null=True, verbose_name='打手昵称')), + ('chenghao', models.CharField(max_length=50, null=True, verbose_name='打手称号')), + ('zhuangtai', models.IntegerField(default=1, verbose_name='打手状态')), + ('zaixianzhuangtai', models.IntegerField(default=1, verbose_name='打手在线状态')), + ('zhanghaozhuangtai', models.IntegerField(default=1, verbose_name='打手账号状态')), + ('jieshao', models.TextField(null=True, verbose_name='打手介绍')), + ('jiedanzongliang', models.IntegerField(default=0, verbose_name='接单总量')), + ('chengjiaozongliang', models.IntegerField(default=0, verbose_name='成交单总量')), + ('tuikuanliang', models.IntegerField(default=0, verbose_name='退款订单总量')), + ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')), + ('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打单赚取总额')), + ('dianhua', models.CharField(max_length=20, null=True, verbose_name='打手电话')), + ('wechat', models.CharField(max_length=50, null=True, verbose_name='打手微信号')), + ('yaoqingren', models.CharField(db_index=True, max_length=7, null=True, verbose_name='邀请人ID')), + ('jinrijiedan', models.IntegerField(default=0, verbose_name='今日接单数量')), + ('jinrishouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日收益金额')), + ('jinyuejiedan', models.IntegerField(default=0, verbose_name='今月接单数量')), + ('jinyueshouyi', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今月收益金额')), + ('jifen', models.IntegerField(default=0, verbose_name='打手积分')), + ('yajin', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='打手押金')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashou_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '打手扩展表', + 'verbose_name_plural': '打手扩展表', + 'db_table': 'user_dashou', + 'indexes': [models.Index(fields=['user'], name='user_dashou_user_id_cf0410_idx'), models.Index(fields=['yaoqingren'], name='user_dashou_yaoqing_894658_idx')], + }, + ), + migrations.CreateModel( + name='UserGuanshi', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('yaoqingma', models.CharField(db_index=True, max_length=20, null=True, verbose_name='邀请码')), + ('dianhua', models.CharField(max_length=20, null=True, verbose_name='管事电话')), + ('wechat', models.CharField(max_length=50, null=True, verbose_name='管事微信号')), + ('yaogingshuliang', models.IntegerField(default=0, verbose_name='邀请打手总数')), + ('zhuangtai', models.IntegerField(choices=[(0, '禁用'), (1, '正常')], default=1, verbose_name='管事状态')), + ('jinrichongzhi', models.IntegerField(default=0, verbose_name='今日充值打手数量')), + ('jinyuechongzhi', models.IntegerField(default=0, verbose_name='今月充值打手数量')), + ('chongzhifenrun', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='充值分佣总额')), + ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现余额')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='guanshi_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '管事扩展表', + 'verbose_name_plural': '管事扩展表', + 'db_table': 'user_guanshi', + 'indexes': [models.Index(fields=['user'], name='user_guansh_user_id_cbadac_idx'), models.Index(fields=['yaoqingma'], name='user_guansh_yaoqing_21d1d4_idx')], + }, + ), + migrations.CreateModel( + name='UserShangjia', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nicheng', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='商家昵称')), + ('zhuangtai', models.IntegerField(choices=[(0, '待审核'), (1, '正常'), (2, '禁用')], default=1, verbose_name='商家状态')), + ('dianhua', models.CharField(max_length=20, null=True, verbose_name='商家电话')), + ('wechat', models.CharField(max_length=50, null=True, verbose_name='商家微信号')), + ('fabu', models.IntegerField(default=0, verbose_name='发布订单总数量')), + ('tuikuan', models.IntegerField(default=0, verbose_name='退款订单总量')), + ('yue', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家余额')), + ('chengjiao', models.IntegerField(default=0, verbose_name='商家成交订单总量')), + ('jinridingdan', models.IntegerField(default=0, verbose_name='商家今日订单总量')), + ('jinriliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今日流水')), + ('jinyuedingdan', models.IntegerField(default=0, verbose_name='商家今月订单总量')), + ('jinyueliushui', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='商家今月流水')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shop_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '商家扩展表', + 'verbose_name_plural': '商家扩展表', + 'db_table': 'user_shangjia', + 'indexes': [models.Index(fields=['user'], name='user_shangj_user_id_823c5c_idx'), models.Index(fields=['nicheng'], name='user_shangj_nicheng_6967bc_idx')], + }, + ), + ] diff --git a/users/migrations/0007_userkefu.py b/users/migrations/0007_userkefu.py new file mode 100644 index 0000000..a159dbf --- /dev/null +++ b/users/migrations/0007_userkefu.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.27 on 2026-02-21 14:37 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0006_alter_officialaccountuser_unionid'), + ] + + operations = [ + migrations.CreateModel( + name='UserKefu', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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='客服状态')), + ('jinrichuli', models.IntegerField(default=0, verbose_name='今日处理订单数')), + ('jinyuechuli', models.IntegerField(default=0, verbose_name='本月处理订单数')), + ('zongchuli', models.IntegerField(default=0, verbose_name='累计处理订单数')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='kefu_profile', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')), + ], + options={ + 'verbose_name': '客服扩展表', + 'verbose_name_plural': '客服扩展表', + 'db_table': 'user_kefu', + 'indexes': [models.Index(fields=['user'], name='user_kefu_user_id_5fe3a1_idx'), models.Index(fields=['zhuangtai'], name='user_kefu_zhuangt_317491_idx')], + }, + ), + ] diff --git a/users/models.py b/users/models.py index b35491c..f406f77 100644 --- a/users/models.py +++ b/users/models.py @@ -202,7 +202,7 @@ class UserGuanshi(QModel): class UserZuzhang(QModel): """组长扩展表(与UserMain一对一关联)""" user = models.OneToOneField( - 'gvsdsdk.User', + UserMain, on_delete=models.CASCADE, related_name='zuzhang_profile', verbose_name='关联用户',