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,89 @@
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.cache.go",
"dao.go",
"forbid_user.go",
"location.go",
"monkey.go",
"user.go",
"user_base.go",
"user_black.go",
"user_card.go",
"user_fan.go",
"user_follow.go",
"user_like.go",
"user_stat.go",
],
importpath = "go-common/app/service/bbq/user/internal/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/notice-service/api/v1:go_default_library",
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/user/internal/conf:go_default_library",
"//app/service/bbq/user/internal/model:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/filter/api/grpc/v1:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/rpc/warden: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/bouk/monkey: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.cache_test.go",
"dao_test.go",
"user_base_test.go",
"user_black_test.go",
"user_card_test.go",
"user_fan_test.go",
"user_follow_test.go",
"user_like_test.go",
"user_stat_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/user/internal/conf:go_default_library",
"//app/service/bbq/user/internal/model:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,102 @@
// 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: -batch=10 -max_group=10 -batch_err=break -nullcache=&api.UserBase{Mid:-1} -check_null_code=$==nil||$.Mid==-1
UserBase(c context.Context, mid []int64) (map[int64]*api.UserBase, error)
}
*/
package dao
import (
"context"
"sync"
"go-common/app/service/bbq/user/api"
"go-common/library/stat/prom"
"go-common/library/sync/errgroup"
)
var _ _cache
// UserBase get data from cache if miss will call source method, then add to cache.
func (d *Dao) UserBase(c context.Context, keys []int64) (res map[int64]*api.UserBase, err error) {
if len(keys) == 0 {
return
}
addCache := true
if res, err = d.CacheUserBase(c, keys); err != nil {
addCache = false
res = nil
err = nil
}
var miss []int64
for _, key := range keys {
if (res == nil) || (res[key] == nil) {
miss = append(miss, key)
}
}
prom.CacheHit.Add("UserBase", int64(len(keys)-len(miss)))
for k, v := range res {
if v == nil || v.Mid == -1 {
delete(res, k)
}
}
missLen := len(miss)
if missLen == 0 {
return
}
missData := make(map[int64]*api.UserBase, missLen)
prom.CacheMiss.Add("UserBase", int64(missLen))
var mutex sync.Mutex
group, ctx := errgroup.WithContext(c)
if missLen > 10 {
group.GOMAXPROCS(10)
}
var run = func(ms []int64) {
group.Go(func() (err error) {
data, err := d.RawUserBase(ctx, ms)
mutex.Lock()
for k, v := range data {
missData[k] = v
}
mutex.Unlock()
return
})
}
var (
i int
n = missLen / 10
)
for i = 0; i < n; i++ {
run(miss[i*n : (i+1)*n])
}
if len(miss[i*n:]) > 0 {
run(miss[i*n:])
}
err = group.Wait()
if res == nil {
res = make(map[int64]*api.UserBase, len(keys))
}
for k, v := range missData {
res[k] = v
}
if err != nil {
return
}
for _, key := range miss {
if res[key] == nil {
missData[key] = &api.UserBase{Mid: -1}
}
}
if !addCache {
return
}
d.cache.Do(c, func(c context.Context) {
d.AddCacheUserBase(c, missData)
})
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoUserBase(t *testing.T) {
convey.Convey("UserBase", t, func(ctx convey.C) {
var (
c = context.Background()
keys = []int64{88895104, 88895105}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.UserBase(c, keys)
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,118 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/api"
"go-common/library/log"
"go-common/library/sync/pipeline/fanout"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/app/service/bbq/user/internal/conf"
acc "go-common/app/service/main/account/api"
filter "go-common/app/service/main/filter/api/grpc/v1"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"go-common/library/net/rpc/warden"
)
// Dao dao
type Dao struct {
c *conf.Config
cache *fanout.Fanout
redis *redis.Pool
db *xsql.DB
accountClient acc.AccountClient
noticeClient notice.NoticeClient
filterClient filter.FilterClient
}
//go:generate $GOPATH/src/go-common/app/tool/cache/gen
type _cache interface {
// cache: -batch=10 -max_group=10 -batch_err=break -nullcache=&api.UserBase{Mid:-1} -check_null_code=$==nil||$.Mid==-1
UserBase(c context.Context, mid []int64) (map[int64]*api.UserBase, error)
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
redis: redis.NewPool(c.Redis),
db: xsql.NewMySQL(c.MySQL),
accountClient: newAccountClient(c.GRPCClient["account"]),
noticeClient: newNoticeClient(c.GRPCClient["notice"]),
filterClient: newFilterClient(c.GRPCClient["filter"]),
}
return
}
// newNoticeClient .
func newFilterClient(cfg *conf.GRPCConf) filter.FilterClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return filter.NewFilterClient(cc)
}
// newNoticeClient .
func newNoticeClient(cfg *conf.GRPCConf) notice.NoticeClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return notice.NewNoticeClient(cc)
}
//newAccountClient .
func newAccountClient(cfg *conf.GRPCConf) acc.AccountClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return acc.NewAccountClient(cc)
}
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return d.db.Ping(ctx)
}
// BeginTran begin mysql transaction
func (d *Dao) BeginTran(c context.Context) (*xsql.Tx, error) {
return d.db.Begin(c)
}
// CreateNotice 创建通知
func (d *Dao) CreateNotice(ctx context.Context, notice *notice.NoticeBase) (err error) {
_, err = d.noticeClient.CreateNotice(ctx, notice)
if err != nil {
log.Errorv(ctx, log.KV("log", "create notice fail: notice="+notice.String()))
return
}
log.V(1).Infov(ctx, log.KV("log", "create notice: notice="+notice.String()))
return
}
// Filter .
func (d *Dao) Filter(ctx context.Context, content string, area string) (level int32, err error) {
req := new(filter.FilterReq)
req.Message = content
req.Area = area
reply, err := d.filterClient.Filter(ctx, req)
if err != nil {
log.Errorv(ctx, log.KV("log", "filter fail : req="+req.String()))
return
}
level = reply.Level
log.V(1).Infov(ctx, log.KV("log", "get filter reply="+reply.String()))
return
}

View File

@@ -0,0 +1,39 @@
package dao
import (
"flag"
"go-common/app/service/bbq/user/internal/conf"
"go-common/library/conf/paladin"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "bbq.bbq.user")
flag.Set("conf_token", "3523c3d66dbc91081454d8ef7af9a680")
flag.Set("tree_id", "75578")
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/test.toml")
flag.Set("deploy.env", "uat")
}
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
if err := paladin.Watch("test.toml", conf.Conf); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,29 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/internal/model"
"go-common/library/log"
)
const (
_insertforbidUser = "insert into forbid_user (`mid`, `expire_time`, `forbid_status`) values (?, ?, ?) on duplicate key update `expire_time` = VALUES(`expire_time`), `forbid_status` = VALUES(`forbid_status`)"
)
//ForbidUser .
func (d *Dao) ForbidUser(c context.Context, mid uint64, exTime uint64) (err error) {
if _, err = d.db.Exec(c, _insertforbidUser, mid, exTime, model.ForbiddenStatus); err != nil {
log.Errorw(c, "event", "ForbidUser", "err", err)
return
}
return
}
//ReleaseUser ..
func (d *Dao) ReleaseUser(c context.Context, mid uint64) (err error) {
if _, err = d.db.Exec(c, _insertforbidUser, mid, 0, model.NormalStatus); err != nil {
log.Errorw(c, "event", "ReleaseUser", "err", err)
return
}
return
}

View File

@@ -0,0 +1,27 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/api"
"go-common/library/database/sql"
"go-common/library/log"
)
var (
locationQuery = "select `loc_id`, `pid`, `name` from `bbq_location` where `loc_id` = ?;"
)
// GetLocation return the location info
func (d *Dao) GetLocation(c context.Context, locId int32) (*api.LocationItem, error) {
row := d.db.QueryRow(c, locationQuery, locId)
var location api.LocationItem
err := row.Scan(&location.Id, &location.Pid, &location.Name)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("table", "bbq_location"), log.KV("loc_id", locId))
return nil, err
}
return &location, nil
}

