schema.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package schema
  2. import (
  3. "context"
  4. "encoding/json"
  5. "git.nspix.com/golang/micro/gateway/http"
  6. "git.nspix.com/golang/rest/internal/array"
  7. "git.nspix.com/golang/rest/scenario"
  8. "gorm.io/gorm"
  9. "gorm.io/gorm/clause"
  10. "gorm.io/gorm/schema"
  11. "reflect"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "time"
  16. )
  17. var (
  18. timeKind = reflect.TypeOf(time.Time{}).Kind()
  19. timePtrKind = reflect.TypeOf(&time.Time{}).Kind()
  20. cached map[string][]*Schema
  21. cacheLocker sync.RWMutex
  22. )
  23. func init() {
  24. cached = make(map[string][]*Schema)
  25. }
  26. type (
  27. Rule struct {
  28. Min int `json:"min"`
  29. Max int `json:"max"`
  30. Type string `json:"type"`
  31. Unique bool `json:"unique"`
  32. Required []string `json:"required"`
  33. Regular string `json:"regular"`
  34. }
  35. enumItem struct {
  36. Key interface{} `json:"key"`
  37. Val interface{} `json:"val"`
  38. }
  39. Options struct {
  40. Readonly []string `json:"readonly"`
  41. Disable []string `json:"disable"`
  42. Visible []string `json:"visible"`
  43. Items []enumItem `json:"items"`
  44. Description string `json:"description"`
  45. }
  46. Schema struct {
  47. Id uint64 `json:"id" gorm:"primary_key"`
  48. Module string `json:"module" gorm:"column:module_name;type:varchar(60)"`
  49. Table string `json:"table" gorm:"column:table_name;type:varchar(120)"`
  50. Enable bool `json:"enable"`
  51. Column string `json:"field" gorm:"type:varchar(120)"`
  52. Label string `json:"label" gorm:"type:varchar(120)"`
  53. Type string `json:"type" gorm:"type:varchar(120)"`
  54. Format string `json:"format" gorm:"type:varchar(120)"`
  55. Scenario string `json:"visible" gorm:"type:varchar(120)"`
  56. Rules string `json:"rules" gorm:"type:varchar(2048)"`
  57. Options string `json:"options" gorm:"type:varchar(4096)"`
  58. Position int `json:"position"`
  59. options *Options
  60. }
  61. Field struct {
  62. Column string `json:"column" gorm:"type:varchar(120)"`
  63. Label string `json:"label" gorm:"type:varchar(120)"`
  64. Type string `json:"type" gorm:"type:varchar(120)"`
  65. Format string `json:"format" gorm:"type:varchar(120)"`
  66. Scenario string `json:"scenario" gorm:"type:varchar(120)"`
  67. Rules string `json:"rules" gorm:"type:varchar(2048)"`
  68. Options string `json:"options" gorm:"type:varchar(4096)"`
  69. }
  70. )
  71. func (r *Rule) String() string {
  72. buf, _ := json.Marshal(r)
  73. return string(buf)
  74. }
  75. func (sc *Schema) GetOptions() *Options {
  76. if sc.options == nil {
  77. sc.options = &Options{}
  78. _ = json.Unmarshal([]byte(sc.Options), sc.options)
  79. }
  80. return sc.options
  81. }
  82. func (sc Schema) TableName() string {
  83. return "schemas"
  84. }
  85. func newStatement(db *gorm.DB) *gorm.Statement {
  86. return &gorm.Statement{
  87. DB: db,
  88. ConnPool: db.Statement.ConnPool,
  89. Context: db.Statement.Context,
  90. Clauses: map[string]clause.Clause{},
  91. }
  92. }
  93. func names(s string) string {
  94. var sb strings.Builder
  95. ss := strings.Split(s, "_")
  96. if len(ss) <= 0 {
  97. return ""
  98. }
  99. sb.WriteString(strings.Title(ss[0]))
  100. for _, i := range ss[1:] {
  101. sb.WriteString(" ")
  102. sb.WriteString(strings.Title(i))
  103. }
  104. return sb.String()
  105. }
  106. func IsNewRecord(value reflect.Value, stmt *gorm.Statement) bool {
  107. for _, pf := range stmt.Schema.PrimaryFields {
  108. if _, isZero := pf.ValueOf(value); isZero {
  109. return true
  110. }
  111. }
  112. return false
  113. }
  114. func InvalidCache(module, table string) {
  115. cacheLocker.Lock()
  116. delete(cached, table+"@"+module)
  117. cacheLocker.Unlock()
  118. }
  119. func VisibleField(module, table, scenario string, db *gorm.DB) []*Schema {
  120. var (
  121. ok bool
  122. err error
  123. sess *gorm.DB
  124. values []*Schema
  125. result []*Schema
  126. )
  127. cacheLocker.RLock()
  128. values, ok = cached[table+"@"+module]
  129. cacheLocker.RUnlock()
  130. if ok {
  131. goto __end
  132. }
  133. values = make([]*Schema, 0)
  134. sess = db.Scopes(func(db *gorm.DB) *gorm.DB {
  135. s := db.Session(&gorm.Session{})
  136. s.Statement = newStatement(s)
  137. return s
  138. })
  139. if err = sess.Where("module_name=? AND table_name=?", module, table).Order("position,id ASC").Find(&values).Error; err != nil {
  140. return values
  141. }
  142. cacheLocker.Lock()
  143. cached[table+"@"+module] = values
  144. cacheLocker.Unlock()
  145. __end:
  146. if scenario == "" {
  147. return values
  148. }
  149. result = make([]*Schema, 0)
  150. for _, model := range values {
  151. if strings.Index(model.Scenario, scenario) > -1 {
  152. result = append(result, model)
  153. }
  154. }
  155. return result
  156. }
  157. func extraSize(str string) int {
  158. var (
  159. sb strings.Builder
  160. flag = -1
  161. )
  162. b := make([]byte, 0)
  163. b = append(b, str...)
  164. for _, s := range b {
  165. if s >= '0' && s <= '9' {
  166. if flag == -1 {
  167. flag = 1
  168. }
  169. sb.WriteByte(s)
  170. } else if flag == 1 {
  171. break
  172. }
  173. }
  174. v, _ := strconv.Atoi(sb.String())
  175. return v
  176. }
  177. func rules(field *schema.Field) string {
  178. r := &Rule{
  179. Required: []string{},
  180. }
  181. if field.GORMDataType == "string" {
  182. r.Max = extraSize(string(field.DataType))
  183. }
  184. if field.PrimaryKey {
  185. r.Unique = true
  186. }
  187. return r.String()
  188. }
  189. func migrate(module string, db *gorm.DB, val interface{}) (err error) {
  190. fields := make([]*Schema, 0)
  191. stmt := newStatement(db)
  192. if err = stmt.Parse(val); err != nil {
  193. return err
  194. }
  195. db.Model(&Schema{}).Find(&fields, "table_name=?", stmt.Table)
  196. err = db.Transaction(func(tx *gorm.DB) error {
  197. var (
  198. i int
  199. format string
  200. label string
  201. fieldName string
  202. model *Schema
  203. isExists bool
  204. visible string
  205. dataType string
  206. count int
  207. enable bool
  208. )
  209. for _, field := range stmt.Schema.Fields {
  210. fieldName = field.DBName
  211. if fieldName == "-" {
  212. continue
  213. }
  214. isExists = false
  215. if fieldName == "" {
  216. fieldName = field.Name
  217. }
  218. label = field.Tag.Get("comment")
  219. if label == "" {
  220. label = names(fieldName)
  221. }
  222. format = dataFormatOf(field)
  223. for _, fv := range fields {
  224. if fv.Column == fieldName {
  225. isExists = true
  226. break
  227. }
  228. }
  229. if isExists {
  230. continue
  231. }
  232. if bv, _ := json.Marshal([]string{scenario.Search, scenario.List, scenario.Create, scenario.Update, scenario.Export, scenario.View}); bv != nil {
  233. visible = string(bv)
  234. }
  235. if !array.In(field.Name, []string{"ID", "CreatedAt", "UpdatedAt", "DeletedAt"}) && count < 10 {
  236. count++
  237. enable = true
  238. } else {
  239. enable = false
  240. }
  241. dataType = strings.ToLower(dataTypeOf(field))
  242. model = &Schema{
  243. Module: module,
  244. Table: stmt.Table,
  245. Column: fieldName,
  246. Enable: enable,
  247. Label: label,
  248. Type: dataType,
  249. Format: format,
  250. Options: `{"readonly":[],"disable":[],"visible":[],"items":[],"lazy":{"enable":false,"type":"dropdown","url":""}}`,
  251. Scenario: visible,
  252. Rules: rules(field),
  253. Position: i,
  254. }
  255. i++
  256. if err2 := tx.Save(model).Error; err2 != nil {
  257. return err2
  258. }
  259. }
  260. return nil
  261. })
  262. return
  263. }
  264. func Migrate(module string, db *gorm.DB, values ...interface{}) (err error) {
  265. for _, val := range values {
  266. if err = db.AutoMigrate(val); err == nil {
  267. if err = migrate(module, db, val); err != nil {
  268. return
  269. }
  270. }
  271. }
  272. return
  273. }
  274. func PrimaryKeyValue(statement *gorm.Statement) interface{} {
  275. var (
  276. field *schema.Field
  277. refValue reflect.Value
  278. )
  279. if len(statement.Schema.PrimaryFields) > 0 {
  280. field = statement.Schema.PrimaryFields[0]
  281. refValue = statement.ReflectValue.FieldByName(field.Name)
  282. if refValue.IsValid() && !refValue.IsZero() {
  283. return refValue.Interface()
  284. }
  285. }
  286. return nil
  287. }
  288. func Initialize(ctx context.Context, db *gorm.DB, svr *http.Server) (err error) {
  289. if db != nil {
  290. if err = db.AutoMigrate(&Schema{}); err != nil {
  291. return
  292. }
  293. }
  294. if svr != nil && db != nil {
  295. routes(db, svr)
  296. }
  297. return
  298. }