user_adapter.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. // 用户姓名只读端口(academic → identity 单向只读,D4 裁决)——**接口在此,实现在 identity 侧**。
  2. //
  3. // ---------------------------------------------------------------------------
  4. // 为什么需要它
  5. // ---------------------------------------------------------------------------
  6. //
  7. // 端点 53(报课学生列表)契约要求 `realName`。姓名列在 `t_user.display_name`(identity 域),
  8. // academic 域**不持有** `t_user`,而"报课学生列表没有姓名"等于页面不可用 ⇒ 必须跨域只读。
  9. //
  10. // ---------------------------------------------------------------------------
  11. // 依赖方向(设计 §1.3 铁律)
  12. // ---------------------------------------------------------------------------
  13. //
  14. // academic/handler → academic/port(**接口**) ← identity/repository(**实现**)
  15. // ↑
  16. // 装配层注入(cmd/api/wiring_academic.go)
  17. //
  18. // 三条硬约束(评审逐条检查):
  19. //
  20. // 1. **academic 不 import identity**(本文件只定义接口与文档,零业务 import);
  21. // 2. 实现放 identity 侧(那里才是 `t_user` 的属主),本包不写任何 `t_user` SQL;
  22. // 3. 端口**只读**(`SELECT`),绝不跨域写。
  23. //
  24. // 与 `MembershipChecker` / `CampusReader` 的区别:那两个是**越权判定**依赖(缺失会放宽安全),
  25. // 本端口只是**展示**依赖 —— 未注入时页面少一列姓名,不影响任何判据(因此可以是 nil)。
  26. package port
  27. import "context"
  28. // UserNameReader 用户显示名只读端口(端点 53 的 `realName`)。
  29. //
  30. // 契约(实现方必须遵守):
  31. // - **批量**取(一页最多 200 个学生 ⇒ 禁止逐条查询);
  32. // - 返回 `map[userID]displayName`;**查不到的 userID 不要放进 map**(不要塞空串),
  33. // 调用方据此区分"没有这个人 / 姓名为空"并下发 `null`;
  34. // - 任一 DB 错误如实返回,调用方决定是降级(下发 null)还是整体失败。
  35. type UserNameReader interface {
  36. // DisplayNames 批量取显示名。
  37. //
  38. // 入参:ctx 请求上下文;userIDs 用户 ULID 列表(可重复、可空)。
  39. // 出参:map[ULID]显示名(仅含查到的);error 非 nil 表示查询失败。
  40. DisplayNames(ctx context.Context, userIDs []string) (map[string]string, error)
  41. }