Files
junhong_cmp_fiber/openspec/changes/archive/2026-02-03-shop-role-inheritance/tasks.md
huang 5a90caa619
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m39s
feat(shop-role): 实现店铺角色继承功能和权限检查优化
- 新增店铺角色管理 API 和数据模型
- 实现角色继承和权限检查逻辑
- 添加流程测试框架和集成测试
- 更新权限服务和账号管理逻辑
- 添加数据库迁移脚本
- 归档 OpenSpec 变更文档

Ultraworked with Sisyphus
2026-02-03 10:06:13 +08:00

13 KiB
Raw Blame History

店铺级角色继承功能实现任务清单

1. 数据库层实现

  • 1.1 创建数据库迁移文件 migrations/YYYYMMDDHHMMSS_add_shop_role_table.up.sql

    • 创建 tb_shop_role
    • 添加唯一约束 (shop_id, role_id) WHERE deleted_at IS NULL
    • 创建索引 idx_shop_role_shop_ididx_shop_role_role_ididx_shop_role_deleted_at
    • 验证:执行 migrate -path migrations -database "..." up 成功
  • 1.2 创建数据库迁移回滚文件 migrations/YYYYMMDDHHMMSS_add_shop_role_table.down.sql

    • 删除 tb_shop_role
    • 验证:执行 migrate -path migrations -database "..." down 成功

2. Model 层实现

  • 2.1 创建 internal/model/shop_role.go

    • 定义 ShopRole 结构体,包含所有字段和 GORM 标签
    • 实现 TableName() 方法返回 "tb_shop_role"
    • 验证:运行 go build ./internal/model/,无编译错误
  • 2.2 创建 internal/model/dto/shop_role_dto.go

    • 定义 AssignShopRolesRequest 结构体(包含 role_ids 字段和 description 标签)
    • 定义 ShopRoleResponse 结构体(包含店铺和角色详情)
    • 定义 ShopRolesResponse 结构体(包含 shop_idroles 列表)
    • 验证:运行 go build ./internal/model/dto/,无编译错误

