refactor: 数据权限过滤从 GORM Callback 改为 Store 层显式调用
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m2s

- 移除 RegisterDataPermissionCallback 和 SkipDataPermission 机制
- 在 Auth 中间件预计算 SubordinateShopIDs 并注入 Context
- 新增 ApplyShopFilter/ApplyEnterpriseFilter/ApplyOwnerShopFilter 等 Helper 函数
- 所有 Store 层查询方法显式调用数据权限过滤函数
- 权限检查函数 CanManageShop/CanManageEnterprise 改为从 Context 获取数据

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 16:38:52 +08:00
parent 4ba1f5b99d
commit 03a0960c4d
46 changed files with 1573 additions and 705 deletions

View File

@@ -2,20 +2,13 @@ package middleware
import (
"context"
"slices"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// ShopStoreInterface 店铺存储接口
// 用于权限检查时查询店铺信息和下级店铺ID
type ShopStoreInterface interface {
GetByID(ctx context.Context, id uint) (*model.Shop, error)
GetByIDs(ctx context.Context, ids []uint) ([]*model.Shop, error)
GetSubordinateShopIDs(ctx context.Context, shopID uint) ([]uint, error)
}
// EnterpriseStoreInterface 企业存储接口
// 用于权限检查时查询企业信息
type EnterpriseStoreInterface interface {
@@ -23,91 +16,80 @@ type EnterpriseStoreInterface interface {
GetByIDs(ctx context.Context, ids []uint) ([]*model.Enterprise, error)
}
// CanManageShop 检查当前用户是否有权管理目标店铺的账号
// 超级管理员和平台用户自动通过
// 代理账号只能管理自己店铺及下级店铺的账号
// 企业账号禁止管理店铺账号
func CanManageShop(ctx context.Context, targetShopID uint, shopStore ShopStoreInterface) error {
// CanManageShop 检查当前用户是否有权管理目标店铺
// 超级管理员和平台用户自动通过SubordinateShopIDs 为 nil
// 代理账号只能管理自己店铺及下级店铺
// 企业账号禁止管理店铺
func CanManageShop(ctx context.Context, targetShopID uint) error {
userType := GetUserTypeFromContext(ctx)
// 超级管理员和平台用户跳过权限检查
if userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform {
// 企业账号禁止管理店铺
if userType == constants.UserTypeEnterprise {
return errors.New(errors.CodeForbidden, "无权限管理店铺")
}
// 从 Context 获取预计算的下级店铺 ID 列表
subordinateIDs := GetSubordinateShopIDs(ctx)
// nil 表示不受限制(超级管理员/平台用户)
if subordinateIDs == nil {
return nil
}
// 企业账号禁止管理店铺账号
if userType != constants.UserTypeAgent {
return errors.New(errors.CodeForbidden, "无权限管理店铺账号")
}
// 获取当前代理账号的店铺ID
currentShopID := GetShopIDFromContext(ctx)
if currentShopID == 0 {
return errors.New(errors.CodeForbidden, "无权限管理店铺账号")
}
// 递归查询下级店铺ID包含自己
subordinateIDs, err := shopStore.GetSubordinateShopIDs(ctx, currentShopID)
if err != nil {
return errors.Wrap(errors.CodeInternalError, err, "查询下级店铺失败")
}
// 检查目标店铺是否在下级列表中
for _, id := range subordinateIDs {
if id == targetShopID {
return nil
}
}
return errors.New(errors.CodeForbidden, "无权限管理该店铺的账号")
}
// CanManageEnterprise 检查当前用户是否有权管理目标企业的账号
// 超级管理员和平台用户自动通过
// 代理账号只能管理归属于自己店铺或下级店铺的企业账号
// 企业账号禁止管理其他企业账号
func CanManageEnterprise(ctx context.Context, targetEnterpriseID uint, enterpriseStore EnterpriseStoreInterface, shopStore ShopStoreInterface) error {
userType := GetUserTypeFromContext(ctx)
// 超级管理员和平台用户跳过权限检查
if userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform {
if slices.Contains(subordinateIDs, targetShopID) {
return nil
}
// 企业账号禁止管理其他企业账号
if userType != constants.UserTypeAgent {
return errors.New(errors.CodeForbidden, "无权限管理企业账号")
return errors.New(errors.CodeForbidden, "无权限管理该店铺")
}
// CanManageEnterprise 检查当前用户是否有权管理目标企业
// 超级管理员和平台用户自动通过SubordinateShopIDs 为 nil
// 代理账号只能管理归属于自己店铺或下级店铺的企业
// 企业账号禁止管理其他企业
func CanManageEnterprise(ctx context.Context, targetEnterpriseID uint, enterpriseStore EnterpriseStoreInterface) error {
userType := GetUserTypeFromContext(ctx)
// 企业账号禁止管理其他企业
if userType == constants.UserTypeEnterprise {
return errors.New(errors.CodeForbidden, "无权限管理企业")
}
// 从 Context 获取预计算的下级店铺 ID 列表
subordinateIDs := GetSubordinateShopIDs(ctx)
// nil 表示不受限制(超级管理员/平台用户)
if subordinateIDs == nil {
return nil
}
// 获取目标企业信息
enterprise, err := enterpriseStore.GetByID(ctx, targetEnterpriseID)
if err != nil {
return errors.Wrap(errors.CodeForbidden, err, "无权限操作该资源或资源不存在")
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
// 代理账号不能管理平台级企业owner_shop_idNULL
// 代理账号不能管理平台级企业owner_shop_idNULL
if enterprise.OwnerShopID == nil {
return errors.New(errors.CodeForbidden, "无权限管理平台级企业账号")
}
// 获取当前代理账号的店铺ID
currentShopID := GetShopIDFromContext(ctx)
if currentShopID == 0 {
return errors.New(errors.CodeForbidden, "无权限管理企业账号")
}
// 递归查询下级店铺ID包含自己
subordinateIDs, err := shopStore.GetSubordinateShopIDs(ctx, currentShopID)
if err != nil {
return errors.Wrap(errors.CodeInternalError, err, "查询下级店铺失败")
return errors.New(errors.CodeForbidden, "无权限管理平台级企业")
}
// 检查企业归属的店铺是否在下级列表中
for _, id := range subordinateIDs {
if id == *enterprise.OwnerShopID {
return nil
}
if slices.Contains(subordinateIDs, *enterprise.OwnerShopID) {
return nil
}
return errors.New(errors.CodeForbidden, "无权限管理该企业的账号")
return errors.New(errors.CodeForbidden, "无权限管理该企业")
}
// ContainsShopID 检查目标店铺 ID 是否在当前用户可管理的店铺列表中
// 平台用户/超管返回 true不受限制
// 代理用户检查是否在 SubordinateShopIDs 中
func ContainsShopID(ctx context.Context, targetShopID uint) bool {
subordinateIDs := GetSubordinateShopIDs(ctx)
if subordinateIDs == nil {
return true // 不受限制
}
return slices.Contains(subordinateIDs, targetShopID)
}