builder.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package publish
  2. import (
  3. "context"
  4. "fmt"
  5. "reflect"
  6. "strings"
  7. "sync"
  8. "github.com/iancoleman/strcase"
  9. "github.com/qor/oss"
  10. "github.com/qor5/admin/utils"
  11. "gorm.io/gorm"
  12. "gorm.io/gorm/schema"
  13. )
  14. type Builder struct {
  15. db *gorm.DB
  16. storage oss.StorageInterface
  17. context context.Context
  18. }
  19. func New(db *gorm.DB, storage oss.StorageInterface) *Builder {
  20. return &Builder{
  21. db: db,
  22. storage: storage,
  23. context: context.Background(),
  24. }
  25. }
  26. func (b *Builder) WithValue(key, val interface{}) *Builder {
  27. b.context = context.WithValue(b.context, key, val)
  28. return b
  29. }
  30. const (
  31. PublishContextKeyPageBuilder = "pagebuilder"
  32. PublishContextKeyL10nBuilder = "l10nbuilder"
  33. PublishContextKeyEventContext = "eventcontext"
  34. )
  35. func (b *Builder) WithPageBuilder(val interface{}) *Builder {
  36. b.context = context.WithValue(b.context, PublishContextKeyPageBuilder, val)
  37. return b
  38. }
  39. func (b *Builder) WithL10nBuilder(val interface{}) *Builder {
  40. b.context = context.WithValue(b.context, PublishContextKeyL10nBuilder, val)
  41. return b
  42. }
  43. func (b *Builder) WithEventContext(val interface{}) *Builder {
  44. b.context = context.WithValue(b.context, PublishContextKeyEventContext, val)
  45. return b
  46. }
  47. func (b *Builder) Context() context.Context {
  48. return b.context
  49. }
  50. // 幂等
  51. func (b *Builder) Publish(record interface{}) (err error) {
  52. err = utils.Transact(b.db, func(tx *gorm.DB) (err error) {
  53. // publish content
  54. if r, ok := record.(PublishInterface); ok {
  55. var objs []*PublishAction
  56. objs, err = r.GetPublishActions(b.db, b.context, b.storage)
  57. if err != nil {
  58. return
  59. }
  60. if err = UploadOrDelete(objs, b.storage); err != nil {
  61. return
  62. }
  63. }
  64. // update status
  65. if r, ok := record.(StatusInterface); ok {
  66. now := b.db.NowFunc()
  67. if version, ok := record.(VersionInterface); ok {
  68. var modelSchema *schema.Schema
  69. modelSchema, err = schema.Parse(record, &sync.Map{}, b.db.NamingStrategy)
  70. if err != nil {
  71. return
  72. }
  73. scope := SetPrimaryKeysConditionWithoutVersion(b.db.Model(reflect.New(modelSchema.ModelType).Interface()), record, modelSchema).Where("version <> ? AND status = ?", version.GetVersion(), StatusOnline)
  74. var count int64
  75. if err = scope.Count(&count).Error; err != nil {
  76. return
  77. }
  78. // update old version
  79. if count > 0 {
  80. var oldVersionUpdateMap = make(map[string]interface{})
  81. if _, ok := record.(ScheduleInterface); ok {
  82. oldVersionUpdateMap["scheduled_end_at"] = nil
  83. oldVersionUpdateMap["actual_end_at"] = &now
  84. }
  85. if _, ok := record.(ListInterface); ok {
  86. oldVersionUpdateMap["list_deleted"] = true
  87. }
  88. oldVersionUpdateMap["status"] = StatusOffline
  89. if err = scope.Updates(oldVersionUpdateMap).Error; err != nil {
  90. return
  91. }
  92. }
  93. }
  94. var updateMap = make(map[string]interface{})
  95. if r, ok := record.(ScheduleInterface); ok {
  96. r.SetPublishedAt(&now)
  97. r.SetScheduledStartAt(nil)
  98. updateMap["scheduled_start_at"] = r.GetScheduledStartAt()
  99. updateMap["actual_start_at"] = r.GetPublishedAt()
  100. }
  101. if _, ok := record.(ListInterface); ok {
  102. updateMap["list_updated"] = true
  103. }
  104. updateMap["status"] = StatusOnline
  105. updateMap["online_url"] = r.GetOnlineUrl()
  106. if err = b.db.Model(record).Updates(updateMap).Error; err != nil {
  107. return
  108. }
  109. }
  110. // publish callback
  111. if r, ok := record.(AfterPublishInterface); ok {
  112. if err = r.AfterPublish(b.db, b.storage, b.context); err != nil {
  113. return
  114. }
  115. }
  116. return
  117. })
  118. return
  119. }
  120. func (b *Builder) UnPublish(record interface{}) (err error) {
  121. err = utils.Transact(b.db, func(tx *gorm.DB) (err error) {
  122. // unpublish content
  123. if r, ok := record.(UnPublishInterface); ok {
  124. var objs []*PublishAction
  125. objs, err = r.GetUnPublishActions(b.db, b.context, b.storage)
  126. if err != nil {
  127. return
  128. }
  129. if err = UploadOrDelete(objs, b.storage); err != nil {
  130. return
  131. }
  132. }
  133. // update status
  134. if _, ok := record.(StatusInterface); ok {
  135. var updateMap = make(map[string]interface{})
  136. if r, ok := record.(ScheduleInterface); ok {
  137. now := b.db.NowFunc()
  138. r.SetUnPublishedAt(&now)
  139. r.SetScheduledEndAt(nil)
  140. updateMap["scheduled_end_at"] = r.GetScheduledEndAt()
  141. updateMap["actual_end_at"] = r.GetUnPublishedAt()
  142. }
  143. if _, ok := record.(ListInterface); ok {
  144. updateMap["list_deleted"] = true
  145. }
  146. updateMap["status"] = StatusOffline
  147. if err = b.db.Model(record).Updates(updateMap).Error; err != nil {
  148. return
  149. }
  150. }
  151. // unpublish callback
  152. if r, ok := record.(AfterUnPublishInterface); ok {
  153. if err = r.AfterUnPublish(b.db, b.storage, b.context); err != nil {
  154. return
  155. }
  156. }
  157. return
  158. })
  159. return
  160. }
  161. func (b *Builder) Sync(models ...interface{}) error {
  162. return nil
  163. }
  164. func UploadOrDelete(objs []*PublishAction, storage oss.StorageInterface) (err error) {
  165. for _, obj := range objs {
  166. if obj.IsDelete {
  167. fmt.Printf("deleting %s \n", obj.Url)
  168. err = storage.Delete(obj.Url)
  169. } else {
  170. fmt.Printf("uploading %s \n", obj.Url)
  171. _, err = storage.Put(obj.Url, strings.NewReader(obj.Content))
  172. }
  173. if err != nil {
  174. return
  175. }
  176. }
  177. return nil
  178. }
  179. func SetPrimaryKeysConditionWithoutVersion(db *gorm.DB, record interface{}, s *schema.Schema) *gorm.DB {
  180. querys := []string{}
  181. args := []interface{}{}
  182. for _, p := range s.PrimaryFields {
  183. if p.Name == "Version" {
  184. continue
  185. }
  186. val, _ := p.ValueOf(db.Statement.Context, reflect.ValueOf(record))
  187. querys = append(querys, fmt.Sprintf("%s = ?", strcase.ToSnake(p.Name)))
  188. args = append(args, val)
  189. }
  190. return db.Where(strings.Join(querys, " AND "), args...)
  191. }