Skip to content

数据库设计

本章详细描述 的数据库设计,包括核心表结构、通用字段规范、ER 关系和命名约定。

设计原则

所有业务表继承 base_model 基类,自动获得 6 个通用字段(id / create_user / create_time / update_user / update_time / is_delete)。表名使用 DB_PREFIX 前缀(默认 fastapi_),软删除通过 is_delete 字段实现。

通用字段规范

所有业务表共享以下通用字段,由 base_model 基类自动提供:

字段名类型约束默认值说明
idINTPRIMARY KEY, AUTO_INCREMENT主键自增
create_userVARCHAR(50)NULLABLENULL创建人(realname)
create_timeDATETIMENOT NULLCURRENT_TIMESTAMP创建时间(UTC)
update_userVARCHAR(50)NULLABLENULL更新人(realname)
update_timeDATETIMENOT NULLCURRENT_TIMESTAMP更新时间(UTC,onupdate)
is_deleteINTNOT NULL, INDEX0软删除:0=正常,1=已删除

核心表结构

系统管理模块

用户表 fastapi_user

字段名类型约束说明
idINTPK用户ID
usernameVARCHAR(50)NOT NULL, UNIQUE登录账号
passwordVARCHAR(200)NOT NULL密码(bcrypt 哈希)
saltVARCHAR(50)NULLABLE密码盐值
realnameVARCHAR(50)NULLABLE真实姓名
avatarVARCHAR(500)NULLABLE头像地址
emailVARCHAR(100)NULLABLE邮箱
mobileVARCHAR(20)NULLABLE手机号
genderINTDEFAULT 0性别:0=未知 1=男 2=女
dept_idINTNULLABLE, INDEX部门ID(外键)
position_idINTNULLABLE, INDEX岗位ID(外键)
level_idINTNULLABLE, INDEX职级ID(外键)
statusINTDEFAULT 1状态:1=正常 2=禁用
create_userVARCHAR(50)NULLABLE通用字段
create_timeDATETIMEDEFAULT NOW()通用字段
update_userVARCHAR(50)NULLABLE通用字段
update_timeDATETIMEDEFAULT NOW()通用字段
is_deleteINTDEFAULT 0, INDEX通用字段

角色表 fastapi_role

字段名类型约束说明
idINTPK角色ID
nameVARCHAR(100)NOT NULL, INDEX角色名称
codeVARCHAR(100)NOT NULL, UNIQUE角色编码
statusINTDEFAULT 1状态:1=正常 2=禁用
sortINTDEFAULT 0排序
remarkVARCHAR(500)NULLABLE备注
+ 通用字段

菜单表 fastapi_menu

字段名类型约束说明
idINTPK菜单ID
parent_idINTDEFAULT 0, INDEX父菜单ID(0=顶级)
nameVARCHAR(100)NOT NULL菜单名称
typeINTNOT NULL类型:1=目录 2=菜单 3=按钮
pathVARCHAR(200)NULLABLE路由路径
componentVARCHAR(200)NULLABLE前端组件路径
permissionVARCHAR(200)NULLABLE权限标识(如 sys:user:add)
iconVARCHAR(100)NULLABLE菜单图标
sortINTDEFAULT 0排序
visibleINTDEFAULT 1是否可见:1=是 2=否
statusINTDEFAULT 1状态:1=正常 2=禁用
+ 通用字段

部门表 fastapi_dept

字段名类型约束说明
idINTPK部门ID
parent_idINTDEFAULT 0, INDEX父部门ID(0=顶级)
nameVARCHAR(100)NOT NULL部门名称
leaderVARCHAR(50)NULLABLE负责人
mobileVARCHAR(20)NULLABLE联系电话
emailVARCHAR(100)NULLABLE邮箱
sortINTDEFAULT 0排序
statusINTDEFAULT 1状态:1=正常 2=禁用
+ 通用字段

岗位表 fastapi_position

字段名类型约束说明
idINTPK岗位ID
nameVARCHAR(255)NOT NULL, INDEX岗位名称
statusINTDEFAULT 0, INDEX状态:1=在用 2=停用
sortINTDEFAULT 0排序
+ 通用字段

职级表 fastapi_level

字段名类型约束说明
idINTPK职级ID
nameVARCHAR(255)NOT NULL, INDEX职级名称
statusINTDEFAULT 0, INDEX状态:1=在用 2=停用
sortINTDEFAULT 0排序
+ 通用字段

关联表

用户角色关联表 fastapi_user_role

字段名类型约束说明
idINTPK主键
user_idINTNOT NULL, INDEX用户ID
role_idINTNOT NULL, INDEX角色ID
+ 通用字段

角色菜单关联表 fastapi_role_menu

