后端: - 修复登录校验、DB 并发(单例+WAL+纯Go sqlite)、会话密钥持久化等核心缺陷 - 新增 Timer CRUD/签到/手动触发、执行日志、仪表盘、驱动元数据等 API - 统一驱动接口: 带 context 的执行器、真实错误返回、敏感字段掩码、连通性测试 - 补全钉钉执行器; Timer 触发/提醒去重; 配置支持默认值与环境变量覆盖 前端: - Vite+Vue3+TS+Pinia+Element Plus, 由驱动元数据动态渲染配置表单 - 登录/仪表盘(倒计时环)/定时器/规则/账号/日志/设置页面, 支持暗色模式 - 主题色 #66ccff, 信号波纹 logo, go:embed 单二进制部署 构建: Dockerfile 增加前端构建阶段; 补充各驱动配置文档 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
106 lines
2.6 KiB
Go
106 lines
2.6 KiB
Go
package rule
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/ssdomei232/goodBaby/handler/db"
|
|
"github.com/ssdomei232/goodBaby/internal/meta"
|
|
"github.com/ssdomei232/goodBaby/internal/ruleConfigChecker"
|
|
"github.com/ssdomei232/goodBaby/model"
|
|
)
|
|
|
|
func parseID(raw string) (uint, error) {
|
|
id, err := strconv.ParseUint(raw, 10, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint(id), nil
|
|
}
|
|
|
|
func boolOr(v *bool, fallback bool) bool {
|
|
if v == nil {
|
|
return fallback
|
|
}
|
|
return *v
|
|
}
|
|
|
|
// findRule 查找属于该用户的规则
|
|
func findRule(ruleID, uid uint) (*model.Rule, error) {
|
|
gormDB, err := db.GetGormDB()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var target model.Rule
|
|
if err := gormDB.Where("id = ? AND uid = ?", ruleID, uid).First(&target).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &target, nil
|
|
}
|
|
|
|
// DeleteRuleByID 根据 ID 删除规则
|
|
func DeleteRuleByID(id uint, uid uint) error {
|
|
gormDB, err := db.GetGormDB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return gormDB.Where("id = ? AND uid = ?", id, uid).Delete(&model.Rule{}).Error
|
|
}
|
|
|
|
// checkRuleConfigAccountAndTimerExist 检查关联的 Timer 与账号是否存在且归属当前用户
|
|
//
|
|
// requiredAccountType 为空表示该规则类型不需要账号。
|
|
func checkRuleConfigAccountAndTimerExist(rule *model.Rule, requiredAccountType string) error {
|
|
gormDB, err := db.GetGormDB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var count int64
|
|
if err := gormDB.Model(&model.Timer{}).
|
|
Where("id = ? AND uid = ?", rule.TimerID, rule.UID).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
return model.ErrValidation("关联的 Timer 不存在")
|
|
}
|
|
|
|
if requiredAccountType == "" {
|
|
// 该类型不需要账号,忽略前端可能传来的 account_id
|
|
rule.AccountID = 0
|
|
return nil
|
|
}
|
|
|
|
if rule.AccountID == 0 {
|
|
return model.ErrValidation("该规则类型必须关联一个账号")
|
|
}
|
|
|
|
var account model.Account
|
|
if err := gormDB.Where("id = ? AND uid = ?", rule.AccountID, rule.UID).First(&account).Error; err != nil {
|
|
return model.ErrValidation("关联的账号不存在")
|
|
}
|
|
if account.Type != requiredAccountType {
|
|
return model.ErrValidation(fmt.Sprintf("规则需要 %s 类型的账号,但关联的是 %s", requiredAccountType, account.Type))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// maskRule 掩码规则配置中的敏感字段
|
|
func maskRule(target model.Rule) model.Rule {
|
|
if ruleMeta, ok := ruleConfigChecker.InitValidatorRegistry().MetaOf(target.Type); ok {
|
|
target.ConfigJson = meta.Mask(target.ConfigJson, ruleMeta.Fields)
|
|
}
|
|
return target
|
|
}
|
|
|
|
func maskRules(rules []model.Rule) []model.Rule {
|
|
masked := make([]model.Rule, 0, len(rules))
|
|
for _, r := range rules {
|
|
masked = append(masked, maskRule(r))
|
|
}
|
|
return masked
|
|
}
|