fix(gerp): 对齐遗留库列名并清理 Asset 表 Flask 期遗留文件列

models.py:CompanyInfo 三个身份证列与 PersonalPage.QQ 补 db_column,匹配库中 *IdCard / Qq 实际列名;新增 0044 仅状态迁移(SeparateDatabaseAndState,零 DDL)记录该对齐。

新增 0045:条件删除 Asset 表 8 个 Flask 期文件资产列(FileName/FileSHA1/FileSize/MimeType/StoragePath/UploaderUUID/RefCount/CreateTime)。这些列 NOT NULL 且无默认值,导致新增企业资产必然报 1364;删除前先解除 UploaderUUID 上的遗留外键 asset_ibfk_1。按 information_schema 判定存在性,幂等可重入。
This commit is contained in:
TermiNexus committed 2026-09-13 22:16:53 +08:00
1 parent 31b0ab885f
commit b5a617d044
3 files changed
+126 -4

No files matched your search

@@ -0,0 +1,46 @@
"""对齐遗留库列名:CompanyInfo 三个身份证列 + PersonalPage.QQ
背景:CompanyInfo / PersonalPage 为 Flask 时期建表,实际列名为 `LegalPersonIdCard` /
`OperatorIdCard` / `FinanceManagerIdCard` / `Qq`(IdCard、Qq),而模型原先按
`IDCard` / `QQ` 推导列名。MySQL 列名大小写不敏感,故线上读写无异常,但
`manage.py gvsdsdk_check_schema` 会报 schema 漂移。
本迁移仅更新 Django 迁移状态以匹配模型新增的 db_column 声明,**不产生任何 DDL**:
真实表列名已与模型 db_column 一致,无需重命名。
"""
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gerp', '0043_remove_userroleworkgroup_position'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='companyinfo',
name='legalpersonidcard',
field=models.CharField(db_column='LegalPersonIdCard', default='', max_length=32, verbose_name='法人身份证'),
),
migrations.AlterField(
model_name='companyinfo',
name='operatoridcard',
field=models.CharField(db_column='OperatorIdCard', default='', max_length=32, verbose_name='经办人身份证'),
),
migrations.AlterField(
model_name='companyinfo',
name='financemanageridcard',
field=models.CharField(db_column='FinanceManagerIdCard', default='', max_length=32, verbose_name='财务身份证'),
),
migrations.AlterField(
model_name='personalpage',
name='qq',
field=models.CharField(db_column='Qq', default='', max_length=256, verbose_name='QQ'),
),
],
database_operations=[],
),
]
@@ -0,0 +1,76 @@
"""清理 Asset 表 Flask 时期遗留的 8 个文件资产列
背景:`Asset` 表最初由 Flask 时期作为文件资产表创建(FileName/FileSHA1/FileSize/
MimeType/StoragePath/UploaderUUID/RefCount/CreateTime),后经 ALTER 追加了 9 个
企业资产列。这 8 个文件列均为 NOT NULL 且无默认值,导致经 Django 新增企业资产时
必然报错 1364(Field 'FileName' doesn't have a default value)。
文件资产已由 `FileAsset` 表承载,此处条件删除这 8 个遗留列:先解除这些列上的遗留
外键(如 UploaderUUID → user.UserUUID),再逐列删除。按 information_schema 判定
存在性,幂等,兼容已清理 / 全新建库 / 上次中断的环境。
反向迁移为尽力恢复:按原类型重加为可空列,不恢复 NOT NULL、唯一索引与外键。
"""
from django.db import migrations
LEGACY_COLUMNS = {
'FileName': 'VARCHAR(256)',
'FileSHA1': 'VARCHAR(40)',
'FileSize': 'INT',
'MimeType': 'VARCHAR(128)',
'StoragePath': 'VARCHAR(512)',
'UploaderUUID': 'BINARY(16)',
'RefCount': 'INT',
'CreateTime': 'DATETIME',
}
def _ExistingColumns(cursor, table):
"""返回表当前实际存在的列名集合。"""
cursor.execute(
'SELECT COLUMN_NAME FROM information_schema.COLUMNS '
'WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
[table],
)
return {row[0] for row in cursor.fetchall()}
def _DropLegacyColumns(apps, schema_editor):
"""先解除遗留列上的外键,再按存在性逐列删除。"""
connection = schema_editor.connection
with connection.cursor() as cursor:
existing = _ExistingColumns(cursor, 'Asset')
cursor.execute(
'SELECT CONSTRAINT_NAME, COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE '
'WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s '
'AND REFERENCED_TABLE_NAME IS NOT NULL',
['Asset'],
)
for constraint, column in cursor.fetchall():
if column in LEGACY_COLUMNS and column in existing:
cursor.execute(f'ALTER TABLE `Asset` DROP FOREIGN KEY `{constraint}`')
for column in LEGACY_COLUMNS:
if column in existing:
cursor.execute(f'ALTER TABLE `Asset` DROP COLUMN `{column}`')
def _RestoreLegacyColumns(apps, schema_editor):
"""反向:按原类型重加为可空列(尽力恢复)。"""
connection = schema_editor.connection
with connection.cursor() as cursor:
existing = _ExistingColumns(cursor, 'Asset')
for column, column_type in LEGACY_COLUMNS.items():
if column not in existing:
cursor.execute(f'ALTER TABLE `Asset` ADD COLUMN `{column}` {column_type} NULL')
class Migration(migrations.Migration):
dependencies = [
('gerp', '0044_align_legacy_column_names'),
]
operations = [
migrations.RunPython(_DropLegacyColumns, _RestoreLegacyColumns),
]
+4 -4
View File
@@ -99,7 +99,7 @@ class CompanyInfo(QModel):
SocialSecurityBank = models.CharField(max_length=128, default='', verbose_name='社保银行')
SocialSecurityAccount = models.CharField(max_length=64, default='', verbose_name='社保账号')
LegalPersonName = models.CharField(max_length=64, default='', verbose_name='法人姓名')
LegalPersonIDCard = models.CharField(max_length=32, default='', verbose_name='法人身份证')
LegalPersonIDCard = models.CharField(max_length=32, default='', db_column='LegalPersonIdCard', verbose_name='法人身份证')
LegalPersonGender = models.CharField(max_length=16, default='', verbose_name='法人性别')
LegalPersonBirthday = models.CharField(max_length=32, default='', verbose_name='法人生日')
LegalPersonNationality = models.CharField(max_length=32, default='', verbose_name='法人国籍')
@@ -111,7 +111,7 @@ class CompanyInfo(QModel):
LegalPersonShareRatio = models.CharField(max_length=16, default='', verbose_name='法人持股比例')
LegalPersonAppointment = models.CharField(max_length=32, default='', verbose_name='法人任职')
OperatorName = models.CharField(max_length=64, default='', verbose_name='经办人姓名')
OperatorIDCard = models.CharField(max_length=32, default='', verbose_name='经办人身份证')
OperatorIDCard = models.CharField(max_length=32, default='', db_column='OperatorIdCard', verbose_name='经办人身份证')
OperatorPhone = models.CharField(max_length=32, default='', verbose_name='经办人电话')
OperatorEmail = models.CharField(max_length=128, default='', verbose_name='经办人邮箱')
OperatorDepartment = models.CharField(max_length=128, default='', verbose_name='经办人部门')
@@ -119,7 +119,7 @@ class CompanyInfo(QModel):
FinanceManagerName = models.CharField(max_length=64, default='', verbose_name='财务负责人')
FinanceManagerPhone = models.CharField(max_length=32, default='', verbose_name='财务电话')
FinanceManagerEmail = models.CharField(max_length=128, default='', verbose_name='财务邮箱')
FinanceManagerIDCard = models.CharField(max_length=32, default='', verbose_name='财务身份证')
FinanceManagerIDCard = models.CharField(max_length=32, default='', db_column='FinanceManagerIdCard', verbose_name='财务身份证')
Remark = models.TextField(default='', verbose_name='备注')
ReviewStatus = models.SmallIntegerField(default=0, verbose_name='审核状态')
ReviewRemark = models.CharField(max_length=512, default='', verbose_name='审核备注')
@@ -980,7 +980,7 @@ class PersonalPage(QModel):
Classification = models.CharField(max_length=256, default='', verbose_name='分类')
Github = models.CharField(max_length=256, default='', verbose_name='GitHub')
Wechat = models.CharField(max_length=256, default='', verbose_name='微信')
QQ = models.CharField(max_length=256, default='', verbose_name='QQ')
QQ = models.CharField(max_length=256, default='', db_column='Qq', verbose_name='QQ')
SelfIntroduction = models.TextField(default='', verbose_name='自我介绍')
ProjectExperience = models.CharField(max_length=256, default='', verbose_name='项目经验')
SatisfiedCustomers = models.CharField(max_length=256, default='', verbose_name='满意客户')