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,55 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["api_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "go-common/app/service/live/xuser/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/banned_service/api/liverpc:go_default_library",
"//app/service/live/fans_medal/api/liverpc:go_default_library",
"//app/service/live/room/api/liverpc:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//library/net/rpc/liverpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/live/xuser/dao/account:all-srcs",
"//app/service/live/xuser/dao/exp:all-srcs",
"//app/service/live/xuser/dao/guard:all-srcs",
"//app/service/live/xuser/dao/roomAdmin:all-srcs",
"//app/service/live/xuser/dao/vip:all-srcs",
"//app/service/live/xuser/dao/xanchor:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"userInfo.go",
],
importpath = "go-common/app/service/live/xuser/dao/account",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,35 @@
package account
import (
"context"
"go-common/app/service/live/xuser/conf"
account "go-common/app/service/main/account/rpc/client"
)
// Dao dao
type Dao struct {
c *conf.Config
accountRPC *account.Service3
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
accountRPC: account.New3(nil),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
// check
return nil
}

View File

@@ -0,0 +1,46 @@
package account
import (
"context"
"github.com/pkg/errors"
v1 "go-common/app/service/main/account/api"
accountM "go-common/app/service/main/account/model"
"go-common/library/ecode"
"go-common/library/log"
)
// GetUserCard3 ...
// 调用account grpc接口cards获取用户信息
func (d *Dao) GetUserCard3(c context.Context, UIDs []int64) (userResult map[int64]*accountM.Card, err error) {
userResult = make(map[int64]*accountM.Card)
lens := len(UIDs)
if lens <= 0 {
return
}
ret, err := d.accountRPC.Cards3(c, &accountM.ArgMids{Mids: UIDs})
if err != nil {
err = errors.WithMessage(ecode.AccountGRPCError, "GET SEA PATROL FAIL")
log.Error("Call main.Account.Cards Error.Infos(%+v) error(%+v)", UIDs, err)
}
// 整理数据
for _, item := range ret {
if item != nil {
userResult[item.Mid] = item
}
}
return
}
// GetUserInfo ...
// 调用account grpc接口info获取用户信息
func (d *Dao) GetUserInfo(c context.Context, UID int64) (userResult *v1.Info, err error) {
userResult = &v1.Info{}
ret, err := d.accountRPC.Info3(c, &accountM.ArgMid{Mid: UID})
if err != nil {
err = errors.WithMessage(ecode.AccountGRPCError, "GET SEA PATROL FAIL")
log.Error("Call main.Account.Cards Error.Infos(%+v) error(%+v)", UID, err)
return
}
userResult = ret
return
}

View File

@@ -0,0 +1,47 @@
package dao
import (
banned_api "go-common/app/service/live/banned_service/api/liverpc"
fans_medal "go-common/app/service/live/fans_medal/api/liverpc"
room_api "go-common/app/service/live/room/api/liverpc"
"go-common/app/service/live/xuser/conf"
"go-common/library/net/rpc/liverpc"
)
// Dao dao
type Dao struct {
c *conf.Config
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
}
return
}
// RoomAPI .
var RoomAPI *room_api.Client
// FansMedalAPI .
var FansMedalAPI *fans_medal.Client
// BannedAPI .
var BannedAPI *banned_api.Client
// InitAPI init all service APIs
func InitAPI() {
RoomAPI = room_api.New(getConf("room"))
FansMedalAPI = fans_medal.New(getConf("fans_medal"))
BannedAPI = banned_api.New(getConf("banned"))
}
func getConf(appName string) *liverpc.ClientConfig {
c := conf.Conf.LiveRPC
if c != nil {
return c[appName]
}
return nil
}

View File

@@ -0,0 +1,46 @@
package dao
import (
"go-common/app/service/live/xuser/conf"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = &conf.Config{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
dao := New(c)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoInitAPI(t *testing.T) {
convey.Convey("InitAPI", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
InitAPI()
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestDaogetConf(t *testing.T) {
convey.Convey("getConf", t, func(ctx convey.C) {
var (
appName = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := getConf(appName)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"memcache.go",
"mysql.go",
],
importpath = "go-common/app/service/live/xuser/dao/exp",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model/exp:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup: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"],
)

View File

@@ -0,0 +1,46 @@
package exp
import (
"context"
"go-common/app/service/live/xuser/conf"
"go-common/library/cache/memcache"
xsql "go-common/library/database/sql"
)
// Dao exp dao
type Dao struct {
c *conf.Config
db *xsql.DB
memcache *memcache.Pool
}
// NewExpDao init mysql db
func NewExpDao(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
db: xsql.NewMySQL(c.UserExpMySQL),
memcache: memcache.NewPool(c.ExpMemcache),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.db.Close()
d.memcache.Close()
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
func (d *Dao) getExpire() (respExpire int32) {
if t := conf.Conf.UserExpExpire; t != nil {
respExpire = t.ExpireTime
} else {
respExpire = _emptyExpire
}
return
}

View File

@@ -0,0 +1,152 @@
package exp
import (
"context"
expModel "go-common/app/service/live/xuser/model/exp"
gmc "go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
"go-common/library/sync/errgroup"
"math/rand"
"strconv"
"time"
)
// redis cache
const (
_prefixExp = "json_e_" // 用户经验cache key,json协议
_emptyExpire = 20 * 24 * 3600
_errorMcLogPrefix = "xuser.exp.dao.memcache"
_promGetSuccess = "xuser_exp_mc:获取用户经验cache成功"
_promDelSuccess = "xuser_exp_mc:成功删除用户经验cache"
_promDelErr = "xuser_exp_mc:删除用户经验cache失败"
_promGetErr = "xuser_exp_mc:批量获取用户经验key失败"
_promScanErr = "xuser_exp_mc:解析用户经验key失败"
// _recExpire = 5 * 24 * 3600
)
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
cacheHitCount = prom.CacheHit
cacheMissCount = prom.CacheMiss
)
// PromError prometheus error count.
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo prometheus info count.
func PromInfo(name string) {
infosCount.Incr(name)
}
// PromCacheHit prometheus cache hit count.
func PromCacheHit(name string) {
cacheHitCount.Incr(name)
}
// PromCacheMiss prometheus cache hit count.
func PromCacheMiss(name string) {
cacheMissCount.Incr(name)
}
func expKey(mid int64) string {
return _prefixExp + strconv.FormatInt(mid, 10)
}
// SetExpListCache 批量设置用户经验cache
func (d *Dao) SetExpListCache(c context.Context, expList map[int64]*expModel.LevelInfo) (err error) {
return d.setExpListCache(c, expList)
}
// DelExpFromMemCache 删除获取用户经验cache,不支持批量
func (d *Dao) DelExpFromMemCache(c context.Context, mid int64) (err error) {
return d.delExpFromMemCache(c, expKey(mid))
}
// GetExpFromMemCache 对外接口
func (d *Dao) GetExpFromMemCache(ctx context.Context, mids []int64) (expList map[int64]*expModel.LevelInfo, missedUids []int64, err error) {
return d.getExpFromMemCache(ctx, mids)
}
// getExpFromMemCache 批量获取用户经验cache
func (d *Dao) getExpFromMemCache(ctx context.Context, mids []int64) (expList map[int64]*expModel.LevelInfo, arrayMissedUids []int64, err error) {
var expKeys []string
expList = make(map[int64]*expModel.LevelInfo)
mapMissedUids := make(map[int64]bool)
arrayMissedUids = make([]int64, 0)
for _, uid := range mids {
expKeys = append(expKeys, expKey(uid))
mapMissedUids[uid] = true
}
group := errgroup.Group{}
group.Go(func() error {
conn := d.memcache.Get(context.TODO())
defer conn.Close()
resp, err := conn.GetMulti(expKeys)
if err != nil {
PromError(_promGetErr)
log.Error(_errorMcLogPrefix+"|conn.Gets(%v) error(%v)", expKeys, err)
return nil
}
// miss uids
for _, item := range resp {
element := &expModel.LevelInfo{}
if err = conn.Scan(item, &element); err != nil {
PromError(_promScanErr)
log.Error(_errorMcLogPrefix+"|item.Scan(%s) error(%v)", item.Value, err)
return err
}
expList[element.UID] = element
mapMissedUids[element.UID] = false
}
return nil
})
group.Wait()
for k, v := range mapMissedUids {
if v {
arrayMissedUids = append(arrayMissedUids, k)
}
}
PromInfo(_promGetSuccess)
return
}
func (d *Dao) delExpFromMemCache(ctx context.Context, key string) (err error) {
conn := d.memcache.Get(ctx)
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
log.Error(_errorMcLogPrefix+"|Delete(%s) error(%v)", key, err)
PromError(_promDelErr)
}
}
PromInfo(_promDelSuccess)
conn.Close()
return
}
func (d *Dao) setExpListCache(ctx context.Context, expList map[int64]*expModel.LevelInfo) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(3600)
conn := d.memcache.Get(context.TODO())
defer conn.Close()
for _, v := range expList {
item := &gmc.Item{Key: expKey(v.UID),
Object: &expModel.LevelInfo{UID: v.UID, UserLevel: v.UserLevel, AnchorLevel: v.AnchorLevel, CTime: v.CTime, MTime: v.MTime},
Expiration: expire, Flags: gmc.FlagJSON}
err := conn.Set(item)
if err != nil {
PromError("mc:设置上报")
log.Error(_errorMcLogPrefix+"|conn.Set(%v) error(%v)", expKey(v.UID), err)
continue
}
}
return
}

View File

@@ -0,0 +1,111 @@
package exp
import (
"context"
"database/sql"
"fmt"
expm "go-common/app/service/live/xuser/model/exp"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_shard = 10
_insExp = "INSERT IGNORE INTO user_exp_%d (uid,uexp,rexp) VALUES(?,?,?)"
_selExp = "SELECT uid,uexp,rexp,ctime,mtime FROM user_exp_%d where uid=?"
_inSelExp = "SELECT uid,uexp,rexp,ctime,mtime FROM user_exp_%d where uid IN (%s)"
_addUexp = "INSERT INTO user_exp_%d(uid,uexp,rexp) VALUES(?,?,0) ON DUPLICATE KEY UPDATE uexp=uexp+%d"
_addRexp = "INSERT INTO user_exp_%d(uid,uexp,rexp) VALUES(?,0,?) ON DUPLICATE KEY UPDATE rexp=rexp+%d"
_errorDBLogPrefix = "xuser.exp.dao.mysql"
)
// InitExp 初始化用户经验,用于首次查询
func (d *Dao) InitExp(c context.Context, uid int64, uexp int64, rexp int64) (row int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_insExp, uid%_shard), uid, uexp, rexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|InitExp d.exp.Exec err: %v", err)
return
}
return res.RowsAffected()
}
// Exp 查询一条记录
func (d *Dao) Exp(c context.Context, uid int64) (exp *expm.Exp, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selExp, uid%_shard), uid)
exp = &expm.Exp{}
if err = row.Scan(&exp.UID, &exp.Uexp, &exp.Rexp, &exp.CTime, &exp.MTime); err == sql.ErrNoRows {
// 查询结果为空时,初始化数据
_, err = d.InitExp(c, uid, 0, 0)
}
if err != nil {
log.Error(_errorDBLogPrefix+"|Exp row.Scan err: %v", err)
return
}
return
}
// MultiExp 批量查询
func (d *Dao) MultiExp(c context.Context, uids []int64) (exps []*expm.Exp, err error) {
exps = make([]*expm.Exp, 0)
var (
suffix int64
uidGroup [_shard][]int64
um = make(map[int64]struct{}, len(uids))
)
for _, uid := range uids {
suffix = uid % _shard
uidGroup[suffix] = append(uidGroup[suffix], uid)
um[uid] = struct{}{}
}
for index, uids := range uidGroup {
if 0 == len(uids) {
continue
}
rows, err1 := d.db.Query(c, fmt.Sprintf(_inSelExp, index, xstr.JoinInts(uids)))
if err1 != nil {
err = err1
log.Error(_errorDBLogPrefix+"|MultiExp d.exp.Query err: %v", err)
return
}
for rows.Next() {
ele := &expm.Exp{}
if err = rows.Scan(&ele.UID, &ele.Uexp, &ele.Rexp, &ele.CTime, &ele.MTime); err != nil {
log.Error(_errorDBLogPrefix+"|MultiExp rows.Scan err: %v", err)
return
}
exps = append(exps, ele)
delete(um, ele.UID)
}
}
// 初始化不存在的数据,补齐数据
for uid := range um {
d.InitExp(c, uid, 0, 0)
ele := &expm.Exp{UID: uid, Uexp: 0, Rexp: 0}
exps = append(exps, ele)
}
return
}
// AddUexp 添加用户经验
func (d *Dao) AddUexp(c context.Context, uid int64, uexp int64) (affect int64, err error) {
upSQL := fmt.Sprintf(_addUexp, uid%_shard, uexp)
res, err := d.db.Exec(c, upSQL, uid, uexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|Exec(%s) error(%v)", upSQL, err)
return
}
return res.RowsAffected()
}
// AddRexp 添加主播经验
func (d *Dao) AddRexp(c context.Context, uid int64, rexp int64) (affect int64, err error) {
upSQL := fmt.Sprintf(_addRexp, uid%_shard, rexp)
res, err := d.db.Exec(c, upSQL, uid, rexp)
if err != nil {
log.Error(_errorDBLogPrefix+"|AddRexp|Exec(%s) error(%v)", upSQL, err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/service/live/xuser/dao/guard",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//app/service/live/xuser/model/dhh:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,48 @@
package guard
import (
"context"
"fmt"
"go-common/library/cache/redis"
"go-common/library/log"
)
// redis cache
const (
_lockKey = "saveGuard:%s"
_cacheKeyUid = "live_user:guard:uid:v1:%d"
_cacheKeyTargetId = "live_user:guard:target_id:v1:%d"
)
// LockOrder lock for same order
func (d *GuardDao) LockOrder(ctx context.Context, orderID string) (ok bool, err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("SET", fmt.Sprintf(_lockKey, orderID), 1, "EX", 3*86400, "NX"))
if err == redis.ErrNil {
log.Info("LockOrder(%s) is ErrNil!", orderID)
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}
// UnlockOrder release lock for same order
func (d *GuardDao) UnlockOrder(ctx context.Context, orderID string) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_lockKey, orderID)))
return
}
// ClearCache delete cache for guard
func (d *GuardDao) ClearCache(ctx context.Context, uid int64, ruid int64) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_cacheKeyUid, uid)))
_, err = redis.String(conn.Do("DEL", fmt.Sprintf(_cacheKeyTargetId, ruid)))
return
}

