membership.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. // 成员关系校验接口(academic 定义,org 侧实现/注入):越权预检的第二道防线。
  2. //
  3. // 依据:unified/03-migration/academic-domain-design.md §1.7「全局无作用域表」——
  4. // `t_user_expense`(全局钱包)/ `t_user_divide_into*`(全局推广体系)不带 institution_id,
  5. // 无法靠 BaseRepo.Scoping 过滤,必须由 Service 层显式断言"该用户确实属于本机构/校区"。
  6. //
  7. // 三层防线分工(架构规格 §3.5):
  8. // 1. 路由角色白名单(handler/route.go,AC-14)——粗粒度;
  9. // 2. **本接口(MembershipChecker)**——Service 层的对象级越权预检(本次);
  10. // 3. Repository 的 BaseRepo.Scoped——数据层强制过滤(academic/repository/base.go)。
  11. package port
  12. import "context"
  13. // MembershipChecker 成员关系校验器(读 t_membership 只读视图)。
  14. //
  15. // 契约:
  16. // - 只读,不写 org 表;
  17. // - 角色码取 org/model.Role*(super_admin/academic/finance/teacher/student/parent/market);
  18. // - `campusID=0` 语义 = 只看机构级(不限定校区)。
  19. type MembershipChecker interface {
  20. // HasMembership 判断用户在指定机构/校区是否具备任一给定角色。
  21. //
  22. // 入参:
  23. // - ctx 请求上下文;
  24. // - userID 用户 canonical_user_id(ULID 26 位;空串 → false);
  25. // - institutionID 机构 ID(0 = 不限机构,仅平台级判断);
  26. // - campusID 校区 ID(0 = 机构级任一校区);
  27. // - roles 期望角色码集合(空集合 = 只要有任一角色即算通过)。
  28. //
  29. // 出参:bool 是否命中;error 仅数据库异常(infraerrors.ErrDBError)。
  30. HasMembership(ctx context.Context, userID string, institutionID, campusID int64, roles ...string) (bool, error)
  31. // AssertInScope 断言用户在作用域内(越权即返回业务错误,供 Service 直接上抛)。
  32. //
  33. // 入参:ctx / userID / institutionID / campusID(语义同 HasMembership,但不限定角色)。
  34. // 出参:nil 在范围内;infraerrors.ErrUnscoped(1004) 越权;infraerrors.ErrDBError(5002) DB 异常。
  35. AssertInScope(ctx context.Context, userID string, institutionID, campusID int64) error
  36. }