activity.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. package activity
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. "github.com/qor5/admin/presets"
  8. "github.com/qor5/web"
  9. "gorm.io/gorm"
  10. )
  11. const (
  12. Create = 1 << iota
  13. Delete
  14. Update
  15. )
  16. type contextKey int
  17. const (
  18. CreatorContextKey contextKey = iota
  19. DBContextKey
  20. )
  21. // @snippet_begin(ActivityBuilder)
  22. type ActivityBuilder struct {
  23. db *gorm.DB // global db
  24. creatorContextKey interface{} // get the creator from context
  25. dbContextKey interface{} // get the db from context
  26. lmb *presets.ModelBuilder // log model builder
  27. logModel ActivityLogInterface // log model
  28. models []*ModelBuilder // registered model builders
  29. tabHeading func(ActivityLogInterface) string // tab heading format
  30. }
  31. // @snippet_end
  32. func New(b *presets.Builder, db *gorm.DB, logModel ...ActivityLogInterface) *ActivityBuilder {
  33. ab := &ActivityBuilder{
  34. db: db,
  35. creatorContextKey: CreatorContextKey,
  36. dbContextKey: DBContextKey,
  37. }
  38. if len(logModel) > 0 {
  39. ab.logModel = logModel[0]
  40. } else {
  41. ab.logModel = &ActivityLog{}
  42. }
  43. if err := db.AutoMigrate(ab.logModel); err != nil {
  44. panic(err)
  45. }
  46. ab.configureAdmin(b)
  47. return ab
  48. }
  49. // GetPresetModelBuilder return the preset model builder
  50. func (ab ActivityBuilder) GetPresetModelBuilder() *presets.ModelBuilder {
  51. return ab.lmb
  52. }
  53. // GetActivityLogs get activity logs
  54. func (ab ActivityBuilder) GetActivityLogs(m interface{}, db *gorm.DB) []*ActivityLog {
  55. objs := ab.GetCustomizeActivityLogs(m, db)
  56. if objs == nil {
  57. return nil
  58. }
  59. logs, ok := objs.(*[]*ActivityLog)
  60. if !ok {
  61. return nil
  62. }
  63. return *logs
  64. }
  65. // GetCustomizeActivityLogs get customize activity logs
  66. func (ab ActivityBuilder) GetCustomizeActivityLogs(m interface{}, db *gorm.DB) interface{} {
  67. mb, ok := ab.GetModelBuilder(m)
  68. if !ok {
  69. return nil
  70. }
  71. if db == nil {
  72. db = ab.db
  73. }
  74. keys := mb.KeysValue(m)
  75. logs := ab.NewLogModelSlice()
  76. err := db.Where("model_name = ? AND model_keys = ?", mb.typ.Name(), keys).Find(logs).Error
  77. if err != nil {
  78. return nil
  79. }
  80. return logs
  81. }
  82. // NewLogModelData new a log model data
  83. func (ab ActivityBuilder) NewLogModelData() interface{} {
  84. return reflect.New(reflect.Indirect(reflect.ValueOf(ab.logModel)).Type()).Interface()
  85. }
  86. // NewLogModelSlice new a log model slice
  87. func (ab ActivityBuilder) NewLogModelSlice() interface{} {
  88. sliceType := reflect.SliceOf(reflect.PtrTo(reflect.Indirect(reflect.ValueOf(ab.logModel)).Type()))
  89. slice := reflect.New(sliceType)
  90. slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
  91. return slice.Interface()
  92. }
  93. // SetCreatorContextKey change the default creator context key
  94. func (ab *ActivityBuilder) SetCreatorContextKey(key interface{}) *ActivityBuilder {
  95. ab.creatorContextKey = key
  96. return ab
  97. }
  98. // SetDBContextKey change the default db context key
  99. func (ab *ActivityBuilder) SetDBContextKey(key interface{}) *ActivityBuilder {
  100. ab.dbContextKey = key
  101. return ab
  102. }
  103. func (ab *ActivityBuilder) SetTabHeading(f func(log ActivityLogInterface) string) *ActivityBuilder {
  104. ab.tabHeading = f
  105. return ab
  106. }
  107. // RegisterModels register mutiple models
  108. func (ab *ActivityBuilder) RegisterModels(models ...interface{}) *ActivityBuilder {
  109. for _, model := range models {
  110. ab.RegisterModel(model)
  111. }
  112. return ab
  113. }
  114. // Model register a model and return model builder
  115. func (ab *ActivityBuilder) RegisterModel(m interface{}) (mb *ModelBuilder) {
  116. if m, exist := ab.GetModelBuilder(m); exist {
  117. return m
  118. }
  119. model := getBasicModel(m)
  120. if model == nil {
  121. panic(fmt.Sprintf("%v is nil", m))
  122. }
  123. reflectType := reflect.Indirect(reflect.ValueOf(model)).Type()
  124. if reflectType.Kind() != reflect.Struct {
  125. panic(fmt.Sprintf("%v is not a struct", reflectType.Name()))
  126. }
  127. keys := getPrimaryKey(reflectType)
  128. mb = &ModelBuilder{
  129. typ: reflectType,
  130. activity: ab,
  131. keys: keys,
  132. ignoredFields: keys,
  133. }
  134. ab.models = append(ab.models, mb)
  135. if presetModel, ok := m.(*presets.ModelBuilder); ok {
  136. mb.presetModel = presetModel
  137. var (
  138. editing = presetModel.Editing()
  139. oldSaver = editing.Saver
  140. oldDeleter = editing.Deleter
  141. )
  142. editing.SaveFunc(func(obj interface{}, id string, ctx *web.EventContext) (err error) {
  143. if mb.skip&Update != 0 && mb.skip&Create != 0 {
  144. return oldSaver(obj, id, ctx)
  145. }
  146. old, ok := findOld(obj, ab.getDBFromContext(ctx.R.Context()))
  147. if err = oldSaver(obj, id, ctx); err != nil {
  148. return err
  149. }
  150. if (!ok || id == "") && mb.skip&Create == 0 {
  151. return mb.AddRecords(ActivityCreate, ctx.R.Context(), obj)
  152. }
  153. if ok && id != "" && mb.skip&Update == 0 {
  154. return mb.AddEditRecordWithOld(ab.getCreatorFromContext(ctx.R.Context()), old, obj, ab.getDBFromContext(ctx.R.Context()))
  155. }
  156. return
  157. })
  158. editing.DeleteFunc(func(obj interface{}, id string, ctx *web.EventContext) (err error) {
  159. if mb.skip&Delete != 0 {
  160. return oldDeleter(obj, id, ctx)
  161. }
  162. old, ok := findOldWithSlug(obj, id, ab.getDBFromContext(ctx.R.Context()))
  163. if err = oldDeleter(obj, id, ctx); err != nil {
  164. return err
  165. }
  166. if ok {
  167. return mb.AddRecords(ActivityDelete, ctx.R.Context(), old)
  168. }
  169. return
  170. })
  171. }
  172. return mb
  173. }
  174. // GetModelBuilder get model builder
  175. func (ab ActivityBuilder) GetModelBuilder(v interface{}) (*ModelBuilder, bool) {
  176. var isPreset bool
  177. if _, ok := v.(*presets.ModelBuilder); ok {
  178. isPreset = true
  179. }
  180. typ := reflect.Indirect(reflect.ValueOf(getBasicModel(v))).Type()
  181. for _, m := range ab.models {
  182. if m.typ == typ {
  183. if !isPreset {
  184. return m, true
  185. }
  186. if isPreset && m.presetModel == v {
  187. return m, true
  188. }
  189. }
  190. }
  191. return &ModelBuilder{}, false
  192. }
  193. // GetModelBuilder get model builder
  194. func (ab ActivityBuilder) MustGetModelBuilder(v interface{}) *ModelBuilder {
  195. mb, ok := ab.GetModelBuilder(v)
  196. if !ok {
  197. panic(fmt.Sprintf("model %v is not registered", v))
  198. }
  199. return mb
  200. }
  201. // GetModelBuilders get all model builders
  202. func (ab ActivityBuilder) GetModelBuilders() []*ModelBuilder {
  203. return ab.models
  204. }
  205. // AddRecords add records log
  206. func (ab *ActivityBuilder) AddRecords(action string, ctx context.Context, vs ...interface{}) error {
  207. if len(vs) == 0 {
  208. return errors.New("data are empty")
  209. }
  210. for _, v := range vs {
  211. if mb, ok := ab.GetModelBuilder(v); ok {
  212. if err := mb.AddRecords(action, ctx, v); err != nil {
  213. return err
  214. }
  215. }
  216. }
  217. return nil
  218. }
  219. // AddCustomizedRecord add customized record
  220. func (ab *ActivityBuilder) AddCustomizedRecord(action string, diff bool, ctx context.Context, obj interface{}) error {
  221. if mb, ok := ab.GetModelBuilder(obj); ok {
  222. return mb.AddCustomizedRecord(action, diff, ctx, obj)
  223. }
  224. return fmt.Errorf("can't find model builder for %v", obj)
  225. }
  226. // AddViewRecord add view record
  227. func (ab *ActivityBuilder) AddViewRecord(creator interface{}, v interface{}, db *gorm.DB) error {
  228. if mb, ok := ab.GetModelBuilder(v); ok {
  229. return mb.AddViewRecord(creator, v, db)
  230. }
  231. return fmt.Errorf("can't find model builder for %v", v)
  232. }
  233. // AddDeleteRecord add delete record
  234. func (ab *ActivityBuilder) AddDeleteRecord(creator interface{}, v interface{}, db *gorm.DB) error {
  235. if mb, ok := ab.GetModelBuilder(v); ok {
  236. return mb.AddDeleteRecord(creator, v, db)
  237. }
  238. return fmt.Errorf("can't find model builder for %v", v)
  239. }
  240. // AddSaverRecord will save a create log or a edit log
  241. func (ab *ActivityBuilder) AddSaveRecord(creator interface{}, now interface{}, db *gorm.DB) error {
  242. if mb, ok := ab.GetModelBuilder(now); ok {
  243. return mb.AddSaveRecord(creator, now, db)
  244. }
  245. return fmt.Errorf("can't find model builder for %v", now)
  246. }
  247. // AddCreateRecord add create record
  248. func (ab *ActivityBuilder) AddCreateRecord(creator interface{}, v interface{}, db *gorm.DB) error {
  249. if mb, ok := ab.GetModelBuilder(v); ok {
  250. return mb.AddCreateRecord(creator, v, db)
  251. }
  252. return fmt.Errorf("can't find model builder for %v", v)
  253. }
  254. // AddEditRecord add edit record
  255. func (ab *ActivityBuilder) AddEditRecord(creator interface{}, now interface{}, db *gorm.DB) error {
  256. if mb, ok := ab.GetModelBuilder(now); ok {
  257. return mb.AddEditRecord(creator, now, db)
  258. }
  259. return fmt.Errorf("can't find model builder for %v", now)
  260. }
  261. // AddEditRecord add edit record
  262. func (ab *ActivityBuilder) AddEditRecordWithOld(creator interface{}, old, now interface{}, db *gorm.DB) error {
  263. if mb, ok := ab.GetModelBuilder(now); ok {
  264. return mb.AddEditRecordWithOld(creator, old, now, db)
  265. }
  266. return fmt.Errorf("can't find model builder for %v", now)
  267. }
  268. // AddEditRecordWithOldAndContext add edit record
  269. func (ab *ActivityBuilder) AddEditRecordWithOldAndContext(ctx context.Context, old, now interface{}) error {
  270. if mb, ok := ab.GetModelBuilder(now); ok {
  271. return mb.AddEditRecordWithOld(ab.getCreatorFromContext(ctx), old, now, ab.getDBFromContext(ctx))
  272. }
  273. return fmt.Errorf("can't find model builder for %v", now)
  274. }
  275. // GetDB get db from context
  276. func (ab *ActivityBuilder) getDBFromContext(ctx context.Context) *gorm.DB {
  277. if contextdb := ctx.Value(ab.dbContextKey); contextdb != nil {
  278. return contextdb.(*gorm.DB)
  279. }
  280. return ab.db
  281. }
  282. // GetDB get creator from context
  283. func (ab *ActivityBuilder) getCreatorFromContext(ctx context.Context) interface{} {
  284. if creator := ctx.Value(ab.creatorContextKey); creator != nil {
  285. return creator
  286. }
  287. return ""
  288. }