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,68 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"databus.go",
"item_likes_redis.go",
"memcached.go",
"redis.go",
"tidb.go",
"user_likes_redis.go",
],
importpath = "go-common/app/job/main/thumbup/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/thumbup/conf:go_default_library",
"//app/job/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/time:go_default_library",
"//library/xstr: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"],
)
go_test(
name = "go_default_test",
srcs = [
"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"],
tags = ["automanaged"],
deps = [
"//app/job/main/thumbup/conf:go_default_library",
"//app/job/main/thumbup/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,104 @@
package dao
import (
"context"
"time"
"go-common/app/job/main/thumbup/conf"
"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"
)
// Dao .
type Dao struct {
// config
c *conf.Config
// tidb
tidb *tidb.DB
itemTidb *tidb.DB
// memcache
mc *memcache.Pool
mcStatsExpire int32
// redis
redis *xredis.Pool
redisStatsExpire int64
redisUserLikesExpire int64
redisItemLikesExpire int64
// databus
statDbus *databus.Databus
// stmt
businessesStmt *tidb.Stmts
itemLikesStmt *tidb.Stmts
userLikesStmt *tidb.Stmts
likeStateStmt *tidb.Stmts
statStmt *tidb.Stmts
updateLikeStateStmt *tidb.Stmts
}
// New .
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
// tidb
tidb: tidb.NewTiDB(c.Tidb),
itemTidb: tidb.NewTiDB(c.ItemTidb),
// memcache
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),
// databus
statDbus: databus.New(c.Databus.Stat),
}
d.statStmt = d.tidb.Prepared(_statSQL)
d.itemLikesStmt = d.itemTidb.Prepared(_itemLikesSQL)
d.businessesStmt = d.tidb.Prepared(_businessesSQL)
d.userLikesStmt = d.tidb.Prepared(_userLikeListSQL)
d.likeStateStmt = d.tidb.Prepared(_likeStateSQL)
d.updateLikeStateStmt = d.tidb.Prepared(_updateLikeStateSQL)
return
}
// Close .
func (d *Dao) Close() {
d.mc.Close()
d.tidb.Close()
d.redis.Close()
}
// Ping .
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.pingMC(c); err != nil {
log.Error("d.pingMC error(%v)", err)
return
}
if err = d.pingRedis(c); err != nil {
log.Error("d.pingRedis error(%v)", err)
}
return
}
// 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)
defer conn.Close()
_, err = conn.Do("SET", "PING", "PONG")
return
}

View File

@@ -0,0 +1,34 @@
package dao
import (
"flag"
"go-common/app/job/main/thumbup/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.thumbup-job")
flag.Set("conf_token", "4de31fc23726f3c68a1cf74b65d61940")
flag.Set("tree_id", "82533")
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-job-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,30 @@
package dao
import (
"context"
"strconv"
"time"
"go-common/app/job/main/thumbup/model"
"go-common/library/log"
)
// PubStatDatabus .
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.statDbus.Send error(%v)", err)
return
}
log.Info("pub stat databus success params(%+v)", msg)
return
}

View File