View File

@@ -0,0 +1,47 @@
package guard
import (
"context"
"go-common/app/service/live/xuser/conf"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
)
// GuardDao vip dao
type GuardDao struct {
c *conf.Config
db *xsql.DB
redis *redis.Pool
}
// NewGuardDao init mysql db
func NewGuardDao(c *conf.Config) (dao *GuardDao) {
dao = &GuardDao{
c: c,
db: xsql.NewMySQL(c.LiveAppMySQL),
redis: redis.NewPool(c.GuardRedis),
}
return
}
// Close close the resource.
func (d *GuardDao) Close() {
d.db.Close()
d.redis.Close()
}
// Ping dao ping
func (d *GuardDao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
func (d *GuardDao) getExpire() (respExpire int32) {
if t := conf.Conf.UserDaHangHaiExpire; t != nil {
respExpire = t.ExpireTime
} else {
respExpire = _emptyExpire
}
return
}

View File

@@ -0,0 +1,197 @@
package guard
import (
"context"
"fmt"
"go-common/library/xstr"
"time"
confm "go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/model"
dhhm "go-common/app/service/live/xuser/model/dhh"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_guardTable = "ap_user_privilege"
)
var (
// add guard info
_addGuardInfo = "REPLACE INTO `%s` (`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time`) VALUES(?,?,?,?,?);"
// get guard info
_getGuardInfo = "SELECT `id`,`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time` FROM `%s` WHERE `uid`=? AND `expired_time`>=? ORDER BY `privilege_type` ASC;"
// get guard info
_getGuardInfo2 = "SELECT `id`,`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time` FROM `%s` WHERE `uid`=? AND `target_id`=? AND `expired_time`>=? ORDER BY `privilege_type` ASC;"
// update guard info
_updGuardInfo = "UPDATE `%s` SET `expired_time`=date_add(expired_time, interval ? day) WHERE `uid`=? AND `target_id`=? AND `expired_time`>=? AND `privilege_type`%s?"
// upsert guard info
_upsertGuardInfo = "INSERT INTO `%s` (`uid`,`target_id`,`privilege_type`,`start_time`,`expired_time`) VALUES(?,?,?,?,?) ON DUPLICATE KEY UPDATE `start_time`=?,`expired_time`=?;"
// 查询大航海信息
_selUID = "SELECT id,uid,target_id,privilege_type,start_time,expired_time,ctime,utime FROM ap_user_privilege where uid IN (%s) AND expired_time >= '%s' "
_selAnchorUID = "SELECT id,uid,target_id,privilege_type,start_time,expired_time,ctime,utime FROM ap_user_privilege where target_id IN (%s) AND expired_time >= '%s' "
_errorDBLogPrefix = "xuser.dahanghai.dao.mysql|"
)
// GetByUIDs 批量查询
func (d *GuardDao) GetByUIDs(c context.Context, uids []int64) (dhhs []*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make([]*dhhm.DHHDB, 0)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
dhhs = append(dhhs, ele)
}
return
}
// GetByUIDsWithMap 批量查询
func (d *GuardDao) GetByUIDsWithMap(c context.Context, uids []int64) (dhhs map[int64][]*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make(map[int64][]*dhhm.DHHDB)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
if _, exist := dhhs[ele.UID]; !exist {
dhhs[ele.UID] = make([]*dhhm.DHHDB, 0)
}
dhhs[ele.UID] = append(dhhs[ele.UID], ele)
}
return
}
// GetByAnchorUIDs 批量查询
func (d *GuardDao) GetByAnchorUIDs(c context.Context, uids []int64) (dhhs []*dhhm.DHHDB, err error) {
reqStartTime := confm.RecordTimeCost()
dhhs = make([]*dhhm.DHHDB, 0)
tm := time.Now()
timeNow := tm.Format("2006-1-2 15:04:05")
rows, err1 := d.db.Query(c, fmt.Sprintf(_selAnchorUID, xstr.JoinInts(uids), timeNow))
if err1 != nil {
reqAfterTime := confm.RecordTimeCost()
err = err1
log.Error(_errorDBLogPrefix+confm.GetFromDHHDBError+"|GetByUIDs err: %v|cost:%dms", err, reqAfterTime-reqStartTime)
return
}
for rows.Next() {
ele := &dhhm.DHHDB{}
if err = rows.Scan(&ele.ID, &ele.UID, &ele.TargetId, &ele.PrivilegeType, &ele.StartTime, &ele.ExpiredTime, &ele.Ctime, &ele.Utime); err != nil {
log.Error(_errorDBLogPrefix+confm.ScanFromDHHDBError+"|GetByUIDs rows.Scan err: %v", err)
return
}
dhhs = append(dhhs, ele)
}
return
}
// GetGuardByUID get guard info by uid
func (d *GuardDao) GetGuardByUID(ctx context.Context, uid int64) (info []*model.GuardInfo, err error) {
sql := fmt.Sprintf(_getGuardInfo, _guardTable)
rows, err := d.db.Query(ctx, sql, uid, time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUID] get user guard record error(%v), uid(%d)", err, uid)
return nil, err
}
defer rows.Close()
for rows.Next() {
var inf model.GuardInfo
err = rows.Scan(&inf.Id, &inf.Uid, &inf.TargetId, &inf.PrivilegeType, &inf.StartTime, &inf.ExpiredTime)
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUID] scan user guard record error(%v), uid(%d)", err, uid)
return nil, err
}
info = append(info, &inf)
}
return
}
// GetGuardByUIDRuid get guard info by uid and ruid
func (d *GuardDao) GetGuardByUIDRuid(ctx context.Context, uid int64, ruid int64) (info []*model.GuardInfo, err error) {
sql := fmt.Sprintf(_getGuardInfo2, _guardTable)
rows, err := d.db.Query(ctx, sql, uid, ruid, time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUIDRuid] get user guard record error(%v), uid(%d), ruid(%d)", err, uid, ruid)
return nil, err
}
defer rows.Close()
for rows.Next() {
var inf model.GuardInfo
err = rows.Scan(&inf.Id, &inf.Uid, &inf.TargetId, &inf.PrivilegeType, &inf.StartTime, &inf.ExpiredTime)
if err != nil {
log.Error("[dao.guard.mysql|GetGuardByUIDRuid] scan user guard record error(%v), uid(%d), ruid(%d)", err, uid, ruid)
return nil, err
}
info = append(info, &inf)
}
return
}
// AddGuard insert guard
func (d *GuardDao) AddGuard(ctx context.Context, req *model.GuardBuy) (err error) {
sql := fmt.Sprintf(_addGuardInfo, _guardTable)
now := time.Now()
endTime := time.Date(now.Year(), now.Month(), now.Day(), int(23), int(59), int(59), int(999), time.Local).AddDate(0, 0, req.Num*30)
res, err := d.db.Exec(ctx, sql, req.Uid, req.Ruid, req.GuardLevel, now.Format("2006-01-02 15:04:05"), endTime.Format("2006-01-02 15:04:05"))
if err != nil {
// unique key exists error
log.Error("[dao.guard.mysql|AddGuard] add user guard record error(%v), req(%v)", err, req)
return
}
if _, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
log.Error("[dao.guard.mysql|AddGuard] get last insert id error(%v), req(%v)", err, req)
}
return
}
// UpdateGuard update guard info
func (d *GuardDao) UpdateGuard(ctx context.Context, req *model.GuardBuy, cond string) (err error) {
sql := fmt.Sprintf(_updGuardInfo, _guardTable, cond)
_, err = d.db.Exec(ctx, sql, req.Num*30, req.Uid, req.Ruid, time.Now().Format("2006-01-02 15:04:05"), req.GuardLevel)
if err != nil {
log.Error("[dao.guard.mysql|UpdateGuard] update user guard record error(%v), req(%v)", err, req)
return
}
return
}
// UpsertGuard upsert guard info
func (d *GuardDao) UpsertGuard(ctx context.Context, req *model.GuardBuy, expiredTime string) (err error) {
sql := fmt.Sprintf(_upsertGuardInfo, _guardTable)
now := time.Now()
_, err = d.db.Exec(ctx, sql, req.Uid, req.Ruid, req.GuardLevel, now.Format("2006-01-02 15:04:05"), expiredTime, now.Format("2006-01-02 15:04:05"), expiredTime)
if err != nil {
log.Error("[dao.guard.mysql|UpsertGuard] upsert user guard record error(%v), req(%v)", err, req)
return
}
return
}

View File

