crud.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. package rest
  2. import (
  3. "context"
  4. "fmt"
  5. "git.nspix.com/golang/micro/gateway/http"
  6. "gorm.io/gorm"
  7. "gorm.io/gorm/clause"
  8. "strings"
  9. )
  10. const (
  11. DefaultNamespace = "default"
  12. NamespaceVariable = "namespace"
  13. UserVariable = "@uid"
  14. DepartmentVariable = "@department"
  15. NamespaceField = "namespace"
  16. CreatedByField = "CreatedBy"
  17. CreatedDeptField = "CreatedDept"
  18. UpdatedByField = "UpdatedBy"
  19. UpdatedDeptField = "UpdatedDept"
  20. )
  21. const (
  22. TypeModule = "module"
  23. TypeTable = "table"
  24. )
  25. type (
  26. CRUD struct {
  27. db *gorm.DB
  28. modules []*Restful
  29. delegate *delegate
  30. }
  31. treeValue struct {
  32. Label string `json:"label"`
  33. Value string `json:"value"`
  34. Type string `json:"type"`
  35. Children []*treeValue `json:"children,omitempty"`
  36. }
  37. )
  38. func (t *treeValue) Append(v *treeValue) {
  39. if t.Children == nil {
  40. t.Children = make([]*treeValue, 0)
  41. }
  42. t.Children = append(t.Children, v)
  43. }
  44. //createStatement create new statement
  45. func (r *CRUD) createStatement(db *gorm.DB) *gorm.Statement {
  46. return &gorm.Statement{
  47. DB: db,
  48. ConnPool: db.Statement.ConnPool,
  49. Context: db.Statement.Context,
  50. Clauses: map[string]clause.Clause{},
  51. }
  52. }
  53. func (r *CRUD) RegisterDelegate(d Delegate) {
  54. r.delegate.Register(d)
  55. }
  56. //Attach 注册一个表
  57. func (r *CRUD) Attach(ctx context.Context, model Model, cbs ...Option) (err error) {
  58. var (
  59. opts *Options
  60. )
  61. opts = newOptions()
  62. for _, cb := range cbs {
  63. cb(opts)
  64. }
  65. if opts.DB == nil {
  66. opts.DB = r.db
  67. }
  68. if err = r.db.AutoMigrate(model); err != nil {
  69. return
  70. }
  71. if err = r.migrateSchema(ctx, DefaultNamespace, model); err != nil {
  72. return
  73. }
  74. opts.Delegate = r.delegate
  75. opts.LookupFunc = r.VisibleSchemas
  76. r.modules = append(r.modules, newRestful(model, opts))
  77. return
  78. }
  79. //migrateSchema 合并表的结构数据
  80. func (r *CRUD) migrateSchema(ctx context.Context, namespace string, model Model) (err error) {
  81. var (
  82. pos int
  83. columnLabel string
  84. columnName string
  85. columnIsExists bool
  86. stmt *gorm.Statement
  87. schemas []*Schema
  88. schemaModels []*Schema
  89. )
  90. schemas, err = r.GetSchemas(ctx, namespace, model.ModuleName(), model.TableName())
  91. stmt = r.createStatement(r.db)
  92. if err = stmt.Parse(model); err != nil {
  93. return
  94. }
  95. if len(schemas) > 0 {
  96. pos = len(schemas)
  97. }
  98. for index, field := range stmt.Schema.Fields {
  99. columnName = field.DBName
  100. if columnName == "-" {
  101. continue
  102. }
  103. if columnName == "" {
  104. columnName = field.Name
  105. }
  106. columnIsExists = false
  107. for _, sm := range schemas {
  108. if sm.Column == columnName {
  109. columnIsExists = true
  110. break
  111. }
  112. }
  113. if columnIsExists {
  114. continue
  115. }
  116. columnLabel = field.Tag.Get("comment")
  117. if columnLabel == "" {
  118. columnLabel = generateFieldName(field.DBName)
  119. }
  120. isPrimaryKey := uint8(0)
  121. if field.PrimaryKey {
  122. isPrimaryKey = 1
  123. }
  124. schemaModel := &Schema{
  125. Namespace: namespace,
  126. ModuleName: model.ModuleName(),
  127. TableName: model.TableName(),
  128. Enable: 1,
  129. Column: columnName,
  130. Label: columnLabel,
  131. Type: strings.ToLower(dataTypeOf(field)),
  132. Format: strings.ToLower(dataFormatOf(field)),
  133. Native: 1,
  134. IsPrimaryKey: isPrimaryKey,
  135. Scenarios: generateFieldScenario(index, field),
  136. Rule: generateFieldRule(field),
  137. Attribute: generateFieldAttribute(field),
  138. Position: pos,
  139. }
  140. schemaModels = append(schemaModels, schemaModel)
  141. pos++
  142. }
  143. if len(schemaModels) > 0 {
  144. err = r.db.Create(schemaModels).Error
  145. }
  146. return
  147. }
  148. //GetSchemas 获取表的结构数据
  149. func (r *CRUD) GetSchemas(ctx context.Context, namespace, moduleName, tableName string) (schemas []*Schema, err error) {
  150. schemas = make([]*Schema, 0)
  151. if len(namespace) == 0 {
  152. namespace = DefaultNamespace
  153. }
  154. sess := r.db.Session(&gorm.Session{NewDB: true, Context: ctx})
  155. err = sess.Where("`namespace`=? AND `module_name`=? AND `table_name`=?", namespace, moduleName, tableName).Order("position,id ASC").Find(&schemas).Error
  156. return
  157. }
  158. //VisibleSchemas 获取指定场景表的数据
  159. func (r *CRUD) VisibleSchemas(ctx context.Context, ns, moduleName, tableName, scene string) (result []*Schema) {
  160. var (
  161. err error
  162. schemas []*Schema
  163. )
  164. if schemas, err = r.GetSchemas(ctx, ns, moduleName, tableName); err == nil {
  165. result = make([]*Schema, 0, len(schemas))
  166. for _, s := range schemas {
  167. if s.Scenarios.Has(scene) || s.Attribute.PrimaryKey {
  168. result = append(result, s)
  169. }
  170. }
  171. }
  172. return
  173. }
  174. func (r *CRUD) handleListSchema(httpCtx *http.Context) (err error) {
  175. var (
  176. schemas []*Schema
  177. )
  178. ns := httpCtx.FormValue(NamespaceVariable)
  179. if ns == "" {
  180. ns = DefaultNamespace
  181. }
  182. if schemas, err = r.GetSchemas(httpCtx.Request().Context(), ns, httpCtx.ParamValue("module"), httpCtx.ParamValue("table")); err == nil {
  183. return httpCtx.Success(schemas)
  184. } else {
  185. return httpCtx.Error(HttpDatabaseQueryFailed, err.Error())
  186. }
  187. }
  188. func (r *CRUD) handleSaveSchema(httpCtx *http.Context) (err error) {
  189. var (
  190. rest *Restful
  191. )
  192. ns := httpCtx.FormValue(NamespaceVariable)
  193. if ns == "" {
  194. ns = DefaultNamespace
  195. }
  196. moduleName := httpCtx.ParamValue("module")
  197. tableName := httpCtx.ParamValue("table")
  198. for _, row := range r.modules {
  199. if row.model.ModuleName() == moduleName && row.model.TableName() == tableName {
  200. rest = row
  201. break
  202. }
  203. }
  204. if rest == nil {
  205. return httpCtx.Error(HTTPUnknownFailed, fmt.Sprintf("module %s table %s schema not found", moduleName, tableName))
  206. }
  207. schemas := make([]*Schema, 0)
  208. if err = httpCtx.Bind(&schemas); err != nil {
  209. return httpCtx.Error(HttpInvalidPayload, err.Error())
  210. }
  211. if err = r.db.Transaction(func(tx *gorm.DB) error {
  212. var (
  213. errTx error
  214. )
  215. for _, scm := range schemas {
  216. if errTx = tx.Save(scm).Error; errTx != nil {
  217. return errTx
  218. }
  219. }
  220. return nil
  221. }); err == nil {
  222. return httpCtx.Success(map[string]interface{}{
  223. "count": len(schemas),
  224. "state": "success",
  225. })
  226. } else {
  227. return httpCtx.Error(HTTPUnknownFailed, err.Error())
  228. }
  229. }
  230. func (r *CRUD) handleListSchemas(httpCtx *http.Context) (err error) {
  231. var (
  232. moduleLabel string
  233. )
  234. ts := make([]*treeValue, 0)
  235. isHandled := false
  236. for _, e := range r.modules {
  237. isHandled = false
  238. for _, tv := range ts {
  239. if tv.Value == e.model.ModuleName() {
  240. tv.Append(&treeValue{Value: e.model.TableName(), Label: e.model.TableName(), Type: TypeTable})
  241. isHandled = true
  242. break
  243. }
  244. }
  245. if isHandled {
  246. continue
  247. }
  248. moduleLabel = e.model.ModuleName()
  249. tv := &treeValue{Label: moduleLabel, Value: e.model.ModuleName(), Type: TypeModule}
  250. tv.Append(&treeValue{Value: e.model.TableName(), Label: e.model.TableName(), Type: TypeTable})
  251. ts = append(ts, tv)
  252. }
  253. return httpCtx.Success(ts)
  254. }
  255. func (r *CRUD) handleDeleteSchema(httpCtx *http.Context) (err error) {
  256. id := httpCtx.ParamValue("id")
  257. model := &Schema{}
  258. if err = r.db.Where("id=?", id).First(model).Error; err == nil {
  259. if err = r.db.Where("id=?", id).Delete(&Schema{}).Error; err == nil {
  260. return httpCtx.Success(map[string]string{
  261. "id": id,
  262. "state": "success",
  263. })
  264. } else {
  265. return httpCtx.Error(HttpDatabaseDeleteFailed, err.Error())
  266. }
  267. } else {
  268. return httpCtx.Error(HttpDatabaseFindFailed, err.Error())
  269. }
  270. }
  271. func (r *CRUD) Router(svr *http.Server, ms ...http.Middleware) {
  272. svr.Handle("GET", "/rest/schemas", r.handleListSchemas, ms...)
  273. svr.Handle("GET", "/rest/schema/:module/:table", r.handleListSchema, ms...)
  274. svr.Handle("PUT", "/rest/schema/:module/:table", r.handleSaveSchema, ms...)
  275. svr.Handle("DELETE", "/rest/schema/:id", r.handleDeleteSchema, ms...)
  276. for _, rest := range r.modules {
  277. if rest.hasScenario(ScenarioList) {
  278. svr.Handle("GET", rest.getScenarioUrl(ScenarioList), rest.getScenarioHandle(ScenarioList), ms...)
  279. }
  280. if rest.hasScenario(ScenarioCreate) {
  281. svr.Handle("POST", rest.getScenarioUrl(ScenarioCreate), rest.getScenarioHandle(ScenarioCreate), ms...)
  282. }
  283. if rest.hasScenario(ScenarioUpdate) {
  284. svr.Handle("PUT", rest.getScenarioUrl(ScenarioUpdate), rest.getScenarioHandle(ScenarioUpdate), ms...)
  285. }
  286. if rest.hasScenario(ScenarioDelete) {
  287. svr.Handle("DELETE", rest.getScenarioUrl(ScenarioDelete), rest.getScenarioHandle(ScenarioDelete), ms...)
  288. }
  289. if rest.hasScenario(ScenarioExport) {
  290. svr.Handle("GET", rest.getScenarioUrl(ScenarioExport), rest.getScenarioHandle(ScenarioExport), ms...)
  291. }
  292. if rest.hasScenario(ScenarioView) {
  293. svr.Handle("GET", rest.getScenarioUrl(ScenarioView), rest.getScenarioHandle(ScenarioView), ms...)
  294. }
  295. }
  296. }
  297. //New create new restful
  298. func New(dialer gorm.Dialector) (r *CRUD, err error) {
  299. r = &CRUD{
  300. delegate: &delegate{},
  301. }
  302. if r.db, err = gorm.Open(dialer); err != nil {
  303. return
  304. }
  305. r.db = r.db.Debug()
  306. err = r.db.AutoMigrate(&Schema{})
  307. return
  308. }