package condition import ( "fmt" "strings" "git.fsdpf.net/go/db" "git.fsdpf.net/go/utils" "github.com/samber/lo" ) type ConditionType string const ( OR ConditionType = "OR" AND ConditionType = "AND" ) type Condition struct { parent *Condition typ ConditionType // 分组类型 describe string // 描述 exprs []*ConditionExpr // 分组表达式成员 childrens []*Condition // 分组子集 } func (this *Condition) AppendTo(c *Condition) { this.parent = c } // 条件类型 func (this Condition) Type() ConditionType { return this.typ } // 判断条件为空 func (this Condition) IsEmpty() bool { if len(this.exprs) > 0 { return false } for _, cond := range this.childrens { if !cond.IsEmpty() { return false } } return true } // 判断条件不为空 func (this Condition) IsNotEmpty() bool { return !this.IsEmpty() } // 判断条件是否恒成立 func (this Condition) IsAlwaysRight() bool { flag := []bool{} for _, expr := range this.exprs { if expr.GetOperator() != EQ { flag = append(flag, false) continue } if expr.GetTokenType() != SQL { flag = append(flag, false) continue } eRight := fmt.Sprintf("%s.%s", expr.GetFieldResource(), expr.GetField()) == strings.ReplaceAll(expr.GetTokenName(), "`", "") if this.typ == OR && eRight { return true } flag = append(flag, eRight) } for _, cond := range this.childrens { cRight := cond.IsAlwaysRight() if this.typ == OR && cRight { return true } flag = append(flag, cRight) } if len(flag) == 0 { return true } return !lo.Some(flag, []bool{false}) } // 生成 SQL 语句 func (this Condition) ToSql(m TokenValue) db.Expression { conditions := []string{} // 表达式 for _, item := range this.exprs { if item.IsIgnoreEmptyParma(m) { continue } conditions = append(conditions, string(item.ToSql(m))) } // 条件子集 for _, item := range this.childrens { conditions = append(conditions, string(item.ToSql(m))) } // 去除无效的表达式 conditions = lo.Filter(conditions, func(item string, _ int) bool { return item != "" }) // 组合 SQL sql := strings.Join(conditions, " "+string(this.Type())+" ") if sql == "" { if this.parent == nil && this.Type() == OR { return db.Raw("false") } else if this.parent == nil && this.Type() == AND { return db.Raw("true") } return db.Raw("") } // 包裹 SQL, 避免语法表达错误 if this.parent == nil || this.Type() == OR { sql = "(" + sql + ")" } if this.describe != "" { sql += "/*" + this.describe + "*/" } return db.Raw(sql) } // 设置条件表达式 func (this *Condition) SetExpr(expr *ConditionExpr) *Condition { expr.AppendTo(this) this.exprs = append(this.exprs, expr) return this } // 设置条件子集 func (this *Condition) SetCondition(c *Condition) *Condition { c.AppendTo(this) this.childrens = append(this.childrens, c) return this } // 设置 Token 匹配前缀 func (this *Condition) SetMatchPrefix(prefix string) *Condition { for _, expr := range this.exprs { expr.SetMatchPrefix(prefix) } for _, cond := range this.childrens { cond.SetMatchPrefix(prefix) } return this } func (this Condition) GetFieldsValue(m TokenValue, isWithResource bool) (result map[string]any) { if this.IsEmpty() { return } // 表达式 for _, item := range this.exprs { if item.GetOperator() != EQ { continue } vField := map[string]any{item.GetField(): item.GetTokenValue(m)} if isWithResource { vField = map[string]any{item.GetFieldResource(): vField} } result = utils.MergeMap(result, vField) } // 条件子集 for _, item := range this.childrens { result = utils.MergeMap(result, item.GetFieldsValue(m, isWithResource)) } return result } /** * @param ConditionOperator operator * @param TokenType types * @return map[string][string] // 如: {"TestA.field_a": "param"} */ func (this Condition) GetFields(operator ConditionOperator, types ...TokenType) map[string]string { result := make(map[string]string) if this.IsEmpty() { return result } // 表达式 for _, item := range this.exprs { if item.GetOperator() != operator || !lo.Contains(types, item.GetTokenType()) { continue } result[fmt.Sprintf("%s.%s", item.GetFieldResource(), item.GetField())] = item.GetTokenName() } // 条件子集 for _, item := range this.childrens { result = lo.Assign(result, item.GetFields(operator, types...)) } return result } func (this *Condition) SetOption(opts ...Option) *Condition { for i := 0; i < len(opts); i++ { opts[i](this) } return this } type Option func(option *Condition) func Type(v ConditionType) Option { return func(option *Condition) { option.typ = v } } func Describe(v string) Option { return func(option *Condition) { option.describe = v } } func New(opts ...Option) *Condition { cond := &Condition{typ: AND} cond.SetOption(opts...) return cond }