修复 users 审计字段缺失导致 API 启动失败

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-05-01 12:25:16 +08:00
parent a2d32613ec
commit c051c2dbf8
3 changed files with 115 additions and 0 deletions
+9
View File
@@ -949,6 +949,15 @@
- 若检测到 `users` 表存在且仅有 `id`、缺少 `user_id`,自动执行 `ALTER TABLE users RENAME COLUMN id TO user_id`,再继续 `create_all/seed`
- 对已对齐 `users.user_id` 的库,该逻辑不产生任何改动。
## users 审计列兼容口径(2026-05-01
- 用户审计字段工程约定为 `users.create_user``users.update_user`
- 为兼容历史库并避免启动 seed 阶段出现 `UndefinedColumn: users.create_user/update_user``init_db()` 在 PostgreSQL 下新增启动期兼容逻辑:
- 若存在 `create_by/created_by`,自动重命名为 `create_user`
- 若存在 `update_by/updated_by`,自动重命名为 `update_user`
- 若目标列仍缺失,自动补齐 nullable 列:`create_user VARCHAR(64)``update_user VARCHAR(64)`
- 对已对齐审计字段的库,该逻辑不产生任何改动。
## GitHub Actions 发布分支口径(2026-05-01
- `.github/workflows/main.yml` 的自动发布触发分支已切换为 `dev`
+82
View File
@@ -102,6 +102,87 @@ def _ensure_user_timestamp_column_compatibility() -> None:
"Detected legacy users.update_date; renamed to users.updated_at for schema compatibility.",
)
def _rename_user_column_if_needed(
connection: Any,
*,
column_names: set[str],
target_column: str,
legacy_candidates: tuple[str, ...],
) -> set[str]:
if target_column in column_names:
return column_names
legacy_column = next(
(candidate for candidate in legacy_candidates if candidate in column_names),
None,
)
if not legacy_column:
return column_names
connection.execute(
text(f"ALTER TABLE users RENAME COLUMN {legacy_column} TO {target_column}"),
)
logger.warning(
"Detected legacy users.%s; renamed to users.%s for schema compatibility.",
legacy_column,
target_column,
)
column_names.remove(legacy_column)
column_names.add(target_column)
return column_names
def _ensure_user_audit_column_compatibility() -> None:
"""
Keep `users` audit columns aligned with the current ORM mapping.
Some legacy deployments use `create_by` / `created_by` and
`update_by` / `updated_by`, or may miss these nullable columns.
"""
if not database_url.startswith("postgresql"):
return
schema = settings.resolved_db_schema
with engine.begin() as connection:
db_inspector = inspect(connection)
if not db_inspector.has_table("users", schema=schema):
return
column_names = {
column["name"]
for column in db_inspector.get_columns("users", schema=schema)
}
column_names = _rename_user_column_if_needed(
connection,
column_names=column_names,
target_column="create_user",
legacy_candidates=("create_by", "created_by"),
)
column_names = _rename_user_column_if_needed(
connection,
column_names=column_names,
target_column="update_user",
legacy_candidates=("update_by", "updated_by"),
)
if "create_user" not in column_names:
connection.execute(
text("ALTER TABLE users ADD COLUMN IF NOT EXISTS create_user VARCHAR(64)"),
)
logger.warning(
"Detected missing users.create_user; added nullable create_user column for schema compatibility.",
)
column_names.add("create_user")
if "update_user" not in column_names:
connection.execute(
text("ALTER TABLE users ADD COLUMN IF NOT EXISTS update_user VARCHAR(64)"),
)
logger.warning(
"Detected missing users.update_user; added nullable update_user column for schema compatibility.",
)
def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
@@ -141,6 +222,7 @@ def init_db() -> None:
_ensure_user_pk_column_compatibility()
_ensure_user_timestamp_column_compatibility()
_ensure_user_audit_column_compatibility()
Base.metadata.create_all(bind=engine)
with SessionLocal() as db:
local_hosts = {"db", "localhost", "127.0.0.1", "::1"}
+24
View File
@@ -154,3 +154,27 @@
- 风险与影响:
- 影响面集中在 `User` 模型状态字段映射。
- 运行环境需确保 `USER_STATUS_COLUMN` 与目标数据库实际字段一致;若配置错误,启动阶段仍可能抛 `UndefinedColumn`
## Work Log - 修复 API 启动时报 users.create_user / users.update_user 列不存在(2026-05-01
- 背景:
- 发布部署日志报错 `psycopg.errors.UndefinedColumn: column users.create_user does not exist`,容器持续重启。
- 触发点在 `init_db -> seed_defaults``Role.users` 关系预加载会查询 `users` 全字段;当历史库缺失 `create_user/update_user` 时,启动阶段直接失败。
- 本次改动(最小闭环):
- 文件:`api/app/core/database.py`
- 新增 `_ensure_user_audit_column_compatibility()` 并在 `init_db()` 中接入(位于 `Base.metadata.create_all()` 前执行)。
- 兼容策略:
- 若存在历史列 `create_by/created_by`,自动重命名为 `create_user`
- 若存在历史列 `update_by/updated_by`,自动重命名为 `update_user`
- 若目标列仍缺失,自动补齐 nullable 列:
- `ALTER TABLE users ADD COLUMN IF NOT EXISTS create_user VARCHAR(64)`
- `ALTER TABLE users ADD COLUMN IF NOT EXISTS update_user VARCHAR(64)`
- 验证:
- `git diff` 检查改动仅落在 `api/app/core/database.py` 与记忆文档。
- 启动期兼容逻辑为 PostgreSQL 定向执行;仅在 `users` 表存在且列不一致时触发 DDL,已对齐环境不会产生变更。
- 风险与影响:
- 影响面限定在 PostgreSQL 的 `users` 表审计字段兼容处理。
- 该兼容动作为启动期一次性 DDL,可能短暂持有 `users` 表 DDL 锁;对已有规范列名的库无行为变化。