3. Store 层实现

  • 3.1 创建 internal/store/postgres/shop_role_store.go

    • 实现 ShopRoleStore 结构体,包含 dbredisClient 字段
    • 实现 NewShopRoleStore() 构造函数
    • 实现 Create() 方法(创建单个店铺角色关联)
    • 实现 BatchCreate() 方法(批量创建)
    • 实现 Delete() 方法(删除指定店铺角色关联)
    • 实现 DeleteByShopID() 方法(删除店铺的所有角色关联)
    • 实现 GetByShopID() 方法(查询店铺的所有角色关联)
    • 实现 GetRoleIDsByShopID() 方法(查询店铺的所有角色 ID
    • 实现 clearShopRoleCache() 私有方法(清理店铺下所有账号的权限缓存)
    • 验证:运行 go build ./internal/store/postgres/,无编译错误
  • 3.2 编写 ShopRoleStore 单元测试

    • 测试文件:internal/store/postgres/shop_role_store_test.go
    • 测试 Create() 成功场景
    • 测试 BatchCreate() 成功场景
    • 测试 Delete() 成功场景
    • 测试 DeleteByShopID() 成功场景
    • 测试 GetByShopID() 成功场景
    • 测试 GetRoleIDsByShopID() 成功场景
    • 测试唯一性约束冲突
    • 验证:运行 source .env.local && go test -v ./internal/store/postgres/ -run TestShopRoleStore,所有测试通过

4. Service 层实现

  • 4.1 创建 internal/service/account/role_resolver.go

    • 实现 GetRoleIDsForAccount(ctx, accountID) ([]uint, error) 方法
    • 实现角色解析逻辑:
      • 超级管理员返回空数组
      • 查询账号级角色,如有则返回
      • 代理账号且无账号级角色,查询店铺级角色并返回
      • 其他用户类型返回空数组
    • 验证:运行 go build ./internal/service/account/,无编译错误
  • 4.2 编写 GetRoleIDsForAccount 单元测试

    • 测试文件:internal/service/account/role_resolver_test.go
    • 测试场景:超级管理员返回空数组
    • 测试场景:平台用户返回账号级角色
    • 测试场景:代理账号有账号级角色,返回账号级角色(不继承)
    • 测试场景:代理账号无账号级角色,继承店铺级角色
    • 测试场景:代理账号无账号级角色且店铺无角色,返回空数组
    • 测试场景:企业账号返回账号级角色
    • 验证:运行 source .env.local && go test -v ./internal/service/account/ -run TestGetRoleIDsForAccount,测试覆盖率 ≥ 90%
  • 4.3 修改 internal/service/permission/service.go

    • 修改 Service 结构体,添加 accountService *account.Service 字段
    • 修改 New() 构造函数,接收 accountService 参数
    • 修改 CheckPermission() 方法,调用 accountService.GetRoleIDsForAccount() 替代直接查询 accountRoleStore
    • 验证:运行 go build ./internal/service/permission/,无编译错误
  • 4.4 更新 Permission Service 单元测试

    • 修改 internal/service/permission/service_test.go
    • 更新 mock accountService 或使用真实 accountService
    • 验证所有现有测试仍然通过
    • 新增测试:代理账号继承店铺角色的权限检查场景
    • 验证:运行 source .env.local && go test -v ./internal/service/permission/ -run TestCheckPermission,所有测试通过
  • 4.5 修改 internal/service/account/service.go

    • 修改 Service 结构体,添加 shopRoleStore *postgres.ShopRoleStore 字段(用于角色解析)
    • 修改 New() 构造函数,接收 shopRoleStore 参数
    • 验证:运行 go build ./internal/service/account/,无编译错误
  • 4.6 创建 internal/service/shop/shop_role.go

    • 实现 AssignRolesToShop(ctx, shopID, roleIDs) ([]*model.ShopRole, error) 方法
    • 实现业务逻辑:
      • 权限检查(调用 middleware.CanManageShop
      • 验证店铺存在
      • 验证角色存在、类型正确RoleType=2、状态启用
      • 空数组表示清空所有角色
      • 删除现有角色关联,批量创建新关联(原子操作)
    • 实现 GetShopRoles(ctx, shopID) ([]*dto.ShopRoleResponse, error) 方法
    • 实现业务逻辑:
      • 权限检查
      • 查询店铺角色关联
      • 查询角色详情并组装响应
    • 验证:运行 go build ./internal/service/shop/,无编译错误
  • 4.7 编写 Shop Service 店铺角色管理单元测试

    • 测试文件:internal/service/shop/shop_role_test.go
    • 测试 AssignRolesToShop() 成功分配单个角色
    • 测试 AssignRolesToShop() 清空所有角色
    • 测试 AssignRolesToShop() 替换现有角色
    • 测试 AssignRolesToShop() 角色类型校验失败
    • 测试 AssignRolesToShop() 角色不存在
    • 测试 AssignRolesToShop() 店铺不存在
    • 测试 AssignRolesToShop() 权限不足
    • 测试 GetShopRoles() 查询已分配角色
    • 测试 GetShopRoles() 查询未分配角色的店铺
    • 测试 GetShopRoles() 权限不足
    • 验证:运行 source .env.local && go test -v ./internal/service/shop/ -run TestShopRole,测试覆盖率 ≥ 90%

5. Handler 层实现

  • 5.1 创建 internal/handler/admin/shop_role.go

    • 实现 ShopRoleHandler 结构体,包含 service *shop.Service 字段
    • 实现 NewShopRoleHandler() 构造函数
    • 实现 AssignShopRoles(c *fiber.Ctx) error 方法
      • 解析路径参数 shop_id
      • 解析请求体 AssignShopRolesRequest
      • 调用 service.AssignRolesToShop()
      • 返回统一响应格式
    • 实现 GetShopRoles(c *fiber.Ctx) error 方法
      • 解析路径参数 shop_id
      • 调用 service.GetShopRoles()
      • 返回统一响应格式
    • 实现 DeleteShopRole(c *fiber.Ctx) error 方法
      • 解析路径参数 shop_idrole_id
      • 调用 service 删除逻辑
      • 返回统一响应格式
    • 验证:运行 go build ./internal/handler/admin/,无编译错误
  • 5.2 编写 Handler 集成测试

    • 测试文件:tests/integration/shop_role_test.go
    • 测试 POST /api/admin/shops/:shop_id/roles 成功分配角色
    • 测试 POST /api/admin/shops/:shop_id/roles 清空角色
    • 测试 POST /api/admin/shops/:shop_id/roles 替换角色
    • 测试 POST /api/admin/shops/:shop_id/roles 角色类型校验失败
    • 测试 POST /api/admin/shops/:shop_id/roles 权限不足
    • 测试 GET /api/admin/shops/:shop_id/roles 查询角色
    • 测试 GET /api/admin/shops/:shop_id/roles 店铺不存在
    • 测试 DELETE /api/admin/shops/:shop_id/roles/:role_id 删除角色
    • 验证:运行 source .env.local && go test -v ./tests/integration/ -run TestShopRole,所有测试通过

6. 路由注册和依赖注入

  • 6.1 修改 internal/routes/shop.go

    • 注册 POST /api/admin/shops/:shop_id/roles 路由到 handlers.ShopRole.AssignShopRoles
    • 注册 GET /api/admin/shops/:shop_id/roles 路由到 handlers.ShopRole.GetShopRoles
    • 注册 DELETE /api/admin/shops/:shop_id/roles/:role_id 路由到 handlers.ShopRole.DeleteShopRole
    • 验证:运行 go build ./internal/routes/,无编译错误
  • 6.2 修改 internal/bootstrap/stores.go

    • Stores 结构体添加 ShopRole *postgres.ShopRoleStore 字段
    • initStores() 中初始化 ShopRole: postgres.NewShopRoleStore(deps.DB, deps.Redis)
    • 验证:运行 go build ./internal/bootstrap/,无编译错误
  • 6.3 修改 internal/bootstrap/services.go

    • 修改 Account Service 初始化,传入 stores.ShopRole
    • 修改 Permission Service 初始化,传入 Account Service 实例
    • 验证:运行 go build ./internal/bootstrap/,无编译错误
  • 6.4 修改 internal/bootstrap/handlers.go

    • Handlers 结构体添加 ShopRole *admin.ShopRoleHandler 字段
    • initHandlers() 中初始化 ShopRole: admin.NewShopRoleHandler(services.Shop)
    • 验证:运行 go build ./internal/bootstrap/,无编译错误
  • 6.5 更新 API 文档生成器

    • 修改 cmd/api/docs.go,在 handlers 初始化中添加 ShopRole: admin.NewShopRoleHandler(nil)
    • 修改 cmd/gendocs/main.go,在 handlers 初始化中添加 ShopRole: admin.NewShopRoleHandler(nil)
    • 验证:运行 go run cmd/gendocs/main.go,生成文档成功,包含新的店铺角色管理接口

7. 常量定义

  • 7.1 检查是否需要新增错误码

    • 检查 pkg/errors/codes.go 是否已有所需错误码
    • 如需新增,添加错误码常量和错误消息
    • 验证:运行 go build ./pkg/errors/,无编译错误
  • 7.2 检查是否需要新增 Redis Key 生成函数

    • 检查 pkg/constants/redis.go 是否需要新增店铺角色相关的 Redis Key
    • 当前使用 RedisUserPermissionsKey(userID) 已满足需求,无需新增
    • 验证:确认缓存清理逻辑使用正确的 Key

8. 端到端测试

  • 8.1 测试完整的店铺角色继承流程

    • 创建测试店铺和代理账号(无账号级角色)
    • 为店铺分配角色
    • 验证账号权限检查返回 true继承店铺角色
    • 为账号分配账号级角色
    • 验证账号权限检查使用账号级角色(不继承店铺角色)
    • 删除账号级角色
    • 验证账号权限检查恢复继承店铺角色
    • 验证:手动测试或编写端到端测试脚本
  • 8.2 测试缓存失效机制

    • 为店铺分配角色,账号继承
    • 触发一次权限检查(缓存写入)
    • 修改店铺角色
    • 再次触发权限检查,验证使用新角色(缓存已失效)
    • 验证:手动测试或编写测试脚本
  • 8.3 测试权限控制

    • 使用平台用户操作任意店铺角色(应成功)
    • 使用代理用户操作自己店铺角色(应成功)
    • 使用代理用户操作下级店铺角色(应成功)
    • 使用代理用户操作无关店铺角色(应失败 403
    • 使用企业用户操作店铺角色(应失败 403
    • 验证:手动测试或编写测试脚本

9. 代码质量和文档

  • 9.1 运行 LSP 诊断检查所有修改的文件

    • 运行 lsp_diagnostics 检查所有新增和修改的 Go 文件
    • 确保无错误、无警告
    • 验证:所有文件通过 LSP 检查
  • 9.2 运行代码规范检查

    • 运行 gofmt -w . 格式化所有 Go 文件
    • 运行 go vet ./... 检查潜在问题
    • 验证:无错误输出
  • 9.3 运行所有单元测试

    • 运行 source .env.local && go test -v ./...
    • 确保所有测试通过,包括现有测试和新增测试
    • 验证:测试通过率 100%,核心逻辑测试覆盖率 ≥ 90%
  • 9.4 运行所有集成测试

    • 运行 source .env.local && go test -v ./tests/integration/
    • 确保所有 API 测试通过
    • 验证:测试通过率 100%
  • 9.5 更新项目文档

    • docs/ 目录创建功能总结文档(如果需要)
    • 更新 README.md如果有重大功能说明
    • 验证:文档清晰、准确、完整

10. 部署准备

  • 10.1 验证数据库迁移

    • 在测试环境执行迁移:migrate -path migrations -database "..." up
    • 验证表创建成功,索引创建成功
    • 验证回滚:migrate -path migrations -database "..." down
    • 验证表删除成功
  • 10.2 性能测试

    • 测试角色解析性能(< 10ms
    • 测试权限检查性能(< 50ms
    • 测试缓存命中性能(< 1ms
    • 验证:性能满足设计要求
  • 10.3 最终验收测试

    • 在模拟生产环境执行完整测试流程
    • 验证向后兼容性(现有账号级角色功能不受影响)
    • 验证不设置店铺角色的店铺行为保持一致
    • 验证所有 API 接口正常工作
    • 验证:功能完整、稳定、性能达标