## 1. DTO 结构定义 - [x] 1.1 在 `internal/model/dto/auth_dto.go` 中新增 `MenuNode` 结构体,包含 `ID`, `PermCode`, `Name`, `URL`, `Sort`, `Children` 字段,所有字段添加 JSON 标签 - [x] 1.2 修改 `LoginResponse` 结构体,新增 `Menus []MenuNode` 和 `Buttons []string` 字段,添加 JSON 标签和 description 注释 - [x] 1.3 运行 `lsp_diagnostics` 验证 DTO 文件无错误 ## 2. Service 层核心方法实现 - [x] 2.1 在 `internal/service/auth/service.go` 中新增 `getUserPermissionsAndMenus()` 方法,接收参数 `ctx, userID, userType, device`,返回 `([]string, []MenuNode, []string, error)` - [x] 2.2 在 `getUserPermissionsAndMenus()` 中实现超级管理员逻辑:调用 `permissionStore.GetAll(ctx, nil)` 查询所有启用的权限 - [x] 2.3 在 `getUserPermissionsAndMenus()` 中实现普通用户逻辑:复用现有的角色权限查询(`accountRoleStore.GetByAccountID()` → `rolePermStore.GetPermIDsByRoleIDs()` → `permissionStore.GetByIDs()`) - [x] 2.4 新增 `classifyPermissions()` 方法,接收参数 `permissions []*model.Permission, device string`,实现权限分类和平台过滤逻辑 - [x] 2.5 在 `classifyPermissions()` 中实现平台过滤:`platform == "all"` 或 `platform == device` 时保留,否则跳过 - [x] 2.6 在 `classifyPermissions()` 中实现权限分类:`perm_type == 1` 的收集到 `menuPerms`,`perm_type == 2` 的提取 `perm_code` 到 `buttonCodes` - [x] 2.7 在 `classifyPermissions()` 中收集所有权限码到 `allCodes` 数组(用于 `permissions` 字段) ## 3. 菜单树构建逻辑 - [x] 3.1 新增 `buildMenuTree()` 方法,接收参数 `permissions []*model.Permission`,返回 `[]MenuNode` - [x] 3.2 在 `buildMenuTree()` 中实现第一步:创建节点映射 `nodeMap := make(map[uint]*dto.MenuNode)`,遍历权限列表构建 MenuNode 对象 - [x] 3.3 在 `buildMenuTree()` 中实现第二步:组织父子关系,根据 `parent_id` 将节点追加到父节点的 `Children` 数组或 `roots` 数组 - [x] 3.4 在 `buildMenuTree()` 中实现孤儿节点处理:如果 `parent_id` 不在 `nodeMap` 中,将节点提升为根节点,并记录警告日志 - [x] 3.5 新增 `sortMenuNodes()` 方法,接收参数 `nodes []MenuNode`,实现递归排序(根据 `Sort` 字段升序) - [x] 3.6 在 `buildMenuTree()` 中调用 `sortMenuNodes(roots)` 完成排序后返回 ## 4. 超级管理员专用逻辑 - [x] 4.1 新增 `getAllPermissionsForSuperAdmin()` 方法,接收参数 `ctx, device`,返回 `([]string, []MenuNode, []string, error)` - [x] 4.2 在 `getAllPermissionsForSuperAdmin()` 中调用 `permissionStore.GetAll(ctx, nil)` 查询所有启用的权限 - [x] 4.3 在 `getAllPermissionsForSuperAdmin()` 中调用 `classifyPermissions(allPerms, device)` 完成分类和过滤 ## 5. 修改 Login 方法 - [x] 5.1 在 `auth.Service.Login()` 方法中,将 `getUserPermissions(ctx, account.ID)` 替换为 `getUserPermissionsAndMenus(ctx, account.ID, account.UserType, device)` - [x] 5.2 在 `Login()` 方法中,将返回的 `(permissions, menus, buttons, err)` 赋值到 `LoginResponse` 的对应字段 - [x] 5.3 处理错误:如果 `getUserPermissionsAndMenus()` 失败,记录错误日志,返回空的 `menus: []` 和 `buttons: []`(不阻塞登录) - [x] 5.4 运行 `lsp_diagnostics` 验证 Service 文件无错误 ## 6. 单元测试 - buildMenuTree - [x] 6.1 创建 `internal/service/auth/menu_tree_test.go` 文件 - [x] 6.2 编写测试 `TestBuildMenuTree_RootNodes`:测试只有根节点的场景(`parent_id = NULL`) - [x] 6.3 编写测试 `TestBuildMenuTree_MultiLevel`:测试两级或三级嵌套菜单场景 - [x] 6.4 编写测试 `TestBuildMenuTree_OrphanNodes`:测试孤儿节点提升为根节点的场景(`parent_id` 不存在) - [x] 6.5 编写测试 `TestBuildMenuTree_Sorting`:测试菜单排序(根据 `sort` 字段升序) - [x] 6.6 编写测试 `TestBuildMenuTree_EmptyInput`:测试空权限列表输入,验证返回空数组 - [x] 6.7 运行单元测试:`source .env.local && go test -v ./internal/service/auth/...`,确保所有测试通过 ## 7. 单元测试 - classifyPermissions - [x] 7.1 创建 `internal/service/auth/classify_test.go` 文件 - [x] 7.2 编写测试 `TestClassifyPermissions_PlatformFilter`:测试平台过滤(`device="web"` 时过滤 `platform="h5"` 的权限) - [x] 7.3 编写测试 `TestClassifyPermissions_MenuAndButton`:测试菜单和按钮权限分类(`perm_type=1` vs `perm_type=2`) - [x] 7.4 编写测试 `TestClassifyPermissions_AllPermissions`:测试所有权限码收集(包含菜单和按钮) - [x] 7.5 编写测试 `TestClassifyPermissions_PlatformAll`:测试 `platform="all"` 的权限对所有端口可见 - [x] 7.6 运行单元测试:`source .env.local && go test -v ./internal/service/auth/...`,确保所有测试通过 ## 8. 单元测试 - getUserPermissionsAndMenus - [x] 8.1 编写测试 `TestGetUserPermissionsAndMenus_SuperAdmin`:测试超级管理员返回所有权限 - [x] 8.2 编写测试 `TestGetUserPermissionsAndMenus_NormalUser`:测试普通用户返回角色权限 - [x] 8.3 编写测试 `TestGetUserPermissionsAndMenus_NoPermissions`:测试用户无权限时返回空数组 - [x] 8.4 编写测试 `TestGetUserPermissionsAndMenus_DeviceFilter`:测试 `device` 参数过滤平台 - [x] 8.5 运行单元测试:`source .env.local && go test -v ./internal/service/auth/...`,确保所有测试通过 ## 9. 集成测试 - Login API - [x] 9.1 创建或修改 `tests/integration/admin_auth_test.go` 文件 - [x] 9.2 编写测试 `TestAdminLogin_MenusAndButtons`:验证登录响应包含 `menus`, `buttons`, `permissions` 三个字段 - [x] 9.3 编写测试 `TestAdminLogin_MenuTreeStructure`:验证 `menus` 为树形结构,包含 `id`, `perm_code`, `name`, `url`, `sort`, `children` 字段 - [x] 9.4 编写测试 `TestAdminLogin_ButtonsArray`:验证 `buttons` 为扁平数组,只包含 `perm_type=2` 的权限码 - [x] 9.5 编写测试 `TestAdminLogin_SuperAdmin`:验证超级管理员登录时返回所有菜单和按钮 - [x] 9.6 编写测试 `TestAdminLogin_PlatformFilter`:验证 `device="web"` 时不返回 `platform="h5"` 的菜单 - [x] 9.7 编写测试 `TestAdminLogin_NoPermissions`:验证无权限用户登录时返回空的 `menus` 和 `buttons` - [x] 9.8 运行集成测试:`source .env.local && go test -v ./tests/integration/...`,确保所有测试通过 ## 10. 集成测试 - GetMe API - [x] 10.1 编写测试 `TestAdminGetMe_NoMenus`:验证 GetMe 接口不返回 `menus` 和 `buttons` 字段 - [x] 10.2 编写测试 `TestAdminGetMe_OnlyUserAndPermissions`:验证 GetMe 接口只返回 `user` 和 `permissions` 字段 - [x] 10.3 运行集成测试:`source .env.local && go test -v ./tests/integration/...`,确保所有测试通过 ## 11. 性能测试 - [x] 11.1 编写性能基准测试 `BenchmarkBuildMenuTree`:测试不同权限数量(50、100、200)的菜单树构建性能 - [x] 11.2 编写性能基准测试 `BenchmarkClassifyPermissions`:测试权限分类性能 - [x] 11.3 运行基准测试:`source .env.local && go test -bench=. -benchmem ./internal/service/auth/...` - [x] 11.4 验证登录接口响应时间增加 < 50ms(在 50 个权限的场景下) ## 12. 代码审查和优化 - [x] 12.1 运行 `lsp_diagnostics` 检查所有修改的文件,确保无类型错误和警告 - [x] 12.2 运行 `gofmt -w ./internal/service/auth/service.go` 格式化代码 - [x] 12.3 运行 `gofmt -w ./internal/model/dto/auth_dto.go` 格式化代码 - [x] 12.4 检查所有注释使用中文,变量名和函数名使用英文 - [x] 12.5 检查错误处理:使用 `errors.New()` 或 `errors.Wrap()`,不使用 `fmt.Errorf()` - [x] 12.6 检查日志记录:孤儿节点检测时记录警告日志(使用 Zap) ## 13. 文档更新 - [x] 13.1 更新 `README.md`:在"核心功能"部分添加"登录接口返回菜单树和按钮权限"说明 - [x] 13.2 创建 `docs/login-menu-button-response/使用指南.md`:说明前端如何使用 `menus` 和 `buttons` 字段 - [x] 13.3 在使用指南中添加 localStorage 缓存示例代码 - [x] 13.4 在使用指南中添加 MenuNode 数据结构说明和示例响应 - [x] 13.5 在使用指南中添加性能影响说明和最佳实践建议 ## 14. 最终验证 - [x] 14.1 运行完整测试套件:`source .env.local && go test ./...`,确保所有测试通过 - [x] 14.2 启动 API 服务:`go run cmd/api/main.go`,验证服务正常启动 - [x] 14.3 使用 Postman/curl 测试登录接口:验证响应包含 `menus`, `buttons`, `permissions` 三个字段 - [x] 14.4 验证响应体大小 < 20KB(普通用户场景) - [x] 14.5 验证 GetMe 接口不返回 `menus` 和 `buttons` 字段 - [x] 14.6 验证向后兼容性:旧版前端仍可使用 `permissions` 字段