From d428a7db2ebe67f939efe55ad391b70d123945a4 Mon Sep 17 00:00:00 2001 From: mei Date: Thu, 13 Aug 2026 09:13:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(drivers):=20=E6=96=B0=E5=A2=9E=E9=9B=A8?= =?UTF-8?q?=E4=BA=91=E5=B7=A5=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release.md | 2 +- drivers/rainyun/accountConfigChecker.go | 67 +++++++++++++++++++++++++ drivers/rainyun/model.go | 17 +++++++ drivers/rainyun/rainyun.go | 38 ++++++++++++++ drivers/rainyun/ruleConfigChecker.go | 45 +++++++++++++++++ drivers/rainyun/runner.go | 21 ++++++++ drivers/rainyun/tool.go | 27 ++++++++++ go.mod | 5 +- go.sum | 2 + handler/runner/interface.go | 2 + internal/accountConfigChecker/reg.go | 2 + internal/ruleConfigChecker/reg.go | 2 + 12 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 drivers/rainyun/accountConfigChecker.go create mode 100644 drivers/rainyun/model.go create mode 100644 drivers/rainyun/rainyun.go create mode 100644 drivers/rainyun/ruleConfigChecker.go create mode 100644 drivers/rainyun/runner.go create mode 100644 drivers/rainyun/tool.go diff --git a/docs/release.md b/docs/release.md index f087f52..cf1287c 100644 --- a/docs/release.md +++ b/docs/release.md @@ -37,7 +37,7 @@ git push origin v2.2.0 编译时通过 ldflags 注入,启动日志里能看到: -``` +```log goodBaby v2.2.0 (a1b2c3d, 2026-07-27T07:00:00Z) 启动中... ``` diff --git a/drivers/rainyun/accountConfigChecker.go b/drivers/rainyun/accountConfigChecker.go new file mode 100644 index 0000000..06c2348 --- /dev/null +++ b/drivers/rainyun/accountConfigChecker.go @@ -0,0 +1,67 @@ +package rainyun + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/internal/meta" + rain "github.com/ssdomei232/rainyun-go-sdk/v2/rainyun/common" + "github.com/ssdomei232/rainyun-go-sdk/v2/rainyun/user" +) + +// RainyunAccountConfigValidator Rainyun账号配置验证器 +type RainyunAccountConfigValidator struct{} + +func (v *RainyunAccountConfigValidator) GetType() string { + return AccountType +} + +func (v *RainyunAccountConfigValidator) Validate(config string) error { + // 解析配置 + cfg, err := parseAccount(config) + if err != nil { + return err + } + + if cfg.APIKey == "" { + return fmt.Errorf("雨云账号配置中APIKey不能为空") + } + + return nil +} + +func parseAccount(config string) (*RainyunAccount, error) { + var cfg RainyunAccount + if err := json.Unmarshal([]byte(config), &cfg); err != nil { + return nil, fmt.Errorf("解析雨云账号配置失败: %v", err) + } + return &cfg, nil +} + +func (v *RainyunAccountConfigValidator) Test(config string) error { + cfg, err := parseAccount(config) + if err != nil { + return err + } + + client := rain.NewClient(cfg.APIKey) + userClient := user.Client{Client: client} + _, err = userClient.GetUserInfo() + + if err != nil { + return fmt.Errorf("测试雨云账号配置失败: %v", err) + } + + return nil +} + +func (v *RainyunAccountConfigValidator) Meta() meta.AccountMeta { + return meta.AccountMeta{ + Type: AccountType, + Label: "雨云", + Description: "对接雨云,方便各位代理/运维删库跑路", + Fields: []meta.Field{ + {Key: "api_key", Label: "API Key", Type: meta.FieldPassword, Required: true, Secret: true, Placeholder: "xxxxxxxxx"}, + }, + } +} diff --git a/drivers/rainyun/model.go b/drivers/rainyun/model.go new file mode 100644 index 0000000..9e2ec5d --- /dev/null +++ b/drivers/rainyun/model.go @@ -0,0 +1,17 @@ +package rainyun + +const ( + // AccountType Rainyun 账号类型标识 + AccountType = "rainyun" + // RuleType 发送 Rainyun 消息的规则类型标识 + RuleType = "rainyun-workorder" +) + +type RainyunWorkOrderRule struct { + Title string `json:"title"` + Msg string `json:"msg"` +} + +type RainyunAccount struct { + APIKey string `json:"api_key"` +} diff --git a/drivers/rainyun/rainyun.go b/drivers/rainyun/rainyun.go new file mode 100644 index 0000000..2215647 --- /dev/null +++ b/drivers/rainyun/rainyun.go @@ -0,0 +1,38 @@ +package rainyun + +import ( + "context" + + "github.com/ssdomei232/goodBaby/model" + rain "github.com/ssdomei232/rainyun-go-sdk/v2/rainyun/common" + "github.com/ssdomei232/rainyun-go-sdk/v2/rainyun/workorder" +) + +func sendRainyunWorkorderMsg(ctx context.Context, rule *model.Rule) error { + rainyunConfig, err := getRainyunConfigFromRule(rule) + if err != nil { + return err + } + + rainyunAccount, err := getRainyunAccountFromRule(rule) + if err != nil { + return err + } + + client := rain.NewClient(rainyunAccount.APIKey) + workorderClient := workorder.Client{Client: client} + + _, err = workorderClient.CreateWorkOrder(&workorder.CreateWorkerorderRequest{ + Content: rainyunConfig.Msg, + IsAuthed: false, + IsUrgent: 1, + Title: rainyunConfig.Title, + Type: "reward", + }) + + if err != nil { + return err + } + + return nil +} diff --git a/drivers/rainyun/ruleConfigChecker.go b/drivers/rainyun/ruleConfigChecker.go new file mode 100644 index 0000000..fa6ecbb --- /dev/null +++ b/drivers/rainyun/ruleConfigChecker.go @@ -0,0 +1,45 @@ +package rainyun + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/internal/meta" +) + +// Rainyun规则验证器 +type RainyunWorkorderRuleValidator struct{} + +func (v *RainyunWorkorderRuleValidator) GetType() string { + return RuleType +} + +func (v *RainyunWorkorderRuleValidator) Validate(configJSON string) error { + var config RainyunWorkOrderRule + if err := json.Unmarshal([]byte(configJSON), &config); err != nil { + return fmt.Errorf("解析雨云规则配置失败: %v", err) + } + + if config.Title == "" { + return fmt.Errorf("雨云规则配置中 title 不能为空") + } + + if config.Msg == "" { + return fmt.Errorf("雨云规则配置中 msg 不能为空") + } + + return nil +} + +func (v *RainyunWorkorderRuleValidator) Meta() meta.RuleMeta { + return meta.RuleMeta{ + Type: RuleType, + Label: "发送雨云工单", + Description: "触发时通过雨云向指定的工单系统发送工单。", + AccountType: AccountType, + Fields: []meta.Field{ + {Key: "title", Label: "工单标题", Type: meta.FieldTextarea, Required: true}, + {Key: "msg", Label: "工单内容", Type: meta.FieldTextarea, Required: true}, + }, + } +} diff --git a/drivers/rainyun/runner.go b/drivers/rainyun/runner.go new file mode 100644 index 0000000..9816d35 --- /dev/null +++ b/drivers/rainyun/runner.go @@ -0,0 +1,21 @@ +package rainyun + +import ( + "context" + "log" + + "github.com/ssdomei232/goodBaby/model" +) + +// RainyunExecutor 雨云执行器 +type RainyunWorkorderExecutor struct{} + +func (e *RainyunWorkorderExecutor) GetType() string { + return RuleType +} + +func (e *RainyunWorkorderExecutor) Execute(ctx context.Context, rule *model.Rule) error { + log.Printf("执行雨云工单规则: %s", rule.Name) + + return sendRainyunWorkorderMsg(ctx, rule) +} diff --git a/drivers/rainyun/tool.go b/drivers/rainyun/tool.go new file mode 100644 index 0000000..7945504 --- /dev/null +++ b/drivers/rainyun/tool.go @@ -0,0 +1,27 @@ +package rainyun + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/handler/db" + "github.com/ssdomei232/goodBaby/model" +) + +// 从 Rule 中获取 RainyunConfig +func getRainyunConfigFromRule(rule *model.Rule) (*RainyunWorkOrderRule, error) { + var rainyunConfig RainyunWorkOrderRule + if err := json.Unmarshal([]byte(rule.ConfigJson), &rainyunConfig); err != nil { + return nil, fmt.Errorf("解析Rainyun规则配置失败: %w", err) + } + return &rainyunConfig, nil +} + +// 从 Rule 中获取 RainyunAccount +func getRainyunAccountFromRule(rule *model.Rule) (*RainyunAccount, error) { + var rainyunAccount RainyunAccount + if err := db.LoadAccountConfig(rule.AccountID, &rainyunAccount); err != nil { + return nil, err + } + return &rainyunAccount, nil +} diff --git a/go.mod b/go.mod index bb96b78..372e9b2 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,14 @@ require ( github.com/gin-contrib/sessions v1.0.4 github.com/gin-gonic/gin v1.11.0 github.com/glebarez/sqlite v1.11.0 + github.com/go-resty/resty/v2 v2.17.2 github.com/google/go-github/v84 v84.0.0 github.com/robfig/cron/v3 v3.0.1 github.com/ssdomei232/napcat-http-go-sdk v1.0.0 + github.com/ssdomei232/rainyun-go-sdk/v2 v2.0.5 github.com/wneessen/go-mail v0.7.2 golang.org/x/crypto v0.48.0 + gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.25.12 ) @@ -29,7 +32,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/go-resty/resty/v2 v2.17.2 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.18.0 // indirect github.com/google/go-querystring v1.2.0 // indirect @@ -68,7 +70,6 @@ require ( golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.42.0 // indirect google.golang.org/protobuf v1.36.9 // indirect - gorm.io/driver/postgres v1.6.0 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect diff --git a/go.sum b/go.sum index 8832d2f..a0583af 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/ssdomei232/napcat-http-go-sdk v1.0.0 h1:BY3iqqlZw5letiCwTmfMrhyqsx0a96eL5LpDuinnOSE= github.com/ssdomei232/napcat-http-go-sdk v1.0.0/go.mod h1:yPsSaMs3OyKMVPXwIs5ZB5G532vRi1GvL/WiWUEwblU= +github.com/ssdomei232/rainyun-go-sdk/v2 v2.0.5 h1:T30N4npW+U10IlZeQ8SooJi9V42uM+f+lrFPRRftHeg= +github.com/ssdomei232/rainyun-go-sdk/v2 v2.0.5/go.mod h1:Wf9X0qEpKQ1/uD4iUGGoPLHydiCxULqCVk+JCoVqdIg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/handler/runner/interface.go b/handler/runner/interface.go index e052846..d5a776f 100644 --- a/handler/runner/interface.go +++ b/handler/runner/interface.go @@ -12,6 +12,7 @@ import ( "github.com/ssdomei232/goodBaby/drivers/fwalert" "github.com/ssdomei232/goodBaby/drivers/github" "github.com/ssdomei232/goodBaby/drivers/onebot" + "github.com/ssdomei232/goodBaby/drivers/rainyun" "github.com/ssdomei232/goodBaby/model" ) @@ -78,6 +79,7 @@ func InitExecutorRegistry() *ExecutorRegistry { registry.Register(&onebot.OneBotExecutor{}) registry.Register(&dingtalk.DingTalkExecutor{}) registry.Register(&fwalert.FwalertExecutor{}) + registry.Register(&rainyun.RainyunWorkorderExecutor{}) // 未来添加新规则类型时,在这里注册即可 globalExecutorRegistry = registry diff --git a/internal/accountConfigChecker/reg.go b/internal/accountConfigChecker/reg.go index 27cc8a7..ba4a3d5 100644 --- a/internal/accountConfigChecker/reg.go +++ b/internal/accountConfigChecker/reg.go @@ -7,6 +7,7 @@ import ( "github.com/ssdomei232/goodBaby/drivers/email" "github.com/ssdomei232/goodBaby/drivers/github" "github.com/ssdomei232/goodBaby/drivers/onebot" + "github.com/ssdomei232/goodBaby/drivers/rainyun" ) var ( @@ -24,6 +25,7 @@ func InitValidatorRegistry() *ValidatorRegistry { r.Register(&email.EmailAccountConfigValidator{}) r.Register(&github.GitHubAccountConfigValidator{}) r.Register(&onebot.OneBotAccountConfigValidator{}) + r.Register(&rainyun.RainyunAccountConfigValidator{}) registry = r }) diff --git a/internal/ruleConfigChecker/reg.go b/internal/ruleConfigChecker/reg.go index f3f6a8d..38d524f 100644 --- a/internal/ruleConfigChecker/reg.go +++ b/internal/ruleConfigChecker/reg.go @@ -9,6 +9,7 @@ import ( "github.com/ssdomei232/goodBaby/drivers/fwalert" "github.com/ssdomei232/goodBaby/drivers/github" "github.com/ssdomei232/goodBaby/drivers/onebot" + "github.com/ssdomei232/goodBaby/drivers/rainyun" ) var ( @@ -29,6 +30,7 @@ func InitValidatorRegistry() *ValidatorRegistry { r.Register(&dingtalk.DingTalkRuleValidator{}) r.Register(&github.GithubMakeRepositoryPublicRuleValidator{}) r.Register(&fwalert.FwalertRuleValidator{}) + r.Register(&rainyun.RainyunWorkorderRuleValidator{}) registry = r })