View File

@@ -0,0 +1,337 @@
package dao
import (
"context"
xsql "database/sql"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/app/service/bbq/user/api"
"go-common/app/service/bbq/user/internal/model"
acc "go-common/app/service/main/account/api"
"go-common/library/database/sql"
"go-common/library/time"
"reflect"
"github.com/bouk/monkey"
)
// MockUserBase .
func (d *Dao) MockUserBase(res map[int64]*api.UserBase, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UserBase", func(_ *Dao, _ context.Context, _ []int64) (map[int64]*api.UserBase, error) {
return res, err
})
}
// MockPing .
func (d *Dao) MockPing(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "Ping", func(_ *Dao, _ context.Context) error {
return err
})
}
// MockBeginTran .
func (d *Dao) MockBeginTran(p1 *xsql.Tx, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "BeginTran", func(_ *Dao, _ context.Context) (*xsql.Tx, error) {
return p1, err
})
}
// MockCreateNotice .
func (d *Dao) MockCreateNotice(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "CreateNotice", func(_ *Dao, _ context.Context, _ *notice.NoticeBase) error {
return err
})
}
// MockFilter .
func (d *Dao) MockFilter(level int32, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "Filter", func(_ *Dao, _ context.Context, _ string, _ string) (int32, error) {
return level, err
})
}
// MockForbidUser .
func (d *Dao) MockForbidUser(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "ForbidUser", func(_ *Dao, _ context.Context, _ uint64, _ uint64) error {
return err
})
}
// MockReleaseUser .
func (d *Dao) MockReleaseUser(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "ReleaseUser", func(_ *Dao, _ context.Context, _ uint64) error {
return err
})
}
// MockGetLocation .
func (d *Dao) MockGetLocation(p1 *api.LocationItem, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "GetLocation", func(_ *Dao, _ context.Context, _ int32) (*api.LocationItem, error) {
return p1, err
})
}
// MockGetUserBProfile .
func (d *Dao) MockGetUserBProfile(res *acc.ProfileReply, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "GetUserBProfile", func(_ *Dao, _ context.Context, _ *api.PhoneCheckReq) (*acc.ProfileReply, error) {
return res, err
})
}
// MockRawUserBase .
func (d *Dao) MockRawUserBase(res map[int64]*api.UserBase, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "RawUserBase", func(_ *Dao, _ context.Context, _ []int64) (map[int64]*api.UserBase, error) {
return res, err
})
}
// MockCacheUserBase .
func (d *Dao) MockCacheUserBase(res map[int64]*api.UserBase, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "CacheUserBase", func(_ *Dao, _ context.Context, _ []int64) (map[int64]*api.UserBase, error) {
return res, err
})
}
// MockAddCacheUserBase .
func (d *Dao) MockAddCacheUserBase(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "AddCacheUserBase", func(_ *Dao, _ context.Context, _ map[int64]*api.UserBase) error {
return err
})
}
// MockUpdateUserField .
func (d *Dao) MockUpdateUserField(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpdateUserField", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ string, _ interface{}) (int64, error) {
return num, err
})
}
// MockAddUserBase .
func (d *Dao) MockAddUserBase(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "AddUserBase", func(_ *Dao, _ context.Context, _ *api.UserBase) (int64, error) {
return num, err
})
}
// MockUpdateUserBaseUname .
func (d *Dao) MockUpdateUserBaseUname(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpdateUserBaseUname", func(_ *Dao, _ context.Context, _ int64, _ string) (int64, error) {
return num, err
})
}
// MockUpdateUserBase .
func (d *Dao) MockUpdateUserBase(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpdateUserBase", func(_ *Dao, _ context.Context, _ int64, _ *api.UserBase) (int64, error) {
return num, err
})
}
// MockCheckUname .
func (d *Dao) MockCheckUname(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "CheckUname", func(_ *Dao, _ context.Context, _ int64, _ string) error {
return err
})
}
// MockTxAddUserBlack .
func (d *Dao) MockTxAddUserBlack(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxAddUserBlack", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockTxCancelUserBlack .
func (d *Dao) MockTxCancelUserBlack(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxCancelUserBlack", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockFetchBlackList .
func (d *Dao) MockFetchBlackList(upMid []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "FetchBlackList", func(_ *Dao, _ context.Context, _ int64) ([]int64, error) {
return upMid, err
})
}
// MockFetchPartBlackList .
func (d *Dao) MockFetchPartBlackList(MID2IDMap map[int64]time.Time, blackMIDs []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "FetchPartBlackList", func(_ *Dao, _ context.Context, _ int64, _ model.CursorValue, _ int) (map[int64]time.Time, []int64, error) {
return MID2IDMap, blackMIDs, err
})
}
// MockIsBlack .
func (d *Dao) MockIsBlack(MIDMap map[int64]bool) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "IsBlack", func(_ *Dao, _ context.Context, _ int64, _ []int64) map[int64]bool {
return MIDMap
})
}
// MockRawUserCard .
func (d *Dao) MockRawUserCard(userCard *model.UserCard, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "RawUserCard", func(_ *Dao, _ context.Context, _ int64) (*model.UserCard, error) {
return userCard, err
})
}
// MockRawUserCards .
func (d *Dao) MockRawUserCards(userCards map[int64]*model.UserCard, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "RawUserCards", func(_ *Dao, _ context.Context, _ []int64) (map[int64]*model.UserCard, error) {
return userCards, err
})
}
// MockRawUserAccCards .
func (d *Dao) MockRawUserAccCards(res *acc.CardsReply, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "RawUserAccCards", func(_ *Dao, _ context.Context, _ []int64) (*acc.CardsReply, error) {
return res, err
})
}
// MockTxAddUserFan .
func (d *Dao) MockTxAddUserFan(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxAddUserFan", func(_ *Dao, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockTxCancelUserFan .
func (d *Dao) MockTxCancelUserFan(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxCancelUserFan", func(_ *Dao, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockIsFan .
func (d *Dao) MockIsFan(MIDMap map[int64]bool) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "IsFan", func(_ *Dao, _ context.Context, _ int64, _ []int64) map[int64]bool {
return MIDMap
})
}
// MockFetchPartFanList .
func (d *Dao) MockFetchPartFanList(MID2IDMap map[int64]time.Time, followedMIDs []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "FetchPartFanList", func(_ *Dao, _ context.Context, _ int64, _ model.CursorValue, _ int) (map[int64]time.Time, []int64, error) {
return MID2IDMap, followedMIDs, err
})
}
// MockTxAddUserFollow .
func (d *Dao) MockTxAddUserFollow(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxAddUserFollow", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockTxCancelUserFollow .
func (d *Dao) MockTxCancelUserFollow(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxCancelUserFollow", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockFetchFollowList .
func (d *Dao) MockFetchFollowList(upMid []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "FetchFollowList", func(_ *Dao, _ context.Context, _ int64) ([]int64, error) {
return upMid, err
})
}
// MockFetchPartFollowList .
func (d *Dao) MockFetchPartFollowList(MID2IDMap map[int64]time.Time, followedMIDs []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "FetchPartFollowList", func(_ *Dao, _ context.Context, _ int64, _ model.CursorValue, _ int) (map[int64]time.Time, []int64, error) {
return MID2IDMap, followedMIDs, err
})
}
// MockIsFollow .
func (d *Dao) MockIsFollow(MIDMap map[int64]bool) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "IsFollow", func(_ *Dao, _ context.Context, _ int64, _ []int64) map[int64]bool {
return MIDMap
})
}
// MockTxAddUserLike .
func (d *Dao) MockTxAddUserLike(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxAddUserLike", func(_ *Dao, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockTxCancelUserLike .
func (d *Dao) MockTxCancelUserLike(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxCancelUserLike", func(_ *Dao, _ *sql.Tx, _ int64, _ int64) (int64, error) {
return num, err
})
}
// MockCheckUserLike .
func (d *Dao) MockCheckUserLike(res []int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "CheckUserLike", func(_ *Dao, _ context.Context, _ int64, _ []int64) ([]int64, error) {
return res, err
})
}
// MockGetUserLikeList .
func (d *Dao) MockGetUserLikeList(likeSvs []*api.LikeSv, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "GetUserLikeList", func(_ *Dao, _ context.Context, _ int64, _ bool, _ model.CursorValue, _ int) ([]*api.LikeSv, error) {
return likeSvs, err
})
}
// MockRawBatchUserStatistics .
func (d *Dao) MockRawBatchUserStatistics(res map[int64]*api.UserStat, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "RawBatchUserStatistics", func(_ *Dao, _ context.Context, _ []int64) (map[int64]*api.UserStat, error) {
return res, err
})
}
// MockTxIncrUserStatisticsFollow .
func (d *Dao) MockTxIncrUserStatisticsFollow(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxIncrUserStatisticsFollow", func(_ *Dao, _ *sql.Tx, _ int64) (int64, error) {
return num, err
})
}
// MockTxIncrUserStatisticsFan .
func (d *Dao) MockTxIncrUserStatisticsFan(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxIncrUserStatisticsFan", func(_ *Dao, _ *sql.Tx, _ int64) (int64, error) {
return num, err
})
}
// MockTxDecrUserStatisticsFollow .
func (d *Dao) MockTxDecrUserStatisticsFollow(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxDecrUserStatisticsFollow", func(_ *Dao, _ *sql.Tx, _ int64) (int64, error) {
return num, err
})
}
// MockTxDecrUserStatisticsFan .
func (d *Dao) MockTxDecrUserStatisticsFan(num int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxDecrUserStatisticsFan", func(_ *Dao, _ *sql.Tx, _ int64) (int64, error) {
return num, err
})
}
// MockTxIncrUserStatisticsField .
func (d *Dao) MockTxIncrUserStatisticsField(rowsAffected int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxIncrUserStatisticsField", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ string) (int64, error) {
return rowsAffected, err
})
}
// MockTxDescUserStatisticsField .
func (d *Dao) MockTxDescUserStatisticsField(rowsAffected int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "TxDescUserStatisticsField", func(_ *Dao, _ context.Context, _ *sql.Tx, _ int64, _ string) (int64, error) {
return rowsAffected, err
})
}
// MockUpdateUserVideoView .
func (d *Dao) MockUpdateUserVideoView(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpdateUserVideoView", func(_ *Dao, _ context.Context, _ int64, _ int64) error {
return err
})
}

