Files
huang 80f560df33
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m17s
refactor(account): 统一账号管理API、完善权限检查和操作审计
- 合并 customer_account 和 shop_account 路由到统一的 account 接口
- 新增统一认证接口 (auth handler)
- 实现越权防护中间件和权限检查工具函数
- 新增操作审计日志模型和服务
- 更新数据库迁移 (版本 39: account_operation_log 表)
- 补充集成测试覆盖权限检查和审计日志场景
2026-02-02 17:23:20 +08:00

6.2 KiB
Raw Blame History

账号管理权限检查规格

ADDED Requirements

Requirement: 三层越权防护架构

系统 SHALL 实现三层越权防护机制,确保账号管理操作的安全性。

Scenario: 路由层中间件拦截企业账号

  • WHEN 企业账号user_type=4访问账号管理接口/api/admin/accounts/*
  • THEN 中间件应返回 403 错误:"无权限访问账号管理功能"

Scenario: Service层权限检查成功

  • WHEN 代理账号创建自己店铺的账号
  • THEN CanManageShop 检查应通过,账号创建成功

Scenario: GORM层自动过滤生效

  • WHEN 代理账号查询账号列表
  • THEN GORM Callback 应自动添加 shop_id IN (当前店铺+下级店铺) 过滤条件

Requirement: 代理账号只能管理自己店铺及下级店铺的账号

系统 SHALL 验证代理账号对目标店铺的管理权限,禁止跨店铺越权操作。

Scenario: 代理创建自己店铺的账号成功

  • WHEN 代理账号shop_id=100创建 shop_id=100 的账号
  • THEN 权限检查通过,账号创建成功

Scenario: 代理创建下级店铺的账号成功

  • WHEN 代理账号shop_id=100下级101,102创建 shop_id=101 的账号
  • THEN GetSubordinateShopIDs 返回 [100,101,102],权限检查通过

Scenario: 代理创建其他店铺的账号失败

  • WHEN 代理账号shop_id=100创建 shop_id=200 的账号
  • THEN CanManageShop 返回错误:"无权限管理该店铺的账号",创建失败

Scenario: 代理创建平台账号失败

  • WHEN 代理账号尝试创建 user_type=2 的平台账号
  • THEN Service 层检查返回错误:"无权限创建平台账号",创建失败

Requirement: 平台账号和超级管理员可以管理所有账号

系统 SHALL 允许平台账号和超级管理员跳过所有权限检查,管理所有账号。

Scenario: 平台账号创建任意类型账号

  • WHEN 平台账号user_type=2创建代理账号user_type=3, shop_id=100
  • THEN 权限检查跳过,账号创建成功

Scenario: 超级管理员创建任意类型账号

  • WHEN 超级管理员user_type=1创建任意类型账号
  • THEN 权限检查跳过,账号创建成功

Scenario: 平台账号查询所有账号

  • WHEN 平台账号调用账号列表接口
  • THEN GORM Callback 跳过过滤,返回所有账号

Requirement: 企业账号禁止访问账号管理接口

系统 SHALL 禁止企业账号访问所有账号管理接口。

Scenario: 企业账号创建账号失败(路由层拦截)

  • WHEN 企业账号user_type=4调用 POST /api/admin/accounts/enterprise
  • THEN 路由层中间件返回 403 错误:"无权限访问账号管理功能"

Scenario: 企业账号更新账号失败Service层拦截

  • WHEN 企业账号绕过路由层,直接调用 AccountService.Update
  • THEN Service 层返回 403 错误:"企业账号不允许更新账号"

Requirement: 统一错误返回防止信息泄露

系统 SHALL 在越权访问时统一返回模糊错误消息,防止攻击者判断资源是否存在。

Scenario: 查询不存在的账号返回模糊错误

  • WHEN 用户查询不存在的账号 ID
  • THEN 返回 403 错误:"无权限操作该资源或资源不存在"

Scenario: 查询越权的账号返回相同错误

  • WHEN 代理账号shop_id=100查询 shop_id=200 的账号
  • THEN 返回 403 错误:"无权限操作该资源或资源不存在"(与不存在的错误消息相同)

Requirement: CanManageShop 权限检查函数

系统 SHALL 提供 CanManageShop 函数验证用户对目标店铺的管理权限。

Scenario: 验证代理对自己店铺的权限

  • WHEN 调用 CanManageShop(ctx, 100, shopStore) 且当前用户 shop_id=100
  • THEN 返回 nil有权限

Scenario: 验证代理对下级店铺的权限

  • WHEN 调用 CanManageShop(ctx, 101, shopStore) 且当前用户 shop_id=100下级包含 101
  • THEN GetSubordinateShopIDs 返回 [100,101,102],返回 nil有权限

Scenario: 验证代理对其他店铺的权限失败

  • WHEN 调用 CanManageShop(ctx, 200, shopStore) 且当前用户 shop_id=100
  • THEN 返回错误:"无权限管理该店铺的账号"

Scenario: 验证平台账号自动通过

  • WHEN 调用 CanManageShop(ctx, 200, shopStore) 且当前用户 user_type=2平台
  • THEN 不调用 GetSubordinateShopIDs直接返回 nil有权限

Requirement: CanManageEnterprise 权限检查函数

系统 SHALL 提供 CanManageEnterprise 函数验证用户对目标企业的管理权限。

Scenario: 验证平台账号管理任意企业

  • WHEN 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且当前用户 user_type=2
  • THEN 返回 nil有权限

Scenario: 验证代理对归属企业的权限

  • WHEN 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=100当前用户 shop_id=100
  • THEN 返回 nil有权限

Scenario: 验证代理对下级店铺企业的权限

  • WHEN 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=101当前用户 shop_id=100下级包含 101
  • THEN 返回 nil有权限

Scenario: 验证代理对其他店铺企业的权限失败

  • WHEN 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=200当前用户 shop_id=100
  • THEN 返回错误:"无权限管理该企业的账号"

Requirement: 权限检查性能优化

系统 SHALL 使用 Redis 缓存优化权限检查性能,确保 API 响应时间 < 200ms。

Scenario: GetSubordinateShopIDs 命中缓存

  • WHEN 调用 GetSubordinateShopIDs(ctx, 100) 且缓存存在
  • THEN 从 Redis 读取缓存,不查询数据库,耗时 < 5ms

Scenario: GetSubordinateShopIDs 缓存未命中

  • WHEN 调用 GetSubordinateShopIDs(ctx, 100) 且缓存不存在
  • THEN 递归查询数据库,写入 Redis 缓存30分钟返回结果

Scenario: 权限检查总耗时 < 10ms

  • WHEN 执行完整权限检查(包含 GetSubordinateShopIDs
  • THEN 总耗时 < 10ms缓存命中时 < 5ms