@@ -0,0 +1,361 @@
package guard
import (
"context"
"encoding/json"
"github.com/pkg/errors"
dahanghaiModel "go-common/app/service/live/xuser/model/dhh"
gmc "go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/stat/prom"
"math/rand"
"strconv"
"time"
)
// redis cache
const (
_prefixUID = "live_user:guard:uid:v1:" // 用户侧key
_prefixTopList = "GOVERNOR_SHOW_TID:" // 最近购买总督key
_prefixAnchorID = "live_user:guard:target_id:v1:" // 主播侧key
_emptyExpire = 3600
_errorRedisLogPrefix = "xuser.dahanghai.dao.redis"
_promGetSuccess = "xuser_dahanghai_redis:获取用户大航海cache成功"
_promDelSuccess = "xuser_dahanghai_redis:成功删除用户大航海cache"
_promDelErr = "xuser_dahanghai_redis:删除用户大航海cache失败"
_promGetErr = "xuser_dahanghai_redis:批量获取用户大航海key失败"
// _promScanErr = "xuser_dahanghai_redis:解析用户大航海key失败"
)
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
cacheHitCount = prom.CacheHit
cacheMissCount = prom.CacheMiss
)
// PromError prometheus error count.
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo prometheus info count.
func PromInfo(name string) {
infosCount.Incr(name)
}
// PromCacheHit prometheus cache hit count.
func PromCacheHit(name string) {
cacheHitCount.Incr(name)
}
// PromCacheMiss prometheus cache hit count.
func PromCacheMiss(name string) {
cacheMissCount.Incr(name)
}
func dahanghaiUIDKey(mid int64) string {
return _prefixUID + strconv.FormatInt(mid, 10)
}
func guardAnchorUIDKey(mid int64) string {
return _prefixAnchorID + strconv.FormatInt(mid, 10)
}
func recentGuardTopKey(mid int64) string {
return _prefixTopList + strconv.FormatInt(mid, 10)
}
// SetDHHListCache ... 批量设置用户守护cache
func (d *GuardDao) SetDHHListCache(c context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
return d.setDHHListCache(c, dhhList, uid)
}
// SetAnchorGuardListCache ... 批量设置主播维度守护信息cache
func (d *GuardDao) SetAnchorGuardListCache(c context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
return d.setAnchorGuardListCache(c, dhhList, uid)
}
// DelDHHFromRedis 删除获取用户守护cache,不支持批量
func (d *GuardDao) DelDHHFromRedis(c context.Context, mid int64) (err error) {
return d.delDHHFromRedis(c, dahanghaiUIDKey(mid))
}
// GetUIDAllGuardFromRedis 获取单个用户的全量守护信息
func (d *GuardDao) GetUIDAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getUIDAllGuardFromRedis(ctx, mids)
}
// GetUIDAllGuardFromRedisBatch 获取批量用户的全量守护信息
func (d *GuardDao) GetUIDAllGuardFromRedisBatch(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getUIDAllGuardFromRedisBatch(ctx, mids)
}
// GetAnchorAllGuardFromRedis 获取单个主播的全量被守护信息(同一个主播仅获取最高级别)
func (d *GuardDao) GetAnchorAllGuardFromRedis(ctx context.Context, anchorUIDs []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getAnchorAllGuardFromRedis(ctx, anchorUIDs)
}
// GetGuardTopListCache 获取单个用户的全量守护信息
func (d *GuardDao) GetGuardTopListCache(ctx context.Context, uid int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return d.getGuardTopListCache(ctx, uid)
}
// GetAnchorRecentTopGuardCache 获取单个主播最近的总督信息
func (d *GuardDao) GetAnchorRecentTopGuardCache(ctx context.Context, uid int64) (resp map[int64]int64, err error) {
return d.getAnchorRecentTopGuardCache(ctx, uid)
}
func (d *GuardDao) getGuardTopListCache(ctx context.Context, uid int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
return
}
// getUIDAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getUIDAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(dahanghaiUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
return nil, nil
}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhList); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
// getUIDAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getUIDAllGuardFromRedisBatch(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(dahanghaiUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
continue
}
dhhListLoop := make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhListLoop); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
} else {
dhhList = append(dhhList, dhhListLoop...)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
// getAnchorAllGuardFromRedis 批量获取用户cache
func (d *GuardDao) getAnchorAllGuardFromRedis(ctx context.Context, mids []int64) (dhhList []*dahanghaiModel.DaHangHaiRedis2, err error) {
var (
conn = d.redis.Get(ctx)
args = redis.Args{}
cacheResult [][]byte
)
defer conn.Close()
for _, uid := range mids {
args = args.Add(guardAnchorUIDKey(uid))
}
if cacheResult, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
PromError(_promGetErr)
log.Error(_errorRedisLogPrefix+"|conn.MGET(%v) error(%v)", args, err)
err = errors.Wrapf(err, "redis.StringMap(conn.Do(MGET,%v)", args)
}
return
}
dhhList = make([]*dahanghaiModel.DaHangHaiRedis2, 0)
dhhListSingle := &dahanghaiModel.DaHangHaiRedis2{}
if len(cacheResult) > 0 {
for k, v := range cacheResult {
if v == nil {
return nil, nil
}
if len(v) > 0 {
if err = json.Unmarshal([]byte(v), &dhhList); err != nil {
if err = json.Unmarshal([]byte(v), &dhhListSingle); err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, k, v)
return nil, nil
}
dhhList = append(dhhList, dhhListSingle)
}
}
}
}
PromInfo(_promGetSuccess)
return
}
func (d *GuardDao) delDHHFromRedis(ctx context.Context, key string) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
_, err = conn.Do("DEL", key)
if err == gmc.ErrNotFound {
err = nil
} else {
log.Error(_errorRedisLogPrefix+"|Delete(%s) error(%v)", key, err)
PromError(_promDelErr)
}
PromInfo(_promDelSuccess)
conn.Close()
return
}
func (d *GuardDao) setDHHListCache(ctx context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
var (
argsMid = redis.Args{}
conn = d.redis.Get(ctx)
dhhJSON []byte
)
defer conn.Close()
key := dahanghaiUIDKey(uid)
if len(dhhList) == 0 {
argsMid = argsMid.Add(key).Add("")
} else {
dhhJSON, err = json.Marshal(dhhList)
if err != nil {
log.Error("[dao.dahanghai.cache|GetDHHFromRedis] json.Marshal rawInfo error(%v), uid(%d)", err, uid)
return
}
argsMid = argsMid.Add(key).Add(string(dhhJSON))
}
if err = conn.Send("SET", argsMid...); err != nil {
err = errors.Wrap(err, "conn.Send(SET) error")
return
}
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
if err = conn.Send("EXPIRE", key, expire); err != nil {
log.Error("setDHHListCache conn.Send(Expire, %s, %d) error(%v)", key, expire, err)
return
}
return
}
func (d *GuardDao) setAnchorGuardListCache(ctx context.Context, dhhList []dahanghaiModel.DaHangHaiRedis2, uid int64) (err error) {
expire := d.getExpire()
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
var (
argsMid = redis.Args{}
conn = d.redis.Get(ctx)
dhhJSON []byte
)
defer conn.Close()
key := guardAnchorUIDKey(uid)
if len(dhhList) == 0 {
argsMid = argsMid.Add(key).Add("")
} else {
dhhJSON, err = json.Marshal(dhhList)
if err != nil {
log.Error("[dao.dahanghai.cache|setAnchorGuardListCache] json.Marshal rawInfo error(%v), uid(%d)", err, uid)
return
}
argsMid = argsMid.Add(key).Add(string(dhhJSON))
}
if err = conn.Send("SET", argsMid...); err != nil {
err = errors.Wrap(err, "conn.Send(SET) error")
return
}
rand.Seed(time.Now().UnixNano())
expire = expire + rand.Int31n(60)
if err = conn.Send("EXPIRE", key, expire); err != nil {
log.Error("setAnchorGuardListCache conn.Send(Expire, %s, %d) error(%v)", key, expire, err)
return
}
return
}
func (d *GuardDao) getAnchorRecentTopGuardCache(ctx context.Context, uid int64) (resp map[int64]int64, err error) {
resp = make(map[int64]int64)
nowTime := time.Now().Unix()
cacheKey := recentGuardTopKey(uid)
var (
conn = d.redis.Get(ctx)
)
values, err := redis.Values(conn.Do("ZRANGEBYSCORE", cacheKey, nowTime, "INF", "WITHSCORES"))
if err != nil {
log.Error("getAnchorRecentTopGuardCache.conn.Do(ZRANGEBYSCORE %v) error(%v)", cacheKey, err)
return
}
if len(values) == 0 {
return
}
var aid, unix int64
for len(values) > 0 {
if values, err = redis.Scan(values, &aid, &unix); err != nil {
log.Error("getAnchorRecentTopGuardCache.redis.Scan(%v) error(%v)", values, err)
return
}
resp[aid] = unix
}
return
}

View File

