feat(gateway): 优化gateway相关功能
Quality check / Web UI (push) Successful in 9m19s

This commit is contained in:
mei
2026-09-12 15:32:27 +08:00
parent 0d89b7fe8f
commit 9b656cebee
84 changed files with 2769 additions and 825 deletions
+63
View File
@@ -0,0 +1,63 @@
package model
// GatewayRule 是挂在消息网关上的通知规则。
//
// 与 Rule 的区别只有触发来源:Rule 由定时器到期触发,
// GatewayRule 由外部系统投递到网关的消息触发,两者互不干扰。
type GatewayRule struct {
ID uint `gorm:"primaryKey" json:"id"`
UID uint `gorm:"index" json:"uid"`
GatewayID uint `gorm:"index" json:"gateway_id"`
Name string `json:"name"`
AccountID uint `json:"account_id"`
Type string `json:"type"`
ConfigJson string `json:"config_json"`
Enabled bool `gorm:"default:true" json:"enabled"`
CreateAt int64 `json:"create_at"`
}
// AsRule 把网关规则换成统一的执行结构。
//
// 执行层只认识 Rule:投递消息前把外部消息覆盖到 ConfigJson 上,
// 再交给同一个 runner 执行即可。
func (r *GatewayRule) AsRule() *Rule {
return &Rule{
UID: r.UID,
Name: r.Name,
AccountID: r.AccountID,
Type: r.Type,
ConfigJson: r.ConfigJson,
Enabled: r.Enabled,
CreateAt: r.CreateAt,
}
}
// GatewayRuleRequest 创建/编辑消息网关规则的请求体
type GatewayRuleRequest struct {
Name string `json:"name"`
GatewayID uint `json:"gateway_id"`
AccountID uint `json:"account_id"`
Type string `json:"type"`
ConfigJson string `json:"config_json"`
Enabled *bool `json:"enabled"`
}
// Validate 校验网关规则请求中与类型无关的通用部分
func (r *GatewayRuleRequest) Validate() error {
if r.Name == "" {
return ErrValidation("规则名称不能为空")
}
if len(r.Name) > 64 {
return ErrValidation("规则名称过长")
}
if r.Type == "" {
return ErrValidation("规则类型不能为空")
}
if r.GatewayID == 0 {
return ErrValidation("规则必须关联一个消息网关")
}
if r.ConfigJson == "" {
return ErrValidation("规则配置不能为空")
}
return nil
}
+11 -7
View File
@@ -2,9 +2,10 @@ package model
// 执行日志的触发来源
const (
TriggerTimer = "timer" // Timer 到期自动触发
TriggerManual = "manual" // 用户在 WebUI 手动测试
TriggerRemind = "remind" // 提醒通知
TriggerTimer = "timer" // Timer 到期自动触发
TriggerManual = "manual" // 用户在 WebUI 手动测试
TriggerRemind = "remind" // 提醒通知
TriggerGateway = "gateway" // 外部系统投递到消息网关触发
)
// ExecutionLog 记录每一次规则执行/提醒的结果,供 WebUI 展示
@@ -15,8 +16,11 @@ type ExecutionLog struct {
RuleName string `json:"rule_name"`
RuleType string `json:"rule_type"`
TimerID uint `json:"timer_id"`
Trigger string `json:"trigger"`
Success bool `json:"success"`
Message string `json:"message"`
CreateAt int64 `gorm:"index" json:"create_at"`
// GatewayID / GatewayRuleID 只有消息网关投递触发时才有值
GatewayID uint `json:"gateway_id"`
GatewayRuleID uint `json:"gateway_rule_id"`
Trigger string `json:"trigger"`
Success bool `json:"success"`
Message string `json:"message"`
CreateAt int64 `gorm:"index" json:"create_at"`
}
+20 -3
View File
@@ -1,13 +1,30 @@
package model
// GatewayTypeWebhook 默认的网关类型:外部系统 POST 一条消息来触发规则
const GatewayTypeWebhook = "webhook"
type MessageGateway struct {
ID uint `gorm:"primaryKey" json:"id"`
UID uint `gorm:"index" json:"uid"`
Name string `json:"name"`
ID uint `gorm:"primaryKey" json:"id"`
UID uint `gorm:"index" json:"uid"`
Name string `json:"name"`
// Type 网关类型,见 internal/gateway 注册表
Type string `gorm:"default:webhook" json:"type"`
Token string `gorm:"uniqueIndex;size:67" json:"token"`
CreateAt int64 `json:"create_at"`
}
type MessageGatewayRequest struct {
Name string `json:"name"`
Type string `json:"type"`
}
// Validate 校验网关请求中与类型无关的通用部分
func (r *MessageGatewayRequest) Validate() error {
if r.Name == "" {
return ErrValidation("网关名称不能为空")
}
if len(r.Name) > 64 {
return ErrValidation("网关名称过长")
}
return nil
}
+6 -11
View File
@@ -2,11 +2,10 @@ package model
// Rule 描述一个 Timer 到期后要执行的动作
type Rule struct {
ID uint `json:"id" gorm:"primaryKey"`
UID uint `gorm:"index" json:"uid"`
TimerID uint `gorm:"index" json:"timer_id"`
GatewayID uint `gorm:"index" json:"gateway_id"`
Name string `json:"name"`
ID uint `json:"id" gorm:"primaryKey"`
UID uint `gorm:"index" json:"uid"`
TimerID uint `gorm:"index" json:"timer_id"`
Name string `json:"name"`
// 关联账号,部分规则类型(如钉钉)不需要账号,此时为 0
AccountID uint `json:"account_id"`
// 规则类型,见 internal/ruleConfigChecker 注册表
@@ -21,7 +20,6 @@ type Rule struct {
type RuleRequest struct {
Name string `json:"name"`
TimerID uint `json:"timer_id"`
GatewayID uint `json:"gateway_id"`
AccountID uint `json:"account_id"`
Type string `json:"type"`
ConfigJson string `json:"config_json"`
@@ -39,11 +37,8 @@ func (r *RuleRequest) Validate() error {
if r.Type == "" {
return ErrValidation("规则类型不能为空")
}
if r.TimerID == 0 && r.GatewayID == 0 {
return ErrValidation("必须关联一个定时器或消息网关")
}
if r.TimerID != 0 && r.GatewayID != 0 {
return ErrValidation("定时器和消息网关只能选择一个")
if r.TimerID == 0 {
return ErrValidation("规则必须关联一个定时器")
}
if r.ConfigJson == "" {
return ErrValidation("规则配置不能为空")
+20
View File
@@ -0,0 +1,20 @@
package model
// RuleTask 是一次规则执行所需的全部输入。
//
// 规则本体与关联账号都由上层(handler/runner、消息网关)从数据库取出后传进来,
// driver 只负责把动作做出去,所以底层实现里不会再出现任何数据库操作。
type RuleTask struct {
// Rule 规则本体,永远不会为空
Rule *Rule
// Account 规则关联的账号;该规则类型不需要账号时为 nil
Account *Account
}
// AccountConfig 返回关联账号的配置 JSON,没有关联账号时返回空字符串
func (t *RuleTask) AccountConfig() string {
if t == nil || t.Account == nil {
return ""
}
return t.Account.Config
}