- GET /api/v1/admin/backthink 加可选 ?trigger=(闭集=内建触发标签 + 本服务订阅的事件主题;
未知/空 ⇒ 400 invalid_trigger,不静默忽略;合法值下推到 SQL ⇒ limit 是匹配条数)
- 新端点 GET /api/v1/admin/backthink/self-wake(writeAuth):闸的只读镜像
{available,running,reason?,configFile?,gate{pending,used,limit,windowSec,default*,*IsDefault}}
三态分开(未接线 / 有配置未运行 / 运行中),读它**不消耗额度**
- 零新 env 键;旋钮源 = $DATA_DIR/config/backthink.json(**写面未做**,前置=组合根接线,已登记)
- 键名沿用设计与现有代码的 selfWakePerWindow(派工书写错成 selfWakeLimit,未改键名以免废掉主人已写的配置)
- 5 个变异(未知值静默忽略/关掉下推/恒报运行中/白名单摘键/前端把看不到画成 0/0)
⚠️ 本片改了 4 个文件面外文件(admin_query.go/routes.go/api.ts/adminSystem.ts)——已逐条说明理由
65 lines
3.2 KiB
Go
65 lines
3.2 KiB
Go
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)
|
||
}
|