sqlx: implements topic data logics

pull/351/head
Michael Li 2 years ago
parent 9ac483f996
commit dda5465d60
No known key found for this signature in database

@ -4,19 +4,42 @@
package core package core
import ( const (
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr" TagCategoryHot TagCategory = "hot"
TagCategoryNew TagCategory = "new"
) )
type ( type TagCategory string
Tag = dbr.Tag
TagFormated = dbr.TagFormated type Tag struct {
) ID int64 `json:"id" db:"id"`
UserID int64 `json:"user_id" db:"user_id"`
Tag string `json:"tag"`
QuoteNum int64 `json:"quote_num" db:"quote_num"`
}
type TagFormated struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
User *UserFormated `json:"user"`
Tag string `json:"tag"`
QuoteNum int64 `json:"quote_num"`
}
// TopicService 话题服务 // TopicService 话题服务
type TopicService interface { type TopicService interface {
CreateTag(tag *Tag) (*Tag, error) UpsertTags(userId int64, tags []string) ([]*Tag, error)
DeleteTag(tag *Tag) error DecrTagsById(ids []int64) error
GetTags(conditions *ConditionsT, offset, limit int) ([]*Tag, error) GetTags(category TagCategory, offset int, limit int) ([]*Tag, error)
GetTagsByKeyword(keyword string) ([]*Tag, error) GetTagsByKeyword(keyword string) ([]*Tag, error)
} }
func (t *Tag) Format() *TagFormated {
return &TagFormated{
ID: t.ID,
UserID: t.UserID,
User: &UserFormated{},
Tag: t.Tag,
QuoteNum: t.QuoteNum,
}
}

@ -71,9 +71,7 @@ func (t *Tag) Delete(db *gorm.DB) error {
}).Error }).Error
} }
func (t *Tag) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*Tag, error) { func (t *Tag) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) (tags []*Tag, err error) {
var tags []*Tag
var err error
if offset >= 0 && limit > 0 { if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit) db = db.Offset(offset).Limit(limit)
} }
@ -87,12 +85,8 @@ func (t *Tag) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*
db = db.Where(k, v) db = db.Where(k, v)
} }
} }
err = db.Where("is_del = 0 and quote_num > 0").Find(&tags).Error
if err = db.Where("is_del = 0 and quote_num > 0").Find(&tags).Error; err != nil { return
return nil, err
}
return tags, nil
} }
func (t *Tag) TagsFrom(db *gorm.DB, tags []string) (res []*Tag, err error) { func (t *Tag) TagsFrom(db *gorm.DB, tags []string) (res []*Tag, err error) {

@ -26,30 +26,82 @@ func newTopicService(db *gorm.DB) core.TopicService {
} }
} }
func (s *topicServant) CreateTag(tag *core.Tag) (*core.Tag, error) { func (s *topicServant) UpsertTags(userId int64, tags []string) (_ []*core.Tag, err error) {
return createTag(s.db, tag) db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
return createTags(db, userId, tags)
} }
func (s *topicServant) DeleteTag(tag *core.Tag) error { func (s *topicServant) DecrTagsById(ids []int64) (err error) {
return deleteTag(s.db, tag) db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
return decrTagsByIds(db, ids)
} }
func (s *topicServant) GetTags(conditions *core.ConditionsT, offset, limit int) ([]*core.Tag, error) { func (s *topicServant) GetTags(category core.TagCategory, offset, limit int) (res []*core.Tag, err error) {
return (&dbr.Tag{}).List(s.db, conditions, offset, limit) conditions := &core.ConditionsT{}
switch category {
case core.TagCategoryHot:
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "quote_num DESC",
}
case core.TagCategoryNew:
// 最新标签
conditions = &core.ConditionsT{
"ORDER": "id DESC",
}
}
// TODO: 优化查询方式,直接返回[]*core.Tag, 目前保持先转换一下
var tags []*dbr.Tag
if tags, err = (&dbr.Tag{}).List(s.db, conditions, offset, limit); err == nil {
for _, tag := range tags {
res = append(res, &core.Tag{
ID: tag.ID,
UserID: tag.UserID,
Tag: tag.Tag,
QuoteNum: tag.QuoteNum,
})
}
}
return
} }
func (s *topicServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) { func (s *topicServant) GetTagsByKeyword(keyword string) (res []*core.Tag, err error) {
tag := &dbr.Tag{}
keyword = "%" + strings.Trim(keyword, " ") + "%" keyword = "%" + strings.Trim(keyword, " ") + "%"
tag := &dbr.Tag{}
var tags []*dbr.Tag
if keyword == "%%" { if keyword == "%%" {
return tag.List(s.db, &dbr.ConditionsT{ tags, err = tag.List(s.db, &dbr.ConditionsT{
"ORDER": "quote_num DESC", "ORDER": "quote_num DESC",
}, 0, 6) }, 0, 6)
} else { } else {
return tag.List(s.db, &dbr.ConditionsT{ tags, err = tag.List(s.db, &dbr.ConditionsT{
"tag LIKE ?": keyword, "tag LIKE ?": keyword,
"ORDER": "quote_num DESC", "ORDER": "quote_num DESC",
}, 0, 6) }, 0, 6)
} }
if err == nil {
for _, tag := range tags {
res = append(res, &core.Tag{
ID: tag.ID,
UserID: tag.UserID,
Tag: tag.Tag,
QuoteNum: tag.QuoteNum,
})
}
}
return
} }

