Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"cache_test.go",
"dao.cache_test.go",
"dao_test.go",
"databus_test.go",
"item_likes_redis_test.go",
"memcached_test.go",
"redis_test.go",
"tidb_test.go",
"user_likes_redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/thumbup/conf:go_default_library",
"//app/service/main/thumbup/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.cache.go",
"dao.go",
"databus.go",
"item_likes_redis.go",
"memcached.go",
"redis.go",
"tidb.go",
"user_likes_redis.go",
],
importpath = "go-common/app/service/main/thumbup/dao",
tags = ["automanaged"],
deps = [
"//app/service/main/thumbup/api:go_default_library",
"//app/service/main/thumbup/conf:go_default_library",
"//app/service/main/thumbup/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/tidb:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/sync/singleflight:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,31 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/main/thumbup/model"
)
//go:generate $GOPATH/src/go-common/app/tool/cache/gen
type _cache interface {
// 用户点赞列表
// cache: -singleflight=true -ignores=||start,end
userLikeList(c context.Context, mid int64, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error)
}
func (d *Dao) cacheSFuserLikeList(mid, businessID int64, state int8, start, end int) string {
return fmt.Sprintf("sf_u%v_%v_%v", mid, businessID, state)
}
// UserLikeList 用户点赞列表
func (d *Dao) UserLikeList(c context.Context, mid int64, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error) {
var ls []*model.ItemLikeRecord
ls, err = d.userLikeList(c, mid, businessID, state, start, end)
for _, x := range ls {
if x.MessageID != -1 {
res = append(res, x)
}
}
return
}

View File