@@ -0,0 +1,74 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"mc.cache_test.go",
"mc_test.go",
"mysql_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"mc.cache.go",
"mc.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/service/live/xuser/dao/roomAdmin",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/banned_service/api/liverpc/v1:go_default_library",
"//app/service/live/fans_medal/api/liverpc/v2:go_default_library",
"//app/service/live/room/api/liverpc/v1:go_default_library",
"//app/service/live/room/api/liverpc/v2:go_default_library",
"//app/service/live/xuser/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/dao:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/orm:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,933 @@
package roomAdmin
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/jinzhu/gorm"
banned "go-common/app/service/live/banned_service/api/liverpc/v1"
v12 "go-common/app/service/live/fans_medal/api/liverpc/v2"
"go-common/app/service/live/room/api/liverpc/v1"
"go-common/app/service/live/room/api/liverpc/v2"
v1pb "go-common/app/service/live/xuser/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/dao"
"go-common/app/service/live/xuser/model"
account "go-common/app/service/main/account/model"
accrpc "go-common/app/service/main/account/rpc/client"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/database/orm"
xsql "go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"io/ioutil"
"math"
"net/http"
"sort"
"strconv"
"time"
)
// Dao dao
type Dao struct {
c *conf.Config
mc *memcache.Pool
redis *redis.Pool
db *xsql.DB
orm *gorm.DB
RoomAdminExpire int32
// acc rpc
acc *accrpc.Service3
client *bm.Client
}
const (
userPrefix = "up_v1_%d"
roomPrefix = "rp_v1_%d"
mcExpire = 3600
maxAdminsNum = 100
)
// KeyUser return the mc key by user mid.
func KeyUser(uid int64) string {
return fmt.Sprintf(userPrefix, uid)
}
// KeyRoom return the mc key by anchor mid.
func KeyRoom(uid int64) string {
return fmt.Sprintf(roomPrefix, uid)
}
//go:generate $GOPATH/src/go-common/app/tool/cache/mc
type _mc interface {
// 获取主播的房管列表
// mc: -key=KeyRoom
CacheRoomAdminRoom(c context.Context, anchor int64) ([]*model.RoomAdmin, error)
// 获取用户的房管列表
// mc: -key=KeyUser
CacheRoomAdminUser(c context.Context, user int64) ([]*model.RoomAdmin, error)
// mc: -key=KeyRoom -expire=d.RoomAdminExpire -encode=json|gzip
AddCacheKeyAnchorRoom(c context.Context, anchor int64, value []*model.RoomAdmin) error
// mc: -key=KeyUser -expire=d.RoomAdminExpire -encode=gob
AddCacheRoomAdminUser(c context.Context, user int64, value []*model.RoomAdmin) error
// mc: -key=KeyRoom
DelCacheKeyAnchorRoom(c context.Context, anchor int64) error
// mc: -key=KeyUser
DelCacheRoomAdminUser(c context.Context, user int64) error
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
mc: memcache.NewPool(c.Memcache),
redis: redis.NewPool(c.Redis),
db: xsql.NewMySQL(c.LiveAppMySQL),
orm: orm.NewMySQL(c.LiveAppORM),
RoomAdminExpire: mcExpire,
acc: accrpc.New3(c.AccountRPC),
client: bm.NewClient(c.BMClient),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
d.redis.Close()
d.orm.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
return d.db.Ping(c)
}
// HasAnyAdmin whether he has any admin in any room.
func (d *Dao) HasAnyAdmin(c context.Context, uid int64) (int64, error) {
noAdmin := int64(0)
hasAdmin := int64(1)
rst, err := d.GetAllByUid(c, uid)
if nil == rst {
return noAdmin, err
}
return hasAdmin, err
}
// GetByUidPage get admins by uid and page.
func (d *Dao) GetByUidPage(c context.Context, uid int64, page int64, pageSize int64) (resp *v1pb.RoomAdminGetByUidResp, err error) {
resp = &v1pb.RoomAdminGetByUidResp{}
resp.Page = &v1pb.RoomAdminGetByUidResp_Page{
Page: page,
PageSize: pageSize,
TotalPage: 1,
TotalCount: 0,
}
rst, err := d.GetAllByUid(c, uid)
//spew.Dump("GetAllByUid", rst, err)
if err != nil {
return
}
if rst == nil {
return
}
sort.Sort(sort.Reverse(model.RoomAdmins(rst)))
//spew.Dump(rst)
resp.Page.TotalCount = int64(len(rst))
resp.Page.PageSize = pageSize
resp.Page.TotalPage = int64(math.Ceil(float64(len(rst)) / float64(resp.Page.PageSize)))
begin := (page - 1) * pageSize
end := page * pageSize
if page*pageSize > int64(len(rst)) {
end = int64(len(rst))
}
roomUid, mids, err := d.getAnchorUidsFromAdmins(c, rst)
// 没获取到房间信息
if err != nil {
return resp, err
}
// 可能从room获取主播信息不全
if int64(len(mids)) < end {
end = int64(len(mids))
}
if begin > end {
begin = end
}
if err != nil {
return resp, err
}
args := &account.ArgMids{Mids: mids[begin:end]}
accData, err := d.acc.Infos3(c, args)
//spew.Dump("d.acc.Infos3", accData, err)
if err != nil {
log.Error("call account.Infos3(%v) error(%v)", args, err)
return resp, err
}
for _, v := range rst[begin:end] {
item := &v1pb.RoomAdminGetByUidResp_Data{
Uid: v.Uid,
Roomid: v.Roomid,
Ctime: v.Ctime.Time().Format("2006-01-02 15:04:05"),
}
if _, ok := roomUid[v.Roomid]; ok {
item.AnchorId = roomUid[v.Roomid]
if _, ok := accData[item.AnchorId]; ok {
item.AnchorCover = accData[item.AnchorId].Face
item.Uname = accData[item.AnchorId].Name
} else {
log.Error("没有这个人的用户信息 uid(%v) data(%v)", item.AnchorId, accData)
}
} else {
log.Error("没有这个人的房间信息 room (%v) data(%v)", v.Roomid, roomUid)
}
resp.Data = append(resp.Data, item)
}
//spew.Dump("resp.Data", resp.Data)
return
}
// GetAllByUid get by uid.
func (d *Dao) GetAllByUid(c context.Context, uid int64) ([]*model.RoomAdmin, error) {
rstMc, err := d.CacheRoomAdminUser(c, uid)
//spew.Dump("HasAnyAdmin1", rstMc, err)
//spew.Dump("lenMc", len(rstMc))
if err != nil {
return nil, err
}
// 空缓存标识
if rstMc != nil {
if rstMc[0].Id == -1 {
return nil, err
}
return rstMc, err
}
rstDb, err := d.GetByUserMysql(c, uid)
if err != nil {
return nil, err
}
if len(rstDb) == 0 {
d.AddCacheNoneUser(c, uid)
return nil, err
}
d.AddCacheRoomAdminUser(c, uid, rstDb)
return rstDb, err
}
// getAnchorUidsFromAdmins .
// 根据批量房管获取对应主播的房间号和UID
func (d *Dao) getAnchorUidsFromAdmins(c context.Context, admins []*model.RoomAdmin) (roomUid map[int64]int64, uids []int64, err error) {
var roomIds []int64
roomUid = make(map[int64]int64)
for _, a := range admins {
roomIds = append(roomIds, a.Roomid)
}
if len(roomIds) == 0 {
return
}
reply, err := dao.RoomAPI.V2Room.GetByIds(c, &v2.RoomGetByIdsReq{Ids: roomIds})
if err != nil {
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error(%v)", roomIds, err)
return roomUid, uids, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", roomIds, err)
return roomUid, uids, err
}
for aRoomId, r := range reply.Data {
roomUid[aRoomId] = r.Uid
uids = append(uids, r.Uid)
}
return
}
// Del delete a roomadmin
func (d *Dao) Del(c context.Context, uid int64, roomId int64) (err error) {
if err = d.DelAllCache(c, uid, roomId); err != nil {
log.Error("DelAllCache(%v) uid (%v) roomid (%v) error(%v)", uid, roomId, err)
return
}
admin, err := d.GetByRoomIdUidMysql(c, uid, roomId)
if len(admin) == 0 {
log.Error("GetByRoomIdUidMysql empty uid(%v) roomId (%v)error(%v)", uid, roomId, err)
return
}
//spew.Dump("GetByRoomIdUidMysql", admin)
if err = d.DelDbAdminMysql(c, admin[0].Id); err != nil {
log.Error("DelCacheRoomAdminUser uid(%v) roomId (%v) error(%v)", uid, roomId, err)
return err
}
return
}
func (d *Dao) getInfoByName(c context.Context, name string) (userInfo *account.Info, err error) {
userInfo = &account.Info{}
infosByName, err := d.acc.InfosByName3(c, &account.ArgNames{
Names: []string{name},
})
if err != nil {
log.Error("d.acc.InfosByName3(%v) error(%v)", name, err)
return userInfo, err
}
log.Info("d.acc.InfosByName3(%v) return (%v)", name, infosByName)
if len(infosByName) != 0 {
for _, info := range infosByName {
return info, err
}
}
return
}
// SearchForAdmin search user list by keyword.
func (d *Dao) SearchForAdmin(c context.Context, keyword string, anchorId int64) (resp []*v1pb.RoomAdminSearchForAdminResp_Data, err error) {
isUid := 0
matchUid, _ := strconv.ParseInt(keyword, 10, 64)
if matchUid != 0 && keyword != "0" {
isUid = 1
}
// get by name
infoMatchName, err := d.getInfoByName(c, keyword)
if err != nil {
return resp, err
}
if nil != infoMatchName && infoMatchName.Mid != 0 {
log.Info("SearchForAdmin infoMatchName keyword (%v) ret (%v)", keyword, infoMatchName)
isAdminName, _ := d.isAdminByUid(c, infoMatchName.Mid, anchorId)
medalData, errMedal := d.getMedalInfoByUids(c, []int64{infoMatchName.Mid})
if errMedal != nil {
return resp, errMedal
}
itemName := &v1pb.RoomAdminSearchForAdminResp_Data{
Uid: infoMatchName.Mid,
IsAdmin: isAdminName,
Uname: infoMatchName.Name,
Face: infoMatchName.Face,
}
if _, ok := medalData[infoMatchName.Mid]; ok {
itemName.MedalName = medalData[infoMatchName.Mid].MedalName
itemName.Level = medalData[infoMatchName.Mid].Level
} else {
log.Info("没有这个人的勋章信息 uid(%v) data(%v)", infoMatchName.Mid, medalData)
}
resp = append(resp, itemName)
}
//spew.Dump("searchForadmin2", resp)
// just name
if 0 == isUid {
return resp, nil
}
// get by uid
infoMatchUid, err := d.getInfoByUid(c, matchUid)
if err != nil {
return resp, err
}
if infoMatchUid == nil {
return resp, nil
}
isAdminUid, _ := d.isAdminByUid(c, matchUid, anchorId)
medalDataUid, err := d.getMedalInfoByUids(c, []int64{infoMatchUid.Mid})
if err != nil {
return resp, err
}
itemUid := &v1pb.RoomAdminSearchForAdminResp_Data{
Uid: infoMatchUid.Mid,
IsAdmin: isAdminUid,
Uname: infoMatchUid.Name,
Face: infoMatchUid.Face,
}
if _, ok := medalDataUid[infoMatchUid.Mid]; ok {
itemUid.MedalName = medalDataUid[infoMatchUid.Mid].MedalName
itemUid.Level = medalDataUid[infoMatchUid.Mid].Level
}
resp = append(resp, itemUid)
//spew.Dump("searchForadmin2", resp)
return
}
func (d *Dao) isAdminByUid(c context.Context, uid int64, anchorId int64) (rst int64, err error) {
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return rst, err
}
if 0 == roomId {
return rst, nil
}
return d.IsAdminByRoomId(c, uid, roomId)
}
func (d *Dao) getInfoByUid(c context.Context, uid int64) (info *account.Info, err error) {
info, err = d.acc.Info3(c, &account.ArgMid{
Mid: uid,
})
if err != nil {
log.Error("d.acc.Info3(%v) error(%v)", uid, err)
return info, err
}
log.Info("d.acc.Info3(%v) return (%v)", uid, info)
return
}
func (d *Dao) getRoomInfoByUid(c context.Context, uid int64) (roomInfo *v1.RoomGetStatusInfoByUidsResp_RoomInfo, err error) {
roomInfo = &v1.RoomGetStatusInfoByUidsResp_RoomInfo{}
reply, err := dao.RoomAPI.V1Room.GetStatusInfoByUids(c, &v1.RoomGetStatusInfoByUidsReq{
Uids: []int64{uid},
ShowHidden: 1,
})
if err != nil {
log.Error("dao.RoomAPI.V1Room.GetStatusInfoByUids (%v) error(%v)", uid, err)
return roomInfo, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", uid, err)
return roomInfo, err
}
if len(reply.Data) == 0 {
return
}
for aUid, aInfo := range reply.Data {
if aUid == uid {
return aInfo, nil
}
}
return
}
func (d *Dao) getMedalInfoByUids(c context.Context, uids []int64) (medalInfo map[int64]*v12.AnchorQueryLiveWearingResp_Medal, err error) {
medalInfo = make(map[int64]*v12.AnchorQueryLiveWearingResp_Medal)
reply, err := dao.FansMedalAPI.V2Anchor.QueryLiveWearing(c, &v12.AnchorQueryLiveWearingReq{
UidList: uids,
})
log.Info("call dao.FansMedalAPI.V2Anchor.QueryLiveWearing (%v) rst (%v)", uids, reply)
if err != nil {
log.Error("dao.FansMedalAPI.V2Anchor.QueryLiveWearing (%v) error(%v)", uids, err)
return medalInfo, err
}
if reply.GetCode() != 0 {
err = ecode.Int(int(reply.GetCode()))
log.Error("dao.RoomAPI.V2Room.GetByIds (%v) error code(%v)", uids, err)
return medalInfo, err
}
if len(reply.Data) == 0 {
return
}
return reply.Data, err
}
// IsAdminByRoomId ...
func (d *Dao) IsAdminByRoomId(c context.Context, uid int64, roomId int64) (rst int64, err error) {
rst = 0
admins, err := d.GetAllByRoomId(c, roomId)
if err != nil {
log.Error("GetAllByRoomId(%v) error(%v)", roomId, err)
return rst, err
}
if len(admins) == 0 {
return rst, nil
}
for _, v := range admins {
if v.Uid == uid {
rst = 1
return rst, nil
}
}
return
}
// GetByAnchorIdPage get by anchor id and page .
func (d *Dao) GetByAnchorIdPage(c context.Context, anchorId int64, page int64, pageSize int64) (resp *v1pb.RoomAdminGetByAnchorResp, err error) {
resp = &v1pb.RoomAdminGetByAnchorResp{}
resp.Page = &v1pb.RoomAdminGetByAnchorResp_Page{
Page: page,
PageSize: pageSize,
TotalPage: 1,
TotalCount: 0,
}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
allAdmins, err := d.GetAllByRoomId(c, roomId)
//spew.Dump("GetAllByUid", allAdmins, err)
if err != nil {
return resp, err
}
if allAdmins == nil {
return resp, nil
}
sort.Sort(sort.Reverse(model.RoomAdmins(allAdmins)))
//spew.Dump(allAdmins)
resp.Page.TotalCount = int64(len(allAdmins))
resp.Page.PageSize = pageSize
resp.Page.TotalPage = int64(math.Ceil(float64(len(allAdmins)) / float64(resp.Page.PageSize)))
begin := (page - 1) * pageSize
end := page * pageSize
if page*pageSize > int64(len(allAdmins)) {
end = int64(len(allAdmins))
}
uids, _ := d.getUidsFromAdmins(c, allAdmins)
if int64(len(uids)) < end {
end = int64(len(uids))
}
if begin > end {
begin = end
}
//spew.Dump("getAnchorUidsFromAdmins", uids, err)
accArgs := &account.ArgMids{Mids: uids[begin:end]}
accData, err := d.acc.Infos3(c, accArgs)
if err != nil {
log.Error("d.acc.Infos3(%v) error(%v)", accArgs, err)
}
medalData, err := d.getMedalInfoByUids(c, uids)
if err != nil {
log.Error("d.getMedalInfoByUids(%v) error(%v)", uids, err)
return resp, err
}
if err != nil {
log.Error("call account.Infos3(%v) error(%v)", accArgs, err)
return resp, err
}
for _, v := range allAdmins[begin:end] {
item := &v1pb.RoomAdminGetByAnchorResp_Data{
Uid: v.Uid,
Ctime: v.Ctime.Time().Format("2006-01-02 15:04:05"),
Roomid: v.Roomid,
}
if _, ok := accData[v.Uid]; ok {
item.Uname = accData[v.Uid].Name
item.Face = accData[v.Uid].Face
} else {
log.Error("没有这个人的用户信息 uid(%v) data(%v)", v.Uid, accData)
}
if _, ok := medalData[v.Uid]; ok {
item.Level = medalData[v.Uid].Level
item.MedalName = medalData[v.Uid].MedalName
} else {
log.Info("没有这个人的勋章信息 uid(%v) data(%v)", v.Uid, medalData)
}
resp.Data = append(resp.Data, item)
}
//spew.Dump("resp.Data", resp.Data)
return
}
// GetAllByRoomId get by uid.
func (d *Dao) GetAllByRoomId(c context.Context, roomId int64) ([]*model.RoomAdmin, error) {
rstMc, err := d.CacheRoomAdminRoom(c, roomId)
//spew.Dump("HasAnyAdmin1", rstMc, err)
//spew.Dump("lenMc", len(rstMc))
if err != nil {
return nil, err
}
// 空缓存标识
if rstMc != nil {
if rstMc[0].Id == -1 {
return nil, err
}
return rstMc, err
}
rstDb, err := d.GetByRoomIdMysql(c, roomId)
if err != nil {
return nil, err
}
if len(rstDb) == 0 {
d.AddCacheNoneRoom(c, roomId)
return nil, err
}
d.AddCacheKeyAnchorRoom(c, roomId, rstDb)
return rstDb, err
}
// getUidsFromAdmins .
// 返回房管列表中的uid
func (d *Dao) getUidsFromAdmins(c context.Context, admins []*model.RoomAdmin) (uids []int64, err error) {
for _, a := range admins {
uids = append(uids, a.Uid)
}
return
}
// DismissAnchor del a admin
func (d *Dao) DismissAnchor(c context.Context, uid int64, anchorId int64) (resp *v1pb.RoomAdminDismissAdminResp, err error) {
resp = &v1pb.RoomAdminDismissAdminResp{}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 0 == isAdmin {
err = ecode.Error(ecode.XUserAddRoomAdminNotAdminError, "该用户已经不是房管啦")
return
}
err = d.Del(c, uid, roomId)
if err != nil {
log.Error("getRoomInfoByUid uid (%v) roomid (%v) error(%v)", uid, roomId, err)
return
}
return
}
// DelAllCache delete cache .
func (d *Dao) DelAllCache(c context.Context, uid int64, roomId int64) (err error) {
if err = d.DelCacheKeyAnchorRoom(c, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return err
}
if err = d.DelCacheRoomAdminUser(c, uid); err != nil {
log.Error("DelCacheRoomAdminUser(%v) error(%v)", uid, err)
return err
}
return
}
// Add add a admin
func (d *Dao) Add(c context.Context, uid int64, anchorId int64) (resp *v1pb.RoomAdminAddResp, err error) {
resp = &v1pb.RoomAdminAddResp{}
roomId, err := d.getRoomIdByUid(c, anchorId)
if err != nil {
return resp, err
}
if 0 == roomId {
return resp, nil
}
allRoomAdmin, err := d.GetAllByRoomId(c, roomId)
//spew.Dump("Add", roomId, allRoomAdmin)
if err != nil {
return resp, err
}
if len(allRoomAdmin) >= maxAdminsNum {
err = ecode.Error(ecode.XUserAddRoomAdminOverLimitError, "最多设置100个房间管理员")
//err = &pb.Error{
// ErrCode: 2,
// ErrMessage: "最多设置100个房间管理员",
//}
return
}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 1 == isAdmin {
err = ecode.Error(ecode.XUserAddRoomAdminIsAdminError, "该用户已经是你的房管啦")
//err = &pb.Error{
// ErrCode: 1,
// ErrMessage: "他已经是房管",
//}
return
}
banArg := &banned.SilentMngIsBlockUserReq{
Uid: uid,
Roomid: roomId,
Type: 1,
}
retBan, err := dao.BannedAPI.V1SilentMng.IsBlockUser(c, banArg)
if err != nil {
log.Error("call dao.BannedAPI.V1SilentMng.IsBlockUser(%v) error(%v)", banArg, err)
return
}
if retBan.Code != 0 || nil == retBan.Data {
log.Error("call dao.BannedAPI.V1SilentMng.IsBlockUser(%v) error return (%v)", banArg, retBan)
}
if retBan.Data.GetIsBlockUser() {
err = ecode.Error(ecode.XUserAddRoomAdminIsSilentError, "他已经被禁言,无法添加房管")
//err = &pb.Error{
// ErrCode: 3,
// ErrMessage: "他已经被禁言,无法添加房管",
//}
return
}
if err = d.DelAllCache(c, uid, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return resp, err
}
if err = d.AddAdminMysql(c, uid, roomId); err != nil {
log.Error("DelCacheKeyAnchorRoom(%v) error(%v)", roomId, err)
return
}
resp.Uid = uid
resp.Roomid = roomId
resp.Userinfo = &v1pb.RoomAdminAddResp_UI{}
resp.Userinfo.Uid = uid
info, _ := d.getInfoByUid(c, uid)
if info != nil {
resp.Userinfo.Uname = info.Name
}
d.adminChange(c, uid, roomId)
return
}
func (d *Dao) getRoomIdByUid(c context.Context, anchorId int64) (roomId int64, err error) {
roomInfo, err := d.getRoomInfoByUid(c, anchorId)
if err != nil {
log.Error("getRoomInfoByUid (%v) error(%v)", anchorId, err)
return roomId, err
}
if roomInfo == nil {
return roomId, nil
}
return roomInfo.RoomId, nil
}
// adminChange send broadcast
func (d *Dao) adminChange(c context.Context, uid int64, roomId int64) (err error) {
roomInfo, err := dao.RoomAPI.V2Room.GetByIds(c, &v2.RoomGetByIdsReq{
Ids: []int64{roomId},
NeedUinfo: 1,
})
if err != nil {
log.Error("dao.RoomAPI.V2Room.GetByIds(%v) error(%v)", roomId, err)
return
}
if roomInfo.Code != 0 || 0 == len(roomInfo.Data) {
log.Error("dao.RoomAPI.V1Room.GetInfoById(%v) error code (%v) data (%v)", roomId, roomInfo.Code, roomInfo.Data)
return
}
postJson := make(map[string]interface{})
postJson["cmd"] = "room_admin_entrance"
postJson["uid"] = uid
postJson["msg"] = "系统提示:你已被主播设为房管"
if err = d.sendBroadcastRoom(roomId, postJson); err != nil {
return err
}
admins, err := d.GetAllByRoomId(c, roomId)
if err != nil {
return err
}
var adminUids []int64
// adminUids := make([]int64, 100)
for _, v := range admins {
adminUids = append(adminUids, v.Uid)
}
postJson2 := make(map[string]interface{})
postJson2["cmd"] = "ROOM_ADMINS"
postJson2["uids"] = adminUids
if err = d.sendBroadcastRoom(roomId, postJson2); err != nil {
return err
}
return
}
// sendBroadcastRoom .
func (d *Dao) sendBroadcastRoom(roomid int64, postJson map[string]interface{}) (err error) {
log.Info("send reward broadcast begin:%d", roomid)
var endPoint = fmt.Sprintf("http://live-dm.bilibili.co/dm/1/push?cid=%d&ensure=1", roomid)
bytesData, err := json.Marshal(postJson)
if err != nil {
log.Error("json.Marshal(%v) error(%v)", postJson, err)
return
}
req, err := http.NewRequest("POST", endPoint, bytes.NewReader(bytesData))
req.Header.Add("Content-Type", "application/json")
if err != nil {
log.Error("http.NewRequest(%v) url(%v) error(%v)", postJson, endPoint, err)
return
}
client := http.Client{
Timeout: time.Second,
}
// use httpClient to send request
response, err := client.Do(req)
if err != nil {
log.Error("sending request to API endpoint(%v) error(%v)", req, err)
return
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error("parse resp body(%v) error(%v)", body, err)
}
log.Info("send reward broadcast end:%d", roomid)
return
}
// IsAdmin return whether a user is admin.
func (d *Dao) IsAdmin(c context.Context, uid int64, anchorId int64, roomId int64) (resp *v1pb.RoomAdminIsAdminResp, err error) {
resp = &v1pb.RoomAdminIsAdminResp{}
isAdmin, err := d.IsAdminByRoomId(c, uid, roomId)
if err != nil {
log.Error("IsAdminByRoomId uid(%v) roomid (%v) error(%v)", uid, roomId, err)
return resp, err
}
if 0 == isAdmin {
if uid != anchorId {
err = ecode.Error(120014, "Ta不是该主播的房管")
//err = ecode.Error{
// ErrCode: 120014, // 接口迁移, code 为老业务error code
// ErrMessage: "Ta不是该主播的房管",
//}
return
}
}
userInfo, err := d.getInfoByUid(c, uid)
if err != nil {
return resp, err
}
if userInfo != nil {
resp.Userinfo = &v1pb.RoomAdminIsAdminResp_UI{}
resp.Roomid = roomId
resp.Uid = uid
resp.Userinfo.Uid = uid
resp.Userinfo.Uname = userInfo.Name
return
}
return
}

