Files
fquiz/memory/2026-05-01.md
T

21 KiB
Raw Blame History

Work Log - 修复 API 启动阶段 users 主键列兼容问题(2026-05-01

  • 背景:

    • 启动报错 psycopg.errors.UndefinedColumn: column users.user_id does not exist,触发点在 seed_defaults -> _seed_permissions 的关系加载 SQL。
    • 当前 ORM 与外键约定统一使用 users.user_id,但历史库可能残留 users.id 主键列命名。
  • 本次改动:

    • api/app/core/database.py
      • 新增 _ensure_user_pk_column_compatibility()
      • init_db() 执行 Base.metadata.create_all() 前,针对 PostgreSQL 做一次兼容检查:
        • users 表存在,且检测到仅有 id 而没有 user_id,自动执行:ALTER TABLE users RENAME COLUMN id TO user_id
        • 其余情况不改动(已是 user_id 或两者都不存在时直接跳过)。
  • 验证:

    • fquiz-api 容器内执行 init_db()init_db_ok
    • fquiz-api 容器内执行查询,确认当前数据库 users 列包含 user_id
  • 风险与影响:

    • 影响范围:仅 PostgreSQL 且仅触发于“存在 users.id 且缺少 users.user_id”的历史库。
    • 该变更属于启动期一次性 DDL 兼容动作;对已规范为 users.user_id 的库无行为变化。

Work Log - 修复 API 启动时报 users.user_name / users.username 列不一致(2026-05-01

  • 背景:

    • API 启动阶段在 seed_defaults -> _seed_permissions 查询中触发 ORM 关系预加载,SQL 使用了 users.user_name,但当前 PostgreSQL users 表实际字段为 username,导致 psycopg.errors.UndefinedColumn,服务启动失败。
  • 本次改动(最小闭环):

    • 文件:api/app/core/config.py
      • 新增配置 user_username_column(环境变量 USER_USERNAME_COLUMN),可选值 username / user_name,默认 username
    • 文件:api/app/models/user.py
      • User.username 列改为基于 settings.user_username_column 动态映射,兼容两类历史库结构。
    • 文件:docker-compose.yml
      • api / celery-worker / celery-beat 注入 USER_USERNAME_COLUMN 环境变量透传。
    • 文件:.env.example
      • 新增 USER_USERNAME_COLUMN=username 示例配置。
  • 验证:

    • python3 -m py_compile api/app/core/config.py api/app/models/user.py api/app/services/seed_service.py -> 通过。
    • USER_USERNAME_COLUMN=user_name POSTGRES_PORT=5434 docker compose up -d --no-deps api -> 启动成功。
    • USER_USERNAME_COLUMN=user_name POSTGRES_PORT=5434 docker compose ps api -> Up (healthy)
    • curl -fsS http://127.0.0.1:8000/health -> {"status":"ok","service":"fquiz-api","version":"0.1.0"}
  • 风险与影响:

    • 影响面集中在 User 模型用户名字段映射。
    • 运行环境需明确 USER_USERNAME_COLUMN 与目标数据库实际字段一致;配置错误会在启动阶段继续抛 UndefinedColumn

Work Log - GitHub Actions 部署分支切换为 dev2026-05-01

  • 背景:

    • 当前 workflow 仅监听 main push,且 deploy job 条件写死为 refs/heads/main,导致 dev 推送不触发自动部署。
  • 本次改动(最小改动):

    • 文件:.github/workflows/main.yml
      • on.push.branchesmain 改为 dev
      • deploy.ifgithub.ref == 'refs/heads/main' 改为 github.ref == 'refs/heads/dev'
  • 预期行为:

    • git push origin dev:自动触发构建,并在构建成功后执行 deploy。
    • main 分支 push:不再触发该 workflow 自动部署链路。
  • 风险与影响:

    • 生产发布入口从 main 切换到 dev,需确认团队分支策略已同步。
    • workflow_dispatch 若在非 dev 分支触发,deploy job 会被 if 条件跳过。

Work Log - 修复 docker db 端口 5433 冲突并改为 54342026-05-01

  • 背景:

    • docker compose 启动 db 报错:Bind for 0.0.0.0:5433 failed: port is already allocated
    • 现有默认口径为宿主机映射 5433->5432,与本机已占用端口冲突。
  • 本次改动(最小闭环):

    • 文件:docker-compose.yml
      • db.ports 默认映射从 ${POSTGRES_PORT:-5433}:5432 改为 ${POSTGRES_PORT:-5434}:5432
    • 文件:.env.example
      • POSTGRES_PORT 默认值从 5433 改为 5434
      • 本机直连 DB_PORT 默认值从 5433 改为 5434
    • 文件:api/app/core/config.py
      • db_port 默认值从 5433 改为 5434,与环境模板和 compose 默认保持一致。
    • 文件:README.md
      • 本地 PostgreSQL 示例端口从 localhost:5433 更新为 localhost:5434
    • 文件:MEMORY.md
      • 宿主机默认 POSTGRES_PORT 与本机直连 DB_PORT 长期口径同步更新为 5434
  • 验证:

    • POSTGRES_PORT=5434 docker compose up -d db -> fquiz-db 启动成功。
    • docker compose ps -a -> fquiz-db 状态 Up ... (healthy)
    • docker inspect fquiz-db --format '{{json .HostConfig.PortBindings}}' -> 5432/tcp 映射 HostPort=5434
  • 风险与影响:

    • 影响范围:本地/部署侧依赖默认 5433 的连接配置需同步为 5434,否则会出现连接失败。
    • 容器内服务间连接不受影响,仍通过 db:5432 通信。

Work Log - 统一 GitHub 发布默认 PostgreSQL 端口为 54342026-05-01

  • 背景:

    • 仓库本地 docker-compose.yml 默认端口已是 5434,但 GitHub Actions 发布脚本内联的 docker-compose.prod.yml 与首次生成 .env 仍使用 5433,存在口径不一致。
  • 本次改动(最小改动):

    • 文件:.github/workflows/main.yml
      • 将发布脚本中 db.ports 默认映射从 ${POSTGRES_PORT:-5433}:5432 改为 ${POSTGRES_PORT:-5434}:5432
      • 将首次初始化 .env 模板中的 POSTGRES_PORT=5433 改为 POSTGRES_PORT=5434
  • 验证:

    • rg -n "POSTGRES_PORT:-5434|POSTGRES_PORT=5434" .github/workflows/main.yml 命中两处,未发现 workflow 内 5433 残留。
  • 风险与影响:

    • 影响范围:仅 GitHub 发布脚本默认值,不影响已存在且手工维护的服务器 .env
    • 若线上环境明确依赖 5433,需在服务器 .env 显式保留 POSTGRES_PORT=5433

Work Log - 修复 API 启动时报 users.password 列不存在(2026-05-01

  • 背景:

    • 启动报错 psycopg.errors.UndefinedColumn: column users.password does not exist
    • 触发点在 seed_defaults -> _seed_permissions 的查询过程中,SQLAlchemy 关系预加载 Role.users 时会查询 users 全字段;当前 ORM 将 password_hash 绑定到 users.password,但目标库字段为 password_hash
  • 本次改动(最小闭环):

    • 文件:api/app/core/config.py
      • 新增配置 user_password_column(环境变量 USER_PASSWORD_COLUMN),可选值 password / password_hash,默认 password_hash
    • 文件:api/app/models/user.py
      • User.password_hash 改为基于 settings.user_password_column 动态映射,兼容两类历史库结构。
    • 文件:docker-compose.yml
      • api / celery-worker / celery-beat 注入 USER_PASSWORD_COLUMN 环境变量透传。
    • 文件:.env.example
      • 新增 USER_PASSWORD_COLUMN=password_hash 示例配置。
  • 验证:

    • python3 -m py_compile api/app/core/config.py api/app/models/user.py api/app/services/seed_service.py -> 通过。
    • rg -n "USER_PASSWORD_COLUMN|user_password_column" ... 命中配置、模型、compose、env 示例,映射链路完整。
  • 风险与影响:

    • 影响面集中在 User 模型密码字段映射。
    • 运行环境需确保 USER_PASSWORD_COLUMN 与目标数据库实际字段一致;若配置错误,启动阶段仍可能抛 UndefinedColumn

Work Log - 修复 API 启动时报 users.state 列不存在(2026-05-01

  • 背景:

    • 启动报错 psycopg.errors.UndefinedColumn: column users.state does not exist
    • 触发点同样在 seed_defaults -> _seed_permissions 的关系预加载过程中,ORM 查询 users 全字段时固定引用了 state,但目标库实际字段为 status
  • 本次改动(最小闭环):

    • 文件:api/app/core/config.py
      • 新增配置 user_status_column(环境变量 USER_STATUS_COLUMN),可选值 status / state,默认 status
    • 文件:api/app/models/user.py
      • User.status 列改为基于 settings.user_status_column 动态映射,兼容两类历史库结构。
    • 文件:docker-compose.yml
      • api / celery-worker / celery-beat 注入 USER_STATUS_COLUMN 环境变量透传。
    • 文件:.env.example
      • 新增 USER_STATUS_COLUMN=status 示例配置。
  • 验证:

    • rg -n "USER_STATUS_COLUMN|user_status_column" 命中配置、模型、compose、env 示例,映射链路完整。
    • git diff 检查仅包含本次预期的 4 个文件改动。
  • 风险与影响:

    • 影响面集中在 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_defaultsRole.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 锁;对已有规范列名的库无行为变化。

Work Log - 修复 /api/v1/admin/roles 在缺失 user_role 表时返回 5002026-05-01

  • 背景:

    • 复现路径:登录后调用 GET /api/v1/admin/roles,接口返回 500
    • 根因:legacy_admin_rbac_service.list_roles 固定查询 legacy 表 user_role,当库仅存在 modern RBAC 表(roles/user_roles/role_menus)时触发 psycopg.errors.UndefinedTable
  • 本次改动(最小闭环):

    • 文件:api/app/services/legacy_admin_rbac_service.py
      • 新增 user_role 存在性探测:_legacy_role_table_exists()
      • list_roles 增加双链路读取:
        • legacy 存在时保持原有 user_role + role_menu_rela + menu 查询;
        • legacy 缺失时自动回退 roles + role_menus + menus + role_permissions + permissions
      • get_role_by_idlist_role_menu_ids 同步支持 modern 回退(兼容按 idcode 查角色)。
      • 新增辅助函数:
        • _load_role_rows(..., role_source=...)
        • _load_role_permission_codes_map(..., role_source=...)
      • 扩展辅助函数以支持 modern 回退:
        • _load_role_menu_ids_map(..., role_source=...)
        • _load_menus_map(..., role_source=...)
  • 验证:

    • python3 -m py_compile api/app/services/legacy_admin_rbac_service.py -> 通过。
    • git push origin HEAD:dev -> 成功(提交 2c3ad31)。
  • 风险与影响:

    • 影响范围限定在后台角色读取接口(/api/v1/admin/roles*)查询链路。
    • 对 legacy 表完整的数据库行为保持不变;仅在 user_role 缺失时触发 modern 回退逻辑。

Work Log - 用户管理优化(编辑/检索/分页)(2026-05-01

  • 背景:

    • Issue FL-121 要求用户管理支持:用户信息修改、用户检索、用户表格分页。
  • 本次改动(最小闭环):

    • 后端 users 列表接口增强(检索 + 分页参数联动):
      • 文件:api/app/api/v1/users.py
      • GET /api/v1/users 新增查询参数:
        • keyword:按 user_id/email/username 模糊检索
        • status:按启用/禁用状态过滤
    • 后端用户服务增强:
      • 文件:api/app/services/user_service.py
      • list_users(...) 支持 keyword/status 条件查询并对 total 同步计数。
      • update_user(...) 支持修改 emailusername
        • 统一 trim/归一化
        • 重复值冲突校验
        • 空值保护
    • 用户更新请求模型补齐:
      • 文件:api/app/schemas/user.py
      • UserUpdateRequest 新增可选字段 email
    • 前端用户管理页增强:
      • 文件:web/src/app/admin/users/page.tsx
      • 新增“用户检索”区:关键字输入 + 状态筛选 + 搜索/重置。
      • 用户列表请求改为受查询条件驱动(query key 包含分页/筛选参数)。
      • 表格接入分页器(页码、每页条数、总数联动后端)。
      • 新增“编辑用户”弹窗,支持修改邮箱/用户名/状态。
      • 编辑提交仅传变更字段,避免无效更新。
  • 验证(未执行编译/构建,遵循当前任务约束):

    • 代码走读与关键路径自检:
      • GET /api/v1/users -> 支持 limit/offset/keyword/status
      • PATCH /api/v1/users/{id} -> 支持 email/username/status 更新。
      • 前端列表查询参数与分页状态联动一致。
  • 风险与影响:

    • 影响范围集中在用户管理模块(/admin/users/api/v1/users*)。
    • 旧调用方不传 keyword/status 时行为保持兼容。
    • 更新失败错误提示文案仍共用“not found or email/username exists”,后续如需更精确错误码可再拆分。

Work Log - 线路管理分布图加入缩放 Slider 与比例显示(2026-05-01

  • 背景:

    • Issue FL-131 要求“线路管理页面分布图优化:加入 slider 显示缩放比例”。
  • 本次改动(最小闭环):

    • 文件:web/src/components/power-line-cesium-map.tsx
      • 新增地图缩放 Slider(竖向),置于已有 +/-/居中 控件上方。
      • 新增“缩放比例 xx%”文本展示。
      • 新增缩放比例与相机高度的双向映射函数(对数映射),解决不同尺度下线性感知不均问题。
      • 监听 viewer.camera.changed,实现鼠标滚轮/按钮缩放时比例实时同步。
      • 拖动 Slider 时调用相机 flyTo 调整高度,保持当前位置不变,仅修改缩放。
      • 新增防抖动状态控制(sliderChangingRef)与无效 setState 保护,减少高频相机事件导致的重复渲染。
  • 验证:

    • 本次遵循任务约束,未执行编译/安装。
    • 通过代码走读确认:
      • +/- 按钮、鼠标滚轮、居中重置都会同步刷新缩放比例。
      • Slider 拖动可直接驱动地图缩放。
  • 风险与影响:

    • 影响范围限定在线路管理分布图前端组件,不涉及后端接口与数据结构。
    • 缩放比例为相对值(基于当前线路包围球动态计算),不同线路之间 100%/0% 对应的绝对相机高度不同,属于预期行为。

Work Log - 线路管理塔杆列表分页(2026-05-01)

  • 背景:

    • Issue FL-132 要求“线路管理”的塔杆列表表格支持分页。
    • 现状是前端固定请求 limit=500 并关闭表格分页,数据量大时浏览与定位效率较差。
  • 本次改动(最小闭环):

    • 文件:web/src/app/admin/power-lines/page.tsx
    • 新增塔杆列表分页状态:
      • towerPagination.current(当前页)
      • towerPagination.pageSize(每页条数,默认 20
    • 塔杆列表请求参数改为按视图分流:
      • 表格视图:limit=pageSizeoffset=(current-1)*pageSize
      • 地图视图:保留 limit=500(保证地图仍可展示较完整线路点位)
    • 表格接入 AntD 分页器:
      • 使用接口返回 total 驱动总数展示
      • 支持切换每页条数
      • 页码与请求参数联动
    • 新增筛选/线路切换时的页码重置:
      • selectedLineId / towerKeyword / towerTypeFilter / towerRiskFilter 变化时自动回到第 1 页,避免落在空页。
  • 验证(未执行编译/构建,遵循任务约束):

    • 代码走读确认:
      • 后端 GET /api/v1/lines/{line_id}/towers 已支持 limit/offset 且返回 total
      • 前端分页状态、请求参数、表格分页器三者联动一致。
  • 风险与影响:

    • 影响范围:仅前端 线路管理 -> 塔杆列表 视图。
    • 地图视图继续使用大页请求(500)避免点位显示回归。

Work Log - 线路管理分布图移除 Slider2026-05-01

  • 背景:

    • 用户在 Issue FL-131 新评论要求:去掉 slider。
  • 本次改动(最小改动):

    • 文件:web/src/components/power-line-cesium-map.tsx
      • 移除右上角竖向缩放 Slider。
      • 移除“缩放比例 xx%”文本展示。
      • 清理 slider 配套的缩放百分比状态、相机监听与映射函数。
      • 保留原有 + / - / 居中重置 缩放按钮能力不变。
  • 验证:

    • 遵循任务约束,未执行编译检查、未安装依赖。
    • 代码走读确认变更范围仅限 slider 相关逻辑。
  • 风险与影响:

    • 影响范围仅为线路管理分布图控件区 UI 与交互;后端接口和数据结构无影响。

Work Log - 新增高程数据管理功能(2026-05-01)

  • 背景:

    • 目标是支撑线路走向图高程渲染,提供“高程数据集管理 + 杆塔高程回填任务”闭环能力。
    • 约束:最小改动优先,不引入重型 GDAL 依赖,先落可运行方案。
  • 本次改动(最小闭环):

    • 后端模型与任务:
      • 新增 api/app/models/elevation.py
        • elevation_dataset:存高程数据集元信息(挂载、文件路径、分辨率、样本统计、bbox、状态)。
        • elevation_apply_job:存线路回填任务(模式、进度统计、状态、错误信息)。
      • 新增 api/app/tasks/elevation_tasks.py
        • Celery 任务 apply_elevation_for_line_job,异步执行指定 job。
      • api/app/core/celery_app.py
        • Celery include 扩展:加入 app.tasks.elevation_tasks
    • 后端 API 与服务:
      • 新增 api/app/schemas/elevation.pydataset/job 请求与响应模型)。
      • 新增 api/app/services/elevation_service.py,提供:
        • 数据集列表/创建/更新/分析;
        • 回填任务列表/详情/创建;
        • 回填执行逻辑(最近邻采样 CSV 点,写回 power_line_tower.altitude_m)。
        • 回填结果写入 raw_extra_json.elevation(数据集来源、采样距离、时间)。
      • 新增 api/app/api/v1/elevation.py
        • GET /api/v1/elevation/datasets
        • POST /api/v1/elevation/datasets
        • PATCH /api/v1/elevation/datasets/{dataset_id}
        • POST /api/v1/elevation/datasets/{dataset_id}/analyze
        • GET /api/v1/elevation/jobs
        • GET /api/v1/elevation/jobs/{job_id}
        • POST /api/v1/elevation/jobs/apply-line
      • api/app/api/router.py 注册 elevation 路由。
      • api/app/core/database.py + api/app/models/__init__.py 注册新模型,确保 init_db 自动建表。
    • 权限/菜单/订阅:
      • api/app/services/seed_service.py
        • 新增权限:elevation.read / elevation.manage
        • 新增后台菜单:admin.elevation -> /admin/elevation
        • admin 默认菜单绑定新增 admin.elevation
      • api/app/services/legacy_authz_service.py
        • admin 默认权限加入 elevation 权限。
        • MENU_CODE_PERMISSION_MAP 增加 admin.elevation
        • legacy synthetic 菜单补齐 admin.elevation
      • api/app/services/topic_registry.py
        • 新增 topic 规则:admin.elevation
      • api/app/services/admin_service.py / legacy_admin_rbac_service.py
        • admin.elevation 设为受保护菜单(不可误删)。
    • 前端页面与类型:
      • 新增 web/src/app/admin/elevation/page.tsx
        • 高程数据集管理(创建、分析、列表)。
        • 回填任务管理(创建、进度/结果查看)。
        • 支持跳转文件管理上传 CSV(复用现有文件系统)。
      • web/src/types/auth.ts 增加 elevation 相关类型定义。
      • web/src/app/admin/page.tsx 新增“高程数据管理”卡片入口。
      • web/src/app/admin/menus/page.tsxadmin.elevation 加入前端受保护菜单编码集合。
  • 验证:

    • 后端语法编译:
      • python3 -m compileall api/app -> 通过。
    • 前端构建:
      • npm run build:web -> 通过。
      • 构建产物中已包含路由:/admin/elevation
  • 风险与影响:

    • 当前实现使用 CSV 点集“最近邻采样”,适合先跑通管理与回填流程;不是严格栅格插值方案。
    • 未引入 GDAL/rasterio,部署更稳,但精度依赖 CSV 样本密度与坐标质量。
    • 回填默认允许 overwrite_all,存在覆盖人工高程风险;前端默认展示“仅填空(推荐)”。