diff --git a/cmd/spider/main.go b/cmd/spider/main.go index 5abf576..0100d9f 100644 --- a/cmd/spider/main.go +++ b/cmd/spider/main.go @@ -23,6 +23,7 @@ func newSpider() fx.Option { fx.Provide(spider.NewVideo), fx.Provide(spider.NewUpdate), fx.Provide(spider.NewPicture), + fx.Provide(spider.NewUpdateDynamic), fx.Provide(bilibili.NewSDK), fx.Provide(health.NewCheckServer), fx.Invoke(lc), @@ -34,6 +35,7 @@ func lc( spiderVideo *spider.Video, spiderUpdate *spider.Update, spiderPicture *spider.Picture, + spiderUpdatePicture *spider.UpdateDynamic, checkServer *health.CheckServer, shutdown fx.Shutdowner, ) { diff --git a/database/init.sql b/database/init.sql index 0b17e63..b79bc8c 100644 --- a/database/init.sql +++ b/database/init.sql @@ -109,3 +109,21 @@ INSERT INTO video_analysis (type, `key`, score) VALUES ('tag', '柚恩', 80); INSERT INTO video_analysis (type, `key`, score) VALUES ('tag', '柚恩不加糖', 100); INSERT INTO video_analysis (type, `key`, score) VALUES ('tag', 'EOE', 80); INSERT INTO video_analysis (type, `key`, score) VALUES ('tag', 'EOE组合', 100); + +create table bilibili_dynamics ( + id bigint unsigned not null auto_increment primary key comment 'id', + uid bigint unsigned not null comment 'B站用户id', + dynamic_id bigint unsigned not null comment 'B站动态id', + pictures json not null comment '图片', + topic_name varchar(64) comment '话题名称', + topic_id bigint unsigned not null comment '话题id', + view_nums bigint unsigned not null default '0' comment '看过的数量', + repost bigint unsigned not null default '0' comment '转发数量', + comment_nums bigint unsigned not null default '0' comment '评论数量', + favor bigint unsigned not null default '0' comment '点赞数量', + sent_at bigint unsigned not null comment '动态发生时间', + created_at bigint unsigned not null comment '创建时间', + updated_at bigint unsigned not null comment '更新时间', + index idx_dynamic_id(dynamic_id) comment '动态id索引', + index idx_sent_at(sent_at) comment '发送时间索引' +)Engine=InnoDB comment '动态' charset 'utf8mb4'; \ No newline at end of file diff --git a/internal/app/api/handler/bilbil_picture.go b/internal/app/api/handler/bilbil_picture.go new file mode 100644 index 0000000..7649a37 --- /dev/null +++ b/internal/app/api/handler/bilbil_picture.go @@ -0,0 +1,40 @@ +package handler + +import ( + "net/http" + + "git.vtb.link/eoefans/internal/app/api/apperrors" + "git.vtb.link/eoefans/internal/app/api/help" + "git.vtb.link/eoefans/internal/app/api/idl" + "git.vtb.link/eoefans/internal/app/api/service" + "github.com/gin-gonic/gin" +) + +func BilibiliLatestPics(s *service.BilbilPicture) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + var req idl.BilibiliPictureLatestReq + if err := ctx.ShouldBindQuery(&req); err != nil { + _ = ctx.Error(apperrors.NewValidationError(400, err.Error()).Wrap(err)) + return + } + + if resp, err := s.Latest(ctx, req); err != nil { + _ = ctx.Error(err) + return + } else { + ctx.JSON(http.StatusOK, help.SuccessJson(resp)) + } + } +} + +func BilibiliRecommendPics(s *service.BilbilPicture) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + var req idl.BilibiliPictureRecommendReq + if resp, err := s.Recommend(ctx,req); err != nil { + _ = ctx.Error(err) + return + } else { + ctx.JSON(http.StatusOK, help.SuccessJson(resp)) + } + } +} diff --git a/internal/app/api/idl/bilibili_picture.go b/internal/app/api/idl/bilibili_picture.go index 0101591..52d09da 100644 --- a/internal/app/api/idl/bilibili_picture.go +++ b/internal/app/api/idl/bilibili_picture.go @@ -1,26 +1,92 @@ package idl -type BilibiliPicture struct { - ID uint64 `gorm:"primarykey"` - Url string `gorm:"column:url"` - DynamicID uint64 `gorm:"column:dynamic_id"` - TopicName string `gorm:"column:topic_name"` - SentAt uint64 `gorm:"column:sent_at"` - CreatedAt uint64 `gorm:"autoCreateTime"` - UpdatedAt uint64 `gorm:"autoUpdateTime"` +import ( + "database/sql/driver" + "encoding/json" + "errors" + "time" +) + +// 图片来源于动态,以动态为单位 +type BilibiliDynamic struct { + ID uint64 `gorm:"primarykey"` + UID uint64 `gorm:"column:uid"` + DynamicID uint64 `gorm:"column:dynamic_id"` + Pictures BilibiliDynamicPictures `gorm:"column:pictures;"` + TopicName string `gorm:"column:topic_name"` + TopicID uint64 `gorm:"column:topic_id"` + View uint64 `gorm:"column:view_nums"` + Repost uint64 `gorm:"column:repost"` + Comment uint64 `gorm:"column:comment_nums"` + Like uint64 `gorm:"column:favor"` + SentAt uint64 `gorm:"column:sent_at"` + CreatedAt uint64 `gorm:"autoCreateTime"` + UpdatedAt uint64 `gorm:"autoUpdateTime"` +} + +func (p BilibiliDynamicPictures) Value() (driver.Value, error) { + return json.Marshal(p) +} + +func (c *BilibiliDynamicPictures) Scan(input interface{}) error { + data, ok := input.([]byte) + if !ok { + return errors.New("invalid input in Scan") + } + result := BilibiliDynamicPictures{} + err := json.Unmarshal(data, &result) + if err != nil { + return err + } + *c = result + return nil +} + +type BilibiliPictureLatestReq struct { + Page int `form:"page,default=1" binding:"omitempty,gt=0"` + TopicID int `form:"topic_id"` +} + +type BilibiliPictureRecommendReq struct { + TopicID int `form:"topic_id"` +} +type BilibiliPicturesCommonResp struct { + Result []*BilibiliDynamicDTO `json:"result"` +} +type BilibiliPicturesLatestResp struct { + BilibiliPicturesCommonResp + Page int `json:"page"` + Total int `json:"total"` +} + +type BilibiliPicturesRecommendResp struct { + BilibiliPicturesCommonResp + Total int `json:"total"` +} +type BilibiliDynamicDTO struct { + DynamicID uint64 `json:"dynamic_id"` + Pictures BilibiliDynamicPictures `json:"pictures"` + SentAt uint64 `json:"sent_at"` } -type BilibiliPictureDTO struct { - ID uint64 `json:"id"` - Url string `json:"url"` - CreatedAt uint64 `json:"created_at"` +type BilibiliDynamicPictures []BilibiliDynamicPicture +type BilibiliDynamicPicture struct { + Height float64 `json:"img_height"` + Size float64 `json:"img_size"` + Width float64 `json:"img_width"` + ImgSrc string `json:"image_src"` } -func (BilibiliPicture) TableName() string { - return "bilibili_pictures" +func (BilibiliDynamic) TableName() string { + return "bilibili_dynamics" } type BilibiliPictureRepository interface { - Create(items []*BilibiliPicture) error + Create(items []*BilibiliDynamic) error FindMaxDynamicID(topicName string) (*uint64, error) + Update(updates map[string]interface{}, dynamicID uint64) error + FindAllByPubDate(from, to time.Time, page, size int64) (list []*BilibiliDynamic, err error) + Latest(page, size, topicID int) (list []*BilibiliDynamic, err error) + //推荐暂时先默认50个 + Recommend(from, to time.Time, size, topicID int) (list []*BilibiliDynamic, err error) } diff --git a/internal/app/api/router/router.go b/internal/app/api/router/router.go index c4ccbe3..fee7fcb 100644 --- a/internal/app/api/router/router.go +++ b/internal/app/api/router/router.go @@ -15,6 +15,7 @@ func Provide() fx.Option { func InitRouters( bvService *service.BilbilVideo, + picService *service.BilbilPicture, authService *service.Auth, userService *service.User, errMiddlewares *middlewares.ErrorInterceptor, @@ -24,9 +25,14 @@ func InitRouters( // http 异常处理 r.Use(errMiddlewares.Handler) - // 视频搜素 + // 视频搜索 r.GET("/v1/video-interface/advanced-search", handler.BilibiliVideoSearch(bvService)) - + //图片 + picApi := r.Group("/v1/pic") + { + picApi.GET("/latest", handler.BilibiliLatestPics(picService)) + picApi.GET("/recommend", handler.BilibiliRecommendPics(picService)) + } // Auth相关 authApi := r.Group("/v1/auth") { diff --git a/internal/app/api/service/bilbil_picture.go b/internal/app/api/service/bilbil_picture.go new file mode 100644 index 0000000..d460ce3 --- /dev/null +++ b/internal/app/api/service/bilbil_picture.go @@ -0,0 +1,64 @@ +package service + +import ( + "context" + "time" + + "git.vtb.link/eoefans/internal/app/api/idl" + "git.vtb.link/eoefans/internal/repository" + "gorm.io/gorm" +) + +const ( + picRecommendDefaultSize = 50 +) + +type BilbilPicture struct { + db *gorm.DB +} + +func NewBilibiliPicture(db *gorm.DB) *BilbilPicture { + return &BilbilPicture{db: db} +} + +func (service *BilbilPicture) Latest(ctx context.Context, req idl.BilibiliPictureLatestReq) (*idl.BilibiliPicturesLatestResp, error) { + tx := service.db.WithContext(ctx) + picRepository := repository.NewBilibiliPicture(tx) + list, err := picRepository.Latest(req.Page, defaultQuerySize, req.TopicID) + if err != nil { + return nil, err + } + resp := idl.BilibiliPicturesLatestResp{ + Page: req.Page, + Total: len(list), + } + for i := range list { + resp.Result = append(resp.Result, &idl.BilibiliDynamicDTO{ + DynamicID: list[i].DynamicID, + Pictures: list[i].Pictures, + SentAt: list[i].SentAt, + }) + } + return &resp, nil +} + +func (service *BilbilPicture) Recommend(ctx context.Context, req idl.BilibiliPictureRecommendReq) (*idl.BilibiliPicturesRecommendResp, error) { + tx := service.db.WithContext(ctx) + picRepository := repository.NewBilibiliPicture(tx) + now := time.Now() + list, err := picRepository.Recommend(now.Add(-(3 * 24 * time.Hour)), now, picRecommendDefaultSize, req.TopicID) + if err != nil { + return nil, err + } + resp := idl.BilibiliPicturesRecommendResp{ + Total: len(list), + } + for i := range list { + resp.Result = append(resp.Result, &idl.BilibiliDynamicDTO{ + DynamicID: list[i].DynamicID, + Pictures: list[i].Pictures, + SentAt: list[i].SentAt, + }) + } + return &resp, nil +} diff --git a/internal/app/provide.go b/internal/app/provide.go index 60ddac0..2200e04 100644 --- a/internal/app/provide.go +++ b/internal/app/provide.go @@ -33,6 +33,7 @@ func MiddlewareProvider() fx.Option { func ServiceProvider() fx.Option { return fx.Provide( service.NewBilbilVideo, + service.NewBilibiliPicture, service.NewAuth, service.NewUser, ) diff --git a/internal/app/spider/picture.go b/internal/app/spider/picture.go index 013365b..30ca805 100644 --- a/internal/app/spider/picture.go +++ b/internal/app/spider/picture.go @@ -84,27 +84,27 @@ func (p *Picture) Run(ctx context.Context) error { func (p *Picture) spider() error { //把当前数据库最大的动态ID查出来 //调用接口,将大于当前动态ID的都入DB,如果存在小于的,则可提前结束,尽量保证没有重复数据 - topics := []string{ - bilibili.TopicNameGoGo, - bilibili.TopicNameMino, - bilibili.TopicNameUn, - bilibili.TopicNameMoMo, - bilibili.TopicNameWan, - bilibili.TopicNameEOE, + topicsMap := map[string]uint64{ + bilibili.TopicNameGoGo: bilibili.TopicIDGoGo, + bilibili.TopicNameMino: bilibili.TopicIDMino, + bilibili.TopicNameUn: bilibili.TopicIDUn, + bilibili.TopicNameMoMo: bilibili.TopicIDMoMo, + bilibili.TopicNameWan: bilibili.TopicIDWan, + bilibili.TopicNameEOE: bilibili.TopicIDEOE, } - for _, topic := range topics { - curMaxDynamicID, err := repository.NewBilibiliPicture(p.db).FindMaxDynamicID(topic) + for topicName, topicID := range topicsMap { + curMaxDynamicID, err := repository.NewBilibiliPicture(p.db).FindMaxDynamicID(topicName) if err != nil { - p.logger.Error("FindMaxDynamicID error", zap.String("topic_name", topic), zap.Error(err)) + p.logger.Error("FindMaxDynamicID error", zap.String("topic_name", topicName), zap.Error(err)) continue } var hasMore uint = 1 var offset uint64 = 0 exist := false //判断有没有已经爬过 for hasMore == 1 && !exist { - data, err := p.sdk.TopicDynamic(topic, offset) + data, err := p.sdk.TopicDynamics(topicName, offset) if err != nil { - p.logger.Error("TopicDynamic error", zap.String("topic_name", topic), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) + p.logger.Error("TopicDynamics error", zap.String("topic_name", topicName), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) time.Sleep(500 * time.Millisecond) break } @@ -115,29 +115,37 @@ func (p *Picture) spider() error { } else { hasMore = 0 } - items := make([]*idl.BilibiliPicture, 0) + items := make([]*idl.BilibiliDynamic, 0) for _, v := range data.Cards { switch v.Desc.Type { case bilibili.DynamicDraw: - dynamicID, _ := strconv.ParseUint(v.Desc.DynamicId, 10, 64) + dynamicID, _ := strconv.ParseUint(v.Desc.DynamicID, 10, 64) if dynamicID <= *curMaxDynamicID { //后面所有的都是爬过的,提前结束,后续也不再请求api exist = true break } + dynamic := &idl.BilibiliDynamic{ + UID: v.Desc.UID, + DynamicID: dynamicID, + TopicName: topicName, + TopicID: topicID, + View: v.Desc.View, + Repost: v.Desc.Repost, + Comment: v.Desc.Comment, + Like: v.Desc.Like, + SentAt: v.Desc.TimeStamp, + } pictures, err := parsePicturesFromCard(v.Card) if err != nil { - p.logger.Error("ParsePicturesFromCard error", zap.String("topic_name", topic), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) + p.logger.Error("ParsePicturesFromCard error", zap.String("topic_name", topicName), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) continue } - for _, url := range pictures { - items = append(items, &idl.BilibiliPicture{ - Url: url, - DynamicID: dynamicID, - TopicName: topic, - SentAt: v.Desc.TimeStamp, - }) + if len(pictures) == 0 { + continue } + dynamic.Pictures = pictures + items = append(items, dynamic) default: continue } @@ -146,7 +154,7 @@ func (p *Picture) spider() error { if len(items) != 0 { err := repository.NewBilibiliPicture(p.db).Create(items) if err != nil { - p.logger.Error("Create bilibli_pictures error", zap.String("topic_name", topic), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) + p.logger.Error("Create bilibli_pictures error", zap.String("topic_name", topicName), zap.String("offset", fmt.Sprintf("%d", offset)), zap.Error(err)) } } } @@ -154,14 +162,19 @@ func (p *Picture) spider() error { return nil } -func parsePicturesFromCard(data string) ([]string, error) { +func parsePicturesFromCard(data string) ([]idl.BilibiliDynamicPicture, error) { var content bilibili.DynamicCardContent if err := json.Unmarshal([]byte(data), &content); err != nil { return nil, err } - pics := make([]string, 0, len(content.Item.Pictures)) + pics := make([]idl.BilibiliDynamicPicture, 0, len(content.Item.Pictures)) for _, v := range content.Item.Pictures { - pics = append(pics, v.ImgSrc) + pics = append(pics, idl.BilibiliDynamicPicture{ + Height: v.Height, + Size: v.Size, + Width: v.Width, + ImgSrc: v.ImgSrc, + }) } return pics, nil } diff --git a/internal/app/spider/update_dynamic.go b/internal/app/spider/update_dynamic.go new file mode 100644 index 0000000..76986ab --- /dev/null +++ b/internal/app/spider/update_dynamic.go @@ -0,0 +1,104 @@ +package spider + +import ( + "context" + + "git.vtb.link/eoefans/internal/pkg/bilibili" + "git.vtb.link/eoefans/internal/repository" + + "time" + + "github.com/pkg/errors" + "go.uber.org/zap" + "gorm.io/gorm" +) + +type UpdateDynamic struct { + stopChan chan bool + db *gorm.DB + logger *zap.Logger + sdk *bilibili.SDK +} + +func NewUpdateDynamic(db *gorm.DB, logger *zap.Logger, sdk *bilibili.SDK) *Update { + return &Update{ + stopChan: make(chan bool), + db: db, + logger: logger, + sdk: sdk, + } +} + +func (u *UpdateDynamic) Stop(ctx context.Context) error { + u.logger.Info("stopping spider server") + + for { + select { + case <-ctx.Done(): + return errors.New("shutdown dynamic update spider server timeout") + default: + close(u.stopChan) + return nil + } + } +} + +func (u *UpdateDynamic) Run(ctx context.Context) error { + go func() { + if err := u.spider(); err != nil { + u.logger.Error("start dynamic update error", zap.Error(err)) + } + }() + + tk := time.NewTicker(180 * time.Minute) + go func(_tk *time.Ticker) { + for { + select { + case <-_tk.C: + u.logger.Info("[tick] dynamic update spider", zap.Time("time", time.Now())) + if err := u.spider(); err != nil { + u.logger.Error("start dynamic update error", zap.Error(err)) + } + case <-u.stopChan: + return + } + } + }(tk) + + return nil +} + +func (u *UpdateDynamic) spider() error { + tx := u.db.WithContext(context.TODO()) + repo := repository.NewBilibiliPicture(tx) + + size := 100 + for p := 1; true; p++ { + list, err := repo.FindAllByPubDate(time.Now().Add(-(3 * 24 * time.Hour)), time.Now(), int64(p), int64(size)) + if err != nil { + u.logger.Error("[UpdateDynamic spider()]FindAllByPubDate error", zap.Int("page", p), zap.Error(err)) + return nil + } + for _, v := range list { + dynamicCard, err := u.sdk.Dynamic(v.DynamicID) + if err != nil { + u.logger.Error("Dynamic error", zap.Int("dynamic_id", int(v.DynamicID)), zap.Error(err)) + continue + } + updates := map[string]interface{}{ + "view": dynamicCard.Desc.View, + "repost": dynamicCard.Desc.Repost, + "comment": dynamicCard.Desc.Comment, + "like": dynamicCard.Desc.Like, + } + if err := repo.Update(updates, v.DynamicID); err != nil { + u.logger.Error("Dynamic Update error", zap.Int("dynamic_id", int(v.DynamicID)), zap.Error(err)) + } + } + if len(list) < size { + break + } + } + + return nil +} diff --git a/internal/pkg/bilibili/video.go b/internal/pkg/bilibili/video.go index a5b82dc..8d8b3b8 100644 --- a/internal/pkg/bilibili/video.go +++ b/internal/pkg/bilibili/video.go @@ -16,6 +16,7 @@ const ( webVideoInfoURL = "https://api.bilibili.com/x/web-interface/view?bvid=%s" webVideoTagInfoURL = "https://api.bilibili.com/x/web-interface/view/detail/tag?aid=%s" topicHistory = "https://api.vc.bilibili.com/topic_svr/v1/topic_svr/topic_history?offset_dynamic_id=%d&" + dynamic = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=%d" ) const ( @@ -35,6 +36,13 @@ const ( TopicNameMoMo = "虞你在一起" TopicNameMino = "和米诺的对抗路日常" TopicNameEOE = "EOE的魔法盒" + + TopicIDWan uint64 = 28953983 + TopicIDUn uint64 = 28950030 + TopicIDGoGo uint64 = 29067608 + TopicIDMoMo uint64 = 28948378 + TopicIDMino uint64 = 29069147 + TopicIDEOE uint64 = 29156150 ) type SDK struct { @@ -61,7 +69,12 @@ type DynamicInfo struct { type DynamicCard struct { Desc struct { Type DynamicType `json:"type"` - DynamicId string `json:"dynamic_id"` + View uint64 `json:"view"` + Repost uint64 `json:"repost"` + Comment uint64 `json:"comment"` + Like uint64 `json:"like"` + UID uint64 `json:"uid"` + DynamicID string `json:"dynamic_id"` TimeStamp uint64 `json:"timestamp"` } `json:"desc"` Card string `json:"card"` @@ -337,12 +350,19 @@ func (sdk *SDK) VideoWebTagInfo(aid string) (data *VideoTagResponse, err error) return data, nil } -func (sdk *SDK) TopicDynamic(topicName string, offsetDynamicId uint64) (data *DynamicInfo, err error) { +func (sdk *SDK) TopicDynamics(topicName string, offsetDynamicId uint64) (data *DynamicInfo, err error) { params := url.Values{} params.Add("topic_name", topicName) url := fmt.Sprintf(topicHistory, offsetDynamicId) url = url + params.Encode() - fmt.Println(url) + if err = sdk.fastGet(url, &data); err != nil { + return nil, err + } + return data, nil +} + +func (sdk *SDK) Dynamic(dynamicId uint64) (data *DynamicCard, err error) { + url := fmt.Sprintf(topicHistory, dynamicId) if err = sdk.fastGet(url, &data); err != nil { return nil, err } diff --git a/internal/repository/bilibili_picture.go b/internal/repository/bilibili_picture.go index 5d63380..2b083e7 100644 --- a/internal/repository/bilibili_picture.go +++ b/internal/repository/bilibili_picture.go @@ -1,6 +1,8 @@ package repository import ( + "time" + "git.vtb.link/eoefans/internal/app/api/idl" "gorm.io/gorm" ) @@ -13,48 +15,69 @@ type BilibiliPictureMysqlImpl struct { tx *gorm.DB } -func (impl *BilibiliPictureMysqlImpl) Create(items []*idl.BilibiliPicture) error { +func (impl *BilibiliPictureMysqlImpl) Create(items []*idl.BilibiliDynamic) error { if len(items) == 0 { return nil } - //针对url去重 - return impl.tx.Transaction(func(_tx *gorm.DB) error { - urls := make([]string, 0, len(items)) - for _, v := range items { - urls = append(urls, v.Url) - } - var exist []*idl.BilibiliPicture - err := _tx.Table(idl.BilibiliPicture{}.TableName()).Where("url in (?)", urls).Distinct("url").Find(&exist).Error - if err != nil { - return err - } - filter := make([]*idl.BilibiliPicture, 0) - for i := range items { - find := false - for j := range exist { - if items[i].Url == exist[j].Url { - find = true - break - } - } - if !find { - filter = append(filter, items[i]) - } - } - err = _tx.Table(idl.BilibiliPicture{}.TableName()).Create(&filter).Error - if err != nil { - return err - } + return impl.tx.Table(idl.BilibiliDynamic{}.TableName()).Create(&items).Error +} + +func (impl *BilibiliPictureMysqlImpl) Update(updates map[string]interface{}, dynamicID uint64) error { + if len(updates) == 0 { return nil - }) + } + return impl.tx.Table(idl.BilibiliDynamic{}.TableName()).Where("dynamic_id=?", dynamicID).Updates(updates).Error } func (impl *BilibiliPictureMysqlImpl) FindMaxDynamicID(topicName string) (*uint64, error) { var id uint64 - conn := impl.tx.Table(idl.BilibiliPicture{}.TableName()) + conn := impl.tx.Table(idl.BilibiliDynamic{}.TableName()) err := conn.Select("max(dynamic_id) as id").Where("topic_name = ?", topicName).Group("dynamic_id").Scan(&id).Error if err != nil { return nil, err } return &id, nil } + +func (impl *BilibiliPictureMysqlImpl) FindAllByPubDate(from, to time.Time, page, size int64) (list []*idl.BilibiliDynamic, err error) { + err = impl.tx.Table(idl.BilibiliDynamic{}.TableName()). + Where("sent_at >= ? AND sent_at <= ?", from.Unix(), to.Unix()). + Select("dynamic_id"). + Offset(int((page - 1) * size)).Limit(int(size)). + Order("sent_at DESC"). + Find(&list).Error + if err != nil { + return nil, err + } + return list, nil +} + +func (impl *BilibiliPictureMysqlImpl) Latest(page, size, topicID int) (list []*idl.BilibiliDynamic, err error) { + conn := impl.tx.Table(idl.BilibiliDynamic{}.TableName()) + if topicID != 0 { + conn = conn.Where("topic_id = ?", topicID) + } + err = conn.Select("dynamic_id,pictures,sent_at"). + Order("sent_at DESC"). + Offset((page - 1) * size). + Limit(size).Find(&list).Error + if err != nil { + return nil, err + } + return list, nil +} + +func (impl *BilibiliPictureMysqlImpl) Recommend(from, to time.Time, size, topicID int) (list []*idl.BilibiliDynamic, err error) { + conn := impl.tx.Table(idl.BilibiliDynamic{}.TableName()) + if topicID != 0 { + conn = conn.Where("topic_id = ?", topicID) + } + err = conn.Select("dynamic_id,pictures,sent_at"). + Where("sent_at >= ? AND sent_at <= ?", from.Unix(), to.Unix()). + Order("favor DESC"). + Limit(size).Find(&list).Error + if err != nil { + return nil, err + } + return list, nil +}