fix(menu): 修复菜单管理编辑无法保存与新增 500 - 根因一:MenuService create/update 以 snake_case 列名作 Sequelize 写入键而实体属性为 camelCase,导致写入被静默忽略(更新假成功)或 NOT NULL 报错(新增 500),改为 camelCase 属性名;根因二:sys_menu 列默认值指向历史遗留重复序列 sys_menu_id_seq1 且未推进,新增菜单 id 冲突,生产库已一次性 setval 同步(max(id)=64,并核对 sys_role/sys_user 序列),db_seed.sql 全部 setval 改为 pg_get_serial_sequence() 形式兼容不同环境序列名;接口与浏览器端到端验证通过(新增→改名→持久化→清理)

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
This commit is contained in:
wkandClaude Sonnet 4.6 noreply@anthropic.com authored and trae-solo-agent committed 2026-10-24 17:26:43 +08:00
1 parent 44fc9a1d54
commit 7ff2405634
5 files changed
+72 -17

No files matched your search

@@ -0,0 +1,8 @@
# Checklist
- [x] MenuService.create() 以 camelCase 属性名写入(parentId/menuName/menuPath/component/icon/sortOrder/menuType/permission/visible),父菜单 parentId=null 语义保留
- [x] MenuService.update() 以 camelCase 属性名写入,部分字段更新不会清空未提交字段
- [x] sys_menu 自增序列失步已修复(生产库 setval 到 max(id)=64;sys_role/sys_user 序列一并核对),db_seed.sql 的 setval 全部改为 pg_get_serial_sequence() 形式以兼容不同环境序列名
- [x] 后端 tsc --noEmit 通过
- [x] PUT /api/menus/:id 修改名称后重新 GET 菜单树已持久化;POST /api/menus 返回 200 且新菜单可见(测试数据已清理)
- [x] 浏览器菜单管理页:编辑名称保存成功、树实时刷新、刷新页面后名称保持;新增菜单可用
+31
View File
@@ -0,0 +1,31 @@
# 修复菜单管理「编辑无法保存」Spec
## Why
菜单管理页面点击「编辑」修改菜单名称后点击保存,接口返回「更新菜单成功」但数据未持久化(名称未变);新增菜单则直接返回 500。根因:`MenuService.create/update` 以 snake_case 数据库列名(`menu_name` 等)作为 Sequelize 的取值键,而实体属性是 camelCase(`menuName` 等),Sequelize 无法匹配从而静默忽略(update)或触发 NOT NULL 报错(create)。
## What Changes
- 修正 `server/src/service/menu.service.ts` 中 `create()` 与 `update()`:将写入 Sequelize 的键改为实体 camelCase 属性名(`parentId/menuName/menuPath/component/icon/sortOrder/menuType/permission/visible`),字段映射由实体 `field` 配置负责。
- 前端与接口协议不变(前端已发送 camelCase body,`menu.controller.ts`、`menu.dto.ts`、`client/src/api/menu.ts` 均无需改动)。
- **BREAKING**: 无(仅为内部映射修正,接口行为从「假成功/500」变为「真实持久化」)。
## Impact
- Affected specs: 系统管理域(sys_menu RBAC)
- Affected code: `server/src/service/menu.service.ts`(仅 create/update 两处取值键);连带修复后「新增菜单」「编辑菜单」均恢复可用
## ADDED Requirements
(无新增能力)
## MODIFIED Requirements
### Requirement: 菜单新增与编辑持久化
菜单管理的「新增」与「编辑」操作 SHALL 将表单值真实写入 `sys_menu` 表。
#### Scenario: 编辑菜单名称后保存
- **WHEN** 用户在菜单管理中点击某菜单「编辑」,修改菜单名称并点击确定
- **THEN** 接口返回成功,重新拉取菜单树显示新名称,且刷新页面后名称保持不变
#### Scenario: 新增菜单
- **WHEN** 用户在菜单管理中点击「新增」,填写必填项并确定
- **THEN** 接口返回 200 并持久化,菜单树中出现新菜单(而非 500)
## REMOVED Requirements
(无)
+16
View File
@@ -0,0 +1,16 @@
# Tasks
- [x] Task 1: 修复 MenuService 写入键映射
- [x] 1.1 阅读 menu.service.ts,确认 create()/update() 中 snake_case 键的位置
- [x] 1.2 将 create() 与 update() 写入 Sequelize 的键改为实体 camelCase 属性名,并确保父子菜单 parentId=null 与 visible 处理逻辑不变
- [x] 1.3 后端 `npx tsc --noEmit` 通过
- [x] Task 2: 修复 sys_menu 自增序列失步(新增菜单 500 的根因)
- [x] 2.1 定位根因:列默认值指向 sys_menu_id_seq1 且停留在 2(显式 id 插入不推进序列),与已存在 id 冲突
- [x] 2.2 生产库一次性同步序列(setval 到 max(id)=64),并顺带核对 sys_role/sys_user 序列
- [x] 2.3 db_seed.sql 全部 setval 改为 pg_get_serial_sequence() 形式,兼容不同环境序列名
- [x] Task 3: 验证
- [x] 3.1 接口自测:PUT /api/menus/:id 修改菜单名称后重新 GET 树,名称已持久化
- [x] 3.2 接口自测:POST /api/menus 新增菜单返回 200 且树中出现新菜单(自测后清理测试数据)
- [x] 3.3 浏览器验证:菜单管理页编辑名称保存后树实时刷新、刷新页面后保持
# Task Dependencies
- [Task 2][Task 3] depend on [Task 1]