schema.go 7.2 KB

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