package base import ( "encoding/json" "fmt" "reflect" "strconv" "strings" "git.fsdpf.net/go/contracts" "git.fsdpf.net/go/contracts/res_type" "git.fsdpf.net/go/db" "git.fsdpf.net/go/db/schema" "github.com/google/uuid" "github.com/spf13/cast" ) // 资源字段 type ResField struct { Uuid string `db:"uuid"` Name string `db:"name"` Code string `db:"code"` CodeResource string `db:"codeResource"` DataType contracts.ResDataType `db:"table_type"` Length string `db:"length"` Comment string `db:"comment"` Default string `db:"default"` } func (this ResField) ToStructField(tags ...string) reflect.StructField { var typ reflect.Type fCode := this.Code fTag := `db:"` + fCode + `" json:"` + fCode + `"` if len(tags) > 0 { fTag = strings.Join(tags, " ") + " " + fTag } switch this.DataType { case contracts.ResDataType_String, contracts.ResDataType_Text, contracts.ResDataType_Enum, contracts.ResDataType_Timestamp, contracts.ResDataType_Date, contracts.ResDataType_Datetime: typ = reflect.TypeOf(string("")) case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger: typ = reflect.TypeOf(res_type.ResFieldByInteger(0)) case contracts.ResDataType_Decimal: typ = reflect.TypeOf(float64(0)) case contracts.ResDataType_Boolean: typ = reflect.TypeOf(true) case contracts.ResDataType_Json: if this.Default != "" && this.Default[0:1] == "[" { typ = reflect.TypeOf(res_type.ResFieldByAnys{}) } else { typ = reflect.TypeOf(map[string]any{}) } } return reflect.StructField{ Name: strings.ToUpper(fCode[:1]) + fCode[1:], Tag: reflect.StructTag(fTag), Type: typ, } } func (this ResField) GetCode() string { return this.Code } func (this ResField) GetCodeResource() string { return this.CodeResource } func (this ResField) GetName() string { return this.Name } func (this ResField) GetDataType() contracts.ResDataType { return this.DataType } func (this ResField) GetQueryDataType() contracts.QueryDataType { switch this.GetDataType() { case contracts.ResDataType_Enum, contracts.ResDataType_Timestamp, contracts.ResDataType_Date, contracts.ResDataType_Datetime, contracts.ResDataType_String, contracts.ResDataType_Text: return contracts.QueryDataType_String case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger: return contracts.QueryDataType_Integer case contracts.ResDataType_Decimal: return contracts.QueryDataType_Float case contracts.ResDataType_Boolean: return contracts.QueryDataType_Bool case contracts.ResDataType_Json: if this.Default != "" && this.Default[0:1] == "[" { return contracts.QueryDataType_Array } return contracts.QueryDataType_Json } return contracts.QueryDataType_String } func (this ResField) ToValue(v any) any { // 类型转换, 填补 cast 未知的类型 switch data := v.(type) { case res_type.ResFieldByInteger: v = int64(data) case res_type.ResFieldByFloat: v = float64(data) case res_type.ResFieldByNumber: v = float64(data) case string: v = strings.Trim(string(data), " ") case []any, []string, []int, []float64, []float32, []int64: if v != nil { b, _ := json.Marshal(v) return string(b) } v = data case map[string]any, map[string]string, map[string]int: if v != nil { b, _ := json.Marshal(v) return string(b) } v = data default: if v != nil { b, _ := json.Marshal(v) return string(b) } v = data } switch this.DataType { case contracts.ResDataType_String, contracts.ResDataType_Text, contracts.ResDataType_Enum, contracts.ResDataType_Timestamp, contracts.ResDataType_Date, contracts.ResDataType_Datetime: return strings.Trim(cast.ToString(v), " ") case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger: return cast.ToInt(v) case contracts.ResDataType_Decimal: return strings.Trim(cast.ToString(v), " ") case contracts.ResDataType_Boolean: v, _ := strconv.ParseBool(fmt.Sprintf("%v", v)) return v case contracts.ResDataType_Json: if v == nil { if this.Default != "" && this.Default[0:1] == "[" { return "[]" } else if this.Default != "" && this.Default[0:1] == "{" { return "{}" } else if this.Default == "" { return "{}" } return this.Default } b, _ := json.Marshal(v) return string(b) } return strings.Trim(cast.ToString(v), " ") } func (this ResField) GetRawDefault(driver string) db.Expression { if this.DataType == "json" && this.Default == "" { return db.Raw("'{}'") } if len(this.Default) > 4 && strings.ToLower(this.Default[0:4]) == "sql:" { sql := strings.ToLower(this.Default[4:]) if sql == "uuid()" { if driver == "sqlite" { return db.Raw("'" + uuid.NewString() + "'") } return db.Raw("uuid()") } return db.Raw(sql) } if this.Default == "" { if this.GetDataType() == contracts.ResDataType_Date || this.GetDataType() == contracts.ResDataType_Datetime { return db.Raw("NULL") } return db.Raw("''") } if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" { return db.Raw(this.Default) } return db.Raw("'" + this.Default + "'") } func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDefinition) { isNull := false comment := this.Name def := any(this.Default) if this.Comment != "" { comment += " [ " + strings.Trim(this.Comment, `' "`) + " ]" } switch this.DataType { case "string": len := 255 if v, err := strconv.Atoi(strings.Trim(this.Length, `' "`)); err == nil { len = v } temp = table.String(this.Code, len) case "smallInteger": // integer 默认长度 4 temp = table.SmallInteger(this.Code) case "boolean": // integer 默认长度 1 temp = table.Boolean(this.Code) case "integer": // integer 默认长度 11 temp = table.Integer(this.Code) case "date", "dateTime": if this.DataType == "date" { temp = table.Date(this.Code) } else { temp = table.DateTime(this.Code) } if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" { def = db.Raw(this.Default) } else if def == "" { isNull = true } case "decimal": allowed := strings.SplitN(this.Length, ",", 2) total := 8 places := 2 if v, err := strconv.Atoi(strings.Trim(allowed[0], `' "`)); err == nil { total = v } if v, err := strconv.Atoi(strings.Trim(allowed[1], `' "`)); err == nil { places = v } temp = table.Decimal(this.Code, total, places) case "enum": allowed := []string{} for _, v := range strings.Split(this.Length, ",") { allowed = append(allowed, strings.Trim(v, `' "`)) } temp = table.Enum(this.Code, allowed) case "json": temp = table.Json(this.Code) isNull = true case "text": temp = table.Text(this.Code) isNull = true } if isNull { temp.Nullable() } else { temp.Default(def) } temp.Comment(comment) return temp } func (this ResField) ToQueryField(t contracts.QueryDataType, alias string, options int) contracts.QueryField { o := &QueryField{ ResField: this, typ: t, alias: alias, } return o.SetOptions(options) }