View File

@@ -0,0 +1,123 @@
package dao
import (
"context"
"fmt"
"go-common/app/service/bbq/user/api"
"go-common/app/service/bbq/user/internal/model"
accountv1 "go-common/app/service/main/account/api"
"go-common/library/log"
"go-common/library/time"
"go-common/library/xstr"
)
const (
_userLikeNum = 128
_userFollowNum = 128
_userFanNum = 128
)
//calcTableID .
func (d *Dao) calcTableID(num, mid int64) string {
id := mid % 100
return fmt.Sprintf("%02d", id)
}
func (d *Dao) getTableIndex(mid int64) int64 {
return mid % 100
}
//userLikeSQL .
func (d *Dao) userLikeSQL(mid int64, sql string) string {
tableName := "user_like_" + d.calcTableID(_userLikeNum, mid)
return fmt.Sprintf(sql, tableName)
}
//userFollowSQL .
func (d *Dao) userFollowSQL(mid int64, sql string) string {
tableName := "user_follow_" + d.calcTableID(_userFollowNum, mid)
return fmt.Sprintf(sql, tableName)
}
//userFanSQL .
func (d *Dao) userFanSQL(mid int64, sql string) string {
tableName := "user_fan_" + d.calcTableID(_userFanNum, mid)
return fmt.Sprintf(sql, tableName)
}
// isMidIn 获取mid的关注up主
// 如果key在map中那么value值肯定为1
func (d *Dao) isMidIn(c context.Context, mid int64, candidateMIDs []int64, sql string) (MIDMap map[int64]bool) {
if len(candidateMIDs) == 0 {
return
}
MIDMap = make(map[int64]bool)
tableName := d.getTableIndex(mid)
midstr := xstr.JoinInts(candidateMIDs)
querySQL := fmt.Sprintf(sql, tableName, midstr)
log.V(1).Infov(c, log.KV("event", "fetch_list"), log.KV("sql", querySQL))
rows, err := d.db.Query(c, querySQL, mid)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("sql", querySQL))
return
}
defer rows.Close()
for rows.Next() {
var followedMid int64
if err = rows.Scan(&followedMid); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("sql", querySQL))
continue
}
MIDMap[followedMid] = true
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("sql", querySQL), log.KV("mid", mid),
log.KV("req_size", len(candidateMIDs)), log.KV("rsp_size", len(MIDMap)))
return MIDMap
}
// fetchPartRelationUserList 获取相关的用户列表可以是关注列表也可以是粉丝列表根据sql区别
func (d *Dao) fetchPartRelationUserList(c context.Context, mid int64, cursor model.CursorValue, sql string) (
MID2IDMap map[int64]time.Time, relationMIDs []int64, err error) {
MID2IDMap = make(map[int64]time.Time)
querySQL := fmt.Sprintf(sql, d.getTableIndex(mid), model.UserListLen)
log.V(1).Infov(c, log.KV("event", "fetch_follow_list"), log.KV("sql", querySQL))
rows, err := d.db.Query(c, querySQL, mid, cursor.CursorTime)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("sql", querySQL))
return
}
defer rows.Close()
conflict := bool(true)
for rows.Next() {
var relationMID int64
var mtime time.Time
if err = rows.Scan(&relationMID, &mtime); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("sql", querySQL))
return
}
// 为了解决同一个mtime的冲突问题
if mtime == cursor.CursorTime && conflict {
if relationMID == cursor.CursorID {
conflict = false
}
continue
}
relationMIDs = append(relationMIDs, relationMID)
MID2IDMap[relationMID] = mtime
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("sql", querySQL),
log.KV("mid", mid), log.KV("relation_num", len(relationMIDs)))
return
}
//GetUserBProfile 获取用户全量b站信息
func (d *Dao) GetUserBProfile(c context.Context, in *api.PhoneCheckReq) (res *accountv1.ProfileReply, err error) {
req := &accountv1.MidReq{
Mid: in.Mid,
RealIp: "",
}
res, err = d.accountClient.Profile3(c, req)
return
}

View File

