43 KiB
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。 - 归档逻辑对齐 quiz:
archive仅修改archived状态,保留原记录。
api/app/api/v1/diary.py- 新增接口:
POST /api/v1/diary/searchGET /api/v1/diary/get/{id}POST /api/v1/diary/createPUT /api/v1/diary/updateDELETE /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__.py、api/app/core/database.py注册diary模型,确保create_all生效。
-
前端页面改造(替换 syslog 复用):
web/src/app/admin/diary/page.tsx- 改为独立 Diary 页面:
- 列表分页
- 条件查询(标题、心情、日期范围、归档状态)
- 新增 / 编辑 / 删除
- 归档 / 取消归档
- 详情弹窗
- API 调整到新后端接口
/api/v1/diary/*。
- 改为独立 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.pyLoginRequest从email + password改为user_id + password(密码最小长度放宽为 1,兼容老库短密码)。
web/src/components/auth-provider.tsxlogin(...)传参改为user_id + password,请求体同步改为{ user_id, password }。
web/src/app/page.tsx- 登录表单输入从
Email改为User ID,记住密码缓存字段同步改为userId。
- 登录表单输入从
-
密码与状态兼容:
api/app/core/security.pyverify_password增加BCrypt兼容分支(老工程口令),保留现有Argon2支持。
api/app/services/legacy_authz_service.py(新增)- 新增用户状态归一:
ENABLED/ACTIVE/1/TRUE -> active,DISABLED/... -> disabled。
- 新增用户状态归一:
api/app/models/user.py- 用户默认状态改为
ENABLED;默认主键改为uuid4().hex(32 位,兼容老表长度习惯)。
- 用户默认状态改为
-
旧表角色/权限装配兼容层(核心):
api/app/services/legacy_authz_service.py(新增)- 从老表链路读取授权信息:
user_role_rela -> user_role计算role_codesrole_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.pyget_current_user改为用户基础信息 + 兼容层授权计算。
api/app/api/v1/ws.py- WS 鉴权改为复用兼容层
role_codes/permission_codes。
- WS 鉴权改为复用兼容层
api/app/services/jwt_generator_service.py- JWT 生成与用户列表角色展示改为兼容层口径,状态过滤兼容
ENABLED。
- JWT 生成与用户列表角色展示改为兼容层口径,状态过滤兼容
api/app/services/user_service.pyserialize_user/queue_user_auth_refresh改为兼容层计算授权信息。- 用户状态写入统一落库存储值
ENABLED/DISABLED。 - 用户角色更新改为直连老表
user_role_rela写入(rela_id + user_id + role_id)。
api/app/services/admin_service.pybuild_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:155,Card组件children类型不匹配(ui-antd兼容层类型定义问题)。
- 后端编译:
-
风险与影响:
- 本轮优先闭环“登录 + 鉴权 + 菜单树”,未对
admin/roles、admin/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_role、user_role_rela、role_menu_rela - 菜单:
menu、role_menu_rela
- 角色:
- 支持:
list/get/create/update/delete role、list/get/create/update/delete menu、list/replace role menus、list permissions。 - 保护项兼容:保留受保护角色与菜单编码删除拦截。
- 修复更新边界:
menu.parent_id支持显式清空;角色/菜单名称做strip空值校验。
- 完成并启用老表 CRUD:
api/app/api/v1/admin.py- 角色/菜单相关路由改为调用
legacy_admin_rbac_service。 - 角色/菜单 path 参数改为字符串:
role_id: str、menu_id: str。 GET /admin/roles/{role_id}/menus返回改为list[str]。
- 角色/菜单相关路由改为调用
api/app/schemas/admin.py- 角色/菜单契约改为字符串 ID:
RolePublic.id、RolePublic.menu_idsRoleCreateRequest.menu_ids、RoleUpdateRequest.menu_idsMenuPublic.id、MenuPublic.parent_idMenuCreateRequest.parent_id、MenuUpdateRequest.parent_idRoleMenuUpdateRequest.menu_ids
- 角色/菜单契约改为字符串 ID:
-
鉴权菜单树 ID 口径对齐:
api/app/services/legacy_authz_service.pybuild_legacy_menu_tree改为直接使用老表menu.menu_id/parent_id作为菜单树 ID,不再生成临时递增 ID。
-
前端类型与页面对齐:
web/src/types/auth.tsRoleItem.id/menu_ids、MenuItem.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:155,Card组件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.tsxCard包装器改用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.tsxweb/src/app/admin/mindmap/_components/mindmap-editor.tsx- 从
antd直引Card改为@/components/ui-antd的Card。
- 严格 TS 隐式 any 清理(与现有 strict 配置对齐):
web/src/app/admin/mermaid-mgr/_components/mermaid-editor.tsxweb/src/app/admin/mermaid-mgr/page.tsxweb/src/app/admin/mindmap/_components/mindmap-editor.tsxweb/src/app/admin/schedule/page.tsxweb/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/src内import { 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/menus500):- 冒烟命中
500,日志定位到legacy_authz_service.build_legacy_menu_tree使用了未定义变量node_id。 - 处理:
api/app/services/legacy_authz_service.py将nodes[node_id]修正为nodes[legacy_id],重建后恢复。
- 冒烟命中
- 容器代码加载校验:
-
冒烟验证(只读,不写库):
- 登录契约校验:
POST /api/v1/auth/login传{user_id, password}-> 401(凭证错误,说明契约已生效);- 传
{email, password}-> 422(缺少user_id)。
- 管理后台主链路:
GET /api/v1/admin/roles-> 200,RolePublic.id/menu_ids为字符串;GET /api/v1/admin/menus-> 200,MenuPublic.id/parent_id为字符串口径;GET /api/v1/admin/me/menus-> 200,菜单树根节点id为字符串;GET /api/v1/admin/roles/sys_mgr/menus-> 200,menu_ids为字符串数组;GET /api/v1/admin/permissions-> 200。
- 登录契约校验:
-
相关文件:
api/requirements.txtapi/app/services/legacy_authz_service.py
-
风险与影响:
- 本轮未执行角色/菜单写接口(create/update/delete),避免对外部数据库产生变更;当前验证覆盖“登录契约 + 权限读取 + 菜单读取”读路径。
Work Log - 第六轮:修复日程页 Modal.footer 类型不兼容(2026-04-23)
-
背景:
docker compose build web在web/src/app/admin/schedule/page.tsx:747失败,报错为Modal.footer回调签名不匹配(OkBtn/CancelBtn被错误标注为() => ReactElement)。 -
本次改动(最小闭环):
web/src/app/admin/schedule/page.tsx- 补充 React 类型导入:
FC、ReactNode。 Modal.footer回调类型从:_origin: unknown{ OkBtn: () => ReactElement; CancelBtn: () => ReactElement }调整为:_origin: ReactNode{ OkBtn: FC; CancelBtn: FC }
- 保持现有按钮渲染与删除/保存逻辑不变,仅修复类型契约。
- 补充 React 类型导入:
-
验证:
npm run build:web-> 通过(Next.js 编译、TypeScript 检查、静态页面生成全部完成)。
-
风险与影响:
- 仅影响前端类型声明,不涉及接口契约、请求参数、业务分支和数据写入行为。
Work Log - 第七轮:后台系统菜单改用 AntD Menu 组件(2026-04-23)
-
背景:当前后台左侧“系统菜单”是递归
Button + Link,移动端是DropdownMenu。按要求切换为 Ant DesignMenu组件承载菜单树。 -
本次改动(最小闭环):
web/src/app/admin/layout.tsx- 新增
Menu as AntMenu与MenuProps引入。 - 移除递归渲染函数
renderMenuNodes与移动端flattenMenuPaths下拉菜单构建逻辑。 - 新增
buildMenuItems:把后端返回的MenuTreeItem[]转为AntMenu的items结构(支持嵌套)。 - 新增
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\")承载后台菜单树。 - 移动端菜单改为左侧
Drawer(placement=\"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- 布局从“左侧固定菜单 + 内容头卡片”调整为“顶部固定导航 + 内容区”。
- 顶栏结构改为:左侧品牌 Logo(
Q + 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=5、user_role=6、menu=64、role_menu_rela=123。
- 表存在:
- 数据导出留档:
- CSV 导出文件:
tmp/84-export/users_84.csvtmp/84-export/user_role_84.csvtmp/84-export/menu_84.csvtmp/84-export/role_menu_rela_84.csv
- 初始化脚本导出:
tmp/84-export/legacy_auth_schema.sqltmp/84-export/legacy_auth_data.sqltmp/84-export/legacy_auth_data_wrapped.sql
- CSV 导出文件:
- 本地库初始化:
- 目标库:本地
fquiz-db的postgres数据库(用户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后回放,导入成功。
- 目标库:本地
- 远端 84 库(
-
验证:
- 远端与本地(postgres)按表对比
count + md5(signature)全量一致:users:5/a50fedde66f156b0442d792b42c355b7user_role:6/7fb7d2520f44efe25e8be15762a3bd3dmenu:64/094b533fe91b853868a0f9e4356da49arole_menu_rela:123/3c752bb95375e80d835b198831d44535
- 远端与本地(postgres)按表对比
-
风险与影响:
- 本次初始化目标是本地
postgres库,不影响本地fquiz库现有表数据。 users表为老工程结构(user_id/user_name/password/state...),与新表结构不同;后续若切回新 RBAC 表需区分数据库与表口径。
- 本次初始化目标是本地
Work Log - 第十轮:数据库默认目标切换到本地并修复启动兼容(2026-04-23)
-
背景:按“现在切到本地库,不用84了”要求,将默认数据库目标从
223.109.142.84切到本地fquiz-db。 -
本次改动(最小闭环):
docker-compose.ymldb服务移除local-dbprofile,改为默认启动。api新增depends_on: db (service_healthy)。api的默认 DB 参数切到本地:DB_HOST=dbDB_PORT=5432DB_NAME=postgresDB_USERNAME=fquizDB_PASSWORD=fquiz
api/app/core/config.py- 非 Docker 默认 DB 参数改为本机本地库:
db_host=127.0.0.1db_port=5433db_name=postgresdb_username=fquizdb_password=fquiz
- 非 Docker 默认 DB 参数改为本机本地库:
.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.jscopyfile 丢失。
- Turbopack 路径:
-
风险与影响:
- 本次仅改后台壳层菜单交互,不涉及 API 契约和业务数据写入。
- 当前构建环境存在
.next产物落盘异常,影响build:web门禁稳定性(与菜单逻辑无直接耦合)。
Work Log - 第十二轮:支持主题色切换并持久化(2026-04-23)
-
背景:按“支持主题色切换”要求,增加可视化主题色切换入口,并要求刷新后保持。
-
本次改动(最小闭环):
web/src/components/ui-antd.tsx- 新增主题外观上下文
ThemeAppearanceContext。 - 新增
useThemeAppearance(),对外暴露:accentColorsetAccentColor(...)
- 新增
THEME_ACCENT_OPTIONS(靛蓝/蓝色/青色/绿色/橙色/红色/粉色/紫色)。 Theme组件增加主题色状态与持久化:- 启动时读取
localStorage["fquiz:theme:accent-color"] - 切换后写回 localStorage
- 切换即更新 AntD
ConfigProvider的colorPrimary,全局生效。
- 启动时读取
- 新增主题外观上下文
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
- AntD 直连变量:
- 通过
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_countdownadmin.passwordadmin.token_usageadmin.historyadmin.vocabularyadmin.diaryadmin.homeworkadmin.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,确保“作业监控”权限链路不受影响。
- 将上述 8 个菜单加入
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_router与life_countdown_router。
- 不再挂载
api/app/api/v1/admin.py- 删除密钥管理专用接口:
GET /api/v1/admin/password/modelsGET /api/v1/admin/password/models/{model_id}/keysPOST /api/v1/admin/password/models/{model_id}/rotate-key
- 删除密钥管理专用接口:
-
前端页面下线:
- 删除路由页面:
web/src/app/admin/life-countdown/page.tsxweb/src/app/admin/password/page.tsxweb/src/app/admin/price-monitor/page.tsxweb/src/app/admin/token-usage/page.tsxweb/src/app/admin/history/page.tsxweb/src/app/admin/poetry/page.tsxweb/src/app/admin/diary/page.tsxweb/src/app/admin/homework/page.tsxweb/src/app/admin/question-bank/page.tsxweb/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_bank与vocabulary底层 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.tsxweb/src/app/admin/mdresolve/page.tsxweb/src/app/admin/data-query/page.tsxweb/src/app/admin/hot-search/page.tsxweb/src/app/admin/filedetector/page.tsxweb/src/app/admin/baidu-pan/page.tsxweb/src/app/admin/group/page.tsxweb/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,统一影响菜单列表、角色菜单关联、权限推导。
- 将 8 个菜单码加入
-
后端:停用对应接口挂载与默认种子:
api/app/api/router.py- 移除
hot_search_router与mdresolve_router挂载。
- 移除
api/app/services/topic_registry.py- 移除
admin.hot_search与admin.hot_search.follow_topicstopic 规则。
- 移除
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.py与api/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.tsxweb/src/app/admin/todos/page.tsxweb/src/app/admin/job/page.tsxweb/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.tsxweb/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_CODES中jwt_generator.*。
- 将 4 个菜单码加入
api/app/services/legacy_admin_rbac_service.py- 将 4 个菜单码加入
REMOVED_MENU_CODES。
- 将 4 个菜单码加入
api/app/services/admin_service.py- 将 4 个菜单码加入
REMOVED_MENU_CODES,并从受保护删除集合中移除。
- 将 4 个菜单码加入
-
后端接口挂载下线:
api/app/api/router.py- 移除
jwt_generator_routerimport 与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 首屏卡在 Loading(2026-04-23)
-
背景:访问
http://localhost:3000/users时,后台壳层页面一直停留在Loading admin workspace...。 -
根因:
web/src/app/admin/layout.tsx的loadMenus仅处理了接口返回!ok场景;- 当
fetchWithAuth("/api/v1/admin/me/menus")发生网络异常(例如 API 未启动、连接失败)抛出异常时,没有兜底catch/finally,导致loadingMenus一直为true。
-
本次改动(最小闭环):
web/src/app/admin/layout.tsxloadMenus增加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_permission中if "admin" in role_codes: allow)。 - 前端权限判定
hasPermission仅判断permission_codes.includes(...),未对admin角色做兜底,可能导致 admin 页面按钮/入口被误隐藏。
- 后端接口鉴权已具备
-
本次改动(最小闭环):
web/src/components/auth-provider.tsxhasPermission调整为:- 若用户角色包含
admin,直接返回true; - 否则按原逻辑判断
permission_codes。
- 若用户角色包含
-
验证:
- 先清理 Next 类型缓存:
rm -rf web/.next npm --workspace web exec tsc --noEmit --pretty false-> 通过。
- 先清理 Next 类型缓存:
-
风险与影响:
- 仅影响前端可见性与交互开关判定,不改后端鉴权规则。
- 后端仍保持真实权限校验,前端放行不会绕过服务端安全边界。
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 - 右上角账号入口改为 Avatar(2026-04-23)
-
背景:要求“右上角账号改成 avatar 组件”。
-
本次改动(最小闭环):
web/src/app/admin/layout.tsx- 顶部账号下拉触发器由“账号”按钮改为 AntD
Avatar。 - 头像文案取当前用户名首字母(大写),空值兜底
U。 - 保持原下拉菜单项不变(账号信息、后台首页、退出登录)。
- 顶部账号下拉触发器由“账号”按钮改为 AntD
-
验证:
npm --workspace web exec tsc --noEmit --pretty false-> 通过。
-
风险与影响:
- 仅涉及前端展示与交互入口样式,不改业务逻辑与接口调用。