list_publish_builder.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package publish
  2. import (
  3. "context"
  4. "errors"
  5. "reflect"
  6. "strconv"
  7. "github.com/qor/oss"
  8. "github.com/qor5/admin/utils"
  9. "github.com/theplant/sliceutils"
  10. "gorm.io/gorm"
  11. )
  12. type ListPublishBuilder struct {
  13. db *gorm.DB
  14. storage oss.StorageInterface
  15. context context.Context
  16. needNextPageFunc func(totalNumberPerPage, currentPageNumber, totalNumberOfItems int) bool
  17. getOldItemsFunc func(record interface{}) (result []interface{}, err error)
  18. totalNumberPerPage int
  19. publishActionsFunc func(db *gorm.DB, lp ListPublisher, result []*OnePageItems, indexPage *OnePageItems) (objs []*PublishAction)
  20. }
  21. func NewListPublishBuilder(db *gorm.DB, storage oss.StorageInterface) *ListPublishBuilder {
  22. return &ListPublishBuilder{
  23. db: db,
  24. storage: storage,
  25. context: context.Background(),
  26. needNextPageFunc: func(totalNumberPerPage, currentPageNumber, totalNumberOfItems int) bool {
  27. return currentPageNumber*totalNumberPerPage+int(0.5*float64(totalNumberPerPage)) <= totalNumberOfItems
  28. },
  29. getOldItemsFunc: func(record interface{}) (result []interface{}, err error) {
  30. err = db.Where("page_number <> ? ", 0).Find(&record).Error
  31. if err != nil && err != gorm.ErrRecordNotFound {
  32. return
  33. }
  34. return sliceutils.Wrap(record), nil
  35. },
  36. totalNumberPerPage: 30,
  37. publishActionsFunc: func(db *gorm.DB, lp ListPublisher, result []*OnePageItems, indexPage *OnePageItems) (objs []*PublishAction) {
  38. for _, onePageItems := range result {
  39. objs = append(objs, &PublishAction{
  40. Url: lp.GetListUrl(strconv.Itoa(onePageItems.PageNumber)),
  41. Content: lp.GetListContent(db, onePageItems),
  42. IsDelete: false,
  43. })
  44. }
  45. if indexPage != nil {
  46. objs = append(objs, &PublishAction{
  47. Url: lp.GetListUrl("index"),
  48. Content: lp.GetListContent(db, indexPage),
  49. IsDelete: false,
  50. })
  51. }
  52. return
  53. },
  54. }
  55. }
  56. func (b *ListPublishBuilder) WithValue(key, val interface{}) *ListPublishBuilder {
  57. b.context = context.WithValue(b.context, key, val)
  58. return b
  59. }
  60. func getAddItems(db *gorm.DB, record interface{}) (result []interface{}, err error) {
  61. err = db.Where("page_number = ? AND list_updated = ?", 0, true).Find(&record).Error
  62. if err != nil && err != gorm.ErrRecordNotFound {
  63. return
  64. }
  65. return sliceutils.Wrap(record), nil
  66. }
  67. func getDeleteItems(db *gorm.DB, record interface{}) (result []interface{}, err error) {
  68. err = db.Where("page_number <> ? AND list_deleted = ?", 0, true).Find(&record).Error
  69. if err != nil && err != gorm.ErrRecordNotFound {
  70. return
  71. }
  72. return sliceutils.Wrap(record), nil
  73. }
  74. func getRepublishItems(db *gorm.DB, record interface{}) (result []interface{}, err error) {
  75. err = db.Where("page_number <> ? AND list_updated = ?", 0, true).Find(&record).Error
  76. if err != nil && err != gorm.ErrRecordNotFound {
  77. return
  78. }
  79. return sliceutils.Wrap(record), nil
  80. }
  81. type ListPublisher interface {
  82. GetListUrl(pageNumber string) string
  83. GetListContent(db *gorm.DB, onePageItems *OnePageItems) string
  84. Sort(array []interface{})
  85. }
  86. // model is a empty struct
  87. // example: Product{}
  88. func (b *ListPublishBuilder) Run(model interface{}) (err error) {
  89. //If model is Product{}
  90. //Generate a records: []*Product{}
  91. records := reflect.MakeSlice(reflect.SliceOf(reflect.New(reflect.TypeOf(model)).Type()), 0, 0).Interface()
  92. addItems, err := getAddItems(b.db, records)
  93. if err != nil {
  94. return
  95. }
  96. deleteItems, err := getDeleteItems(b.db, records)
  97. if err != nil {
  98. return
  99. }
  100. republishItems, err := getRepublishItems(b.db, records)
  101. if err != nil {
  102. return
  103. }
  104. if len(deleteItems) == 0 && len(addItems) == 0 && len(republishItems) == 0 {
  105. return nil
  106. }
  107. oldItems, err := b.getOldItemsFunc(records)
  108. if err != nil {
  109. return
  110. }
  111. var newItems []interface{}
  112. if len(deleteItems) != 0 {
  113. var deleteMap = make(map[int][]int)
  114. for _, item := range deleteItems {
  115. lp := item.(ListInterface)
  116. deleteMap[lp.GetPageNumber()] = append(deleteMap[lp.GetPageNumber()], lp.GetPosition())
  117. }
  118. for _, item := range oldItems {
  119. lp := item.(ListInterface)
  120. if position, exist := deleteMap[lp.GetPageNumber()]; exist && utils.Contains(position, lp.GetPosition()) {
  121. continue
  122. }
  123. newItems = append(newItems, item)
  124. }
  125. } else {
  126. newItems = oldItems
  127. }
  128. if len(addItems) != 0 {
  129. newItems = append(newItems, addItems...)
  130. }
  131. lp := model.(ListPublisher)
  132. lp.Sort(newItems)
  133. var oldResult []*OnePageItems
  134. if len(oldItems) > 0 {
  135. oldResult = paginate(oldItems)
  136. }
  137. var republishResult []*OnePageItems
  138. if len(republishItems) > 0 {
  139. republishResult = paginate(republishItems)
  140. }
  141. newResult := rePaginate(newItems, b.totalNumberPerPage, b.needNextPageFunc)
  142. needPublishResults, indexResult := getNeedPublishResultsAndIndexResult(oldResult, newResult, republishResult)
  143. var objs []*PublishAction
  144. objs = b.publishActionsFunc(b.db, lp, needPublishResults, indexResult)
  145. err = utils.Transact(b.db, func(tx *gorm.DB) (err1 error) {
  146. if err1 = UploadOrDelete(objs, b.storage); err1 != nil {
  147. return
  148. }
  149. for _, items := range needPublishResults {
  150. for _, item := range items.Items {
  151. if listItem, ok := item.(ListInterface); ok {
  152. if err1 = b.db.Model(item).Updates(map[string]interface{}{
  153. "list_updated": listItem.GetListUpdated(),
  154. "list_deleted": listItem.GetListDeleted(),
  155. "page_number": listItem.GetPageNumber(),
  156. "position": listItem.GetPosition(),
  157. }).Error; err1 != nil {
  158. return
  159. }
  160. } else {
  161. return errors.New("model must be ListInterface")
  162. }
  163. }
  164. }
  165. for _, item := range deleteItems {
  166. if _, ok := item.(ListInterface); ok {
  167. if err1 = b.db.Model(item).Updates(map[string]interface{}{
  168. "list_updated": false,
  169. "list_deleted": false,
  170. "page_number": 0,
  171. "position": 0,
  172. }).Error; err1 != nil {
  173. return
  174. }
  175. } else {
  176. return errors.New("model must be ListInterface")
  177. }
  178. }
  179. return
  180. })
  181. return
  182. }
  183. func (b *ListPublishBuilder) NeedNextPageFunc(f func(totalNumberPerPage, currentPageNumber, totalNumberOfItems int) bool) *ListPublishBuilder {
  184. b.needNextPageFunc = f
  185. return b
  186. }
  187. func (b *ListPublishBuilder) GetOldItemsFunc(f func(record interface{}) (result []interface{}, err error)) *ListPublishBuilder {
  188. b.getOldItemsFunc = f
  189. return b
  190. }
  191. func (b *ListPublishBuilder) TotalNumberPerPage(number int) *ListPublishBuilder {
  192. b.totalNumberPerPage = number
  193. return b
  194. }
  195. func (b *ListPublishBuilder) PublishActionsFunc(f func(db *gorm.DB, lp ListPublisher, result []*OnePageItems, indexPage *OnePageItems) (objs []*PublishAction)) *ListPublishBuilder {
  196. b.publishActionsFunc = f
  197. return b
  198. }
  199. type OnePageItems struct {
  200. Items []interface{}
  201. PageNumber int
  202. }
  203. //Repaginate completely
  204. //Regardless of the PageNumber and Position of the old data
  205. //Resort and repaginate all data
  206. func rePaginate(array []interface{}, totalNumberPerPage int, needNextPageFunc func(itemsCountInOnePage, currentPageNumber, allItemsCount int) bool) (result []*OnePageItems) {
  207. var pageNumber int
  208. for i := 1; needNextPageFunc(totalNumberPerPage, i, len(array)); i++ {
  209. pageNumber = i
  210. var items = array[(i-1)*totalNumberPerPage : i*totalNumberPerPage]
  211. for k := range items {
  212. model := items[k].(ListInterface)
  213. model.SetPageNumber(pageNumber)
  214. model.SetPosition(k)
  215. model.SetListUpdated(false)
  216. model.SetListDeleted(false)
  217. }
  218. result = append(result, &OnePageItems{
  219. Items: items,
  220. PageNumber: pageNumber,
  221. })
  222. }
  223. var items = array[(pageNumber * totalNumberPerPage):]
  224. pageNumber = pageNumber + 1
  225. for k := range items {
  226. model := items[k].(ListInterface)
  227. model.SetPageNumber(pageNumber)
  228. model.SetPosition(k)
  229. model.SetListUpdated(false)
  230. model.SetListDeleted(false)
  231. }
  232. result = append(result, &OnePageItems{
  233. Items: items,
  234. PageNumber: pageNumber,
  235. })
  236. return
  237. }
  238. //For old data
  239. //Old data has PageNumber and Position
  240. //Sort pages according to the PageNumber and position
  241. func paginate(array []interface{}) (result []*OnePageItems) {
  242. lp := array[0].(ListPublisher)
  243. lp.Sort(array)
  244. var pageMap = make(map[int][]interface{})
  245. for _, item := range array {
  246. data := item.(ListInterface)
  247. pageMap[data.GetPageNumber()] = append(pageMap[data.GetPageNumber()], item)
  248. }
  249. for pageNumber, items := range pageMap {
  250. result = append(result, &OnePageItems{items, pageNumber})
  251. }
  252. return
  253. }
  254. //Compare new pages and old pages
  255. //Pick out the pages which are needed to republish
  256. func getNeedPublishResultsAndIndexResult(oldResults, newResults, republishResults []*OnePageItems) (needPublishResults []*OnePageItems, indexResult *OnePageItems) {
  257. if len(oldResults) == 0 {
  258. return newResults, newResults[len(newResults)-1]
  259. }
  260. var republishMap = make(map[int]bool)
  261. for _, republishResult := range republishResults {
  262. republishMap[republishResult.PageNumber] = true
  263. }
  264. for i, newResult := range newResults {
  265. // Add need publish pages to needPublishResults
  266. if _, exist := republishMap[newResult.PageNumber]; exist {
  267. needPublishResults = append(needPublishResults, newResult)
  268. if i == len(newResults)-1 {
  269. indexResult = newResult
  270. }
  271. continue
  272. }
  273. // Add new page whose page number over old page's max page number
  274. if i > len(oldResults)-1 {
  275. needPublishResults = append(needPublishResults, newResult)
  276. if i == len(newResults)-1 {
  277. indexResult = newResult
  278. }
  279. continue
  280. }
  281. // Compare new page and old page
  282. // If the items are different, add to needPublishResults
  283. if len(newResult.Items) == len(oldResults[i].Items) {
  284. for position, item := range newResult.Items {
  285. model := item.(ListInterface)
  286. oldModel := oldResults[i].Items[position].(ListInterface)
  287. if model.GetPosition() != oldModel.GetPosition() {
  288. needPublishResults = append(needPublishResults, newResult)
  289. if i == len(newResults)-1 {
  290. indexResult = newResult
  291. }
  292. continue
  293. }
  294. }
  295. } else {
  296. needPublishResults = append(needPublishResults, newResult)
  297. if i == len(newResults)-1 {
  298. indexResult = newResult
  299. }
  300. continue
  301. }
  302. }
  303. return
  304. }