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"
|
|
|
|
)
|
|
|
|
|
2024-05-09 10:47:51 +08:00
|
|
|
type TokenType string
|
2024-05-09 09:26:57 +08:00
|
|
|
type ConditionOperator string
|
|
|
|
|
2024-05-08 22:58:27 +08:00
|
|
|
const (
|
2024-05-09 10:47:51 +08:00
|
|
|
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)
|
|
|
|
|
2024-05-09 10:44:33 +08:00
|
|
|
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
|
2024-05-09 10:47:51 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:44:33 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:47:51 +08:00
|
|
|
func (this ConditionExpr) GetTokenType() TokenType {
|
2023-07-20 14:55:00 +08:00
|
|
|
return this.tokenType
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:44:33 +08:00
|
|
|
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()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:44:33 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:44:33 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-09 10:47:51 +08:00
|
|
|
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
|
|
|
}
|