@ -288,21 +288,15 @@ func (s *tweetManageServant) VisiblePost(post *core.Post, visibility core.PostVi
db.Rollback() db.Rollback()
return err return err
} }
// tag处理 // tag处理
tags := strings.Split(post.Tags, ",") tags := strings.Split(post.Tags, ",")
for _, t := range tags { // TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
tag := &dbr.Tag{ if oldVisibility == dbr.PostVisitPrivate {
Tag: t, // 从私密转为非私密才需要重新创建tag
} createTags(db, post.UserID, tags)
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善 } else if visibility == dbr.PostVisitPrivate {
if oldVisibility == dbr.PostVisitPrivate { // 从非私密转为私密才需要删除tag
// 从私密转为非私密才需要重新创建tag deleteTags(db, tags)
createTag(db, tag)
} else if visibility == dbr.PostVisitPrivate {
// 从非私密转为私密才需要删除tag
deleteTag(db, tag)
}
} }
db.Commit() db.Commit()
s.cacheIndex.SendAction(core.IdxActVisiblePost, post) s.cacheIndex.SendAction(core.IdxActVisiblePost, post)

@ -70,19 +70,31 @@ func (s *userManageServant) GetUsersByKeyword(keyword string) ([]*core.User, err
} }
} }
func (s *userManageServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) { func (s *userManageServant) GetTagsByKeyword(keyword string) (res []*core.Tag, err error) {
tag := &dbr.Tag{} tag := &dbr.Tag{}
keyword = "%" + strings.Trim(keyword, " ") + "%" keyword = "%" + strings.Trim(keyword, " ") + "%"
var tags []*dbr.Tag
if keyword == "%%" { if keyword == "%%" {
return tag.List(s.db, &dbr.ConditionsT{ tags, err = tag.List(s.db, &dbr.ConditionsT{
"ORDER": "quote_num DESC", "ORDER": "quote_num DESC",
}, 0, 6) }, 0, 6)
} else { } else {
return tag.List(s.db, &dbr.ConditionsT{ tags, err = tag.List(s.db, &dbr.ConditionsT{
"tag LIKE ?": keyword, "tag LIKE ?": keyword,
"ORDER": "quote_num DESC", "ORDER": "quote_num DESC",
}, 0, 6) }, 0, 6)
} }
if err == nil {
for _, tag := range tags {
res = append(res, &core.Tag{
ID: tag.ID,
UserID: tag.UserID,
Tag: tag.Tag,
QuoteNum: tag.QuoteNum,
})
}
}
return
} }
func (s *userManageServant) CreateUser(user *dbr.User) (*core.User, error) { func (s *userManageServant) CreateUser(user *dbr.User) (*core.User, error) {

@ -5,35 +5,52 @@
package jinzhu package jinzhu
import ( import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr" "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"gorm.io/gorm" "gorm.io/gorm"
) )
func createTag(db *gorm.DB, tag *dbr.Tag) (*dbr.Tag, error) { func createTags(db *gorm.DB, userId int64, tags []string) (res []*core.Tag, err error) {
t, err := tag.Get(db) for _, name := range tags {
if err != nil { tag := &dbr.Tag{Tag: name}
tag.QuoteNum = 1 if tag, err = tag.Get(db); err == nil {
return tag.Create(db) // 更新
} tag.QuoteNum++
if err = tag.Update(db); err != nil {
// 更新 return
t.QuoteNum++ }
err = t.Update(db) } else {
tag.UserID, tag.QuoteNum = userId, 1
if err != nil { if tag, err = (&dbr.Tag{
return nil, err UserID: userId,
QuoteNum: 1,
}).Create(db); err != nil {
return
}
}
res = append(res, &core.Tag{
ID: tag.ID,
UserID: tag.UserID,
Tag: tag.Tag,
QuoteNum: tag.QuoteNum,
})
} }
return
return t, nil
} }
func deleteTag(db *gorm.DB, tag *dbr.Tag) error { func decrTagsByIds(db *gorm.DB, ids []int64) (err error) {
tag, err := tag.Get(db) for _, id := range ids {
if err != nil { tag := &dbr.Tag{Model: &dbr.Model{ID: id}}
return err if tag, err = tag.Get(db); err == nil {
tag.QuoteNum--
if err = tag.Update(db); err != nil {
return
}
} else {
continue
}
} }
tag.QuoteNum-- return nil
return tag.Update(db)
} }
func deleteTags(db *gorm.DB, tags []string) error { func deleteTags(db *gorm.DB, tags []string) error {

@ -24,7 +24,7 @@ type sqlxServant struct {
db *sqlx.DB db *sqlx.DB
} }
func (s *sqlxServant) with(handle func(*sqlx.Tx) error) error { func (s *sqlxServant) with(handle func(tx *sqlx.Tx) error) error {
tx, err := s.db.Beginx() tx, err := s.db.Beginx()
if err != nil { if err != nil {
return err return err
@ -60,6 +60,19 @@ func sqlxDB() *sqlx.DB {
return _db return _db
} }
func in(db *sqlx.DB, query string, args ...interface{}) (string, []interface{}, error) {
q, params, err := sqlx.In(query, args...)
if err != nil {
return "", nil, err
}
return db.Rebind(q), params, nil
}
func r(query string) string {
db := sqlxDB()
return db.Rebind(t(query))
}
func c(query string) *sqlx.Stmt { func c(query string) *sqlx.Stmt {
db := sqlxDB() db := sqlxDB()
stmt, err := db.Preparex(db.Rebind(t(query))) stmt, err := db.Preparex(db.Rebind(t(query)))

@ -5,9 +5,11 @@
package sakila package sakila
import ( import (
"strings"
"time"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/pkg/debug"
) )
var ( var (
@ -16,40 +18,133 @@ var (
type topicServant struct { type topicServant struct {
*sqlxServant *sqlxServant
stmtAddTag *sqlx.Stmt stmtNewestTags *sqlx.Stmt
stmtDelTag *sqlx.Stmt stmtHotTags *sqlx.Stmt
stmtListTag *sqlx.Stmt stmtTagsByKeywordA *sqlx.Stmt
stmtTagsByKeywordB *sqlx.Stmt
stmtInsertTag *sqlx.Stmt
stmtTagsByIdA string
stmtTagsByIdB string
stmtDecrTagsById string
stmtTagsByName string
stmtIncrTagsById string
} }
func (s *topicServant) CreateTag(tag *core.Tag) (*core.Tag, error) { func (s *topicServant) UpsertTags(userId int64, tags []string) (res []*core.Tag, xerr error) {
// TODO if len(tags) <= 0 {
debug.NotImplemented() return nil, nil
return nil, nil }
xerr = s.with(func(tx *sqlx.Tx) error {
query, args, err := in(s.db, s.stmtTagsByName, tags)
var ts []*core.Tag
if err = tx.Select(&ts, query, args...); err != nil {
return err
}
var upTags []string
if len(ts) > 0 {
var ids []int64
for _, t := range ts {
ids = append(ids, t.ID)
upTags = append(upTags, t.Tag)
t.QuoteNum++
// prepare remain tags just delete updated tag
for i, name := range tags {
if name == t.Tag {
size := len(tags)
tags[i] = tags[size-1]
tags = tags[:size-1]
break
}
}
}
if query, args, err = in(s.db, s.stmtIncrTagsById, ids); err != nil {
return err
}
if _, err = tx.Exec(query, args...); err != nil {
return err
}
res = append(res, ts...)
}
// process remain tags
if len(tags) == 0 {
return nil
}
var ids []int64
now := time.Now().Unix()
for _, tag := range tags {
res, err := s.stmtInsertTag.Exec(userId, tag, now, now)
if err != nil {
return err
}
id, err := res.LastInsertId()
if err != nil {
return err
}
ids = append(ids, id)
}
var newTags []*core.Tag
query, args, err = in(s.db, s.stmtTagsByIdB, ids)
if err != nil {
return err
}
if err = tx.Select(&newTags, query, args...); err != nil {
return err
}
res = append(res, newTags...)
return nil
})
return
} }
func (s *topicServant) DeleteTag(tag *core.Tag) error { func (s *topicServant) DecrTagsById(ids []int64) error {
// TODO return s.with(func(tx *sqlx.Tx) error {
debug.NotImplemented() query, args, err := in(s.db, s.stmtTagsByIdA, ids)
return nil if err != nil {
return err
}
var ids []int64
if err = tx.Select(&ids, query, args...); err != nil {
return err
}
query, args, err = in(s.db, s.stmtDecrTagsById, time.Now().Unix(), ids)
_, err = tx.Exec(query, args...)
return err
})
} }
func (s *topicServant) GetTags(conditions *core.ConditionsT, offset, limit int) ([]*core.Tag, error) { func (s *topicServant) GetTags(category core.TagCategory, offset int, limit int) (res []*core.Tag, err error) {
// TODO switch category {
debug.NotImplemented() case core.TagCategoryHot:
return nil, nil err = s.stmtHotTags.Select(&res, offset, limit)
case core.TagCategoryNew:
err = s.stmtHotTags.Select(&res, offset, limit)
}
return
} }
func (s *topicServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) { func (s *topicServant) GetTagsByKeyword(keyword string) (res []*core.Tag, err error) {
// TODO keyword = "%" + strings.Trim(keyword, " ") + "%"
debug.NotImplemented() if keyword == "%%" {
return nil, nil err = s.stmtTagsByKeywordA.Select(&res)
} else {
err = s.stmtTagsByKeywordB.Select(&res)
}
return
} }
func newTopicService(db *sqlx.DB) core.TopicService { func newTopicService(db *sqlx.DB) core.TopicService {
return &topicServant{ return &topicServant{
sqlxServant: newSqlxServant(db), sqlxServant: newSqlxServant(db),
stmtAddTag: c(`SELECT * FROM @person WHERE first_name=?`), stmtNewestTags: c(`SELECT id, user_id, tag, quote_num FROM @tag WHERE is_del = 0 AND quote_num > 0 ORDER BY id DESC OFFSET ? LIMIT ?`),
stmtDelTag: c(`SELECT * FROM @person WHERE first_name=?`), stmtHotTags: c(`SELECT id, user_id, tag, quote_num FROM @tag WHERE is_del = 0 AND quote_num > 0 ORDER BY quote_num DESC OFFSET ? LIMIT ?`),
stmtListTag: c(`SELECT * FROM @person WHERE first_name=?`), stmtTagsByKeywordA: c(`SELECT id, user_id, tag, quote_num FROM @tag WHERE is_del = 0 ORDER BY quote_num DESC OFFSET 0 LIMIT 6`),
stmtTagsByKeywordB: c(`SELECT id, user_id, tag, quote_num FROM @tag WHERE is_del = 0 AND tag LIKE ? ORDER BY quote_num DESC OFFSET 0 LIMIT 6`),
stmtInsertTag: c(`INSERT INFO @tag (user_id, tag, created_on, modified_on, quote_num) VALUES (?, ?, ?, ?, 1)`),
stmtTagsByIdA: r(`SELECT id FROM @tag WHERE id IN (?) AND is_del = 0 AND quote_num >= 0`),
stmtTagsByIdB: r(`SELECT id, user_id, tag, quote_num FROM @tag WHERE id IN (?)`),
stmtDecrTagsById: r(`UPDATE @tag SET quote_num=quote_num-1, modified_on=? WHERE id IN (?)`),
stmtTagsByName: r(`SELECT id, user_id, tag, quote_num FROM @tag WHERE tag IN (?) AND is_del = 0 AND quote_num >= 0`),
stmtIncrTagsById: r(`UPDATE @tag SET quote_num=quote_num+1 WHERE id IN (?)`),
} }
} }

@ -10,12 +10,7 @@ import (
"github.com/rocboss/paopao-ce/pkg/debug" "github.com/rocboss/paopao-ce/pkg/debug"
) )
const ( type TagType = core.TagCategory
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
)
type TagType string
type TweetDetailReq struct { type TweetDetailReq struct {
TweetId int64 `form:"id"` TweetId int64 `form:"id"`

@ -19,10 +19,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type TagType string type TagType = core.TagCategory
const TagTypeHot TagType = "hot"
const TagTypeNew TagType = "new"
type PostListReq struct { type PostListReq struct {
Conditions *core.ConditionsT Conditions *core.ConditionsT
@ -157,13 +154,7 @@ func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (_ *core.Po
// 私密推文不创建标签与用户提醒 // 私密推文不创建标签与用户提醒
if post.Visibility != core.PostVisitPrivate { if post.Visibility != core.PostVisitPrivate {
// 创建标签 // 创建标签
for _, t := range tags { ds.UpsertTags(userID, tags)
tag := &core.Tag{
UserID: userID,
Tag: t,
}
ds.CreateTag(tag)
}
// 创建用户消息提醒 // 创建用户消息提醒
for _, u := range param.Users { for _, u := range param.Users {
@ -543,22 +534,7 @@ func GetPostTags(param *PostTagsReq) ([]*core.TagFormated, error) {
if num > conf.AppSetting.MaxPageSize { if num > conf.AppSetting.MaxPageSize {
num = conf.AppSetting.MaxPageSize num = conf.AppSetting.MaxPageSize
} }
tags, err := ds.GetTags(core.TagCategory(param.Type), 0, num)
conditions := &core.ConditionsT{}
if param.Type == TagTypeHot {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "quote_num DESC",
}
}
if param.Type == TagTypeNew {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "id DESC",
}
}
tags, err := ds.GetTags(conditions, 0, num)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -306,13 +306,7 @@ func (s *privSrv) CreateTweet(req *web.CreateTweetReq) (_ *web.CreateTweetResp,
// 私密推文不创建标签与用户提醒 // 私密推文不创建标签与用户提醒
if post.Visibility != core.PostVisitPrivate { if post.Visibility != core.PostVisitPrivate {
// 创建标签 // 创建标签
for _, t := range tags { s.Ds.UpsertTags(req.User.ID, tags)
tag := &core.Tag{
UserID: req.User.ID,
Tag: t,
}
s.Ds.CreateTag(tag)
}
// 创建用户消息提醒 // 创建用户消息提醒
for _, u := range req.Users { for _, u := range req.Users {

@ -75,20 +75,7 @@ func (s *pubSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error
if num > conf.AppSetting.MaxPageSize { if num > conf.AppSetting.MaxPageSize {
num = conf.AppSetting.MaxPageSize num = conf.AppSetting.MaxPageSize
} }
tags, err := s.Ds.GetTags(req.Type, 0, num)
conditions := &core.ConditionsT{}
if req.Type == web.TagTypeHot {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "quote_num DESC",
}
} else if req.Type == web.TagTypeNew {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "id DESC",
}
}
tags, err := s.Ds.GetTags(conditions, 0, num)
if err != nil { if err != nil {
return nil, _errGetPostTagsFailed return nil, _errGetPostTagsFailed
} }

@ -282,15 +282,7 @@ declare module Item {
/** 标签名 */ /** 标签名 */
tag: string, tag: string,
/** 引用数 */ /** 引用数 */
quote_num: number, quote_num: number
/** 创建时间 */
created_on: number,
/** 修改时间 */
modified_on?: number,
/** 删除时间 */
deleted_on?: number,
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
} }
interface PagerProps { interface PagerProps {

Loading…
Cancel
Save