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
import (
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
const (
TagCategoryHot TagCategory = "hot"
TagCategoryNew TagCategory = "new"
)
type (
Tag = dbr.Tag
TagFormated = dbr.TagFormated
)
type TagCategory string
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 话题服务
type TopicService interface {
CreateTag(tag *Tag) (*Tag, error)
DeleteTag(tag *Tag) error
GetTags(conditions *ConditionsT, offset, limit int) ([]*Tag, error)
UpsertTags(userId int64, tags []string) ([]*Tag, error)
DecrTagsById(ids []int64) error
GetTags(category TagCategory, offset int, limit int) ([]*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
}
func (t *Tag) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*Tag, error) {
var tags []*Tag
var err error
func (t *Tag) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) (tags []*Tag, err error) {
if offset >= 0 && limit > 0 {
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)
}
}
if err = db.Where("is_del = 0 and quote_num > 0").Find(&tags).Error; err != nil {
return nil, err
}
return tags, nil
err = db.Where("is_del = 0 and quote_num > 0").Find(&tags).Error
return
}
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) {
return createTag(s.db, tag)
func (s *topicServant) UpsertTags(userId int64, tags []string) (_ []*core.Tag, err error) {
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 {
return deleteTag(s.db, tag)
func (s *topicServant) DecrTagsById(ids []int64) (err error) {
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) {
return (&dbr.Tag{}).List(s.db, conditions, offset, limit)
func (s *topicServant) GetTags(category core.TagCategory, offset, limit int) (res []*core.Tag, err error) {
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) {
tag := &dbr.Tag{}
func (s *topicServant) GetTagsByKeyword(keyword string) (res []*core.Tag, err error) {
keyword = "%" + strings.Trim(keyword, " ") + "%"
tag := &dbr.Tag{}
var tags []*dbr.Tag
if keyword == "%%" {
return tag.List(s.db, &dbr.ConditionsT{
tags, err = tag.List(s.db, &dbr.ConditionsT{
"ORDER": "quote_num DESC",
}, 0, 6)
} else {
return tag.List(s.db, &dbr.ConditionsT{
tags, err = tag.List(s.db, &dbr.ConditionsT{
"tag LIKE ?": keyword,
"ORDER": "quote_num DESC",
}, 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()
return err
}
// tag处理
tags := strings.Split(post.Tags, ",")
for _, t := range tags {
tag := &dbr.Tag{
Tag: t,
}
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
if oldVisibility == dbr.PostVisitPrivate {
// 从私密转为非私密才需要重新创建tag
createTag(db, tag)
} else if visibility == dbr.PostVisitPrivate {
// 从非私密转为私密才需要删除tag
deleteTag(db, tag)
}
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
if oldVisibility == dbr.PostVisitPrivate {
// 从私密转为非私密才需要重新创建tag
createTags(db, post.UserID, tags)
} else if visibility == dbr.PostVisitPrivate {
// 从非私密转为私密才需要删除tag
deleteTags(db, tags)
}
db.Commit()
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{}
keyword = "%" + strings.Trim(keyword, " ") + "%"
var tags []*dbr.Tag
if keyword == "%%" {
return tag.List(s.db, &dbr.ConditionsT{
tags, err = tag.List(s.db, &dbr.ConditionsT{
"ORDER": "quote_num DESC",
}, 0, 6)
} else {
return tag.List(s.db, &dbr.ConditionsT{
tags, err = tag.List(s.db, &dbr.ConditionsT{
"tag LIKE ?": keyword,
"ORDER": "quote_num DESC",
}, 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) {

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

@ -24,7 +24,7 @@ type sqlxServant struct {
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()
if err != nil {
return err
@ -60,6 +60,19 @@ func sqlxDB() *sqlx.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 {
db := sqlxDB()
stmt, err := db.Preparex(db.Rebind(t(query)))

@ -5,9 +5,11 @@
package sakila
import (
"strings"
"time"
"github.com/jmoiron/sqlx"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/pkg/debug"
)
var (
@ -16,40 +18,133 @@ var (
type topicServant struct {
*sqlxServant
stmtAddTag *sqlx.Stmt
stmtDelTag *sqlx.Stmt
stmtListTag *sqlx.Stmt
stmtNewestTags *sqlx.Stmt
stmtHotTags *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) {
// TODO
debug.NotImplemented()
return nil, nil
func (s *topicServant) UpsertTags(userId int64, tags []string) (res []*core.Tag, xerr error) {
if len(tags) <= 0 {
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 {
// TODO
debug.NotImplemented()
return nil
func (s *topicServant) DecrTagsById(ids []int64) error {
return s.with(func(tx *sqlx.Tx) error {
query, args, err := in(s.db, s.stmtTagsByIdA, ids)
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) {
// TODO
debug.NotImplemented()
return nil, nil
func (s *topicServant) GetTags(category core.TagCategory, offset int, limit int) (res []*core.Tag, err error) {
switch category {
case core.TagCategoryHot:
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) {
// TODO
debug.NotImplemented()
return nil, nil
func (s *topicServant) GetTagsByKeyword(keyword string) (res []*core.Tag, err error) {
keyword = "%" + strings.Trim(keyword, " ") + "%"
if keyword == "%%" {
err = s.stmtTagsByKeywordA.Select(&res)
} else {
err = s.stmtTagsByKeywordB.Select(&res)
}
return
}
func newTopicService(db *sqlx.DB) core.TopicService {
return &topicServant{
sqlxServant: newSqlxServant(db),
stmtAddTag: c(`SELECT * FROM @person WHERE first_name=?`),
stmtDelTag: c(`SELECT * FROM @person WHERE first_name=?`),
stmtListTag: c(`SELECT * FROM @person WHERE first_name=?`),
sqlxServant: newSqlxServant(db),
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 ?`),
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 ?`),
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"
)
const (
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
)
type TagType string
type TagType = core.TagCategory
type TweetDetailReq struct {
TweetId int64 `form:"id"`

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

@ -306,13 +306,7 @@ func (s *privSrv) CreateTweet(req *web.CreateTweetReq) (_ *web.CreateTweetResp,
// 私密推文不创建标签与用户提醒
if post.Visibility != core.PostVisitPrivate {
// 创建标签
for _, t := range tags {
tag := &core.Tag{
UserID: req.User.ID,
Tag: t,
}
s.Ds.CreateTag(tag)
}
s.Ds.UpsertTags(req.User.ID, tags)
// 创建用户消息提醒
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 {
num = conf.AppSetting.MaxPageSize
}
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)
tags, err := s.Ds.GetTags(req.Type, 0, num)
if err != nil {
return nil, _errGetPostTagsFailed
}

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

Loading…
Cancel
Save