refactor: 数据权限过滤从 GORM Callback 改为 Store 层显式调用
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m2s
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:
@@ -5,16 +5,19 @@ import (
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// UserContextInfo 用户上下文信息
|
||||
type UserContextInfo struct {
|
||||
UserID uint
|
||||
UserType int
|
||||
ShopID uint
|
||||
EnterpriseID uint
|
||||
CustomerID uint
|
||||
UserID uint
|
||||
UserType int
|
||||
ShopID uint
|
||||
EnterpriseID uint
|
||||
CustomerID uint
|
||||
SubordinateShopIDs []uint // 代理用户的下级店铺ID列表,nil 表示不受数据权限限制
|
||||
}
|
||||
|
||||
// SetUserContext 将用户信息设置到 context 中
|
||||
@@ -25,6 +28,10 @@ func SetUserContext(ctx context.Context, info *UserContextInfo) context.Context
|
||||
ctx = context.WithValue(ctx, constants.ContextKeyShopID, info.ShopID)
|
||||
ctx = context.WithValue(ctx, constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
||||
ctx = context.WithValue(ctx, constants.ContextKeyCustomerID, info.CustomerID)
|
||||
// SubordinateShopIDs: nil 表示不限制,空切片表示无权限
|
||||
if info.SubordinateShopIDs != nil {
|
||||
ctx = context.WithValue(ctx, constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -134,12 +141,21 @@ func SetUserToFiberContext(c *fiber.Ctx, info *UserContextInfo) {
|
||||
c.Locals(constants.ContextKeyShopID, info.ShopID)
|
||||
c.Locals(constants.ContextKeyEnterpriseID, info.EnterpriseID)
|
||||
c.Locals(constants.ContextKeyCustomerID, info.CustomerID)
|
||||
if info.SubordinateShopIDs != nil {
|
||||
c.Locals(constants.ContextKeySubordinateShopIDs, info.SubordinateShopIDs)
|
||||
}
|
||||
|
||||
// 设置到标准 context(用于 GORM 数据权限过滤)
|
||||
// 设置到标准 context(用于数据权限过滤)
|
||||
ctx := SetUserContext(c.UserContext(), info)
|
||||
c.SetUserContext(ctx)
|
||||
}
|
||||
|
||||
// AuthShopStoreInterface 店铺存储接口
|
||||
// 用于 Auth 中间件获取下级店铺 ID,避免循环依赖
|
||||
type AuthShopStoreInterface interface {
|
||||
GetSubordinateShopIDs(ctx context.Context, shopID uint) ([]uint, error)
|
||||
}
|
||||
|
||||
// AuthConfig Auth 中间件配置
|
||||
type AuthConfig struct {
|
||||
// TokenExtractor 自定义 token 提取函数
|
||||
@@ -153,6 +169,10 @@ type AuthConfig struct {
|
||||
|
||||
// SkipPaths 跳过认证的路径列表
|
||||
SkipPaths []string
|
||||
|
||||
// ShopStore 店铺存储,用于预计算代理用户的下级店铺 ID
|
||||
// 可选,不传则不预计算 SubordinateShopIDs
|
||||
ShopStore AuthShopStoreInterface
|
||||
}
|
||||
|
||||
// Auth 认证中间件
|
||||
@@ -196,6 +216,21 @@ func Auth(config AuthConfig) fiber.Handler {
|
||||
return errors.Wrap(errors.CodeInvalidToken, err, "认证令牌无效")
|
||||
}
|
||||
|
||||
// 预计算代理用户的下级店铺 ID
|
||||
if config.ShopStore != nil &&
|
||||
userInfo.UserType == constants.UserTypeAgent &&
|
||||
userInfo.ShopID > 0 {
|
||||
shopIDs, err := config.ShopStore.GetSubordinateShopIDs(c.UserContext(), userInfo.ShopID)
|
||||
if err != nil {
|
||||
// 降级处理:只包含自己的店铺 ID
|
||||
shopIDs = []uint{userInfo.ShopID}
|
||||
logger.GetAppLogger().Warn("预计算下级店铺失败,降级为只包含自己",
|
||||
zap.Uint("shop_id", userInfo.ShopID),
|
||||
zap.Error(err))
|
||||
}
|
||||
userInfo.SubordinateShopIDs = shopIDs
|
||||
}
|
||||
|
||||
// 将用户信息设置到 context
|
||||
SetUserToFiberContext(c, userInfo)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user