字段名类型约束说明
idINTPK主键
role_idINTNOT NULL, INDEX角色ID
menu_idINTNOT NULL, INDEX菜单ID
+ 通用字段

数据字典模块

字典表 fastapi_dict

字段名类型约束说明
idINTPK字典ID
nameVARCHAR(200)NOT NULL字典名称
codeVARCHAR(200)NOT NULL, UNIQUE字典编码
statusINTDEFAULT 1状态
remarkVARCHAR(500)NULLABLE备注
+ 通用字段

字典项表 fastapi_dict_item

字段名类型约束说明
idINTPK字典项ID
dict_codeVARCHAR(200)NOT NULL, INDEX所属字典编码
labelVARCHAR(200)NOT NULL显示名
valueVARCHAR(200)NOT NULL字典值
sortINTDEFAULT 0排序
statusINTDEFAULT 1状态
remarkVARCHAR(500)NULLABLE备注
+ 通用字段

内容管理模块

文章表 fastapi_article

字段名类型约束说明
idINTPK文章ID
category_idINTNULLABLE, INDEX分类ID
titleVARCHAR(200)NOT NULL文章标题
coverVARCHAR(500)NULLABLE封面图片
summaryVARCHAR(500)NULLABLE摘要
contentTEXTNULLABLE正文(富文本)
authorVARCHAR(50)NULLABLE作者
sourceVARCHAR(200)NULLABLE来源
view_countINTDEFAULT 0浏览次数
is_topINTDEFAULT 0是否置顶
statusINTDEFAULT 1状态
sortINTDEFAULT 0排序
+ 通用字段

文章分类表 fastapi_category

字段名类型约束说明
idINTPK分类ID
parent_idINTDEFAULT 0, INDEX父分类ID
nameVARCHAR(100)NOT NULL分类名称
sortINTDEFAULT 0排序
statusINTDEFAULT 1状态
+ 通用字段

通知公告表 fastapi_notice

字段名类型约束说明
idINTPK通知ID
titleVARCHAR(200)NOT NULL标题
contentTEXTNULLABLE内容
typeINTDEFAULT 1类型:1=通知 2=公告
statusINTDEFAULT 1状态
+ 通用字段

日志模块

登录日志表 fastapi_login_log

字段名类型约束说明
idINTPK日志ID
usernameVARCHAR(50)NULLABLE登录账号
ipVARCHAR(50)NULLABLE登录IP
locationVARCHAR(200)NULLABLE登录地点
browserVARCHAR(100)NULLABLE浏览器
osVARCHAR(100)NULLABLE操作系统
statusINTDEFAULT 1状态:1=成功 2=失败
msgVARCHAR(500)NULLABLE提示消息
+ 通用字段

操作日志表 fastapi_operation_log

字段名类型约束说明
idINTPK日志ID
usernameVARCHAR(50)NULLABLE操作人
moduleVARCHAR(100)NULLABLE操作模块
actionVARCHAR(100)NULLABLE操作动作
methodVARCHAR(10)NULLABLE请求方法
urlVARCHAR(500)NULLABLE请求URL
paramsTEXTNULLABLE请求参数
ipVARCHAR(50)NULLABLE操作IP
statusINTDEFAULT 1状态
durationINTNULLABLE耗时(毫秒)
+ 通用字段

定时任务模块

任务表 fastapi_job

字段名类型约束说明
idINTPK任务ID
nameVARCHAR(200)NOT NULL任务名称
job_groupVARCHAR(100)NULLABLE任务分组
invoke_targetVARCHAR(500)NOT NULL调用目标
cron_expressionVARCHAR(200)NOT NULLCron 表达式
misfire_policyINTDEFAULT 3执行策略
concurrentINTDEFAULT 0是否并发
statusINTDEFAULT 1状态:1=运行 2=暂停
remarkVARCHAR(500)NULLABLE备注
+ 通用字段

任务日志表 fastapi_job_log

字段名类型约束说明
idINTPK日志ID
job_idINTNOT NULL, INDEX任务ID
job_nameVARCHAR(200)NULLABLE任务名称
invoke_targetVARCHAR(500)NULLABLE调用目标
job_messageVARCHAR(500)NULLABLE执行结果
statusINTDEFAULT 1状态
exception_infoTEXTNULLABLE异常信息
start_timeDATETIMENULLABLE开始时间
end_timeDATETIMENULLABLE结束时间
+ 通用字段

ER 关系图

┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ fastapi │ │ fastapi │ │ fastapi │
│ _user │ │ _user_role │ │ _role │
│──────────────│ │──────────────│ │──────────────│
│ id (PK) │←───│ user_id (FK) │ │ id (PK) │
│ username │ │ role_id (FK) │───→│ name │
│ password │ └──────────────┘ │ code │
│ realname │ └──────┬───────┘
│ dept_id (FK) │───→┌──────────────┐ │
│ position_id │ │ fastapi │ │
│ (FK) │ │ _dept │ │
│ level_id (FK)│ │──────────────│ │
│ │ │ id (PK) │ │
│ │ │ parent_id │ │
│ │ │ name │ │
│ │ └──────────────┘ │
│ │ │
│ │ ┌──────────────┐ │
│ │ │ fastapi │ │
│ │ │ _role_menu │ │
│ │ │──────────────│ │
│ │ │ role_id (FK) │←───────────┘
│ │ │ menu_id (FK) │───→┌──────────────┐
│ │ └──────────────┘ │ fastapi │
└──────────────┘ │ _menu │
 │──────────────│
┌──────────────┐ │ id (PK) │
│ fastapi │ │ parent_id │
│ _position │ │ name │
│──────────────│ │ permission │
│ id (PK) │ │ type │
│ name │ └──────────────┘
└──────────────┘

┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ fastapi │ │ fastapi │ │ fastapi │
│ _dict │ │ _dict_item │ │ _config │
│──────────────│ │──────────────│ │──────────────│
│ id (PK) │←───│ dict_code │ │ id (PK) │
│ name │ │ label │ │ name │
│ code │ │ value │ │ code │
└──────────────┘ └──────────────┘ └──────────────┘

┌──────────────┐ ┌──────────────┐
│ fastapi │ │ fastapi │
│ _article │ │ _category │
│──────────────│ │──────────────│
│ id (PK) │ │ id (PK) │
│ category_id │───→│ parent_id │
│ title │ │ name │
│ content │ └──────────────┘
└──────────────┘

命名约定

约定项规范示例
表名{DB_PREFIX}{module_name}fastapi_position
字段名小写下划线(snake_case)create_userdict_code
主键id,INT,AUTO_INCREMENT
外键{关联表名}_iduser_iddept_id
索引外键字段、高频查询字段statususername
布尔状态INT 类型,0/1 或 1/2is_delete: 0/1status: 1/2
时间字段DATETIME 类型create_timeupdate_time

外键约束

不使用数据库层面的外键约束(FOREIGN KEY),而是通过应用层代码维护引用完整性。这样做的好处是:简化跨库迁移、避免级联删除风险、提升写入性能。删除关联数据时,由 Service 层的 _before_delete 钩子检查引用关系。

多数据库驱动支持

通过 DB_DRIVER 环境变量切换数据库驱动,src/core/database.pybuild_engine() 函数按驱动构建方言感知的 SQLAlchemy 引擎:

驱动DB_DRIVER连接 URL 格式特殊处理
MySQLmysql(默认)mysql+pymysql://user:pass@host:3306/dbCLIENT.FOUND_ROWS 避免重复提交时 rowcount=0 误判
PostgreSQLpostgresqlpostgresql+psycopg://user:pass@host:5432/dboptions="-c timezone=UTC" 确保 naive UTC 一致
SQL Servermssqlmssql+pymssql://user:pass@host:1433/dblogin_timeout 连接超时
SQLitesqlitesqlite:///./db.sqlite3check_same_thread=False,跳过连接池参数
Oracleoracleoracle+oracledb://user:pass@host:1521/dbthin 模式,UTF-8 编码
python
# src/core/database.py - 引擎构建核心逻辑
# ============================================================
# 引擎构建
# ============================================================
def build_engine(driver: str | None = None, database_url: str | None = None):
    """按 DB_DRIVER 构建方言感知的 SQLAlchemy 引擎

    - mysql:      connect_timeout + client_flag FOUND_ROWS(UPDATE rowcount=匹配行数,
                  避免重复提交相同内容时 rowcount=0 被误判"更新失败");pymysql 懒加载
    - postgresql: connect_timeout + options="-c timezone=UTC"(aware-UTC 默认值落库为
                  naive UTC,与 base_model.py 的 naive-UTC 读取约定一致,防时区偏移)
    - mssql:      login_timeout(pymssql 连接超时参数)
    - oracle:     oracledb 懒加载(thin 模式,UTF-8);connect_args 传 encoding/nencoding
    - sqlite:     仅 check_same_thread=False,跳过连接池容量参数(QueuePool 默认即可)
    """
    from core.config import (
        SQLALCHEMY_DATABASE_URL, DB_DRIVER, FASTAPI_DEBUG,
        DB_POOL_SIZE, DB_MAX_OVERFLOW, DB_POOL_RECYCLE,
        DB_POOL_TIMEOUT, DB_CONNECT_TIMEOUT,
    )
    from core.config.database import _normalize_driver

    drv = _normalize_driver(driver or DB_DRIVER)
    url = database_url or SQLALCHEMY_DATABASE_URL
    common = {"pool_pre_ping": True, "echo": FASTAPI_DEBUG, "echo_pool": FASTAPI_DEBUG}

    if drv == 'mysql':
        # 懒加载:仅 mysql 分支需要 pymysql,sqlite/pg/mssql 无需安装
        from pymysql.constants import CLIENT
        return create_engine(
            url,
            pool_size=DB_POOL_SIZE,
            pool_recycle=DB_POOL_RECYCLE,
            pool_timeout=DB_POOL_TIMEOUT,
            max_overflow=DB_MAX_OVERFLOW,
            pool_use_lifo=True,
            connect_args={
                "connect_timeout": DB_CONNECT_TIMEOUT,
                "client_flag": CLIENT.FOUND_ROWS,
            },
            **common,
        )

    if drv == 'postgresql':
        return create_engine(
            url,
            pool_size=DB_POOL_SIZE,
            pool_recycle=DB_POOL_RECYCLE,
            pool_timeout=DB_POOL_TIMEOUT,
            max_overflow=DB_MAX_OVERFLOW,
            pool_use_lifo=True,
            connect_args={
                "connect_timeout": DB_CONNECT_TIMEOUT,
                "options": "-c timezone=UTC",
            },
            **common,
        )

    if drv == 'mssql':
        return create_engine(
            url,
            pool_size=DB_POOL_SIZE,
            pool_recycle=DB_POOL_RECYCLE,
            pool_timeout=DB_POOL_TIMEOUT,
            max_overflow=DB_MAX_OVERFLOW,
            pool_use_lifo=True,
            connect_args={"login_timeout": DB_CONNECT_TIMEOUT},
            **common,
        )

    if drv == 'oracle':
        # 懒加载:仅 oracle 分支需要 oracledb(thin 模式,无需 Oracle Instant Client)
        import oracledb  # noqa: F401
        return create_engine(
            url,
            pool_size=DB_POOL_SIZE,
            pool_recycle=DB_POOL_RECYCLE,
            pool_timeout=DB_POOL_TIMEOUT,
            max_overflow=DB_MAX_OVERFLOW,
            pool_use_lifo=True,
            connect_args={"encoding": "UTF-8", "nencoding": "UTF-8"},
            **common,
        )

    # sqlite
    return create_engine(url, connect_args={"check_same_thread": False}, **common)

业务代码无需修改,SQLAlchemy 自动处理方言差异(如分页语法、自增策略等)。

请求级会话管理

python
# src/core/database.py
# 请求级数据库 session 上下文变量
_db_ctx: ContextVar = ContextVar('_db_ctx')


def get_db_session():
    """获取当前请求的数据库 session(仅限请求上下文内调用)"""
    try:
        return _db_ctx.get()
    except LookupError:
        raise RuntimeError(
            "get_db_session() 只能在请求上下文内调用(缺少 db_session 中间件注入)。"
            "后台线程/启动阶段请使用 SessionLocal() 创建独立会话。"
        ) from None


# ============================================================
# 独立提交
# ============================================================
def commit_independently(obj):
    """
    使用独立session提交对象,不受请求事务影响

    日志等审计数据无论业务成功或失败都应持久化,
    因此需要绕过 AOP 事务中间件的 commit/rollback 机制。
    """
    independent_session = SessionLocal()
    try:
        independent_session.add(obj)
        independent_session.commit()
    except Exception:
        independent_session.rollback()
        raise
    finally:
        independent_session.close()

约束命名约定

database.py 中定义了自定义约束命名约定,索引前缀使用 idx_ 而非默认的 ix_

python
convention = {
 "ix": "idx_%(table_name)s_%(column_0_name)s",
 "uq": "uq_%(table_name)s_%(column_0_name)s",
 "ck": "ck_%(table_name)s_%(constraint_name)s",
 "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
 "pk": "pk_%(table_name)s",
}
metadata = MetaData(naming_convention=convention)
Base = declarative_base(metadata=metadata)

总结

数据库设计以「通用字段 + 软删除 + 应用层外键」为核心策略。所有业务表继承 base_model 获得统一的通用字段,通过 is_delete 实现软删除,通过应用层代码维护引用完整性。核心表覆盖系统管理(用户/角色/菜单/部门/岗位/职级)、数据字典、内容管理(文章/分类/通知)、日志管理、定时任务等功能域。多数据库驱动通过 src/core/database.pybuild_engine() 按方言构建引擎,业务代码与具体数据库解耦。请求级会话通过 contextvars + db_session_middleware 实现,审计数据可通过 commit_independently() 绕过请求事务独立提交。

小蚂蚁云团队 · 提供技术支持