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

## 1. 数据库层实现

- [x] 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_id`、`idx_shop_role_role_id`、`idx_shop_role_deleted_at`
  - 验证：执行 `migrate -path migrations -database "..." up` 成功

- [x] 1.2 创建数据库迁移回滚文件 `migrations/YYYYMMDDHHMMSS_add_shop_role_table.down.sql`
  - 删除 `tb_shop_role` 表
  - 验证：执行 `migrate -path migrations -database "..." down` 成功

## 2. Model 层实现

- [x] 2.1 创建 `internal/model/shop_role.go`
  - 定义 `ShopRole` 结构体，包含所有字段和 GORM 标签
  - 实现 `TableName()` 方法返回 `"tb_shop_role"`
  - 验证：运行 `go build ./internal/model/`，无编译错误

- [x] 2.2 创建 `internal/model/dto/shop_role_dto.go`
  - 定义 `AssignShopRolesRequest` 结构体（包含 `role_ids` 字段和 description 标签）
  - 定义 `ShopRoleResponse` 结构体（包含店铺和角色详情）
  - 定义 `ShopRolesResponse` 结构体（包含 `shop_id` 和 `roles` 列表）
  - 验证：运行 `go build ./internal/model/dto/`，无编译错误

## 3. Store 层实现

- [x] 3.1 创建 `internal/store/postgres/shop_role_store.go`
  - 实现 `ShopRoleStore` 结构体，包含 `db` 和 `redisClient` 字段
  - 实现 `NewShopRoleStore()` 构造函数
  - 实现 `Create()` 方法（创建单个店铺角色关联）
  - 实现 `BatchCreate()` 方法（批量创建）
  - 实现 `Delete()` 方法（删除指定店铺角色关联）
  - 实现 `DeleteByShopID()` 方法（删除店铺的所有角色关联）
  - 实现 `GetByShopID()` 方法（查询店铺的所有角色关联）
  - 实现 `GetRoleIDsByShopID()` 方法（查询店铺的所有角色 ID）
  - 实现 `clearShopRoleCache()` 私有方法（清理店铺下所有账号的权限缓存）
  - 验证：运行 `go build ./internal/store/postgres/`，无编译错误

- [x] 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 层实现

- [x] 4.1 创建 `internal/service/account/role_resolver.go`
  - 实现 `GetRoleIDsForAccount(ctx, accountID) ([]uint, error)` 方法
  - 实现角色解析逻辑：
    - 超级管理员返回空数组
    - 查询账号级角色，如有则返回
    - 代理账号且无账号级角色，查询店铺级角色并返回
    - 其他用户类型返回空数组
  - 验证：运行 `go build ./internal/service/account/`，无编译错误

- [x] 4.2 编写 `GetRoleIDsForAccount` 单元测试
  - 测试文件：`internal/service/account/role_resolver_test.go`
  - 测试场景：超级管理员返回空数组
  - 测试场景：平台用户返回账号级角色
  - 测试场景：代理账号有账号级角色，返回账号级角色（不继承）
  - 测试场景：代理账号无账号级角色，继承店铺级角色
  - 测试场景：代理账号无账号级角色且店铺无角色，返回空数组
  - 测试场景：企业账号返回账号级角色
  - 验证：运行 `source .env.local && go test -v ./internal/service/account/ -run TestGetRoleIDsForAccount`，测试覆盖率 ≥ 90%

- [x] 4.3 修改 `internal/service/permission/service.go`
  - 修改 `Service` 结构体，添加 `accountService *account.Service` 字段
  - 修改 `New()` 构造函数，接收 `accountService` 参数
  - 修改 `CheckPermission()` 方法，调用 `accountService.GetRoleIDsForAccount()` 替代直接查询 `accountRoleStore`
  - 验证：运行 `go build ./internal/service/permission/`，无编译错误

- [x] 4.4 更新 `Permission Service` 单元测试
  - 修改 `internal/service/permission/service_test.go`
  - 更新 mock accountService 或使用真实 accountService
  - 验证所有现有测试仍然通过
  - 新增测试：代理账号继承店铺角色的权限检查场景
  - 验证：运行 `source .env.local && go test -v ./internal/service/permission/ -run TestCheckPermission`，所有测试通过

- [x] 4.5 修改 `internal/service/account/service.go`
  - 修改 `Service` 结构体，添加 `shopRoleStore *postgres.ShopRoleStore` 字段（用于角色解析）
  - 修改 `New()` 构造函数，接收 `shopRoleStore` 参数
  - 验证：运行 `go build ./internal/service/account/`，无编译错误

- [x] 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/`，无编译错误

