PayrollManagementSystem/backend/app/models/base.py
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 FastAPI 六层结构(api/services/repositories/models/schemas/core),
统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存
db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、
登出黑名单(TTL=令牌剩余有效期)、登录日志。

前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。

工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。

测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-09 18:46:36 +08:00

84 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""ORM 基类、命名约定与公共列。
命名约定按 PROJECT_STANDARDS 红线设置,使 Alembic autogenerate 产出的
约束名自动符合规范(唯一键 `uk_表_字段`、索引 `idx_表_字段`),
无需在每张表上手工指定 —— 手工指定迟早会漏。
"""
from datetime import datetime
from sqlalchemy import DateTime, MetaData, text
from sqlalchemy.dialects.mysql import BIGINT, TINYINT
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
# 约束命名模板。`column_0_name` 取约束涉及的第一列,
# 对单列唯一键/索引即为字段名,符合规范的 uk_表_字段 / idx_表_字段
NAMING_CONVENTION = {
"ix": "idx_%(table_name)s_%(column_0_name)s",
"uq": "uk_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s",
"pk": "pk_%(table_name)s",
}
# 主键与外键统一使用无符号 BIGINT(规范模板为 BIGINT UNSIGNED)。
# 两边类型必须完全一致,否则 MySQL 拒绝建立外键约束。
BIGINT_UNSIGNED = BIGINT(unsigned=True)
# 状态类字段统一用 TINYINT(0/1 或少量枚举值)
TINYINT_TYPE = TINYINT
__all__ = ["BIGINT_UNSIGNED", "TINYINT_TYPE", "Base", "TimestampMixin", "mysql_table_args"]
class Base(DeclarativeBase):
"""全部 ORM 模型的基类。"""
metadata = MetaData(naming_convention=NAMING_CONVENTION)
class TimestampMixin:
"""创建时间 / 更新时间公共列。
取默认值交给数据库而非应用层:即使有人直接用 SQL 改数据,时间戳依然准确;
应用层赋值则会被绕过。
`update_time` 必须用 `text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")`
而非 `server_onupdate=func.current_timestamp()` —— 后者只是告诉 ORM
「这个值由服务端维护」,**不会生成 ON UPDATE 子句**,结果是更新时间永远不刷新。
"""
create_time: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=text("CURRENT_TIMESTAMP"),
comment="创建时间",
)
update_time: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
comment="更新时间",
)
def mysql_table_args(comment: str, *extra: object) -> tuple[object, ...]:
"""构造 MySQL 表的公共参数,返回可直接赋给 `__table_args__` 的元组。
显式指定引擎、字符集与排序规则:compose 已把服务端排序规则覆盖为
`utf8mb4_general_ci`,若某处按 MySQL 8.0 默认的 `utf8mb4_0900_ai_ci` 建表,
跨表 JOIN 会报 `Illegal mix of collations`。
:param comment: 表注释(规范红线要求中文 COMMENT)
:param extra: 额外的约束对象(UniqueConstraint / Index 等)
"""
return (
*extra,
{
"mysql_engine": "InnoDB",
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_general_ci",
"comment": comment,
},
)