@@ -0,0 +1,26 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoUserLikeList(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
state = int8(1)
start = int(0)
end = int(10)
)
convey.Convey("UserLikeList", t, func(ctx convey.C) {
_, err := d.UserLikeList(c, mid, businessID, state, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
// ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,58 @@
// Code generated by $GOPATH/src/go-common/app/tool/cache/gen. DO NOT EDIT.
/*
Package dao is a generated cache proxy package.
It is generated from:
type _cache interface {
// 用户点赞列表
// cache: -singleflight=true -ignores=||start,end
userLikeList(c context.Context, mid int64, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error)
}
*/
package dao
import (
"context"
"go-common/app/service/main/thumbup/model"
"go-common/library/stat/prom"
"golang.org/x/sync/singleflight"
)
var _ _cache
var cacheSingleFlights = [1]*singleflight.Group{{}}
// userLikeList 用户点赞列表
func (d *Dao) userLikeList(c context.Context, id int64, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error) {
addCache := true
res, err = d.CacheUserLikeList(c, id, businessID, state, start, end)
if err != nil {
addCache = false
err = nil
}
if len(res) != 0 {
prom.CacheHit.Incr("userLikeList")
return
}
var rr interface{}
sf := d.cacheSFuserLikeList(id, businessID, state, start, end)
rr, err, _ = cacheSingleFlights[0].Do(sf, func() (r interface{}, e error) {
prom.CacheMiss.Incr("userLikeList")
r, e = d.RawUserLikeList(c, id, businessID, state, start, end)
return
})
res = rr.([]*model.ItemLikeRecord)
if err != nil {
return
}
miss := res
if !addCache {
return
}
d.cache.Do(c, func(c context.Context) {
d.AddCacheUserLikeList(c, id, miss, businessID, state)
})
return
}

View File

@@ -0,0 +1,26 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaouserLikeList(t *testing.T) {
var (
c = context.TODO()
id = int64(1)
businessID = int64(1)
state = int8(1)
start = int(1)
end = int(1)
)
convey.Convey("userLikeList", t, func(ctx convey.C) {
_, err := d.userLikeList(c, id, businessID, state, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
// ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,166 @@
package dao
import (
"context"
"time"
"go-common/app/service/main/thumbup/conf"
"go-common/app/service/main/thumbup/model"
"go-common/library/cache/memcache"
xredis "go-common/library/cache/redis"
"go-common/library/database/tidb"
"go-common/library/log"
"go-common/library/queue/databus"
"go-common/library/stat/prom"
"go-common/library/sync/pipeline/fanout"
)
// PromError prom error
func PromError(name string) {
prom.BusinessErrCount.Incr(name)
}
// Dao dao
type Dao struct {
// config
c *conf.Config
// db
tidb *tidb.DB
// memcache
mc *memcache.Pool
mcStatsExpire int32
//redis
redis *xredis.Pool
redisStatsExpire int64
redisUserLikesExpire int64
redisItemLikesExpire int64
// redisSortExpire int64
// stmt
businessesStmt *tidb.Stmts
likeStateStmt *tidb.Stmts
userLikeCountStmt *tidb.Stmts
itemLikeListStmt *tidb.Stmts
userLikeListStmt *tidb.Stmts
statsOriginStmt *tidb.Stmts
statStmt *tidb.Stmts
updateLikeStmt *tidb.Stmts
updateCountChangeStmt *tidb.Stmts
statDbus *databus.Databus
likeDbus *databus.Databus
itemDbus *databus.Databus
userDbus *databus.Databus
cache *fanout.Fanout
async *fanout.Fanout
tidbAsync *fanout.Fanout
BusinessMap map[string]*model.Business
BusinessIDMap map[int64]*model.Business
}
// New dao new
func New(c *conf.Config) (d *Dao) {
d = &Dao{
// config
c: c,
// mc
mc: memcache.NewPool(c.Memcache.Config),
mcStatsExpire: int32(time.Duration(c.Memcache.StatsExpire) / time.Second),
// redis
redis: xredis.NewPool(c.Redis.Config),
redisStatsExpire: int64(time.Duration(c.Redis.StatsExpire) / time.Second),
redisUserLikesExpire: int64(time.Duration(c.Redis.UserLikesExpire) / time.Second),
redisItemLikesExpire: int64(time.Duration(c.Redis.ItemLikesExpire) / time.Second),
// db
tidb: tidb.NewTiDB(c.Tidb),
statDbus: databus.New(c.StatDatabus),
likeDbus: databus.New(c.LikeDatabus),
itemDbus: databus.New(c.ItemDatabus),
userDbus: databus.New(c.UserDatabus),
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(10240)),
async: fanout.New("async", fanout.Worker(2), fanout.Buffer(10240)),
tidbAsync: fanout.New("tidb-async", fanout.Worker(10), fanout.Buffer(10240)),
}
d.businessesStmt = d.tidb.Prepared(_tidbBusinessesSQL)
d.likeStateStmt = d.tidb.Prepared(_tidbLikeMidSQL)
d.userLikeCountStmt = d.tidb.Prepared(_tidbUserLikeCountSQL)
d.itemLikeListStmt = d.tidb.Prepared(_tidbItemLikeListSQL)
d.userLikeListStmt = d.tidb.Prepared(_tidbUserLikeListSQL)
d.statsOriginStmt = d.tidb.Prepared(_tidbStatsOriginSQL)
d.statStmt = d.tidb.Prepared(_tidbStatSQL)
d.updateLikeStmt = d.tidb.Prepared(_tidbUpdateLikeSQL)
d.updateCountChangeStmt = d.tidb.Prepared(_tidbupdateCountChange)
d.loadBusiness()
go d.loadBusinessproc()
return d
}
// Ping check connection success.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.pingMC(c); err != nil {
PromError("mc:Ping")
log.Error("d.pingMC error(%v)", err)
return
}
if err = d.pingRedis(c); err != nil {
PromError("redis:Ping")
log.Error("d.pingRedis error(%v)", err)
return
}
return
}
// Close close resource.
func (d *Dao) Close() {
d.async.Close()
d.tidbAsync.Close()
d.tidb.Close()
d.mc.Close()
d.redis.Close()
}
// pingMc ping memcache
func (d *Dao) pingMC(c context.Context) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
item := memcache.Item{Key: "ping", Value: []byte{1}, Expiration: 100}
err = conn.Set(&item)
return
}
// pingRedis ping redis.
func (d *Dao) pingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
if _, err = conn.Do("SET", "PING", "PONG"); err != nil {
PromError("redis: ping remote")
log.Error("remote redis: conn.Do(SET,PING,PONG) error(%v)", err)
}
conn.Close()
return
}
// LoadBusiness .
func (d *Dao) loadBusiness() {
var business []*model.Business
var err error
businessMap := make(map[string]*model.Business)
businessIDMap := make(map[int64]*model.Business)
for {
if business, err = d.Businesses(context.TODO()); err != nil {
time.Sleep(time.Second)
continue
}
for _, b := range business {
businessMap[b.Name] = b
businessIDMap[b.ID] = b
}
d.BusinessMap = businessMap
d.BusinessIDMap = businessIDMap
return
}
}
func (d *Dao) loadBusinessproc() {
for {
time.Sleep(time.Minute * 5)
d.loadBusiness()
}
}

View File

@@ -0,0 +1,39 @@
package dao
import (
"flag"
"os"
"testing"
"go-common/app/service/main/thumbup/conf"
)
var d *Dao
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.thumbup-service")
flag.Set("conf_token", "VhnSEtd0oymsNQaDUYuEknoWu2mVOOVK")
flag.Set("tree_id", "7720")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/thumbup-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}
// INSERT INTO `bilibili_likes`.`counts_01`(`id`, `mtime`, `ctime`, `business_id`, `origin_id`, `message_id`, `likes_count`, `dislikes_count`, `likes_change`, `dislikes_change`) VALUES (98, '2018-06-13 14:51:11', '2018-06-13 14:51:11', 1, 99901, 8888, 1, 2, 3, 4);
// INSERT INTO `bilibili_likes`.`counts_01`(`id`, `mtime`, `ctime`, `business_id`, `origin_id`, `message_id`, `likes_count`, `dislikes_count`, `likes_change`, `dislikes_change`) VALUES (99, '2018-06-13 14:56:56', '2018-06-13 14:56:56', 1, 101, 8888, 0, 0, 0, 0);
// INSERT INTO `bilibili_likes`.`likes`(`id`, `mtime`, `ctime`, `business_id`, `origin_id`, `message_id`, `mid`, `type`) VALUES (0, '2018-11-01 18:03:28', '2018-11-01 18:03:28', 1, 1, 1, 1, 1);
// INSERT INTO `bilibili_likes`.`counts`(`id`, `mtime`, `ctime`, `business_id`, `origin_id`, `message_id`, `likes_count`, `dislikes_count`, `likes_change`, `dislikes_change`, `up_mid`) VALUES (0, '2018-11-03 12:16:25', '2018-11-03 12:16:25', 1, 1, 1, 1, 1, 0, 0, 0);

View File

@@ -0,0 +1,72 @@
package dao
import (
"context"
"strconv"
"time"
"go-common/app/service/main/thumbup/model"
"go-common/library/log"
)
// PubStatDatabus pub stat databus
func (d *Dao) PubStatDatabus(c context.Context, business string, mid int64, s *model.Stats, upMid int64) (err error) {
msg := &model.StatMsg{Type: business, ID: s.ID, Count: s.Likes, Timestamp: time.Now().Unix(), OriginID: s.OriginID, DislikeCount: s.Dislikes, Mid: mid, UpMid: upMid}
if err = d.statDbus.Send(c, strconv.FormatInt(s.ID, 10), msg); err != nil {
log.Error("d.databus.Send error(%v)", err)
PromError("databus:stat")
return
}
log.Info("s.PubStatDatabus (%+v)", msg)
return
}
// PubLikeDatabus .
func (d *Dao) PubLikeDatabus(c context.Context, p *model.LikeMsg) (err error) {
if err = d.likeDbus.Send(c, strconv.FormatInt(p.Mid, 10), p); err != nil {
log.Error("d.likeDbus.Send error(%v)", err)
PromError("databus:like")
return
}
log.Info("s.PubLikeDatabus success (%+v)", p)
return
}
// PubItemMsg .
func (d *Dao) PubItemMsg(c context.Context, business string, originID, messageID int64, state int8) (err error) {
msg := &model.ItemMsg{
State: state,
Business: business,
OriginID: originID,
MessageID: messageID,
}
if err = d.itemDbus.Send(c, strconv.FormatInt(messageID, 10), msg); err != nil {
log.Error("d.PubItemMsg.databus.Send error(%v)", err)
PromError("databus:item")
return
}
log.Info("s.PubItemMsg success (%+v)", msg)
return
}
// PubUserMsg .
func (d *Dao) PubUserMsg(c context.Context, business string, mid int64, state int8) (err error) {
msg := &model.UserMsg{
Mid: mid,
State: state,
Business: business,
}
if err = d.userDbus.Send(c, strconv.FormatInt(mid, 10), msg); err != nil {
log.Error("d.PubUserMsg.databus.Send error(%v)", err)
PromError("databus:user")
return
}
log.Info("s.PubUserMsg success (%+v)", msg)
return
}
// AddCacheUserLikeList .
func (d *Dao) AddCacheUserLikeList(c context.Context, mid int64, miss []*model.ItemLikeRecord, businessID int64, state int8) (err error) {
err = d.PubUserMsg(c, d.BusinessIDMap[businessID].Name, mid, state)
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"go-common/app/service/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoPubStatDatabus(t *testing.T) {
var (
c = context.TODO()
business = "archive"
mid = int64(1)
s = &model.Stats{}
)
convey.Convey("PubStatDatabus", t, func(ctx convey.C) {
err := d.PubStatDatabus(c, business, mid, s, 1)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,206 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/main/thumbup/model"
"go-common/library/cache/redis"
"go-common/library/log"
xtime "go-common/library/time"
)
func itemLikesKey(businessID, messageID int64, state int8) string {
return fmt.Sprintf("i2_m_%d_b_%d_%d", messageID, businessID, state)
}
// CacheItemLikeList .
func (d *Dao) CacheItemLikeList(c context.Context, messageID, businessID int64, state int8, start, end int) (res []*model.UserLikeRecord, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
items, err := redis.Values(conn.Do("ZREVRANGE", key, start, end, "withscores"))
if err != nil {
if err == redis.ErrNil {
err = nil
return
}
PromError("redis:CacheItemLikeList")
log.Errorv(c, log.KV("CacheItemLikeList", fmt.Sprintf("%+v", err)))
return
}
for len(items) > 0 {
var id, t int64
if items, err = redis.Scan(items, &id, &t); err != nil {
PromError("redis:CacheItemLikeList")
log.Errorv(c, log.KV("CacheItemLikeList", fmt.Sprintf("%+v", err)))
return
}
res = append(res, &model.UserLikeRecord{Mid: id, Time: xtime.Time(t)})
}
return
}
// AddCacheItemLikeList .
func (d *Dao) AddCacheItemLikeList(c context.Context, messageID int64, miss []*model.UserLikeRecord, businessID int64, state int8) (err error) {
if len(miss) == 0 {
return
}
limit := d.BusinessIDMap[businessID].MessageLikesLimit
var count int
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
if err = conn.Send("DEL", key); err != nil {
PromError("redis:项目点赞列表")
log.Errorv(c, log.KV("AddCacheItemLikeList", fmt.Sprintf("AddCacheItemLikeList conn.Send(DEL, %s) error(%+v)", key, err)))
return
}
count++
for _, item := range miss {
id := item.Mid
score := int64(item.Time)
if err = conn.Send("ZADD", key, "CH", score, id); err != nil {
PromError("redis:项目点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("AddCacheItemLikeList conn.Send(ZADD, %s, %d, %v) error(%v)", key, score, id, err)))
return
}
count++
}
if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(limit + 1)); err != nil {
PromError("redis:项目点赞列表rm")
log.Errorv(c, log.KV("log", fmt.Sprintf("AddCacheItemLikeList conn.Send(ZREMRANGEBYRANK, %s, 0, %d) error(%v)", key, -(limit+1), err)))
return
}
count++
if err = conn.Send("EXPIRE", key, d.redisItemLikesExpire); err != nil {
PromError("redis:项目点赞列表过期")
log.Errorv(c, log.KV("log", fmt.Sprintf("AddCacheItemLikeList conn.Send(EXPIRE, %s, %d) error(%v)", key, d.redisItemLikesExpire, err)))
return
}
count++
if err = conn.Flush(); err != nil {
PromError("redis:项目点赞列表flush")
log.Errorv(c, log.KV("log", fmt.Sprintf("AddCacheItemLikeList conn.Flush error(%v)", err)))
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
PromError("redis:项目点赞列表receive")
log.Errorv(c, log.KV("log", fmt.Sprintf("AddCacheItemLikeList conn.Receive error(%v)", err)), log.KV("miss", miss))
return
}
}
return
}
// ExpireItemLikesCache .
func (d *Dao) ExpireItemLikesCache(c context.Context, messageID, businessID int64, state int8) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.redisItemLikesExpire)); err != nil {
PromError("redis:expire项目点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s) error(%v)", key, err)))
}
return
}
// ItemLikeExists .
func (d *Dao) ItemLikeExists(c context.Context, messageID, businessID int64, mids []int64, state int8) (res map[int64]int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
res = make(map[int64]int64)
for _, name := range mids {
conn.Send("ZSCORE", key, name)
}
if err = conn.Flush(); err != nil {
PromError("redis:ItemLikeExists")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Flush() error(%v)", err)))
return
}
for _, name := range mids {
var s int64
if s, err = redis.Int64(conn.Receive()); err == nil {
res[name] = s
} else if err == redis.ErrNil {
err = nil
} else {
PromError("redis:ItemLikeExists")
log.Errorv(c, log.KV("log", fmt.Sprintf("ItemLikeExists conn.Receive() error(%v)", err)))
return
}
}
return
}
// AppendCacheItemLikeList .
func (d *Dao) AppendCacheItemLikeList(c context.Context, messageID int64, item *model.UserLikeRecord, businessID int64, state int8) (err error) {
if item == nil {
return
}
limit := d.BusinessIDMap[businessID].MessageLikesLimit
var count int
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
id := item.Mid
score := int64(item.Time)
if err = conn.Send("ZADD", key, "CH", score, id); err != nil {
PromError("redis:项目点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZADD, %s, %d, %v) error(%v)", key, score, id, err)))
return
}
count++
if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(limit + 1)); err != nil {
PromError("redis:项目点赞列表rm")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREMRANGEBYRANK, %s, 0, %d) error(%v)", key, -(limit+1), err)))
return
}
count++
if err = conn.Send("EXPIRE", key, d.redisItemLikesExpire); err != nil {
PromError("redis:项目点赞列表过期")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s, %d) error(%v)", key, d.redisItemLikesExpire, err)))
return
}
count++
if err = conn.Flush(); err != nil {
PromError("redis:项目点赞列表flush")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Flush error(%v)", err)))
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
PromError("redis:项目点赞列表receive")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Receive error(%v)", err)))
return
}
}
return
}
// DelItemLikeCache .
func (d *Dao) DelItemLikeCache(c context.Context, messageID, businessID int64, mid int64, state int8) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, state)
if _, err = conn.Do("ZREM", key, mid); err != nil {
PromError("redis:zrem项目点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREM, %s, %v) error(%v)", key, mid, err)))
}
return
}
// ItemLikesCountCache .
func (d *Dao) ItemLikesCountCache(c context.Context, businessID, messageID int64) (res int, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, model.StateLike)
res, err = redis.Int(conn.Do("ZCOUNT", key, "(0", "+inf"))
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("dao.ItemLikesCountCache(%d, %d) err:%v", businessID, messageID, err)))
PromError("redis:项目点赞总数")
}
return
}

