Files
fquiz/memory/2026-04-23.md
T
2026-04-24 15:50:52 +08:00

43 KiB
Raw Blame History

Work Log - 日记管理按 quiz 表与逻辑重构(2026-04-23

  • 背景:按“用老工程表,参考老工程逻辑改造当前工程日记管理功能”要求,将 fquiz/admin/diary 从系统日志复用页切换为独立 Diary 模块。

  • 本次改动(最小闭环):

    • 后端模型与接口落地(对齐 quiz 口径):

      • api/app/models/diary.py
        • 新增主表 diary,字段:
          • id/title/content/diary_date/mood/weather/archived/create_date/create_user/update_date/update_user
        • 索引:idx_diary_create_user/idx_diary_diary_date/idx_diary_mood/idx_diary_archived
      • api/app/schemas/diary.py
        • 新增 DiaryMood 枚举:HAPPY/CALM/SAD/ANGRY/TIRED/EXCITED
        • 新增 DiaryQueryRequest/DiaryCreateRequest/DiaryUpdateRequest/DiarySummary/DiaryPageResponse
      • api/app/services/diary_service.py
        • 新增 create/search/get/update/delete/archive 逻辑。
        • 查询逻辑对齐 quiz:按 create_user 隔离数据,支持 title/mood/diary_date_start/diary_date_end/archived 过滤,排序 diary_date DESC, create_date DESC
        • 归档逻辑对齐 quizarchive 仅修改 archived 状态,保留原记录。
      • api/app/api/v1/diary.py
        • 新增接口:
          • POST /api/v1/diary/search
          • GET /api/v1/diary/get/{id}
          • POST /api/v1/diary/create
          • PUT /api/v1/diary/update
          • DELETE /api/v1/diary/delete/{id}
          • POST /api/v1/diary/{id}/archive?archived=...
        • 权限口径:
          • 读:menu.read | menu.manage
          • 写:menu.manage
      • 路由与建表注册:
        • api/app/api/router.py 挂载 diary 路由。
        • api/app/models/__init__.pyapi/app/core/database.py 注册 diary 模型,确保 create_all 生效。
    • 前端页面改造(替换 syslog 复用):

      • web/src/app/admin/diary/page.tsx
        • 改为独立 Diary 页面:
          • 列表分页
          • 条件查询(标题、心情、日期范围、归档状态)
          • 新增 / 编辑 / 删除
          • 归档 / 取消归档
          • 详情弹窗
        • API 调整到新后端接口 /api/v1/diary/*
      • web/src/types/auth.ts
        • 新增 DiaryMood/DiarySummary/DiaryListResponse 类型。
      • web/src/app/admin/page.tsx
        • 首页卡片文案从“上帝视角”调整为“日记管理”。
      • api/app/services/seed_service.py
        • 菜单 admin.diary 名称更新为“日记管理”(路径保持 /admin/diary)。
  • 验证:

    • python3 -m py_compile api/app/models/diary.py api/app/schemas/diary.py api/app/services/diary_service.py api/app/api/v1/diary.py api/app/api/router.py api/app/models/__init__.py api/app/core/database.py api/app/services/seed_service.py -> 通过。
    • python3 -m compileall api/app -> 通过。
    • 前端 ESLint 未完成(环境依赖缺失):
      • npm run lint 提示 eslint: not found
      • 直接执行 eslint 后提示缺少 typescript 依赖(Cannot find module 'typescript')。
  • 风险与影响:

    • admin.diary 保留 menu.read/menu.manage 权限体系(未引入独立 diary.read/diary.manage),属于兼容方案;后续若需要更细权限边界,可单独拆分权限码。
    • 本次仅对齐了老工程 diary 主链路,不包含分组/tag 维度扩展(老工程该模块本身也未强依赖)。

Work Log - 登录链路切换为 quiz 老表与老逻辑兼容(2026-04-23)

  • 背景:按“改用老工程 /root/.openclaw/workspace/quiz 中的表,参照老工程登录逻辑改造”要求,当前轮以“登录可用 + 会话鉴权可用 + 菜单可拉取”为最小闭环。

  • 本次改动(最小闭环):

    • 登录请求契约切换:

      • api/app/schemas/auth.py
        • LoginRequestemail + password 改为 user_id + password(密码最小长度放宽为 1,兼容老库短密码)。
      • web/src/components/auth-provider.tsx
        • login(...) 传参改为 user_id + password,请求体同步改为 { user_id, password }
      • web/src/app/page.tsx
        • 登录表单输入从 Email 改为 User ID,记住密码缓存字段同步改为 userId
    • 密码与状态兼容:

      • api/app/core/security.py
        • verify_password 增加 BCrypt 兼容分支(老工程口令),保留现有 Argon2 支持。
      • api/app/services/legacy_authz_service.py(新增)
        • 新增用户状态归一:ENABLED/ACTIVE/1/TRUE -> activeDISABLED/... -> disabled
      • api/app/models/user.py
        • 用户默认状态改为 ENABLED;默认主键改为 uuid4().hex32 位,兼容老表长度习惯)。
    • 旧表角色/权限装配兼容层(核心):

      • api/app/services/legacy_authz_service.py(新增)
        • 从老表链路读取授权信息:
          • user_role_rela -> user_role 计算 role_codes
          • role_menu_rela -> menu 映射 permission_codes
        • 管理员识别兼容:admin/sys_mgr/administrator 或角色名含“管理员”,统一附加 admin 角色别名。
        • 提供 build_legacy_menu_tree(...),按老 menu 表生成 /admin/me/menus 所需菜单树。
    • 后端鉴权链路接入兼容层:

      • api/app/services/auth_service.py
        • 登录改为按 user_id 查询。
        • Access Token 的 role_codes/permission_codes 改为通过兼容层计算(不再依赖新 RBAC 表)。
      • api/app/core/dependencies.py
        • get_current_user 改为用户基础信息 + 兼容层授权计算。
      • api/app/api/v1/ws.py
        • WS 鉴权改为复用兼容层 role_codes/permission_codes
      • api/app/services/jwt_generator_service.py
        • JWT 生成与用户列表角色展示改为兼容层口径,状态过滤兼容 ENABLED
      • api/app/services/user_service.py
        • serialize_user/queue_user_auth_refresh 改为兼容层计算授权信息。
        • 用户状态写入统一落库存储值 ENABLED/DISABLED
        • 用户角色更新改为直连老表 user_role_rela 写入(rela_id + user_id + role_id)。
      • api/app/services/admin_service.py
        • build_menu_tree 优先走老表菜单树构建,失败时回退现有实现。
  • 验证:

    • 后端编译:
      • python3 -m py_compile api/app/services/legacy_authz_service.py api/app/core/security.py api/app/schemas/auth.py api/app/schemas/user.py api/app/services/auth_service.py api/app/core/dependencies.py api/app/services/user_service.py api/app/api/v1/ws.py api/app/services/jwt_generator_service.py api/app/services/admin_service.py api/app/models/user.py -> 通过。
      • python3 -m compileall api/app -> 通过。
    • 前端构建:
      • npm run build:web -> 失败(既有类型问题,非本轮新增):
        • web/src/app/admin/layout.tsx:155Card 组件 children 类型不匹配(ui-antd 兼容层类型定义问题)。
  • 风险与影响:

    • 本轮优先闭环“登录 + 鉴权 + 菜单树”,未对 admin/rolesadmin/menus 全量 CRUD 全面切换到老表语义;这些接口仍存在新旧口径混用风险。
    • 旧表无独立 permissions 表,当前 permission_codes 为“角色菜单到权限码”的兼容映射;若老库菜单编码与映射不一致,可能出现权限显隐偏差。
    • 注册接口当前保留并做了缺省兜底,但老库角色初始化策略(如默认 user 角色)依赖目标库实际数据,需联调确认。

Work Log - 第二轮:角色/菜单管理切换 quiz 老表口径(2026-04-23

  • 背景:按“继续做第二轮”要求,将后台角色/菜单管理从现有 RBAC ORM 表切换为 quiz 老表链路,并同步前后端 ID 类型契约。

  • 本次改动(最小闭环):

    • 后端接口与服务切换:

      • api/app/services/legacy_admin_rbac_service.py
        • 完成并启用老表 CRUD
          • 角色:user_roleuser_role_relarole_menu_rela
          • 菜单:menurole_menu_rela
        • 支持:list/get/create/update/delete rolelist/get/create/update/delete menulist/replace role menuslist permissions
        • 保护项兼容:保留受保护角色与菜单编码删除拦截。
        • 修复更新边界:menu.parent_id 支持显式清空;角色/菜单名称做 strip 空值校验。
      • api/app/api/v1/admin.py
        • 角色/菜单相关路由改为调用 legacy_admin_rbac_service
        • 角色/菜单 path 参数改为字符串:role_id: strmenu_id: str
        • GET /admin/roles/{role_id}/menus 返回改为 list[str]
      • api/app/schemas/admin.py
        • 角色/菜单契约改为字符串 ID
          • RolePublic.idRolePublic.menu_ids
          • RoleCreateRequest.menu_idsRoleUpdateRequest.menu_ids
          • MenuPublic.idMenuPublic.parent_id
          • MenuCreateRequest.parent_idMenuUpdateRequest.parent_id
          • RoleMenuUpdateRequest.menu_ids
    • 鉴权菜单树 ID 口径对齐:

      • api/app/services/legacy_authz_service.py
        • build_legacy_menu_tree 改为直接使用老表 menu.menu_id/parent_id 作为菜单树 ID,不再生成临时递增 ID。
    • 前端类型与页面对齐:

      • web/src/types/auth.ts
        • RoleItem.id/menu_idsMenuItem.id/parent_id 改为字符串类型。
      • web/src/app/admin/roles/page.tsx
        • 角色编辑状态与 menu_ids 改为字符串口径。
      • web/src/app/admin/menus/page.tsx
        • 菜单编辑状态与 parent_id 改为字符串口径;
        • ID 排序改为兼容字符串(纯数字优先数值比较,否则字典序)。
      • web/src/app/admin/layout.tsx
        • 菜单路径项 id 改为字符串。
      • web/src/app/admin/users/page.tsx
        • 角色兜底项 id 改为字符串(fallback-*)。
  • 验证:

    • 后端编译:
      • python3 -m py_compile api/app/schemas/admin.py api/app/api/v1/admin.py api/app/services/legacy_admin_rbac_service.py api/app/services/legacy_authz_service.py -> 通过。
      • python3 -m compileall api/app -> 通过。
    • 前端构建:
      • npm run build:web -> 失败,仍为既有问题:
        • web/src/app/admin/layout.tsx:155Card 组件 children 类型不匹配(ui-antd 兼容层类型定义问题,非本轮新增)。
  • 风险与影响:

    • 老表 menu 字段语义与当前前端展示字段并非一一对应(如 component/cacheable),当前仍为兼容映射;如需完全对齐 quiz UI 语义,后续需补充映射规则。
    • permissions 仍是基于菜单编码映射的兼容推导;若线上老表菜单编码偏差,权限显示与鉴权可能出现边缘不一致,需要联调验证。

Work Log - 第三轮:前端构建门禁打通(2026-04-23)

  • 背景:第二轮后 npm run build:web 仍被前端类型问题阻断,影响发布门禁。

  • 本次改动(最小闭环):

    • antd Card 类型兼容修复:
      • web/src/components/ui-antd.tsx
        • Card 包装器改用 antd 官方 CardProps 类型,不再依赖 ComponentProps<typeof AntCard> 推导。
        • AntCard 做显式可渲染组件收敛(AntCardComponent),避免 JSX element type 'AntCard' does not have construct or call signatures
    • 页面层统一复用兼容层 Card
      • web/src/app/admin/mermaid-mgr/_components/mermaid-editor.tsx
      • web/src/app/admin/mindmap/_components/mindmap-editor.tsx
      • antd 直引 Card 改为 @/components/ui-antdCard
    • 严格 TS 隐式 any 清理(与现有 strict 配置对齐):
      • web/src/app/admin/mermaid-mgr/_components/mermaid-editor.tsx
      • web/src/app/admin/mermaid-mgr/page.tsx
      • web/src/app/admin/mindmap/_components/mindmap-editor.tsx
      • web/src/app/admin/schedule/page.tsx
      • web/src/app/admin/todos/page.tsx
      • 补全 onChange/onClick/onFinish/showTotal/footer 等回调参数类型标注,消除 noImplicitAny 阻断。
  • 验证:

    • npm run build:web -> 通过(TypeScript 检查通过 + 54 个页面静态生成完成)。
  • 风险与影响:

    • 本轮仅做类型与组件兼容修复,未改业务逻辑与接口契约。
    • 后续若继续在 web/src/app/admin/** 新增 AntD 回调,仍需显式参数类型以避免 strict 下再次触发 implicit any

Work Log - 第四轮:类型门禁巡检与基线固化(2026-04-23)

  • 背景:第三轮已打通 build:web,第四轮目标是做“全量巡检”防止同类问题回归。

  • 本次动作:

    • 代码扫描:
      • web/srcimport { Card } from "antd"(避免回到不稳定类型入口);
      • onFinish/onChange/onClick/onPressEnter 中常见未标注参数模式(event/value/values/total/_)。
    • 类型基线检查:
      • npm --workspace web exec tsc --noEmit --pretty false
  • 结果:

    • 扫描未发现新的高风险命中。
    • tsc --noEmit 全量通过,web 当前类型门禁基线稳定。
  • 影响:

    • 第四轮未新增业务逻辑改动,属于稳定性巡检与基线确认。

Work Log - 第五轮:后端联调冒烟与运行时修复(2026-04-23)

  • 背景:按“继续第五轮”要求,对登录与后台角色/菜单主链路做联调冒烟,验证第二轮老表改造在运行容器中的真实表现。

  • 本次动作与发现:

    • 容器代码加载校验:
      • 初次冒烟发现运行中的 api 仍走旧登录契约(email + password),因此执行 docker compose up --build -d api 重新加载最新代码。
    • 运行时阻断修复 1(启动失败):
      • 重建后 api 启动报错:ModuleNotFoundError: No module named 'bcrypt'
      • 处理:api/requirements.txt 新增 bcrypt==4.2.1,重建 api 后恢复健康。
    • 运行时阻断修复 2/admin/me/menus 500):
      • 冒烟命中 500,日志定位到 legacy_authz_service.build_legacy_menu_tree 使用了未定义变量 node_id
      • 处理:api/app/services/legacy_authz_service.pynodes[node_id] 修正为 nodes[legacy_id],重建后恢复。
  • 冒烟验证(只读,不写库):

    • 登录契约校验:
      • POST /api/v1/auth/login{user_id, password} -> 401(凭证错误,说明契约已生效);
      • {email, password} -> 422(缺少 user_id)。
    • 管理后台主链路:
      • GET /api/v1/admin/roles -> 200RolePublic.id/menu_ids 为字符串;
      • GET /api/v1/admin/menus -> 200MenuPublic.id/parent_id 为字符串口径;
      • GET /api/v1/admin/me/menus -> 200,菜单树根节点 id 为字符串;
      • GET /api/v1/admin/roles/sys_mgr/menus -> 200menu_ids 为字符串数组;
      • GET /api/v1/admin/permissions -> 200。
  • 相关文件:

    • api/requirements.txt
    • api/app/services/legacy_authz_service.py
  • 风险与影响:

    • 本轮未执行角色/菜单写接口(create/update/delete),避免对外部数据库产生变更;当前验证覆盖“登录契约 + 权限读取 + 菜单读取”读路径。

Work Log - 第六轮:修复日程页 Modal.footer 类型不兼容(2026-04-23

  • 背景:docker compose build webweb/src/app/admin/schedule/page.tsx:747 失败,报错为 Modal.footer 回调签名不匹配(OkBtn/CancelBtn 被错误标注为 () => ReactElement)。

  • 本次改动(最小闭环):

    • web/src/app/admin/schedule/page.tsx
      • 补充 React 类型导入:FCReactNode
      • Modal.footer 回调类型从:
        • _origin: unknown
        • { OkBtn: () => ReactElement; CancelBtn: () => ReactElement } 调整为:
        • _origin: ReactNode
        • { OkBtn: FC; CancelBtn: FC }
      • 保持现有按钮渲染与删除/保存逻辑不变,仅修复类型契约。
  • 验证:

    • npm run build:web -> 通过(Next.js 编译、TypeScript 检查、静态页面生成全部完成)。
  • 风险与影响:

    • 仅影响前端类型声明,不涉及接口契约、请求参数、业务分支和数据写入行为。

Work Log - 第七轮:后台系统菜单改用 AntD Menu 组件(2026-04-23

  • 背景:当前后台左侧“系统菜单”是递归 Button + Link,移动端是 DropdownMenu。按要求切换为 Ant Design Menu 组件承载菜单树。

  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • 新增 Menu as AntMenuMenuProps 引入。
      • 移除递归渲染函数 renderMenuNodes 与移动端 flattenMenuPaths 下拉菜单构建逻辑。
      • 新增 buildMenuItems:把后端返回的 MenuTreeItem[] 转为 AntMenuitems 结构(支持嵌套)。
      • 新增 collectSubmenuKeys:收集所有带子节点菜单并展开,保持与旧实现一致的“默认全展开”体验。
      • 新增 findActiveMenuState:基于 pathname 计算 selectedKeys/openKeys,保证当前路由高亮准确。
      • 桌面端左侧“系统菜单”改为 AntMenu mode=\"inline\" 渲染。
      • 移动端菜单入口改为在卡片内直接渲染 AntMenu mode=\"inline\"(不再使用下拉菜单承载)。
      • 顶部“账号”菜单保持原 DropdownMenu 方案不变。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(Next.js 编译、TypeScript、静态页面生成全部完成)。
  • 风险与影响:

    • 移动端菜单交互由“点击按钮弹出”改为“卡片内直接展示”,视觉高度会增加,但菜单可见性更高。
    • 本次只调整菜单承载组件,不涉及菜单接口、权限判断和路由结构变更。

Work Log - 第八轮:菜单导航切回左侧布局(2026-04-23)

  • 背景:收到“menu 导航放到左侧”的要求,当前后台壳层为顶部横向 Menu,与期望不一致。

  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • 移除顶部横向 AntMenu(mode=\"horizontal\") 导航区。
      • 新增左侧栏布局(md 及以上):AntMenu(mode=\"inline\") 承载后台菜单树。
      • 移动端菜单改为左侧 Drawerplacement=\"left\")承载 AntMenu,顶部仅保留“菜单”按钮触发抽屉。
      • 保留现有菜单数据来源(/api/v1/admin/me/menus)、路由高亮逻辑(selectedKeys)与账号区交互不变。
      • 菜单展开状态统一为 menuOpenKeys,桌面侧栏与移动抽屉共用,保证一致性。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(Next.js 编译、TypeScript、静态页面生成全部完成)。
  • 风险与影响:

    • 布局从“顶部导航”改为“左侧导航”,横向空间分配会变化,但仅影响后台壳层,不涉及页面业务逻辑与接口契约。

Work Log - 第八轮:后台壳层改为顶部固定导航(2026-04-23)

  • 背景:按“参考 Ant Design 文档站顶部固定导航(左侧 Logo + 主导航)”要求,重构当前后台壳层布局。

  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • 布局从“左侧固定菜单 + 内容头卡片”调整为“顶部固定导航 + 内容区”。
      • 顶栏结构改为:左侧品牌 LogoQ + fquiz+ 中部主导航(AntMenu mode=horizontal+ 右侧账号区。
      • 保留现有菜单数据链路:继续通过 /api/v1/admin/me/menus 拉取并渲染树形菜单。
      • 保留路由高亮逻辑,并补充 openKeys 计算用于移动端内嵌菜单展开。
      • 新增移动端菜单折叠入口(菜单/收起菜单),避免小屏导航不可达。
      • 保留 menuError 提示、登录态判断与账号退出逻辑,不改接口契约。
  • 验证:

    • npm run build:web -> 通过(Next.js 编译、TypeScript 检查、静态页面生成全部完成)。
  • 风险与影响:

    • 桌面端导航入口从左侧改为顶部,用户需要适应新的操作路径。
    • 当顶级菜单数量较多时,水平菜单会进入 AntD 的溢出折叠(...)交互,属于组件默认行为。

Work Log - 第九轮:84 库用户/角色/菜单数据导出并初始化本地库(2026-04-23)

  • 背景:按“查询 84 的用户表、角色表、菜单表、菜单角色关系数据,然后初始化本地库”要求执行。

  • 本次动作(最小闭环):

    • 远端 84 库(223.109.142.84:5432/postgres)连通与数据核验:
      • 表存在:users / user_role / menu / role_menu_rela
      • 行数:users=5user_role=6menu=64role_menu_rela=123
    • 数据导出留档:
      • CSV 导出文件:
        • tmp/84-export/users_84.csv
        • tmp/84-export/user_role_84.csv
        • tmp/84-export/menu_84.csv
        • tmp/84-export/role_menu_rela_84.csv
      • 初始化脚本导出:
        • tmp/84-export/legacy_auth_schema.sql
        • tmp/84-export/legacy_auth_data.sql
        • tmp/84-export/legacy_auth_data_wrapped.sql
    • 本地库初始化:
      • 目标库:本地 fquiz-dbpostgres 数据库(用户 fquiz)。
      • DROP TABLE IF EXISTS public.role_menu_rela, public.menu, public.user_role, public.users CASCADE
      • 回放 schema + data 完成建表和数据导入。
      • 由于 menu.parent_id 自关联外键,导入数据时在同一会话启用 session_replication_role=replica 后回放,导入成功。
  • 验证:

    • 远端与本地(postgres)按表对比 count + md5(signature) 全量一致:
      • users: 5 / a50fedde66f156b0442d792b42c355b7
      • user_role: 6 / 7fb7d2520f44efe25e8be15762a3bd3d
      • menu: 64 / 094b533fe91b853868a0f9e4356da49a
      • role_menu_rela: 123 / 3c752bb95375e80d835b198831d44535
  • 风险与影响:

    • 本次初始化目标是本地 postgres 库,不影响本地 fquiz 库现有表数据。
    • users 表为老工程结构(user_id/user_name/password/state...),与新表结构不同;后续若切回新 RBAC 表需区分数据库与表口径。

Work Log - 第十轮:数据库默认目标切换到本地并修复启动兼容(2026-04-23)

  • 背景:按“现在切到本地库,不用84了”要求,将默认数据库目标从 223.109.142.84 切到本地 fquiz-db

  • 本次改动(最小闭环):

    • docker-compose.yml
      • db 服务移除 local-db profile,改为默认启动。
      • api 新增 depends_on: db (service_healthy)
      • api 的默认 DB 参数切到本地:
        • DB_HOST=db
        • DB_PORT=5432
        • DB_NAME=postgres
        • DB_USERNAME=fquiz
        • DB_PASSWORD=fquiz
    • api/app/core/config.py
      • 非 Docker 默认 DB 参数改为本机本地库:
        • db_host=127.0.0.1
        • db_port=5433
        • db_name=postgres
        • db_username=fquiz
        • db_password=fquiz
    • .env.example
      • 同步改为本机本地库默认值(不再出现 84 默认地址)。
    • api/app/services/seed_service.py
      • _seed_initial_admin 中初始管理员状态由 active 调整为 ENABLED,兼容老 users.state 约束,解决本地库启动时 users_state_check 报错。
  • 验证:

    • python3 -m py_compile api/app/services/seed_service.py -> 通过。
    • docker compose up -d --build api -> 通过,fquiz-api 正常启动。
    • docker compose ps -> api/db/web 全部 healthy/up
    • GET /health -> 200。
    • POST /api/v1/auth/login(错误密码探测)-> 401(说明 API 可用且查询链路正常)。
  • 风险与影响:

    • 默认目标已改为本地库;若后续要临时连外部库,需显式设置 DATABASE_URL 或覆盖 DB_HOST 等环境变量。

Work Log - 第十一轮:后台菜单改为左侧内嵌并支持右上角显隐(2026-04-23)

  • 背景:按“改成内嵌菜单,放在页面左侧,通过右上角菜单按钮隐藏/显示”要求调整后台壳层交互。

  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • 移除移动端 Drawer 侧滑菜单方案,统一为页面内嵌左侧菜单。
      • 新增状态 menuVisible,默认 true,控制左侧菜单区域显示/隐藏。
      • 顶栏右上角“菜单”按钮改为全端可见,并切换文案:
        • 菜单显示时:隐藏菜单
        • 菜单隐藏时:显示菜单
      • 内容区网格列根据 menuVisible 动态切换:
        • 显示:md:grid-cols-[280px_minmax(0,1fr)]
        • 隐藏:md:grid-cols-1
      • 左侧菜单保留 AntMenu inline + openKeys/selectedKeys 逻辑,保持当前路由高亮与展开行为。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • next build 多次在当前环境命中 .next 产物文件 ENOENT(非本次业务逻辑错误):
      • Turbopack 路径:_buildManifest.js.tmp 丢失;
      • Webpack 路径:edge-runtime-webpack.js copyfile 丢失。
  • 风险与影响:

    • 本次仅改后台壳层菜单交互,不涉及 API 契约和业务数据写入。
    • 当前构建环境存在 .next 产物落盘异常,影响 build:web 门禁稳定性(与菜单逻辑无直接耦合)。

Work Log - 第十二轮:支持主题色切换并持久化(2026-04-23)

  • 背景:按“支持主题色切换”要求,增加可视化主题色切换入口,并要求刷新后保持。

  • 本次改动(最小闭环):

    • web/src/components/ui-antd.tsx
      • 新增主题外观上下文 ThemeAppearanceContext
      • 新增 useThemeAppearance(),对外暴露:
        • accentColor
        • setAccentColor(...)
      • 新增 THEME_ACCENT_OPTIONS(靛蓝/蓝色/青色/绿色/橙色/红色/粉色/紫色)。
      • Theme 组件增加主题色状态与持久化:
        • 启动时读取 localStorage["fquiz:theme:accent-color"]
        • 切换后写回 localStorage
        • 切换即更新 AntD ConfigProvidercolorPrimary,全局生效。
    • web/src/app/admin/layout.tsx
      • 顶栏右侧新增主题色选择器(Select),使用 THEME_ACCENT_OPTIONS 渲染。
      • 选择器值绑定 useThemeAppearance().accentColor,变更时调用 setAccentColor
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(Next.js 编译、TypeScript、静态页面生成全部完成)。
  • 风险与影响:

    • 主题色选择属于前端 UI 层改动,不影响后端接口与数据模型。

Work Log - 登录页怪兽主视觉改为“毛怪 + 大眼仔”双角色(2026-04-23

  • 背景:按“把登录页面的怪兽换成毛怪和大眼仔”要求,保留登录/注册链路不变,仅重做首页登录视觉。

  • 本次改动(最小闭环):

    • web/src/app/page.tsx
      • 保留 useAuth 登录/注册/退出与表单提交流程不变。
      • 将单怪兽舞台替换为双角色舞台:sulley + mike
      • 保留“眼睛跟随鼠标”交互,并新增大眼仔眼球偏移参数(MIKE_GAZE_MAX_X/Y)。
      • 保留“密码输入时挪开视线”交互:毛怪转头避视、大眼仔轻微眯眼。
      • 替换旧 .monster-* 样式为 .duo-* / .sulley-* / .mike-* 样式,维持响应式布局与背景动效。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(Next.js 编译、TypeScript、静态页面生成全部完成)。
  • 风险与影响:

    • 本次为前端视觉层改动,不涉及 API 契约、鉴权逻辑或后端数据结构。
    • 角色形象为页面内 CSS 卡通实现,不依赖外部图片资源。

Work Log - 第九轮:按 AntD 配色收口全局变量(2026-04-23)

  • 背景:当前前端虽然已切到 AntD 组件栈,但大量页面仍使用历史 --gray-* / --accent-* 变量与 Tailwind 类,导致视觉不完全跟随 AntD 主题色。

  • 本次改动(最小闭环):

    • web/src/components/ui-antd.tsx
      • Theme 内新增 ThemeCssVarsScope,通过 antdTheme.useToken() 把旧语义变量映射到 AntD token。
      • 覆盖变量包括:
        • AntD 直连变量:--ant-color-primary--ant-color-bg-layout--ant-color-border-secondary--ant-color-text-secondary--ant-color-text
        • 旧语义变量:--gray-*--accent-*--red-*--green-*--orange-*--indigo-*--color-panel-solid--border
      • 通过 display: contents 避免新增布局层级影响页面结构。
    • web/src/app/globals.css
      • 新增上述变量的静态兜底值,避免主题注入前变量缺失。
      • body 背景与文字颜色改为走 AntD 变量(--ant-color-bg-layout--ant-color-text)。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(Next.js 编译、TypeScript、静态页面生成全部完成)。
  • 风险与影响:

    • 本次不改接口与业务逻辑,仅调整前端主题变量层与全局视觉基线。
    • 旧页面里保留的 var(--gray/* --accent/*) 写法将统一跟随 AntD token,后续可按需渐进式替换为原生 AntD token class/style。

Work Log - 第十轮:去掉前端 URL 的 /admin 前缀(2026-04-23

  • 背景:希望用户侧访问地址不再出现 /admin,但保留现有 app/admin/** 页面实现与权限链路。

  • 本次改动(最小闭环):

    • web/src/middleware.ts(新增)
      • 新地址(不含 /admin)统一 rewrite 到现有 /admin/** 页面路由。
      • 旧地址 /admin/** 自动 redirect 到无前缀地址,兼容历史链接与书签。
      • 根路径 //api/_next、常见静态文件请求走 bypass,不参与改写。
      • 约定:原 /admin 首页映射为 /dashboard
    • web/src/app/admin/layout.tsx
      • 菜单数据加载后统一把 path/admin/** 规范化为无前缀地址(/admin -> /dashboard)。
      • 顶部 Logo 跳转从 /admin 改为 /dashboard
    • web/src/app/page.tsx
      • 首页快捷入口改为无前缀地址:/dashboard/users/requirements
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过(含 Proxy/Middleware 生效,编译与静态生成完成)。
  • 风险与影响:

    • 现有硬编码 /admin/** 链接仍可用,但会发生一次 30x 重定向到无前缀地址。
    • Next.js 16 对 middleware 命名提示迁移到 proxy,当前功能正常;后续可按官方建议改名以消除提示。

Work Log - 第十轮:下线 8 个后台功能(2026-04-23

  • 背景:按“删除生命倒计时、密钥管理、价格监控、历史答卷、诗词本、日记管理、家庭作业、试题管理功能”要求,执行最小闭环下线。

  • 本次改动(最小闭环):

    • 菜单与权限口径收敛:

      • api/app/services/seed_service.py
        • 移除默认菜单:
          • admin.life_countdown
          • admin.password
          • admin.token_usage
          • admin.history
          • admin.vocabulary
          • admin.diary
          • admin.homework
          • admin.question_bank
        • ROLE_MENU_BINDINGS["admin"] 同步移除上述菜单。
        • 移除默认权限 life_countdown.read/manage
      • api/app/services/legacy_authz_service.py
        • 将上述 8 个菜单加入 DISABLED_MENU_CODES,保证老库已有记录也不会再出现在 /api/v1/admin/me/menus
        • 权限映射移除对应菜单码;补充 admin.job_mgr -> question_bank.read/manage,确保“作业监控”权限链路不受影响。
      • api/app/services/legacy_admin_rbac_service.py
        • 增加 REMOVED_MENU_CODES 过滤:菜单列表、角色菜单返回、权限推导、菜单可用性校验均排除已下线菜单。
      • api/app/services/admin_service.py
        • 受保护菜单集合同步移除上述 8 个菜单码(兼容回退路径)。
      • web/src/app/admin/menus/page.tsx
        • 前端受保护菜单集合同步移除上述 8 个菜单码。
    • 后端接口下线:

      • api/app/api/router.py
        • 不再挂载 diary_routerlife_countdown_router
      • api/app/api/v1/admin.py
        • 删除密钥管理专用接口:
          • GET /api/v1/admin/password/models
          • GET /api/v1/admin/password/models/{model_id}/keys
          • POST /api/v1/admin/password/models/{model_id}/rotate-key
    • 前端页面下线:

      • 删除路由页面:
        • web/src/app/admin/life-countdown/page.tsx
        • web/src/app/admin/password/page.tsx
        • web/src/app/admin/price-monitor/page.tsx
        • web/src/app/admin/token-usage/page.tsx
        • web/src/app/admin/history/page.tsx
        • web/src/app/admin/poetry/page.tsx
        • web/src/app/admin/diary/page.tsx
        • web/src/app/admin/homework/page.tsx
        • web/src/app/admin/question-bank/page.tsx
        • web/src/app/admin/vocabulary/page.tsx
      • web/src/app/admin/page.tsx 移除上述功能卡片入口。
      • web/src/app/admin/vocabulary-proficiency/page.tsx 移除“进入诗词本”跳转按钮。
      • 保留“作业监控”功能:
        • 将原 question-bank 页面实现迁移到 web/src/app/admin/job/_components/job-question-bank-page.tsx
        • web/src/app/admin/job/page.tsx 改为引用该实现。
  • 验证:

    • 后端语法校验:
      • python3 -m py_compile api/app/services/seed_service.py api/app/services/legacy_authz_service.py api/app/services/legacy_admin_rbac_service.py api/app/services/admin_service.py api/app/api/v1/admin.py api/app/api/router.py -> 通过。
      • python3 -m compileall api/app -> 通过。
    • 前端构建:
      • npm run build:web -> 通过。
      • 输出路由确认已不再生成:
        • /admin/life-countdown/admin/password/admin/price-monitor/admin/token-usage/admin/history/admin/poetry/admin/diary/admin/homework/admin/question-bank/admin/vocabulary
  • 风险与影响:

    • 本次下线为“菜单 + 路由 + 部分接口”闭环;question_bankvocabulary 底层 API 仍保留,供“作业监控/分组管理/知识点管理/单词统计”等保留模块复用。
    • 若线上老库仍有已下线菜单记录,当前会被服务层过滤,不再对外展示。

Work Log - 下线 8 个后台功能模块(2026-04-23

  • 背景:按要求删除系统中的以下功能:

    • 微信小程序(admin.wxapp
    • MD解析(admin.mdresolve
    • 数据查询(admin.data_query
    • 热搜(admin.hot_search
    • 文件识别(admin.filedetector
    • 百度网盘(admin.baidu_pan
    • 分组管理(admin.tag
    • 知识点管理(admin.knowledge_point_mgr
  • 本次改动(最小闭环):

    • 前端:删除 8 个页面路由文件(访问即 404):

      • web/src/app/admin/wxapp/page.tsx
      • web/src/app/admin/mdresolve/page.tsx
      • web/src/app/admin/data-query/page.tsx
      • web/src/app/admin/hot-search/page.tsx
      • web/src/app/admin/filedetector/page.tsx
      • web/src/app/admin/baidu-pan/page.tsx
      • web/src/app/admin/group/page.tsx
      • web/src/app/admin/knowledge/page.tsx
    • 前端:移除后台首页入口卡片:

      • web/src/app/admin/page.tsx
    • 前端:菜单管理页取消上述 8 个菜单码的“受保护菜单”限制(允许删除历史残留菜单):

      • web/src/app/admin/menus/page.tsx
    • 后端:菜单树硬过滤(即使老库仍有菜单记录,也不会下发给前端导航):

      • api/app/services/legacy_authz_service.py
        • 新增 DISABLED_MENU_CODES 并在菜单树构建、权限推导时过滤。
      • api/app/services/admin_service.py
        • 新增 REMOVED_MENU_CODES 并在 list_menus/get_menu_by_id/get_menu_by_code/create_menu/build_menu_tree/_load_menus_by_ids 过滤。
      • api/app/services/legacy_admin_rbac_service.py
        • 将 8 个菜单码加入 REMOVED_MENU_CODES,统一影响菜单列表、角色菜单关联、权限推导。
    • 后端:停用对应接口挂载与默认种子:

      • api/app/api/router.py
        • 移除 hot_search_routermdresolve_router 挂载。
      • api/app/services/topic_registry.py
        • 移除 admin.hot_searchadmin.hot_search.follow_topics topic 规则。
      • api/app/services/seed_service.py
        • 移除 8 个菜单定义与 admin 默认菜单绑定。
        • 移除 seed_hot_search_defaults 导入与调用。
  • 验证:

    • python3 -m py_compile api/app/services/legacy_authz_service.py api/app/services/seed_service.py api/app/api/router.py api/app/services/topic_registry.py api/app/services/legacy_admin_rbac_service.py api/app/services/admin_service.py -> 通过。
    • python3 -m compileall api/app -> 通过。
    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm run build:web -> 通过;后台静态路由从 54 个下降到 36 个,已不再包含上述 8 个页面。
  • 风险与影响:

    • 本次不做数据库物理删行迁移;若库内仍有历史菜单记录,将被后端过滤而不可见。
    • api/app/api/v1/hot_search.pyapi/app/api/v1/mdresolve.py 文件仍保留在仓库,但未挂载到 api_router,外部不可访问。

Work Log - 下线功能补充收口(2026-04-23)

  • 补充改动:

    • 删除历史别名页面 web/src/app/admin/tag/page.tsx,彻底下线“分组管理”的旧路由入口。
  • 验证补充:

    • 因删除页面后 .next 残留类型缓存导致 tsc 引用旧路径,执行 rm -rf web/.next 后重跑:
      • npm --workspace web exec tsc --noEmit --pretty false -> 通过
      • npm run build:web -> 通过
    • 产物路由确认不再包含 /admin/tag 及本轮下线的 8 个功能页面。

Work Log - 下线 4 个后台功能模块(2026-04-23

  • 背景:按要求删除以下功能:

    • 脚本管理(admin.cron_task_mgr
    • 待办管理(admin.todos
    • 作业监控(admin.job_mgr
    • JWT 生成器(admin.jwt_generator
  • 本次改动(最小闭环):

    • 前端下线:

      • 删除路由页面:
        • web/src/app/admin/cron/page.tsx
        • web/src/app/admin/todos/page.tsx
        • web/src/app/admin/job/page.tsx
        • web/src/app/admin/jwt-generator/page.tsx
      • 后台首页移除 4 个入口卡片:
        • web/src/app/admin/page.tsx
      • 菜单管理页移除 4 个菜单码受保护限制(允许清理历史残留):
        • web/src/app/admin/menus/page.tsx
      • 为保留“队列管理”能力,将原待办页面实现迁移到:
        • web/src/app/admin/jobqueue/_components/jobqueue-todo-page.tsx
        • web/src/app/admin/jobqueue/page.tsx 改为引用新组件。
    • 后端菜单/权限链路下线:

      • api/app/services/seed_service.py
        • 移除 4 个菜单定义及 admin 默认菜单绑定。
        • 移除 jwt_generator.read/jwt_generator.manage 默认权限与 admin 角色默认绑定。
      • api/app/services/legacy_authz_service.py
        • 将 4 个菜单码加入 DISABLED_MENU_CODES,并移除对应菜单权限映射。
        • 移除 DEFAULT_ADMIN_PERMISSION_CODESjwt_generator.*
      • api/app/services/legacy_admin_rbac_service.py
        • 将 4 个菜单码加入 REMOVED_MENU_CODES
      • api/app/services/admin_service.py
        • 将 4 个菜单码加入 REMOVED_MENU_CODES,并从受保护删除集合中移除。
    • 后端接口挂载下线:

      • api/app/api/router.py
        • 移除 jwt_generator_router import 与 include_router,JWT 生成器 API 不再对外挂载。
  • 验证:

    • python3 -m py_compile api/app/services/seed_service.py api/app/services/legacy_authz_service.py api/app/services/legacy_admin_rbac_service.py api/app/services/admin_service.py api/app/api/router.py -> 通过。
    • python3 -m compileall api/app -> 通过。
    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
    • npm --workspace web exec next build --webpack -> 通过;产物路由确认不再包含:
      • /admin/cron
      • /admin/todos
      • /admin/job
      • /admin/jwt-generator
  • 风险与影响:

    • api/app/api/v1/jwt_generator.py 文件仍保留在仓库,但已从路由汇总中移除,不可外部访问。
    • 当前环境下 Next 构建偶发 .next 产物拷贝 ENOENT 警告(非本次改动引入,存在环境不稳定性);本轮最终构建已成功产出路由清单。

Work Log - 修复 /users 首屏卡在 Loading2026-04-23

  • 背景:访问 http://localhost:3000/users 时,后台壳层页面一直停留在 Loading admin workspace...

  • 根因:

    • web/src/app/admin/layout.tsxloadMenus 仅处理了接口返回 !ok 场景;
    • fetchWithAuth("/api/v1/admin/me/menus") 发生网络异常(例如 API 未启动、连接失败)抛出异常时,没有兜底 catch/finally,导致 loadingMenus 一直为 true
  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • loadMenus 增加 try/catch/finally
      • 异常场景下会:
        • 清空 menuTree
        • 设置 menuError(显示可见错误)
        • finally 中统一 setLoadingMenus(false),避免页面卡死在 loading。
      • 无用户场景补充 setMenuError(""),避免遗留错误文案。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
  • 风险与影响:

    • 仅涉及后台壳层菜单加载状态管理,不改接口契约与业务数据。

Work Log - 默认 admin 视为全权限(2026-04-23

  • 背景:要求“默认 admin 有全部权限”。

  • 现状定位:

    • 后端接口鉴权已具备 admin 角色兜底(require_permission/require_any_permissionif "admin" in role_codes: allow)。
    • 前端权限判定 hasPermission 仅判断 permission_codes.includes(...),未对 admin 角色做兜底,可能导致 admin 页面按钮/入口被误隐藏。
  • 本次改动(最小闭环):

    • web/src/components/auth-provider.tsx
      • hasPermission 调整为:
        • 若用户角色包含 admin,直接返回 true
        • 否则按原逻辑判断 permission_codes
  • 验证:

    • 先清理 Next 类型缓存:rm -rf web/.next
    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
  • 风险与影响:

    • 仅影响前端可见性与交互开关判定,不改后端鉴权规则。
    • 后端仍保持真实权限校验,前端放行不会绕过服务端安全边界。

Work Log - 去掉登录后首页,直接进入后台(2026-04-23)

  • 背景:要求“去掉首页,登录完成直接进入后台”。

  • 本次改动(最小闭环):

    • web/src/app/page.tsx
      • 移除已登录态的首页欢迎面板(欢迎文案 / Ping / 快捷入口)。
      • 新增登录态跳转:当 !initializing && user 时执行 router.replace("/dashboard")
      • 已登录时仅短暂显示“正在进入后台...”,避免闪现旧首页内容。
    • web/src/app/admin/layout.tsx
      • 未登录访问后台时按钮文案从“返回首页”调整为“前往登录”(/)。
      • 账号下拉中的“返回首页”调整为“后台首页”(/dashboard)。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
  • 风险与影响:

    • 仅涉及前端路由与文案,不改后端接口与鉴权逻辑。

Work Log - 右上角账号入口改为 Avatar2026-04-23

  • 背景:要求“右上角账号改成 avatar 组件”。

  • 本次改动(最小闭环):

    • web/src/app/admin/layout.tsx
      • 顶部账号下拉触发器由“账号”按钮改为 AntD Avatar
      • 头像文案取当前用户名首字母(大写),空值兜底 U
      • 保持原下拉菜单项不变(账号信息、后台首页、退出登录)。
  • 验证:

    • npm --workspace web exec tsc --noEmit --pretty false -> 通过。
  • 风险与影响:

    • 仅涉及前端展示与交互入口样式,不改业务逻辑与接口调用。