package backthink import ( "context" "git.yeij.top/AskaEth/Cyrene_v2/internal/iot" ) // 本文件是「触发源取值域」的**唯一真源**:`Record.Trigger`(= `Trigger.Source` 字符串) // 可能出现的取值,以及本服务订阅哪些事件主题。 // // 为什么要独立出来:admin 的 `GET /api/v1/admin/backthink?trigger=` **服务端筛选** // (design/12 §N C.6,切片 C2)必须**如实拒绝未知取值**(400),而「未知」只能相对 // 一个闭集判断。若在网关里另抄一份主题清单,新增订阅主题时就会出现「数据里有这个取值、 // 筛选却报错」的静默漂移 —— 因此订阅表与筛选白名单从同一处取。 // // ⚠️ 与 design/12 §C.6「`trigger` 的取值域不封闭」**不矛盾**:那条约束的是**显示**口径 // (客户端按字面值显示、不做闭集校验、不隐藏不认识的取值);这里定义的是**筛选白名单** // —— 面向的是「筛选参数打错了要报错,而不是静默返回一个空列表」(静默的空列表会被 // 读成「这段时间没有这一类的思考」,那正是本片要避免的假事实)。 // 内建触发标签。它们是 `Trigger.Source` 的字面值,也是 `Record.Trigger` 的落库值, // 生产构造点分别是 heartbeatTrigger / startupTrigger(trigger.go)。 const ( // HeartbeatSource 是周期性自主反思(含启动轮之外的每次心跳)的标签。 HeartbeatSource = "heartbeat" // StartupSource 是进程启动后第一轮思考的标签(裁决 07.4.4)。 StartupSource = "startup" ) // EventSubjects 返回本服务订阅的事件主题 —— **唯一真源**:`subscribe` 用它注册订阅, // `TriggerFilterValues` 用它拼筛选白名单,因此新增一个订阅主题时两处同时生效 // (不会出现「数据库里有新主题的记录、筛选却 400」的漂移)。 func EventSubjects() []string { return []string{iot.DeviceUpdateSubject, SubjectReminderTrigger, SubjectSystemEvent} } // TriggerFilterValues 返回 `Record.Trigger` 可能出现的**全部取值**(闭集): // 三个内建标签(心跳 / 启动 / 自排)+ 订阅的事件主题。 // // 它是 admin 筛选参数的白名单,也是「面板可选项」的真源;顺序稳定,便于报错文案 // 与客户端下拉复用。 func TriggerFilterValues() []string { return append([]string{HeartbeatSource, StartupSource, SelfWakeSource}, EventSubjects()...) } // IsTriggerValue 报告 v 是否是 `Record.Trigger` 可能出现的取值(白名单判定)。 func IsTriggerValue(v string) bool { for _, x := range TriggerFilterValues() { if x == v { return true } } return false } // ListByTrigger 按触发源读取记录(服务端筛选的读口,design/12 §N C.6 / 切片 C2)。 // 它把筛选**下推**到存储层:`limit` 因此是「**匹配**的记录条数」,而不是 // 「先取一页再筛掉不匹配的」(后者会给面板一个静默的假空值)。 // // 委托给 `*Store`(自己不认识 SQL);`List` 的语义、排序、空串口径一字不改。 func (b *Backthink) ListByTrigger(ctx context.Context, trigger string, limit int) ([]Record, error) { return b.store.ListByTrigger(ctx, trigger, limit) }