- [x] 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 层实现

- [x] 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_id` 和 `role_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. 路由注册和依赖注入

- [x] 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/`，无编译错误

- [x] 6.2 修改 `internal/bootstrap/stores.go`
  - 在 `Stores` 结构体添加 `ShopRole *postgres.ShopRoleStore` 字段
  - 在 `initStores()` 中初始化 `ShopRole: postgres.NewShopRoleStore(deps.DB, deps.Redis)`
  - 验证：运行 `go build ./internal/bootstrap/`，无编译错误

- [x] 6.3 修改 `internal/bootstrap/services.go`
  - 修改 Account Service 初始化，传入 `stores.ShopRole`
  - 修改 Permission Service 初始化，传入 `Account Service` 实例
  - 验证：运行 `go build ./internal/bootstrap/`，无编译错误

- [x] 6.4 修改 `internal/bootstrap/handlers.go`
  - 在 `Handlers` 结构体添加 `ShopRole *admin.ShopRoleHandler` 字段
  - 在 `initHandlers()` 中初始化 `ShopRole: admin.NewShopRoleHandler(services.Shop)`
  - 验证：运行 `go build ./internal/bootstrap/`，无编译错误

- [x] 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. 常量定义

- [x] 7.1 检查是否需要新增错误码
  - 检查 `pkg/errors/codes.go` 是否已有所需错误码
  - 如需新增，添加错误码常量和错误消息
  - 验证：运行 `go build ./pkg/errors/`，无编译错误

- [x] 7.2 检查是否需要新增 Redis Key 生成函数
  - 检查 `pkg/constants/redis.go` 是否需要新增店铺角色相关的 Redis Key
  - 当前使用 `RedisUserPermissionsKey(userID)` 已满足需求，无需新增
  - 验证：确认缓存清理逻辑使用正确的 Key

## 8. 端到端测试

- [ ] 8.1 测试完整的店铺角色继承流程
  - 创建测试店铺和代理账号（无账号级角色）
  - 为店铺分配角色
  - 验证账号权限检查返回 true（继承店铺角色）
  - 为账号分配账号级角色
  - 验证账号权限检查使用账号级角色（不继承店铺角色）
  - 删除账号级角色
  - 验证账号权限检查恢复继承店铺角色
  - 验证：手动测试或编写端到端测试脚本

- [ ] 8.2 测试缓存失效机制
  - 为店铺分配角色，账号继承
  - 触发一次权限检查（缓存写入）
  - 修改店铺角色
  - 再次触发权限检查，验证使用新角色（缓存已失效）
  - 验证：手动测试或编写测试脚本

- [ ] 8.3 测试权限控制
  - 使用平台用户操作任意店铺角色（应成功）
  - 使用代理用户操作自己店铺角色（应成功）
  - 使用代理用户操作下级店铺角色（应成功）
  - 使用代理用户操作无关店铺角色（应失败 403）
  - 使用企业用户操作店铺角色（应失败 403）
  - 验证：手动测试或编写测试脚本

## 9. 代码质量和文档

- [x] 9.1 运行 LSP 诊断检查所有修改的文件
  - 运行 `lsp_diagnostics` 检查所有新增和修改的 Go 文件
  - 确保无错误、无警告
  - 验证：所有文件通过 LSP 检查

- [x] 9.2 运行代码规范检查
  - 运行 `gofmt -w .` 格式化所有 Go 文件
  - 运行 `go vet ./...` 检查潜在问题
  - 验证：无错误输出

- [x] 9.3 运行所有单元测试
  - 运行 `source .env.local && go test -v ./...`
  - 确保所有测试通过，包括现有测试和新增测试
  - 验证：测试通过率 100%，核心逻辑测试覆盖率 ≥ 90%

- [x] 9.4 运行所有集成测试
  - 运行 `source .env.local && go test -v ./tests/integration/`
  - 确保所有 API 测试通过
  - 验证：测试通过率 100%

- [x] 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 接口正常工作
  - 验证：功能完整、稳定、性能达标
