fix(distribution): 支持客服在主站后台查看与管理分站二维码

- 路由权限支持:新增 RequireAnyPermission 中间件,允许持有 qrcode:view 或 distribution:site_view 的管理员调用 GET /admin/distribution/sites 获取分站列表
- 服务层站点穿透:ListSitesForAdmin 允许持有 qrcode:view 权限的管理员(客服)获取全部可用分销站点用于码池维护;站长/分销商等站点成员仍按其绑定站点收窄
- 前端交互与权限分组:分站二维码池 loadSites 补充异常捕获,分配权限弹窗补充「分销管理」分组展示名
- 权限来源:cs 角色的 qrcode:view/manage 由既有迁移 000016 提供,故本次不新增数据库迁移
- 权限边界:不采用 distribution:all_view 实现该需求——它是跨站写操作的总开关(改站/删站/重置站长密码),而该权限依赖的 ops 角色同时是分站站长,授予后会击穿多租户隔离;并补充回归断言锁定该边界
- 验证方式:全量门禁 ./scripts/check.sh 通过(SQL 约束检查、Go 单测与覆盖率、路由测试、前端类型检查、单测与生产构建)
This commit is contained in:
yml2213
2026-10-11 17:11:46 +08:00
parent 6a99c1121b
commit cb8aceb433
8 changed files with 202 additions and 8 deletions
@@ -188,6 +188,33 @@ func (r *Repository) hasGlobalDistributionAccessWithDB(ctx context.Context, db *
return n > 0, nil
}
func (r *Repository) hasPermission(ctx context.Context, adminID uint64, permCode string) (bool, error) {
if adminID == 0 {
return true, nil
}
if r == nil || r.db == nil {
return false, nil
}
var n int64
if err := r.db.WithContext(ctx).Table("roles").
Joins("JOIN admin_user_roles aur ON aur.role_id = roles.id").
Where("aur.admin_user_id = ? AND roles.code = ?", adminID, "super_admin").
Count(&n).Error; err != nil {
return false, err
}
if n > 0 {
return true, nil
}
if err := r.db.WithContext(ctx).Table("permissions").
Joins("JOIN role_permissions rp ON rp.permission_id = permissions.id").
Joins("JOIN admin_user_roles aur ON aur.role_id = rp.role_id").
Where("aur.admin_user_id = ? AND permissions.code = ?", adminID, permCode).
Count(&n).Error; err != nil {
return false, err
}
return n > 0, nil
}
func (r *Repository) listActiveMemberSiteIDs(ctx context.Context, adminID uint64) ([]uint64, error) {
var members []model.DistributionSiteMember
if err := r.db.WithContext(ctx).Where("admin_user_id = ? AND status = 'active'", adminID).Find(&members).Error; err != nil {