diff --git a/api/rule/rule.go b/api/rule/rule.go index 86051a9..a4125f8 100644 --- a/api/rule/rule.go +++ b/api/rule/rule.go @@ -1,11 +1,13 @@ package rule import ( + "fmt" "strconv" "github.com/gin-gonic/gin" "github.com/ssdomei232/goodBaby/api/user" "github.com/ssdomei232/goodBaby/handler/db" + "github.com/ssdomei232/goodBaby/internal/ruleConfigChecker" "github.com/ssdomei232/goodBaby/model" ) @@ -53,6 +55,23 @@ func HandleCreateRule(c *gin.Context) { return } + if newRule.Type == "" { + c.JSON(400, gin.H{"code": 400, "data": "规则类型不能为空"}) + return + } + + if newRule.Name == "" { + c.JSON(400, gin.H{"code": 400, "data": "规则名称不能为空"}) + return + } + + // 规则校验 + validatorRegistry := ruleConfigChecker.InitValidatorRegistry() + if err := validatorRegistry.Validate(newRule.Type, newRule.ConfigJson); err != nil { + c.JSON(400, gin.H{"code": 400, "data": fmt.Sprintf("规则配置验证失败: %s", err.Error())}) + return + } + newRule.UID = userInfo.ID result := gormDB.Create(&newRule) if result.Error != nil { diff --git a/handler/runner/bilibili.go b/handler/runner/bilibili.go new file mode 100644 index 0000000..71fe552 --- /dev/null +++ b/handler/runner/bilibili.go @@ -0,0 +1,23 @@ +package runner + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/bilibili" + "github.com/ssdomei232/goodBaby/model" +) + +// BilibiliDynamicExecutor B站动态执行器 +type BilibiliDynamicExecutor struct{} + +func (e *BilibiliDynamicExecutor) GetType() string { + return "bilibili-dynamic" +} + +func (e *BilibiliDynamicExecutor) Execute(rule *model.Rule) error { + log.Printf("执行B站动态规则: %s (ID: %d)", rule.Name, rule.ID) + + bilibili.SendBiliDynamicMsg(rule) + + return nil +} diff --git a/handler/runner/email.go b/handler/runner/email.go new file mode 100644 index 0000000..7c0500c --- /dev/null +++ b/handler/runner/email.go @@ -0,0 +1,23 @@ +package runner + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/email" + "github.com/ssdomei232/goodBaby/model" +) + +// EmailExecutor 邮件执行器 +type EmailExecutor struct{} + +func (e *EmailExecutor) GetType() string { + return "email" +} + +func (e *EmailExecutor) Execute(rule *model.Rule) error { + log.Printf("执行邮件规则: %s (ID: %d)", rule.Name, rule.ID) + + email.SendMail(rule) + + return nil +} diff --git a/handler/runner/github.go b/handler/runner/github.go new file mode 100644 index 0000000..4a8cc38 --- /dev/null +++ b/handler/runner/github.go @@ -0,0 +1,23 @@ +package runner + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/github" + "github.com/ssdomei232/goodBaby/model" +) + +// GithubMakeRepoPublicExecutor GitHub仓库公开执行器 +type GithubMakeRepoPublicExecutor struct{} + +func (e *GithubMakeRepoPublicExecutor) GetType() string { + return "github-make-repo-public" +} + +func (e *GithubMakeRepoPublicExecutor) Execute(rule *model.Rule) error { + log.Printf("执行GitHub仓库公开规则: %s (ID: %d)", rule.Name, rule.ID) + + github.MakeRepositoryPublic(rule) + + return nil +} diff --git a/handler/runner/interface.go b/handler/runner/interface.go new file mode 100644 index 0000000..408fc01 --- /dev/null +++ b/handler/runner/interface.go @@ -0,0 +1,76 @@ +package runner + +import ( + "fmt" + + "github.com/ssdomei232/goodBaby/model" +) + +// RuleExecutor 规则执行器接口 +type RuleExecutor interface { + // Execute 执行规则 + Execute(rule *model.Rule) error + // GetType 获取执行器支持的规则类型 + GetType() string +} + +// ExecutorRegistry 执行器注册表 +type ExecutorRegistry struct { + executors map[string]RuleExecutor +} + +// NewExecutorRegistry 创建新的执行器注册表 +func NewExecutorRegistry() *ExecutorRegistry { + return &ExecutorRegistry{ + executors: make(map[string]RuleExecutor), + } +} + +// Register 注册规则执行器 +func (er *ExecutorRegistry) Register(executor RuleExecutor) { + er.executors[executor.GetType()] = executor +} + +// Execute 根据规则类型执行规则 +func (er *ExecutorRegistry) Execute(rule *model.Rule) error { + executor, exists := er.executors[rule.Type] + if !exists { + return fmt.Errorf("不支持的规则类型: %s", rule.Type) + } + + return executor.Execute(rule) +} + +// GetSupportedTypes 获取所有支持的规则类型 +func (er *ExecutorRegistry) GetSupportedTypes() []string { + types := make([]string, 0, len(er.executors)) + for t := range er.executors { + types = append(types, t) + } + return types +} + +var globalExecutorRegistry *ExecutorRegistry + +// InitExecutorRegistry 初始化执行器注册表并注册所有执行器 +func InitExecutorRegistry() *ExecutorRegistry { + registry := NewExecutorRegistry() + + // 注册所有规则执行器 + registry.Register(&BilibiliDynamicExecutor{}) + registry.Register(&EmailExecutor{}) + registry.Register(&GithubMakeRepoPublicExecutor{}) + registry.Register(&OneBotExecutor{}) + // 未来添加新规则类型时,在这里注册即可 + + globalExecutorRegistry = registry + return registry +} + +// GetGlobalExecutorRegistry 获取全局执行器注册表 +func GetGlobalExecutorRegistry() *ExecutorRegistry { + if globalExecutorRegistry == nil { + panic("执行器注册表未初始化,请先调用 InitExecutorRegistry()") + } + return globalExecutorRegistry +} diff --git a/handler/runner/onebot.go b/handler/runner/onebot.go new file mode 100644 index 0000000..2bddbeb --- /dev/null +++ b/handler/runner/onebot.go @@ -0,0 +1,23 @@ +package runner + +import ( + "log" + + "github.com/ssdomei232/goodBaby/drivers/onebot" + "github.com/ssdomei232/goodBaby/model" +) + +// OneBotExecutor OneBot执行器 +type OneBotExecutor struct{} + +func (e *OneBotExecutor) GetType() string { + return "onebot" +} + +func (e *OneBotExecutor) Execute(rule *model.Rule) error { + log.Printf("执行OneBot规则: %s (ID: %d)", rule.Name, rule.ID) + + onebot.SendOneBotMsg(rule) + + return nil +} diff --git a/handler/runner/runner.go b/handler/runner/runner.go index c39b5a6..f5d8c3c 100644 --- a/handler/runner/runner.go +++ b/handler/runner/runner.go @@ -3,10 +3,6 @@ package runner import ( "log" - "github.com/ssdomei232/goodBaby/drivers/bilibili" - "github.com/ssdomei232/goodBaby/drivers/email" - "github.com/ssdomei232/goodBaby/drivers/github" - "github.com/ssdomei232/goodBaby/drivers/onebot" "github.com/ssdomei232/goodBaby/handler/db" "github.com/ssdomei232/goodBaby/model" ) @@ -28,21 +24,15 @@ func Runner(timer *model.Timer) { // 2. 执行每个 Rule for _, rule := range rules { - // 执行 rule go executeRule(&rule) } } -// super switch +// executeRule 执行单个规则 func executeRule(rule *model.Rule) { - switch rule.Type { - case "bilibili-dynamic": - bilibili.SendBiliDynamicMsg(rule) - case "email": - email.SendMail(rule) - case "github-make-repo-public": - github.MakeRepositoryPublic(rule) - case "onebot": - onebot.SendOneBotMsg(rule) + executorRegistry := GetGlobalExecutorRegistry() + + if err := executorRegistry.Execute(rule); err != nil { + log.Printf("执行规则失败 [ID: %d, Type: %s]: %v", rule.ID, rule.Type, err) } } diff --git a/internal/ruleConfigChecker/bilibili.go b/internal/ruleConfigChecker/bilibili.go new file mode 100644 index 0000000..c5f3f59 --- /dev/null +++ b/internal/ruleConfigChecker/bilibili.go @@ -0,0 +1,28 @@ +package ruleConfigChecker + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/drivers/bilibili" +) + +// BilibiliDynamicRuleValidator B站动态规则验证器 +type BilibiliDynamicRuleValidator struct{} + +func (v *BilibiliDynamicRuleValidator) GetType() string { + return "bilibili-dynamic" +} + +func (v *BilibiliDynamicRuleValidator) Validate(configJSON string) error { + var config bilibili.BiliDynamicConfig + if err := json.Unmarshal([]byte(configJSON), &config); err != nil { + return fmt.Errorf("解析B站动态规则配置失败: %v", err) + } + + if config.Msg == "" { + return fmt.Errorf("B站动态规则配置中 msg 不能为空") + } + + return nil +} diff --git a/internal/ruleConfigChecker/dingtalk.go b/internal/ruleConfigChecker/dingtalk.go new file mode 100644 index 0000000..c75c3db --- /dev/null +++ b/internal/ruleConfigChecker/dingtalk.go @@ -0,0 +1,28 @@ +package ruleConfigChecker + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/drivers/dingtalk" +) + +// DingTalkRuleValidator 钉钉规则验证器 +type DingTalkRuleValidator struct{} + +func (v *DingTalkRuleValidator) GetType() string { + return "dingtalk" +} + +func (v *DingTalkRuleValidator) Validate(configJSON string) error { + var config dingtalk.DingTalkConfig + if err := json.Unmarshal([]byte(configJSON), &config); err != nil { + return fmt.Errorf("解析钉钉规则配置失败: %v", err) + } + + if config.AccessToken == "" { + return fmt.Errorf("钉钉规则配置中 access_token 不能为空") + } + + return nil +} diff --git a/internal/ruleConfigChecker/email.go b/internal/ruleConfigChecker/email.go new file mode 100644 index 0000000..373fb49 --- /dev/null +++ b/internal/ruleConfigChecker/email.go @@ -0,0 +1,44 @@ +package ruleConfigChecker + +import ( + "encoding/json" + "fmt" + "net/mail" + + "github.com/ssdomei232/goodBaby/drivers/email" +) + +// EmailRuleValidator 邮件规则验证器 +type EmailRuleValidator struct{} + +func (v *EmailRuleValidator) GetType() string { + return "email" +} + +func (v *EmailRuleValidator) Validate(configJSON string) error { + var config email.EmailRule + 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 不能为空") + } + + if len(config.Destinations) == 0 { + return fmt.Errorf("邮件规则配置中 destinations 不能为空") + } + + // 验证邮箱地址格式 + for _, dest := range config.Destinations { + if _, err := mail.ParseAddress(dest); err != nil { + return fmt.Errorf("无效的邮箱地址: %s", dest) + } + } + + return nil +} diff --git a/internal/ruleConfigChecker/onebot.go b/internal/ruleConfigChecker/onebot.go new file mode 100644 index 0000000..e10e7fa --- /dev/null +++ b/internal/ruleConfigChecker/onebot.go @@ -0,0 +1,32 @@ +package ruleConfigChecker + +import ( + "encoding/json" + "fmt" + + "github.com/ssdomei232/goodBaby/drivers/onebot" +) + +// OneBotRuleValidator OneBot规则验证器 +type OneBotRuleValidator struct{} + +func (v *OneBotRuleValidator) GetType() string { + return "onebot" +} + +func (v *OneBotRuleValidator) Validate(configJSON string) error { + var config onebot.OneBotConfig + if err := json.Unmarshal([]byte(configJSON), &config); err != nil { + return fmt.Errorf("解析OneBot规则配置失败: %v", err) + } + + if config.Msg == "" { + return fmt.Errorf("OneBot规则配置中 msg 不能为空") + } + + if len(config.SendGroups) == 0 { + return fmt.Errorf("OneBot规则配置中 send_groups 不能为空") + } + + return nil +} diff --git a/internal/ruleConfigChecker/ruleConfigChecker.go b/internal/ruleConfigChecker/ruleConfigChecker.go new file mode 100644 index 0000000..983719f --- /dev/null +++ b/internal/ruleConfigChecker/ruleConfigChecker.go @@ -0,0 +1,74 @@ +package ruleConfigChecker + +import ( + "encoding/json" + "fmt" +) + +// RuleValidator 规则验证器接口 +type RuleValidator interface { + // Validate 验证规则配置是否合法 + Validate(configJSON string) error + // GetType 获取验证器支持的规则类型 + GetType() string +} + +// ValidatorRegistry 验证器注册表 +type ValidatorRegistry struct { + validators map[string]RuleValidator +} + +// NewValidatorRegistry 创建新的验证器注册表 +func NewValidatorRegistry() *ValidatorRegistry { + return &ValidatorRegistry{ + validators: make(map[string]RuleValidator), + } +} + +// Register 注册规则验证器 +func (vr *ValidatorRegistry) Register(validator RuleValidator) { + vr.validators[validator.GetType()] = validator +} + +// Validate 根据规则类型验证配置 +func (vr *ValidatorRegistry) Validate(ruleType string, configJSON string) error { + validator, exists := vr.validators[ruleType] + if !exists { + return fmt.Errorf("不支持的规则类型: %s", ruleType) + } + + if configJSON == "" { + return fmt.Errorf("规则配置不能为空") + } + + // 首先验证 JSON 格式是否正确 + var js json.RawMessage + if err := json.Unmarshal([]byte(configJSON), &js); err != nil { + return fmt.Errorf("规则配置 JSON 格式错误: %v", err) + } + + // 调用具体验证器的验证逻辑 + return validator.Validate(configJSON) +} + +// GetSupportedTypes 获取所有支持的规则类型 +func (vr *ValidatorRegistry) GetSupportedTypes() []string { + types := make([]string, 0, len(vr.validators)) + for t := range vr.validators { + types = append(types, t) + } + return types +} + +// InitValidatorRegistry 初始化验证器注册表并注册所有验证器 +func InitValidatorRegistry() *ValidatorRegistry { + registry := NewValidatorRegistry() + + // 注册所有规则验证器 + registry.Register(&BilibiliDynamicRuleValidator{}) + registry.Register(&EmailRuleValidator{}) + registry.Register(&OneBotRuleValidator{}) + registry.Register(&DingTalkRuleValidator{}) + + return registry +} diff --git a/main.go b/main.go index 2672968..03a5385 100644 --- a/main.go +++ b/main.go @@ -12,9 +12,16 @@ import ( "github.com/ssdomei232/goodBaby/api/rule" "github.com/ssdomei232/goodBaby/api/user" "github.com/ssdomei232/goodBaby/handler/checker" + "github.com/ssdomei232/goodBaby/handler/runner" ) func main() { + runner.InitExecutorRegistry() + + c := cron.New() + c.AddFunc("@every 10m", checker.CheckTimers) + c.Start() + r := gin.Default() store := cookie.NewStore(generateRandomKey(32)) r.Use(sessions.Sessions("goodbaby-session", store)) @@ -40,10 +47,6 @@ func main() { } r.Run(":8088") - - c := cron.New() - c.AddFunc("@every 10m", checker.CheckTimers) - c.Start() } func generateRandomKey(length int) []byte {