View File

@@ -0,0 +1,137 @@
package dao
import (
"context"
"go-common/app/service/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoitemLikesKey(t *testing.T) {
var (
businessID = int64(1)
messageID = int64(1)
state = int8(1)
)
convey.Convey("itemLikesKey", t, func(ctx convey.C) {
p1 := itemLikesKey(businessID, messageID, state)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoCacheItemLikeList(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
businessID = int64(1)
state = int8(1)
start = int(1)
end = int(1)
)
convey.Convey("CacheItemLikeList", t, func(ctx convey.C) {
_, err := d.CacheItemLikeList(c, messageID, businessID, state, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
// ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddCacheItemLikeList(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
miss = []*model.UserLikeRecord{}
businessID = int64(1)
state = int8(1)
)
convey.Convey("AddCacheItemLikeList", t, func(ctx convey.C) {
err := d.AddCacheItemLikeList(c, messageID, miss, businessID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoExpireItemLikesCache(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
businessID = int64(1)
state = int8(1)
)
convey.Convey("ExpireItemLikesCache", t, func(ctx convey.C) {
ok, err := d.ExpireItemLikesCache(c, messageID, businessID, state)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
}
func TestDaoItemLikeExists(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
businessID = int64(1)
mids = []int64{}
state = int8(1)
)
convey.Convey("ItemLikeExists", t, func(ctx convey.C) {
res, err := d.ItemLikeExists(c, messageID, businessID, mids, state)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAppendCacheItemLikeList(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
item = &model.UserLikeRecord{}
businessID = int64(1)
state = int8(1)
)
convey.Convey("AppendCacheItemLikeList", t, func(ctx convey.C) {
err := d.AppendCacheItemLikeList(c, messageID, item, businessID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoDelItemLikeCache(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
businessID = int64(1)
mid = int64(1)
state = int8(1)
)
convey.Convey("DelItemLikeCache", t, func(ctx convey.C) {
err := d.DelItemLikeCache(c, messageID, businessID, mid, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoItemLikesCountCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
messageID = int64(1)
)
convey.Convey("ItemLikesCountCache", t, func(ctx convey.C) {
res, err := d.ItemLikesCountCache(c, businessID, messageID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,147 @@
package dao
import (
"context"
"fmt"
"sync"
"go-common/app/service/main/thumbup/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/xstr"
"go-common/library/sync/errgroup"
)
const (
_bulkSize = 100
)
func statsKey(businessID, messageID int64) string {
return fmt.Sprintf("m_%d_b_%d", messageID, businessID)
}
func recoverStatsValue(c context.Context, s string) (res *model.Stats) {
var (
vs []int64
err error
)
res = new(model.Stats)
if s == "" {
return
}
if vs, err = xstr.SplitInts(s); err != nil || len(vs) < 2 {
PromError("mc:stats解析")
log.Error("dao.recoverStatsValue(%s) err: %v", s, err)
return
}
res = &model.Stats{Likes: vs[0], Dislikes: vs[1]}
return
}
// AddStatsCache .
func (d *Dao) AddStatsCache(c context.Context, businessID int64, vs ...*model.Stats) (err error) {
if len(vs) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
for _, v := range vs {
if v == nil {
continue
}
key := statsKey(businessID, v.ID)
bs := xstr.JoinInts([]int64{v.Likes, v.Dislikes})
item := memcache.Item{Key: key, Value: []byte(bs), Expiration: d.mcStatsExpire}
if err = conn.Set(&item); err != nil {
PromError("mc:增加计数缓存")
log.Error("conn.Set(%s) error(%v)", key, err)
return
}
}
return
}
// DelStatsCache del stats cache
func (d *Dao) DelStatsCache(c context.Context, businessID int64, messageID int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := statsKey(businessID, messageID)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
PromError("mc:DelStatsCache")
log.Error("d.DelStatsCache(%s) error(%+v)", key, err)
}
return
}
// AddStatsCacheMap .
func (d *Dao) AddStatsCacheMap(c context.Context, businessID int64, stats map[int64]*model.Stats) (err error) {
var s []*model.Stats
for _, v := range stats {
s = append(s, v)
}
return d.AddStatsCache(c, businessID, s...)
}
// StatsCache .
func (d *Dao) StatsCache(c context.Context, businessID int64, messageIDs []int64) (cached map[int64]*model.Stats, missed []int64, err error) {
if len(messageIDs) == 0 {
return
}
cached = make(map[int64]*model.Stats, len(messageIDs))
allKeys := make([]string, 0, len(messageIDs))
midmap := make(map[string]int64, len(messageIDs))
for _, id := range messageIDs {
k := statsKey(businessID, id)
allKeys = append(allKeys, k)
midmap[k] = id
}
group, errCtx := errgroup.WithContext(c)
mutex := sync.Mutex{}
keysLen := len(allKeys)
for i := 0; i < keysLen; i += _bulkSize {
var keys []string
if (i + _bulkSize) > keysLen {
keys = allKeys[i:]
} else {
keys = allKeys[i : i+_bulkSize]
}
group.Go(func() (err error) {
conn := d.mc.Get(errCtx)
replys, err := conn.GetMulti(keys)
defer conn.Close()
if err != nil {
PromError("mc:获取计数缓存")
log.Error("conn.Gets(%v) error(%v)", keys, err)
err = nil
return
}
for _, reply := range replys {
var s string
if err = conn.Scan(reply, &s); err != nil {
PromError("获取计数缓存json解析")
log.Error("json.Unmarshal(%v) error(%v)", reply.Value, err)
err = nil
continue
}
stat := recoverStatsValue(c, s)
stat.ID = midmap[reply.Key]
mutex.Lock()
cached[midmap[reply.Key]] = stat
delete(midmap, reply.Key)
mutex.Unlock()
}
return
})
}
group.Wait()
missed = make([]int64, 0, len(midmap))
for _, aid := range midmap {
missed = append(missed, aid)
}
return
}

View File

@@ -0,0 +1,93 @@
package dao
import (
"context"
"go-common/app/service/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaostatsKey(t *testing.T) {
var (
businessID = int64(1)
messageID = int64(1)
)
convey.Convey("statsKey", t, func(ctx convey.C) {
p1 := statsKey(businessID, messageID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaorecoverStatsValue(t *testing.T) {
var (
c = context.TODO()
s = ""
)
convey.Convey("recoverStatsValue", t, func(ctx convey.C) {
res := recoverStatsValue(c, s)
ctx.Convey("Then res should not be nil.", func(ctx convey.C) {
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
vs = &model.Stats{}
)
convey.Convey("AddStatsCache", t, func(ctx convey.C) {
err := d.AddStatsCache(c, businessID, vs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoDelStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
messageID = int64(1)
)
convey.Convey("DelStatsCache", t, func(ctx convey.C) {
err := d.DelStatsCache(c, businessID, messageID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddStatsCacheMap(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
stats map[int64]*model.Stats
)
convey.Convey("AddStatsCacheMap", t, func(ctx convey.C) {
err := d.AddStatsCacheMap(c, businessID, stats)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
messageIDs = []int64{1}
)
convey.Convey("StatsCache", t, func(ctx convey.C) {
cached, missed, err := d.StatsCache(c, businessID, messageIDs)
ctx.Convey("Then err should be nil.cached,missed should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missed, convey.ShouldNotBeNil)
ctx.So(cached, convey.ShouldBeEmpty)
})
})
}

View File

@@ -0,0 +1,106 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/main/thumbup/model"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/xstr"
)
func hashStatsKey(businessID, originID int64) string {
return fmt.Sprintf("stats_o_%d_b_%d", originID, businessID)
}
// ExpireHashStatsCache .
func (d *Dao) ExpireHashStatsCache(c context.Context, businessID, originID int64) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := hashStatsKey(businessID, originID)
if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.redisStatsExpire)); err != nil {
PromError("redis:计数缓存设定过期")
log.Error("conn.Do(EXPIRE, %s, %d) error(%v)", key, d.redisStatsExpire, err)
}
return
}
// DelHashStatsCache del hash cache
func (d *Dao) DelHashStatsCache(c context.Context, businessID, originID int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := hashStatsKey(businessID, originID)
if _, err = conn.Do("del", key); err != nil {
PromError("redis:计数缓存删除")
log.Error("conn.Do(DEL, %s) error(%v)", key, err)
}
return
}
// HashStatsCache .
func (d *Dao) HashStatsCache(c context.Context, businessID, originID int64, messageIDs []int64) (res map[int64]*model.Stats, err error) {
if len(messageIDs) == 0 {
return
}
conn := d.redis.Get(c)
defer conn.Close()
key := hashStatsKey(businessID, originID)
var ss []string
var commonds []interface{}
commonds = append(commonds, key)
for _, m := range messageIDs {
commonds = append(commonds, m)
}
if ss, err = redis.Strings(conn.Do("HMGET", commonds...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("conn.Do(HMGET, %s, %v) error(%v)", key, messageIDs, err)
PromError("redis:获取统计信息")
}
return
}
res = make(map[int64]*model.Stats)
for i, id := range messageIDs {
if ss[i] == "" {
continue
}
stat := &model.Stats{ID: id, OriginID: originID}
num, _ := xstr.SplitInts(ss[i])
if len(num) > 1 {
stat.Likes = num[0]
stat.Dislikes = num[1]
}
res[id] = stat
}
return
}
// AddHashStatsCache .
func (d *Dao) AddHashStatsCache(c context.Context, businessID, originID int64, stats ...*model.Stats) (err error) {
if len(stats) == 0 {
return
}
conn := d.redis.Get(c)
defer conn.Close()
key := hashStatsKey(businessID, originID)
var commonds = []interface{}{key}
for _, stat := range stats {
commonds = append(commonds, stat.ID, xstr.JoinInts([]int64{stat.Likes, stat.Dislikes}))
}
if _, err = conn.Do("HMSET", commonds...); err != nil {
PromError("redis:增加统计信息")
log.Error("conn.DO(HMSET, %s, %v) error(%v)", key, commonds, err)
}
return
}
// AddHashStatsCacheMap .
func (d *Dao) AddHashStatsCacheMap(c context.Context, businessID, originID int64, stats map[int64]*model.Stats) (err error) {
var s []*model.Stats
for _, v := range stats {
s = append(s, v)
}
return d.AddHashStatsCache(c, businessID, originID, s...)
}

View File

@@ -0,0 +1,98 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/thumbup/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaohashStatsKey(t *testing.T) {
var (
businessID = int64(1)
originID = int64(1)
)
convey.Convey("hashStatsKey", t, func(ctx convey.C) {
p1 := hashStatsKey(businessID, originID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoExpireHashStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
)
convey.Convey("ExpireHashStatsCache", t, func(ctx convey.C) {
ok, err := d.ExpireHashStatsCache(c, businessID, originID)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
}
func TestDaoDelHashStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
)
convey.Convey("DelHashStatsCache", t, func(ctx convey.C) {
err := d.DelHashStatsCache(c, businessID, originID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoHashStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
messageIDs = []int64{1}
)
convey.Convey("HashStatsCache", t, func(ctx convey.C) {
res, err := d.HashStatsCache(c, businessID, originID, messageIDs)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddHashStatsCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
stats = &model.Stats{}
)
convey.Convey("AddHashStatsCache", t, func(ctx convey.C) {
err := d.AddHashStatsCache(c, businessID, originID, stats)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddHashStatsCacheMap(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
stats map[int64]*model.Stats
)
convey.Convey("AddHashStatsCacheMap", t, func(ctx convey.C) {
err := d.AddHashStatsCacheMap(c, businessID, originID, stats)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,426 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"sync"
"time"
"go-common/app/service/main/thumbup/model"
sql "go-common/library/database/tidb"
"go-common/library/log"
xtime "go-common/library/time"
"go-common/library/xstr"
"go-common/library/sync/errgroup"
)
const (
_tidbBulkSize = 50
_tidbBusinessesSQL = "SELECT id, name, message_list_type, user_likes_limit, message_likes_limit, enable_originid, user_list_type FROM business WHERE dtime = '0000-00-00 00:00:00'"
_tidbLikeMidSQL = "SELECT type FROM likes WHERE mid=? AND business_id = ? AND origin_id = ? AND message_id = ?"
_tidbUserLikeListSQL = "SELECT message_id, mtime FROM likes WHERE mid = ? AND business_id =? AND type = ? ORDER BY mtime desc LIMIT ? OFFSET ?"
_tidbUserHasLikeSQL = "SELECT message_id, mtime FROM likes WHERE mid = ? AND business_id =? AND type = ? AND message_id in (%s)"
_tidbItemLikeListSQL = "SELECT mid, mtime FROM likes WHERE business_id =? AND origin_id =? AND message_id =? AND type = ? ORDER BY mtime desc LIMIT ? OFFSET ?"
_tidbStatSQL = "SELECT message_id, likes_count, dislikes_count, origin_id, likes_change, dislikes_change FROM counts WHERE business_id = ? AND origin_id = ? AND message_id = ?"
_tidbStatsSQL = "SELECT message_id, likes_count, dislikes_count, likes_change, dislikes_change FROM counts WHERE business_id = ? AND origin_id = 0 AND message_id in (%s)"
_tidbStatsOriginSQL = "SELECT message_id, likes_count, dislikes_count, likes_change, dislikes_change FROM counts WHERE business_id = ? AND origin_id = ?"
_tidbUserLikeCountSQL = "SELECT COUNT(*) FROM likes WHERE business_id =? AND mid = ? AND type = ?"
_tidbUpdateLikeSQL = "INSERT INTO likes (business_id, origin_id, message_id, mid, type, mtime) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE type=?, mtime = ?"
_tidbUpdateCountsSQL = "INSERT INTO counts (business_id, origin_id, message_id, likes_count, dislikes_count, up_mid) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE "
_tidbupdateCountChange = "UPDATE counts SET likes_change = likes_change + ?, dislikes_change = dislikes_change + ? WHERE business_id = ? AND origin_id = ? AND message_id = ?"
_tidbItemHasLikeSQL = "SELECT mid, mtime FROM likes WHERE business_id = ? AND origin_id = ? AND message_id = ? AND type =? AND mid in (%s)"
)
// Businesses get business list
func (d *Dao) Businesses(c context.Context) (res []*model.Business, err error) {
var rows *sql.Rows
if rows, err = d.businessesStmt.Query(c); err != nil {
PromError("tidb:业务查询")
log.Error("tidb.businessesStmt.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.Business{}
if err = rows.Scan(&b.ID, &b.Name, &b.MessageListType, &b.UserLikesLimit, &b.MessageLikesLimit, &b.EnableOriginID, &b.UserListType); err != nil {
PromError("tidb:业务Scan")
log.Error("tidb.rows.Business.Scan error(%v)", err)
return
}
res = append(res, b)
}
err = rows.Err()
return
}
// LikeState get like state
func (d *Dao) LikeState(c context.Context, mid, businessID, originID, messageID int64) (res int8, err error) {
if err = d.likeStateStmt.QueryRow(c, mid, businessID, originID, messageID).Scan(&res); err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
PromError("tidb:LikeByMid")
log.Error("tidbLikeByMid(%d,%d,%d,%d) error(%v)", mid, businessID, originID, messageID, err)
}
return
}
// UserLikeCount count
func (d *Dao) UserLikeCount(c context.Context, businessID, mid int64, typ int8) (res int, err error) {
if err = d.userLikeCountStmt.QueryRow(c, businessID, mid, typ).Scan(&res); err != nil {
PromError("tidb:UserLikeCount")
log.Error("tidbUserLikeCount(%d,%d,%d) error(%v)", businessID, mid, typ, err)
}
return
}
// RawItemLikeList item like list
func (d *Dao) RawItemLikeList(c context.Context, messageID, businessID, originID int64, state int8, start, end int) (res []*model.UserLikeRecord, err error) {
limit := end - start + 1
var rows *sql.Rows
if rows, err = d.itemLikeListStmt.Query(c, businessID, originID, messageID, state, limit, start); err != nil {
PromError("tidb:项目点赞列表")
log.Error("tidb.ItemLikeList.Query error(%v,%v,%v,%v,%v, %v)", businessID, originID, messageID, state, limit, err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.UserLikeRecord{}
var t time.Time
if err = rows.Scan(&b.Mid, &t); err != nil {
PromError("tidb:业务Scan")
log.Error("tidb.rows.Business.Scan error(%v)", err)
return
}
b.Time = xtime.Time(t.Unix())
res = append(res, b)
}
err = rows.Err()
return
}
// RawUserLikeList .
func (d *Dao) RawUserLikeList(c context.Context, mid, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error) {
limit := end - start
var rows *sql.Rows
if rows, err = d.userLikeListStmt.Query(c, mid, businessID, state, limit, start); err != nil {
PromError("tidb:用户点赞列表")
log.Error("tidb.UserLikeList.Query error(%v,%v,%v,%v,%v)", mid, businessID, state, limit, err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.ItemLikeRecord{}
var t time.Time
if err = rows.Scan(&b.MessageID, &t); err != nil {
PromError("tidb:用户Scan")
log.Error("tidb.rows.Business.Scan error(%v)", err)
return
}
b.Time = xtime.Time(t.Unix())
res = append(res, b)
}
err = rows.Err()
return
}
// MessageStats .
func (d *Dao) MessageStats(c context.Context, businessID int64, ids []int64) (res map[int64]*model.Stats, err error) {
res = make(map[int64]*model.Stats)
var (
group = &errgroup.Group{}
mutex = &sync.Mutex{}
)
keysLen := len(ids)
for i := 0; i < keysLen; i += _tidbBulkSize {
var keys []int64
if (i + _tidbBulkSize) > keysLen {
keys = ids[i:]
} else {
keys = ids[i : i+_tidbBulkSize]
}
group.Go(func() error {
statsSQL := fmt.Sprintf(_tidbStatsSQL, xstr.JoinInts(keys))
rows, e := d.tidb.Query(c, statsSQL, businessID)
if e != nil {
err = e
return nil
}
defer rows.Close()
for rows.Next() {
s := &model.Stats{}
var likeChange, dislikeChange int64
e = rows.Scan(&s.ID, &s.Likes, &s.Dislikes, &likeChange, &dislikeChange)
s.Likes += likeChange
s.Dislikes += dislikeChange
if e != nil {
err = e
continue
}
mutex.Lock()
res[s.ID] = s
mutex.Unlock()
}
return nil
})
}
group.Wait()
if err != nil {
PromError("tidb stats Scan")
log.Error("tidb.rows.Stats.Scan error(%v)", err)
}
if len(res) == 0 {
res = nil
}
return
}
// OriginStats .
func (d *Dao) OriginStats(c context.Context, businessID, originID int64) (res map[int64]*model.Stats, err error) {
var rows *sql.Rows
if rows, err = d.statsOriginStmt.Query(c, businessID, originID); err != nil {
PromError("tidb:计数表")
log.Error("db.Stats.Query error(%v,%v,%v)", businessID, originID, err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.Stats{OriginID: originID}
var likeChange, dislikeChange int64
if err = rows.Scan(&b.ID, &b.Likes, &b.Dislikes, &likeChange, &dislikeChange); err != nil {
PromError("tidb:计数Scan")
log.Error("tidb.rows.Stats.Scan error(%v)", err)
return
}
b.Likes += likeChange
b.Dislikes += dislikeChange
if res == nil {
res = make(map[int64]*model.Stats)
}
res[b.ID] = b
}
err = rows.Err()
return
}
// Stat .
func (d *Dao) Stat(c context.Context, businessID, originID, messageID int64) (res model.Stats, err error) {
var likeChange, dislikeChange int64
if err = d.statStmt.QueryRow(c, businessID, originID, messageID).Scan(&res.ID, &res.Likes, &res.Dislikes, &res.OriginID, &likeChange, &dislikeChange); err != nil {
if err == sql.ErrNoRows {
err = nil
res.ID = messageID
res.OriginID = originID
return
}
PromError("tidb:查询计数表")
log.Error("tidb.Stat.Query error(%v,%v,%v,%v)", businessID, originID, messageID, err)
}
res.Likes += likeChange
res.Dislikes += dislikeChange
return
}
// RawStats stat changes
func (d *Dao) RawStats(c context.Context, businessID, originID, messageID int64) (res model.RawStats, err error) {
if err = d.statStmt.QueryRow(c, businessID, originID, messageID).Scan(&res.ID, &res.Likes, &res.Dislikes, &res.OriginID, &res.LikesChange, &res.DislikesChange); err != nil {
if err == sql.ErrNoRows {
err = nil
res.ID = messageID
res.OriginID = originID
return
}
PromError("tidb:查询change计数表")
log.Error("tidb.StatChanges.Query error(%v,%v,%v,%v)", businessID, originID, messageID, err)
}
return
}
// TxUpdateCounts update like info via transaction.
func (d *Dao) tidbTxUpdateCounts(c context.Context, tx *sql.Tx, businessID, originID, messageID int64, likesCount, dislikesCount, upMid int64) (err error) {
likeSQL := _tidbUpdateCountsSQL
if (likesCount == 0) && (dislikesCount == 0) {
return
}
if (likesCount != 0) && (dislikesCount == 0) {
if likesCount > 0 {
likeSQL += fmt.Sprintf("likes_count = likes_count + %v", likesCount)
} else {
likeSQL += fmt.Sprintf("likes_count = likes_count %v", likesCount)
}
} else if (likesCount == 0) && (dislikesCount != 0) {
if dislikesCount > 0 {
likeSQL += fmt.Sprintf("dislikes_count = dislikes_count + %v", dislikesCount)
} else {
likeSQL += fmt.Sprintf("dislikes_count = dislikes_count %v", dislikesCount)
}
} else {
if likesCount > 0 {
likeSQL += fmt.Sprintf("likes_count = likes_count + %v", likesCount)
} else {
likeSQL += fmt.Sprintf("likes_count = likes_count %v", likesCount)
}
if dislikesCount > 0 {
likeSQL += fmt.Sprintf(", dislikes_count = dislikes_count + %v", dislikesCount)
} else {
likeSQL += fmt.Sprintf(", dislikes_count = dislikes_count %v", dislikesCount)
}
}
if upMid > 0 {
likeSQL += fmt.Sprintf(", up_mid = %d", upMid)
}
if _, err = tx.Exec(likeSQL, businessID, originID, messageID, likesCount, dislikesCount, upMid); err != nil {
PromError("tidb:更新计数表")
log.Error("dao.tidbTxUpdateCounts tx.Exec(%s, %v,%v,%v) error(%v)", likeSQL, businessID, originID, messageID, err)
}
return
}
// tidbTxStat .
func (d *Dao) tidbTxStat(c context.Context, tx *sql.Tx, businessID, originID, messageID int64) (res model.Stats, err error) {
var likeChange, dislikeChange int64
if err = tx.Stmts(d.statStmt).QueryRow(c, businessID, originID, messageID).Scan(&res.ID, &res.Likes, &res.Dislikes, &res.OriginID, &likeChange, &dislikeChange); err != nil {
if err == sql.ErrNoRows {
err = nil
res.ID = messageID
res.OriginID = originID
return
}
PromError("tidb:查询计数表")
log.Error("tidb: Tx db.Stat.Query error(%v,%v,%v,%v)", businessID, originID, messageID, err)
}
res.Likes += likeChange
res.Dislikes += dislikeChange
return
}
// UpdateCount .
func (d *Dao) UpdateCount(c context.Context, businessID, originID, messageID int64, likeChange, dislikeChange int64) (err error) {
if _, err = d.updateCountChangeStmt.Exec(c, likeChange, dislikeChange, businessID, originID, messageID); err != nil {
PromError("tidb:更新change计数表")
log.Error("db.tidbUpdateCountChange.Exec error(%v,%v,%v,%v)", businessID, originID, messageID, err)
}
return
}
// tidbBeginTran begin transaction.
func (d *Dao) tidbBeginTran(c context.Context) (*sql.Tx, error) {
return d.tidb.Begin(c)
}
// UpdateCounts .
func (d *Dao) UpdateCounts(c context.Context, businessID, originID, messageID int64, likeCount, dislikeCount int64, upMid int64) (err error) {
var tx *sql.Tx
if tx, err = d.tidbBeginTran(c); err != nil {
log.Error("tx.BeginTran() error(%v)", err)
return
}
var stat model.Stats
if stat, err = d.tidbTxStat(c, tx, businessID, originID, messageID); err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback() error(%v)", err1)
}
return
}
if stat.Likes+likeCount < 0 {
likeCount = -stat.Likes
}
if stat.Dislikes+dislikeCount < 0 {
dislikeCount = -stat.Dislikes
}
if err = d.tidbTxUpdateCounts(c, tx, businessID, originID, messageID, likeCount, dislikeCount, upMid); err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback() error(%v)", err1)
}
return
}
if err = tx.Commit(); err != nil {
PromError("like:更新喜欢计数")
log.Error("tx.Commit() error(%v)", err)
return
}
return
}
// UpdateUpMids .
func (d *Dao) UpdateUpMids(c context.Context, businessID int64, data []*model.UpMidsReq) (rows int64, err error) {
var tx *sql.Tx
if tx, err = d.tidbBeginTran(c); err != nil {
log.Error("UpdateUpMids tx.BeginTran() error(%v)", err)
return
}
var res xsql.Result
for _, u := range data {
if res, err = tx.Exec("UPDATE counts SET up_mid = ? WHERE business_id = ? AND origin_id = ? AND message_id = ?", u.UpMid, businessID, u.OriginID, u.MessageID); err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("UpdateUpMids tx.Rollback() error(%v)", err1)
}
return
}
aff, _ := res.RowsAffected()
rows += aff
}
if err = tx.Commit(); err != nil {
PromError("like:更新UpMids")
log.Error("UpdateUpMids tx.Commit() error(%v)", err)
}
return
}
// ItemHasLike .
func (d *Dao) ItemHasLike(c context.Context, businessID int64, originID, messageID int64, mids []int64, typ int8) (res map[int64]int64, err error) {
var rows *sql.Rows
sqlStr := fmt.Sprintf(_tidbItemHasLikeSQL, xstr.JoinInts(mids))
if rows, err = d.tidb.Query(c, sqlStr, businessID, originID, messageID, typ); err != nil {
PromError("tidb:itemHasLike")
log.Error("tidb.ItemHasLike.Query asc error(%v,%v,%v,%v,%v)", businessID, originID, messageID, typ, err)
return
}
defer rows.Close()
for rows.Next() {
var t time.Time
var mid int64
if res == nil {
res = make(map[int64]int64)
}
if err = rows.Scan(&mid, &t); err != nil {
PromError("tidb:itemHasLikeScan")
log.Error("tidb.rows.itemHasLike.Scan error(%v)", err)
return
}
res[mid] = t.Unix()
}
err = rows.Err()
return
}
// UserHasLike .
func (d *Dao) UserHasLike(c context.Context, businessID, mid int64, messageIDs []int64, typ int8) (res []*model.ItemLikeRecord, err error) {
var rows *sql.Rows
sqlStr := fmt.Sprintf(_tidbUserHasLikeSQL, xstr.JoinInts(messageIDs))
if rows, err = d.tidb.Query(c, sqlStr, mid, businessID, typ); err != nil {
PromError("tidb:userHasLike")
log.Error("tidb.UserHasLike.Query asc error(%v,%v,%v,%v,%v)", mid, businessID, messageIDs, typ, err)
return
}
defer rows.Close()
for rows.Next() {
var t xtime.Time
var messageID int64
if err = rows.Scan(&messageID, &t); err != nil {
PromError("tidb:userHasLikeScan")
log.Error("tidb.rows.userHasLike.Scan error(%v)", err)
return
}
tmp := &model.ItemLikeRecord{
MessageID: messageID,
Time: t,
}
res = append(res, tmp)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,254 @@
package dao
import (
"context"
"fmt"
"testing"
"go-common/app/service/main/thumbup/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBusinesses(t *testing.T) {
var (
c = context.TODO()
)
convey.Convey("Businesses", t, func(ctx convey.C) {
res, err := d.Businesses(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoLikeState(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
originID = int64(1)
messageID = int64(1)
)
convey.Convey("LikeState", t, func(ctx convey.C) {
res, err := d.LikeState(c, mid, businessID, originID, messageID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoUserLikeCount(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
mid = int64(1)
typ = int8(1)
)
convey.Convey("UserLikeCount", t, func(ctx convey.C) {
res, err := d.UserLikeCount(c, businessID, mid, typ)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawItemLikeList(t *testing.T) {
var (
c = context.TODO()
messageID = int64(1)
businessID = int64(1)
originID = int64(1)
state = int8(1)
start = int(1)
end = int(1)
)
convey.Convey("RawItemLikeList", t, func(ctx convey.C) {
_, err := d.RawItemLikeList(c, messageID, businessID, originID, state, start, end)
ctx.Convey("Then err should be nil.res,all should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoRawUserLikeList(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
state = int8(1)
start = int(1)
end = int(1)
)
convey.Convey("RawUserLikeList", t, func(ctx convey.C) {
_, err := d.RawUserLikeList(c, mid, businessID, state, start, end)
ctx.Convey("Then err should be nil.res,all should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
// ctx.So(all, convey.ShouldNotBeNil)
// ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoMessageStats(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
ids = []int64{1}
)
convey.Convey("MessageStats", t, func(ctx convey.C) {
res, err := d.MessageStats(c, businessID, ids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldBeEmpty)
})
})
}
func TestDaoOriginStats(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
)
convey.Convey("OriginStats", t, func(ctx convey.C) {
res, err := d.OriginStats(c, businessID, originID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoStat(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
messageID = int64(1)
)
convey.Convey("Stat", t, func(ctx convey.C) {
res, err := d.Stat(c, businessID, originID, messageID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaotidbRawStats(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
messageID = int64(1)
)
convey.Convey("RawStats", t, func(ctx convey.C) {
res, err := d.RawStats(c, businessID, originID, messageID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdateCount(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
messageID = int64(1)
likeChange = int64(1)
dislikeChange = int64(1)
)
convey.Convey("UpdateCount", t, func(ctx convey.C) {
err := d.UpdateCount(c, businessID, originID, messageID, likeChange, dislikeChange)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
// INSERT INTO `bilibili_likes`.`counts`(`id`, `mtime`, `ctime`, `business_id`, `origin_id`, `message_id`, `likes_count`, `dislikes_count`, `likes_change`, `dislikes_change`, `up_mid`) VALUES (0, '2018-11-14 17:25:40', '2018-11-03 12:06:27', 3, 0, 10099865, 100, 0, 0, 0, 8167601);
func Test_updateCounts(t *testing.T) {
convey.Convey("get data", t, func() {
c := context.Background()
messageID := int64(10099865)
bid := int64(3)
oid := int64(0)
stat, err := d.Stat(c, bid, oid, messageID)
convey.So(err, convey.ShouldBeNil)
args := [][2]int64{
{1, 1},
{-1000, -1000},
{1, 0},
{0, 1},
{-1, 0},
{-1, -1},
{0, -1},
{0, 0},
{100, -10000},
{100, 20},
{-10, 20},
}
var l, dd int64
for _, x := range args {
l = x[0]
dd = x[1]
convey.Convey(fmt.Sprintf("like %v dislike %v", l, dd), func() {
err := d.UpdateCounts(c, bid, oid, messageID, l, dd, 0)
convey.So(err, convey.ShouldBeNil)
nstat, err := d.Stat(c, bid, oid, messageID)
convey.So(err, convey.ShouldBeNil)
likes := stat.Likes + l
if likes < 0 {
likes = 0
}
dislikes := stat.Dislikes + dd
if dislikes < 0 {
dislikes = 0
}
convey.So(nstat.Likes, convey.ShouldEqual, likes)
convey.So(nstat.Dislikes, convey.ShouldEqual, dislikes)
})
}
})
}
func TestDaoUpdateUpMids(t *testing.T) {
var (
c = context.TODO()
businessID = int64(2)
)
data := []*model.UpMidsReq{
{MessageID: 100, UpMid: 100},
{MessageID: 200, UpMid: 200},
{MessageID: 300, UpMid: 300},
}
convey.Convey("UpdateUpMids", t, func(ctx convey.C) {
_, err := d.UpdateUpMids(c, businessID, data)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoItemHasLike(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
originID = int64(1)
messageID = int64(1)
mids = []int64{1, 2, 3}
)
convey.Convey("ItemHasLike", t, func(ctx convey.C) {
res, err := d.ItemHasLike(c, businessID, originID, messageID, mids, model.StateLike)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeEmpty)
})
})
}

View File

@@ -0,0 +1,173 @@
package dao
import (
"context"
"fmt"
pb "go-common/app/service/main/thumbup/api"
"go-common/app/service/main/thumbup/model"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/stat/prom"
xtime "go-common/library/time"
pkgerr "github.com/pkg/errors"
)
func userLikesKey(businessID, mid int64, state int8) string {
return fmt.Sprintf("u_m_%d_b_%d_%d", mid, businessID, state)
}
// CacheUserLikeList .
func (d *Dao) CacheUserLikeList(c context.Context, mid, businessID int64, state int8, start, end int) (res []*model.ItemLikeRecord, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, state)
items, err := redis.Values(conn.Do("ZREVRANGE", key, start, end, "withscores"))
if err != nil {
if err == redis.ErrNil {
err = nil
return
}
err = pkgerr.Wrap(err, "get user like cache")
PromError("redis:CacheUserLikeList")
log.Errorv(c, log.KV("CacheUserLikeList", fmt.Sprintf("%+v", err)))
return
}
for len(items) > 0 {
var id, t int64
if items, err = redis.Scan(items, &id, &t); err != nil {
err = pkgerr.Wrap(err, "get user like cache")
PromError("redis:CacheUserLikeList")
log.Errorv(c, log.KV("CacheUserLikeList", fmt.Sprintf("%+v", err)))
return
}
res = append(res, &model.ItemLikeRecord{MessageID: id, Time: xtime.Time(t)})
}
return
}
// ExpireUserLikesCache .
func (d *Dao) ExpireUserLikesCache(c context.Context, mid, businessID int64, state int8) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, state)
if ok, err = redis.Bool(conn.Do("EXPIRE", key, d.redisUserLikesExpire)); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:expire用户点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s) error(%v)", key, err)))
}
return
}
// UserLikeExists .
func (d *Dao) UserLikeExists(c context.Context, mid, businessID int64, messageIDs []int64, state int8) (res map[int64]*pb.UserLikeState, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, state)
res = make(map[int64]*pb.UserLikeState)
prom.CacheHit.Incr("userLikeList")
for _, name := range messageIDs {
conn.Send("ZSCORE", key, name)
}
if err = conn.Flush(); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:UserLikeExists")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Flush() error(%v)", err)))
return
}
for _, name := range messageIDs {
var ts int64
if ts, err = redis.Int64(conn.Receive()); err == nil {
res[name] = &pb.UserLikeState{
Mid: mid,
Time: xtime.Time(ts),
State: pb.State(state),
}
} else if err == redis.ErrNil {
err = nil
} else {
err = pkgerr.Wrap(err, "")
PromError("redis:UserLikeExists")
log.Errorv(c, log.KV("log", fmt.Sprintf("UserLikeExists conn.Receive() error(%v)", err)))
return
}
}
return
}
// AppendCacheUserLikeList .
func (d *Dao) AppendCacheUserLikeList(c context.Context, mid int64, item *model.ItemLikeRecord, businessID int64, state int8) (err error) {
if item == nil {
return
}
limit := d.BusinessIDMap[businessID].UserLikesLimit
var count int
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, state)
id := item.MessageID
score := int64(item.Time)
if err = conn.Send("ZADD", key, "CH", score, id); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:用户点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZADD, %s, %d, %v) error(%v)", key, score, id, err)))
return
}
count++
if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(limit + 1)); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:用户点赞列表rm")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREMRANGEBYRANK, %s, 0, %d) error(%v)", key, -(limit+1), err)))
return
}
count++
if err = conn.Send("EXPIRE", key, d.redisUserLikesExpire); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:用户点赞列表过期")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s, %d) error(%v)", key, d.redisUserLikesExpire, err)))
return
}
count++
if err = conn.Flush(); err != nil {
PromError("redis:用户点赞列表flush")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Flush error(%v)", err)))
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:用户点赞列表receive")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Receive error(%v)", err)))
return
}
}
return
}
// DelUserLikeCache .
func (d *Dao) DelUserLikeCache(c context.Context, mid, businessID int64, messageID int64, state int8) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, state)
if _, err = conn.Do("ZREM", key, messageID); err != nil {
err = pkgerr.Wrap(err, "")
PromError("redis:zrem用户点赞列表")
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREM, %s, %v) error(%v)", key, messageID, err)))
}
return
}
// UserLikesCountCache .
func (d *Dao) UserLikesCountCache(c context.Context, businessID, mid int64) (res int, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, model.StateLike)
res, err = redis.Int(conn.Do("ZCOUNT", key, "(0", "+inf"))
if err != nil {
err = pkgerr.Wrap(err, "")
log.Errorv(c, log.KV("log", fmt.Sprintf("dao.UserLikesCountCache(%d, %d) err:%v", businessID, mid, err)))
PromError("redis:用户点赞总数")
}
return
}

View File

@@ -0,0 +1,137 @@
package dao
import (
"context"
"go-common/app/service/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaouserLikesKey(t *testing.T) {
var (
businessID = int64(1)
mid = int64(1)
state = int8(1)
)
convey.Convey("userLikesKey", t, func(ctx convey.C) {
p1 := userLikesKey(businessID, mid, state)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoCacheUserLikeList(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
state = int8(1)
start = int(1)
end = int(10)
)
convey.Convey("CacheUserLikeList", t, func(ctx convey.C) {
_, err := d.CacheUserLikeList(c, mid, businessID, state, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
// ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddCacheUserLikeList(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
miss = []*model.ItemLikeRecord{}
businessID = int64(1)
state = int8(1)
)
convey.Convey("AddCacheUserLikeList", t, func(ctx convey.C) {
err := d.AddCacheUserLikeList(c, mid, miss, businessID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoExpireUserLikesCache(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
state = int8(1)
)
convey.Convey("ExpireUserLikesCache", t, func(ctx convey.C) {
ok, err := d.ExpireUserLikesCache(c, mid, businessID, state)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
}
func TestDaoUserLikeExists(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
messageIDs = []int64{}
state = int8(1)
)
convey.Convey("UserLikeExists", t, func(ctx convey.C) {
res, err := d.UserLikeExists(c, mid, businessID, messageIDs, state)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAppendCacheUserLikeList(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
item = &model.ItemLikeRecord{}
businessID = int64(1)
state = int8(1)
)
convey.Convey("AppendCacheUserLikeList", t, func(ctx convey.C) {
err := d.AppendCacheUserLikeList(c, mid, item, businessID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoDelUserLikeCache(t *testing.T) {
var (
c = context.TODO()
mid = int64(1)
businessID = int64(1)
messageID = int64(1)
state = int8(1)
)
convey.Convey("DelUserLikeCache", t, func(ctx convey.C) {
err := d.DelUserLikeCache(c, mid, businessID, messageID, state)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoUserLikesCountCache(t *testing.T) {
var (
c = context.TODO()
businessID = int64(1)
mid = int64(1)
)
convey.Convey("UserLikesCountCache", t, func(ctx convey.C) {
res, err := d.UserLikesCountCache(c, businessID, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}