From c152a9e5731577f74f205b35e6a1a0946369f775 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Mon, 30 Mar 2026 18:03:40 +0800 Subject: [PATCH] fix --- doc/log-mgmt-frontend-design.md | 367 +++++++++++++++++++++++++++++ internal/models/log_event.go | 34 ++- internal/models/syslog_rule.go | 34 ++- internal/models/trap_dictionary.go | 28 ++- internal/models/trap_rule.go | 34 ++- internal/models/trap_shield.go | 28 ++- 6 files changed, 474 insertions(+), 51 deletions(-) create mode 100644 doc/log-mgmt-frontend-design.md diff --git a/doc/log-mgmt-frontend-design.md b/doc/log-mgmt-frontend-design.md new file mode 100644 index 0000000..05527d3 --- /dev/null +++ b/doc/log-mgmt-frontend-design.md @@ -0,0 +1,367 @@ +# Ops Logs 前端页面设计文档(Log Mgmt) + +## 1. 背景与目标 + +`Logs` 服务负责采集并归一化设备侧日志(Syslog / SNMP Trap),并提供规则与字典等配置能力。前端需要在统一的后台界面中完成: + +1. 日志查询(查看归一化后的日志事件及详情) +2. Syslog 规则配置 +3. Trap 规则配置 +4. Trap 字典配置 +5. Trap 屏蔽/抑制规则配置 + +本设计以当前代码库的后端模型与前端实现为准:后端路由在 `internal/routers/register.go`,前端页面在 `front/src/views/ops/pages/log-mgmt/**/index.vue`。 + +--- + +## 2. 范围(页面数量与路由) + +本模块共 5 个页面,对应后端 5 组资源(列表+CRUD 或列表+详情抽屉)。 + +| 页面 | 菜单/路由路径 | 前端组件 | +|---|---|---| +| 日志查询 | `/log-mgmt/entries` | `front/src/views/ops/pages/log-mgmt/entries/index.vue` | +| Syslog 匹配规则 | `/log-mgmt/syslog-rules` | `front/src/views/ops/pages/log-mgmt/syslog-rules/index.vue` | +| SNMP Trap 匹配规则 | `/log-mgmt/trap-rules` | `front/src/views/ops/pages/log-mgmt/trap-rules/index.vue` | +| Trap 字典 | `/log-mgmt/trap-dictionary` | `front/src/views/ops/pages/log-mgmt/trap-dictionary/index.vue` | +| Trap 屏蔽/抑制 | `/log-mgmt/trap-suppressions` | `front/src/views/ops/pages/log-mgmt/trap-suppressions/index.vue` | + +路由与菜单配置参考: +- `front/src/router/local-menu-flat.ts` / `front/src/router/local-menu-items.ts` +- `front/src/views/ops/pages/system-settings/system-logs/index.vue`(页面入口按钮) +- `front/src/views/ops/pages/monitor/log/index.vue`(嵌入 `LogMgmtEntries`) + +--- + +## 3. 数据对象与接口映射 + +后端认证:API 路由组启用 `middleware.JwtAuth(true)`。 + +前端请求的 API Base:`front/src/api/ops/logs.ts` 中为 `/Logs/v1`。 + +### 3.1 日志事件(entries) + +- 接口:`GET /Logs/v1/entries` +- 返回结构(前端类型):`LogEntriesResult`(`total`、`page`、`page_size`、`items`) +- 日志事件字段(前端类型 `LogEvent`): + - `id` + - `created_at` + - `source_kind`(`syslog` / `snmp_trap`) + - `remote_addr` + - `raw_payload` + - `normalized_summary` + - `normalized_detail` + - `device_name` + - `severity_code` + - `trap_oid` + - `alert_sent` + +后端实现:`internal/models/log_event.go`、`internal/logic/controllers/crud.go`(`ListLogEvents`)。 + +### 3.2 Syslog 规则(syslog-rules) + +- 接口: + - `GET /Logs/v1/syslog-rules` + - `POST /Logs/v1/syslog-rules` + - `PUT /Logs/v1/syslog-rules/:id` + - `DELETE /Logs/v1/syslog-rules/:id` +- 规则字段(前端类型 `SyslogRule` / 后端 `SyslogRule`): + - `id`、`created_at`、`updated_at` + - `name` + - `enabled` + - `priority` + - `device_name_contains` + - `keyword_regex` + - `alert_name` + - `severity_code` + - `policy_id` + +后端实现:`internal/models/syslog_rule.go`、`internal/logic/controllers/crud.go`。 + +### 3.3 Trap 规则(trap-rules) + +- 接口: + - `GET /Logs/v1/trap-rules` + - `POST /Logs/v1/trap-rules` + - `PUT /Logs/v1/trap-rules/:id` + - `DELETE /Logs/v1/trap-rules/:id` +- 规则字段(前端类型 `TrapRule` / 后端 `TrapRule`): + - `name` + - `enabled` + - `priority` + - `oid_prefix` + - `varbind_match_regex` + - `alert_name` + - `severity_code` + - `policy_id` + +后端实现:`internal/models/trap_rule.go`、`internal/logic/controllers/crud.go`。 + +### 3.4 Trap 字典(trap-dictionary) + +- 接口: + - `GET /Logs/v1/trap-dictionary` + - `POST /Logs/v1/trap-dictionary` + - `PUT /Logs/v1/trap-dictionary/:id` + - `DELETE /Logs/v1/trap-dictionary/:id` +- 字典条目字段(前端类型 `TrapDictionaryEntry` / 后端 `TrapDictionaryEntry`): + - `oid_prefix`(后端约束:uniqueIndex) + - `title` + - `description` + - `severity_code` + - `recovery_message` + - `enabled` + +后端实现:`internal/models/trap_dictionary.go`、`internal/logic/controllers/crud.go`。 + +### 3.5 Trap 屏蔽/抑制(trap-suppressions) + +- 接口: + - `GET /Logs/v1/trap-suppressions` + - `POST /Logs/v1/trap-suppressions` + - `PUT /Logs/v1/trap-suppressions/:id` + - `DELETE /Logs/v1/trap-suppressions/:id` +- 屏蔽规则字段(前端类型 `TrapShield` / 后端 `TrapShield`): + - `name` + - `enabled` + - `source_ip_cidr` + - `oid_prefix` + - `interface_hint` + - `time_windows_json`(JSON 字符串) + +后端实现:`internal/models/trap_shield.go`、`internal/logic/controllers/crud.go`。 + +--- + +## 4. 页面设计详情(逐页) + +### 4.1 日志查询页(`/log-mgmt/entries`) + +目标:以“可筛选的列表 + 详情抽屉”方式查看归一化日志事件。 + +#### 1)顶部筛选区 + +- 使用 `search-table` 组件 +- 筛选项:`source_kind`(下拉) + - `全部`(value='') + - `Syslog`(value='syslog') + - `SNMP Trap`(value='snmp_trap') + +筛选触发:`@search` 调用 `handleSearch`,重置则 `@reset` 调用 `handleReset`。 + +#### 2)列表表格列(Columns) + +表格由 `columns` 定义,主要列: + +- `ID` +- `来源`:`source_kind`,通过 `sourceKindLabel()` 显示(`syslog`->`Syslog`、`snmp_trap`->`SNMP Trap`) +- `时间`:`created_at` +- `来源地址`:`remote_addr` +- `设备`:`device_name` +- `级别`:`severity_code` +- `OID`:`trap_oid` +- `原始报文`:`raw_payload` + - 使用 slot `raw_payload`:省略显示,保留 `tooltip` +- `已告警`:`alert_sent` + - 使用 slot `alert_sent`:`a-tag`(已转发/否) +- `操作`:slot `operations` + - `详情`:打开右侧抽屉 + +#### 3)详情抽屉(a-drawer) + +- 打开逻辑:点击表格行操作中的 `详情`,调用 `openDetail(record)` +- 抽屉展示:`a-descriptions`(1 列,bordered) +- 展示字段: + - 来源类型(`source_kind`) + - 采集时间(`created_at`) + - 来源地址(`remote_addr`,空则 `-`) + - 设备名(`device_name`) + - 严重级别(`severity_code`) + - Trap OID(`trap_oid`) + - 已转发告警(`alert_sent`) + - 摘要(`normalized_summary`) + - 详情(`normalized_detail`,`pre-block` 预格式化展示) + - 原始报文(`raw_payload`,`pre-block` 预格式化展示) + +#### 4)分页策略 + +- 分页参数由前端 `pagination.current/pageSize` 控制,并随筛选条件一起请求后端: + - 调用 `fetchLogEntries({ page, page_size, source_kind })` + +### 4.2 Syslog 规则页(`/log-mgmt/syslog-rules`) + +目标:规则的“列表 + 新建/编辑弹窗 + 删除确认”。 + +#### 1)通用列表与本地过滤 + +- 使用 `search-table`,并在前端进行“关键词本地过滤”,过滤字段: + - `name` + - `alert_name` + - `keyword_regex` +- 搜索输入字段: + - `keyword`(label:`关键词`,placeholder:`规则名 / 告警名`) + +说明:该页(以及 trap-*、dictionary、suppressions 三类列表页)采用“先拉取全量 -> 本地过滤 -> 切片分页”的方式。 + +#### 2)表格列 + +- `ID` +- `名称`:`name` +- `优先级`:`priority` +- `启用`:`enabled`(slot `enabled`,tag:启用/禁用) +- `设备名包含`:`device_name_contains` +- `关键字正则`:`keyword_regex` +- `告警名`:`alert_name` +- `级别`:`severity_code` +- `策略ID`:`policy_id` +- `操作`:编辑/删除 + +#### 3)新建/编辑弹窗(a-modal) + +- 弹窗标题: + - 新建:`新建 Syslog 规则` + - 编辑:`编辑规则 #${editingId}` +- 表单 `a-form`(布局 `vertical`) +- 表单字段: + - `name`:`a-input`(必填) + - `enabled`:`a-switch` + - `priority`:`a-input-number` + - `device_name_contains`:`a-input` + - `keyword_regex`:`a-input` + - `alert_name`:`a-input` + - `severity_code`:`a-input` + - `policy_id`:`a-input-number`(min=0) + +提交逻辑: +- 编辑:`updateSyslogRule(editingId, { ...formData })` +- 新建:`createSyslogRule({ ...formData })` +- 成功后关闭弹窗并刷新列表 `fetchList()`。 + +#### 4)删除确认 + +- `Modal.confirm` 二次确认 +- 删除接口:`deleteSyslogRule(id)` + +### 4.3 Trap 规则页(`/log-mgmt/trap-rules`) + +目标:TrapRule 的列表+弹窗 CRUD,与 Syslog 规则页同构。 + +#### 1)本地过滤关键词 + +- 字段:`keyword` +- 匹配来源: + - `name` + - `oid_prefix` + - `alert_name` + +#### 2)表格列 + +- `ID`、`名称`、`优先级`、`启用` +- `OID 前缀`:`oid_prefix` +- `Varbind 正则`:`varbind_match_regex` +- `告警名`:`alert_name` +- `级别`:`severity_code` +- `策略ID`:`policy_id` +- 操作:编辑/删除 + +#### 3)弹窗表单字段 + +- `name`(必填) +- `enabled` +- `priority` +- `oid_prefix` +- `varbind_match_regex` +- `alert_name` +- `severity_code` +- `policy_id`(min=0) + +### 4.4 Trap 字典页(`/log-mgmt/trap-dictionary`) + +目标:TrapDictionaryEntry 的列表+弹窗 CRUD。 + +#### 1)本地过滤关键词 + +- 匹配字段: + - `oid_prefix` + - `title` + - `description` + +#### 2)表格列 + +- `ID` +- `OID 前缀`:`oid_prefix` +- `标题`:`title` +- `级别`:`severity_code` +- `启用`:`enabled` +- `描述`:`description` +- 操作:编辑/删除 + +#### 3)弹窗表单字段 + +- `oid_prefix`(必填,建议提示“唯一前缀”) +- `title`(必填) +- `description`:`a-textarea`(rows=3) +- `severity_code` +- `enabled` +- `recovery_message`:`a-textarea`(rows=2) + +### 4.5 Trap 屏蔽/抑制页(`/log-mgmt/trap-suppressions`) + +目标:TrapShield 的列表+弹窗 CRUD,并对 `time_windows_json` 做前端校验。 + +#### 1)本地过滤关键词 + +- 匹配字段: + - `name` + - `oid_prefix` + - `source_ip_cidr` + +#### 2)表格列 + +- `ID` +- `名称`:`name` +- `启用`:`enabled` +- `源 IP/CIDR`:`source_ip_cidr` +- `OID 前缀`:`oid_prefix` +- `接口提示`:`interface_hint` +- 操作:编辑/删除 + +#### 3)弹窗表单字段 + +- `name`(必填) +- `enabled` +- `source_ip_cidr` +- `oid_prefix` +- `interface_hint` +- `time_windows_json`:`a-textarea`(rows=4,placeholder=`{}`) + +#### 4)time_windows_json JSON 校验 + +- 当 `time_windows_json` 非空时: + - 去 `trim` 后尝试 `JSON.parse(tw)` + - 校验失败:`Message.warning('时间窗 JSON 格式无效')` 并阻止提交 + +--- + +## 5. 页面交互一致性要求(实现要点) + +为了保证各列表页体验一致,本模块约定: + +1. 列表页使用统一的 `search-table` 布局(顶部搜索、表格、分页、刷新) +2. 规则类/字典/屏蔽页采用“拉取全量 -> 本地过滤 -> 切片分页”的方式 +3. 创建/编辑统一使用 `a-modal`,提交按钮触发 `formRef.validate()` +4. 删除统一使用 `Modal.confirm`,成功后刷新列表并给出 `Message.success` +5. `trap-suppressions` 对 `time_windows_json` 进行 JSON 字符串合法性校验 + +--- + +## 6. 数据流(简图) + +```mermaid +flowchart LR + UI[前端页面(search-table + 表格/弹窗/抽屉)] --> API[front/src/api/ops/logs.ts] + API --> BE[后端路由 internal/routers/register.go] + BE --> DB[(Postgres)] + BE --> Refresh[ingest.Global.Refresh()(规则/字典/屏蔽变更后触发)] +``` + diff --git a/internal/models/log_event.go b/internal/models/log_event.go index 2064d81..7965b7c 100644 --- a/internal/models/log_event.go +++ b/internal/models/log_event.go @@ -2,18 +2,30 @@ package models import "time" +// LogEvent 表示一条归一化/存储后的日志事件。 type LogEvent struct { - ID uint `gorm:"primaryKey" json:"id"` - CreatedAt time.Time `json:"created_at"` - SourceKind string `gorm:"size:16;index" json:"source_kind"` - RemoteAddr string `gorm:"size:64" json:"remote_addr"` - RawPayload string `gorm:"type:text" json:"raw_payload"` - NormalizedSummary string `gorm:"type:text" json:"normalized_summary"` - NormalizedDetail string `gorm:"type:text" json:"normalized_detail"` - DeviceName string `gorm:"size:512;index" json:"device_name"` - SeverityCode string `gorm:"size:32" json:"severity_code"` - TrapOID string `gorm:"size:512;index" json:"trap_oid"` - AlertSent bool `json:"alert_sent"` + // ID 是数据库主键。 + ID uint `gorm:"primaryKey" json:"id"` + // CreatedAt 记录创建时间(写入日志事件时)。 + CreatedAt time.Time `json:"created_at"` + // SourceKind 表示日志来源类型(例如 trap/syslog 等)。 + SourceKind string `gorm:"size:16;index" json:"source_kind"` + // RemoteAddr 表示日志发送方地址。 + RemoteAddr string `gorm:"size:64" json:"remote_addr"` + // RawPayload 保存原始负载内容。 + RawPayload string `gorm:"type:text" json:"raw_payload"` + // NormalizedSummary 保存归一化后的摘要信息。 + NormalizedSummary string `gorm:"type:text" json:"normalized_summary"` + // NormalizedDetail 保存归一化后的详细信息。 + NormalizedDetail string `gorm:"type:text" json:"normalized_detail"` + // DeviceName 表示关联设备名称。 + DeviceName string `gorm:"size:512;index" json:"device_name"` + // SeverityCode 表示告警/严重度编码。 + SeverityCode string `gorm:"size:32" json:"severity_code"` + // TrapOID 表示关联的 Trap OID(若来源为 trap)。 + TrapOID string `gorm:"size:512;index" json:"trap_oid"` + // AlertSent 表示是否已将告警发送出去。 + AlertSent bool `json:"alert_sent"` } func (LogEvent) TableName() string { diff --git a/internal/models/syslog_rule.go b/internal/models/syslog_rule.go index 849c46b..806a27e 100644 --- a/internal/models/syslog_rule.go +++ b/internal/models/syslog_rule.go @@ -2,18 +2,30 @@ package models import "time" +// SyslogRule 表示一条 Syslog 规则,用于匹配设备日志并触发告警。 type SyslogRule struct { - ID uint `gorm:"primaryKey" json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Name string `gorm:"size:256" json:"name"` - Enabled bool `gorm:"default:true" json:"enabled"` - Priority int `gorm:"index" json:"priority"` - DeviceNameContains string `gorm:"size:512" json:"device_name_contains"` - KeywordRegex string `gorm:"size:512" json:"keyword_regex"` - AlertName string `gorm:"size:256" json:"alert_name"` - SeverityCode string `gorm:"size:32" json:"severity_code"` - PolicyID uint `json:"policy_id"` + // ID 是数据库主键。 + ID uint `gorm:"primaryKey" json:"id"` + // CreatedAt 记录创建时间(GORM 自动维护)。 + CreatedAt time.Time `json:"created_at"` + // UpdatedAt 记录更新时间(GORM 自动维护)。 + UpdatedAt time.Time `json:"updated_at"` + // Name 规则名称,用于展示/标识。 + Name string `gorm:"size:256" json:"name"` + // Enabled 表示该规则是否启用。 + Enabled bool `gorm:"default:true" json:"enabled"` + // Priority 表示匹配优先级(数值越高/低需以业务约定为准)。 + Priority int `gorm:"index" json:"priority"` + // DeviceNameContains 表示设备名称包含条件。 + DeviceNameContains string `gorm:"size:512" json:"device_name_contains"` + // KeywordRegex 表示关键字/内容匹配的正则表达式。 + KeywordRegex string `gorm:"size:512" json:"keyword_regex"` + // AlertName 表示告警名称。 + AlertName string `gorm:"size:256" json:"alert_name"` + // SeverityCode 表示严重级别编码。 + SeverityCode string `gorm:"size:32" json:"severity_code"` + // PolicyID 表示关联的告警/处理策略 ID。 + PolicyID uint `json:"policy_id"` } func (SyslogRule) TableName() string { diff --git a/internal/models/trap_dictionary.go b/internal/models/trap_dictionary.go index a950f52..90d1abf 100644 --- a/internal/models/trap_dictionary.go +++ b/internal/models/trap_dictionary.go @@ -2,16 +2,26 @@ package models import "time" +// TrapDictionaryEntry 表示 Trap 字典条目,用于描述某个 OID 前缀对应的告警元信息。 type TrapDictionaryEntry struct { - ID uint `gorm:"primaryKey" json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - OIDPrefix string `gorm:"size:512;uniqueIndex" json:"oid_prefix"` - Title string `gorm:"size:512" json:"title"` - Description string `gorm:"type:text" json:"description"` - SeverityCode string `gorm:"size:32" json:"severity_code"` - RecoveryMessage string `gorm:"type:text" json:"recovery_message"` - Enabled bool `gorm:"default:true" json:"enabled"` + // ID 是数据库主键。 + ID uint `gorm:"primaryKey" json:"id"` + // CreatedAt 记录创建时间(GORM 自动维护)。 + CreatedAt time.Time `json:"created_at"` + // UpdatedAt 记录更新时间(GORM 自动维护)。 + UpdatedAt time.Time `json:"updated_at"` + // OIDPrefix 表示该字典条目对应的 OID 前缀(唯一)。 + OIDPrefix string `gorm:"size:512;uniqueIndex" json:"oid_prefix"` + // Title 表示字典条目的标题。 + Title string `gorm:"size:512" json:"title"` + // Description 表示字典条目的说明文本。 + Description string `gorm:"type:text" json:"description"` + // SeverityCode 表示默认严重级别编码。 + SeverityCode string `gorm:"size:32" json:"severity_code"` + // RecoveryMessage 表示恢复/消警时的消息模板内容。 + RecoveryMessage string `gorm:"type:text" json:"recovery_message"` + // Enabled 表示该字典条目是否启用。 + Enabled bool `gorm:"default:true" json:"enabled"` } func (TrapDictionaryEntry) TableName() string { diff --git a/internal/models/trap_rule.go b/internal/models/trap_rule.go index 78a9353..32b88d9 100644 --- a/internal/models/trap_rule.go +++ b/internal/models/trap_rule.go @@ -2,18 +2,30 @@ package models import "time" +// TrapRule 表示一条 SNMP Trap 规则,用于匹配并触发告警策略。 type TrapRule struct { - ID uint `gorm:"primaryKey" json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Name string `gorm:"size:256" json:"name"` - Enabled bool `gorm:"default:true" json:"enabled"` - Priority int `gorm:"index" json:"priority"` - OIDPrefix string `gorm:"size:512" json:"oid_prefix"` - VarbindMatchRegex string `gorm:"size:512" json:"varbind_match_regex"` - AlertName string `gorm:"size:256" json:"alert_name"` - SeverityCode string `gorm:"size:32" json:"severity_code"` - PolicyID uint `json:"policy_id"` + // ID 是数据库主键。 + ID uint `gorm:"primaryKey" json:"id"` + // CreatedAt 记录创建时间(GORM 自动维护)。 + CreatedAt time.Time `json:"created_at"` + // UpdatedAt 记录更新时间(GORM 自动维护)。 + UpdatedAt time.Time `json:"updated_at"` + // Name 规则名称,用于展示/标识。 + Name string `gorm:"size:256" json:"name"` + // Enabled 表示该规则是否启用。 + Enabled bool `gorm:"default:true" json:"enabled"` + // Priority 表示匹配优先级(数值越高/低需以业务约定为准)。 + Priority int `gorm:"index" json:"priority"` + // OIDPrefix 表示匹配的 OID 前缀。 + OIDPrefix string `gorm:"size:512" json:"oid_prefix"` + // VarbindMatchRegex 表示对 varbind 内容的正则匹配条件。 + VarbindMatchRegex string `gorm:"size:512" json:"varbind_match_regex"` + // AlertName 表示告警名称。 + AlertName string `gorm:"size:256" json:"alert_name"` + // SeverityCode 表示严重级别编码。 + SeverityCode string `gorm:"size:32" json:"severity_code"` + // PolicyID 表示关联的告警/处理策略 ID。 + PolicyID uint `json:"policy_id"` } func (TrapRule) TableName() string { diff --git a/internal/models/trap_shield.go b/internal/models/trap_shield.go index 1a13ce4..d76fa91 100644 --- a/internal/models/trap_shield.go +++ b/internal/models/trap_shield.go @@ -2,16 +2,26 @@ package models import "time" +// TrapShield 表示一条针对 SNMP Trap 的“屏蔽/防护”规则。 type TrapShield struct { - ID uint `gorm:"primaryKey" json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Name string `gorm:"size:256" json:"name"` - Enabled bool `gorm:"default:true" json:"enabled"` - SourceIPCIDR string `gorm:"size:64" json:"source_ip_cidr"` - OIDPrefix string `gorm:"size:512" json:"oid_prefix"` - InterfaceHint string `gorm:"size:256" json:"interface_hint"` - TimeWindowsJSON string `gorm:"type:text" json:"time_windows_json"` + // ID 是数据库主键。 + ID uint `gorm:"primaryKey" json:"id"` + // CreatedAt 记录创建时间(GORM 自动维护)。 + CreatedAt time.Time `json:"created_at"` + // UpdatedAt 记录更新时间(GORM 自动维护)。 + UpdatedAt time.Time `json:"updated_at"` + // Name 规则名称,用于展示/标识。 + Name string `gorm:"size:256" json:"name"` + // Enabled 表示该规则是否启用。 + Enabled bool `gorm:"default:true" json:"enabled"` + // SourceIPCIDR 表示规则适用的源 IP 网段(CIDR)。 + SourceIPCIDR string `gorm:"size:64" json:"source_ip_cidr"` + // OIDPrefix 表示匹配的 OID 前缀。 + OIDPrefix string `gorm:"size:512" json:"oid_prefix"` + // InterfaceHint 关联提示信息(例如接口/线路标识),用于定位设备来源。 + InterfaceHint string `gorm:"size:256" json:"interface_hint"` + // TimeWindowsJSON 以 JSON 文本形式描述规则生效时间窗口。 + TimeWindowsJSON string `gorm:"type:text" json:"time_windows_json"` } func (TrapShield) TableName() string {