|
@@ -5,16 +5,18 @@ import (
|
|
"encoding/csv"
|
|
"encoding/csv"
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "path"
|
|
|
|
+ "reflect"
|
|
|
|
+ "strconv"
|
|
|
|
+ "strings"
|
|
|
|
+ "time"
|
|
|
|
+
|
|
"git.nspix.com/golang/micro/gateway/http"
|
|
"git.nspix.com/golang/micro/gateway/http"
|
|
"git.nspix.com/golang/rest/v2/errors"
|
|
"git.nspix.com/golang/rest/v2/errors"
|
|
"git.nspix.com/golang/rest/v2/internal/inflector"
|
|
"git.nspix.com/golang/rest/v2/internal/inflector"
|
|
lru "github.com/hashicorp/golang-lru"
|
|
lru "github.com/hashicorp/golang-lru"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
"gorm.io/gorm/clause"
|
|
- "path"
|
|
|
|
- "reflect"
|
|
|
|
- "strconv"
|
|
|
|
- "strings"
|
|
|
|
)
|
|
)
|
|
|
|
|
|
const (
|
|
const (
|
|
@@ -52,6 +54,7 @@ type Entity struct {
|
|
scenarios []string
|
|
scenarios []string
|
|
hooks []Hook
|
|
hooks []Hook
|
|
lruCache *lru.Cache
|
|
lruCache *lru.Cache
|
|
|
|
+ createdAt time.Time
|
|
}
|
|
}
|
|
|
|
|
|
func (e *Entity) ID() string {
|
|
func (e *Entity) ID() string {
|
|
@@ -194,7 +197,7 @@ func (e *Entity) getScenarioUrl(scenario string) string {
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "/:id"
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "/:id"
|
|
case ScenarioExport:
|
|
case ScenarioExport:
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "-export"
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "-export"
|
|
- case "mapping":
|
|
|
|
|
|
+ case ScenarioMapping:
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "-mapping"
|
|
uri = e.opts.Prefix + "/" + e.model.ModuleName() + "/" + e.singularName + "-mapping"
|
|
}
|
|
}
|
|
return path.Clean(uri)
|
|
return path.Clean(uri)
|
|
@@ -216,7 +219,7 @@ func (e *Entity) getScenarioHandle(scenario string) http.HandleFunc {
|
|
handleFunc = e.actionDelete
|
|
handleFunc = e.actionDelete
|
|
case ScenarioExport:
|
|
case ScenarioExport:
|
|
handleFunc = e.actionExport
|
|
handleFunc = e.actionExport
|
|
- case "mapping":
|
|
|
|
|
|
+ case ScenarioMapping:
|
|
handleFunc = e.actionMapping
|
|
handleFunc = e.actionMapping
|
|
}
|
|
}
|
|
return handleFunc
|
|
return handleFunc
|
|
@@ -232,11 +235,6 @@ func (e *Entity) prepareConditions(ctx *http.Context, query *Query, schemas []*S
|
|
)
|
|
)
|
|
model = reflect.New(e.reflectType).Interface()
|
|
model = reflect.New(e.reflectType).Interface()
|
|
activeModel, _ = model.(FilterColumnInterface)
|
|
activeModel, _ = model.(FilterColumnInterface)
|
|
-
|
|
|
|
- namespace := ctx.ParamValue("@namespace")
|
|
|
|
- if namespace != "" {
|
|
|
|
- query.AndWhere(NewQueryCondition("namespace", namespace))
|
|
|
|
- }
|
|
|
|
//处理默认的搜索
|
|
//处理默认的搜索
|
|
for _, schema := range schemas {
|
|
for _, schema := range schemas {
|
|
if activeModel != nil {
|
|
if activeModel != nil {
|
|
@@ -285,7 +283,6 @@ func (e *Entity) prepareConditions(ctx *http.Context, query *Query, schemas []*S
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
//处理排序
|
|
//处理排序
|
|
sortPar := ctx.FormValue("sort")
|
|
sortPar := ctx.FormValue("sort")
|
|
if sortPar != "" {
|
|
if sortPar != "" {
|
|
@@ -360,9 +357,12 @@ func (e *Entity) actionIndex(ctx *http.Context) (err error) {
|
|
models := reflect.New(sliceValue.Type())
|
|
models := reflect.New(sliceValue.Type())
|
|
models.Elem().Set(sliceValue)
|
|
models.Elem().Set(sliceValue)
|
|
query = NewQuery(e.opts.DB)
|
|
query = NewQuery(e.opts.DB)
|
|
- searchSchemas := visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioSearch)
|
|
|
|
- indexSchemas := visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioList)
|
|
|
|
|
|
+ searchSchemas := visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioSearch)
|
|
|
|
+ indexSchemas := visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioList)
|
|
e.prepareConditions(ctx, query, searchSchemas)
|
|
e.prepareConditions(ctx, query, searchSchemas)
|
|
|
|
+ if e.opts.EnableNamespace {
|
|
|
|
+ query.AndFilterWhere(NewQueryCondition("namespace", namespace))
|
|
|
|
+ }
|
|
query.Offset(pageIndex * pageSize).Limit(pageSize)
|
|
query.Offset(pageIndex * pageSize).Limit(pageSize)
|
|
if err = query.All(models.Interface()); err != nil {
|
|
if err = query.All(models.Interface()); err != nil {
|
|
return ctx.Error(HttpDatabaseQueryFailed, err.Error())
|
|
return ctx.Error(HttpDatabaseQueryFailed, err.Error())
|
|
@@ -392,16 +392,22 @@ func (e *Entity) actionView(ctx *http.Context) (err error) {
|
|
scenario := ctx.FormValue("scenario")
|
|
scenario := ctx.FormValue("scenario")
|
|
idStr := ctx.ParamValue("id")
|
|
idStr := ctx.ParamValue("id")
|
|
model = reflect.New(e.reflectType).Interface()
|
|
model = reflect.New(e.reflectType).Interface()
|
|
- if err = e.opts.DB.Where(e.primaryKey+"=? AND namespace=?", idStr, namespace).First(model).Error; err != nil {
|
|
|
|
|
|
+ conditions := map[string]interface{}{
|
|
|
|
+ e.primaryKey: idStr,
|
|
|
|
+ }
|
|
|
|
+ if e.opts.EnableNamespace {
|
|
|
|
+ conditions["namespace"] = namespace
|
|
|
|
+ }
|
|
|
|
+ if err = e.opts.DB.Where(conditions).First(model).Error; err != nil {
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
}
|
|
}
|
|
if ctx.FormValue("format") != "" {
|
|
if ctx.FormValue("format") != "" {
|
|
//获取指定场景下面的字段进行渲染显示
|
|
//获取指定场景下面的字段进行渲染显示
|
|
var schemas []*Schema
|
|
var schemas []*Schema
|
|
if scenario == "" {
|
|
if scenario == "" {
|
|
- schemas = visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioView)
|
|
|
|
|
|
+ schemas = visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioView)
|
|
} else {
|
|
} else {
|
|
- schemas = visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), scenario)
|
|
|
|
|
|
+ schemas = visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), scenario)
|
|
}
|
|
}
|
|
requestCtx := ctx.Request().Context()
|
|
requestCtx := ctx.Request().Context()
|
|
if requestCtx == nil {
|
|
if requestCtx == nil {
|
|
@@ -426,9 +432,12 @@ func (e *Entity) actionExport(ctx *http.Context) (err error) {
|
|
models := reflect.New(sliceValue.Type())
|
|
models := reflect.New(sliceValue.Type())
|
|
models.Elem().Set(sliceValue)
|
|
models.Elem().Set(sliceValue)
|
|
query = NewQuery(e.opts.DB)
|
|
query = NewQuery(e.opts.DB)
|
|
- searchSchemas := visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioSearch)
|
|
|
|
- exportSchemas := visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioList)
|
|
|
|
|
|
+ searchSchemas := visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioSearch)
|
|
|
|
+ exportSchemas := visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioList)
|
|
e.prepareConditions(ctx, query, searchSchemas)
|
|
e.prepareConditions(ctx, query, searchSchemas)
|
|
|
|
+ if e.opts.EnableNamespace {
|
|
|
|
+ query.AndFilterWhere(NewQueryCondition("namespace", namespace))
|
|
|
|
+ }
|
|
if err = query.All(models.Interface()); err != nil {
|
|
if err = query.All(models.Interface()); err != nil {
|
|
return ctx.Error(HttpDatabaseExportFailed, err.Error())
|
|
return ctx.Error(HttpDatabaseExportFailed, err.Error())
|
|
}
|
|
}
|
|
@@ -485,10 +494,10 @@ func (e *Entity) actionCreate(ctx *http.Context) (err error) {
|
|
if err = ctx.Bind(model); err != nil {
|
|
if err = ctx.Bind(model); err != nil {
|
|
return ctx.Error(HttpInvalidPayload, err.Error())
|
|
return ctx.Error(HttpInvalidPayload, err.Error())
|
|
}
|
|
}
|
|
- schemas = visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioCreate)
|
|
|
|
|
|
+ schemas = visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioCreate)
|
|
//设置某个字段的值
|
|
//设置某个字段的值
|
|
e.setFieldValue(refModel, "namespace", namespace)
|
|
e.setFieldValue(refModel, "namespace", namespace)
|
|
- //设置默认的创建用户和更新用户信息
|
|
|
|
|
|
+ //global set field value
|
|
e.setFieldValue(refModel, "CreatedBy", ctx.ParamValue("@uid"))
|
|
e.setFieldValue(refModel, "CreatedBy", ctx.ParamValue("@uid"))
|
|
e.setFieldValue(refModel, "CreatedDept", ctx.ParamValue("@department"))
|
|
e.setFieldValue(refModel, "CreatedDept", ctx.ParamValue("@department"))
|
|
e.setFieldValue(refModel, "UpdatedBy", ctx.ParamValue("@uid"))
|
|
e.setFieldValue(refModel, "UpdatedBy", ctx.ParamValue("@uid"))
|
|
@@ -538,18 +547,16 @@ func (e *Entity) actionCreate(ctx *http.Context) (err error) {
|
|
"table": e.model.TableName(),
|
|
"table": e.model.TableName(),
|
|
"state": "created",
|
|
"state": "created",
|
|
})
|
|
})
|
|
- } else {
|
|
|
|
- //数据校验不合法
|
|
|
|
- if validateError, ok := err.(*errors.StructError); ok {
|
|
|
|
- ctx.Response().Header().Set("Content-Type", "application/json")
|
|
|
|
- return json.NewEncoder(ctx.Response()).Encode(map[string]interface{}{
|
|
|
|
- "errno": HttpValidateFailed,
|
|
|
|
- "result": validateError,
|
|
|
|
- })
|
|
|
|
- } else {
|
|
|
|
- return ctx.Error(HttpDatabaseCreateFailed, err.Error())
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
+ //form validation
|
|
|
|
+ if validateError, ok := err.(*errors.StructError); ok {
|
|
|
|
+ ctx.Response().Header().Set("Content-Type", "application/json")
|
|
|
|
+ return json.NewEncoder(ctx.Response()).Encode(map[string]interface{}{
|
|
|
|
+ "errno": HttpValidateFailed,
|
|
|
|
+ "result": validateError,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ return ctx.Error(HttpDatabaseCreateFailed, err.Error())
|
|
}
|
|
}
|
|
|
|
|
|
func (e *Entity) actionUpdate(ctx *http.Context) (err error) {
|
|
func (e *Entity) actionUpdate(ctx *http.Context) (err error) {
|
|
@@ -573,10 +580,16 @@ func (e *Entity) actionUpdate(ctx *http.Context) (err error) {
|
|
//默认设置更新用户
|
|
//默认设置更新用户
|
|
e.setFieldValue(refModel, "UpdatedBy", ctx.ParamValue("@uid"))
|
|
e.setFieldValue(refModel, "UpdatedBy", ctx.ParamValue("@uid"))
|
|
e.setFieldValue(refModel, "UpdatedDept", ctx.ParamValue("@department"))
|
|
e.setFieldValue(refModel, "UpdatedDept", ctx.ParamValue("@department"))
|
|
- if err = e.opts.DB.Where(e.primaryKey+"=? AND namespace=?", idStr, namespace).First(model).Error; err != nil {
|
|
|
|
|
|
+ conditions := map[string]interface{}{
|
|
|
|
+ e.primaryKey: idStr,
|
|
|
|
+ }
|
|
|
|
+ if e.opts.EnableNamespace {
|
|
|
|
+ conditions["namespace"] = namespace
|
|
|
|
+ }
|
|
|
|
+ if err = e.opts.DB.Where(conditions).First(model).Error; err != nil {
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
}
|
|
}
|
|
- schemas = visibleSchemas(e.opts.DB, namespace, e.model.ModuleName(), e.model.TableName(), ScenarioUpdate)
|
|
|
|
|
|
+ schemas = visibleSchemas(namespace, e.model.ModuleName(), e.model.TableName(), ScenarioUpdate)
|
|
for _, scm := range schemas {
|
|
for _, scm := range schemas {
|
|
oldValues[scm.Column] = e.getFieldValue(refModel, scm.Column)
|
|
oldValues[scm.Column] = e.getFieldValue(refModel, scm.Column)
|
|
}
|
|
}
|
|
@@ -621,7 +634,7 @@ func (e *Entity) actionUpdate(ctx *http.Context) (err error) {
|
|
}
|
|
}
|
|
return errTx
|
|
return errTx
|
|
}); err == nil {
|
|
}); err == nil {
|
|
- e.invalidCache(ctx.ParamValue("@namespace"))
|
|
|
|
|
|
+ e.invalidCache(namespace)
|
|
pkVal := e.getPrimaryKeyValue(model)
|
|
pkVal := e.getPrimaryKeyValue(model)
|
|
if len(e.hooks) > 0 {
|
|
if len(e.hooks) > 0 {
|
|
for _, hook := range e.hooks {
|
|
for _, hook := range e.hooks {
|
|
@@ -633,17 +646,16 @@ func (e *Entity) actionUpdate(ctx *http.Context) (err error) {
|
|
"table": e.model.TableName(),
|
|
"table": e.model.TableName(),
|
|
"state": "updated",
|
|
"state": "updated",
|
|
})
|
|
})
|
|
- } else {
|
|
|
|
- if validateError, ok := err.(*errors.StructError); ok {
|
|
|
|
- ctx.Response().Header().Set("Content-Type", "application/json")
|
|
|
|
- return json.NewEncoder(ctx.Response()).Encode(map[string]interface{}{
|
|
|
|
- "errno": HttpValidateFailed,
|
|
|
|
- "result": validateError,
|
|
|
|
- })
|
|
|
|
- } else {
|
|
|
|
- return ctx.Error(HttpDatabaseUpdateFailed, err.Error())
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
+ //form validation
|
|
|
|
+ if validateError, ok := err.(*errors.StructError); ok {
|
|
|
|
+ ctx.Response().Header().Set("Content-Type", "application/json")
|
|
|
|
+ return json.NewEncoder(ctx.Response()).Encode(map[string]interface{}{
|
|
|
|
+ "errno": HttpValidateFailed,
|
|
|
|
+ "result": validateError,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ return ctx.Error(HttpDatabaseUpdateFailed, err.Error())
|
|
}
|
|
}
|
|
|
|
|
|
func (e *Entity) actionDelete(ctx *http.Context) (err error) {
|
|
func (e *Entity) actionDelete(ctx *http.Context) (err error) {
|
|
@@ -658,7 +670,13 @@ func (e *Entity) actionDelete(ctx *http.Context) (err error) {
|
|
idStr := ctx.ParamValue("id")
|
|
idStr := ctx.ParamValue("id")
|
|
namespace = ctx.ParamValue("@namespace")
|
|
namespace = ctx.ParamValue("@namespace")
|
|
model = reflect.New(e.reflectType).Interface()
|
|
model = reflect.New(e.reflectType).Interface()
|
|
- if err = e.opts.DB.Where(e.primaryKey+"=? AND namespace=?", idStr, namespace).First(model).Error; err != nil {
|
|
|
|
|
|
+ conditions := map[string]interface{}{
|
|
|
|
+ e.primaryKey: idStr,
|
|
|
|
+ }
|
|
|
|
+ if e.opts.EnableNamespace {
|
|
|
|
+ conditions["namespace"] = namespace
|
|
|
|
+ }
|
|
|
|
+ if err = e.opts.DB.Where(conditions).First(model).Error; err != nil {
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
return ctx.Error(HttpDatabaseFindFailed, err.Error())
|
|
}
|
|
}
|
|
if err = e.opts.DB.Transaction(func(tx *gorm.DB) error {
|
|
if err = e.opts.DB.Transaction(func(tx *gorm.DB) error {
|
|
@@ -676,7 +694,7 @@ func (e *Entity) actionDelete(ctx *http.Context) (err error) {
|
|
}
|
|
}
|
|
return errTx
|
|
return errTx
|
|
}); err == nil {
|
|
}); err == nil {
|
|
- e.invalidCache(ctx.ParamValue("@namespace"))
|
|
|
|
|
|
+ e.invalidCache(namespace)
|
|
return ctx.Success(map[string]interface{}{
|
|
return ctx.Success(map[string]interface{}{
|
|
"id": e.getPrimaryKeyValue(model),
|
|
"id": e.getPrimaryKeyValue(model),
|
|
"table": e.model.TableName(),
|
|
"table": e.model.TableName(),
|
|
@@ -696,6 +714,7 @@ func newEntity(model Model, opts *Options) *Entity {
|
|
entity := &Entity{
|
|
entity := &Entity{
|
|
model: model,
|
|
model: model,
|
|
opts: opts,
|
|
opts: opts,
|
|
|
|
+ createdAt: time.Now(),
|
|
reflectValue: reflect.Indirect(reflect.ValueOf(model)),
|
|
reflectValue: reflect.Indirect(reflect.ValueOf(model)),
|
|
}
|
|
}
|
|
entity.lruCache, _ = lru.New(50)
|
|
entity.lruCache, _ = lru.New(50)
|