entity.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. package crud
  2. import (
  3. "context"
  4. "encoding/csv"
  5. "encoding/json"
  6. "fmt"
  7. "git.nspix.com/golang/micro/gateway/http"
  8. "git.nspix.com/golang/rest/orm/query"
  9. "git.nspix.com/golang/rest/orm/schema"
  10. "git.nspix.com/golang/rest/orm/validator"
  11. "git.nspix.com/golang/rest/scenario"
  12. "gorm.io/gorm"
  13. "gorm.io/gorm/clause"
  14. "reflect"
  15. "strconv"
  16. "strings"
  17. "sync/atomic"
  18. "time"
  19. )
  20. var seq int64
  21. const (
  22. ScenarioCreate string = "create"
  23. ScenarioUpdate = "update"
  24. ScenarioDelete = "delete"
  25. ScenarioExport = "export"
  26. ScenarioList = "list"
  27. ScenarioView = "view"
  28. )
  29. type Entity struct {
  30. Module string
  31. db *gorm.DB
  32. enable *int32
  33. formatter *Formatter
  34. refType reflect.Type
  35. refValue reflect.Value
  36. stmt *gorm.Statement
  37. ctx context.Context
  38. prefix string
  39. naming struct {
  40. label string
  41. plural string
  42. singular string
  43. }
  44. seq int64
  45. Value interface{}
  46. Scenarios []string
  47. Urls map[string]string
  48. Callbacks Callbacks
  49. instance *CRUD
  50. }
  51. type (
  52. Entities []*Entity
  53. )
  54. func (e Entities) Len() int {
  55. return len(e)
  56. }
  57. func (e Entities) Less(i, j int) bool {
  58. return e[i].seq < e[j].seq
  59. }
  60. func (e Entities) Swap(i, j int) {
  61. e[i], e[j] = e[j], e[i]
  62. }
  63. func (e *Entity) primaryKey() string {
  64. if e.stmt.Schema != nil {
  65. if len(e.stmt.Schema.PrimaryFieldDBNames) > 0 {
  66. return e.stmt.Schema.PrimaryFieldDBNames[0]
  67. }
  68. }
  69. return "id"
  70. }
  71. func (e *Entity) hasScenarios(name string) bool {
  72. for _, s := range e.Scenarios {
  73. if name == s {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func (e *Entity) buildConditions(ctx *http.Context, search *query.Query, fields []*schema.Schema, val interface{}, cb EachQueryCallback) {
  80. var (
  81. err error
  82. )
  83. for _, field := range fields {
  84. if cb != nil {
  85. if err = cb(val, field, search, ctx); err != nil {
  86. continue
  87. }
  88. }
  89. if fcb := e.instance.getCallbackByFormat(field.Format); fcb != nil {
  90. if err = fcb(val, field, search, ctx); err != nil {
  91. continue
  92. }
  93. }
  94. switch field.Format {
  95. case "text", "textarea", "string":
  96. search.AndFilterWhere(query.NewConditionWithOperator("like", field.Column, ctx.FormValue(field.Column)))
  97. case "datetime", "date", "time":
  98. ss := make([]string, 0)
  99. formValue := ctx.FormValue(field.Column)
  100. if formValue != "" {
  101. if err := json.Unmarshal([]byte(ctx.FormValue(field.Column)), &ss); err == nil {
  102. if len(ss) == 2 {
  103. search.AndFilterWhere(
  104. query.NewConditionWithOperator(">=", field.Column, ss[0]),
  105. query.NewConditionWithOperator("<=", field.Column, ss[1]),
  106. )
  107. } else if len(ss) == 1 {
  108. search.AndFilterWhere(
  109. query.NewConditionWithOperator("=", field.Column, ss[0]),
  110. )
  111. }
  112. } else {
  113. var sep string
  114. seps := []byte{',', '/'}
  115. for _, s := range seps {
  116. if strings.IndexByte(formValue, s) > -1 {
  117. sep = string(s)
  118. }
  119. }
  120. if ss := strings.Split(formValue, sep); len(ss) == 2 {
  121. search.AndFilterWhere(
  122. query.NewConditionWithOperator(">=", field.Column, strings.TrimSpace(ss[0])),
  123. query.NewConditionWithOperator("<=", field.Column, strings.TrimSpace(ss[1])),
  124. )
  125. }
  126. }
  127. }
  128. case "duration", "number", "integer", "decimal":
  129. ss := make([]string, 0)
  130. formValue := ctx.FormValue(field.Column)
  131. if formValue != "" {
  132. if json.Valid([]byte(field.Column)) {
  133. if err := json.Unmarshal([]byte(ctx.FormValue(field.Column)), &ss); err == nil {
  134. if len(ss) == 2 {
  135. switch ss[0] {
  136. case ">", ">=", "<", "<=", "=":
  137. search.AndWhere(query.NewConditionWithOperator(ss[0], field.Column, ss[1]))
  138. default:
  139. search.AndWhere(query.NewConditionWithOperator("=", field.Column, ss[1]))
  140. }
  141. } else if len(ss) == 1 {
  142. search.AndWhere(
  143. query.NewConditionWithOperator("=", field.Column, ss[0]),
  144. )
  145. }
  146. }
  147. } else {
  148. search.AndWhere(query.NewConditionWithOperator("=", field.Column, formValue))
  149. }
  150. }
  151. default:
  152. if field.Type == "string" {
  153. search.AndFilterWhere(query.NewConditionWithOperator("like", field.Column, ctx.FormValue(field.Column)))
  154. } else {
  155. search.AndFilterWhere(query.NewConditionWithOperator("=", field.Column, ctx.FormValue(field.Column)))
  156. }
  157. }
  158. }
  159. }
  160. //构建排序规则
  161. func (e *Entity) buildSortable(ctx *http.Context, search *query.Query) {
  162. sortPar := ctx.FormValue("sort")
  163. if sortPar != "" {
  164. sorts := strings.Split(sortPar, ",")
  165. for _, s := range sorts {
  166. if s[0] == '-' {
  167. search.OrderBy(s[1:], "DESC")
  168. } else {
  169. if s[0] == '+' {
  170. search.OrderBy(s[1:], "ASC")
  171. } else {
  172. search.OrderBy(s, "ASC")
  173. }
  174. }
  175. }
  176. }
  177. }
  178. func (e *Entity) actionIndex(c *http.Context) (err error) {
  179. page, _ := strconv.Atoi(c.FormValue("page"))
  180. pagesize, _ := strconv.Atoi(c.FormValue("pagesize"))
  181. if pagesize <= 0 {
  182. pagesize = 15
  183. }
  184. pv := page
  185. if pv > 0 {
  186. pv--
  187. }
  188. sliceValue := reflect.MakeSlice(reflect.SliceOf(e.refType), 0, 0)
  189. models := reflect.New(sliceValue.Type())
  190. models.Elem().Set(sliceValue)
  191. search := query.New(e.db)
  192. if e.Callbacks.BeforeQuery != nil {
  193. if err = e.Callbacks.BeforeQuery(e.Value, search, c); err != nil {
  194. return c.Error(8004, err.Error())
  195. }
  196. }
  197. searchSchemas := schema.VisibleField(e.Module, e.stmt.Table, scenario.Search, e.db)
  198. listSchemas := schema.VisibleField(e.Module, e.stmt.Table, scenario.List, e.db)
  199. e.buildConditions(c, search, searchSchemas, e.Value, e.Callbacks.EachQuery)
  200. e.buildSortable(c, search)
  201. search.Offset(pv * pagesize).Limit(pagesize)
  202. //添加排序支持
  203. if err = search.All(models.Interface()); err != nil {
  204. return c.Error(8004, err.Error())
  205. }
  206. return c.Success(map[string]interface{}{
  207. "page": page,
  208. "pageSize": pagesize,
  209. "totalCount": search.Limit(0).Offset(0).Count(e.Value),
  210. "data": e.formatter.formatSchemas(models.Interface(), listSchemas, e.stmt),
  211. })
  212. }
  213. func (e *Entity) actionCreate(c *http.Context) (err error) {
  214. val := reflect.New(e.refType).Interface()
  215. if err = c.Bind(val); err != nil {
  216. return c.Error(8002, err.Error())
  217. }
  218. if e.Callbacks.BeforeInsert != nil {
  219. if err = e.Callbacks.BeforeInsert(val, c); err != nil {
  220. return c.Error(8004, err.Error())
  221. }
  222. }
  223. if e.Callbacks.BeforeSave != nil {
  224. if err = e.Callbacks.BeforeSave(val, c); err != nil {
  225. return c.Error(8004, err.Error())
  226. }
  227. }
  228. sess := e.db.Create(val)
  229. if err = sess.Error; err != nil {
  230. if err, ok := sess.Error.(*validator.StructError); ok {
  231. return c.Error(1001, err.Error())
  232. } else {
  233. return c.Error(8004, sess.Error.Error())
  234. }
  235. }
  236. if e.Callbacks.AfterInsert != nil {
  237. _ = e.Callbacks.AfterInsert(val, c)
  238. }
  239. if e.Callbacks.AfterSave != nil {
  240. _ = e.Callbacks.AfterSave(val, c)
  241. }
  242. return c.Success(map[string]interface{}{
  243. "id": schema.PrimaryKeyValue(sess.Statement),
  244. "state": "success",
  245. })
  246. }
  247. func (e *Entity) actionUpdate(c *http.Context) (err error) {
  248. idStr := c.ParamValue("id")
  249. val := reflect.New(e.refType).Interface()
  250. if err = e.db.Where(e.primaryKey()+"=?", idStr).First(val).Error; err != nil {
  251. return c.Error(8004, err.Error())
  252. }
  253. if err = c.Bind(val); err != nil {
  254. return c.Error(8002, err.Error())
  255. }
  256. if e.Callbacks.BeforeUpdate != nil {
  257. if err = e.Callbacks.BeforeUpdate(val, c); err != nil {
  258. return c.Error(8004, err.Error())
  259. }
  260. }
  261. if e.Callbacks.BeforeSave != nil {
  262. if err = e.Callbacks.BeforeSave(val, c); err != nil {
  263. return c.Error(8004, err.Error())
  264. }
  265. }
  266. sess := e.db.Model(val).Updates(val)
  267. if sess.Error != nil {
  268. if err, ok := sess.Error.(*validator.StructError); ok {
  269. return c.Error(1001, err.Error())
  270. } else {
  271. return c.Error(8004, sess.Error.Error())
  272. }
  273. }
  274. if e.Callbacks.AfterUpdate != nil {
  275. _ = e.Callbacks.AfterUpdate(val, c)
  276. }
  277. if e.Callbacks.AfterSave != nil {
  278. _ = e.Callbacks.AfterSave(val, c)
  279. }
  280. return c.Success(map[string]interface{}{
  281. "id": idStr,
  282. "state": "success",
  283. })
  284. }
  285. func (e *Entity) actionDelete(c *http.Context) (err error) {
  286. idStr := c.ParamValue("id")
  287. val := reflect.New(e.refType).Interface()
  288. if err = e.db.Where(e.primaryKey()+"=?", idStr).First(val).Error; err != nil {
  289. return c.Error(8004, err.Error())
  290. }
  291. if e.Callbacks.BeforeDelete != nil {
  292. if err = e.Callbacks.BeforeDelete(val, c); err != nil {
  293. return c.Error(8004, err.Error())
  294. }
  295. }
  296. if err = e.db.Where(e.primaryKey()+"=?", c.ParamValue("id")).Model(val).Delete(val).Error; err == nil {
  297. if e.Callbacks.AfterDelete != nil {
  298. _ = e.Callbacks.AfterDelete(val, c)
  299. }
  300. return c.Success(map[string]interface{}{
  301. "state": "success",
  302. "id": idStr,
  303. })
  304. } else {
  305. return c.Error(8009, err.Error())
  306. }
  307. }
  308. func (e *Entity) actionView(c *http.Context) (err error) {
  309. val := reflect.New(e.refType).Interface()
  310. if err = e.db.Where(e.primaryKey()+"=?", c.ParamValue("id")).First(val).Error; err != nil {
  311. return c.Error(8004, err.Error())
  312. }
  313. if c.FormValue("format") == "true" {
  314. listSchemas := schema.VisibleField(e.Module, e.stmt.Table, scenario.View, e.db)
  315. return c.Success(e.formatter.formatSchema(val, listSchemas, e.stmt))
  316. } else {
  317. return c.Success(val)
  318. }
  319. }
  320. func (e *Entity) actionExport(c *http.Context) (err error) {
  321. sliceValue := reflect.MakeSlice(reflect.SliceOf(e.refType), 0, 0)
  322. models := reflect.New(sliceValue.Type())
  323. models.Elem().Set(sliceValue)
  324. search := query.New(e.db)
  325. if e.Callbacks.BeforeQuery != nil {
  326. if err = e.Callbacks.BeforeQuery(e.Value, search, c); err != nil {
  327. return c.Error(8004, err.Error())
  328. }
  329. }
  330. searchSchemas := schema.VisibleField(e.Module, e.stmt.Table, scenario.Search, e.db)
  331. e.buildConditions(c, search, searchSchemas, e.Value, e.Callbacks.EachQuery)
  332. //添加排序支持
  333. e.buildSortable(c, search)
  334. if err = search.All(models.Interface()); err != nil {
  335. return c.Error(8004, err.Error())
  336. }
  337. c.Response().Header().Set("Content-Type", "text/csv")
  338. c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment;filename=%s.csv", e.naming.plural+"-"+time.Now().Format("20060102")))
  339. exportSchemas := schema.VisibleField(e.Module, e.stmt.Table, scenario.List, e.db)
  340. value := e.formatter.formatSchemas(models.Interface(), exportSchemas, e.stmt)
  341. writer := csv.NewWriter(c.Response())
  342. ss := make([]string, len(exportSchemas))
  343. for i, field := range exportSchemas {
  344. ss[i] = field.Label
  345. }
  346. _ = writer.Write(ss)
  347. if values, ok := value.([]interface{}); ok {
  348. for _, val := range values {
  349. row, ok2 := val.(map[string]interface{})
  350. if !ok2 {
  351. continue
  352. }
  353. for i, field := range exportSchemas {
  354. if v, ok := row[field.Column]; ok {
  355. ss[i] = fmt.Sprint(v)
  356. } else {
  357. ss[i] = ""
  358. }
  359. }
  360. _ = writer.Write(ss)
  361. }
  362. }
  363. writer.Flush()
  364. return
  365. }
  366. func (e *Entity) SetModule(module string) {
  367. e.Module = module
  368. }
  369. func newEntity(ctx context.Context, value interface{}, db *gorm.DB) *Entity {
  370. if ctx == nil {
  371. ctx = context.Background()
  372. }
  373. e := &Entity{
  374. db: db,
  375. ctx: ctx,
  376. Value: value,
  377. Urls: make(map[string]string),
  378. seq: atomic.AddInt64(&seq, 1),
  379. }
  380. e.stmt = &gorm.Statement{
  381. DB: db,
  382. ConnPool: db.ConnPool,
  383. Context: ctx,
  384. Clauses: map[string]clause.Clause{},
  385. }
  386. if err := e.stmt.Parse(value); err != nil {
  387. panic(err.Error())
  388. }
  389. e.refValue = reflect.Indirect(reflect.ValueOf(value))
  390. e.refType = e.refValue.Type()
  391. return e
  392. }