@@ -0,0 +1,281 @@
package dao
import (
"context"
xsql "database/sql"
"encoding/json"
"fmt"
"go-common/app/service/bbq/user/api"
"go-common/app/service/bbq/user/internal/conf"
"go-common/app/service/bbq/user/internal/model"
"go-common/library/cache/redis"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"strconv"
"strings"
)
const (
_incrUserBase = "insert into user_base (`mid`,`uname`,`face`,`birthday`,`exp`,`level`,`user_type`,`complete_degree`,`sex`) values(?,?,?,?,?,?,?,?,?)"
//_incrUserUname = "insert into user_base (`mid`,`uname`) values(?,?)"
// TODO: 用户签名数据暂时隐藏
// _userBase = "select `mid`,`uname`,`face`,`birthday`,`exp`,`level`,`ctime`,`mtime`,`signature`,`region`,`sex`, `user_type`,`complete_degree`from user_base where `mid` in (%s)"
_userBase = "select `mid`,`uname`,`face`,`birthday`,`exp`,`level`,`ctime`,`mtime`,'',`region`,`sex`, `user_type`,`complete_degree`from user_base where `mid` in (%s)"
_updateUserUname = "update user_base set `uname` = ? where `mid` = ?"
_selectUname = "select `mid` from user_base where `uname` = ? and `mid` != ?"
_selectBZhanUpUname = "select `mid` from user_statistics_hive where `uname` = ? and `mid` != ? and `fan_total` > 10000"
_updateUser = "update user_base set uname=?, face=?, birthday=?, sex=?, region=?, signature=?, complete_degree=? where mid=?"
_updateUserField = "update user_base set `%s` = ? where mid = ?"
)
// keyUserBase 用户基础信息缓存key
func keyUserBase(mid int64) string {
return fmt.Sprintf(model.CacheKeyUserBase, mid)
}
// RawUserBase 从数据库获取用户基础信息
func (d *Dao) RawUserBase(c context.Context, mids []int64) (res map[int64]*api.UserBase, err error) {
if len(mids) == 0 {
return
}
var midStr string
for _, mid := range mids {
if len(midStr) != 0 {
midStr += ","
}
midStr += strconv.FormatInt(mid, 10)
}
querySQL := fmt.Sprintf(_userBase, midStr)
rows, err := d.db.Query(c, querySQL)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_query"), log.KV("error", err), log.KV("sql", querySQL))
return
}
defer rows.Close()
for rows.Next() {
userBase := new(api.UserBase)
if err = rows.Scan(&userBase.Mid,
&userBase.Uname,
&userBase.Face,
&userBase.Birthday,
&userBase.Exp,
&userBase.Level,
&userBase.Ctime,
&userBase.Mtime,
&userBase.Signature,
&userBase.Region,
&userBase.Sex,
&userBase.UserType,
&userBase.CompleteDegree); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("error", err), log.KV("sql", querySQL))
return
}
if res == nil {
res = make(map[int64]*api.UserBase)
}
res[userBase.Mid] = userBase
}
log.Infov(c, log.KV("event", "mysql_query"), log.KV("row_num", len(res)), log.KV("sql", querySQL))
return
}
// CacheUserBase multi get user base from cache.
func (d *Dao) CacheUserBase(c context.Context, mids []int64) (res map[int64]*api.UserBase, err error) {
if res == nil {
res = make(map[int64]*api.UserBase)
}
keys := make([]string, 0, len(mids))
keyMidMap := make(map[int64]bool, len(mids))
for _, mid := range mids {
key := keyUserBase(mid)
if _, exist := keyMidMap[mid]; !exist {
// duplicate mid
keyMidMap[mid] = true
keys = append(keys, key)
}
}
conn := d.redis.Get(c)
defer conn.Close()
for _, key := range keys {
conn.Send("GET", key)
}
conn.Flush()
var data []byte
for i := 0; i < len(keys); i++ {
if data, err = redis.Bytes(conn.Receive()); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Errorv(c, log.KV("event", "redis_get"), log.KV("key", keys[i]))
}
continue
}
baseItem := new(api.UserBase)
json.Unmarshal(data, baseItem)
res[baseItem.Mid] = baseItem
}
log.Infov(c, log.KV("event", "redis_get"), log.KV("row_num", len(res)))
return
}
// AddCacheUserBase 添加用户缓存
func (d *Dao) AddCacheUserBase(c context.Context, userBases map[int64]*api.UserBase) (err error) {
keyValueMap := make(map[string][]byte, len(userBases))
for mid, userBase := range userBases {
key := keyUserBase(mid)
if _, exist := keyValueMap[key]; !exist {
data, _ := json.Marshal(userBase)
keyValueMap[key] = data
}
}
conn := d.redis.Get(c)
defer conn.Close()
for key, value := range keyValueMap {
conn.Send("SET", key, value, "EX", model.CacheExpireUserBase)
}
conn.Flush()
for i := 0; i < len(keyValueMap); i++ {
conn.Receive()
}
log.Infov(c, log.KV("event", "redis_set"), log.KV("row_num", len(userBases)))
return
}
//DelCacheUserBase 删除用户缓存
func (d *Dao) DelCacheUserBase(c context.Context, mid int64) {
var key = keyUserBase(mid)
conn := d.redis.Get(c)
defer conn.Close()
conn.Do("DEL", key)
}
////TxAddUserBase .
//func (d *Dao) TxAddUserBase(c context.Context, tx *sql.Tx, userBase *api.UserBase) (num int64, err error) {
// var res xsql.Result
// if res, err = tx.Exec(_incrUserBase, userBase.Mid, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Exp, userBase.Level, userBase.UserType, userBase.CompleteDegree); err != nil {
// log.Error("incr user base err(%v)", err)
// return
// }
// d.DelCacheUserBase(c, userBase.Mid)
// return res.LastInsertId()
//}
//
// UpdateUserField .
func (d *Dao) UpdateUserField(c context.Context, tx *sql.Tx, mid int64, field string, f interface{}) (num int64, err error) {
var res xsql.Result
querySQL := fmt.Sprintf(_updateUserField, field)
if res, err = tx.Exec(querySQL, f, mid); err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("update user mid(%d) field(%s) value(%v) err(%v)", mid, field, f, err)))
return
}
log.V(1).Infow(c, "log", "update user field", "mid", mid, "field", field, "value", f)
d.DelCacheUserBase(c, mid)
return res.RowsAffected()
}
//AddUserBase .
func (d *Dao) AddUserBase(c context.Context, userBase *api.UserBase) (num int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _incrUserBase, userBase.Mid, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Exp, userBase.Level, userBase.UserType, userBase.CompleteDegree, userBase.Sex); err != nil {
log.Error("incr user base err(%v)", err)
return
}
d.DelCacheUserBase(c, userBase.Mid)
return res.LastInsertId()
}
//UpdateUserBaseUname .
func (d *Dao) UpdateUserBaseUname(c context.Context, mid int64, uname string) (num int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _updateUserUname, uname, mid); err != nil {
log.Error("update user base uname err(%v)", err)
return
}
d.DelCacheUserBase(c, mid)
return res.RowsAffected()
}
// UpdateUserBase 更新用户基础信息
func (d *Dao) UpdateUserBase(c context.Context, mid int64, userBase *api.UserBase) (num int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _updateUser, userBase.Uname, userBase.Face, userBase.Birthday, userBase.Sex,
userBase.Region, userBase.Signature, userBase.CompleteDegree, mid); err != nil {
log.Errorv(c, log.KV("event", "mysql_update"), log.KV("mid", mid), log.KV("error", err))
return
}
d.DelCacheUserBase(c, mid)
return res.RowsAffected()
}
// CheckUname 检测昵称
func (d *Dao) CheckUname(c context.Context, mid int64, uname string) (err error) {
// 前缀不能为Qing_
if strings.HasPrefix(uname, "Qing_") {
err = ecode.UserUnamePrefixErr
return
}
//特殊字符
if !model.CheckUnameSpecial(uname) {
err = ecode.UserUnameSpecial
return
}
//字符长度
if !model.CheckUnameLength(uname) {
err = ecode.UserUnameLength
return
}
//bbq是否存在
tmp := int64(0)
row := d.db.QueryRow(c, _selectUname, uname, mid)
if err = row.Scan(&tmp); err != nil && err != sql.ErrNoRows {
err = ecode.EditUserBaseErr
return
}
if tmp != 0 {
err = ecode.UserUnameExisted
log.Infow(c, "log", "uname已存在", "uname", uname, "mid", mid)
return
}
//自己b站的昵称
var userCard *model.UserCard
if userCard, err = d.RawUserCard(c, mid); err != nil {
err = ecode.EditUserBaseErr
return
}
if userCard.Name == uname {
return nil
}
//是否是万粉的昵称
row2 := d.db.QueryRow(c, _selectBZhanUpUname, uname, mid)
if tmpErr := row2.Scan(&tmp); tmpErr != nil && tmpErr != sql.ErrNoRows {
err = ecode.EditUserBaseErr
log.V(1).Infow(c, "log", "获取B站万粉资料为空", "uname", uname)
return
}
if tmp != 0 {
err = ecode.UserUnameExisted
log.Infow(c, "log", "uname命中B站万粉up主", "uname", uname, "mid", mid)
return
}
// 昵称是否包含敏感词
level, filterErr := d.Filter(c, uname, "BBQ_account")
if filterErr != nil {
log.Errorv(c, log.KV("log", "filter fail"))
} else if level >= 20 {
err = ecode.UserUnameFilterErr
log.Warnv(c, log.KV("log", fmt.Sprintf("uname filter fail: uname=%s, level=%d", uname, level)))
return
}
// 运营不允许使用的uname列表中
if conf.UnameConf.UnameForbidden(uname) {
log.Infow(c, "log", "hit fobidden uname", "uname", uname)
err = ecode.UserUnameExisted
return
}
return nil
}

