condition/condition_expr.go

284 lines
6.5 KiB
Go
Raw Normal View History

2023-07-20 14:55:00 +08:00
package condition
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"git.fsdpf.net/go/db"
2024-05-09 09:26:57 +08:00
"git.fsdpf.net/go/req"
2023-07-20 14:55:00 +08:00
"github.com/samber/lo"
"github.com/spf13/cast"
)
type TokenType string
2024-05-09 09:26:57 +08:00
type ConditionOperator string
2024-05-08 22:58:27 +08:00
const (
SQL TokenType = "sql"
FUNC TokenType = "func"
PARAM TokenType = "param"
STRING TokenType = "string"
2024-05-08 22:58:27 +08:00
)
const (
2024-05-09 09:26:57 +08:00
IS_NULL ConditionOperator = "IS NULL"
2024-05-09 10:08:28 +08:00
IS_NOT_NULL ConditionOperator = "IS NOT NULL"
EQ ConditionOperator = "="
NE ConditionOperator = "!="
GT ConditionOperator = ">"
GE ConditionOperator = ">="
LT ConditionOperator = "<"
LE ConditionOperator = "<="
LIKE ConditionOperator = "LIKE"
NOT_LIKE ConditionOperator = "NOT LIKE"
IN ConditionOperator = "IN"
NOT_IN ConditionOperator = "NOT IN"
REGEXP ConditionOperator = "REGEXP"
NOT_REGEXP ConditionOperator = "NOT REGEXP"
2024-05-08 22:58:27 +08:00
)
2024-05-09 10:35:14 +08:00
type ExprOption func(option *ConditionExpr)
type TokenValue interface {
2024-05-09 09:26:57 +08:00
GetParam(k string) req.GlobalParams
GetGlobalParamsUser() req.User
}
2023-07-20 14:55:00 +08:00
type ConditionExpr struct {
2024-05-09 09:26:57 +08:00
parent *Condition
operator ConditionOperator
2023-07-20 14:55:00 +08:00
field string
fieldResource string
fieldSqlFunc string
fieldSqlFuncParam string
ignoreEmptyParma bool
tokenType TokenType
2023-07-20 14:55:00 +08:00
token string
matchPrefix string // 匹配前缀
}
2024-05-09 09:26:57 +08:00
func (this ConditionExpr) GetOperator() ConditionOperator {
2023-07-20 14:55:00 +08:00
return this.operator
}
2024-05-09 09:26:57 +08:00
func (this *ConditionExpr) SetMatchPrefix(s string) *ConditionExpr {
2023-07-20 14:55:00 +08:00
this.matchPrefix = s
return this
}
func (this ConditionExpr) GetField() string {
return this.field
}
func (this ConditionExpr) GetFieldResource() string {
return this.fieldResource
}
2024-05-09 09:26:57 +08:00
func (this *ConditionExpr) AppendTo(c *Condition) {
2023-07-20 14:55:00 +08:00
this.parent = c
}
func (this ConditionExpr) ToSql(m TokenValue) db.Expression {
2023-07-20 14:55:00 +08:00
first := "`" + this.fieldResource + "`.`" + this.field + "`"
if strings.Contains(this.field, "->") {
first = "`" + this.fieldResource + "`." + this.field + ""
}
value := this.GetTokenSqlValue(m)
2024-05-09 09:26:57 +08:00
operator := ConditionOperator(strings.ToUpper(string(this.GetOperator())))
2023-07-20 14:55:00 +08:00
if value == "" {
// @todo return true
// value = "''"
}
//
secondary := ""
switch operator {
2024-05-08 22:58:27 +08:00
case IS_NULL:
case IS_NOT_NULL:
2023-07-20 14:55:00 +08:00
secondary = ""
2024-05-08 22:58:27 +08:00
case EQ, NE,
GT, GE,
LT, LE,
REGEXP, NOT_REGEXP:
2023-07-20 14:55:00 +08:00
2024-05-08 22:58:27 +08:00
if this.GetTokenType() == SQL {
2023-07-20 14:55:00 +08:00
secondary = value
} else {
secondary = "'" + strings.Trim(value, "'") + "'"
}
2024-05-08 22:58:27 +08:00
case LIKE, NOT_LIKE:
2023-07-20 14:55:00 +08:00
secondary = "'%" + strings.Trim(value, "'") + "%'"
2024-05-08 22:58:27 +08:00
case IN, NOT_IN:
2023-07-20 14:55:00 +08:00
secondary = "(" + lo.Ternary(value == "", "''", value) + ")"
}
if this.fieldSqlFunc == "json_member_of" {
if this.fieldSqlFuncParam == "" {
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s, JSON_ARRAY(%s))", secondary, first))
// return db.Raw(fmt.Sprintf("%s MEMBER OF(%s)", secondary, first))
} else {
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s->>'%s', JSON_ARRAY(%s))", secondary, this.fieldSqlFuncParam, first))
// return db.Raw(fmt.Sprintf("%s MEMBER OF(%s->'%s')", secondary, first, this.FieldSqlFuncParam))
}
} else if this.fieldSqlFunc == "json_contains" {
if this.fieldSqlFuncParam == "" {
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s, JSON_ARRAY(%s))", first, secondary))
} else {
return db.Raw(fmt.Sprintf("JSON_CONTAINS(%s->>'%s', JSON_ARRAY(%s))", first, this.fieldSqlFuncParam, secondary))
}
} else if this.fieldSqlFunc != "" && this.fieldSqlFuncParam != "" {
first = this.fieldSqlFunc + "(" + first + ", " + this.fieldSqlFuncParam + ")"
} else if this.fieldSqlFunc != "" {
first = this.fieldSqlFunc + "(" + first + ")"
}
return db.Raw(strings.Trim(first+" "+string(operator)+" "+secondary, " "))
}
func (this ConditionExpr) GetTokenName() string {
return this.token
}
func (this ConditionExpr) GetTokenType() TokenType {
2023-07-20 14:55:00 +08:00
return this.tokenType
}
func (this *ConditionExpr) GetTokenSqlValue(m TokenValue) string {
2024-05-08 22:58:27 +08:00
if this.GetTokenType() == SQL {
2023-07-20 14:55:00 +08:00
return this.token
}
rv := reflect.ValueOf(this.GetTokenValue(m))
if rv.Kind() == reflect.Ptr {
rv = reflect.Indirect(rv)
}
switch rv.Kind() {
case reflect.Invalid:
return ""
case reflect.Slice:
aStr := cast.ToStringSlice(rv.Interface())
for i := 0; i < len(aStr); i++ {
if aStr[i] != "" {
aStr[i] = "'" + strings.Trim(db.MysqlRealEscapeString(aStr[i]), "'") + "'"
}
}
// 强制使用 in
2024-05-08 22:58:27 +08:00
if this.operator == EQ {
this.operator = IN
2023-07-20 14:55:00 +08:00
}
return strings.Join(aStr, ", ")
case reflect.Struct:
b, _ := json.Marshal(rv.Interface())
return fmt.Sprintf("'%s'", b)
default:
return db.MysqlRealEscapeString(fmt.Sprintf("%v", rv.Interface()))
}
}
func (this ConditionExpr) GetTokenValue(m TokenValue) any {
2023-07-20 14:55:00 +08:00
switch this.GetTokenType() {
2024-05-08 22:58:27 +08:00
case PARAM:
2023-07-20 14:55:00 +08:00
if this.matchPrefix != "" {
return m.GetParam(fmt.Sprintf("%s.%s", this.matchPrefix, this.token)).Value()
}
return m.GetParam(this.token).Value()
2024-05-08 22:58:27 +08:00
case STRING:
2023-07-20 14:55:00 +08:00
return this.token
2024-05-08 22:58:27 +08:00
case FUNC:
2023-07-20 14:55:00 +08:00
switch this.token {
case "UserID":
return m.GetGlobalParamsUser().ID()
case "UserUuid":
return m.GetGlobalParamsUser().Uuid()
case "UserRolesUuid":
return m.GetGlobalParamsUser().Roles()
case "UserPlatform":
2023-12-27 22:54:40 +08:00
return m.GetGlobalParamsUser().Runtime().Platform()
case "UserSaaS":
return m.GetGlobalParamsUser().Runtime().SaaS()
2023-07-20 14:55:00 +08:00
}
default:
return nil
}
return nil
}
func (this ConditionExpr) IsIgnoreEmptyParma(m TokenValue) bool {
2023-07-20 14:55:00 +08:00
if !this.ignoreEmptyParma {
return false
}
2024-05-08 22:58:27 +08:00
if this.tokenType != PARAM {
2023-07-20 14:55:00 +08:00
return false
}
param := this.GetTokenValue(m)
if param == "" || param == nil {
return true
}
return false
}
2024-05-09 10:35:14 +08:00
func (this *ConditionExpr) SetOption(opts ...ExprOption) *ConditionExpr {
for i := 0; i < len(opts); i++ {
opts[i](this)
}
return this
}
2024-05-09 10:08:28 +08:00
func Operator(v ConditionOperator) ExprOption {
return func(option *ConditionExpr) {
option.operator = v
}
}
func Token(token string, tType TokenType) ExprOption {
2024-05-09 10:08:28 +08:00
return func(option *ConditionExpr) {
2024-05-09 10:15:14 +08:00
option.token = token
option.tokenType = tType
2024-05-09 10:08:28 +08:00
}
}
func IgnoreEmptyParma(v bool) ExprOption {
return func(option *ConditionExpr) {
option.ignoreEmptyParma = v
}
}
func FieldSqlFn(fn, fnParam string) ExprOption {
return func(option *ConditionExpr) {
option.fieldSqlFunc = fn
option.fieldSqlFuncParam = fnParam
}
}
2024-05-09 10:21:15 +08:00
func NewExpr(rResource, rField string, opts ...ExprOption) *ConditionExpr {
2024-05-09 10:08:28 +08:00
expr := &ConditionExpr{
2023-07-20 14:55:00 +08:00
field: rField,
fieldResource: rResource,
2024-05-09 10:21:15 +08:00
token: "",
2024-05-09 10:08:28 +08:00
tokenType: STRING,
operator: EQ,
ignoreEmptyParma: false,
fieldSqlFunc: "",
fieldSqlFuncParam: "",
2023-07-20 14:55:00 +08:00
}
2024-05-09 10:35:14 +08:00
expr.SetOption(opts...)
2024-05-09 10:08:28 +08:00
return expr
2023-07-20 14:55:00 +08:00
}