rest.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. package rest
  2. import (
  3. "context"
  4. "encoding/csv"
  5. "encoding/json"
  6. "fmt"
  7. "git.nspix.com/golang/micro/gateway/http"
  8. errpkg "git.nspix.com/golang/rest/v3/error"
  9. "git.nspix.com/golang/rest/v3/inflector"
  10. "gorm.io/gorm"
  11. "gorm.io/gorm/clause"
  12. "path"
  13. "reflect"
  14. "strconv"
  15. "strings"
  16. )
  17. const (
  18. HttpAccessDenied = 8004 //拒绝访问
  19. HttpInvalidPayload = 8002 //请求内容无效
  20. HttpRequestCallbackFailed = 8003 //执行回调失败
  21. HttpValidateFailed = 8008 //数据校验失败
  22. HttpDatabaseQueryFailed = 8010 //查询失败
  23. HttpDatabaseFindFailed = 8011 //查找失败
  24. HttpDatabaseCreateFailed = 8012 //创建失败
  25. HttpDatabaseUpdateFailed = 8013 //更新失败
  26. HttpDatabaseDeleteFailed = 8014 //删除失败
  27. HttpDatabaseExportFailed = 8015 //数据导出失败
  28. HTTPUnknownFailed = 9001 //未知错误
  29. )
  30. const (
  31. ErrorAccessDeniedMessage = "access denied"
  32. )
  33. type (
  34. DiffAttr struct {
  35. Column string `json:"column"`
  36. Label string `json:"label"`
  37. OldValue interface{} `json:"old_value"`
  38. NewValue interface{} `json:"new_value"`
  39. }
  40. Restful struct {
  41. model Model
  42. opts *Options
  43. reflectType reflect.Type
  44. reflectValue reflect.Value
  45. singularName string
  46. pluralizeName string
  47. statement *gorm.Statement
  48. primaryKey string
  49. }
  50. )
  51. // getScenarioUrl 获取某个场景下HTTP请求的URL
  52. func (r *Restful) getScenarioUrl(scenario string) string {
  53. var uri string
  54. switch scenario {
  55. case ScenarioList:
  56. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.pluralizeName
  57. case ScenarioView:
  58. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.singularName + "/:id"
  59. case ScenarioCreate:
  60. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.singularName
  61. case ScenarioUpdate:
  62. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.singularName + "/:id"
  63. case ScenarioDelete:
  64. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.singularName + "/:id"
  65. case ScenarioExport:
  66. uri = r.opts.ApiPrefix + "/" + r.model.ModuleName() + "/" + r.singularName + "-export"
  67. }
  68. return path.Clean(uri)
  69. }
  70. // getScenarioHandle 获取某个场景下HTTP请求的处理回调
  71. func (r *Restful) getScenarioHandle(scenario string) http.HandleFunc {
  72. var handleFunc http.HandleFunc
  73. switch scenario {
  74. case ScenarioList:
  75. handleFunc = r.actionIndex
  76. case ScenarioView:
  77. handleFunc = r.actionView
  78. case ScenarioCreate:
  79. handleFunc = r.actionCreate
  80. case ScenarioUpdate:
  81. handleFunc = r.actionUpdate
  82. case ScenarioDelete:
  83. handleFunc = r.actionDelete
  84. case ScenarioExport:
  85. handleFunc = r.actionExport
  86. }
  87. return handleFunc
  88. }
  89. func (r *Restful) setFieldValue(model reflect.Value, column string, value interface{}) {
  90. var (
  91. name string
  92. )
  93. refVal := reflect.Indirect(model)
  94. for _, field := range r.statement.Schema.Fields {
  95. if field.DBName == column {
  96. name = field.Name
  97. break
  98. } else if field.Name == column {
  99. name = column
  100. break
  101. }
  102. }
  103. if name == "" {
  104. return
  105. }
  106. fieldVal := refVal.FieldByName(name)
  107. if fieldVal.CanSet() {
  108. fieldVal.Set(reflect.ValueOf(value))
  109. }
  110. }
  111. //getFieldValue get field value from reflect value
  112. func (r *Restful) getFieldValue(model reflect.Value, column string) interface{} {
  113. var (
  114. name string
  115. )
  116. refVal := reflect.Indirect(model)
  117. for _, field := range r.statement.Schema.Fields {
  118. if field.DBName == column {
  119. name = field.Name
  120. break
  121. } else if field.Name == column {
  122. name = column
  123. break
  124. }
  125. }
  126. if name == "" {
  127. return nil
  128. }
  129. fieldVal := refVal.FieldByName(name)
  130. return fieldVal.Interface()
  131. }
  132. func (r *Restful) hasScenario(s string) bool {
  133. if v, ok := r.model.(FlexibleModel); ok {
  134. for _, n := range v.Scenarios() {
  135. if s == n {
  136. return true
  137. }
  138. }
  139. return false
  140. }
  141. return true
  142. }
  143. func (r *Restful) prepareConditions(ctx context.Context, requestCtx *http.Context, query *Query, schemas []*Schema) (err error) {
  144. var (
  145. ok bool
  146. skip bool
  147. formValue string
  148. model interface{}
  149. activeModel ActiveModel
  150. )
  151. model = reflect.New(r.reflectType).Interface()
  152. if r.opts.Delegate != nil {
  153. if err = r.opts.Delegate.BeforeQuery(ctx, query); err != nil {
  154. return
  155. }
  156. }
  157. if activeModel, ok = model.(ActiveModel); ok {
  158. if err = activeModel.BeforeQuery(ctx, query); err != nil {
  159. return
  160. }
  161. }
  162. //处理默认的搜索
  163. for _, schema := range schemas {
  164. skip = false
  165. if skip {
  166. continue
  167. }
  168. if schema.Native == 0 {
  169. continue
  170. }
  171. formValue = requestCtx.FormValue(schema.Column)
  172. switch schema.Format {
  173. case FormatString, FormatText:
  174. if schema.Attribute.Match == MatchExactly {
  175. query.AndFilterWhere(NewCond(schema.Column, formValue))
  176. } else {
  177. query.AndFilterWhere(NewCond(schema.Column, formValue).WithExpr("LIKE"))
  178. }
  179. case FormatTime, FormatDate, FormatDatetime, FormatTimestamp:
  180. var sep string
  181. seps := []byte{',', '/'}
  182. for _, s := range seps {
  183. if strings.IndexByte(formValue, s) > -1 {
  184. sep = string(s)
  185. }
  186. }
  187. if ss := strings.Split(formValue, sep); len(ss) == 2 {
  188. query.AndFilterWhere(
  189. NewCond(schema.Column, strings.TrimSpace(ss[0])).WithExpr(">="),
  190. NewCond(schema.Column, strings.TrimSpace(ss[1])).WithExpr("<="),
  191. )
  192. } else {
  193. query.AndFilterWhere(NewCond(schema.Column, formValue))
  194. }
  195. case FormatInteger, FormatFloat:
  196. query.AndFilterWhere(NewCond(schema.Column, formValue))
  197. default:
  198. if schema.Type == TypeString {
  199. if schema.Attribute.Match == MatchExactly {
  200. query.AndFilterWhere(NewCond(schema.Column, formValue))
  201. } else {
  202. query.AndFilterWhere(NewCond(schema.Column, formValue).WithExpr("LIKE"))
  203. }
  204. } else {
  205. query.AndFilterWhere(NewCond(schema.Column, formValue))
  206. }
  207. }
  208. }
  209. //处理排序
  210. sortPar := requestCtx.FormValue("sort")
  211. if sortPar != "" {
  212. sorts := strings.Split(sortPar, ",")
  213. for _, s := range sorts {
  214. if s[0] == '-' {
  215. query.OrderBy(s[1:], "DESC")
  216. } else {
  217. if s[0] == '+' {
  218. query.OrderBy(s[1:], "ASC")
  219. } else {
  220. query.OrderBy(s, "ASC")
  221. }
  222. }
  223. }
  224. }
  225. if activeModel, ok = model.(ActiveModel); ok {
  226. err = activeModel.AfterQuery(ctx, query)
  227. }
  228. if r.opts.Delegate != nil {
  229. err = r.opts.Delegate.AfterQuery(ctx, query)
  230. }
  231. return
  232. }
  233. func (r *Restful) actionIndex(httpCtx *http.Context) (err error) {
  234. var (
  235. page int
  236. pageSize int
  237. pageIndex int
  238. query *Query
  239. namespace string
  240. )
  241. if !r.hasScenario(ScenarioList) {
  242. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  243. }
  244. ctx := httpCtx.Request().Context()
  245. if ctx == nil {
  246. ctx = context.Background()
  247. }
  248. namespace = httpCtx.ParamValue(NamespaceVariable)
  249. ctx = context.WithValue(ctx, NamespaceField, namespace)
  250. ctx = context.WithValue(ctx, "request", httpCtx.Request())
  251. page, _ = strconv.Atoi(httpCtx.FormValue("page"))
  252. pageSize, _ = strconv.Atoi(httpCtx.FormValue("pagesize"))
  253. if pageSize <= 0 {
  254. pageSize = 15
  255. }
  256. pageIndex = page
  257. if pageIndex > 0 {
  258. pageIndex--
  259. }
  260. sliceValue := reflect.MakeSlice(reflect.SliceOf(r.reflectType), 0, 0)
  261. models := reflect.New(sliceValue.Type())
  262. models.Elem().Set(sliceValue)
  263. query = NewQuery(r.opts.DB)
  264. searchSchemas := r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioSearch)
  265. indexSchemas := r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioList)
  266. if err = r.prepareConditions(ctx, httpCtx, query, searchSchemas); err != nil {
  267. return httpCtx.Error(HttpDatabaseQueryFailed, err.Error())
  268. }
  269. if r.opts.EnableNamespace {
  270. query.AndFilterWhere(NewQueryCondition(NamespaceField, namespace))
  271. }
  272. query.Offset(pageIndex * pageSize).Limit(pageSize)
  273. if err = query.All(models.Interface()); err != nil {
  274. return httpCtx.Error(HttpDatabaseQueryFailed, err.Error())
  275. }
  276. resp := &ListResponse{
  277. Page: page,
  278. PageSize: pageSize,
  279. TotalCount: query.Limit(0).Offset(0).Count(r.model),
  280. }
  281. if resp.TotalCount > 0 {
  282. resp.Data = r.opts.Formatter.formatModels(ctx, models.Interface(), indexSchemas, r.statement)
  283. } else {
  284. resp.Data = make([]string, 0)
  285. }
  286. return httpCtx.Success(resp)
  287. }
  288. func (r *Restful) actionCreate(httpCtx *http.Context) (err error) {
  289. var (
  290. errTx error
  291. namespace string
  292. model interface{}
  293. schemas []*Schema
  294. refModel reflect.Value
  295. diffAttrs []*DiffAttr
  296. )
  297. ctx := httpCtx.Request().Context()
  298. if ctx == nil {
  299. ctx = context.Background()
  300. }
  301. diffAttrs = make([]*DiffAttr, 0, 10)
  302. if !r.hasScenario(ScenarioCreate) {
  303. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  304. }
  305. namespace = httpCtx.ParamValue(NamespaceVariable)
  306. refModel = reflect.New(r.reflectType)
  307. model = refModel.Interface()
  308. if err = httpCtx.Bind(model); err != nil {
  309. return httpCtx.Error(HttpInvalidPayload, err.Error())
  310. }
  311. schemas = r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioCreate)
  312. //global set field value
  313. r.setFieldValue(refModel, NamespaceField, namespace)
  314. r.setFieldValue(refModel, CreatedByField, httpCtx.ParamValue(UserVariable))
  315. r.setFieldValue(refModel, CreatedDeptField, httpCtx.ParamValue(DepartmentVariable))
  316. r.setFieldValue(refModel, UpdatedByField, httpCtx.ParamValue(UserVariable))
  317. r.setFieldValue(refModel, UpdatedDeptField, httpCtx.ParamValue(DepartmentVariable))
  318. if err = r.opts.DB.Transaction(func(tx *gorm.DB) error {
  319. if r.opts.Delegate != nil {
  320. if errTx = r.opts.Delegate.BeforeCreate(ctx, model); errTx != nil {
  321. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  322. }
  323. if errTx = r.opts.Delegate.BeforeSave(ctx, model); errTx != nil {
  324. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  325. }
  326. }
  327. if activeModel, ok := model.(ActiveModel); ok {
  328. if errTx = activeModel.BeforeCreate(ctx, model); errTx != nil {
  329. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  330. }
  331. if errTx = activeModel.BeforeSave(ctx, model); errTx != nil {
  332. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  333. }
  334. }
  335. //创建数据
  336. if errTx = tx.Create(model).Error; errTx != nil {
  337. return errTx
  338. }
  339. //对比差异数据
  340. for _, scm := range schemas {
  341. diffAttrs = append(diffAttrs, &DiffAttr{
  342. Column: scm.Column,
  343. Label: scm.Label,
  344. OldValue: nil,
  345. NewValue: r.getFieldValue(refModel, scm.Column),
  346. })
  347. }
  348. if activeModel, ok := model.(ActiveModel); ok {
  349. if errTx = activeModel.AfterCreate(ctx, model, diffAttrs); errTx != nil {
  350. return httpCtx.Error(HttpRequestCallbackFailed, err.Error())
  351. }
  352. if errTx = activeModel.AfterSave(ctx, model, diffAttrs); errTx != nil {
  353. return httpCtx.Error(HttpRequestCallbackFailed, err.Error())
  354. }
  355. }
  356. if r.opts.Delegate != nil {
  357. if errTx = r.opts.Delegate.AfterCreate(ctx, model, diffAttrs); errTx != nil {
  358. return httpCtx.Error(HttpRequestCallbackFailed, err.Error())
  359. }
  360. if errTx = r.opts.Delegate.AfterSave(ctx, model, diffAttrs); errTx != nil {
  361. return httpCtx.Error(HttpRequestCallbackFailed, err.Error())
  362. }
  363. }
  364. return errTx
  365. }); err == nil {
  366. pkVal := r.getFieldValue(refModel, r.primaryKey)
  367. return httpCtx.Success(&CreateResponse{
  368. ID: pkVal,
  369. Topic: r.model.TableName(),
  370. State: "created",
  371. })
  372. }
  373. //form validation
  374. if validateError, ok := err.(*errpkg.StructError); ok {
  375. httpCtx.Response().Header().Set("Content-Type", "application/json")
  376. return json.NewEncoder(httpCtx.Response()).Encode(map[string]interface{}{
  377. "errno": HttpValidateFailed,
  378. "result": validateError,
  379. })
  380. }
  381. return httpCtx.Error(HttpDatabaseCreateFailed, err.Error())
  382. }
  383. func (r *Restful) actionUpdate(httpCtx *http.Context) (err error) {
  384. var (
  385. errTx error
  386. namespace string
  387. model interface{}
  388. schemas []*Schema
  389. refModel reflect.Value
  390. oldValues = make(map[string]interface{})
  391. diffs = make(map[string]interface{})
  392. diffAttrs = make([]*DiffAttr, 0)
  393. )
  394. ctx := httpCtx.Request().Context()
  395. if ctx == nil {
  396. ctx = context.Background()
  397. }
  398. if !r.hasScenario(ScenarioUpdate) {
  399. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  400. }
  401. namespace = httpCtx.ParamValue(NamespaceVariable)
  402. idStr := httpCtx.ParamValue("id")
  403. refModel = reflect.New(r.reflectType)
  404. model = refModel.Interface()
  405. //默认设置更新用户
  406. r.setFieldValue(refModel, UpdatedByField, httpCtx.ParamValue(UserVariable))
  407. r.setFieldValue(refModel, UpdatedDeptField, httpCtx.ParamValue(DepartmentVariable))
  408. conditions := map[string]interface{}{
  409. r.primaryKey: idStr,
  410. }
  411. if r.opts.EnableNamespace {
  412. conditions[NamespaceField] = namespace
  413. }
  414. if err = r.opts.DB.Where(conditions).First(model).Error; err != nil {
  415. return httpCtx.Error(HttpDatabaseFindFailed, err.Error())
  416. }
  417. schemas = r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioUpdate)
  418. for _, scm := range schemas {
  419. oldValues[scm.Column] = r.getFieldValue(refModel, scm.Column)
  420. }
  421. if err = httpCtx.Bind(model); err != nil {
  422. return httpCtx.Error(HttpInvalidPayload, err.Error())
  423. }
  424. if err = r.opts.DB.Transaction(func(tx *gorm.DB) error {
  425. if r.opts.Delegate != nil {
  426. if errTx = r.opts.Delegate.BeforeUpdate(ctx, model); errTx != nil {
  427. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  428. }
  429. if errTx = r.opts.Delegate.BeforeSave(ctx, model); errTx != nil {
  430. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  431. }
  432. }
  433. if activeModel, ok := model.(ActiveModel); ok {
  434. if errTx = activeModel.BeforeUpdate(ctx, model); errTx != nil {
  435. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  436. }
  437. if errTx = activeModel.BeforeSave(ctx, model); errTx != nil {
  438. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  439. }
  440. }
  441. //对比差异数据
  442. for _, scm := range schemas {
  443. v := r.getFieldValue(refModel, scm.Column)
  444. if oldValues[scm.Column] != v {
  445. diffs[scm.Column] = v
  446. diffAttrs = append(diffAttrs, &DiffAttr{
  447. Column: scm.Column,
  448. Label: scm.Label,
  449. OldValue: oldValues[scm.Column],
  450. NewValue: v,
  451. })
  452. }
  453. }
  454. //进行局部数据更新
  455. if len(diffs) > 0 {
  456. if errTx = tx.Model(model).Updates(diffs).Error; errTx != nil {
  457. return errTx
  458. }
  459. }
  460. if activeModel, ok := model.(ActiveModel); ok {
  461. if errTx = activeModel.AfterUpdate(ctx, model, diffAttrs); errTx != nil {
  462. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  463. }
  464. if errTx = activeModel.AfterSave(ctx, model, diffAttrs); errTx != nil {
  465. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  466. }
  467. }
  468. if r.opts.Delegate != nil {
  469. if errTx = r.opts.Delegate.AfterUpdate(ctx, model, diffAttrs); errTx != nil {
  470. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  471. }
  472. if errTx = r.opts.Delegate.AfterSave(ctx, model, diffAttrs); errTx != nil {
  473. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  474. }
  475. }
  476. return errTx
  477. }); err == nil {
  478. pkVal := r.getFieldValue(refModel, r.primaryKey)
  479. return httpCtx.Success(&UpdateResponse{
  480. ID: pkVal,
  481. Topic: r.model.TableName(),
  482. State: "updated",
  483. })
  484. }
  485. //form validation
  486. if validateError, ok := err.(*errpkg.StructError); ok {
  487. httpCtx.Response().Header().Set("Content-Type", "application/json")
  488. return json.NewEncoder(httpCtx.Response()).Encode(map[string]interface{}{
  489. "errno": HttpValidateFailed,
  490. "result": validateError,
  491. })
  492. }
  493. return httpCtx.Error(HttpDatabaseUpdateFailed, err.Error())
  494. }
  495. func (r *Restful) actionDelete(httpCtx *http.Context) (err error) {
  496. var (
  497. errTx error
  498. model interface{}
  499. namespace string
  500. )
  501. if !r.hasScenario(ScenarioDelete) {
  502. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  503. }
  504. ctx := httpCtx.Request().Context()
  505. if ctx == nil {
  506. ctx = context.Background()
  507. }
  508. idStr := httpCtx.ParamValue("id")
  509. namespace = httpCtx.ParamValue(NamespaceVariable)
  510. model = reflect.New(r.reflectType).Interface()
  511. conditions := map[string]interface{}{
  512. r.primaryKey: idStr,
  513. }
  514. if r.opts.EnableNamespace {
  515. conditions[NamespaceField] = namespace
  516. }
  517. if err = r.opts.DB.Where(conditions).First(model).Error; err != nil {
  518. return httpCtx.Error(HttpDatabaseFindFailed, err.Error())
  519. }
  520. if err = r.opts.DB.Transaction(func(tx *gorm.DB) error {
  521. if r.opts.Delegate != nil {
  522. if errTx = r.opts.Delegate.BeforeDelete(ctx, model); errTx != nil {
  523. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  524. }
  525. }
  526. if activeModel, ok := model.(ActiveModel); ok {
  527. if errTx = activeModel.BeforeDelete(ctx, model); errTx != nil {
  528. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  529. }
  530. }
  531. if errTx = tx.Delete(model).Error; errTx != nil {
  532. return errTx
  533. }
  534. if activeModel, ok := model.(ActiveModel); ok {
  535. if errTx = activeModel.AfterDelete(ctx, model); errTx != nil {
  536. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  537. }
  538. }
  539. if r.opts.Delegate != nil {
  540. if errTx = r.opts.Delegate.AfterDelete(ctx, model); errTx != nil {
  541. return httpCtx.Error(HttpRequestCallbackFailed, errTx.Error())
  542. }
  543. }
  544. return errTx
  545. }); err == nil {
  546. return httpCtx.Success(&DeleteResponse{
  547. ID: r.getFieldValue(reflect.ValueOf(model), r.primaryKey),
  548. Topic: r.model.TableName(),
  549. State: "updated",
  550. })
  551. } else {
  552. return httpCtx.Error(HttpDatabaseDeleteFailed, err.Error())
  553. }
  554. }
  555. func (r *Restful) actionExport(httpCtx *http.Context) (err error) {
  556. var (
  557. query *Query
  558. namespace string
  559. )
  560. if !r.hasScenario(ScenarioExport) {
  561. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  562. }
  563. ctx := httpCtx.Request().Context()
  564. if ctx == nil {
  565. ctx = context.Background()
  566. }
  567. ctx = context.WithValue(ctx, NamespaceVariable, namespace)
  568. namespace = httpCtx.ParamValue(NamespaceVariable)
  569. sliceValue := reflect.MakeSlice(reflect.SliceOf(r.reflectType), 0, 0)
  570. models := reflect.New(sliceValue.Type())
  571. models.Elem().Set(sliceValue)
  572. query = NewQuery(r.opts.DB)
  573. searchSchemas := r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioSearch)
  574. exportSchemas := r.opts.LookupFunc(ctx, namespace, r.model.ModuleName(), r.model.TableName(), ScenarioList)
  575. if err = r.prepareConditions(ctx, httpCtx, query, searchSchemas); err != nil {
  576. return httpCtx.Error(HttpDatabaseQueryFailed, err.Error())
  577. }
  578. if r.opts.EnableNamespace {
  579. query.AndFilterWhere(NewQueryCondition(NamespaceVariable, namespace))
  580. }
  581. if err = query.All(models.Interface()); err != nil {
  582. return httpCtx.Error(HttpDatabaseExportFailed, err.Error())
  583. }
  584. httpCtx.Response().Header().Set("Content-Type", "text/csv")
  585. httpCtx.Response().Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
  586. httpCtx.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment;filename=%s.csv", r.singularName))
  587. value := r.opts.Formatter.formatModels(ctx, models.Interface(), exportSchemas, r.statement)
  588. writer := csv.NewWriter(httpCtx.Response())
  589. ss := make([]string, len(exportSchemas))
  590. for i, field := range exportSchemas {
  591. ss[i] = field.Label
  592. }
  593. _ = writer.Write(ss)
  594. if values, ok := value.([]interface{}); ok {
  595. for _, val := range values {
  596. row, ok2 := val.(map[string]interface{})
  597. if !ok2 {
  598. continue
  599. }
  600. for i, field := range exportSchemas {
  601. if v, ok := row[field.Column]; ok {
  602. ss[i] = fmt.Sprint(v)
  603. } else {
  604. ss[i] = ""
  605. }
  606. }
  607. _ = writer.Write(ss)
  608. }
  609. }
  610. writer.Flush()
  611. return
  612. }
  613. func (r *Restful) actionView(httpCtx *http.Context) (err error) {
  614. var (
  615. model interface{}
  616. namespace string
  617. )
  618. if !r.hasScenario(ScenarioView) {
  619. return httpCtx.Error(HttpAccessDenied, ErrorAccessDeniedMessage)
  620. }
  621. namespace = httpCtx.ParamValue(NamespaceVariable)
  622. scenario := httpCtx.FormValue("scenario")
  623. idStr := httpCtx.ParamValue("id")
  624. model = reflect.New(r.reflectType).Interface()
  625. conditions := map[string]interface{}{
  626. r.primaryKey: idStr,
  627. }
  628. if r.opts.EnableNamespace {
  629. conditions[NamespaceField] = namespace
  630. }
  631. if err = r.opts.DB.Where(conditions).First(model).Error; err != nil {
  632. return httpCtx.Error(HttpDatabaseFindFailed, err.Error())
  633. }
  634. if httpCtx.FormValue("format") != "" {
  635. //获取指定场景下面的字段进行渲染显示
  636. var schemas []*Schema
  637. if scenario == "" {
  638. schemas = r.opts.LookupFunc(httpCtx.Request().Context(), namespace, r.model.ModuleName(), r.model.TableName(), ScenarioView)
  639. } else {
  640. schemas = r.opts.LookupFunc(httpCtx.Request().Context(), namespace, r.model.ModuleName(), r.model.TableName(), scenario)
  641. }
  642. requestCtx := httpCtx.Request().Context()
  643. if requestCtx == nil {
  644. requestCtx = context.Background()
  645. }
  646. requestCtx = context.WithValue(requestCtx, NamespaceVariable, namespace)
  647. return httpCtx.Success(r.opts.Formatter.formatModel(requestCtx, model, schemas, r.statement))
  648. }
  649. return httpCtx.Success(model)
  650. }
  651. func newRestful(model Model, opts *Options) *Restful {
  652. var (
  653. err error
  654. tableName string
  655. )
  656. r := &Restful{
  657. opts: opts,
  658. model: model,
  659. reflectValue: reflect.Indirect(reflect.ValueOf(model)),
  660. }
  661. r.reflectType = r.reflectValue.Type()
  662. tableName = model.TableName()
  663. r.singularName = inflector.Singularize(tableName)
  664. r.pluralizeName = inflector.Pluralize(tableName)
  665. r.statement = &gorm.Statement{
  666. DB: r.opts.DB,
  667. ConnPool: r.opts.DB.ConnPool,
  668. Clauses: map[string]clause.Clause{},
  669. }
  670. if err = r.statement.Parse(model); err == nil {
  671. if r.statement.Schema != nil {
  672. if r.statement.Schema.PrimaryFieldDBNames != nil && len(r.statement.Schema.PrimaryFieldDBNames) > 0 {
  673. r.primaryKey = r.statement.Schema.PrimaryFieldDBNames[0]
  674. }
  675. }
  676. }
  677. return r
  678. }