package host import ( "context" "strings" "hyapp/pkg/xerr" ) type RoleScope string const ( RoleScopeCountry RoleScope = "country" RoleScopeRegion RoleScope = "region" RoleScopeGlobal RoleScope = "global" RoleScopeSceneOrganizationExpansion = "organization_role_expansion" RoleScopeSceneManagerOperations = "manager_operations" // RoleScopeSceneManagerAddBDLeader 兼容上一版 gateway/admin 的策略查询,解析后统一返回 manager_operations。 RoleScopeSceneManagerAddBDLeader = "manager_add_bd_leader" ) // RoleScopePolicy 是 user-service 拥有的 App 范围策略;调用方只消费解析结果,不能再按产品名复制分支。 type RoleScopePolicy struct { Scene string BaseScope RoleScope ExpandedScope RoleScope RegionExpansionConfigurable bool UpdatedAtMS int64 } // RoleScopePolicyRepository 只暴露小配置表的精确键读取和原子批量写入,避免把后台配置混入 Host 关系事务接口。 type RoleScopePolicyRepository interface { GetRoleScopePolicy(ctx context.Context, scene string) (RoleScopePolicy, bool, error) UpsertRoleScopePolicies(ctx context.Context, policies []RoleScopePolicy, updatedAtMS int64) ([]RoleScopePolicy, error) } // DefaultRoleScopePolicy 定义未配置新 App 的安全基线。产品差异必须写入 owner 表,不能再扩展代码注册表。 func DefaultRoleScopePolicy(rawScene string) (RoleScopePolicy, error) { scene := normalizeRoleScopeScene(rawScene) switch scene { case RoleScopeSceneOrganizationExpansion: return RoleScopePolicy{ Scene: scene, BaseScope: RoleScopeRegion, ExpandedScope: RoleScopeRegion, }, nil case RoleScopeSceneManagerOperations: return RoleScopePolicy{ Scene: scene, BaseScope: RoleScopeCountry, ExpandedScope: RoleScopeRegion, RegionExpansionConfigurable: true, }, nil default: return RoleScopePolicy{}, xerr.New(xerr.InvalidArgument, "role scope scene is invalid") } } // ResolveRoleScopePolicy 先验证 scene,再读取 user DB 的 App 精确配置;数据库错误必须向上返回,不能降级扩大权限。 func ResolveRoleScopePolicy(ctx context.Context, repository RoleScopePolicyRepository, rawScene string) (RoleScopePolicy, error) { defaultPolicy, err := DefaultRoleScopePolicy(rawScene) if err != nil { return RoleScopePolicy{}, err } if repository == nil { return RoleScopePolicy{}, xerr.New(xerr.Unavailable, "role scope policy repository is not configured") } policy, found, err := repository.GetRoleScopePolicy(ctx, defaultPolicy.Scene) if err != nil { return RoleScopePolicy{}, err } if !found { return defaultPolicy, nil } policy.Scene = defaultPolicy.Scene if err := validateRoleScopePolicy(policy); err != nil { return RoleScopePolicy{}, err } return policy, nil } // ResolveRoleScopePolicy 是 Host service 内所有角色边界查询的统一入口。 func (s *Service) ResolveRoleScopePolicy(ctx context.Context, rawScene string) (RoleScopePolicy, error) { return ResolveRoleScopePolicy(ctx, s.roleScopePolicyRepository, rawScene) } // UpdateRoleScopePolicies 把两个 scene 作为同一 App 配置快照提交,避免后台只写一半后留下混合语义。 func (s *Service) UpdateRoleScopePolicies(ctx context.Context, policies []RoleScopePolicy) ([]RoleScopePolicy, error) { if s == nil || s.roleScopePolicyRepository == nil { return nil, xerr.New(xerr.Unavailable, "role scope policy repository is not configured") } if len(policies) != 2 { return nil, xerr.New(xerr.InvalidArgument, "both role scope policies are required") } seen := make(map[string]struct{}, len(policies)) normalized := make([]RoleScopePolicy, 0, len(policies)) for _, policy := range policies { policy.Scene = normalizeRoleScopeScene(policy.Scene) if _, exists := seen[policy.Scene]; exists { return nil, xerr.New(xerr.InvalidArgument, "role scope scene is duplicated") } if err := validateRoleScopePolicy(policy); err != nil { return nil, err } seen[policy.Scene] = struct{}{} normalized = append(normalized, policy) } if _, ok := seen[RoleScopeSceneOrganizationExpansion]; !ok { return nil, xerr.New(xerr.InvalidArgument, "organization role scope policy is required") } if _, ok := seen[RoleScopeSceneManagerOperations]; !ok { return nil, xerr.New(xerr.InvalidArgument, "manager role scope policy is required") } return s.roleScopePolicyRepository.UpsertRoleScopePolicies(ctx, normalized, s.now().UTC().UnixMilli()) } func normalizeRoleScopeScene(rawScene string) string { scene := strings.ToLower(strings.TrimSpace(rawScene)) if scene == RoleScopeSceneManagerAddBDLeader { return RoleScopeSceneManagerOperations } return scene } func validateRoleScopePolicy(policy RoleScopePolicy) error { if policy.Scene != RoleScopeSceneOrganizationExpansion && policy.Scene != RoleScopeSceneManagerOperations { return xerr.New(xerr.InvalidArgument, "role scope scene is invalid") } if !validRoleScope(policy.BaseScope) || !validRoleScope(policy.ExpandedScope) { return xerr.New(xerr.InvalidArgument, "role scope is invalid") } if policy.Scene == RoleScopeSceneOrganizationExpansion { if policy.BaseScope == RoleScopeCountry || policy.RegionExpansionConfigurable || policy.ExpandedScope != policy.BaseScope { return xerr.New(xerr.InvalidArgument, "organization role scope policy is invalid") } return nil } if !policy.RegionExpansionConfigurable && policy.ExpandedScope != policy.BaseScope { return xerr.New(xerr.InvalidArgument, "non-configurable manager scope must keep one scope") } if policy.RegionExpansionConfigurable && roleScopeRank(policy.ExpandedScope) < roleScopeRank(policy.BaseScope) { return xerr.New(xerr.InvalidArgument, "expanded manager scope cannot be narrower than base scope") } return nil } func validRoleScope(scope RoleScope) bool { return scope == RoleScopeCountry || scope == RoleScopeRegion || scope == RoleScopeGlobal } func roleScopeRank(scope RoleScope) int { switch scope { case RoleScopeCountry: return 1 case RoleScopeRegion: return 2 case RoleScopeGlobal: return 3 default: return 0 } } // EffectiveScope 只允许声明为 configurable 的策略接受经理个人扩区开关。 func (p RoleScopePolicy) EffectiveScope(regionExpansionEnabled bool) RoleScope { if p.RegionExpansionConfigurable && regionExpansionEnabled { return p.ExpandedScope } return p.BaseScope }