123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- package rest
- import (
- "context"
- "fmt"
- "git.nspix.com/golang/micro/gateway/http"
- "gorm.io/gorm"
- "gorm.io/gorm/clause"
- "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 {
- 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{},
- }
- }
- 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
- }
- func (r *CRUD) handleListSchema(httpCtx *http.Context) (err error) {
- var (
- schemas []*Schema
- )
- ns := httpCtx.FormValue(NamespaceVariable)
- if ns == "" {
- ns = DefaultNamespace
- }
- if schemas, err = r.GetSchemas(httpCtx.Request().Context(), ns, httpCtx.ParamValue("module"), httpCtx.ParamValue("table")); err == nil {
- return httpCtx.Success(schemas)
- } else {
- return httpCtx.Error(HttpDatabaseQueryFailed, err.Error())
- }
- }
- func (r *CRUD) handleSaveSchema(httpCtx *http.Context) (err error) {
- var (
- rest *Restful
- )
- ns := httpCtx.FormValue(NamespaceVariable)
- if ns == "" {
- ns = DefaultNamespace
- }
- moduleName := httpCtx.ParamValue("module")
- tableName := httpCtx.ParamValue("table")
- for _, row := range r.modules {
- if row.model.ModuleName() == moduleName && row.model.TableName() == tableName {
- rest = row
- break
- }
- }
- if rest == nil {
- return httpCtx.Error(HTTPUnknownFailed, fmt.Sprintf("module %s table %s schema not found", moduleName, tableName))
- }
- schemas := make([]*Schema, 0)
- if err = httpCtx.Bind(&schemas); err != nil {
- return httpCtx.Error(HttpInvalidPayload, err.Error())
- }
- if err = r.db.Transaction(func(tx *gorm.DB) error {
- var (
- errTx error
- )
- for _, scm := range schemas {
- if errTx = tx.Save(scm).Error; errTx != nil {
- return errTx
- }
- }
- return nil
- }); err == nil {
- return httpCtx.Success(map[string]interface{}{
- "count": len(schemas),
- "state": "success",
- })
- } else {
- return httpCtx.Error(HTTPUnknownFailed, err.Error())
- }
- }
- func (r *CRUD) handleListSchemas(httpCtx *http.Context) (err error) {
- var (
- moduleLabel string
- )
- ts := make([]*treeValue, 0)
- isHandled := false
- for _, e := range r.modules {
- isHandled = false
- for _, tv := range ts {
- if tv.Value == e.model.ModuleName() {
- tv.Append(&treeValue{Value: e.model.TableName(), Label: e.model.TableName(), Type: TypeTable})
- isHandled = true
- break
- }
- }
- if isHandled {
- continue
- }
- moduleLabel = e.model.ModuleName()
- tv := &treeValue{Label: moduleLabel, Value: e.model.ModuleName(), Type: TypeModule}
- tv.Append(&treeValue{Value: e.model.TableName(), Label: e.model.TableName(), Type: TypeTable})
- ts = append(ts, tv)
- }
- return httpCtx.Success(ts)
- }
- func (r *CRUD) handleDeleteSchema(httpCtx *http.Context) (err error) {
- id := httpCtx.ParamValue("id")
- model := &Schema{}
- if err = r.db.Where("id=?", id).First(model).Error; err == nil {
- if err = r.db.Where("id=?", id).Delete(&Schema{}).Error; err == nil {
- return httpCtx.Success(map[string]string{
- "id": id,
- "state": "success",
- })
- } else {
- return httpCtx.Error(HttpDatabaseDeleteFailed, err.Error())
- }
- } else {
- return httpCtx.Error(HttpDatabaseFindFailed, err.Error())
- }
- }
- func (r *CRUD) Router(svr *http.Server, ms ...http.Middleware) {
- svr.Handle("GET", "/rest/schemas", r.handleListSchemas, ms...)
- svr.Handle("GET", "/rest/schema/:module/:table", r.handleListSchema, ms...)
- svr.Handle("PUT", "/rest/schema/:module/:table", r.handleSaveSchema, ms...)
- svr.Handle("DELETE", "/rest/schema/:id", r.handleDeleteSchema, ms...)
- for _, rest := range r.modules {
- if rest.hasScenario(ScenarioList) {
- svr.Handle("GET", rest.getScenarioUrl(ScenarioList), rest.getScenarioHandle(ScenarioList), ms...)
- }
- if rest.hasScenario(ScenarioCreate) {
- svr.Handle("POST", rest.getScenarioUrl(ScenarioCreate), rest.getScenarioHandle(ScenarioCreate), ms...)
- }
- if rest.hasScenario(ScenarioUpdate) {
- svr.Handle("PUT", rest.getScenarioUrl(ScenarioUpdate), rest.getScenarioHandle(ScenarioUpdate), ms...)
- }
- if rest.hasScenario(ScenarioDelete) {
- svr.Handle("DELETE", rest.getScenarioUrl(ScenarioDelete), rest.getScenarioHandle(ScenarioDelete), ms...)
- }
- if rest.hasScenario(ScenarioExport) {
- svr.Handle("GET", rest.getScenarioUrl(ScenarioExport), rest.getScenarioHandle(ScenarioExport), ms...)
- }
- if rest.hasScenario(ScenarioView) {
- svr.Handle("GET", rest.getScenarioUrl(ScenarioView), rest.getScenarioHandle(ScenarioView), ms...)
- }
- }
- }
- //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.db = r.db.Debug()
- err = r.db.AutoMigrate(&Schema{})
- return
- }
|