package rest import ( "fmt" "git.nspix.com/golang/micro/gateway/http" "git.nspix.com/golang/rest/v3/cache" "gorm.io/gorm" "time" ) type ( Api struct { crud *CRUD svr *http.Server exists []string middleware []http.Middleware } feedRequest struct { ModuleName string `json:"module_name"` TableName string `json:"table_name"` ValueField string `json:"value_field"` LabelField string `json:"label_field"` Condition string `json:"condition,omitempty"` Limit int `json:"limit,omitempty"` } feedResponse struct { Label interface{} `json:"label" yaml:"label"` Value interface{} `json:"value" yaml:"value"` } ) func (f *feedRequest) cacheID() string { return "feed:" + f.ModuleName + f.TableName + f.ValueField + f.LabelField } func (api *Api) handleListSchema(httpCtx *http.Context) (err error) { var ( schemas []*Schema ) ns := httpCtx.FormValue(NamespaceVariable) if ns == "" { ns = DefaultNamespace } if schemas, err = api.crud.GetSchemas(httpCtx.Request().Context(), ns, httpCtx.ParamValue("module"), httpCtx.ParamValue("table")); err == nil { return httpCtx.Success(schemas) } else { return httpCtx.Error(HttpDatabaseQueryFailed, err.Error()) } } //handleSaveSchema 保存schema func (api *Api) 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 api.crud.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 = api.crud.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 { if api.crud.enableCache { cache.Delete(fmt.Sprintf("schema:%s:%s:%s", ns, moduleName, tableName)) } return httpCtx.Success(map[string]interface{}{ "count": len(schemas), "state": "success", }) } else { return httpCtx.Error(HTTPUnknownFailed, err.Error()) } } //handleListSchemas 查看表schema func (api *Api) handleListSchemas(httpCtx *http.Context) (err error) { var ( moduleLabel string ) ts := make([]*treeValue, 0) isHandled := false for _, e := range api.crud.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) } //handleDeleteSchema 删除指定的表结构 func (api *Api) handleDeleteSchema(httpCtx *http.Context) (err error) { id := httpCtx.ParamValue("id") model := &Schema{} if err = api.crud.db.Where("id=?", id).First(model).Error; err == nil { if err = api.crud.db.Where("id=?", id).Delete(&Schema{}).Error; err == nil { if api.crud.enableCache { cache.Delete(fmt.Sprintf("schema:%s:%s:%s", model.Namespace, model.ModuleName, model.TableName)) } return httpCtx.Success(map[string]string{ "id": id, "state": "success", }) } else { return httpCtx.Error(HttpDatabaseDeleteFailed, err.Error()) } } else { return httpCtx.Error(HttpDatabaseFindFailed, err.Error()) } } //handleFeed 订阅指定模块数据 func (api *Api) handleFeed(httpCtx *http.Context) (err error) { req := &feedRequest{} if err = httpCtx.Bind(req); err != nil { return httpCtx.Error(HttpInvalidPayload, err.Error()) } if !api.isExists(req.TableName + "@" + req.ModuleName) { return httpCtx.Error(HTTPUnknownFailed, fmt.Sprintf("model %s not register", req.TableName)) } if api.crud.enableCache { if v, ok := cache.Get(req.cacheID()); ok { return httpCtx.Success(v) } } result := make([]map[string]interface{}, 0, 10) query := api.crud.db.Table(req.TableName).Select(req.LabelField, req.ValueField) if req.Condition != "" { query = query.Where(req.Condition) } if req.Limit > 0 { query = query.Limit(req.Limit) } if err = query.Scan(&result).Error; err == nil { feeds := make([]feedResponse, 0, len(result)) for _, pairs := range result { feed := feedResponse{} for k, v := range pairs { if k == req.LabelField { feed.Label = v } if k == req.ValueField { feed.Value = v } } feeds = append(feeds, feed) } if api.crud.enableCache { cache.SetEx(req.cacheID(), feeds, time.Second*10) } return httpCtx.Success(feeds) } else { return httpCtx.Error(HttpDatabaseFindFailed, err.Error()) } } func (api *Api) isExists(id string) bool { if api.exists == nil { return false } for _, s := range api.exists { if s == id { return true } } return false } func (api *Api) Attach(rest *Restful, ms ...http.Middleware) { if api.svr == nil { return } id := rest.model.TableName() + "@" + rest.model.ModuleName() if api.isExists(id) { return } if len(ms) == 0 { ms = api.middleware } if rest.hasScenario(ScenarioList) { api.svr.Handle("GET", rest.getScenarioUrl(ScenarioList), rest.getScenarioHandle(ScenarioList), ms...) } if rest.hasScenario(ScenarioCreate) { api.svr.Handle("POST", rest.getScenarioUrl(ScenarioCreate), rest.getScenarioHandle(ScenarioCreate), ms...) } if rest.hasScenario(ScenarioUpdate) { api.svr.Handle("PUT", rest.getScenarioUrl(ScenarioUpdate), rest.getScenarioHandle(ScenarioUpdate), ms...) } if rest.hasScenario(ScenarioDelete) { api.svr.Handle("DELETE", rest.getScenarioUrl(ScenarioDelete), rest.getScenarioHandle(ScenarioDelete), ms...) } if rest.hasScenario(ScenarioExport) { api.svr.Handle("GET", rest.getScenarioUrl(ScenarioExport), rest.getScenarioHandle(ScenarioExport), ms...) } if rest.hasScenario(ScenarioView) { api.svr.Handle("GET", rest.getScenarioUrl(ScenarioView), rest.getScenarioHandle(ScenarioView), ms...) } api.exists = append(api.exists, id) } func (api *Api) Router(svr *http.Server, ms ...http.Middleware) { api.svr = svr api.middleware = ms svr.Handle("GET", "/rest/schemas", api.handleListSchemas, ms...) svr.Handle("GET", "/rest/schema/:module/:table", api.handleListSchema, ms...) svr.Handle("PUT", "/rest/schema/:module/:table", api.handleSaveSchema, ms...) svr.Handle("DELETE", "/rest/schema/:id", api.handleDeleteSchema, ms...) svr.Handle("POST", "/rest/feed", api.handleFeed, ms...) api.exists = make([]string, 0, 10) for _, rest := range api.crud.modules { api.Attach(rest, ms...) } }