package condition import ( "encoding/json" "fmt" "reflect" "strings" "git.fsdpf.net/go/contracts" "git.fsdpf.net/go/db" "github.com/samber/lo" "github.com/spf13/cast" ) type ConditionExpr struct { parent contracts.Condition operator contracts.ConditionOperator field string fieldResource string fieldSqlFunc string fieldSqlFuncParam string ignoreEmptyParma bool tokenType contracts.ConditionTokenType token string matchPrefix string // 匹配前缀 } func (this ConditionExpr) GetOperator() contracts.ConditionOperator { return this.operator } func (this *ConditionExpr) SetMatchPrefix(s string) contracts.ConditionExpr { this.matchPrefix = s return this } func (this ConditionExpr) GetField() string { return this.field } func (this ConditionExpr) GetFieldResource() string { return this.fieldResource } func (this *ConditionExpr) AppendTo(c contracts.Condition) { this.parent = c } func (this ConditionExpr) ToSql(m contracts.ModelParam) db.Expression { first := "`" + this.fieldResource + "`.`" + this.field + "`" if strings.Contains(this.field, "->") { first = "`" + this.fieldResource + "`." + this.field + "" } value := this.GetTokenSqlValue(m) operator := contracts.ConditionOperator(strings.ToUpper(string(this.GetOperator()))) if value == "" { // @todo return true // value = "''" } // secondary := "" switch operator { case contracts.ConditionOperator_IS_NULL: case contracts.ConditionOperator_IS_NOT_NULL: secondary = "" case contracts.ConditionOperator_EQ, contracts.ConditionOperator_NE, contracts.ConditionOperator_GT, contracts.ConditionOperator_GE, contracts.ConditionOperator_LT, contracts.ConditionOperator_LE, contracts.ConditionOperator_REGEXP, contracts.ConditionOperator_NOT_REGEXP: if this.GetTokenType() == contracts.ConditionTokenType_SQL { secondary = value } else { secondary = "'" + strings.Trim(value, "'") + "'" } case contracts.ConditionOperator_LIKE, contracts.ConditionOperator_NOT_LIKE: secondary = "'%" + strings.Trim(value, "'") + "%'" case contracts.ConditionOperator_IN, contracts.ConditionOperator_NOT_IN: 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() contracts.ConditionTokenType { return this.tokenType } func (this *ConditionExpr) GetTokenSqlValue(m contracts.ModelParam) string { if this.GetTokenType() == contracts.ConditionTokenType_SQL { 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 if this.operator == contracts.ConditionOperator_EQ { this.operator = contracts.ConditionOperator_IN } 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 contracts.ModelParam) any { switch this.GetTokenType() { case contracts.ConditionTokenType_PARAM: if this.matchPrefix != "" { return m.GetParam(fmt.Sprintf("%s.%s", this.matchPrefix, this.token)).Value() } return m.GetParam(this.token).Value() case contracts.ConditionTokenType_STRING: return this.token case contracts.ConditionTokenType_FUNC: switch this.token { case "UserID": return m.GetGlobalParamsUser().ID() case "UserUuid": return m.GetGlobalParamsUser().Uuid() case "UserRolesUuid": return m.GetGlobalParamsUser().Roles() case "UserPlatform": return m.GetGlobalParamsUser().Runtime().Platform() case "UserSaaS": return m.GetGlobalParamsUser().Runtime().SaaS() } default: return nil } return nil } func (this ConditionExpr) IsIgnoreEmptyParma(m contracts.ModelParam) bool { if !this.ignoreEmptyParma { return false } if this.tokenType != contracts.ConditionTokenType_PARAM { return false } param := this.GetTokenValue(m) if param == "" || param == nil { return true } return false } func NewConditionExpr(rResource, rField, token string, operator contracts.ConditionOperator, tType contracts.ConditionTokenType, ignoreEmptyParma bool, fn, fnParam string) contracts.ConditionExpr { return &ConditionExpr{ operator: operator, field: rField, fieldResource: rResource, fieldSqlFunc: fn, fieldSqlFuncParam: fnParam, ignoreEmptyParma: ignoreEmptyParma, tokenType: tType, token: string(token), } }