Compare commits

..

10 Commits

Author SHA1 Message Date
Yavolte 3b130c9d14 fix tier 2025-06-26 18:30:17 +08:00
Yavolte 8409716217 add types 2025-06-26 14:07:30 +08:00
Yavolte 368a9e868c fix tier loop 2025-06-25 11:15:08 +08:00
Yavolte 206932038e add tier value support 2025-06-25 11:10:20 +08:00
Yavolte 5c09f1eefc update mulit value 2025-06-23 22:50:31 +08:00
Yavolte 0c1c0da166 add generic type 2025-06-18 14:47:19 +08:00
Yavolte 01a671dff8 添加valueLookup支持配置 2025-06-17 18:27:09 +08:00
Yavolte 2631943904 add permission model 2025-06-17 17:17:02 +08:00
Yavolte e85cc0b8ca fix singularize table names 2025-06-12 10:48:10 +08:00
Yavolte 3020468afb fix bugs 2025-06-12 10:37:21 +08:00
7 changed files with 170 additions and 34 deletions

View File

@ -84,9 +84,14 @@ func BuildConditions(ctx context.Context, r *http.Request, query *Query, schemas
} }
//如果是多选的话直接使用IN操作 //如果是多选的话直接使用IN操作
columnName := row.Column + "[]" columnName := row.Column + "[]"
if qs.Has(columnName) && len(qs[columnName]) > 1 { if qs.Has(columnName) {
query.AndFilterWhere(newConditionWithOperator("IN", row.Column, qs[columnName])) if len(qs[columnName]) > 1 {
continue query.AndFilterWhere(newConditionWithOperator("IN", row.Column, qs[columnName]))
continue
} else if len(qs[columnName]) == 1 {
query.AndFilterWhere(newCondition(row.Column, qs[columnName][0]))
continue
}
} }
formValue = qs.Get(row.Column) formValue = qs.Get(row.Column)
switch row.Format { switch row.Format {

View File

@ -28,6 +28,7 @@ import (
type Model struct { type Model struct {
naming types.Naming //命名规则 naming types.Naming //命名规则
value reflect.Value //模块值 value reflect.Value //模块值
modelValue any //模块实例
db *gorm.DB //数据库 db *gorm.DB //数据库
primaryKey string //主键 primaryKey string //主键
urlPrefix string //url前缀 urlPrefix string //url前缀
@ -232,13 +233,17 @@ func (m *Model) ModuleName() string {
// TableName 表的名称 // TableName 表的名称
func (m *Model) TableName() string { func (m *Model) TableName() string {
return m.naming.ModuleName return m.naming.TableName
} }
func (m *Model) Naming() types.Naming { func (m *Model) Naming() types.Naming {
return m.naming return m.naming
} }
func (m *Model) Value() reflect.Value {
return m.value
}
func (m *Model) SetDB(db *gorm.DB) *gorm.DB { func (m *Model) SetDB(db *gorm.DB) *gorm.DB {
m.db = db m.db = db
return m.db return m.db
@ -261,12 +266,15 @@ func (m *Model) Fields() []*schema.Field {
// 权限示例: "module.model.scenario" // 权限示例: "module.model.scenario"
// 比如: organize:user:list, organize:user:create // 比如: organize:user:list, organize:user:create
func (m *Model) Permission(scenario string) string { func (m *Model) Permission(scenario string) string {
if pm, ok := m.modelValue.(PermissionModel); ok {
return pm.GetPermission(scenario)
}
ss := make([]string, 0, 4) ss := make([]string, 0, 4)
switch scenario { switch scenario {
case types.ScenarioList: case types.ScenarioList:
ss = append(ss, m.naming.ModuleName, m.naming.Singular, "list") ss = append(ss, m.naming.ModuleName, m.naming.Singular, "list")
case types.ScenarioView: case types.ScenarioView:
ss = append(ss, m.naming.ModuleName, m.naming.Singular, "get") ss = append(ss, m.naming.ModuleName, m.naming.Singular, "detail")
case types.ScenarioCreate: case types.ScenarioCreate:
ss = append(ss, m.naming.ModuleName, m.naming.Singular, "create") ss = append(ss, m.naming.ModuleName, m.naming.Singular, "create")
case types.ScenarioUpdate: case types.ScenarioUpdate:
@ -1081,6 +1089,7 @@ func newModel(v any, db *gorm.DB, naming types.Naming) *Model {
value: reflect.Indirect(reflect.ValueOf(v)), value: reflect.Indirect(reflect.ValueOf(v)),
valueLookup: defaultValueLookup, valueLookup: defaultValueLookup,
} }
model.modelValue = reflect.New(model.value.Type()).Interface()
model.statement = &gorm.Statement{ model.statement = &gorm.Statement{
DB: model.getDB(), DB: model.getDB(),
ConnPool: model.getDB().ConnPool, ConnPool: model.getDB().ConnPool,

View File

@ -13,6 +13,7 @@ type options struct {
router types.HttpRouter router types.HttpRouter
writer types.HttpWriter writer types.HttpWriter
permissionChecker types.PermissionChecker permissionChecker types.PermissionChecker
valueLookup types.ValueLookupFunc
formatter *Formatter formatter *Formatter
resourceDirectory string resourceDirectory string
} }
@ -30,6 +31,7 @@ func (o *options) Clone() *options {
permissionChecker: o.permissionChecker, permissionChecker: o.permissionChecker,
formatter: o.formatter, formatter: o.formatter,
resourceDirectory: o.resourceDirectory, resourceDirectory: o.resourceDirectory,
valueLookup: o.valueLookup,
} }
} }
@ -47,6 +49,12 @@ func WithUriPrefix(s string) Option {
} }
} }
func WithValueLookup(f types.ValueLookupFunc) Option {
return func(o *options) {
o.valueLookup = f
}
}
// WithModuleName 模块名称 // WithModuleName 模块名称
func WithModuleName(s string) Option { func WithModuleName(s string) Option {
return func(o *options) { return func(o *options) {

113
rest.go
View File

@ -506,15 +506,19 @@ func AutoMigrate(ctx context.Context, model any, cbs ...Option) (modelValue *Mod
return return
} }
//路由模块处理 //路由模块处理
singularizeTable := inflector.Singularize(table)
modelValue = newModel(model, opts.db, types.Naming{ modelValue = newModel(model, opts.db, types.Naming{
Pluralize: inflector.Pluralize(table), Pluralize: inflector.Pluralize(singularizeTable),
Singular: inflector.Singularize(table), Singular: singularizeTable,
ModuleName: opts.moduleName, ModuleName: opts.moduleName,
TableName: table, TableName: table,
}) })
if scenarioModel, ok := model.(types.ScenarioModel); ok { if scenarioModel, ok := model.(types.ScenarioModel); ok {
modelValue.scenarios = scenarioModel.Scenario() modelValue.scenarios = scenarioModel.Scenario()
} }
if opts.valueLookup != nil {
modelValue.valueLookup = opts.valueLookup
}
modelValue.hookMgr = hookMgr modelValue.hookMgr = hookMgr
modelValue.schemaLookup = VisibleSchemas modelValue.schemaLookup = VisibleSchemas
modelValue.permissionChecker = opts.permissionChecker modelValue.permissionChecker = opts.permissionChecker
@ -648,63 +652,128 @@ func VisibleSchemas(ctx context.Context, db *gorm.DB, domain, moduleName, tableN
} }
// ModelTypes 查询指定模型的类型 // ModelTypes 查询指定模型的类型
func ModelTypes(ctx context.Context, db *gorm.DB, model any, domainName, labelColumn, valueColumn string) (values []*types.TypeValue) { func ModelTypes[T any](ctx context.Context, db *gorm.DB, model any, domainName, labelColumn, valueColumn string) (values []*types.TypeValue[T], err error) {
tx := db.WithContext(ctx) tx := db.WithContext(ctx)
result := make([]map[string]any, 0, 10) result := make([]map[string]any, 0, 10)
if domainName == "" { if domainName == "" {
tx.Model(model).Select(labelColumn, valueColumn).Scan(&result) err = tx.Model(model).Select(labelColumn, valueColumn).Scan(&result).Error
} else { } else {
tx.Model(model).Select(labelColumn, valueColumn).Where("domain=?", domainName).Scan(&result) err = tx.Model(model).Select(labelColumn, valueColumn).Where("domain=?", domainName).Scan(&result).Error
} }
values = make([]*types.TypeValue, 0, len(result)) if err != nil {
return
}
values = make([]*types.TypeValue[T], 0, len(result))
for _, pairs := range result { for _, pairs := range result {
feed := &types.TypeValue{} feed := &types.TypeValue[T]{}
for k, v := range pairs { for k, v := range pairs {
if k == labelColumn { if k == labelColumn {
feed.Label = v if s, ok := v.(string); ok {
feed.Label = s
} else {
feed.Label = fmt.Sprint(s)
}
continue
} }
if k == valueColumn { if k == valueColumn {
feed.Value = v if p, ok := v.(T); ok {
feed.Value = p
}
continue
} }
} }
values = append(values, feed) values = append(values, feed)
} }
return values return values, nil
}
// ModelTiers 查询指定模型的层级数据
func ModelTiers[T comparable](ctx context.Context, db *gorm.DB, model any, domainName, parentColumn, labelColumn, valueColumn string) (values []*types.TierValue[T], err error) {
tx := db.WithContext(ctx)
result := make([]map[string]any, 0, 10)
if domainName == "" {
err = tx.Model(model).Select(parentColumn, labelColumn, valueColumn).Scan(&result).Error
} else {
err = tx.Model(model).Select(parentColumn, labelColumn, valueColumn).Where("domain=?", domainName).Scan(&result).Error
}
if err != nil {
return
}
values = make([]*types.TierValue[T], 0, len(result))
for _, pairs := range result {
feed := &types.TierValue[T]{}
for k, v := range pairs {
if k == labelColumn {
if s, ok := v.(string); ok {
feed.Label = s
} else {
feed.Label = fmt.Sprint(s)
}
continue
}
if k == valueColumn {
if p, ok := v.(T); ok {
feed.Value = p
}
continue
}
if k == parentColumn {
if p, ok := v.(T); ok {
feed.Parent = p
}
continue
}
}
values = append(values, feed)
}
var none T
return recursiveTier(none, values), nil
} }
// GetFieldValue 获取模型某个字段的值 // GetFieldValue 获取模型某个字段的值
func GetFieldValue(stmt *gorm.Statement, refValue reflect.Value, column string) interface{} { func GetFieldValue(stmt *gorm.Statement, refValue reflect.Value, column string) any {
var ( var (
idx = -1 rawField *schema.Field
) )
refVal := reflect.Indirect(refValue) refVal := reflect.Indirect(refValue)
for i, field := range stmt.Schema.Fields { for _, field := range stmt.Schema.Fields {
if field.DBName == column || field.Name == column { if field.DBName == column || field.Name == column {
idx = i rawField = field
break break
} }
} }
if idx > -1 { if rawField == nil {
return refVal.Field(idx).Interface() return nil
} }
return nil var targetValue reflect.Value
targetValue = refVal
for _, i := range rawField.StructField.Index {
targetValue = targetValue.Field(i)
}
return targetValue.Interface()
} }
// SetFieldValue 设置模型某个字段的值 // SetFieldValue 设置模型某个字段的值
func SetFieldValue(stmt *gorm.Statement, refValue reflect.Value, column string, value any) { func SetFieldValue(stmt *gorm.Statement, refValue reflect.Value, column string, value any) {
var ( var (
idx = -1 rawField *schema.Field
) )
refVal := reflect.Indirect(refValue) refVal := reflect.Indirect(refValue)
for i, field := range stmt.Schema.Fields { for _, field := range stmt.Schema.Fields {
if field.DBName == column || field.Name == column { if field.DBName == column || field.Name == column {
idx = i rawField = field
break break
} }
} }
if idx > -1 { if rawField == nil {
refVal.Field(idx).Set(reflect.ValueOf(value)) return
} }
var targetValue reflect.Value
targetValue = refVal
for _, i := range rawField.StructField.Index {
targetValue = targetValue.Field(i)
}
targetValue.Set(reflect.ValueOf(value))
} }
// SafeSetFileValue 安全设置模型某个字段的值 // SafeSetFileValue 安全设置模型某个字段的值

View File

@ -44,6 +44,11 @@ type (
HttpTableName(req *http.Request) string HttpTableName(req *http.Request) string
} }
// 权限模型定义
PermissionModel interface {
GetPermission(scenario string) string
}
//创建后的回调,这个回调不在事物内 //创建后的回调,这个回调不在事物内
afterCreated interface { afterCreated interface {
AfterCreated(ctx context.Context, tx *gorm.DB) AfterCreated(ctx context.Context, tx *gorm.DB)

View File

@ -119,16 +119,25 @@ type (
} }
// TypeValue 键值对数据 // TypeValue 键值对数据
TypeValue struct { TypeValue[T any] struct {
Label any `json:"label"` Label string `json:"label"`
Value any `json:"value"` Value T `json:"value"`
}
//TierValue 层级数据
TierValue[T comparable] struct {
Label string `json:"label"`
Value T `json:"value"`
Parent T `json:"-"`
Used bool `json:"-"`
Children []*TierValue[T] `json:"children"`
} }
// NestedValue 层级数据 // NestedValue 层级数据
NestedValue struct { NestedValue[T any] struct {
Label any `json:"label"` Label string `json:"label"`
Value any `json:"value"` Value T `json:"value"`
Children []*NestedValue `json:"children,omitempty"` Children []*NestedValue[T] `json:"children,omitempty"`
} }
//ValueLookupFunc 查找域的函数 //ValueLookupFunc 查找域的函数
@ -240,3 +249,17 @@ type (
Status string `json:"status"` Status string `json:"status"`
} }
) )
func (t *TierValue[T]) HasChild(value T) bool {
for _, child := range t.Children {
if child.Value == value {
return true
}
if len(child.Children) > 0 {
if child.HasChild(value) {
return true
}
}
}
return false
}

View File

@ -4,6 +4,8 @@ import (
"reflect" "reflect"
"slices" "slices"
"strings" "strings"
"git.nobla.cn/golang/rest/types"
) )
func hasToken(hack string, need string) bool { func hasToken(hack string, need string) bool {
@ -43,3 +45,18 @@ func isEmpty(val any) bool {
return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface()) return reflect.DeepEqual(val, reflect.Zero(v.Type()).Interface())
} }
} }
func recursiveTier[T comparable](parent T, values []*types.TierValue[T]) []*types.TierValue[T] {
items := make([]*types.TierValue[T], 0, len(values)/2)
for idx, row := range values {
if row.Used {
continue
}
if row.Parent == parent {
values[idx].Used = true
row.Children = recursiveTier(row.Value, values)
items = append(items, row)
}
}
return items
}