View File

@@ -0,0 +1,276 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/api"
"go-common/library/log"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyUserBase(t *testing.T) {
convey.Convey("keyUserBase", t, func(ctx convey.C) {
var (
mid = int64(88895104)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyUserBase(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTmpUserBase(t *testing.T) {
convey.Convey("JustGetUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{88895104}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.UserBase(c, mids)
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 TestDaoRawUserBase(t *testing.T) {
convey.Convey("RawUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{88895104}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.RawUserBase(c, mids)
log.Infow(c, "log", "xxxxxxxxxxx", "res", res)
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 TestDaoCacheUserBase(t *testing.T) {
convey.Convey("CacheUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.CacheUserBase(c, mids)
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 TestDaoAddCacheUserBase(t *testing.T) {
convey.Convey("AddCacheUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
userBases map[int64]*api.UserBase
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCacheUserBase(c, userBases)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelCacheUserBase(t *testing.T) {
convey.Convey("DelCacheUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.DelCacheUserBase(c, mid)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
//
//func TestDaoTxAddUserBase(t *testing.T) {
// convey.Convey("TxAddUserBase", t, func(ctx convey.C) {
// var (
// c = context.Background()
// tx = &sql.Tx{}
// userBase = &api.UserBase{}
// )
// ctx.Convey("When everything goes positive", func(ctx convey.C) {
// num, err := d.TxAddUserBase(c, tx, userBase)
// ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
// ctx.So(err, convey.ShouldBeNil)
// ctx.So(num, convey.ShouldNotBeNil)
// })
// })
// })
//}
//
//func TestDaoAddUserBaseUname(t *testing.T) {
// convey.Convey("AddUserBaseUname", t, func(ctx convey.C) {
// var (
// c = context.Background()
// mid = int64(88895104)
// uname = "dfsfwe"
// )
// ctx.Convey("When everything goes positive", func(ctx convey.C) {
// num, err := d.AddUserBaseUname(c, mid, uname)
// ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
// ctx.So(err, convey.ShouldBeNil)
// ctx.So(num, convey.ShouldNotBeNil)
// })
// })
// })
//}
//
//func TestDaoAddUserBase(t *testing.T) {
// convey.Convey("AddUserBase", t, func(ctx convey.C) {
// var (
// c = context.Background()
// userBase = &api.UserBase{}
// )
// ctx.Convey("When everything goes positive", func(ctx convey.C) {
// num, err := d.AddUserBase(c, userBase)
// ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
// ctx.So(err, convey.ShouldBeNil)
// ctx.So(num, convey.ShouldNotBeNil)
// })
// })
// })
//}
func TestDaoUpdateUserBaseUname(t *testing.T) {
convey.Convey("UpdateUserBaseUname", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
uname = "sdfewxc"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserBaseUname(c, mid, uname)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateUserBase(t *testing.T) {
convey.Convey("UpdateUserBase", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
userBase = &api.UserBase{Uname: "sdfweo", Sex: 1, Face: "htttttt"}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserBase(c, mid, userBase)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateUserField(t *testing.T) {
convey.Convey("UpdateUserField", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserField(c, tx, mid, "uname", "unnnnnn")
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
convey.Convey("UpdateUserField", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserField(c, tx, mid, "face", "faaaaaa")
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
convey.Convey("UpdateUserField", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserField(c, tx, mid, "region", 11111)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
convey.Convey("update cms_tag", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
//tx.Rollback()
tx.Commit()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.UpdateUserField(c, tx, mid, "cms_tag", 1)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCheckUname(t *testing.T) {
convey.Convey("CheckUname", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
uname = "lkwejroiw"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.CheckUname(c, mid, uname)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,80 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"go-common/app/service/bbq/user/internal/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/time"
)
const (
_addUserBlack = "insert into user_black_%02d (`mid`,`black_mid`) values (?,?) on duplicate key update state=0"
_cancelUserBlack = "update user_black_%02d set state=1 where mid=? and black_mid=?"
_getUserPartBlacks = "select black_mid, mtime from user_black_%02d where mid=? and state=0 and mtime<=? order by mtime desc, black_mid desc limit %d"
_getUserBlacks = "select black_mid from user_black_%02d where mid=? and state=0"
_isBlack = "select black_mid from user_black_%02d where mid=? and state=0 and black_mid in (%s)"
)
// TxAddUserBlack .
func (d *Dao) TxAddUserBlack(c context.Context, tx *sql.Tx, mid, upMid int64) (num int64, err error) {
var res xsql.Result
// sql中含有ignore该情况为了简化是否已存在的判断
querySQL := fmt.Sprintf(_addUserBlack, d.getTableIndex(mid))
if res, err = tx.Exec(querySQL, mid, upMid); err != nil {
log.Errorv(c, log.KV("event", "add_user_black"), log.KV("err", err), log.KV("mid", mid), log.KV("up_mid", upMid), log.KV("sql", querySQL))
return
}
return res.RowsAffected()
}
// TxCancelUserBlack .
func (d *Dao) TxCancelUserBlack(c context.Context, tx *sql.Tx, mid, upMid int64) (num int64, err error) {
var res xsql.Result
querySQL := fmt.Sprintf(_cancelUserBlack, d.getTableIndex(mid))
if res, err = tx.Exec(querySQL, mid, upMid); err != nil {
log.Errorv(c, log.KV("event", "cancel_user_black"), log.KV("err", err), log.KV("mid", mid), log.KV("up_mid", upMid), log.KV("sql", querySQL))
return
}
return res.RowsAffected()
}
// FetchBlackList 获取mid的所有拉黑up主
func (d *Dao) FetchBlackList(c context.Context, mid int64) (upMid []int64, err error) {
querySQL := fmt.Sprintf(_getUserBlacks, d.getTableIndex(mid))
rows, err := d.db.Query(c, querySQL, mid)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("table", "user_black"))
return
}
defer rows.Close()
for rows.Next() {
var m int64
if err = rows.Scan(&m); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("table", "user_black"))
return
}
upMid = append(upMid, m)
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("sql", querySQL),
log.KV("mid", mid), log.KV("black_num", len(upMid)))
return
}
// FetchPartBlackList 获取mid的拉黑up主
func (d *Dao) FetchPartBlackList(c context.Context, mid int64, cursor model.CursorValue, size int) (
MID2IDMap map[int64]time.Time, blackMIDs []int64, err error) {
MID2IDMap, blackMIDs, err = d.fetchPartRelationUserList(c, mid, cursor, _getUserPartBlacks)
return
}
// IsBlack 获取mid的拉黑用户
func (d *Dao) IsBlack(c context.Context, mid int64, candidateMIDs []int64) (MIDMap map[int64]bool) {
if len(candidateMIDs) == 0 {
return
}
MIDMap = d.isMidIn(c, mid, candidateMIDs, _isBlack)
return
}

View File

@@ -0,0 +1,105 @@
package dao
import (
"context"
"github.com/smartystreets/goconvey/convey"
"go-common/app/service/bbq/user/internal/model"
xtime "go-common/library/time"
"testing"
"time"
)
func TestDaoTxAddUserBlack(t *testing.T) {
convey.Convey("TxAddUserBlack", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
upMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxAddUserBlack(c, tx, mid, upMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxCancelUserBlack(t *testing.T) {
convey.Convey("TxCancelUserBlack", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
upMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxCancelUserBlack(c, tx, mid, upMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoFetchBlackList(t *testing.T) {
convey.Convey("FetchBlackList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
upMid, err := d.FetchBlackList(c, mid)
ctx.Convey("Then err should be nil.upMid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(upMid, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoFetchPartBlackList(t *testing.T) {
convey.Convey("FetchPartBlackList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
cursor model.CursorValue
size = int(10)
)
cursor.CursorID = model.MaxInt64
cursor.CursorTime = xtime.Time(time.Now().Unix())
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MID2IDMap, blackMIDs, err := d.FetchPartBlackList(c, mid, cursor, size)
ctx.Convey("Then err should be nil.MID2IDMap,blackMIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(blackMIDs, convey.ShouldNotBeNil)
ctx.So(MID2IDMap, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoIsBlack(t *testing.T) {
convey.Convey("IsBlack", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
candidateMIDs = []int64{88895105}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MIDMap := d.IsBlack(c, mid, candidateMIDs)
ctx.Convey("Then MIDMap should not be nil.", func(ctx convey.C) {
ctx.So(MIDMap, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,86 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/internal/model"
acc "go-common/app/service/main/account/api"
"go-common/library/log"
"go-common/library/net/metadata"
)
// RawUserCard 从主站获取用户基础信息
func (d *Dao) RawUserCard(c context.Context, mid int64) (userCard *model.UserCard, err error) {
req := &acc.MidReq{
Mid: mid,
RealIp: metadata.String(c, metadata.RemoteIP),
}
res, err := d.accountClient.Card3(c, req)
if err != nil {
log.Error("user card rpc error(%v)", err)
return
}
vipInfo := model.VIPInfo{
Type: res.Card.Vip.Type,
Status: res.Card.Vip.Status,
DueDate: res.Card.Vip.DueDate,
}
userCard = &model.UserCard{
MID: res.Card.Mid,
Name: res.Card.Name,
Sex: res.Card.Sex,
Rank: res.Card.Rank,
Face: res.Card.Face,
Sign: res.Card.Sign,
Level: res.Card.Level,
VIPInfo: vipInfo,
}
return
}
// RawUserCards 从主站获取用户基础信息
func (d *Dao) RawUserCards(c context.Context, mids []int64) (userCards map[int64]*model.UserCard, err error) {
req := &acc.MidsReq{
Mids: mids,
RealIp: metadata.String(c, metadata.RemoteIP),
}
res, err := d.accountClient.Cards3(c, req)
if err != nil {
log.Error("user card rpc error(%v)", err)
return
}
userCards = make(map[int64]*model.UserCard, len(mids))
for _, card := range res.Cards {
vipInfo := model.VIPInfo{
Type: card.Vip.Type,
Status: card.Vip.Status,
DueDate: card.Vip.DueDate,
}
userCard := &model.UserCard{
MID: card.Mid,
Name: card.Name,
Sex: card.Sex,
Rank: card.Rank,
Face: card.Face,
Sign: card.Sign,
Level: card.Level,
VIPInfo: vipInfo,
}
userCards[card.Mid] = userCard
}
return
}
// RawUserAccCards 批量获取账号信息
func (d *Dao) RawUserAccCards(c context.Context, mids []int64) (res *acc.CardsReply, err error) {
req := &acc.MidsReq{
Mids: mids,
RealIp: metadata.String(c, metadata.RemoteIP),
}
res, err = d.accountClient.Cards3(c, req)
if err != nil {
log.Error("d.accountClient.Cards3 err [%v]", err)
}
return
}

View File

@@ -0,0 +1,56 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRawUserCard(t *testing.T) {
convey.Convey("RawUserCard", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
userCard, err := d.RawUserCard(c, mid)
ctx.Convey("Then err should be nil.userCard should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(userCard, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoRawUserCards(t *testing.T) {
convey.Convey("RawUserCards", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{88895104}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
userCards, err := d.RawUserCards(c, mids)
ctx.Convey("Then err should be nil.userCards should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(userCards, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoRawUserAccCards(t *testing.T) {
convey.Convey("RawUserAccCards", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{88895104}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.RawUserAccCards(c, mids)
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,50 @@
package dao
import (
"context"
xsql "database/sql"
"go-common/app/service/bbq/user/internal/model"
"go-common/library/database/sql"
"go-common/library/time"
)
const (
_addUserFan = "insert into %s (`mid`,`fan_mid`) values (?,?) on duplicate key update state=0"
_cancelUserFan = "update %s set state=1 where mid = ? and fan_mid = ?"
_getUserPartFans = "select fan_mid, mtime from user_fan_%02d where mid=? and state=0 and mtime<=? order by mtime desc, fan_mid desc limit %d"
_isFan = "select fan_mid from user_fan_%02d where mid = ? and state=0 and fan_mid in (%s)"
)
//TxAddUserFan .
func (d *Dao) TxAddUserFan(tx *sql.Tx, mid, fanMid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userFanSQL(mid, _addUserFan), mid, fanMid); err != nil {
return
}
return res.LastInsertId()
}
//TxCancelUserFan .
func (d *Dao) TxCancelUserFan(tx *sql.Tx, mid, fanMid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userFanSQL(mid, _cancelUserFan), mid, fanMid); err != nil {
return
}
return res.RowsAffected()
}
// IsFan 获取mid的粉丝
func (d *Dao) IsFan(c context.Context, mid int64, candidateMIDs []int64) (MIDMap map[int64]bool) {
if len(candidateMIDs) == 0 {
return
}
MIDMap = d.isMidIn(c, mid, candidateMIDs, _isFan)
return
}
// FetchPartFanList 获取mid的粉丝列表
func (d *Dao) FetchPartFanList(c context.Context, mid int64, cursor model.CursorValue, size int) (
MID2IDMap map[int64]time.Time, followedMIDs []int64, err error) {
MID2IDMap, followedMIDs, err = d.fetchPartRelationUserList(c, mid, cursor, _getUserPartFans)
return
}

View File

@@ -0,0 +1,89 @@
package dao
import (
"context"
"github.com/smartystreets/goconvey/convey"
"go-common/app/service/bbq/user/internal/model"
xtime "go-common/library/time"
"testing"
"time"
)
func TestDaoTxAddUserFan(t *testing.T) {
convey.Convey("TxAddUserFan", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
fanMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxAddUserFan(tx, mid, fanMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxCancelUserFan(t *testing.T) {
convey.Convey("TxCancelUserFan", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
fanMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxCancelUserFan(tx, mid, fanMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoIsFan(t *testing.T) {
convey.Convey("IsFan", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
candidateMIDs = []int64{88895105}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MIDMap := d.IsFan(c, mid, candidateMIDs)
ctx.Convey("Then MIDMap should not be nil.", func(ctx convey.C) {
ctx.So(MIDMap, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoFetchPartFanList(t *testing.T) {
convey.Convey("FetchPartFanList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
cursor model.CursorValue
size = int(10)
)
cursor.CursorID = model.MaxInt64
cursor.CursorTime = xtime.Time(time.Now().Unix())
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MID2IDMap, followedMIDs, err := d.FetchPartFanList(c, mid, cursor, size)
ctx.Convey("Then err should be nil.MID2IDMap,followedMIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(followedMIDs, convey.ShouldNotBeNil)
ctx.So(MID2IDMap, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,78 @@
package dao
import (
"context"
xsql "database/sql"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/time"
"go-common/app/service/bbq/user/internal/model"
)
const (
_addUserFollow = "insert into %s (`mid`,`followed_mid`)values(?,?) on duplicate key update state=0"
_cancelUserFollow = "update %s set state=1 where mid=? and followed_mid=?"
_getUserFollows = "select followed_mid from %s where mid = ? and state=0"
_getUserPartFollows = "select followed_mid, mtime from user_follow_%02d where mid=? and state=0 and mtime<=? order by mtime desc, followed_mid desc limit %d"
_isFollow = "select followed_mid from user_follow_%02d where mid=? and state=0 and followed_mid in (%s)"
)
//TxAddUserFollow .
func (d *Dao) TxAddUserFollow(c context.Context, tx *sql.Tx, mid, upMid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userFollowSQL(mid, _addUserFollow), mid, upMid); err != nil {
log.Errorv(c, log.KV("event", "user_follow"), log.KV("err", err), log.KV("mid", mid), log.KV("up_mid", upMid))
return
}
return res.RowsAffected()
}
//TxCancelUserFollow .
func (d *Dao) TxCancelUserFollow(c context.Context, tx *sql.Tx, mid, upMid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userFollowSQL(mid, _cancelUserFollow), mid, upMid); err != nil {
log.Errorv(c, log.KV("event", "cancel_user_follow"), log.KV("err", err), log.KV("mid", mid), log.KV("up_mid", upMid))
return
}
return res.RowsAffected()
}
// FetchFollowList 获取mid的所有关注up主
func (d *Dao) FetchFollowList(c context.Context, mid int64) (upMid []int64, err error) {
querySQL := d.userFollowSQL(mid, _getUserFollows)
rows, err := d.db.Query(c, querySQL, mid)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("table", "user_follow"))
return
}
defer rows.Close()
for rows.Next() {
var m int64
if err = rows.Scan(&m); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("table", "user_follow"))
return
}
upMid = append(upMid, m)
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("table", "user_follow"),
log.KV("mid", mid), log.KV("follow_num", len(upMid)))
return
}
// FetchPartFollowList 获取mid的关注up主
// cursor_id代表相应的up_midcursor_time表示mtime
func (d *Dao) FetchPartFollowList(c context.Context, mid int64, cursor model.CursorValue, size int) (
MID2IDMap map[int64]time.Time, followedMIDs []int64, err error) {
MID2IDMap, followedMIDs, err = d.fetchPartRelationUserList(c, mid, cursor, _getUserPartFollows)
return
}
// IsFollow 获取mid的关注up主
func (d *Dao) IsFollow(c context.Context, mid int64, candidateMIDs []int64) (MIDMap map[int64]bool) {
if len(candidateMIDs) == 0 {
return
}
MIDMap = d.isMidIn(c, mid, candidateMIDs, _isFollow)
return
}

View File

@@ -0,0 +1,105 @@
package dao
import (
"context"
"github.com/smartystreets/goconvey/convey"
"go-common/app/service/bbq/user/internal/model"
xtime "go-common/library/time"
"testing"
"time"
)
func TestDaoTxAddUserFollow(t *testing.T) {
convey.Convey("TxAddUserFollow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
upMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxAddUserFollow(c, tx, mid, upMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxCancelUserFollow(t *testing.T) {
convey.Convey("TxCancelUserFollow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
upMid = int64(88895105)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxCancelUserFollow(c, tx, mid, upMid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoFetchFollowList(t *testing.T) {
convey.Convey("FetchFollowList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
upMid, err := d.FetchFollowList(c, mid)
ctx.Convey("Then err should be nil.upMid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(upMid, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoFetchPartFollowList(t *testing.T) {
convey.Convey("FetchPartFollowList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
cursor model.CursorValue
size = int(10)
)
cursor.CursorID = model.MaxInt64
cursor.CursorTime = xtime.Time(time.Now().Unix())
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MID2IDMap, followedMIDs, err := d.FetchPartFollowList(c, mid, cursor, size)
ctx.Convey("Then err should be nil.MID2IDMap,followedMIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(followedMIDs, convey.ShouldNotBeNil)
ctx.So(MID2IDMap, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoIsFollow(t *testing.T) {
convey.Convey("IsFollow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
candidateMIDs = []int64{88895105}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
MIDMap := d.IsFollow(c, mid, candidateMIDs)
ctx.Convey("Then MIDMap should not be nil.", func(ctx convey.C) {
ctx.So(MIDMap, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,111 @@
package dao
import (
"context"
xsql "database/sql"
"encoding/json"
"fmt"
"go-common/app/service/bbq/user/api"
"go-common/app/service/bbq/user/internal/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/time"
"go-common/library/xstr"
)
//常量
const (
_addUserLike = "insert into %s (`mid`, `opid`) values (?,?) on duplicate key update state=0"
_cancelUserLike = "update %s set state = 1 where mid=? and opid=?"
_selectUserLike = "select opid from user_like_%02d where mid = ? and state = 0 and opid in (%s)"
_spaceUserLike = "select opid, mtime from user_like_%02d where mid=%d and state=0 and mtime %s ? order by mtime %s, opid %s limit %d"
)
//TxAddUserLike .
func (d *Dao) TxAddUserLike(tx *sql.Tx, mid, svid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userLikeSQL(mid, _addUserLike), mid, svid); err != nil {
return
}
return res.RowsAffected()
}
//TxCancelUserLike .
func (d *Dao) TxCancelUserLike(tx *sql.Tx, mid, svid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(d.userLikeSQL(mid, _cancelUserLike), mid, svid); err != nil {
return
}
return res.RowsAffected()
}
// CheckUserLike 检测用户是否点赞
func (d *Dao) CheckUserLike(c context.Context, mid int64, svids []int64) (res []int64, err error) {
log.V(1).Info("user like mid(%d) svids(%v)", mid, svids)
ls := len(svids)
if ls == 0 || mid == 0 {
return
}
idStr := xstr.JoinInts(svids)
querySQL := fmt.Sprintf(_selectUserLike, d.getTableIndex(mid), idStr)
rows, err := d.db.Query(c, querySQL, mid)
log.V(1).Infov(c, log.KV("log", fmt.Sprintf("user like rows(%v) err(%v)", rows, err)))
if err != nil {
if err == sql.ErrNoRows {
err = nil
}
return
}
for rows.Next() {
opid := int64(0)
rows.Scan(&opid)
res = append(res, opid)
}
log.V(1).Infov(c, log.KV("log", fmt.Sprintf("user like res(%v)", res)))
return
}
// GetUserLikeList 返回用户点赞列表
// 当前cursorID表示opid
func (d *Dao) GetUserLikeList(c context.Context, mid int64, cursorNext bool, cursor model.CursorValue, size int) (
likeSvs []*api.LikeSv, err error) {
compareSymbol := string(">=")
orderDirection := "asc"
if cursorNext {
compareSymbol = "<="
orderDirection = "desc"
}
querySQL := fmt.Sprintf(_spaceUserLike, d.getTableIndex(mid), mid, compareSymbol, orderDirection, orderDirection, size)
log.V(1).Infov(c, log.KV("like_list_sql", querySQL))
rows, err := d.db.Query(c, querySQL, cursor.CursorTime)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("table", "user_like"),
log.KV("mid", mid), log.KV("sql", querySQL))
return
}
defer rows.Close()
var svID int64
var curMtime time.Time
conflict := bool(true)
for rows.Next() {
if err = rows.Scan(&svID, &curMtime); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("table", "user_like"),
log.KV("sql", querySQL))
return
}
// 为了解决同一个mtime的冲突问题
if curMtime == cursor.CursorTime && conflict {
if svID == cursor.CursorID {
conflict = false
}
continue
}
cursorValue := model.CursorValue{CursorID: svID, CursorTime: curMtime}
jsonStr, _ := json.Marshal(cursorValue) // marshal的时候相信库函数不做err判断
likeSvs = append(likeSvs, &api.LikeSv{Svid: svID, CursorValue: string(jsonStr)})
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("table", "user_like"),
log.KV("mid", mid), log.KV("id", cursor.CursorID), log.KV("size", len(likeSvs)))
return
}

View File

@@ -0,0 +1,75 @@
package dao
import (
"context"
"go-common/app/service/bbq/user/internal/model"
xtime "go-common/library/time"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoTxAddUserLike(t *testing.T) {
convey.Convey("TxAddUserLike", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
svid = int64(133)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxAddUserLike(tx, mid, svid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxCancelUserLike(t *testing.T) {
convey.Convey("TxCancelUserLike", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
svid = int64(133)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxCancelUserLike(tx, mid, svid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoGetUserLikeList(t *testing.T) {
convey.Convey("GetUserLikeList", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
cursorNext bool
cursor model.CursorValue
size = int(10)
)
cursorNext = true
cursor.CursorID = model.MaxInt64
cursor.CursorTime = xtime.Time(time.Now().Unix())
ctx.Convey("When everything goes positive", func(ctx convey.C) {
likeSvs, err := d.GetUserLikeList(c, mid, cursorNext, cursor, size)
ctx.Convey("Then err should be nil.likeSvs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(likeSvs, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,121 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"go-common/app/service/bbq/user/api"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_batchUserStatistics = "select `mid`,`av_total` - `unshelf_av_total`,`follow_total`,`fan_total`,`like_total`,`rev_like_total`,`black_total`,`play_total` from user_statistics where mid in (%s)"
_incrUserStatisticsFollow = "update user_statistics set `follow_total` = `follow_total` + 1 where `mid` = ?"
_incrUserStatisticsFan = "update user_statistics set `fan_total` = `fan_total` + 1 where `mid` = ?"
_decrUserStatisticsFollow = "update user_statistics set `follow_total` = `follow_total` - 1 where `mid` = ? and `follow_total` > 0"
_decrUserStatisticsFan = "update user_statistics set `fan_total` = `fan_total` - 1 where `mid` = ? and `fan_total` > 0"
_incrUserStatisticField = "insert into user_statistics (`mid`, `%s`) values (?, 1) on duplicate key update `%s` = `%s` + 1"
_decrUserStatisticsField = "update user_statistics set `%s` = `%s` - 1 where `mid` = ? and `%s` > 0"
_updateUserVideoView = "update `user_statistics` set `play_total` = ? where `mid` = ?;"
)
// RawBatchUserStatistics 从数据库获取用户基础信息
func (d *Dao) RawBatchUserStatistics(c context.Context, mids []int64) (res map[int64]*api.UserStat, err error) {
if len(mids) == 0 {
return
}
res = make(map[int64]*api.UserStat)
midStr := xstr.JoinInts(mids)
querySQL := fmt.Sprintf(_batchUserStatistics, midStr)
rows, err := d.db.Query(c, querySQL)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_query"), log.KV("error", err), log.KV("sql", querySQL))
return
}
defer rows.Close()
for rows.Next() {
var mid int64
stat := new(api.UserStat)
if err = rows.Scan(&mid, &stat.Sv, &stat.Follow, &stat.Fan, &stat.Like, &stat.Liked, &stat.Black, &stat.View); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("error", err), log.KV("sql", querySQL))
return
}
res[mid] = stat
}
log.Infov(c, log.KV("event", "mysql_query"), log.KV("row_num", len(res)), log.KV("sql", querySQL))
return
}
//TxIncrUserStatisticsFollow .
func (d *Dao) TxIncrUserStatisticsFollow(tx *sql.Tx, mid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_incrUserStatisticsFollow, mid); err != nil {
log.Error("incr user Video follow err(%v)", err)
return
}
return res.RowsAffected()
}
//TxIncrUserStatisticsFan .
func (d *Dao) TxIncrUserStatisticsFan(tx *sql.Tx, mid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_incrUserStatisticsFan, mid); err != nil {
log.Error("incr user Video fan err(%v)", err)
return
}
return res.RowsAffected()
}
//TxDecrUserStatisticsFollow .
func (d *Dao) TxDecrUserStatisticsFollow(tx *sql.Tx, mid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_decrUserStatisticsFollow, mid); err != nil {
log.Error("decr user Video follow err(%v)", err)
return
}
return res.RowsAffected()
}
//TxDecrUserStatisticsFan .
func (d *Dao) TxDecrUserStatisticsFan(tx *sql.Tx, mid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_decrUserStatisticsFan, mid); err != nil {
log.Error("decr user Video fan err(%v)", err)
return
}
return res.RowsAffected()
}
//TxIncrUserStatisticsField .
func (d *Dao) TxIncrUserStatisticsField(c context.Context, tx *sql.Tx, mid int64, field string) (rowsAffected int64, err error) {
var res xsql.Result
querySQL := fmt.Sprintf(_incrUserStatisticField, field, field, field)
if res, err = tx.Exec(querySQL, mid); err != nil {
log.Errorv(c, log.KV("event", "incr_user_statistic"), log.KV("field", field), log.KV("mid", mid), log.KV("err", err))
return
}
return res.RowsAffected()
}
//TxDescUserStatisticsField .
func (d *Dao) TxDescUserStatisticsField(c context.Context, tx *sql.Tx, mid int64, field string) (rowsAffected int64, err error) {
var res xsql.Result
querySQL := fmt.Sprintf(_decrUserStatisticsField, field, field, field)
if res, err = tx.Exec(querySQL, mid); err != nil {
log.Errorv(c, log.KV("event", "incr_user_statistic"), log.KV("field", field), log.KV("mid", mid), log.KV("err", err))
return
}
return res.RowsAffected()
}
// UpdateUserVideoView .
func (d *Dao) UpdateUserVideoView(c context.Context, mid int64, views int64) error {
_, err := d.db.Exec(c, _updateUserVideoView, views, mid)
return err
}

View File

@@ -0,0 +1,146 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRawBatchUserStatistics(t *testing.T) {
convey.Convey("RawBatchUserStatistics", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{88895104}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.RawBatchUserStatistics(c, mids)
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 TestDaoTxIncrUserStatisticsFollow(t *testing.T) {
convey.Convey("TxIncrUserStatisticsFollow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxIncrUserStatisticsFollow(tx, mid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxIncrUserStatisticsFan(t *testing.T) {
convey.Convey("TxIncrUserStatisticsFan", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxIncrUserStatisticsFan(tx, mid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxDecrUserStatisticsFollow(t *testing.T) {
convey.Convey("TxDecrUserStatisticsFollow", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxDecrUserStatisticsFollow(tx, mid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxDecrUserStatisticsFan(t *testing.T) {
convey.Convey("TxDecrUserStatisticsFan", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
num, err := d.TxDecrUserStatisticsFan(tx, mid)
ctx.Convey("Then err should be nil.num should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(num, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxIncrUserStatisticsField(t *testing.T) {
convey.Convey("TxIncrUserStatisticsField", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
field = "like_total"
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rowsAffected, err := d.TxIncrUserStatisticsField(c, tx, mid, field)
ctx.Convey("Then err should be nil.rowsAffected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rowsAffected, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxDescUserStatisticsField(t *testing.T) {
convey.Convey("TxDescUserStatisticsField", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(88895104)
field = "like_total"
)
tx, _ := d.BeginTran(c)
defer func() {
tx.Rollback()
}()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rowsAffected, err := d.TxDescUserStatisticsField(c, tx, mid, field)
ctx.Convey("Then err should be nil.rowsAffected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rowsAffected, convey.ShouldNotBeNil)
})
})
})
}