123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- package publish
- import (
- "context"
- "fmt"
- "reflect"
- "strings"
- "sync"
- "github.com/iancoleman/strcase"
- "github.com/qor/oss"
- "github.com/qor5/admin/utils"
- "gorm.io/gorm"
- "gorm.io/gorm/schema"
- )
- type Builder struct {
- db *gorm.DB
- storage oss.StorageInterface
- context context.Context
- }
- func New(db *gorm.DB, storage oss.StorageInterface) *Builder {
- return &Builder{
- db: db,
- storage: storage,
- context: context.Background(),
- }
- }
- func (b *Builder) WithValue(key, val interface{}) *Builder {
- b.context = context.WithValue(b.context, key, val)
- return b
- }
- const (
- PublishContextKeyPageBuilder = "pagebuilder"
- PublishContextKeyL10nBuilder = "l10nbuilder"
- PublishContextKeyEventContext = "eventcontext"
- )
- func (b *Builder) WithPageBuilder(val interface{}) *Builder {
- b.context = context.WithValue(b.context, PublishContextKeyPageBuilder, val)
- return b
- }
- func (b *Builder) WithL10nBuilder(val interface{}) *Builder {
- b.context = context.WithValue(b.context, PublishContextKeyL10nBuilder, val)
- return b
- }
- func (b *Builder) WithEventContext(val interface{}) *Builder {
- b.context = context.WithValue(b.context, PublishContextKeyEventContext, val)
- return b
- }
- func (b *Builder) Context() context.Context {
- return b.context
- }
- // 幂等
- func (b *Builder) Publish(record interface{}) (err error) {
- err = utils.Transact(b.db, func(tx *gorm.DB) (err error) {
- // publish content
- if r, ok := record.(PublishInterface); ok {
- var objs []*PublishAction
- objs, err = r.GetPublishActions(b.db, b.context, b.storage)
- if err != nil {
- return
- }
- if err = UploadOrDelete(objs, b.storage); err != nil {
- return
- }
- }
- // update status
- if r, ok := record.(StatusInterface); ok {
- now := b.db.NowFunc()
- if version, ok := record.(VersionInterface); ok {
- var modelSchema *schema.Schema
- modelSchema, err = schema.Parse(record, &sync.Map{}, b.db.NamingStrategy)
- if err != nil {
- return
- }
- scope := SetPrimaryKeysConditionWithoutVersion(b.db.Model(reflect.New(modelSchema.ModelType).Interface()), record, modelSchema).Where("version <> ? AND status = ?", version.GetVersion(), StatusOnline)
- var count int64
- if err = scope.Count(&count).Error; err != nil {
- return
- }
- // update old version
- if count > 0 {
- var oldVersionUpdateMap = make(map[string]interface{})
- if _, ok := record.(ScheduleInterface); ok {
- oldVersionUpdateMap["scheduled_end_at"] = nil
- oldVersionUpdateMap["actual_end_at"] = &now
- }
- if _, ok := record.(ListInterface); ok {
- oldVersionUpdateMap["list_deleted"] = true
- }
- oldVersionUpdateMap["status"] = StatusOffline
- if err = scope.Updates(oldVersionUpdateMap).Error; err != nil {
- return
- }
- }
- }
- var updateMap = make(map[string]interface{})
- if r, ok := record.(ScheduleInterface); ok {
- r.SetPublishedAt(&now)
- r.SetScheduledStartAt(nil)
- updateMap["scheduled_start_at"] = r.GetScheduledStartAt()
- updateMap["actual_start_at"] = r.GetPublishedAt()
- }
- if _, ok := record.(ListInterface); ok {
- updateMap["list_updated"] = true
- }
- updateMap["status"] = StatusOnline
- updateMap["online_url"] = r.GetOnlineUrl()
- if err = b.db.Model(record).Updates(updateMap).Error; err != nil {
- return
- }
- }
- // publish callback
- if r, ok := record.(AfterPublishInterface); ok {
- if err = r.AfterPublish(b.db, b.storage, b.context); err != nil {
- return
- }
- }
- return
- })
- return
- }
- func (b *Builder) UnPublish(record interface{}) (err error) {
- err = utils.Transact(b.db, func(tx *gorm.DB) (err error) {
- // unpublish content
- if r, ok := record.(UnPublishInterface); ok {
- var objs []*PublishAction
- objs, err = r.GetUnPublishActions(b.db, b.context, b.storage)
- if err != nil {
- return
- }
- if err = UploadOrDelete(objs, b.storage); err != nil {
- return
- }
- }
- // update status
- if _, ok := record.(StatusInterface); ok {
- var updateMap = make(map[string]interface{})
- if r, ok := record.(ScheduleInterface); ok {
- now := b.db.NowFunc()
- r.SetUnPublishedAt(&now)
- r.SetScheduledEndAt(nil)
- updateMap["scheduled_end_at"] = r.GetScheduledEndAt()
- updateMap["actual_end_at"] = r.GetUnPublishedAt()
- }
- if _, ok := record.(ListInterface); ok {
- updateMap["list_deleted"] = true
- }
- updateMap["status"] = StatusOffline
- if err = b.db.Model(record).Updates(updateMap).Error; err != nil {
- return
- }
- }
- // unpublish callback
- if r, ok := record.(AfterUnPublishInterface); ok {
- if err = r.AfterUnPublish(b.db, b.storage, b.context); err != nil {
- return
- }
- }
- return
- })
- return
- }
- func (b *Builder) Sync(models ...interface{}) error {
- return nil
- }
- func UploadOrDelete(objs []*PublishAction, storage oss.StorageInterface) (err error) {
- for _, obj := range objs {
- if obj.IsDelete {
- fmt.Printf("deleting %s \n", obj.Url)
- err = storage.Delete(obj.Url)
- } else {
- fmt.Printf("uploading %s \n", obj.Url)
- _, err = storage.Put(obj.Url, strings.NewReader(obj.Content))
- }
- if err != nil {
- return
- }
- }
- return nil
- }
- func SetPrimaryKeysConditionWithoutVersion(db *gorm.DB, record interface{}, s *schema.Schema) *gorm.DB {
- querys := []string{}
- args := []interface{}{}
- for _, p := range s.PrimaryFields {
- if p.Name == "Version" {
- continue
- }
- val, _ := p.ValueOf(db.Statement.Context, reflect.ValueOf(record))
- querys = append(querys, fmt.Sprintf("%s = ?", strcase.ToSnake(p.Name)))
- args = append(args, val)
- }
- return db.Where(strings.Join(querys, " AND "), args...)
- }
|