重构: base 包资源核心类型迁移到 req/resx,新增 res_watcher/res_api_param
base/resource.go、resource_hooks.go、resource_test.go、query_field.go 删除,ResField 等具体实现搬到 req/resx(见 res_field.go 里的类型别名)。res_listener.go 替换成 res_watcher.go,对应资源变更监听概念改名。新增 res_api_param.go 及配套测试(ResApi 参数建模,给 MCP tool 的 JSON Schema 生成用)。
This commit is contained in:
+4
-269
@@ -1,274 +1,9 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/schema"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/spf13/cast"
|
||||
"git.fsdpf.net/go/req/resx"
|
||||
)
|
||||
|
||||
// ResField 资源字段
|
||||
type ResField struct {
|
||||
Uuid string `db:"uuid" json:"uuid"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
CodeResource string `db:"codeResource" json:"codeResource"`
|
||||
DataType req.ResDataType `db:"data_type" json:"data_type"`
|
||||
Length string `db:"length" json:"length"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
Default string `db:"default" json:"default"`
|
||||
Virtual bool `db:"virtual" json:"virtual"`
|
||||
VirtualExpr string `db:"virtual_expr" json:"virtual_expr"`
|
||||
}
|
||||
|
||||
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 req.ResString, req.ResText, req.ResEnum,
|
||||
req.ResTimestamp, req.ResDate, req.ResDatetime:
|
||||
typ = reflect.TypeOf(string(""))
|
||||
case req.ResInteger, req.ResSmallInteger:
|
||||
typ = reflect.TypeOf(int64(0))
|
||||
case req.ResDecimal:
|
||||
typ = reflect.TypeOf(float64(0))
|
||||
case req.ResBoolean:
|
||||
typ = reflect.TypeOf(true)
|
||||
case req.ResJson:
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
typ = reflect.TypeOf([]any{})
|
||||
} 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) IsVirtual() bool {
|
||||
return this.Virtual
|
||||
}
|
||||
|
||||
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() req.ResDataType {
|
||||
return this.DataType
|
||||
}
|
||||
|
||||
func (this ResField) GetQueryDataType() req.RouteParamType {
|
||||
switch this.GetDataType() {
|
||||
case req.ResEnum,
|
||||
req.ResTimestamp, req.ResDate,
|
||||
req.ResDatetime, req.ResString,
|
||||
req.ResText:
|
||||
return req.ReqString
|
||||
case req.ResInteger, req.ResSmallInteger:
|
||||
return req.ReqInteger
|
||||
case req.ResDecimal:
|
||||
return req.ReqFloat
|
||||
case req.ResBoolean:
|
||||
return req.ReqBool
|
||||
case req.ResJson:
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
return req.ReqArray
|
||||
}
|
||||
return req.ReqJson
|
||||
}
|
||||
return req.ReqString
|
||||
}
|
||||
|
||||
func (this ResField) ToValue(v any) any {
|
||||
if this.DataType == req.ResJson {
|
||||
if v == nil {
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
return db.V("[]")
|
||||
} else if this.Default != "" && this.Default[0:1] == "{" {
|
||||
return db.V("{}")
|
||||
} else if this.Default == "" {
|
||||
return db.V("{}")
|
||||
}
|
||||
return this.GetRawDefault()
|
||||
}
|
||||
|
||||
if str, ok := v.(string); ok {
|
||||
return str
|
||||
} else if raw, ok := v.(db.Expression); ok {
|
||||
return raw
|
||||
} else if b, err := json.Marshal(v); err == nil {
|
||||
return string(b)
|
||||
} else {
|
||||
panic(fmt.Sprintf("%s, 类型转换错误, %s", this.Code, err))
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (this ResField) GetRawDefault() db.Expression {
|
||||
if this.DataType == req.ResJson {
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
return db.V("[]")
|
||||
} else if this.Default != "" && this.Default[0:1] == "{" {
|
||||
return db.V("{}")
|
||||
} else if this.Default == "" {
|
||||
return db.V("{}")
|
||||
}
|
||||
} else if this.DataType == req.ResBoolean {
|
||||
if v, _ := strconv.ParseBool(this.Default); v {
|
||||
return db.V(true)
|
||||
}
|
||||
return db.V(false)
|
||||
}
|
||||
|
||||
if len(this.Default) > 4 && strings.ToLower(this.Default[0:4]) == "sql:" {
|
||||
return db.L(this.Default[4:])
|
||||
}
|
||||
|
||||
if this.Default == "" {
|
||||
if this.GetDataType() == req.ResDate || this.GetDataType() == req.ResDatetime {
|
||||
return db.V(nil)
|
||||
}
|
||||
return db.V("")
|
||||
}
|
||||
|
||||
if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" {
|
||||
return db.L(this.Default)
|
||||
}
|
||||
|
||||
return db.V(this.Default)
|
||||
}
|
||||
|
||||
func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDefinition) {
|
||||
switch this.Code {
|
||||
case "id":
|
||||
return table.BigIncrements("id").AutoIncrement().Comment("ID")
|
||||
case "enabled":
|
||||
return table.Boolean("enabled").Default("1").Comment("是否有效")
|
||||
case "created_user":
|
||||
return table.Char("created_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("创建者")
|
||||
case "owned_user":
|
||||
return table.Char("owned_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("拥有者")
|
||||
case "created_at":
|
||||
return table.Timestamp("created_at").UseCurrent().Comment("创建时间")
|
||||
case "updated_at":
|
||||
return table.Timestamp("updated_at").UseCurrent().Default(db.L("ON UPDATE CURRENT_TIMESTAMP")).Comment("更新时间")
|
||||
case "deleted_at":
|
||||
return table.DateTime("deleted_at").Nullable().Comment("删除时间")
|
||||
}
|
||||
|
||||
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", "timestamp":
|
||||
if this.DataType == "date" {
|
||||
temp = table.Date(this.Code)
|
||||
} else {
|
||||
temp = table.DateTime(this.Code)
|
||||
}
|
||||
|
||||
if strings.ToUpper(this.Default) == "SQL:CURRENT_TIMESTAMP" {
|
||||
def = db.L("CURRENT_TIMESTAMP")
|
||||
} 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
|
||||
case "vector":
|
||||
temp = table.Vector(this.Code, cast.ToInt(this.Length))
|
||||
isNull = false
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown type: %s", this.DataType))
|
||||
}
|
||||
|
||||
if this.IsVirtual() {
|
||||
temp.VirtualAs(this.VirtualExpr)
|
||||
}
|
||||
|
||||
if isNull {
|
||||
temp.Nullable()
|
||||
} else {
|
||||
temp.Default(def)
|
||||
}
|
||||
|
||||
temp.Comment(comment)
|
||||
|
||||
return temp
|
||||
}
|
||||
|
||||
func (this ResField) ToQueryField(t req.RouteParamType, alias string, options byte) req.QueryField {
|
||||
o := &QueryField{
|
||||
ResField: this,
|
||||
typ: t,
|
||||
alias: alias,
|
||||
}
|
||||
|
||||
return o.SetOptions(options)
|
||||
}
|
||||
// ResField 的实现搬到了 git.fsdpf.net/go/req/resx(resx 自己需要用它做字段级脱敏,
|
||||
// 不能反过来 import contracts/base,见 resx.ResField 的注释),这里保留类型别名兼容原有引用。
|
||||
type ResField = resx.ResField
|
||||
|
||||
Reference in New Issue
Block a user