Documentation
¶
Overview ¶
Package httpface 管理面/用户面 HTTP 边界:响应书写(WriteJSON/WriteErr/ WriteServiceErr,service 错误→HTTP 状态映射表唯一一份)+ 请求参数边界 (ClampLimit——两面包共享的分页上限钳制)。
依赖仅 internal/service/errors 叶子包(零内部依赖),不反向依赖 handler/service/auth/server 任何上层包——依赖图无环。
Index ¶
- Constants
- func ClampLimit(l int) int
- func ResolveStatsWindow(from, to *time.Time, window *string, now time.Time) (time.Time, time.Time, error)
- func WriteErr(w http.ResponseWriter, status int, msg string)
- func WriteJSON(w http.ResponseWriter, status int, v any)
- func WriteServiceErr(w http.ResponseWriter, err error)
- func WriteStatsEcho(w http.ResponseWriter, exec domain.Exec)
Constants ¶
const ( // HeaderStatsEffectiveFrom 本次读取真正使用的窗口下界(RFC3339,UTC)。 HeaderStatsEffectiveFrom = "X-Stats-Effective-From" // HeaderStatsEffectiveTo 本次读取真正使用的窗口上界(RFC3339,UTC,半开)。 HeaderStatsEffectiveTo = "X-Stats-Effective-To" // HeaderStatsStorage 本次读取实际使用的存储(cube|raw)。 HeaderStatsStorage = "X-Stats-Storage" )
统计响应回显头(spec-stats-window-plan-2026-09-25 §4.4(b)):三个 200 体是 **裸数组**(加字段即破契约)⇒ 生效窗口与实际存储只能走响应头。
Variables ¶
This section is empty.
Functions ¶
func ClampLimit ¶
ClampLimit 分页 limit 统一钳制(上限 200,与 /usage_logs 同语义):超限 裁剪到 200;≤0 原样透传(repo 归一下限 ≤0→20)。管理面误操作 limit=2147483647 不再全表物化(spec 2026-08-17 边界收敛,6 端点共享)。
func ResolveStatsWindow ¶
func ResolveStatsWindow(from, to *time.Time, window *string, now time.Time) (time.Time, time.Time, error)
ResolveStatsWindow 统计端点请求边界的**窗口形态解析**(P3,spec §7.3/§7.3b): 把线上三字段(from/to/window,三态都可选)解码成 domain.Admit 唯一接受的一对 绝对时刻。窗口**恰择一**:
(from != nil && to != nil && window == nil) -> 绝对窗口,原样直透
(from == nil && to == nil && window != nil) -> 相对窗口,服务端自持 now:
to = ceilHour(now)、from = to − d
其余(两态都给 / 只给一端 / 两态都不给) -> 400 reason=window_ambiguous
window 不是 time.ParseDuration 能解析的时长串 -> 400 reason=window_invalid
**这是解码,不是判定**——本函数不问"这个窗口能不能被服务"(cost / coverage / zone 三条判定全部且只由 domain.Admit 回答,Admit 仍是唯一判定入口)。为什么 落在这里:
- 它必须**唯一**。6 个启用 `window` 的端点分居两个包(handler 与 handler/user),httpface 是本仓库既有的"两面包共享的请求参数边界" (包注释里的 ClampLimit 就是同一理由的先例);写两份 = 两处可漂移。
- 它必须返回 **400 线缆载体**。只有 httpface 能同时命名 domain(取 StatsRejectWindowAmbiguous)与 serviceerr(取承载类型)而不成环 (httpface 不能 import internal/service)。放在 domain 则每个调用点都要把 `*domain.StatsWindowError` 再包成 `*serviceerr.StatsWindowError`——那层包装 就是本 spec 明令禁止的垫片。
- 它**不能**放进 Admit。wire 形态(三个可选字段)在 domain 之外:Admit 收到 的已经是 (from, to)。把可选性灌进 domain 会让 domain 认识 HTTP 形态。
**只收时长,不收日历天**(spec §7.3b,评审 m3):`d` 不是 time.ParseDuration 的单位,`7d` 的拒绝是 ParseDuration 的默认行为,此处只负责把它映射成 400 + 机读 reason(用户裁决:`24h` 在 DST 切换日 ≠ 一个日历天,而本 API 唯一的对齐 基准是固定 1h 网格,另立 AddDate 日历语义会引入第二套窗口长度语义)。
now 由调用方注入(handler 既有的可注入时钟 h.now)——本函数不读墙钟,故 A11/A12 可确定性断言。返回值 error 恒为 nil 或 `*serviceerr.StatsWindowError`(经 WriteServiceErr 出 400 + 机读字段)。
func WriteErr ¶
func WriteErr(w http.ResponseWriter, status int, msg string)
WriteErr 写 {"error": msg} JSON 信封。
func WriteJSON ¶
func WriteJSON(w http.ResponseWriter, status int, v any)
WriteJSON 写 JSON 响应(Content-Type application/json + encoder 编码, 含尾换行——与各包历史副本逐字节一致)。
func WriteServiceErr ¶
func WriteServiceErr(w http.ResponseWriter, err error)
WriteServiceErr 统一把 service 错误映射为 HTTP 状态(映射表唯一一份)。 404 输出 err.Error()(service 层已把缺失 id 详情包装进 ErrNotFound,与 批量 404 同语义);未命中哨兵 → 500 "internal error"(不泄露内部细节)。 400 额外序列化统计窗口拒绝的机读字段(spec §4.4(c))。
func WriteStatsEcho ¶
func WriteStatsEcho(w http.ResponseWriter, exec domain.Exec)
WriteStatsEcho 写三个回显头,值**直接取自 service 判定后回传的 Exec**。 调用方绝不重算窗口判定:判定 owner 只有 domain.Admit 一处(重算即第二份 判定,而两份判定的 now 不同还可能给出不同窗口,回显就会撒谎)。
无跨源部署(管理台/用户台同源由本进程服务),故不需要 Access-Control-Expose-Headers;若将来跨源,这三个头必须先暴露。
Types ¶
This section is empty.