crud.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package rest
  2. import (
  3. "context"
  4. "fmt"
  5. "git.nspix.com/golang/rest/v3/cache"
  6. loggerpkg "git.nspix.com/golang/rest/v3/logger"
  7. "gorm.io/gorm"
  8. "gorm.io/gorm/clause"
  9. "gorm.io/gorm/logger"
  10. "strings"
  11. "time"
  12. )
  13. const (
  14. DefaultNamespace = "default"
  15. NamespaceVariable = "namespace"
  16. UserVariable = "@uid"
  17. DepartmentVariable = "@department"
  18. NamespaceField = "namespace"
  19. CreatedByField = "CreatedBy"
  20. CreatedDeptField = "CreatedDept"
  21. UpdatedByField = "UpdatedBy"
  22. UpdatedDeptField = "UpdatedDept"
  23. )
  24. const (
  25. TypeModule = "module"
  26. TypeTable = "table"
  27. )
  28. type (
  29. CRUD struct {
  30. api *Api
  31. db *gorm.DB
  32. modules []*Restful
  33. delegate *delegate
  34. enableCache bool
  35. }
  36. treeValue struct {
  37. Label string `json:"label"`
  38. Value string `json:"value"`
  39. Type string `json:"type"`
  40. Children []*treeValue `json:"children,omitempty"`
  41. }
  42. )
  43. func (t *treeValue) Append(v *treeValue) {
  44. if t.Children == nil {
  45. t.Children = make([]*treeValue, 0)
  46. }
  47. t.Children = append(t.Children, v)
  48. }
  49. //createStatement 创建一个声明
  50. func (r *CRUD) createStatement(db *gorm.DB) *gorm.Statement {
  51. return &gorm.Statement{
  52. DB: db,
  53. ConnPool: db.Statement.ConnPool,
  54. Context: db.Statement.Context,
  55. Clauses: map[string]clause.Clause{},
  56. }
  57. }
  58. func (r *CRUD) DB() *gorm.DB {
  59. return r.db
  60. }
  61. //DisableCache 禁用缓存
  62. func (r *CRUD) DisableCache() *CRUD {
  63. r.enableCache = false
  64. return r
  65. }
  66. //WithCache 启用缓存
  67. func (r *CRUD) WithCache() *CRUD {
  68. r.enableCache = true
  69. return r
  70. }
  71. //WithDebug 开启调试
  72. func (r *CRUD) WithDebug() *CRUD {
  73. r.db.Logger = r.db.Logger.LogMode(logger.Info)
  74. return r
  75. }
  76. //RegisterDelegate 注册一个旁观者
  77. func (r *CRUD) RegisterDelegate(d Delegate) {
  78. r.delegate.Register(d)
  79. }
  80. //Attach 注册一个模型
  81. func (r *CRUD) Attach(ctx context.Context, model Model, cbs ...Option) (err error) {
  82. var (
  83. ok bool
  84. opts *Options
  85. dynamicModel *Dynamic
  86. )
  87. opts = newOptions()
  88. for _, cb := range cbs {
  89. cb(opts)
  90. }
  91. if opts.DB == nil {
  92. opts.DB = r.db
  93. }
  94. if dynamicModel, ok = model.(*Dynamic); ok {
  95. sess := r.db.Session(&gorm.Session{NewDB: true})
  96. sess.Dialector = newDynamicDialector(model.TableName(), r.db.Dialector)
  97. if err = sess.AutoMigrate(dynamicModel.Interface()); err != nil {
  98. return
  99. }
  100. } else {
  101. if err = r.db.AutoMigrate(model); err != nil {
  102. return
  103. }
  104. //不启用schema
  105. if opts.Schema {
  106. if err = r.migrateSchema(ctx, DefaultNamespace, model); err != nil {
  107. return
  108. }
  109. }
  110. }
  111. opts.Delegate = r.delegate
  112. opts.LookupFunc = r.VisibleSchemas
  113. rt := newRestful(model, opts)
  114. r.modules = append(r.modules, rt)
  115. if r.api != nil {
  116. r.api.Attach(rt)
  117. }
  118. return
  119. }
  120. //migrateSchema 合并表的结构数据
  121. func (r *CRUD) migrateSchema(ctx context.Context, namespace string, model Model) (err error) {
  122. var (
  123. pos int
  124. columnLabel string
  125. columnName string
  126. columnIsExists bool
  127. stmt *gorm.Statement
  128. schemas []*Schema
  129. schemaModels []*Schema
  130. )
  131. schemas, err = r.GetSchemas(ctx, namespace, model.ModuleName(), model.TableName())
  132. stmt = r.createStatement(r.db)
  133. if err = stmt.Parse(model); err != nil {
  134. return
  135. }
  136. if len(schemas) > 0 {
  137. pos = len(schemas)
  138. }
  139. for index, field := range stmt.Schema.Fields {
  140. columnName = field.DBName
  141. if columnName == "-" {
  142. continue
  143. }
  144. if columnName == "" {
  145. columnName = field.Name
  146. }
  147. columnIsExists = false
  148. for _, sm := range schemas {
  149. if sm.Column == columnName {
  150. columnIsExists = true
  151. break
  152. }
  153. }
  154. if columnIsExists {
  155. continue
  156. }
  157. columnLabel = field.Tag.Get("comment")
  158. if columnLabel == "" {
  159. columnLabel = generateFieldName(field.DBName)
  160. }
  161. isPrimaryKey := uint8(0)
  162. if field.PrimaryKey {
  163. isPrimaryKey = 1
  164. }
  165. schemaModel := &Schema{
  166. Namespace: namespace,
  167. ModuleName: model.ModuleName(),
  168. TableName: model.TableName(),
  169. Enable: 1,
  170. Column: columnName,
  171. Label: columnLabel,
  172. Type: strings.ToLower(dataTypeOf(field)),
  173. Format: strings.ToLower(dataFormatOf(field)),
  174. Native: 1,
  175. IsPrimaryKey: isPrimaryKey,
  176. Scenarios: generateFieldScenario(index, field),
  177. Rule: generateFieldRule(field),
  178. Attribute: generateFieldAttribute(field),
  179. Position: pos,
  180. }
  181. schemaModels = append(schemaModels, schemaModel)
  182. pos++
  183. }
  184. if len(schemaModels) > 0 {
  185. err = r.db.Create(schemaModels).Error
  186. }
  187. return
  188. }
  189. //GetSchemas 获取表的结构数据
  190. func (r *CRUD) GetSchemas(ctx context.Context, namespace, moduleName, tableName string) (schemas []*Schema, err error) {
  191. cacheKey := fmt.Sprintf("schema:%s:%s:%s", namespace, moduleName, tableName)
  192. if r.enableCache {
  193. if v, ok := cache.Get(cacheKey); ok {
  194. return v.([]*Schema), nil
  195. }
  196. }
  197. schemas = make([]*Schema, 0)
  198. if len(namespace) == 0 {
  199. namespace = DefaultNamespace
  200. }
  201. sess := r.db.Session(&gorm.Session{NewDB: true, Context: ctx})
  202. if err = sess.Where("`namespace`=? AND `module_name`=? AND `table_name`=?", namespace, moduleName, tableName).Order("position,id ASC").Find(&schemas).Error; err == nil {
  203. if r.enableCache {
  204. cache.SetEx(cacheKey, schemas, time.Minute*30)
  205. }
  206. }
  207. return
  208. }
  209. //VisibleSchemas 获取指定场景表的数据
  210. func (r *CRUD) VisibleSchemas(ctx context.Context, ns, moduleName, tableName, scene string) (result []*Schema) {
  211. var (
  212. err error
  213. schemas []*Schema
  214. )
  215. if schemas, err = r.GetSchemas(ctx, ns, moduleName, tableName); err == nil {
  216. result = make([]*Schema, 0, len(schemas))
  217. for _, s := range schemas {
  218. if s.Scenarios.Has(scene) || s.Attribute.PrimaryKey {
  219. result = append(result, s)
  220. }
  221. }
  222. }
  223. return
  224. }
  225. //New create new restful
  226. func New(dialer gorm.Dialector) (r *CRUD, err error) {
  227. r = &CRUD{
  228. delegate: &delegate{},
  229. enableCache: true,
  230. }
  231. if r.db, err = gorm.Open(dialer); err != nil {
  232. return
  233. }
  234. r.api = &Api{crud: r}
  235. r.db.Logger = loggerpkg.New()
  236. err = r.db.AutoMigrate(&Schema{})
  237. return
  238. }