View File

@@ -0,0 +1,34 @@
package roomAdmin
import (
"flag"
"go-common/app/service/live/xuser/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "live.xuser")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
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.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,156 @@
// Code generated by $GOPATH/src/go-common/app/tool/cache/mc. DO NOT EDIT.
/*
Package roomAdmin is a generated mc cache package.
It is generated from:
type _mc interface {
// 获取主播的房管列表
// mc: -key=KeyRoom
CacheRoomAdminRoom(c context.Context, anchor int64) ([]*model.RoomAdmin, error)
// 获取用户的房管列表
// mc: -key=KeyUser
CacheRoomAdminUser(c context.Context, user int64) ([]*model.RoomAdmin, error)
// mc: -key=KeyRoom -expire=d.RoomAdminExpire -encode=json|gzip
AddCacheKeyAnchorRoom(c context.Context, anchor int64, value []*model.RoomAdmin) error
// mc: -key=KeyUser -expire=d.RoomAdminExpire -encode=gob
AddCacheRoomAdminUser(c context.Context, user int64, value []*model.RoomAdmin) error
// mc: -key=KeyRoom
DelCacheKeyAnchorRoom(c context.Context, anchor int64) error
// mc: -key=KeyUser
DelCacheRoomAdminUser(c context.Context, user int64) error
}
*/
package roomAdmin
import (
"context"
"fmt"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
)
var _ _mc
// CacheRoomAdminRoom 获取主播的房管列表
func (d *Dao) CacheRoomAdminRoom(c context.Context, id int64) (res []*model.RoomAdmin, err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
reply, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:CacheRoomAdminRoom")
log.Errorv(c, log.KV("CacheRoomAdminRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
res = []*model.RoomAdmin{}
err = conn.Scan(reply, &res)
if err != nil {
prom.BusinessErrCount.Incr("mc:CacheRoomAdminRoom")
log.Errorv(c, log.KV("CacheRoomAdminRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// CacheRoomAdminUser 获取用户的房管列表
func (d *Dao) CacheRoomAdminUser(c context.Context, id int64) (res []*model.RoomAdmin, err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
reply, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:CacheRoomAdminUser")
log.Errorv(c, log.KV("CacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
res = []*model.RoomAdmin{}
err = conn.Scan(reply, &res)
if err != nil {
prom.BusinessErrCount.Incr("mc:CacheRoomAdminUser")
log.Errorv(c, log.KV("CacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheKeyAnchorRoom Set data to mc
func (d *Dao) AddCacheKeyAnchorRoom(c context.Context, id int64, val []*model.RoomAdmin) (err error) {
if len(val) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
item := &memcache.Item{Key: key, Object: val, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheKeyAnchorRoom")
log.Errorv(c, log.KV("AddCacheKeyAnchorRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheRoomAdminUser Set data to mc
func (d *Dao) AddCacheRoomAdminUser(c context.Context, id int64, val []*model.RoomAdmin) (err error) {
if len(val) == 0 {
return
}
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
item := &memcache.Item{Key: key, Object: val, Expiration: d.RoomAdminExpire, Flags: memcache.FlagGOB}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminUser")
log.Errorv(c, log.KV("AddCacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// DelCacheKeyAnchorRoom delete data from mc
func (d *Dao) DelCacheKeyAnchorRoom(c context.Context, id int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(id)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheKeyAnchorRoom")
log.Errorv(c, log.KV("DelCacheKeyAnchorRoom", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// DelCacheRoomAdminUser delete data from mc
func (d *Dao) DelCacheRoomAdminUser(c context.Context, id int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(id)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
prom.BusinessErrCount.Incr("mc:DelCacheRoomAdminUser")
log.Errorv(c, log.KV("DelCacheRoomAdminUser", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}

View File

@@ -0,0 +1,103 @@
package roomAdmin
import (
"context"
"go-common/app/service/live/xuser/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminCacheRoomAdminRoom(t *testing.T) {
convey.Convey("CacheRoomAdminRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.CacheRoomAdminRoom(c, id)
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 TestRoomAdminCacheRoomAdminUser(t *testing.T) {
convey.Convey("CacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
res, err := d.CacheRoomAdminUser(c, id)
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 TestRoomAdminAddCacheKeyAnchorRoom(t *testing.T) {
convey.Convey("AddCacheKeyAnchorRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
val = []*model.RoomAdmin{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheKeyAnchorRoom(c, id, val)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddCacheRoomAdminUser(t *testing.T) {
convey.Convey("AddCacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
val = []*model.RoomAdmin{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheRoomAdminUser(c, id, val)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminDelCacheKeyAnchorRoom(t *testing.T) {
convey.Convey("DelCacheKeyAnchorRoom", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelCacheKeyAnchorRoom(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminDelCacheRoomAdminUser(t *testing.T) {
convey.Convey("DelCacheRoomAdminUser", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelCacheRoomAdminUser(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,105 @@
package roomAdmin
import (
"context"
"fmt"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/memcache"
"go-common/library/log"
"go-common/library/stat/prom"
)
// AddCacheNoneUser write an flag in cache represents empty
func (d *Dao) AddCacheNoneUser(c context.Context, uid int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyUser(uid)
var roomAdmins []model.RoomAdmin
roomAdmins = append(roomAdmins, model.RoomAdmin{
Id: -1,
})
item := &memcache.Item{Key: key, Object: roomAdmins, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminAnchor")
log.Errorv(c, log.KV("AddCacheRoomAdminAnchor", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
// AddCacheNoneRoom write an flag in cache represents empty
func (d *Dao) AddCacheNoneRoom(c context.Context, uid int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
key := KeyRoom(uid)
var roomAdmins []model.RoomAdmin
roomAdmins = append(roomAdmins, model.RoomAdmin{
Id: -1,
})
item := &memcache.Item{Key: key, Object: roomAdmins, Expiration: d.RoomAdminExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
if err = conn.Set(item); err != nil {
prom.BusinessErrCount.Incr("mc:AddCacheRoomAdminAnchor")
log.Errorv(c, log.KV("AddCacheRoomAdminAnchor", fmt.Sprintf("%+v", err)), log.KV("key", key))
return
}
return
}
//
//// GetByUserMC .
//func (d *Dao) GetByUserMC(c context.Context, uid int64) (rst []*model.RoomAdmin, hit int64, err error) {
// hit = 0
// key := d.getUserKey(uid)
// conn := d.mc.Get(c)
// defer conn.Close()
//
// reply, err := conn.Get(key)
// //spew.Dump("GetByUserMC1", reply, err)
//
// if err != nil {
// if err == memcache.ErrNotFound {
// err = nil
// return
// }
// log.Error("GetByUserMC get (%v) error(%v)", key, err)
// return
// }
//
// hit = 1
//
// if reply.Object == nil {
// return nil, hit, err
// }
//
// if err = conn.Scan(reply, rst); err != nil {
// log.Error("GetByUserMC Scan (%+v) error(%v)", reply, err)
// }
//
// //spew.Dump("GetByUserMC2", rst, err)
// return
//}
//
//// SetByUserMc .
//func (d *Dao) SetByUserMc(c context.Context, uid int64, rst []*model.RoomAdmin) (err error) {
// key := d.getUserKey(uid)
// conn := d.mc.Get(c)
// defer conn.Close()
//
// err = conn.Set(&memcache.Item{
// Key: key,
// Object: rst,
// Flags: memcache.FlagJSON,
// Expiration: mcExpire,
// })
//
// //spew.Dump("SetByUserMc", err)
// if err != nil {
// log.Error("SetByUserMc set(%v) value (%+v) error (%v)", key, rst, err)
// return
// }
// return
//}

View File

@@ -0,0 +1,38 @@
package roomAdmin
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminAddCacheNoneUser(t *testing.T) {
convey.Convey("AddCacheNoneUser", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheNoneUser(c, uid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddCacheNoneRoom(t *testing.T) {
convey.Convey("AddCacheNoneRoom", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddCacheNoneRoom(c, uid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,81 @@
package roomAdmin
import (
"context"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
)
// GetByUserMysql get admins by user .
func (dao *Dao) GetByUserMysql(c context.Context, uid int64) (admins []*model.RoomAdmin, err error) {
err = dao.orm.Model(&model.RoomAdmin{}).Find(&admins, "uid=?", uid).Order("ctime DESC").Error
if err != nil {
log.Error("GetByUserMysql (%v) error(%v)", uid, err)
return nil, err
}
return
}
// GetByRoomIdMysql get admins by roomId.
func (dao *Dao) GetByRoomIdMysql(c context.Context, roomId int64) (admins []*model.RoomAdmin, err error) {
err = dao.orm.Table("ap_room_admin").Model(&model.RoomAdmin{}).Find(&admins, "roomid=?", roomId).Order("ctime DESC").Error
if err != nil {
log.Error("GetByUserMysql (%v) error(%v)", roomId, err)
return nil, err
}
return
}
// DelDbAdminMysql delete a admin .
func (dao *Dao) DelDbAdminMysql(c context.Context, id int64) (err error) {
param := &model.RoomAdmin{
Id: id,
}
//spew.Dump("deldbAdmin", param);
err = dao.orm.Table("ap_room_admin").Model(&model.RoomAdmin{}).Delete(param).Error
if err != nil {
log.Error("DelDbAdminMysql (%v) error(%v)", id, err)
return err
}
return
}
// AddAdminMysql delete a admin .
func (dao *Dao) AddAdminMysql(c context.Context, uid int64, roomId int64) (err error) {
param := &model.RoomAdmin{
Uid: uid,
Roomid: roomId,
}
//spew.Dump("AddAdminMysql", param)
err = dao.orm.Model(&model.RoomAdmin{}).Save(param).Error
if err != nil {
log.Error("AddAdminMysql uid (%v) roomid (%v)error(%v)", uid, roomId, err)
return err
}
return
}
// GetByRoomIdUidMysql delete a admin .
func (dao *Dao) GetByRoomIdUidMysql(c context.Context, uid int64, roomId int64) (resp []*model.RoomAdmin, err error) {
//param := &model.RoomAdmin{
// Uid: uid,
// Roomid: roomId,
//}
err = dao.orm.Model(&model.RoomAdmin{}).Where("uid=? AND roomid =?", uid, roomId).Find(&resp).Error
//spew.Dump("GetByRoomIdUidMysql", resp)
if err != nil {
log.Error("AddAdminMysql uid (%v) roomid (%v)error(%v)", uid, roomId, err)
return resp, err
}
return
}

View File

@@ -0,0 +1,88 @@
package roomAdmin
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestRoomAdminGetByUserMysql(t *testing.T) {
convey.Convey("GetByUserMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
admins, err := d.GetByUserMysql(c, uid)
ctx.Convey("Then err should be nil.admins should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(admins, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminGetByRoomIdMysql(t *testing.T) {
convey.Convey("GetByRoomIdMysql", t, func(ctx convey.C) {
var (
c = context.Background()
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
admins, err := d.GetByRoomIdMysql(c, roomId)
ctx.Convey("Then err should be nil.admins should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(admins, convey.ShouldNotBeNil)
})
})
})
}
func TestRoomAdminDelDbAdmin(t *testing.T) {
convey.Convey("DelDbAdminMysql", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.DelDbAdminMysql(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminAddAdminMysql(t *testing.T) {
convey.Convey("AddAdminMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.AddAdminMysql(c, uid, roomId)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestRoomAdminGetByRoomIdUidDb(t *testing.T) {
convey.Convey("GetByRoomIdUidMysql", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
roomId = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
resp, err := d.GetByRoomIdUidMysql(c, uid, roomId)
ctx.Convey("Then err should be nil.resp should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resp, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1 @@
package roomAdmin

View File

@@ -0,0 +1 @@
package roomAdmin

View File

@@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"cache_test.go",
"dao_test.go",
"mysql_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.go",
"mysql.go",
],
importpath = "go-common/app/service/live/xuser/dao/vip",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/xuser/conf:go_default_library",
"//app/service/live/xuser/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,168 @@
package vip
import (
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/redis"
"go-common/library/log"
"go-common/library/net/metadata"
"time"
)
// redis cache
const (
_userInfoRedisKey = "us:infoo_v2:%d" // 用户缓存key prefix
_vipFieldName = "vip" // v3 vip attr field
_levelFieldName = "level" // v2 level attr field, Todo: remove level attr
_userExpired = 86400 // user cache expire time
)
type vipCache struct {
Vip interface{} `json:"vip"`
VipTime string `json:"vip_time"`
Svip interface{} `json:"svip"`
SvipTime string `json:"svip_time"`
}
// GetVipFromCache get user vip info from cache
func (d *Dao) GetVipFromCache(ctx context.Context, uid int64) (info *model.VipInfo, err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
reply, err := redis.String(conn.Do("HGET", getUserCacheKey(uid), _vipFieldName))
if err != nil {
if err == redis.ErrNil {
// key or field not exists, return nil, nil, back to db
log.Info("[dao.vip.cache|GetVipFromCache] cache key or field not exists err(%v), uid(%d)", err, uid)
return nil, nil
}
log.Error("[dao.vip.cache|GetVipFromCache] hget error(%v), uid(%d)", err, uid)
return
}
if reply == "" {
return nil, nil
}
// ===== begin eat others' dog food =====
// 1.兼容缓存中vip/svip可能是int or string的问题
rawInfo := &vipCache{}
if err = json.Unmarshal([]byte(reply), rawInfo); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] json.Unmarshal rawInfo error(%v), uid(%d), reply(%s)",
err, uid, reply)
// parse cache json error, return nil, nil, back to db and restore cache
return nil, nil
}
if info, err = d.formatVipCache(rawInfo); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] format rawInfo error(%v), uid(%d), reply(%s)", err, uid, reply)
return nil, nil
}
// 2.注意!!! cache里的vip_time/svip_time不一定正确可能含有已经过期的time
currentTime := time.Now().Unix()
// vip time
if info.Vip, err = d.checkVipTime(info.VipTime, info.Vip, currentTime); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] check vip time error(%v), uid(%d), info(%v), reply(%s)",
err, uid, info, reply)
return nil, nil
}
if info.Svip, err = d.checkVipTime(info.SvipTime, info.Svip, currentTime); err != nil {
log.Error("[dao.vip.cache|GetVipFromCache] check svip time error(%v), uid(%d), info(%v), reply(%s)",
err, uid, info, reply)
return nil, nil
}
// ===== end =====
return
}
// formatVipCache 转换vip/svip的格式
func (d *Dao) formatVipCache(info *vipCache) (v *model.VipInfo, err error) {
v = &model.VipInfo{
VipTime: info.VipTime,
SvipTime: info.SvipTime,
}
if v.Vip, err = toInt(info.Vip); err != nil {
return
}
if v.Svip, err = toInt(info.Svip); err != nil {
return
}
// format info struct
v = d.initInfo(v)
return
}
// checkVipTime 检查缓存中vip_time/svip_time是否过期
func (d *Dao) checkVipTime(t string, f int, compare int64) (int, error) {
if t == model.TimeEmpty {
if f != 0 {
return 0, errors.New("empty time with not zero flag.")
}
} else {
vt, err := time.Parse(model.TimeNano, t)
if err != nil {
return 0, errors.New("time parse error.")
}
if vt.Unix() <= compare {
return 0, nil
}
}
return f, nil
}
// SetVipCache set vip to cache
func (d *Dao) SetVipCache(ctx context.Context, uid int64, info *model.VipInfo) (err error) {
var vipJson []byte
conn := d.redis.Get(ctx)
key := getUserCacheKey(uid)
defer conn.Close()
// format info struct
info = d.initInfo(info)
// format info json string
vipJson, err = json.Marshal(info)
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] json.Marshal error(%v), uid(%d), info(%v)", err, uid, info)
// if marshal error, clear cache
goto CLEAR
}
_, err = conn.Do("HSET", key, _vipFieldName, string(vipJson))
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] HSET error(%v), uid(%d), info(%v)", err, uid, info)
// if hset error, clear cache
goto CLEAR
}
_, err = conn.Do("EXPIRE", key, _userExpired)
if err != nil {
log.Error("[dao.vip.cache|SetVipCache] EXPIRE error(%v), uid(%d), info(%v)", err, uid, info)
// if set expire error, clear cache
goto CLEAR
}
return
CLEAR:
log.Error("[dao.vip.cache|SetVipCache] set error, aysnc clear, uid(%d), info(%v)", uid, info)
go d.ClearCache(metadata.WithContext(ctx), uid)
return
}
// ClearCache clear user's vip and level field cache
// Todo: remove level attr
func (d *Dao) ClearCache(ctx context.Context, uid int64) (err error) {
conn := d.redis.Get(ctx)
defer conn.Close()
key := getUserCacheKey(uid)
_, err = conn.Do("HDEL", key, _vipFieldName, _levelFieldName)
if err != nil {
err = errors.Wrapf(err, "conn.Do(HDEL, %s, %s, %s)", key, _vipFieldName, _levelFieldName)
log.Error("[dao.vip.cache|ClearCache] hdel uid(%d) vip and level attr err(%v)", uid, err)
}
return
}
func getUserCacheKey(uid int64) string {
return fmt.Sprintf(_userInfoRedisKey, uid)
}

View File

@@ -0,0 +1,169 @@
package vip
import (
"context"
"fmt"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
"testing"
"time"
)
func TestDao_GetVipFromCache(t *testing.T) {
initd()
Convey("test get vip cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
info *model.VipInfo
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_GetVipFromCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// should get nil info and err
Convey("should get nil info and err", func() {
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// set empty data
Convey("set empty data", func() {
conn.Do("HSET", key, _vipFieldName, "")
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// set not a json
Convey("set not a json", func() {
conn.Do("HSET", key, _vipFieldName, "test")
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldBeNil)
So(err, ShouldBeNil)
})
// test vip/svip string format
Convey("test vip/svip string format", func() {
vipTime := time.Now().Add(time.Hour * 12).Format(model.TimeNano)
svipTime := time.Now().AddDate(0, -1, 0).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":"1","vip_time":"%s","svip":0,"svip_time":"%s"}`, vipTime, svipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info.Vip, ShouldEqual, 1)
So(info.Svip, ShouldEqual, 0)
So(err, ShouldBeNil)
})
// set valid data
Convey("set valid data", func() {
vipTime := time.Now().Add(time.Hour * 12).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":1,"vip_time":"%s","svip":0,"svip_time":"0000-00-00 00:00:00"}`, vipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 1)
So(info.VipTime, ShouldEqual, vipTime)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, model.TimeEmpty)
So(err, ShouldBeNil)
})
// expired vip time
Convey("set valid but expired vip time", func() {
vip := 1
vipTime := time.Now().AddDate(0, 0, -1).Format(model.TimeNano)
svip := 1
svipTime := time.Now().AddDate(0, -1, -1).Format(model.TimeNano)
conn.Do("HSET", key, _vipFieldName, fmt.Sprintf(`{"vip":%d,"vip_time":"%s","svip":%d,"svip_time":"%s"}`, vip, vipTime, svip, svipTime))
info, err = d.GetVipFromCache(ctx, u.Uid)
log.Info("expired vip time, info(%+v)", info)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, vipTime)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, svipTime)
So(err, ShouldBeNil)
})
}))
}
func TestDao_SetVipCache(t *testing.T) {
initd()
Convey("test set vip cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
info *model.VipInfo
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_GetVipFromCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// nil info
Convey("nil info", func() {
err = d.SetVipCache(ctx, u.Uid, nil)
So(err, ShouldBeNil)
info, err = d.GetVipFromCache(ctx, u.Uid)
log.Info("TestDao_SetVipCache get info1(%v), err(%v)", info, err)
So(err, ShouldBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, model.TimeEmpty)
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, model.TimeEmpty)
})
// set valid info
Convey("set valid info", func() {
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 6).Format(model.TimeNano),
}
err = d.SetVipCache(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err := d.GetVipFromCache(ctx, u.Uid)
log.Info("TestDao_SetVipCache get info2(%v), err(%v)", info, err)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, info.Vip)
So(info2.VipTime, ShouldEqual, info.VipTime)
So(info2.Svip, ShouldEqual, info.Svip)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
})
}))
}
func TestDao_ClearCache(t *testing.T) {
initd()
Convey("test clear cache", t, testWithTestUser(func(u *TestUser) {
var (
ctx = context.Background()
err error
conn = d.redis.Get(ctx)
key = getUserCacheKey(u.Uid)
)
log.Info("TestDao_ClearCache uid(%d), key(%s)", u.Uid, key)
// delete key at begin
conn.Do("DEL", key)
// del already deleted key
Convey("del already deleted key", func() {
err = d.ClearCache(ctx, u.Uid)
So(err, ShouldBeNil)
})
// set valid info
Convey("set valid info", func() {
err = d.SetVipCache(ctx, u.Uid, nil)
So(err, ShouldBeNil)
err = d.ClearCache(ctx, u.Uid)
So(err, ShouldBeNil)
})
}))
}

View File

@@ -0,0 +1,84 @@
package vip
import (
"context"
"fmt"
"go-common/app/service/live/xuser/conf"
"go-common/app/service/live/xuser/model"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"strconv"
)
// Dao vip dao
type Dao struct {
c *conf.Config
db *xsql.DB
redis *redis.Pool
}
// New new vip dao
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
db: xsql.NewMySQL(c.LiveUserMysql),
redis: redis.NewPool(c.VipRedis),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.db.Close()
d.redis.Close()
}
// initInfo init info struct
func (d *Dao) initInfo(info *model.VipInfo) *model.VipInfo {
if info == nil {
info = &model.VipInfo{Vip: 0, VipTime: model.TimeEmpty, Svip: 0, SvipTime: model.TimeEmpty}
} else {
if info.VipTime == "" {
info.VipTime = model.TimeEmpty
}
if info.SvipTime == "" {
info.SvipTime = model.TimeEmpty
}
}
return info
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
// TODO: add mc,redis... if you use
return nil
}
// toInt try trans interface input to int
func toInt(in interface{}) (int, error) {
switch in.(type) {
case int:
return in.(int), nil
case int32:
return int(in.(int32)), nil
case int64:
return int(in.(int64)), nil
case float32:
return int(in.(float32)), nil
case float64:
return int(in.(float64)), nil
case string:
i, err := strconv.Atoi(in.(string))
if err != nil {
return 0, err
}
return i, nil
case []byte:
i, err := strconv.Atoi(string(in.([]byte)))
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("invalid input(%v)", in)
}

View File

@@ -0,0 +1,70 @@
package vip
import (
"context"
"flag"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/conf"
"math/rand"
"path/filepath"
"testing"
)
// vip dao and conf
var (
d *Dao
)
type TestUser struct {
Uid int64
}
// initd init vip dao
func initd() {
dir, _ := filepath.Abs("../../cmd/test.toml")
flag.Set("conf", dir)
flag.Set("deploy_env", "uat")
conf.Init()
d = New(conf.Conf)
}
func initTestUser() *TestUser {
return &TestUser{
Uid: int64(rand.Int31()),
}
}
func (t *TestUser) Reset() {
d.ClearCache(context.Background(), t.Uid)
d.deleteVip(context.Background(), t.Uid)
}
func testWithTestUser(f func(u *TestUser)) func() {
u := initTestUser()
return func() {
f(u)
u.Reset()
}
}
func TestToInt(t *testing.T) {
var (
err error
out int
)
Convey("test toInt", t, func() {
out, err = toInt(1)
So(out, ShouldEqual, 1)
So(err, ShouldBeNil)
})
Convey("test toInt", t, func() {
out, err = toInt("123")
So(out, ShouldEqual, 123)
So(err, ShouldBeNil)
})
Convey("test toInt", t, func() {
out, err = toInt("test")
So(out, ShouldEqual, 0)
So(err, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,204 @@
package vip
import (
"context"
"crypto/md5"
"database/sql"
"encoding/hex"
"fmt"
"strconv"
"time"
"go-common/app/service/live/xuser/model"
"go-common/library/log"
xtime "go-common/library/time"
"github.com/pkg/errors"
xsql "go-common/library/database/sql"
)
const (
_userVipRecordPrefix = "user_vip_record_%d"
_userVipRecordCount = 10
_userLevelPrefix = "user_"
)
var (
errUpdateVipTimeInvalid = errors.New("update vip but vip_time invalid")
)
var (
// get vip info from user_x table
_getVipInfo = "SELECT `vip`,`vip_time`,`svip`,`svip_time` FROM `%s` WHERE uid=?;"
// insert into user_vip_record_n
_insertUserVipRecord = "INSERT INTO `%s` (`uid`,`vip_type`,`vip_num`,`order_id`,`platform`,`source`) VALUES (?,?,?,?,?,?);"
// update user_vip_record_n before_time & status after update vip success
_updateUserVipRecordStatus = "UPDATE `%s` SET `before_vip`=?,`before_vip_time`=?,`before_svip`=?,`before_svip_time`=?,`status`=? WHERE `id`=?;"
// update user_x vip info
_updateAllVip = "UPDATE `%s` SET `vip`=?,`vip_time`=?,`svip`=?,`svip_time`=? WHERE uid=?;"
_updateVip = "UPDATE `%s` SET `vip`=?,`vip_time`=? WHERE uid=?;"
// insert vip
_insertVip = "INSERT INTO `%s` (`uid`,`vip`,`vip_time`,`svip`,`svip_time`) VALUES (?,?,?,?,?);"
// delete vip
_deleteVip = "DELETE FROM `%s` WHERE `uid`=? LIMIT 1;"
// insert ap_vip_record
_insertApVipRecord = "INSERT INTO `ap_vip_record`(`uid`,`type`,`vip_time`,`platform`) VALUES (?,?,?,?);"
)
// GetVipFromDB get vip info by uid
// return error maybe sql no row or just scan error, how to handle decided by upper business
func (d *Dao) GetVipFromDB(ctx context.Context, uid int64) (info *model.VipInfo, err error) {
var (
vipTime, sVipTime xtime.Time
currentTime = xtime.Time(time.Now().Unix())
)
row := d.db.QueryRow(ctx, fmt.Sprintf(_getVipInfo, getUserLevelTable(uid)), uid)
info = &model.VipInfo{}
if err = row.Scan(&info.Vip, &vipTime, &info.Svip, &sVipTime); err != nil {
log.Error("[dao.vip.mysql|GetVipFromDB] row scan error(%v), uid(%d)", err, uid)
// no rows in user_x table, async insert one, don't return error
if err == xsql.ErrNoRows {
go d.createVip(context.TODO(), uid, info)
err = nil
return
}
return
}
// format info vip time
if vipTime <= 0 {
info.VipTime = model.TimeEmpty
} else {
info.VipTime = vipTime.Time().Format(model.TimeNano)
}
if sVipTime <= 0 {
info.SvipTime = model.TimeEmpty
} else {
info.SvipTime = sVipTime.Time().Format(model.TimeNano)
}
// format vip & svip
// 注意!!! db里的数据不一定正确可能含有已经过期的time
if vipTime <= currentTime {
info.Vip = 0
}
if sVipTime <= currentTime {
info.Svip = 0
}
return
}
// AddVip update user_n vip fields, add vip/svip time
// weather add vip or svip, vipTime should not be empty
func (d *Dao) AddVip(ctx context.Context, uid int64, vipTime, sVipTime xtime.Time) (row int64, err error) {
var (
vt, st string
res sql.Result
updateType string
currentTime = xtime.Time(time.Now().Unix())
)
if vipTime <= currentTime {
return 0, errUpdateVipTimeInvalid
}
vt = vipTime.Time().Format(model.TimeNano)
if sVipTime > currentTime {
// update vip and svip
st = sVipTime.Time().Format(model.TimeNano)
updateType = "all"
res, err = d.db.Exec(ctx, fmt.Sprintf(_updateAllVip, getUserLevelTable(uid)), 1, vt, 1, st, uid)
} else {
// update vip only
updateType = "vip"
res, err = d.db.Exec(ctx, fmt.Sprintf(_updateVip, getUserLevelTable(uid)), 1, vt, uid)
}
if err != nil {
log.Error("[dao.vip.mysql|AddVip] update vip error(%v), type(%s), uid(%d), vip(%s), svip(%s)",
err, updateType, uid, vt, st)
return
}
row, _ = res.RowsAffected()
return
}
// createVip create user_n vip row, for internal usage only. Do not use in business!
func (d *Dao) createVip(ctx context.Context, uid int64, info *model.VipInfo) (err error) {
info = d.initInfo(info)
log.Info("[dao.vip.mysql|createVip] create user_n row, uid(%d), info(%v)", uid, info)
_, err = d.db.Exec(ctx, fmt.Sprintf(_insertVip, getUserLevelTable(uid)),
uid, info.Vip, info.VipTime, info.Svip, info.SvipTime)
if err != nil {
log.Error("[dao.vip.mysql|createVip] create error(%v), uid(%d), info(%v)", err, uid, info)
}
return
}
// deleteVip delete user_n vip row, for internal usage only. Do not use in business!
func (d *Dao) deleteVip(ctx context.Context, uid int64) (err error) {
log.Info("[dao.vip.mysql|deleteVip] delete user_n row, uid(%d)", uid)
_, err = d.db.Exec(ctx, fmt.Sprintf(_deleteVip, getUserLevelTable(uid)), uid)
if err != nil {
log.Error("[dao.vip.mysql|deleteVip] delete error(%v), uid(%d)", err, uid)
}
return
}
// CreateVipRecord create user vip record if not exists
// return error maybe unique key exists err or other db error, upper business should notice
// unique key is (uid,order_id)
func (d *Dao) CreateVipRecord(ctx context.Context, req *model.VipBuy) (recordID int64, err error) {
res, err := d.db.Exec(ctx, fmt.Sprintf(_insertUserVipRecord, getUserVipRecordTable(req.Uid)),
req.Uid, req.GoodID, req.GoodNum, req.OrderID, req.Platform, req.Source)
if err != nil {
log.Error("[dao.vip.mysql|CreateUserVipRecord] create user vip record error(%v), req(%v)", err, req)
return
}
if recordID, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
log.Error("[dao.vip.mysql|CreateUserVipRecord] get last insert id error(%v), req(%v)", err, req)
}
return
}
// UpdateVipRecord update user vip record after buy success
func (d *Dao) UpdateVipRecord(ctx context.Context, recordID, uid int64, info *model.VipInfo) (err error) {
execSql := fmt.Sprintf(_updateUserVipRecordStatus, getUserVipRecordTable(uid))
_, err = d.db.Exec(ctx, execSql, info.Vip, info.VipTime, info.Svip, info.SvipTime, model.BuyStatusSuccess, recordID)
if err != nil {
log.Error("[dao.vip.mysql|UpdateVipRecordLater] update error(%v), record id(%d), uid(%d), info(%v)",
err, recordID, uid, info)
}
return
}
// CreateApVipRecord create ap_vip_record
func (d *Dao) CreateApVipRecord(ctx context.Context, record *model.VipRecord) (row int64, err error) {
res, err := d.db.Exec(ctx, _insertApVipRecord, record.Uid, record.VipType, record.AfterVipTime, record.Platform)
if err != nil {
log.Error("[dao.vip.mysql|CreateApVipRecord] insert ap_vip_record error(%v), record(%v)", err, record)
return
}
row, _ = res.RowsAffected()
return
}
// getUserLevelTable get user_x table by uid
func getUserLevelTable(uid int64) string {
uidStr := strconv.FormatInt(uid, 10)
md5Ctx := md5.New()
md5Ctx.Write([]byte(uidStr))
cipher := md5Ctx.Sum(nil)
return _userLevelPrefix + hex.EncodeToString(cipher)[0:1]
}
// getUserVipRecordTable get user_vip_record_x table by uid
func getUserVipRecordTable(uid int64) string {
return fmt.Sprintf(_userVipRecordPrefix, uid%_userVipRecordCount)
}

View File

@@ -0,0 +1,150 @@
package vip
import (
"context"
"fmt"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/service/live/xuser/model"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
"math/rand"
"testing"
"time"
)
func Test_getUserLevelTable(t *testing.T) {
initd()
Convey("test get user_x table name by uid", t, func() {
var uid = int64(123)
table := getUserLevelTable(uid)
So(table, ShouldEqual, "user_2")
})
}
func Test_getUserVipRecordTable(t *testing.T) {
initd()
Convey("test get user_vip_record_x table name by uid", t, func() {
var uid = rand.Int63()
log.Info("Test_getUserVipRecordTable uid(%d)", uid)
table := getUserVipRecordTable(uid)
t := fmt.Sprintf(_userVipRecordPrefix, uid%_userVipRecordCount)
So(table, ShouldEqual, t)
})
}
func TestDao_GetVipFromDB(t *testing.T) {
initd()
Convey("test get vip from db", t, testWithTestUser(func(u *TestUser) {
log.Info("TestDao_GetVipFromDB uid(%d), table(%s)", u.Uid, getUserLevelTable(u.Uid))
var (
ctx = context.Background()
err error
info *model.VipInfo
)
// delete random uid at begin
err = d.deleteVip(ctx, u.Uid)
So(err, ShouldBeNil)
// get nil result from db
Convey("get nil result from db", func() {
info, err = d.GetVipFromDB(ctx, u.Uid)
So(err, ShouldResemble, sql.ErrNoRows)
So(info, ShouldNotBeNil)
So(info.Vip, ShouldEqual, 0)
So(info.VipTime, ShouldEqual, "")
So(info.Svip, ShouldEqual, 0)
So(info.SvipTime, ShouldEqual, "")
})
// insert and then get
Convey("insert and then get", func() {
var info2 *model.VipInfo
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err = d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_GetVipFromDB info2(%v)", info2)
So(err, ShouldBeNil)
So(info, ShouldResemble, info2)
})
// valid info but expired vip time
Convey("insert valid but expired vip time", func() {
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().AddDate(0, -1, 0).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().AddDate(-1, 0, 0).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
info2, err := d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_GetVipFromDB info2(%v)", info2)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, 0)
So(info2.Svip, ShouldEqual, 0)
So(info2.VipTime, ShouldEqual, info.VipTime)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
})
}))
}
func TestDao_AddVip(t *testing.T) {
initd()
Convey("test add vip", t, testWithTestUser(func(u *TestUser) {
log.Info("TestDao_GetVipFromDB uid(%d), table(%s)", u.Uid, getUserLevelTable(u.Uid))
var (
ctx = context.Background()
err error
info *model.VipInfo
row int64
)
// create one row at begin
info = &model.VipInfo{
Vip: 1,
VipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
Svip: 1,
SvipTime: time.Now().Add(time.Hour * 12).Format(model.TimeNano),
}
err = d.createVip(ctx, u.Uid, info)
So(err, ShouldBeNil)
// empty vip time, should return err
Convey("empty vip time, should return err", func() {
row, err = d.AddVip(ctx, u.Uid, 0, 0)
So(row, ShouldEqual, 0)
So(err, ShouldResemble, errUpdateVipTimeInvalid)
})
// add vip and svip time
Convey("add vip and svip time", func() {
// add one month vip
dt := xtime.Time(30 * 86400)
vtime, err := time.Parse(model.TimeNano, info.VipTime)
So(err, ShouldBeNil)
newvtime := xtime.Time(vtime.Unix()) + dt
log.Info("TestDao_AddVip info(%v), oldvt(%v), newvtime(%v), dt(%v)", info, vtime.Unix(), newvtime, dt)
row, err := d.AddVip(ctx, u.Uid, newvtime, 0)
So(row, ShouldEqual, 1)
So(err, ShouldBeNil)
info2, err := d.GetVipFromDB(ctx, u.Uid)
log.Info("TestDao_AddVip info2(%v)", info2)
So(err, ShouldBeNil)
So(info2.Vip, ShouldEqual, info.Vip)
So(info2.Svip, ShouldEqual, info.Svip)
So(info2.SvipTime, ShouldEqual, info.SvipTime)
So(info2.VipTime, ShouldEqual, newvtime.Time().Format(model.TimeNano))
})
}))
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"anchorUpdate.go",
"dao.go",
],
importpath = "go-common/app/service/live/xuser/dao/xanchor",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/live/dao-anchor/api/grpc/v1:go_default_library",
"//app/service/live/xuser/conf:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,15 @@
package xanchor
import (
"context"
XanchorV1 "go-common/app/service/live/dao-anchor/api/grpc/v1"
)
// UpdateAnchorInfo 更新主播经验db
func (d *Dao) UpdateAnchorInfo(ctx context.Context, params *XanchorV1.AnchorIncreReq) (err error) {
_, err = d.xuserGRPC.AnchorIncre(ctx, params)
if err != nil {
return
}
return
}

View File

@@ -0,0 +1,39 @@
package xanchor
import (
"context"
xanchor "go-common/app/service/live/dao-anchor/api/grpc/v1"
"go-common/app/service/live/xuser/conf"
)
// Dao dao
type Dao struct {
c *conf.Config
xuserGRPC *xanchor.Client
}
var _rsCli *xanchor.Client
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
var err error
if _rsCli, err = xanchor.NewClient(c.XanchorClient); err != nil {
panic(err)
}
dao = &Dao{
c: c,
xuserGRPC: _rsCli,
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
// check
return nil
}