package rest import ( "context" loggerpkg "git.nspix.com/golang/rest/v3/logger" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/logger" "strings" ) const ( DefaultNamespace = "default" NamespaceVariable = "namespace" UserVariable = "@uid" DepartmentVariable = "@department" NamespaceField = "namespace" CreatedByField = "CreatedBy" CreatedDeptField = "CreatedDept" UpdatedByField = "UpdatedBy" UpdatedDeptField = "UpdatedDept" ) const ( TypeModule = "module" TypeTable = "table" ) type ( CRUD struct { api *Api db *gorm.DB modules []*Restful delegate *delegate } treeValue struct { Label string `json:"label"` Value string `json:"value"` Type string `json:"type"` Children []*treeValue `json:"children,omitempty"` } ) func (t *treeValue) Append(v *treeValue) { if t.Children == nil { t.Children = make([]*treeValue, 0) } t.Children = append(t.Children, v) } //createStatement create new statement func (r *CRUD) createStatement(db *gorm.DB) *gorm.Statement { return &gorm.Statement{ DB: db, ConnPool: db.Statement.ConnPool, Context: db.Statement.Context, Clauses: map[string]clause.Clause{}, } } //WithDebug 开启调试 func (r *CRUD) WithDebug() *CRUD { r.db.Logger = r.db.Logger.LogMode(logger.Info) return r } //RegisterDelegate 注册一个旁观者 func (r *CRUD) RegisterDelegate(d Delegate) { r.delegate.Register(d) } //Attach 注册一个表 func (r *CRUD) Attach(ctx context.Context, model Model, cbs ...Option) (err error) { var ( opts *Options ) opts = newOptions() for _, cb := range cbs { cb(opts) } if opts.DB == nil { opts.DB = r.db } if err = r.db.AutoMigrate(model); err != nil { return } if err = r.migrateSchema(ctx, DefaultNamespace, model); err != nil { return } opts.Delegate = r.delegate opts.LookupFunc = r.VisibleSchemas r.modules = append(r.modules, newRestful(model, opts)) return } //migrateSchema 合并表的结构数据 func (r *CRUD) migrateSchema(ctx context.Context, namespace string, model Model) (err error) { var ( pos int columnLabel string columnName string columnIsExists bool stmt *gorm.Statement schemas []*Schema schemaModels []*Schema ) schemas, err = r.GetSchemas(ctx, namespace, model.ModuleName(), model.TableName()) stmt = r.createStatement(r.db) if err = stmt.Parse(model); err != nil { return } if len(schemas) > 0 { pos = len(schemas) } for index, field := range stmt.Schema.Fields { columnName = field.DBName if columnName == "-" { continue } if columnName == "" { columnName = field.Name } columnIsExists = false for _, sm := range schemas { if sm.Column == columnName { columnIsExists = true break } } if columnIsExists { continue } columnLabel = field.Tag.Get("comment") if columnLabel == "" { columnLabel = generateFieldName(field.DBName) } isPrimaryKey := uint8(0) if field.PrimaryKey { isPrimaryKey = 1 } schemaModel := &Schema{ Namespace: namespace, ModuleName: model.ModuleName(), TableName: model.TableName(), Enable: 1, Column: columnName, Label: columnLabel, Type: strings.ToLower(dataTypeOf(field)), Format: strings.ToLower(dataFormatOf(field)), Native: 1, IsPrimaryKey: isPrimaryKey, Scenarios: generateFieldScenario(index, field), Rule: generateFieldRule(field), Attribute: generateFieldAttribute(field), Position: pos, } schemaModels = append(schemaModels, schemaModel) pos++ } if len(schemaModels) > 0 { err = r.db.Create(schemaModels).Error } return } //GetSchemas 获取表的结构数据 func (r *CRUD) GetSchemas(ctx context.Context, namespace, moduleName, tableName string) (schemas []*Schema, err error) { schemas = make([]*Schema, 0) if len(namespace) == 0 { namespace = DefaultNamespace } sess := r.db.Session(&gorm.Session{NewDB: true, Context: ctx}) err = sess.Where("`namespace`=? AND `module_name`=? AND `table_name`=?", namespace, moduleName, tableName).Order("position,id ASC").Find(&schemas).Error return } //VisibleSchemas 获取指定场景表的数据 func (r *CRUD) VisibleSchemas(ctx context.Context, ns, moduleName, tableName, scene string) (result []*Schema) { var ( err error schemas []*Schema ) if schemas, err = r.GetSchemas(ctx, ns, moduleName, tableName); err == nil { result = make([]*Schema, 0, len(schemas)) for _, s := range schemas { if s.Scenarios.Has(scene) || s.Attribute.PrimaryKey { result = append(result, s) } } } return } //New create new restful func New(dialer gorm.Dialector) (r *CRUD, err error) { r = &CRUD{ delegate: &delegate{}, } if r.db, err = gorm.Open(dialer); err != nil { return } r.api = &Api{crud: r} r.db.Logger = loggerpkg.New() err = r.db.AutoMigrate(&Schema{}) return }