@@ -0,0 +1,27 @@
package dao
import (
"context"
"go-common/app/job/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoPubStatDatabus(t *testing.T) {
convey.Convey("PubStatDatabus", t, func(convCtx convey.C) {
var (
c = context.Background()
business = "archive"
mid = int64(2233)
s = &model.Stats{}
upMid = int64(333)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.PubStatDatabus(c, business, mid, s, upMid)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,116 @@
package dao
import (
"context"
"fmt"
"go-common/app/job/main/thumbup/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
func itemLikesKey(businessID, messageID int64, state int8) string {
return fmt.Sprintf("i2_m_%d_b_%d_%d", messageID, businessID, state)
}
// 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 {
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s) error(%v)", key, err)))
}
return
}
// AddItemLikesCache .
func (d *Dao) AddItemLikesCache(c context.Context, businessID, messageID int64, typ int8, limit int, items []*model.UserLikeRecord) (err error) {
if len(items) == 0 {
return
}
conn := d.redis.Get(c)
defer conn.Close()
key := itemLikesKey(businessID, messageID, typ)
if err = conn.Send("DEL", key); err != nil {
log.Errorv(c, log.KV("AddCacheItemLikeList", fmt.Sprintf("AddCacheItemLikeList conn.Send(DEL, %s) error(%+v)", key, err)))
return
}
args := redis.Args{}.Add(key).Add("CH")
for _, item := range items {
args = args.Add(int64(item.Time)).Add(item.Mid)
}
if err = conn.Send("ZADD", args...); err != nil {
log.Error("zadd key(%s) args(%v) error(%v)", key, args, err)
return
}
if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(limit + 1)); err != nil {
log.Error("zremrangebyrank error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisItemLikesExpire); err != nil {
log.Error("expire key(%s) error(%v)", key, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("redis flush error(%v)", err)
return
}
for i := 0; i < 4; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("redis receive error(%v)", err)
return
}
}
return
}
// AppendCacheItemLikeList .
func (d *Dao) AppendCacheItemLikeList(c context.Context, messageID int64, item *model.UserLikeRecord, businessID int64, state int8, limit int) (err error) {
if item == nil {
return
}
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 {
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 {
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 {
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 {
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 {
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 {
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREM, %s, %v) error(%v)", key, mid, err)))
}
return
}

View File

@@ -0,0 +1,100 @@
package dao
import (
"context"
"testing"
"go-common/app/job/main/thumbup/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoitemLikesKey(t *testing.T) {
convey.Convey("itemLikesKey", t, func(convCtx convey.C) {
var (
businessID = int64(33)
messageID = int64(5566)
state = int8(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := itemLikesKey(businessID, messageID, state)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoExpireItemLikesCache(t *testing.T) {
convey.Convey("ExpireItemLikesCache", t, func(convCtx convey.C) {
var (
c = context.Background()
messageID = int64(5566)
businessID = int64(33)
state = int8(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
ok, err := d.ExpireItemLikesCache(c, messageID, businessID, state)
convCtx.Convey("Then err should be nil.ok should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddItemLikesCache(t *testing.T) {
convey.Convey("AddItemLikesCache", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
messageID = int64(5566)
typ = int8(1)
limit = int(100)
items = []*model.UserLikeRecord{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AddItemLikesCache(c, businessID, messageID, typ, limit, items)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAppendCacheItemLikeList(t *testing.T) {
convey.Convey("AppendCacheItemLikeList", t, func(convCtx convey.C) {
var (
c = context.Background()
messageID = int64(5566)
item = &model.UserLikeRecord{}
businessID = int64(33)
state = int8(1)
limit = int(100)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AppendCacheItemLikeList(c, messageID, item, businessID, state, limit)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelItemLikeCache(t *testing.T) {
convey.Convey("DelItemLikeCache", t, func(convCtx convey.C) {
var (
c = context.Background()
messageID = int64(5566)
businessID = int64(33)
mid = int64(2233)
state = int8(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.DelItemLikeCache(c, messageID, businessID, mid, state)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,37 @@
package dao
import (
"context"
"fmt"
"go-common/app/job/main/thumbup/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/xstr"
)
func statsKey(businessID, messageID int64) string {
return fmt.Sprintf("m_%d_b_%d", messageID, businessID)
}
// AddStatsCache .
func (d *Dao) AddStatsCache(c context.Context, businessID int64, ms ...*model.Stats) (err error) {
if len(ms) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
for _, m := range ms {
if m == nil {
continue
}
key := statsKey(businessID, m.ID)
bs := xstr.JoinInts([]int64{m.Likes, m.Dislikes})
item := memcache.Item{Key: key, Value: []byte(bs), Expiration: d.mcStatsExpire}
if err = conn.Set(&item); err != nil {
log.Error("conn.Set(%s) error(%v)", key, err)
return
}
}
return
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"testing"
"go-common/app/job/main/thumbup/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaostatsKey(t *testing.T) {
convey.Convey("statsKey", t, func(convCtx convey.C) {
var (
businessID = int64(33)
messageID = int64(2233)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := statsKey(businessID, messageID)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddStatsCache(t *testing.T) {
convey.Convey("AddStatsCache", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
ms = &model.Stats{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AddStatsCache(c, businessID, ms)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,44 @@
package dao
import (
"context"
"fmt"
"go-common/app/job/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 {
log.Error("conn.Do(EXPIRE, %s, %d) error(%v)", key, d.redisStatsExpire, err)
}
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 {
log.Error("conn.DO(HMSET, %s, %v) error(%v)", key, commonds, err)
}
return
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"go-common/app/job/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaohashStatsKey(t *testing.T) {
convey.Convey("hashStatsKey", t, func(convCtx convey.C) {
var (
businessID = int64(33)
originID = int64(7788)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := hashStatsKey(businessID, originID)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoExpireHashStatsCache(t *testing.T) {
convey.Convey("ExpireHashStatsCache", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
originID = int64(7788)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
ok, err := d.ExpireHashStatsCache(c, businessID, originID)
convCtx.Convey("Then err should be nil.ok should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddHashStatsCache(t *testing.T) {
convey.Convey("AddHashStatsCache", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
originID = int64(7788)
stats = &model.Stats{}
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AddHashStatsCache(c, businessID, originID, stats)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,226 @@
package dao
import (
"context"
"fmt"
"time"
"go-common/app/job/main/thumbup/model"
sql "go-common/library/database/tidb"
"go-common/library/log"
xtime "go-common/library/time"
)
const (
_statSQL = "SELECT message_id, likes_count, dislikes_count, origin_id, likes_change, dislikes_change FROM counts WHERE business_id = ? AND origin_id = ? AND message_id = ?"
_itemLikesSQL = "SELECT mid, mtime FROM likes WHERE business_id =? AND origin_id =? AND message_id =? AND type = ? ORDER BY mtime desc LIMIT ?"
_likeStateSQL = "SELECT type FROM likes WHERE mid=? AND business_id=? AND origin_id=? AND message_id=?"
_businessesSQL = "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'"
_updateCountsSQL = "INSERT INTO counts (business_id, origin_id, message_id, likes_count, dislikes_count, up_mid) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE "
_userLikeListSQL = "SELECT message_id, mtime FROM likes WHERE mid = ? AND business_id =? AND type = ? ORDER BY mtime desc LIMIT ?"
_updateLikeStateSQL = "INSERT INTO likes (business_id, origin_id, message_id, mid, type, mtime) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE type=?, mtime=?"
)
// Business .
func (d *Dao) Business(c context.Context) (res []*model.Business, err error) {
var rows *sql.Rows
if rows, err = d.businessesStmt.Query(c); err != nil {
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 {
return
}
res = append(res, b)
}
err = rows.Err()
return
}
// UserLikes .
func (d *Dao) UserLikes(c context.Context, mid, businessID int64, typ int8, limit int) (res []*model.ItemLikeRecord, err error) {
var rows *sql.Rows
if rows, err = d.userLikesStmt.Query(c, mid, businessID, typ, limit); err != nil {
log.Error("d.tidb.userList error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
b := &model.ItemLikeRecord{}
var t time.Time
if err = rows.Scan(&b.MessageID, &t); err != nil {
log.Error("tidb.rows.userList error(%v)", err)
return
}
b.Time = xtime.Time(t.Unix())
res = append(res, b)
}
err = rows.Err()
return
}
// ItemLikes .
func (d *Dao) ItemLikes(c context.Context, businessID, originID, messageID int64, typ int8, limit int) (res []*model.UserLikeRecord, err error) {
var rows *sql.Rows
if rows, err = d.itemLikesStmt.Query(c, businessID, originID, messageID, typ, limit); err != nil {
return
}
defer rows.Close()
for rows.Next() {
b := &model.UserLikeRecord{}
var t time.Time
if err = rows.Scan(&b.Mid, &t); err != nil {
return
}
b.Time = xtime.Time(t.Unix())
res = append(res, b)
}
err = rows.Err()
return
}
// LikeState .
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
}
log.Error("tidbLikeState(%d,%d,%d,%d) error(%v)", mid, businessID, originID, messageID, err)
}
return
}
// UpdateLikeState .
func (d *Dao) UpdateLikeState(c context.Context, mid, businessID, originID, messageID int64, state int8, likeTime time.Time) (res model.Stats, err error) {
var tx *sql.Tx
if tx, err = d.tidb.Begin(c); err != nil {
log.Error("d.tidb.Begin() error(%v)", err)
return
}
if _, err = tx.Stmts(d.updateLikeStateStmt).Exec(c, businessID, originID, messageID, mid, state, likeTime, state, likeTime); err != nil {
log.Error("d.tidbUpdateLikeState error(%v)", err)
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback error(%v)", err)
}
return
}
if res, 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 err = tx.Commit(); err != nil {
log.Error("tx.Commit() error(%v)", err)
}
return
}
// UpdateCounts .
func (d *Dao) UpdateCounts(c context.Context, businessID, originID, messageID, likeCounts, dislikeCounts, upMid int64) (err error) {
var tx *sql.Tx
if tx, err = d.tidb.Begin(c); err != nil {
log.Error("tx.tidb.Begin() 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+likeCounts < 0 {
likeCounts = -stat.Likes
}
if stat.Dislikes+dislikeCounts < 0 {
dislikeCounts = -stat.Dislikes
}
if err = d.tidbTxUpdateCounts(c, tx, businessID, originID, messageID, likeCounts, dislikeCounts, upMid); err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback() error(%v)", err1)
}
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit() error(%v)", 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
}
log.Error("tidb: Tx db.Stat.Query error(%v,%v,%v,%v)", businessID, originID, messageID, err)
return
}
res.Likes += likeChange
res.Dislikes += dislikeChange
return
}
// TxUpdateCounts .
func (d *Dao) tidbTxUpdateCounts(c context.Context, tx *sql.Tx, businessID, originID, messageID int64, likesCount, dislikesCount, upMid int64) (err error) {
likeSQL := _updateCountsSQL
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 {
log.Error("dao.tidbTxUpdateCounts tx.Exec(%s, %v,%v,%v) error(%v)", likeSQL, businessID, originID, messageID, 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
}
log.Error("tidb.Stat.Query error(%v)", err)
}
res.Likes += likeChange
res.Dislikes += dislikeChange
return
}

View File

@@ -0,0 +1,122 @@
package dao
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBusiness(t *testing.T) {
convey.Convey("Business", t, func(convCtx convey.C) {
var (
c = context.Background()
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.Business(c)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUserLikes(t *testing.T) {
convey.Convey("UserLikes", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(2233)
businessID = int64(33)
typ = int8(1)
limit = int(100)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.UserLikes(c, mid, businessID, typ, limit)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoItemLikes(t *testing.T) {
convey.Convey("ItemLikes", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
originID = int64(7788)
messageID = int64(5566)
typ = int8(1)
limit = int(100)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.ItemLikes(c, businessID, originID, messageID, typ, limit)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateLikeState(t *testing.T) {
convey.Convey("UpdateLikeState", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(2233)
businessID = int64(33)
originID = int64(7788)
messageID = int64(5566)
state = int8(1)
likeTime = time.Now()
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.UpdateLikeState(c, mid, businessID, originID, messageID, state, likeTime)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateCounts(t *testing.T) {
convey.Convey("UpdateCounts", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
originID = int64(7788)
messageID = int64(5566)
likeCounts = int64(100)
dislikeCounts = int64(100)
upMid = int64(999)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.UpdateCounts(c, businessID, originID, messageID, likeCounts, dislikeCounts, upMid)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoStat(t *testing.T) {
convey.Convey("Stat", t, func(convCtx convey.C) {
var (
c = context.Background()
businessID = int64(33)
originID = int64(7788)
messageID = int64(5566)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
res, err := d.Stat(c, businessID, originID, messageID)
convCtx.Convey("Then err should be nil.res should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,116 @@
package dao
import (
"context"
"fmt"
"go-common/app/job/main/thumbup/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
func userLikesKey(businessID, mid int64, state int8) string {
return fmt.Sprintf("u_m_%d_b_%d_%d", mid, businessID, state)
}
// 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 {
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(EXPIRE, %s) error(%v)", key, err)))
}
return
}
// AppendCacheUserLikeList .
func (d *Dao) AppendCacheUserLikeList(c context.Context, mid int64, item *model.ItemLikeRecord, businessID int64, state int8, limit int) (err error) {
if item == nil {
return
}
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 {
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 {
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 {
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 {
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 {
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Receive error(%v)", err)))
return
}
}
return
}
// AddUserLikesCache .
func (d *Dao) AddUserLikesCache(c context.Context, mid, businessID int64, items []*model.ItemLikeRecord, typ int8, limit int) (err error) {
if len(items) == 0 {
return
}
conn := d.redis.Get(c)
defer conn.Close()
key := userLikesKey(businessID, mid, typ)
if err = conn.Send("DEL", key); err != nil {
log.Errorv(c, log.KV("AddCacheUserLikeList", fmt.Sprintf("conn.Send(DEL, %s) error(%+v)", key, err)))
return
}
args := redis.Args{}.Add(key).Add("CH")
for _, item := range items {
args = args.Add(int64(item.Time)).Add(item.MessageID)
}
if err = conn.Send("ZADD", args...); err != nil {
log.Error("zadd key(%s) args(%v) error(%v)", key, args, err)
return
}
if err = conn.Send("ZREMRANGEBYRANK", key, 0, -(limit + 1)); err != nil {
log.Error("zremrangebyrank error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisUserLikesExpire); err != nil {
log.Error("expire key(%s) error(%v)", key, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("redis flush error(%v)", err)
return
}
for i := 0; i < 4; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("redis 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 {
log.Errorv(c, log.KV("log", fmt.Sprintf("conn.Send(ZREM, %s, %v) error(%v)", key, messageID, err)))
}
return
}

View File

@@ -0,0 +1,99 @@
package dao
import (
"context"
"go-common/app/job/main/thumbup/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaouserLikesKey(t *testing.T) {
convey.Convey("userLikesKey", t, func(convCtx convey.C) {
var (
businessID = int64(33)
mid = int64(2233)
state = int8(1)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
p1 := userLikesKey(businessID, mid, state)
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
convCtx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoExpireUserLikesCache(t *testing.T) {
convey.Convey("ExpireUserLikesCache", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
businessID = int64(0)
state = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
ok, err := d.ExpireUserLikesCache(c, mid, businessID, state)
convCtx.Convey("Then err should be nil.ok should not be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
convCtx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAppendCacheUserLikeList(t *testing.T) {
convey.Convey("AppendCacheUserLikeList", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
item = &model.ItemLikeRecord{}
businessID = int64(0)
state = int8(0)
limit = int(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AppendCacheUserLikeList(c, mid, item, businessID, state, limit)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddUserLikesCache(t *testing.T) {
convey.Convey("AddUserLikesCache", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
businessID = int64(0)
items = []*model.ItemLikeRecord{}
typ = int8(0)
limit = int(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.AddUserLikesCache(c, mid, businessID, items, typ, limit)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelUserLikeCache(t *testing.T) {
convey.Convey("DelUserLikeCache", t, func(convCtx convey.C) {
var (
c = context.Background()
mid = int64(0)
businessID = int64(0)
messageID = int64(0)
state = int8(0)
)
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
err := d.DelUserLikeCache(c, mid, businessID, messageID, state)
convCtx.Convey("Then err should be nil.", func(convCtx convey.C) {
convCtx.So(err, convey.ShouldBeNil)
})
})
})
}