修复数据库初始化和Schema类型匹配问题

This commit is contained in:
zhangtao
2025-09-04 02:50:29 +08:00
parent 3b04534b32
commit 9fda6f3bfc
20 changed files with 3655 additions and 1011 deletions
@@ -1,156 +0,0 @@
# -*- coding: utf-8 -*-
"""
优化数据库Schema设计
修订 ID: optimize_schema_202412
创建时间: 2024-12-19
修订者: AI Assistant
变更内容:
1. 统一字段长度限制
2. 添加缺失的外键索引
3. 优化字典数据缓存策略
4. 统一布尔类型字段命名规范
5. 标准化外键删除策略
"""
from alembic import op
import sqlalchemy as sa
# 修订标识符
revision = 'optimize_schema_202412'
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
"""应用优化变更"""
# 1. 统一用户表字段长度限制
op.alter_column('system_users', 'username',
existing_type=sa.VARCHAR(length=150),
type_=sa.String(length=32),
existing_nullable=False,
existing_comment='用户名/登录账号')
op.alter_column('system_users', 'name',
existing_type=sa.VARCHAR(length=40),
type_=sa.String(length=32),
existing_nullable=False,
existing_comment='姓名')
op.alter_column('system_users', 'email',
existing_type=sa.VARCHAR(length=255),
type_=sa.String(length=64),
existing_nullable=True,
existing_comment='邮箱')
# 2. 添加creator_id索引
op.create_index('ix_system_users_creator_id', 'system_users', ['creator_id'], unique=False)
# 3. 统一角色表字段长度
op.alter_column('system_role', 'name',
existing_type=sa.VARCHAR(length=40),
type_=sa.String(length=32),
existing_nullable=False,
existing_comment='角色名称')
# 4. 统一部门表字段长度
op.alter_column('system_dept', 'name',
existing_type=sa.VARCHAR(length=40),
type_=sa.String(length=32),
existing_nullable=False,
existing_comment='部门名称')
# 5. 优化字典表字段长度
op.alter_column('system_dict_type', 'dict_name',
existing_type=sa.VARCHAR(length=100),
type_=sa.String(length=64),
existing_nullable=False,
existing_comment='字典名称')
op.alter_column('system_dict_data', 'dict_label',
existing_type=sa.VARCHAR(length=100),
type_=sa.String(length=64),
existing_nullable=False,
existing_comment='字典标签')
op.alter_column('system_dict_data', 'dict_value',
existing_type=sa.VARCHAR(length=100),
type_=sa.String(length=64),
existing_nullable=False,
existing_comment='字典键值')
# 6. 添加菜单表索引优化
op.create_index('ix_system_menu_parent_id', 'system_menu', ['parent_id'], unique=False)
op.create_index('ix_system_menu_status', 'system_menu', ['status'], unique=False)
print("数据库Schema优化完成!")
def downgrade() -> None:
"""回滚优化变更"""
# 1. 恢复用户表字段长度
op.alter_column('system_users', 'username',
existing_type=sa.String(length=32),
type_=sa.VARCHAR(length=150),
existing_nullable=False,
existing_comment='用户名/登录账号')
op.alter_column('system_users', 'name',
existing_type=sa.String(length=32),
type_=sa.VARCHAR(length=40),
existing_nullable=False,
existing_comment='姓名')
op.alter_column('system_users', 'email',
existing_type=sa.String(length=64),
type_=sa.VARCHAR(length=255),
existing_nullable=True,
existing_comment='邮箱')
# 2. 移除creator_id索引
op.drop_index('ix_system_users_creator_id', table_name='system_users')
# 3. 恢复角色表字段长度
op.alter_column('system_role', 'name',
existing_type=sa.String(length=32),
type_=sa.VARCHAR(length=40),
existing_nullable=False,
existing_comment='角色名称')
# 4. 恢复部门表字段长度
op.alter_column('system_dept', 'name',
existing_type=sa.String(length=32),
type_=sa.VARCHAR(length=40),
existing_nullable=False,
existing_comment='部门名称')
# 5. 恢复字典表字段长度
op.alter_column('system_dict_type', 'dict_name',
existing_type=sa.String(length=64),
type_=sa.VARCHAR(length=100),
existing_nullable=False,
existing_comment='字典名称')
op.alter_column('system_dict_data', 'dict_label',
existing_type=sa.String(length=64),
type_=sa.VARCHAR(length=100),
existing_nullable=False,
existing_comment='字典标签')
op.alter_column('system_dict_data', 'dict_value',
existing_type=sa.String(length=64),
type_=sa.VARCHAR(length=100),
existing_nullable=False,
existing_comment='字典键值')
# 6. 移除菜单表索引
op.drop_index('ix_system_menu_parent_id', table_name='system_menu')
op.drop_index('ix_system_menu_status', table_name='system_menu')
print("数据库Schema回滚完成!")
@@ -27,7 +27,7 @@ class JobModel(CreatorMixin):
start_date: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, comment='开始时间')
end_date: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, comment='结束时间')
job_logs: Mapped[Optional[list['JobLogModel']]] = relationship(back_populates="job", lazy="select")
job_logs: Mapped[Optional[list['JobLogModel']]] = relationship(back_populates="job", lazy="selectin")
class JobLogModel(MappedBase):
@@ -50,4 +50,4 @@ class JobLogModel(MappedBase):
job_id: Mapped[Optional[int]] = mapped_column(ForeignKey('monitor_job.id'), comment='任务ID')
# 任务关联关系
job: Mapped[Optional["JobModel"]] = relationship(back_populates="job_logs", lazy="select")
job: Mapped[Optional["JobModel"]] = relationship(back_populates="job_logs", lazy="selectin")
@@ -21,10 +21,10 @@ class DeptModel(ModelMixin):
parent: Mapped[Optional["DeptModel"]] = relationship("DeptModel", cascade="all, delete-orphan", uselist=False)
# 角色关联关系
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_role_depts", back_populates="depts", lazy="select")
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_role_depts", back_populates="depts", lazy="selectin")
# 用户关联关系
users: Mapped[List["UserModel"]] = relationship(back_populates="dept", lazy="select")
users: Mapped[List["UserModel"]] = relationship(back_populates="dept", lazy="selectin")
# code: Mapped[Optional[str]] = mapped_column(String(20),nullable=True,unique=True,comment="部门编码")
# leader_id: Mapped[Optional[int]] = mapped_column(Integer,nullable=True,comment="负责人ID")
@@ -17,7 +17,7 @@ class DictTypeModel(CreatorMixin):
dict_name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment='字典名称')
dict_type: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment='字典类型')
dict_datas: Mapped[List["DictDataModel"]] = relationship(back_populates="dict_type_rel", lazy="select")
dict_datas: Mapped[List["DictDataModel"]] = relationship(back_populates="dict_type_rel", lazy="selectin")
class DictDataModel(CreatorMixin):
@@ -38,5 +38,5 @@ class DictDataModel(CreatorMixin):
dict_type_id: Mapped[Optional[int]] = mapped_column(ForeignKey('system_dict_type.id'), nullable=True, comment='字典类型ID')
# 字典类型关联关系
dict_type_rel: Mapped[Optional["DictTypeModel"]] = relationship(back_populates="dict_datas", lazy="select")
dict_type_rel: Mapped[Optional["DictTypeModel"]] = relationship(back_populates="dict_datas", lazy="selectin")
@@ -45,7 +45,7 @@ class MenuModel(ModelMixin):
parent: Mapped[Optional['MenuModel']] = relationship(cascade='all, delete-orphan', primaryjoin="MenuModel.parent_id == MenuModel.id", uselist=False)
# 角色关联关系
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_role_menus", back_populates="menus", lazy="select")
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_role_menus", back_populates="menus", lazy="selectin")
# link: Mapped[Optional[str]] = mapped_column(String(255), comment='外链地址')
# iframe: Mapped[Optional[str]] = mapped_column(String(255), comment='内嵌iframe地址')
@@ -23,7 +23,7 @@ class PositionModel(CreatorMixin):
order: Mapped[int] = mapped_column(Integer, nullable=False, default=1, comment="显示排序")
# 用户关联关系
users: Mapped[List["UserModel"]] = relationship(secondary="system_user_positions", back_populates="positions", lazy="select")
users: Mapped[List["UserModel"]] = relationship(secondary="system_user_positions", back_populates="positions", lazy="selectin")
# users: Mapped[List["UserModel"]] = relationship(secondary="system_user_positions", back_populates="positions", lazy="select")
@@ -79,7 +79,7 @@ class RoleModel(CreatorMixin):
order: Mapped[int] = mapped_column(Integer, nullable=False, default=999, comment="显示排序")
data_scope: Mapped[int] = mapped_column(Integer, nullable=False, default=1, comment="数据权限范围")
menus: Mapped[List["MenuModel"]] = relationship(secondary="system_role_menus", back_populates="roles", lazy="select")
depts: Mapped[List["DeptModel"]] = relationship(secondary="system_role_depts", back_populates="roles", lazy="select")
users: Mapped[List["UserModel"]] = relationship(secondary="system_user_roles", back_populates="roles", lazy="select")
menus: Mapped[List["MenuModel"]] = relationship(secondary="system_role_menus", back_populates="roles", lazy="selectin")
depts: Mapped[List["DeptModel"]] = relationship(secondary="system_role_depts", back_populates="roles", lazy="selectin")
users: Mapped[List["UserModel"]] = relationship(secondary="system_user_roles", back_populates="roles", lazy="selectin")
@@ -74,12 +74,12 @@ class UserModel(CreatorMixin):
name: Mapped[str] = mapped_column(String(32),nullable=False,comment="昵称")
mobile: Mapped[Optional[str]] = mapped_column(String(20),nullable=True,unique=True,comment="手机号")
email: Mapped[Optional[str]] = mapped_column(String(64),nullable=True,unique=True,comment="邮箱")
gender: Mapped[Optional[int]] = mapped_column(Integer,default=2,nullable=True,comment="性别(0:男 1:女 2:未知)")
gender: Mapped[Optional[str]] = mapped_column(String(1), default='0',nullable=True,comment="性别(0:男 1:女 2:未知)")
avatar: Mapped[Optional[str]] = mapped_column(String(500),nullable=True,comment="头像URL地址")
is_superuser: Mapped[bool] = mapped_column(Boolean,default=False,nullable=False,comment="是否超管")
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True),nullable=True,comment="最后登录时间")
dept_id: Mapped[Optional[int]] = mapped_column(Integer,ForeignKey('system_dept.id', ondelete="SET NULL", onupdate="CASCADE"),nullable=True, index=True, comment="部门ID")
dept: Mapped[Optional["DeptModel"]] = relationship(back_populates="users",foreign_keys=[dept_id],lazy="select")
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_user_roles",back_populates="users",lazy="select")
positions: Mapped[List["PositionModel"]] = relationship(secondary="system_user_positions",back_populates="users")
dept: Mapped[Optional["DeptModel"]] = relationship(back_populates="users",foreign_keys=[dept_id],lazy="selectin")
roles: Mapped[List["RoleModel"]] = relationship(secondary="system_user_roles",back_populates="users",lazy="selectin")
positions: Mapped[List["PositionModel"]] = relationship(secondary="system_user_positions",back_populates="users",lazy="selectin")
+4 -1
View File
@@ -278,7 +278,10 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
if 3 in data_scopes:
# 3、本部门及以下数据
dept_objs = await CRUDBase(DeptModel, self.auth).list()
# 直接查询部门表,避免递归调用CRUD
dept_sql = select(DeptModel)
dept_result = await self.db.execute(dept_sql)
dept_objs = dept_result.scalars().all()
id_map = get_child_id_map(dept_objs)
dept_child_ids = get_child_recursion(id=self.current_user.dept_id, id_map=id_map)
for child_id in dept_child_ids:
+3 -1
View File
@@ -4,11 +4,13 @@
提供跨数据库兼容的基础模型类和类型装饰器
"""
import uuid
from datetime import datetime
from typing import Optional, Dict, Any
import json
from sqlalchemy import Boolean, String, Integer, DateTime, ForeignKey, Text, BigInteger
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, declared_attr, mapped_column
from sqlalchemy.types import TypeDecorator
@@ -61,7 +63,7 @@ class CreatorMixin(ModelMixin):
return relationship(
"UserModel",
primaryjoin=f"{cls.__name__}.creator_id == UserModel.id",
lazy="select",
lazy="selectin",
foreign_keys=[cls.creator_id],
viewonly=True
)
@@ -8,11 +8,7 @@
"dept_id": 1
},
{
"role_id": 3,
"dept_id": 1
},
{
"role_id": 3,
"role_id": 2,
"dept_id": 6
}
]
+86 -86
View File
@@ -1,346 +1,346 @@
[
{
"role_id": 2,
"role_id": 1,
"menu_id": 1
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 2
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 3
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 4
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 5
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 6
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 7
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 8
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 9
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 10
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 11
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 12
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 13
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 14
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 15
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 16
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 17
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 18
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 19
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 20
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 21
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 22
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 23
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 24
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 25
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 26
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 27
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 28
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 29
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 30
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 31
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 32
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 33
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 34
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 35
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 36
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 37
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 38
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 39
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 40
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 41
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 42
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 43
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 44
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 45
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 46
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 47
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 48
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 49
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 50
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 51
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 52
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 53
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 54
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 55
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 56
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 57
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 58
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 59
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 60
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 61
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 62
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 63
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 64
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 65
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 66
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 67
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 68
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 69
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 70
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 71
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 72
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 73
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 74
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 75
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 76
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 77
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 78
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 79
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 80
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 81
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 82
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 83
},
{
"role_id": 2,
"role_id": 1,
"menu_id": 84
},
{
"role_id": 3,
"role_id": 2,
"menu_id": 1
},
{
"role_id": 3,
"role_id": 2,
"menu_id": 2
}
]
@@ -8,7 +8,7 @@
"position_id": 7
},
{
"user_id": 2,
"user_id": 3,
"position_id": 1
}
]
+1
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import uuid
import json
from pathlib import Path
from typing import Dict, List