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,25 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/usersuit/cmd:all-srcs",
"//app/service/main/usersuit/conf:all-srcs",
"//app/service/main/usersuit/dao/invite:all-srcs",
"//app/service/main/usersuit/dao/medal:all-srcs",
"//app/service/main/usersuit/dao/pendant:all-srcs",
"//app/service/main/usersuit/http:all-srcs",
"//app/service/main/usersuit/model:all-srcs",
"//app/service/main/usersuit/rpc/client:all-srcs",
"//app/service/main/usersuit/rpc/server:all-srcs",
"//app/service/main/usersuit/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,221 @@
### Version 2.18.1
> 1. 修正批量获取用户佩戴勋章时的闭包问题
### Verson 2.18.0
> 1. 佩戴勋章区分来源
### Verson 2.17.2
> 1. 修复大会员无法佩戴不在背包里的大会员挂件
### Version 2.17.1
> 1. 记录邀请码购买时的 IPv6 地址
> 2. 佩戴挂件时不再检查是否为 VIP
### Version 2.16.3
> 1. 修复批量获取佩戴挂件时的空挂件现象
> 2. 修复获取单个用户佩戴挂件时的空挂件现象
### Version 2.16.0
> 1. 变更vip挂件装备逻辑
> 2. vip 挂件直落装备随意切换在vip有效期内
### Version 2.15.0
> 1. 在购买邀请码时添加扣除硬币时遗漏的IP参数
### Version 2.14.0
> 1. dao层的ut使用新规范完善
### Version 2.13.1
> 1. 删除block-service import
### Version 2.13.0
> 1. 新增挂件红点提醒
> 2. context.todo -> context.backround
### Version 2.12.3
> 1. fix medal popup bug
### Version 2.12.2
> 1. blockinfo use memberRPC
### Version 2.12.1
> 1. fix 分享group rpc 接口
### Version 2.12.0
> 1. 新增分享group rpc 接口
### Version 2.11.1
> 1. fix message
### Version 2.11.0
> 1. 改为 metadata RemoteIP
### Version 2.10.4
> 1. 迁移identify-->verify
### Version 2.10.3
> 1.use account notify
### Version 2.10.2
> 1.change medal owners mc key
### Version 2.10.1
> 1.ignore paymentSystem err status callback
### Version 2.10.0
> 1.usersuit load gid ref pid
### Version 2.9.3
> 1.usersuit notify
### Version 2.9.2
> 1.fix medal user
### Version 2.9.1
> 1.refined medal all
### Version 2.9.0
> 1.add mid pkg and equip local cache
### Version 2.8.6
> 1.fix redis expire time
### Version 2.8.5
> 1.add medal multi activated rpc api
### Version 2.8.4
> 1.add medal activated rpc
### Version 2.8.3
> 1.refined all info
### Version 2.8.1
> 1.edit allinfo
### Version 2.8.0
> 1.incr chan size
### Version 2.7.0
> 1.refined model and pb
### Version 2.6.8
> 1.refined code
### Version 2.6.7
> 1.fix level
### Version 2.6.6
> 1.refined medal logic
### Version 2.6.5
> 1.refined medal activated logic
### Version 2.6.4
> 1.add medal activated info api
### Version 2.6.3
> 1.medal info add level
### Version 2.6.2
> 1.update sql
### Version 2.6.1
> 1.fix 勋章cond字段bug
### Version 2.6.0
> 1.勋章重构
### Version 2.5.0
> 1.bm
### Version 2.4.3
> 1.remove useless code
### Version 2.4.1
> 1.add register
### Version 2.4.0
> 1.add mutiple grant pandent rpc
### Version 2.4.0
> 1.usersuit medal 勋章重构
### Version 2.3.4
> 1.默认返回的节操7000
### Version 2.3.3
> 1.delete vipinfo proto
### Version 2.3.2
> 1.add equip rpc
### Version 2.3.1
> 1. 给member-service的rpc接口加default
### Version 2.3.0
> 1.usersuit 去除member-service的myinfo接口
### Version 2.2.13
> 1.fix panic bug
#### Version 2.2.12
> 1.fix pendant not online display
#### Version 2.2.11
> 1.pendant and group local cache rebuild
#### Version 2.2.10
> 1.修改邀请码接口 member-service myInfo + coin数据
#### Version 2.2.9
> 1.add empty cache for Equipment
#### Version 2.2.8
> 1.fix reids flush issue
#### Version 2.2.7
> 1.fix pendant purge cache
#### Version 2.2.6
> 1.fix redis conn.close
#### Version 2.2.5
> 1.背包缓存清理改为同步执行
#### Version 2.2.4
> 1.装备挂件db写操作背包信息从cache改为从db中取
#### Version 2.2.3
> 1.fix normal vip equip failed
#### Version 2.2.2
> 1.修复equipment单个接口nil
#### Version 2.2.1
> 1.equipment rpc 批量查询改为100.
#### Version 2.2.0
> 1.增加 equipment 批量rpc接口
#### Version 2.1.3
> 1.fix pendant expire bug
#### Version 2.1.2
> 1.fix panic
#### Version 2.1.1
> 1.pay
#### Version 2.1.0
> 1.change part ut to convey
> 2.remove unused conf item
#### Version 2.0.1
> 1.添加购买历史
#### Version 2.0.0
> 1.初始化挂件信息
#### Version 1.0.0
> 1.添加邀请码功能:包括购买邀请码、使用邀请码、获取邀请码状态

View File

@@ -0,0 +1,11 @@
# Owner
zhaogangtao
zhoujiahui
wanghuan01
# Author
wanghuan01
# Reviewer
zhaogangtao
linmiao

View File

@@ -0,0 +1,16 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- wanghuan01
- zhaogangtao
- zhoujiahui
labels:
- main
- service
- service/main/usersuit
options:
no_parent_owners: true
reviewers:
- linmiao
- wanghuan01
- zhaogangtao

View File

@@ -0,0 +1,13 @@
#### usersuit-service
##### 项目简介
> 1.用户套装服务
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common
##### 特别说明
> 1.model目录可能会被其他项目引用请谨慎更改并通知各方。

View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = [
"convey-test.toml",
"usersuit-service.toml",
],
importpath = "go-common/app/service/main/usersuit/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/http:go_default_library",
"//app/service/main/usersuit/rpc/server:go_default_library",
"//app/service/main/usersuit/service:go_default_library",
"//library/log:go_default_library",
"//library/net/trace: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,126 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
dir = "./"
family = "usersuit-service"
env = "qa"
vipURI = "http://vip.bilibili.co"
payURL = "http://pay.bilibili.com/api/add.pay.order"
notifyURL = "http://account.bilibili.co/api/communicate/purge_cache"
accountIntranetURI = "http://account.bilibili.co"
[host]
accountURI = "https://account.bilibili.com"
messageCo = "http://message.bilibili.co"
[xlog]
dir = "/data/log/usersuit-service"
[payinfo]
MerchantID="1"
MerchantProductID="1"
CallBackURL="http://account.bilibili.com/api/pendant/orderpendantcallback"
[bm]
addr = "0.0.0.0:7261"
maxListen = 1000
timeout = "1s"
[rpcServer]
proto = "tcp"
addr = "0.0.0.0:7269"
[gorpcclient]
[member]
timeout = "1s"
[coin]
timeout = "30s"
[point]
timeout = "30s"
[mysql]
addr = "172.16.33.205:3309"
dsn = "usersuit:cDXL6vncmCQanx4iMgGogu6gYz33NmA1@tcp(172.16.33.205:3309)/usersuit?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 100
idle = 10
queryTimeout = "2s"
execTimeout = "10s"
tranTimeout = "5s"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[redis]
name = "usersuit-service"
proto = "tcp"
addr = "172.18.33.61:6888"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
inviteExpire = "30s"
pendantExpire = "24h"
[memcache]
name = "usersuit-service"
proto = "tcp"
addr = "172.18.33.60:11214"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
medalexpire = "720h"
pointexpire = "168h"
[MedalCache]
size = 10000
expire = "3s"
[EquipCache]
size = 10000
expire = "3s"
[httpClient]
key = "837f620f5c0a8010"
secret = "2a9057021014b4b843b635664c69d5df"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
[httpClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[accountNotify]
key = "4ba46ba31f9a44ef"
secret = "e4c5a7fce28695209e6b4f0af8cf91c5"
group = "AccountNotify-MainAccount-P"
topic = "AccountNotify-T"
action = "pub"
offset = "old"
buffer = 128
name = "usersuit-service/databus"
proto = "tcp"
addr = "172.18.33.50:6205"
active = 1
idle = 1
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"

View File

@@ -0,0 +1,52 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/main/usersuit/conf"
"go-common/app/service/main/usersuit/http"
rpc "go-common/app/service/main/usersuit/rpc/server"
"go-common/app/service/main/usersuit/service"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
// init conf,log,trace,stat,perf.
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Xlog)
defer log.Close()
trace.Init(conf.Conf.Tracer)
defer trace.Close()
// service init
svr := service.New(conf.Conf)
rpcSvr := rpc.New(conf.Conf, svr)
http.Init(conf.Conf, svr)
// signal handler
log.Info("usersuit-service start")
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("usersuit-service get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
rpcSvr.Close()
time.Sleep(time.Second * 2)
svr.Close()
log.Info("usersuit-service exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,129 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
dir = "./"
family = "usersuit-service"
env = "qa"
accountIntranetURI = "http://account.bilibili.co"
accountURI = "https://account.bilibili.com"
vipURI = "http://vip.bilibili.co"
payURL = "http://pay.bilibili.com/api/add.pay.order"
NotifyURL = "http://account.bilibili.co/api/communicate/purge_cache"
[xlog]
dir = "/data/log/usersuit-service"
[payinfo]
MerchantID="1"
MerchantProductID="1"
CallBackURL="http://account.bilibili.com/api/pendant/orderpendantcallback"
[bm]
addr = "0.0.0.0:7261"
maxListen = 1000
timeout = "1s"
[rpcServer]
proto = "tcp"
addr = "0.0.0.0:7269"
[gorpcclient]
[member]
timeout = "1s"
[coin]
timeout = "30s"
[point]
timeout = "30s"
[mysql]
addr = "172.16.33.205:3309"
dsn = "usersuit:cDXL6vncmCQanx4iMgGogu6gYz33NmA1@tcp(172.16.33.205:3309)/usersuit?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 100
idle = 10
queryTimeout = "2s"
execTimeout = "10s"
tranTimeout = "5s"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[redis]
name = "usersuit-service"
proto = "tcp"
addr = "172.16.33.54:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
inviteExpire = "30s"
pendantExpire = "24h"
[memcache]
name = "usersuit-service"
proto = "tcp"
# addr = "172.16.33.54:6379"
addr = "127.0.0.1:11211"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
medalexpire = "720h"
pointexpire = "168h"
[MedalCache]
size = 10000
expire = "3s"
[EquipCache]
size = 10000
expire = "3s"
[httpClient]
key = "837f620f5c0a8010"
secret = "2a9057021014b4b843b635664c69d5df"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
[httpClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[accountNotify]
key = "4ba46ba31f9a44ef"
secret = "99985eb4451cfb1b899ca0fbe3c4bdc8"
group = "AccountNotify-MainAccount-P"
topic = "AccountNotify-T"
action = "pub"
buffer = 128
name = "usersuit-admin/databus"
proto = "tcp"
addr = "172.16.33.158:6205"
active = 1
idle = 1
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[host]
messageCo = "http://message.bilibili.co"
accountCoURI = "http://account.bilibili.co"
apiCoURI = "http://api.bilibili.co"
liveAPICo = "http://api.live.bilibili.co"

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 = ["conf.go"],
importpath = "go-common/app/service/main/usersuit/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml: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,160 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/trace"
"go-common/library/queue/databus"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// Conf global variable.
var (
Conf = &Config{}
client *conf.Client
confPath string
)
// Config struct of conf.
type Config struct {
AccountIntranetURI string
AccountURI string
VipURI string
PayURL string
NotifyURL string
PayInfo *PayInfo
// base
// host
Host *Host
// log
Xlog *log.Config
// tracer
Tracer *trace.Config
// Verify
Verify *verify.Config
// http
BM *bm.ServerConfig
// RPCServer rpc server
RPCServer *rpc.ServerConfig
// MySQL mysql
MySQL *sql.Config
// Redis .
Redis *Redis
// Memcache memcache.
Memcache *Memcache
// HTTPClient http client
HTTPClient *bm.ClientConfig
// GORPCClient
GORPCClient *GORPCClient
// MedalCache
MedalCache *LocalCache
// EquipCache
EquipCache *LocalCache
// AccountNotify account notify.
AccountNotify *databus.Config
}
// GORPCClient .
type GORPCClient struct {
Member *rpc.ClientConfig
Coin *rpc.ClientConfig
Point *rpc.ClientConfig
}
// Host define host conf.
type Host struct {
MessageCo string
AccountCoURI string
APICoURI string
LiveAPICo string
}
// PayInfo pay basic info
type PayInfo struct {
MerchantID string
MerchantProductID string
CallBackURL string
}
// Redis .
type Redis struct {
*redis.Config
InviteExpire time.Duration
PendantExpire time.Duration
}
// LocalCache .
type LocalCache struct {
Size int
Expire time.Duration
}
// Memcache define memcache conf.
type Memcache struct {
*memcache.Config
MedalExpire time.Duration
PointExpire time.Duration
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init int config
func Init() error {
if confPath != "" {
return local()
}
return remote()
}

View File

@@ -0,0 +1,63 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"acc_test.go",
"dao_test.go",
"mysql_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//vendor/github.com/satori/go.uuid:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"acc.go",
"dao.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/service/main/usersuit/dao/invite",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster: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,42 @@
package dao
import (
"context"
"net/url"
"strconv"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_accErrWasFormal = -659
)
// BeFormal be formal member.
func (d *Dao) BeFormal(c context.Context, mid int64, cookie, ip string) (err error) {
params := url.Values{}
params.Set("Cookie", cookie)
params.Set("mid", strconv.FormatInt(mid, 10))
// request
req, err := d.httpClient.NewRequest("POST", d.beFormalURI, ip, params)
if err != nil {
log.Error("account beformal uri(%s) error(%v)", d.beFormalURI+params.Encode(), err)
return
}
req.Header.Set("Cookie", cookie)
var res struct {
Code int `json:"code"`
}
if err = d.httpClient.Do(c, req, &res); err != nil {
return
}
if res.Code != 0 {
if res.Code == _accErrWasFormal {
return
}
err = ecode.Int(res.Code)
log.Error("account beformal uri(%s) error(%v)", d.beFormalURI+params.Encode(), err)
}
return
}

View File

@@ -0,0 +1,26 @@
package dao
import (
"testing"
"github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
func TestDaoBeFormal(t *testing.T) {
convey.Convey("BeFormal", t, func(ctx convey.C) {
var (
mid = int64(88888970)
cookie = "fts=1532412723; buvid3=9FE379B1-4742-4414-83BC-070DC6D5C30128620infoc; rpdid=oxlilxsoqdoskipoimqw; UM_distinctid=164cb9399eb124-053863286996f2-163b6952-1fa400-164cb9399ec15e; LIVE_BUVID=dfbc64db5d227b750394fc08dc27ad5e; LIVE_BUVID__ckMd5=612509d11892d4bd; im_notify_type_27956255=0; sid=66taifil; im_local_unread_27956255=0; im_seqno_27956255=4; CNZZDATA2724999=cnzz_eid%3D937547062-1534212022-%26ntime%3D1534212022; pgv_pvi=1353490432; finger=14bc3c4e; DedeUserID=27956255; DedeUserID__ckMd5=2dfe07340adf4bc9; SESSDATA=3f325bec%2C1538121389%2Cb8e04f4a; bili_jct=008e9de4d371cf91f089a66b77e1d8d7; PHPSESSID=3btocfs7f8mo3u8cpkhfv155e0; bp_t_offset_27956255=157578003585063279; _dfcaptcha=aa24be76e8c462474766bc1f4db916a0"
ip = "127.0.0.1"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
defer gock.OffAll()
httpMock("POST", d.beFormalURI).Reply(0).JSON(`{"code":0}`)
err := d.BeFormal(c, mid, cookie, ip)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"time"
"go-common/app/service/main/usersuit/conf"
"go-common/library/cache/redis"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
const (
_beFormal = "/api/member/beFormal"
)
// Dao struct answer history of Dao
type Dao struct {
db *sql.DB
redis *redis.Pool
//http
httpClient *bm.Client
c *conf.Config
beFormalURI string
inviteExpire int32
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.MySQL),
redis: redis.NewPool(c.Redis.Config),
inviteExpire: int32(time.Duration(c.Redis.InviteExpire) / time.Second),
httpClient: bm.NewClient(c.HTTPClient),
beFormalURI: c.AccountIntranetURI + _beFormal,
}
return
}
// Close close connections of mc, redis, db.
func (d *Dao) Close() {
d.db.Close()
d.redis.Close()
}
// Ping ping health.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
log.Error("dao.db.Ping() error(%v)", err)
return
}
if err = d.pingRedis(c); err != nil {
log.Error("dao.pingRedis() error(%v)", err)
}
return
}

View File

@@ -0,0 +1,47 @@
package dao
import (
"context"
"flag"
"os"
"strings"
"testing"
"go-common/app/service/main/usersuit/conf"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
c = context.Background()
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.usersuit-service")
flag.Set("conf_token", "BVWgBtBvS2pkTBbmxAl0frX6KRA14d5P")
flag.Set("tree_id", "6813")
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/convey-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,88 @@
package dao
import (
"context"
"database/sql"
"time"
"go-common/app/service/main/usersuit/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
)
const (
_addInviteSQL = "INSERT INTO invite_code(mid,code,buy_ip,buy_ip_ng,expires,ctime) VALUES(?,?,?,?,?,?)"
_updateInviteSQL = "UPDATE invite_code SET imid=?,used_at=? WHERE code=?"
_getInviteSQL = "SELECT mid,imid,code,expires,used_at,ctime FROM invite_code WHERE code=?"
_getInvitesSQL = "SELECT mid,imid,code,expires,used_at,ctime FROM invite_code WHERE mid=?"
_getCurrentCountSQL = "SELECT count(1) FROM invite_code WHERE mid=? AND ctime>=? AND ctime<=?"
)
// Begin begin transaction
func (d *Dao) Begin(c context.Context) (tx *xsql.Tx, err error) {
return d.db.Begin(c)
}
// TxAddInvite transaction invite
func (d *Dao) TxAddInvite(c context.Context, tx *xsql.Tx, inv *model.Invite) (affected int64, err error) {
var res sql.Result
if res, err = tx.Exec(_addInviteSQL, inv.Mid, inv.Code, inv.IP, inv.IPng, inv.Expires, inv.Ctime); err != nil {
log.Error("add invite, dao.db.Exec(%d, %s, %d, %v, %d, %v) error(%v)", inv.Mid, inv.Code, inv.IP, inv.IPng, inv.Expires, inv.Ctime, err)
return
}
return res.RowsAffected()
}
// UpdateInvite update invite
func (d *Dao) UpdateInvite(c context.Context, imid int64, usedAt int64, code string) (affected int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _updateInviteSQL, imid, usedAt, code); err != nil {
log.Error("update invite, dao.db.Exec(%d, %d, %s) error(%v)", imid, usedAt, code, err)
return
}
return res.RowsAffected()
}
// Invite invite info
func (d *Dao) Invite(c context.Context, code string) (res *model.Invite, err error) {
row := d.db.QueryRow(c, _getInviteSQL, code)
res = new(model.Invite)
if err = row.Scan(&res.Mid, &res.Imid, &res.Code, &res.Expires, &res.UsedAt, &res.Ctime); err != nil {
if err == sql.ErrNoRows {
res = nil
err = nil
} else {
log.Error("get invite, row.Scan() error(%v)", err)
}
}
return
}
// Invites invite list
func (d *Dao) Invites(c context.Context, mid int64) (res []*model.Invite, err error) {
var rows *xsql.Rows
if rows, err = d.db.Query(c, _getInvitesSQL, mid); err != nil {
log.Error("get invites, dao.db.Query(%d) error(%v)", mid, err)
return
}
defer rows.Close()
for rows.Next() {
inv := new(model.Invite)
if err = rows.Scan(&inv.Mid, &inv.Imid, &inv.Code, &inv.Expires, &inv.UsedAt, &inv.Ctime); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
res = append(res, inv)
}
err = rows.Err()
return
}
// CurrentCount current count
func (d *Dao) CurrentCount(c context.Context, mid int64, start, end time.Time) (res int64, err error) {
row := d.db.QueryRow(c, _getCurrentCountSQL, mid, start, end)
if err = row.Scan(&res); err != nil {
log.Error("get current count, mid: %d, start: %d, end: %d, row.Scan() error(%v)", mid, start, end, err)
}
return
}

View File

@@ -0,0 +1,107 @@
package dao
import (
"testing"
"time"
"go-common/app/service/main/usersuit/model"
"github.com/satori/go.uuid"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBegin(t *testing.T) {
convey.Convey("Begin", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
tx, err := d.Begin(c)
ctx.Convey("Then err should be nil.tx should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tx, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxAddInvite(t *testing.T) {
convey.Convey("TxAddInvite", t, func(ctx convey.C) {
var (
tx, _ = d.Begin(c)
inv = &model.Invite{
Mid: 1,
IPng: []byte{1, 1, 1, 1},
Code: uuid.NewV4().String(),
}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
affected, err := d.TxAddInvite(c, tx, inv)
ctx.Convey("Then err should be nil.affected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(affected, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpdateInvite(t *testing.T) {
convey.Convey("UpdateInvite", t, func(ctx convey.C) {
var (
imid = int64(0)
usedAt = int64(0)
code = "2bbf90926c984a53"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
affected, err := d.UpdateInvite(c, imid, usedAt, code)
ctx.Convey("Then err should be nil.affected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(affected, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoInvite(t *testing.T) {
convey.Convey("Invite", t, func(ctx convey.C) {
var (
code = "2bbf90926c984a53"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Invite(c, code)
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 TestDaoInvites(t *testing.T) {
convey.Convey("Invites", t, func(ctx convey.C) {
var (
mid = int64(88888970)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Invites(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCurrentCount(t *testing.T) {
convey.Convey("CurrentCount", t, func(ctx convey.C) {
var (
mid = int64(0)
start = time.Now()
end = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.CurrentCount(c, mid, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,86 @@
package dao
import (
"context"
"strconv"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_prefixBuyFlag = "b_"
_prefixApplyFlag = "a_"
)
func keyBuyFlag(mid int64) string {
return _prefixBuyFlag + strconv.FormatInt(mid, 10)
}
func keyApplyFlag(code string) string {
return _prefixApplyFlag + code
}
// pingRedis ping redis.
func (d *Dao) pingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
if _, err = redis.String(conn.Do("PING")); err != nil {
log.Error("redis.String(conn.Do(PING)) error(%v)", err)
}
return
}
// SetBuyFlagCache set buy flag cache
func (d *Dao) SetBuyFlagCache(c context.Context, mid int64, f string) (ok bool, err error) {
key := keyBuyFlag(mid)
conn := d.redis.Get(c)
defer conn.Close()
var res interface{}
if res, err = conn.Do("SET", key, f, "EX", d.inviteExpire, "NX"); err != nil {
log.Error("conn.Do(SET, %s, %s, EX, %d, NX)", key, f)
return
}
if res != nil {
ok = true
}
return
}
// DelBuyFlagCache del buy flag cache
func (d *Dao) DelBuyFlagCache(c context.Context, mid int64) (err error) {
key := keyBuyFlag(mid)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("DEL", key); err != nil {
log.Error("conn.Do(DEL, %s)", key)
}
return
}
// SetApplyFlagCache set apply flag cache
func (d *Dao) SetApplyFlagCache(c context.Context, code, f string) (ok bool, err error) {
key := keyApplyFlag(code)
conn := d.redis.Get(c)
defer conn.Close()
var res interface{}
if res, err = conn.Do("SET", key, f, "EX", d.inviteExpire, "NX"); err != nil {
log.Error("conn.Do(SET, %s, %s, EX, %d, NX)", key, f, d.inviteExpire)
return
}
if res != nil {
ok = true
}
return
}
// DelApplyFlagCache del apply Flag Cache
func (d *Dao) DelApplyFlagCache(c context.Context, code string) (err error) {
key := keyApplyFlag(code)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("DEL", key); err != nil {
log.Error("conn.Do(DEL, %s)", key)
}
return
}

View File

@@ -0,0 +1,106 @@
package dao
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyBuyFlag(t *testing.T) {
convey.Convey("keyBuyFlag", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyBuyFlag(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaokeyApplyFlag(t *testing.T) {
convey.Convey("keyApplyFlag", t, func(ctx convey.C) {
var (
code = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyApplyFlag(code)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaopingRedis(t *testing.T) {
convey.Convey("pingRedis", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingRedis(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoSetBuyFlagCache(t *testing.T) {
convey.Convey("SetBuyFlagCache", t, func(ctx convey.C) {
var (
mid = int64(0)
f = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.SetBuyFlagCache(c, mid, f)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoDelBuyFlagCache(t *testing.T) {
convey.Convey("DelBuyFlagCache", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelBuyFlagCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoSetApplyFlagCache(t *testing.T) {
convey.Convey("SetApplyFlagCache", t, func(ctx convey.C) {
var (
code = ""
f = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.SetApplyFlagCache(c, code, f)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoDelApplyFlagCache(t *testing.T) {
convey.Convey("DelApplyFlagCache", t, func(ctx convey.C) {
var (
code = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelApplyFlagCache(c, code)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,67 @@
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",
"depend_test.go",
"localcache_test.go",
"memcache_test.go",
"mysql_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"depend.go",
"localcache.go",
"memcache.go",
"mysql.go",
],
importmap = "go-common/app/service/main/usersuit/dao/medal",
importpath = "go-common/app/service/main/usersuit/dao/medal",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom:go_default_library",
"//vendor/github.com/bluele/gcache: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,68 @@
package medal
import (
"context"
"time"
"go-common/app/service/main/usersuit/conf"
"go-common/library/cache/memcache"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"github.com/bluele/gcache"
)
const (
_sendMsgPath = "/api/notify/send.user.notify.do"
_getWaredFansMedalPath = "/fans_medal/v1/fans_medal/get_weared_medal"
)
// Dao struct info of Dao.
type Dao struct {
db *sql.DB
c *conf.Config
client *bm.Client
// memcache
mc *memcache.Pool
mcExpire int32
pointExpire int32
// send message URI.
sendMsgURI string
// get weared fans medal URI.
getWaredFansMedalURI string
// medalStore
medalStore gcache.Cache
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.MySQL),
client: bm.NewClient(c.HTTPClient),
// memcache
mc: memcache.NewPool(c.Memcache.Config),
mcExpire: int32(time.Duration(c.Memcache.MedalExpire) / time.Second),
pointExpire: int32(time.Duration(c.Memcache.PointExpire) / time.Second),
sendMsgURI: c.Host.MessageCo + _sendMsgPath,
getWaredFansMedalURI: c.Host.LiveAPICo + _getWaredFansMedalPath,
medalStore: gcache.New(c.MedalCache.Size).LFU().Build(),
}
return
}
// Ping ping health.
func (d *Dao) Ping(c context.Context) (err error) {
return d.pingMC(c)
}
// Close close connections of mc, redis, db.
func (d *Dao) Close() {
if d.mc != nil {
d.mc.Close()
}
if d.db != nil {
d.db.Close()
}
}

View File

@@ -0,0 +1,47 @@
package medal
import (
"context"
"flag"
"os"
"testing"
"go-common/app/service/main/usersuit/conf"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
c = context.Background()
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.usersuit-service")
flag.Set("conf_token", "BVWgBtBvS2pkTBbmxAl0frX6KRA14d5P")
flag.Set("tree_id", "6813")
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/convey-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.client.SetTransport(gock.DefaultTransport)
m.Run()
os.Exit(0)
}
// func httpMock(method, url string) *gock.Request {
// r := gock.New(url)
// r.Method = strings.ToUpper(method)
// return r
// }

View File

@@ -0,0 +1,66 @@
package medal
import (
"context"
"fmt"
"net/url"
"strconv"
"go-common/library/log"
)
// SendMsg send message.
func (d *Dao) SendMsg(c context.Context, mid int64, title string, context string) (err error) {
params := url.Values{}
params.Set("mc", "2_1_13")
params.Set("title", title)
params.Set("data_type", "4")
params.Set("context", context)
params.Set("mid_list", fmt.Sprintf("%d", mid))
var res struct {
Code int `json:"code"`
Data *struct {
Status int8 `json:"status"`
Remark string `json:"remark"`
} `json:"data"`
}
if err = d.client.Post(c, d.sendMsgURI, "", params, &res); err != nil {
log.Error("sendMsgURL(%s) error(%v)", d.sendMsgURI+"?"+params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("sendMsgURL(%s) res(%d)", d.sendMsgURI+"?"+params.Encode(), res.Code)
}
log.Info("d.sendMsgURL url(%s) res(%d)", d.sendMsgURI+"?"+params.Encode(), res.Code)
return
}
// GetWearedfansMedal get weared fans medals from live.
func (d *Dao) GetWearedfansMedal(c context.Context, mid int64, source int8) (isLove bool, err error) {
params := url.Values{}
params.Set("uid", strconv.FormatInt(mid, 10))
params.Set("source", strconv.FormatInt(int64(source), 10))
var res struct {
Code int `json:"code"`
Data *struct {
Max int8 `json:"max"`
Cnt int8 `json:"cnt"`
MasterMax int8 `json:"master_max"`
} `json:"data"`
}
if err = d.client.Post(c, d.getWaredFansMedalURI, "", params, &res); err != nil {
log.Error("GetWearedfansMedal(%s) error(%v)", d.getWaredFansMedalURI+"?"+params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("GetWearedfansMedal(%s) res(%d)", d.getWaredFansMedalURI+"?"+params.Encode(), res.Code)
}
log.Info("GetWearedfansMedal(%s) res(%+v)", d.getWaredFansMedalURI+"?"+params.Encode(), res)
if res.Code == 0 {
if res.Data.Cnt > 0 {
isLove = true
return
}
}
return
}

View File

@@ -0,0 +1,39 @@
package medal
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestMedalSendMsg(t *testing.T) {
convey.Convey("SendMsg", t, func(ctx convey.C) {
var (
mid = int64(88889017)
title = ""
context = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SendMsg(c, mid, title, context)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalGetWearedfansMedal(t *testing.T) {
convey.Convey("GetWearedfansMedal", t, func(ctx convey.C) {
var (
mid = int64(88889017)
source = int8(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
isLove, err := d.GetWearedfansMedal(c, mid, source)
ctx.Convey("Then err should be nil.isLove should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(isLove, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,84 @@
package medal
import (
"context"
"time"
"go-common/library/log"
"go-common/library/stat/prom"
"github.com/bluele/gcache"
"github.com/pkg/errors"
)
func (d *Dao) loadMedal(c context.Context, mid int64) (int64, bool, error) {
nid, nofound, err := d.medalActivatedCache(c, mid)
if err != nil {
return 0, nofound, err
}
d.storeMedal(mid, nid, nofound)
return nid, nofound, nil
}
func (d *Dao) storeMedal(mid int64, nid int64, nofound bool) {
if nofound {
return
}
d.medalStore.SetWithExpire(mid, nid, time.Duration(d.c.MedalCache.Expire))
}
func (d *Dao) localMedal(mid int64) (int64, error) {
prom.CacheHit.Incr("local_medal_cache")
item, err := d.medalStore.Get(mid)
if err != nil {
prom.CacheMiss.Incr("local_medal_cache")
return 0, err
}
nid, ok := item.(int64)
if !ok {
prom.CacheMiss.Incr("local_medal_cache")
return 0, errors.New("Not a medal")
}
return nid, nil
}
// MedalActivatedCache get medal cache.
func (d *Dao) MedalActivatedCache(c context.Context, mid int64) (int64, bool, error) {
nid, err := d.localMedal(mid)
if err != nil {
if err != gcache.KeyNotFoundError {
log.Error("Failed to get medal from local: mid: %d: %+v", mid, err)
}
return d.loadMedal(c, mid)
}
return nid, false, nil
}
// MedalsActivatedCache get multi medals cache.
func (d *Dao) MedalsActivatedCache(c context.Context, mids []int64) (map[int64]int64, []int64, error) {
nids := make(map[int64]int64, len(mids))
lcMissed := make([]int64, 0, len(mids))
for _, mid := range mids {
nid, err := d.localMedal(mid)
if err != nil {
if err != gcache.KeyNotFoundError {
log.Error("Failed to get medal from local: mid: %d: %+v", mid, err)
}
lcMissed = append(lcMissed, mid)
continue
}
nids[mid] = nid
}
if len(lcMissed) == 0 {
return nids, nil, nil
}
mcMedals, mcMissed, err := d.medalsActivatedCache(c, lcMissed)
if err != nil {
return nil, nil, err
}
for mid, nid := range mcMedals {
d.storeMedal(mid, nid, false)
nids[mid] = nid
}
return nids, mcMissed, nil
}

View File

@@ -0,0 +1,85 @@
package medal
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestMedalloadMedal(t *testing.T) {
convey.Convey("loadMedal", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.loadMedal(c, mid)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalstoreMedal(t *testing.T) {
convey.Convey("storeMedal", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nid = int64(1)
nofound bool
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.storeMedal(mid, nid, nofound)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestMedallocalMedal(t *testing.T) {
convey.Convey("localMedal", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.localMedal(mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalMedalActivatedCache(t *testing.T) {
convey.Convey("MedalActivatedCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.MedalActivatedCache(c, mid)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalMedalsActivatedCache(t *testing.T) {
convey.Convey("MedalsActivatedCache", t, func(ctx convey.C) {
var (
mids = []int64{88889017, 1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.MedalsActivatedCache(c, mids)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,273 @@
package medal
import (
"context"
"strconv"
"github.com/pkg/errors"
"go-common/app/service/main/usersuit/model"
gmc "go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_prefixActivatedNid = "usma:" // key of activated medal nid
_prefixOwners = "umos:" // key of owners info
_prefixRedPoint = "usrp:" // key of red point
_prefixPopup = "uspp:" // key of new medal popup
)
// medalactivated medal nid key.
func activatedNidKey(mid int64) string {
return _prefixActivatedNid + strconv.FormatInt(mid, 10)
}
// ownersKey medal_owner key.
func ownersKey(mid int64) string {
return _prefixOwners + strconv.FormatInt(mid, 10)
}
//RedPointKey new medal RedPoint key.
func RedPointKey(mid int64) string {
return _prefixRedPoint + strconv.FormatInt(mid, 10)
}
// PopupKey new medal popup key.
func PopupKey(mid int64) string {
return _prefixPopup + strconv.FormatInt(mid, 10)
}
func (d *Dao) pingMC(c context.Context) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(&gmc.Item{Key: "ping", Value: []byte{1}, Expiration: d.mcExpire}); err != nil {
err = errors.WithStack(err)
}
return
}
// MedalOwnersCache get medal_owner cache.
func (d *Dao) MedalOwnersCache(c context.Context, mid int64) (res []*model.MedalOwner, notFound bool, err error) {
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(ownersKey(mid))
if err != nil {
if err == gmc.ErrNotFound {
res = nil
err = nil
notFound = true
return
}
err = errors.WithStack(err)
return
}
res = make([]*model.MedalOwner, 0)
if err = conn.Scan(item, &res); err != nil {
err = errors.WithStack(err)
}
return
}
// SetMedalOwnersache set medal_owner cache.
func (d *Dao) SetMedalOwnersache(c context.Context, mid int64, nos []*model.MedalOwner) (err error) {
key := ownersKey(mid)
item := &gmc.Item{Key: key, Object: nos, Expiration: d.mcExpire, Flags: gmc.FlagJSON}
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(item); err != nil {
err = errors.WithStack(err)
}
return
}
// DelMedalOwnersCache delete medal_owner cache.
func (d *Dao) DelMedalOwnersCache(c context.Context, mid int64) (err error) {
key := ownersKey(mid)
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
err = errors.WithStack(err)
}
}
return
}
// MedalsActivatedCache multi get user activated medal nid from memcache.
func (d *Dao) medalsActivatedCache(c context.Context, mids []int64) (nids map[int64]int64, missed []int64, err error) {
nids = make(map[int64]int64, len(mids))
keys := make([]string, len(mids))
mm := make(map[string]int64, len(mids))
for i, mid := range mids {
var key = activatedNidKey(mid)
keys[i] = key
mm[key] = mid
}
conn := d.mc.Get(c)
defer conn.Close()
items, err := conn.GetMulti(keys)
if err != nil {
if err == gmc.ErrNotFound {
err = nil
}
return
}
for _, item := range items {
var nid int64
if err = conn.Scan(item, &nid); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
continue
}
nids[mm[item.Key]] = nid
delete(mm, item.Key)
}
missed = make([]int64, 0, len(mm))
for _, m := range mm {
missed = append(missed, m)
}
return
}
// MedalActivatedCache get user activated medal nid.
func (d *Dao) medalActivatedCache(c context.Context, mid int64) (nid int64, notFound bool, err error) {
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(activatedNidKey(mid))
if err != nil {
if err == gmc.ErrNotFound {
nid = 0
err = nil
notFound = true
return
}
err = errors.WithStack(err)
return
}
if err = conn.Scan(item, &nid); err != nil {
err = errors.WithStack(err)
}
return
}
// SetMedalActivatedCache set activated medal cache.
func (d *Dao) SetMedalActivatedCache(c context.Context, mid, nid int64) (err error) {
key := activatedNidKey(mid)
item := &gmc.Item{Key: key, Object: nid, Expiration: d.mcExpire, Flags: gmc.FlagJSON}
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(item); err != nil {
err = errors.WithStack(err)
}
return
}
// DelMedalActivatedCache delete activated medal cache.
func (d *Dao) DelMedalActivatedCache(c context.Context, mid int64) (err error) {
key := activatedNidKey(mid)
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
err = errors.WithStack(err)
}
}
return
}
// PopupCache get new medal info popup cache.
func (d *Dao) PopupCache(c context.Context, mid int64) (nid int64, err error) {
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(PopupKey(mid))
if err != nil {
if err == gmc.ErrNotFound {
nid = 0
err = nil
return
}
err = errors.WithStack(err)
return
}
if err = conn.Scan(item, &nid); err != nil {
err = errors.WithStack(err)
}
return
}
// SetPopupCache set popup cache.
func (d *Dao) SetPopupCache(c context.Context, mid, nid int64) (err error) {
key := PopupKey(mid)
item := &gmc.Item{Key: key, Object: nid, Expiration: d.pointExpire, Flags: gmc.FlagJSON}
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(item); err != nil {
err = errors.WithStack(err)
}
return
}
// DelPopupCache delete new medal info popup cache.
func (d *Dao) DelPopupCache(c context.Context, mid int64) (err error) {
key := PopupKey(mid)
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
err = errors.WithStack(err)
}
}
return
}
// RedPointCache get new medal info red point cache.
func (d *Dao) RedPointCache(c context.Context, mid int64) (nid int64, err error) {
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(RedPointKey(mid))
if err != nil {
if err == gmc.ErrNotFound {
err = nil
return
}
err = errors.WithStack(err)
return
}
if err = conn.Scan(item, &nid); err != nil {
err = errors.WithStack(err)
}
return
}
// SetRedPointCache set red point cache.
func (d *Dao) SetRedPointCache(c context.Context, mid, nid int64) (err error) {
key := RedPointKey(mid)
item := &gmc.Item{Key: key, Object: nid, Expiration: d.pointExpire, Flags: gmc.FlagJSON}
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Set(item); err != nil {
err = errors.WithStack(err)
}
return
}
// DelRedPointCache delete new medal info red point cache.
func (d *Dao) DelRedPointCache(c context.Context, mid int64) (err error) {
key := RedPointKey(mid)
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(key); err != nil {
if err == gmc.ErrNotFound {
err = nil
} else {
err = errors.WithStack(err)
}
}
return
}

View File

@@ -0,0 +1,271 @@
package medal
import (
"go-common/app/service/main/usersuit/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestMedalactivatedNidKey(t *testing.T) {
convey.Convey("activatedNidKey", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := activatedNidKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalownersKey(t *testing.T) {
convey.Convey("ownersKey", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := ownersKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalRedPointKey(t *testing.T) {
convey.Convey("RedPointKey", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := RedPointKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalPopupKey(t *testing.T) {
convey.Convey("PopupKey", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := PopupKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalpingMC(t *testing.T) {
convey.Convey("pingMC", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingMC(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalSetMedalOwnersache(t *testing.T) {
convey.Convey("SetMedalOwnersache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nos = []*model.MedalOwner{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetMedalOwnersache(c, mid, nos)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalMedalOwnersCache(t *testing.T) {
convey.Convey("MedalOwnersCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nos = []*model.MedalOwner{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.SetMedalOwnersache(c, mid, nos)
res, notFound, err := d.MedalOwnersCache(c, mid)
ctx.Convey("Then err should be nil.res,notFound should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(notFound, convey.ShouldNotBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalDelMedalOwnersCache(t *testing.T) {
convey.Convey("DelMedalOwnersCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelMedalOwnersCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalmedalsActivatedCache(t *testing.T) {
convey.Convey("medalsActivatedCache", t, func(ctx convey.C) {
var (
mids = []int64{88889017}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nids, missed, err := d.medalsActivatedCache(c, mids)
ctx.Convey("Then err should be nil.nids,missed should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missed, convey.ShouldNotBeNil)
ctx.So(nids, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalSetMedalActivatedCache(t *testing.T) {
convey.Convey("SetMedalActivatedCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetMedalActivatedCache(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalmedalActivatedCache(t *testing.T) {
convey.Convey("medalActivatedCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nid, notFound, err := d.medalActivatedCache(c, mid)
ctx.Convey("Then err should be nil.nid,notFound should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(notFound, convey.ShouldNotBeNil)
ctx.So(nid, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalDelMedalActivatedCache(t *testing.T) {
convey.Convey("DelMedalActivatedCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelMedalActivatedCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalPopupCache(t *testing.T) {
convey.Convey("PopupCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nid, err := d.PopupCache(c, mid)
ctx.Convey("Then err should be nil.nid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(nid, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalSetPopupCache(t *testing.T) {
convey.Convey("SetPopupCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetPopupCache(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalDelPopupCache(t *testing.T) {
convey.Convey("DelPopupCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelPopupCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalRedPointCache(t *testing.T) {
convey.Convey("RedPointCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nid, err := d.RedPointCache(c, mid)
ctx.Convey("Then err should be nil.nid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(nid, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalSetRedPointCache(t *testing.T) {
convey.Convey("SetRedPointCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
nid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetRedPointCache(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalDelRedPointCache(t *testing.T) {
convey.Convey("DelRedPointCache", t, func(ctx convey.C) {
var (
mid = int64(88889017)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelRedPointCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,181 @@
package medal
import (
"context"
"fmt"
"go-common/app/service/main/usersuit/model"
"go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_sharding = 10
_insOwnerSQL = "INSERT INTO medal_owner_%s(mid,nid) VALUES (?,?)"
_updateOwnerSQL = "UPDATE medal_owner_%s SET is_activated=? WHERE mid=? AND nid=?"
_updateOwnerUnallSQL = "UPDATE medal_owner_%s SET is_activated=0 WHERE mid=? AND nid!=?"
_selInfoAllSQL = "SELECT id,name,description,image,image_small,cond,gid,level,level_rank,sort FROM medal_info ORDER BY sort ASC,gid ASC,level ASC"
_selOwnerByMidSQL = "SELECT id,mid,nid,is_activated,ctime,mtime FROM medal_owner_%s WHERE mid=? AND is_del=0 ORDER BY ctime DESC"
_selInfoByNidSQL = "SELECT name FROM medal_info WHERE id=? AND is_online=1"
_selInstalledOwnerBYMidSQL = "SELECT nid FROM medal_owner_%s WHERE mid=? AND is_activated=1 AND is_del=0 LIMIT 1"
_countOwnerBYNidMidSQL = "SELECT COUNT(*) FROM medal_owner_%s WHERE mid=? AND nid=?"
_OwnerBYNidMidSQL = "SELECT id,mid,nid,is_activated,ctime,mtime FROM medal_owner_%s WHERE mid=? AND nid=?"
_selGroupAllSQL = "SELECT id,name,pid,rank FROM medal_group WHERE is_online=1 ORDER BY pid ASC,rank ASC"
)
func (d *Dao) hit(id int64) string {
return fmt.Sprintf("%d", id%_sharding)
}
// AddMedalOwner insert into medal_owner.
func (d *Dao) AddMedalOwner(c context.Context, mid, nid int64) (err error) {
if _, err = d.db.Exec(c, fmt.Sprintf(_insOwnerSQL, d.hit(mid)), mid, nid); err != nil {
err = errors.WithStack(err)
}
return
}
// InstallMedalOwner update medal_owner set is_activated=1.
func (d *Dao) InstallMedalOwner(c context.Context, mid, nid int64) (err error) {
if _, err = d.db.Exec(c, fmt.Sprintf(_updateOwnerSQL, d.hit(mid)), model.OwnerInstall, mid, nid); err != nil {
err = errors.WithStack(err)
}
return
}
// UninstallMedalOwner update medal_owner set is_activated=0.
func (d *Dao) UninstallMedalOwner(c context.Context, mid, nid int64) (err error) {
if _, err = d.db.Exec(c, fmt.Sprintf(_updateOwnerSQL, d.hit(mid)), model.OwnerUninstall, mid, nid); err != nil {
err = errors.WithStack(err)
}
return
}
// UninstallAllMedalOwner uninst all medal_owner set is_activated=0.
func (d *Dao) UninstallAllMedalOwner(c context.Context, mid, nid int64) (err error) {
if _, err = d.db.Exec(c, fmt.Sprintf(_updateOwnerUnallSQL, d.hit(mid)), mid, nid); err != nil {
err = errors.WithStack(err)
}
return
}
// MedalInfoAll retun all medal_info where is_online=1.
func (d *Dao) MedalInfoAll(c context.Context) (res map[int64]*model.MedalInfo, err error) {
res = make(map[int64]*model.MedalInfo)
rows, err := d.db.Query(c, _selInfoAllSQL)
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
info := new(model.MedalInfo)
if err = rows.Scan(&info.ID, &info.Name, &info.Description, &info.Image, &info.ImageSmall, &info.Condition, &info.GID, &info.Level, &info.LevelRank, &info.Sort); err != nil {
err = errors.WithStack(err)
return
}
info.Build()
res[info.ID] = info
}
err = rows.Err()
return
}
// MedalOwnerByMid return medal_owner by mid.
func (d *Dao) MedalOwnerByMid(c context.Context, mid int64) (res []*model.MedalOwner, err error) {
res = make([]*model.MedalOwner, 0)
rows, err := d.db.Query(c, fmt.Sprintf(_selOwnerByMidSQL, d.hit(mid)), mid)
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.MedalOwner)
if err = rows.Scan(&r.ID, &r.MID, &r.NID, &r.IsActivated, &r.CTime, &r.MTime); err != nil {
err = errors.WithStack(err)
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// MedalInfoByNid return medal_info by nid.
func (d *Dao) MedalInfoByNid(c context.Context, nid int64) (res *model.MedalInfo, err error) {
res = &model.MedalInfo{}
rows := d.db.QueryRow(c, _selInfoByNidSQL, nid)
if err = rows.Scan(&res.Name); err != nil {
if err != sql.ErrNoRows {
err = errors.Wrap(err, "InstalledOwnerBYMid")
return
}
err = nil
}
return
}
// ActivatedOwnerByMid retun nid of medal_owner by mid where is_activated=1.
func (d *Dao) ActivatedOwnerByMid(c context.Context, mid int64) (nid int64, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selInstalledOwnerBYMidSQL, d.hit(mid)), mid)
if err = row.Scan(&nid); err != nil {
if err != sql.ErrNoRows {
err = errors.Wrap(err, "InstalledOwnerBYMid")
return
}
err = nil
}
return
}
// CountOwnerBYNidMid retun number of medal_owner by mid and nid.
func (d *Dao) CountOwnerBYNidMid(c context.Context, mid, nid int64) (count int64, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_countOwnerBYNidMidSQL, d.hit(mid)), mid, nid)
if err = row.Scan(&count); err != nil {
if err != sql.ErrNoRows {
err = errors.Wrap(err, "CountOwnerBYNidMid")
return
}
count = 0
err = nil
}
return
}
// OwnerBYNidMid retun medal_owner by mid and nid.
func (d *Dao) OwnerBYNidMid(c context.Context, mid, nid int64) (res *model.MedalOwner, err error) {
res = &model.MedalOwner{}
row := d.db.QueryRow(c, fmt.Sprintf(_OwnerBYNidMidSQL, d.hit(mid)), mid, nid)
if err = row.Scan(&res.ID, &res.MID, &res.NID, &res.IsActivated, &res.CTime, &res.MTime); err != nil {
if err != sql.ErrNoRows {
err = errors.Wrap(err, "OwnerBYNidMid")
return
}
res = nil
err = nil
}
return
}
// MedalGroupAll retun all medal_group where is_online=1.
func (d *Dao) MedalGroupAll(c context.Context) (res []*model.MedalGroup, err error) {
rows, err := d.db.Query(c, _selGroupAllSQL)
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
info := new(model.MedalGroup)
if err = rows.Scan(&info.ID, &info.Name, &info.PID, &info.Rank); err != nil {
err = errors.WithStack(err)
return
}
res = append(res, info)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,183 @@
package medal
import (
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestMedalhit(t *testing.T) {
convey.Convey("hit", t, func(ctx convey.C) {
var (
id = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.hit(id)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalAddMedalOwner(t *testing.T) {
convey.Convey("AddMedalOwner", t, func(ctx convey.C) {
var (
mid = time.Now().Unix()
nid = int64(5)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddMedalOwner(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalInstallMedalOwner(t *testing.T) {
convey.Convey("InstallMedalOwner", t, func(ctx convey.C) {
var (
mid = int64(0)
nid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.InstallMedalOwner(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalUninstallMedalOwner(t *testing.T) {
convey.Convey("UninstallMedalOwner", t, func(ctx convey.C) {
var (
mid = int64(0)
nid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UninstallMedalOwner(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalUninstallAllMedalOwner(t *testing.T) {
convey.Convey("UninstallAllMedalOwner", t, func(ctx convey.C) {
var (
mid = int64(0)
nid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UninstallAllMedalOwner(c, mid, nid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestMedalMedalInfoAll(t *testing.T) {
convey.Convey("MedalInfoAll", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.MedalInfoAll(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalMedalOwnerByMid(t *testing.T) {
convey.Convey("MedalOwnerByMid", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.MedalOwnerByMid(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalMedalInfoByNid(t *testing.T) {
convey.Convey("MedalInfoByNid", t, func(ctx convey.C) {
var (
nid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.MedalInfoByNid(c, nid)
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 TestMedalActivatedOwnerByMid(t *testing.T) {
convey.Convey("ActivatedOwnerByMid", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nid, err := d.ActivatedOwnerByMid(c, mid)
ctx.Convey("Then err should be nil.nid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(nid, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalCountOwnerBYNidMid(t *testing.T) {
convey.Convey("CountOwnerBYNidMid", t, func(ctx convey.C) {
var (
mid = int64(0)
nid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
count, err := d.CountOwnerBYNidMid(c, mid, nid)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestMedalOwnerBYNidMid(t *testing.T) {
convey.Convey("OwnerBYNidMid", t, func(ctx convey.C) {
var (
mid = int64(32141)
nid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.OwnerBYNidMid(c, mid, nid)
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 TestMedalMedalGroupAll(t *testing.T) {
convey.Convey("MedalGroupAll", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.MedalGroupAll(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,73 @@
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",
"local_equip_test.go",
"memcache_test.go",
"mysql_test.go",
"pay_test.go",
"redis_test.go",
"vip_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"local_equip.go",
"memcache.go",
"mysql.go",
"pay.go",
"redis.go",
"vip.go",
],
importpath = "go-common/app/service/main/usersuit/dao/pendant",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/bluele/gcache: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,79 @@
package pendant
import (
"context"
"time"
"go-common/app/service/main/usersuit/conf"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"github.com/bluele/gcache"
)
const (
_info = "/internal/v1/user/"
)
// Dao struct info of Dao.
type Dao struct {
db *sql.DB
c *conf.Config
client *bm.Client
// redis
redis *redis.Pool
pendantExpire int32
// memcache
mc *memcache.Pool
pointExpire int32
vipInfoURL string
payURL string
notifyURL string
// equipStore
equipStore gcache.Cache
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.MySQL),
client: bm.NewClient(c.HTTPClient),
// redis
redis: redis.NewPool(c.Redis.Config),
pendantExpire: int32(time.Duration(c.Redis.PendantExpire) / time.Second),
// memcache
mc: memcache.NewPool(c.Memcache.Config),
pointExpire: int32(time.Duration(c.Memcache.PointExpire) / time.Second),
vipInfoURL: c.VipURI + _info,
payURL: c.PayURL,
notifyURL: c.NotifyURL,
equipStore: gcache.New(c.EquipCache.Size).LFU().Build(),
}
return
}
// Ping ping health.
func (d *Dao) Ping(c context.Context) (err error) {
return d.pingRedis(c)
}
// Close close connections of mc, redis, db.
func (d *Dao) Close() {
if d.redis != nil {
d.redis.Close()
}
if d.db != nil {
d.db.Close()
}
}
func (d *Dao) pingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
_, err = conn.Do("SET", "PING", "PONG")
conn.Close()
return
}

View File

@@ -0,0 +1,48 @@
package pendant
import (
"context"
"flag"
"os"
"strings"
"testing"
"go-common/app/service/main/usersuit/conf"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
c = context.Background()
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.usersuit-service")
flag.Set("conf_token", "BVWgBtBvS2pkTBbmxAl0frX6KRA14d5P")
flag.Set("tree_id", "6813")
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/convey-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.client.SetTransport(gock.DefaultTransport)
m.Run()
os.Exit(0)
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,85 @@
package pendant
import (
"context"
"time"
"go-common/app/service/main/usersuit/model"
"go-common/library/log"
"go-common/library/stat/prom"
"github.com/bluele/gcache"
"github.com/pkg/errors"
)
func (d *Dao) loadEquip(c context.Context, mid int64) (*model.PendantEquip, error) {
equip, err := d.equipCache(c, mid)
if err != nil {
return nil, err
}
d.storeEquip(mid, equip)
return equip, nil
}
func (d *Dao) storeEquip(mid int64, equip *model.PendantEquip) {
if equip == nil {
return
}
d.equipStore.SetWithExpire(mid, equip, time.Duration(d.c.EquipCache.Expire))
}
func (d *Dao) localEquip(mid int64) (*model.PendantEquip, error) {
prom.CacheHit.Incr("local_equip_cache")
item, err := d.equipStore.Get(mid)
if err != nil {
prom.CacheMiss.Incr("local_equip_cache")
return nil, err
}
equip, ok := item.(*model.PendantEquip)
if !ok {
prom.CacheMiss.Incr("local_equip_cache")
return nil, errors.New("Not a equip")
}
return equip, nil
}
// EquipCache get equip cache.
func (d *Dao) EquipCache(c context.Context, mid int64) (*model.PendantEquip, error) {
equip, err := d.localEquip(mid)
if err != nil {
if err != gcache.KeyNotFoundError {
log.Error("Failed to get equip from local: mid: %d: %+v", mid, err)
}
return d.loadEquip(c, mid)
}
return equip, nil
}
// EquipsCache get multi equip cache.
func (d *Dao) EquipsCache(c context.Context, mids []int64) (map[int64]*model.PendantEquip, []int64, error) {
equips := make(map[int64]*model.PendantEquip, len(mids))
lcMissed := make([]int64, 0, len(mids))
for _, mid := range mids {
equip, err := d.localEquip(mid)
if err != nil {
if err != gcache.KeyNotFoundError {
log.Error("Failed to get equip from local: mid: %d: %+v", mid, err)
}
lcMissed = append(lcMissed, mid)
continue
}
equips[equip.Mid] = equip
}
if len(lcMissed) == 0 {
return equips, nil, nil
}
rdsEquips, rdsMissed, err := d.equipsCache(c, lcMissed)
if err != nil {
return nil, nil, err
}
for _, equip := range rdsEquips {
d.storeEquip(equip.Mid, equip)
equips[equip.Mid] = equip
}
return equips, rdsMissed, nil
}

View File

@@ -0,0 +1,88 @@
package pendant
import (
"go-common/app/service/main/usersuit/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestPendantloadEquip(t *testing.T) {
convey.Convey("loadEquip", t, func(ctx convey.C) {
var (
mid = int64(650454)
info = &model.PendantEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.AddEquipCache(c, mid, info)
p1, err := d.loadEquip(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantstoreEquip(t *testing.T) {
convey.Convey("storeEquip", t, func(ctx convey.C) {
var (
mid = int64(650454)
equip = &model.PendantEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.storeEquip(mid, equip)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestPendantlocalEquip(t *testing.T) {
convey.Convey("localEquip", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.localEquip(mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantEquipCache(t *testing.T) {
convey.Convey("EquipCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.EquipCache(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantEquipsCache(t *testing.T) {
convey.Convey("EquipsCache", t, func(ctx convey.C) {
var (
mids = []int64{650454, 1}
info = &model.PendantEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.AddEquipCache(c, int64(650454), info)
d.AddEquipCache(c, int64(1), info)
p1, p2, err := d.EquipsCache(c, mids)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,54 @@
package pendant
import (
"context"
"fmt"
gmc "go-common/library/cache/memcache"
)
const (
_prefixRedPointFlag = "r_p_f_%d"
)
func redPointFlagKey(mid int64) string {
return fmt.Sprintf(_prefixRedPointFlag, mid)
}
// RedPointCache get new pendant info red point cache.
func (d *Dao) RedPointCache(c context.Context, mid int64) (pid int64, err error) {
conn := d.mc.Get(c)
defer conn.Close()
item, err := conn.Get(redPointFlagKey(mid))
if err != nil {
if err == gmc.ErrNotFound {
err = nil
}
return
}
err = conn.Scan(item, &pid)
return
}
// SetRedPointCache set red point cache.
func (d *Dao) SetRedPointCache(c context.Context, mid, pid int64) (err error) {
var (
item = &gmc.Item{Key: redPointFlagKey(mid), Object: pid, Expiration: d.pointExpire, Flags: gmc.FlagJSON}
conn = d.mc.Get(c)
)
defer conn.Close()
err = conn.Set(item)
return
}
// DelRedPointCache delete new pendant info red point cache.
func (d *Dao) DelRedPointCache(c context.Context, mid int64) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
if err = conn.Delete(redPointFlagKey(mid)); err != nil {
if err == gmc.ErrNotFound {
err = nil
}
}
return
}

View File

@@ -0,0 +1,69 @@
package pendant
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestPendantredPointFlagKey(t *testing.T) {
convey.Convey("redPointFlagKey", t, func(ctx convey.C) {
var (
mid = int64(123)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := redPointFlagKey(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantRedPointCache(t *testing.T) {
convey.Convey("RedPointCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(123)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
pid, err := d.RedPointCache(c, mid)
ctx.Convey("Then err should be nil.pid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(pid, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantSetRedPointCache(t *testing.T) {
convey.Convey("SetRedPointCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(123)
pid = int64(10)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SetRedPointCache(c, mid, pid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantDelRedPointCache(t *testing.T) {
convey.Convey("DelRedPointCache", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(123)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelRedPointCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,515 @@
package pendant
import (
"bytes"
"context"
"database/sql"
"fmt"
"time"
"go-common/app/service/main/usersuit/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
"github.com/pkg/errors"
)
const (
_getGroupOnlineSQL = "SELECT id,name,rank,status,image,image_model,frequency_limit,time_limit FROM pendant_group WHERE status = 1 or id in (30,31) ORDER BY rank"
_getGroupByIDSQL = "SELECT id,name,rank,status,image,image_model,frequency_limit,time_limit FROM pendant_group where id = ?"
_selGIDRefPIDSQL = "SELECT gid,pid FROM pendant_group_ref"
_getPendantInfoSQL = "SELECT id,name,image,image_model,status FROM pendant_info"
_getPendantInfoByIDSQL = "SELECT id,name,image,image_model,status FROM pendant_info where id = ? limit 1"
_getPendantInfosSQL = "SELECT id,name,image,image_model,status,rank FROM pendant_info ORDER BY rank"
_getPendantPriceSQL = "SELECT pid,type,price FROM pendant_price where pid = ? "
_getOrderHistorySQL = "SELECT mid,order_id,pay_id,appid,status,pid,time_length,cost,buy_time,is_callback,callback_time,pay_type FROM user_pendant_order WHERE %s"
_getUserPackageSQL = "SELECT mid,pid,expires,type,status,is_vip FROM user_pendant_pkg WHERE mid = ? AND pid = ? "
_getUserPackageByMidSQL = "SELECT mid,pid,expires,type,status,is_vip FROM user_pendant_pkg WHERE mid = ? AND expires >= ? AND status > 0 ORDER BY mtime DESC"
_countOrderHistorySQL = "SELECT count(1) FROM user_pendant_order WHERE %s"
_getPendantEquipByMidSQL = "SELECT mid,pid,expires FROM user_pendant_equip WHERE mid = ? and expires >= ?"
_getPendantEquipByMidsSQL = "SELECT mid,pid,expires FROM user_pendant_equip WHERE mid IN (%s) and expires >= ?"
_insertPendantPackageSQL = "INSERT INTO user_pendant_pkg(mid,pid,expires,type,status,is_vip) VALUES (?,?,?,?,?,?)"
_insertOrderHistory = "INSERT INTO user_pendant_order(mid,order_id,pay_id,appid,status,pid,time_length,cost,buy_time,is_callback,callback_time,pay_type) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"
_insertOperationSQL = "INSERT INTO pendant_grant_history(mid,pid,source_type,operator_name,operator_action) VALUES (?,?,?,?,?)"
_insertEquipSQL = "INSERT INTO user_pendant_equip(mid,pid,expires) VALUES (?,?,?) ON DUPLICATE KEY UPDATE pid=VALUES(pid),expires=VALUES(expires)"
_updatePackageSQL = "UPDATE user_pendant_pkg %s WHERE mid=? AND pid=?"
_updatePackageExpireSQL = "UPDATE user_pendant_pkg SET status=0 WHERE mid=? AND expires<?"
_updateOrderInfoSQL = "UPDATE user_pendant_order SET status=?,pay_id=?,is_callback=?,callback_time=? WHERE order_id=?"
_updateEquipMIDSQL = "UPDATE user_pendant_equip SET pid=0,expires=0 WHERE mid=?"
)
//PendantGroupInfo return all group info
func (d *Dao) PendantGroupInfo(c context.Context) (res []*model.PendantGroupInfo, err error) {
var row *xsql.Rows
res = make([]*model.PendantGroupInfo, 0)
if row, err = d.db.Query(c, _getGroupOnlineSQL); err != nil {
log.Error("PendantGroupInfo query error %v", err)
return
}
defer row.Close()
for row.Next() {
info := new(model.PendantGroupInfo)
if err = row.Scan(&info.ID, &info.Name, &info.Rank, &info.Status, &info.Image, &info.ImageModel, &info.FrequencyLimit, &info.TimeLimit); err != nil {
log.Error("PendantGroupInfo scan error %v", err)
return
}
res = append(res, info)
}
return
}
// GroupByID return group info by id
func (d *Dao) GroupByID(c context.Context, gid int64) (res *model.PendantGroupInfo, err error) {
var row *xsql.Row
res = new(model.PendantGroupInfo)
row = d.db.QueryRow(c, _getGroupByIDSQL, gid)
if err = row.Scan(&res.ID, &res.Name, &res.Rank, &res.Status, &res.Image, &res.ImageModel, &res.FrequencyLimit, &res.TimeLimit); err != nil {
if err == xsql.ErrNoRows {
res = nil
err = nil
return
}
log.Error("PendantGroupInfo scan error %v", err)
return
}
return
}
// GIDRefPID gid relation of pid .
func (d *Dao) GIDRefPID(c context.Context) (gidMap map[int64][]int64, pidMap map[int64]int64, err error) {
var rows *xsql.Rows
if rows, err = d.db.Query(c, _selGIDRefPIDSQL); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
gidMap = make(map[int64][]int64)
pidMap = make(map[int64]int64)
for rows.Next() {
var gid, pid int64
if err = rows.Scan(&gid, &pid); err != nil {
if err == xsql.ErrNoRows {
gidMap = nil
pidMap = nil
err = nil
return
}
err = errors.WithStack(err)
return
}
pidMap[pid] = gid
gidMap[gid] = append(gidMap[gid], pid)
}
return
}
// PendantList return pendant info
func (d *Dao) PendantList(c context.Context) (res []*model.Pendant, err error) {
var (
row *xsql.Rows
)
res = make([]*model.Pendant, 0)
if row, err = d.db.Query(c, _getPendantInfosSQL); err != nil {
log.Error("PendantInfo query error %v", err)
return
}
defer row.Close()
for row.Next() {
info := new(model.Pendant)
if err = row.Scan(&info.ID, &info.Name, &info.Image, &info.ImageModel, &info.Status, &info.Rank); err != nil {
log.Error("PendantInfo scan error %v", err)
return
}
res = append(res, info)
}
return
}
// Pendants return pendant info by ids
func (d *Dao) Pendants(c context.Context, pids []int64) (res []*model.Pendant, err error) {
var (
row *xsql.Rows
bf bytes.Buffer
)
res = make([]*model.Pendant, 0)
bf.WriteString(_getPendantInfoSQL)
bf.WriteString(" where id in(")
bf.WriteString(xstr.JoinInts(pids))
bf.WriteString(") and status = 1 ORDER BY rank")
if row, err = d.db.Query(c, bf.String()); err != nil {
log.Error("Pendants query error %v", err)
return
}
defer row.Close()
for row.Next() {
info := new(model.Pendant)
if err = row.Scan(&info.ID, &info.Name, &info.Image, &info.ImageModel, &info.Status); err != nil {
log.Error("Pendants scan error %v", err)
return
}
res = append(res, info)
}
return
}
// PendantInfo return pendant info by id
func (d *Dao) PendantInfo(c context.Context, pid int64) (res *model.Pendant, err error) {
var (
row *xsql.Row
)
res = new(model.Pendant)
row = d.db.QueryRow(c, _getPendantInfoByIDSQL, pid)
if err = row.Scan(&res.ID, &res.Name, &res.Image, &res.ImageModel, &res.Status); err != nil {
if err == xsql.ErrNoRows {
res = nil
err = nil
return
}
log.Error("Pendant scan error %v", err)
return
}
return
}
// PendantPrice return pendant price
func (d *Dao) PendantPrice(c context.Context, pid int64) (res map[int64]*model.PendantPrice, err error) {
var row *xsql.Rows
res = make(map[int64]*model.PendantPrice)
if row, err = d.db.Query(c, _getPendantPriceSQL, pid); err != nil {
log.Error("PendantPrice query error %v", err)
return
}
defer row.Close()
for row.Next() {
info := new(model.PendantPrice)
if err = row.Scan(&info.Pid, &info.Type, &info.Price); err != nil {
log.Error("PendantPrice scan error %v", err)
return
}
res[info.Type] = info
}
return
}
// getOrderInfoSQL return a sql string
func (d *Dao) getOrderInfoSQL(c context.Context, arg *model.ArgOrderHistory, tp string) (sql string, values []interface{}) {
values = make([]interface{}, 0, 5)
var cond bytes.Buffer
cond.WriteString("mid = ?")
values = append(values, arg.Mid)
if arg.OrderID != "" {
cond.WriteString(" AND order_id = ?")
values = append(values, arg.OrderID)
}
if arg.Pid != 0 {
cond.WriteString(" AND pid = ?")
values = append(values, arg.Pid)
}
if arg.Status != 0 {
cond.WriteString(" AND status = ?")
values = append(values, arg.Status)
}
if arg.PayType != 0 {
cond.WriteString(" AND pay_type = ?")
values = append(values, arg.PayType)
}
if arg.PayID != "" {
cond.WriteString(" AND pay_id = ?")
values = append(values, arg.PayID)
}
if arg.StartTime != 0 {
cond.WriteString(" AND buy_time >= ?")
values = append(values, arg.StartTime)
}
if arg.EndTime != 0 {
cond.WriteString(" AND buy_time <= ?")
values = append(values, arg.EndTime)
}
if tp == "info" {
cond.WriteString(" order by buy_time DESC LIMIT ?,20")
values = append(values, (arg.Page-1)*20)
sql = fmt.Sprintf(_getOrderHistorySQL, cond.String())
} else if tp == "count" {
sql = fmt.Sprintf(_countOrderHistorySQL, cond.String())
}
return
}
// OrderInfo return order info
func (d *Dao) OrderInfo(c context.Context, arg *model.ArgOrderHistory) (res []*model.PendantOrderInfo, count int64, err error) {
sqlstr, values := d.getOrderInfoSQL(c, arg, "info")
var (
row *xsql.Rows
r *xsql.Row
)
res = make([]*model.PendantOrderInfo, 0)
if row, err = d.db.Query(c, sqlstr, values...); err != nil {
log.Error("PendantOrderInfo query error %v", err)
return
}
defer row.Close()
cstr, values2 := d.getOrderInfoSQL(c, arg, "count")
r = d.db.QueryRow(c, cstr, values2...)
for row.Next() {
info := new(model.PendantOrderInfo)
if err = row.Scan(&info.Mid, &info.OrderID, &info.PayID, &info.AppID, &info.Stauts, &info.Pid, &info.TimeLength, &info.Cost, &info.BuyTime, &info.IsCallback, &info.CallbackTime, &info.PayType); err != nil {
log.Error("PendantOrderInfo scan error %v", err)
return
}
if info.PayType == 3 {
info.PayPrice = info.PayPrice / 100
}
res = append(res, info)
}
err = r.Scan(&count)
if err == xsql.ErrNoRows {
res = nil
err = nil
return
}
return
}
// OrderInfoByID return order info by order id
func (d *Dao) OrderInfoByID(c context.Context, orderID string) (res *model.PendantOrderInfo, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_getOrderHistorySQL, "order_id=?"), orderID)
res = new(model.PendantOrderInfo)
if err = row.Scan(&res.Mid, &res.OrderID, &res.PayID, &res.AppID, &res.Stauts, &res.Pid, &res.TimeLength, &res.Cost, &res.BuyTime, &res.IsCallback, &res.CallbackTime, &res.PayType); err != nil {
if err == xsql.ErrNoRows {
res = nil
err = nil
return
}
log.Error("OrderInfoByID scan error %v", err)
return
}
return
}
// AddOrderInfo add order log
func (d *Dao) AddOrderInfo(c context.Context, arg *model.PendantOrderInfo) (id int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _insertOrderHistory, arg.Mid, arg.OrderID, arg.PayID, arg.AppID, arg.Stauts, arg.Pid, arg.TimeLength, arg.Cost, arg.BuyTime, arg.IsCallback, arg.CallbackTime, arg.PayType); err != nil {
log.Error("AddOrderInfo insert error %v", err)
return
}
return res.LastInsertId()
}
// TxAddOrderInfo add order log
func (d *Dao) TxAddOrderInfo(c context.Context, arg *model.PendantOrderInfo, tx *xsql.Tx) (id int64, err error) {
var res sql.Result
if res, err = tx.Exec(_insertOrderHistory, arg.Mid, arg.OrderID, arg.PayID, arg.AppID, arg.Stauts, arg.Pid, arg.TimeLength, arg.Cost, arg.BuyTime, arg.IsCallback, arg.CallbackTime, arg.PayType); err != nil {
log.Error("TxAddOrderInfo insert error %v", err)
return
}
return res.LastInsertId()
}
// UpdateOrderInfo update order info
func (d *Dao) UpdateOrderInfo(c context.Context, arg *model.PendantOrderInfo) (id int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _updateOrderInfoSQL, arg.Stauts, arg.PayID, arg.IsCallback, arg.CallbackTime, arg.OrderID); err != nil {
log.Error("UpdateOrderInfo update error %v", err)
return
}
return res.LastInsertId()
}
// TxUpdateOrderInfo update order info
func (d *Dao) TxUpdateOrderInfo(c context.Context, arg *model.PendantOrderInfo, tx *xsql.Tx) (id int64, err error) {
var res sql.Result
if res, err = tx.Exec(_updateOrderInfoSQL, arg.Stauts, arg.PayID, arg.IsCallback, arg.CallbackTime, arg.OrderID); err != nil {
log.Error("UpdateOrderInfo update error %v", err)
return
}
return res.LastInsertId()
}
// PackageByMid get pendant in user's package
func (d *Dao) PackageByMid(c context.Context, mid int64) (res []*model.PendantPackage, err error) {
var (
row *xsql.Rows
t = time.Now().Unix()
)
res = make([]*model.PendantPackage, 0)
if row, err = d.db.Query(c, _getUserPackageByMidSQL, mid, t); err != nil {
log.Error("Package query error %v", err)
return
}
defer row.Close()
for row.Next() {
info := new(model.PendantPackage)
if err = row.Scan(&info.Mid, &info.Pid, &info.Expires, &info.Type, &info.Status, &info.IsVIP); err != nil {
log.Error("Package scan error %v", err)
return
}
res = append(res, info)
}
return
}
// PackageByID get pendant in user's package
func (d *Dao) PackageByID(c context.Context, mid, pid int64) (res *model.PendantPackage, err error) {
var row *xsql.Row
res = new(model.PendantPackage)
row = d.db.QueryRow(c, _getUserPackageSQL, mid, pid)
if err = row.Scan(&res.Mid, &res.Pid, &res.Expires, &res.Type, &res.Status, &res.IsVIP); err != nil {
if err == xsql.ErrNoRows {
res = nil
err = nil
return
}
log.Error("Package scan error %v", err)
return
}
return
}
// EquipByMid obtain pendant equiped
func (d *Dao) EquipByMid(c context.Context, mid, t int64) (res *model.PendantEquip, noRow bool, err error) {
var row *xsql.Row
res = new(model.PendantEquip)
row = d.db.QueryRow(c, _getPendantEquipByMidSQL, mid, t)
if err = row.Scan(&res.Mid, &res.Pid, &res.Expires); err != nil {
if err == xsql.ErrNoRows {
noRow = true
res = nil
err = nil
return
}
err = errors.WithStack(err)
}
return
}
// EquipByMids obtain equipss by mids .
func (d *Dao) EquipByMids(c context.Context, mids []int64, t int64) (res map[int64]*model.PendantEquip, err error) {
res = make(map[int64]*model.PendantEquip)
rows, err := d.db.Query(c, fmt.Sprintf(_getPendantEquipByMidsSQL, xstr.JoinInts(mids)), t)
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
pe := &model.PendantEquip{}
if err = rows.Scan(&pe.Mid, &pe.Pid, &pe.Expires); err != nil {
if err == xsql.ErrNoRows {
err = nil
return
}
err = errors.WithStack(err)
}
if _, ok := res[pe.Mid]; !ok {
res[pe.Mid] = pe
}
}
err = rows.Err()
return
}
// AddEquip add equip
func (d *Dao) AddEquip(c context.Context, arg *model.PendantEquip) (n int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _insertEquipSQL, arg.Mid, arg.Pid, arg.Expires); err != nil {
return 0, err
}
return res.RowsAffected()
}
// UpEquipMID uninstall user pid by mid.
func (d *Dao) UpEquipMID(c context.Context, mid int64) (n int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _updateEquipMIDSQL, mid); err != nil {
return 0, err
}
return res.RowsAffected()
}
// TxUpdatePackageInfo update package info
func (d *Dao) TxUpdatePackageInfo(c context.Context, arg *model.PendantPackage, tx *xsql.Tx) (n int64, err error) {
var (
bf bytes.Buffer
values = make([]interface{}, 0, 4)
res sql.Result
)
if arg.Status != 0 && arg.Expires != 0 {
bf.WriteString("SET status=?,expires=?,type=?")
values = append(values, arg.Status)
values = append(values, arg.Expires)
values = append(values, arg.Type)
} else if arg.Status != 0 {
bf.WriteString("SET status=?,type=?")
values = append(values, arg.Status)
values = append(values, arg.Type)
} else if arg.Expires != 0 {
bf.WriteString("SET expires=?,type=?")
values = append(values, arg.Expires)
values = append(values, arg.Type)
}
values = append(values, arg.Mid)
values = append(values, arg.Pid)
if res, err = tx.Exec(fmt.Sprintf(_updatePackageSQL, bf.String()), values...); err != nil {
log.Error("TxUpdatePackageInfo update error %v", err)
return
}
return res.RowsAffected()
}
// CheckPackageExpire check expire items and update
func (d *Dao) CheckPackageExpire(c context.Context, mid, expires int64) (rows int64, err error) {
var res sql.Result
if res, err = d.db.Exec(c, _updatePackageExpireSQL, mid, expires); err != nil {
log.Error("CheckPackageExpire error %v", err)
return
}
return res.RowsAffected()
}
// BeginTran begin a tx.
func (d *Dao) BeginTran(c context.Context) (res *xsql.Tx, err error) {
if res, err = d.db.Begin(c); err != nil || res == nil {
log.Error("BeginTran error %v", err)
return
}
return
}
// TxAddPackage add a pendant in package
func (d *Dao) TxAddPackage(c context.Context, arg *model.PendantPackage, tx *xsql.Tx) (id int64, err error) {
var res sql.Result
if res, err = tx.Exec(_insertPendantPackageSQL, arg.Mid, arg.Pid, arg.Expires, arg.Type, arg.Status, arg.IsVIP); err != nil {
log.Error("TxAddPackage insert error %v", err)
return
}
return res.LastInsertId()
}
// TxAddHistory add a history of operation
func (d *Dao) TxAddHistory(c context.Context, arg *model.PendantHistory, tx *xsql.Tx) (id int64, err error) {
var res sql.Result
if res, err = tx.Exec(_insertOperationSQL, arg.Mid, arg.Pid, arg.SourceType, arg.OperatorName, arg.OperatorAction); err != nil {
log.Error("TxAddHistory insert error %v", err)
return
}
return res.LastInsertId()
}

View File

@@ -0,0 +1,372 @@
package pendant
import (
"strconv"
"testing"
"time"
"go-common/app/service/main/usersuit/model"
"github.com/smartystreets/goconvey/convey"
)
func TestPendantPendantGroupInfo(t *testing.T) {
convey.Convey("PendantGroupInfo", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PendantGroupInfo(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantGroupByID(t *testing.T) {
convey.Convey("GroupByID", t, func(ctx convey.C) {
var (
gid = int64(4)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.GroupByID(c, gid)
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 TestPendantGIDRefPID(t *testing.T) {
convey.Convey("GIDRefPID", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
gidMap, pidMap, err := d.GIDRefPID(c)
ctx.Convey("Then err should be nil.gidMap,pidMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(pidMap, convey.ShouldNotBeNil)
ctx.So(gidMap, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantPendantList(t *testing.T) {
convey.Convey("PendantList", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PendantList(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantPendants(t *testing.T) {
convey.Convey("Pendants", t, func(ctx convey.C) {
var (
pids = []int64{4}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Pendants(c, pids)
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 TestPendantPendantInfo(t *testing.T) {
convey.Convey("PendantInfo", t, func(ctx convey.C) {
var (
pid = int64(4)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PendantInfo(c, pid)
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 TestPendantPendantPrice(t *testing.T) {
convey.Convey("PendantPrice", t, func(ctx convey.C) {
var (
pid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PendantPrice(c, pid)
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 TestPendantgetOrderInfoSQL(t *testing.T) {
convey.Convey("getOrderInfoSQL", t, func(ctx convey.C) {
var (
arg = &model.ArgOrderHistory{}
tp = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
sql, values := d.getOrderInfoSQL(c, arg, tp)
ctx.Convey("Then sql,values should not be nil.", func(ctx convey.C) {
ctx.So(values, convey.ShouldNotBeNil)
ctx.So(sql, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantOrderInfo(t *testing.T) {
convey.Convey("OrderInfo", t, func(ctx convey.C) {
var (
arg = &model.ArgOrderHistory{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, count, err := d.OrderInfo(c, arg)
ctx.Convey("Then err should be nil.res,count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantOrderInfoByID(t *testing.T) {
convey.Convey("OrderInfoByID", t, func(ctx convey.C) {
var (
orderID = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.OrderInfoByID(c, orderID)
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 TestPendantAddOrderInfo(t *testing.T) {
convey.Convey("AddOrderInfo", t, func(ctx convey.C) {
var (
arg = &model.PendantOrderInfo{Mid: 650454, OrderID: strconv.FormatInt(time.Now().Unix(), 10)}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.AddOrderInfo(c, arg)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantTxAddOrderInfo(t *testing.T) {
convey.Convey("TxAddOrderInfo", t, func(ctx convey.C) {
var (
arg = &model.PendantOrderInfo{Mid: 650454, OrderID: strconv.FormatInt(time.Now().UnixNano(), 10)}
tx, _ = d.BeginTran(c)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.TxAddOrderInfo(c, arg, tx)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantUpdateOrderInfo(t *testing.T) {
convey.Convey("UpdateOrderInfo", t, func(ctx convey.C) {
var (
arg = &model.PendantOrderInfo{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.UpdateOrderInfo(c, arg)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantTxUpdateOrderInfo(t *testing.T) {
convey.Convey("TxUpdateOrderInfo", t, func(ctx convey.C) {
var (
arg = &model.PendantOrderInfo{}
tx, _ = d.BeginTran(c)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.TxUpdateOrderInfo(c, arg, tx)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantPackageByMid(t *testing.T) {
convey.Convey("PackageByMid", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PackageByMid(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantPackageByID(t *testing.T) {
convey.Convey("PackageByID", t, func(ctx convey.C) {
var (
mid = int64(650454)
pid = int64(21)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.PackageByID(c, mid, pid)
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 TestPendantEquipByMid(t *testing.T) {
convey.Convey("EquipByMid", t, func(ctx convey.C) {
var (
mid = int64(88888929)
no = int64(44)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, noRow, err := d.EquipByMid(c, mid, no)
ctx.Convey("Then err should be nil.res,noRow should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(noRow, convey.ShouldNotBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantEquipByMids(t *testing.T) {
convey.Convey("EquipByMids", t, func(ctx convey.C) {
var (
mids = []int64{650454}
no = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.EquipByMids(c, mids, no)
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 TestPendantAddEquip(t *testing.T) {
convey.Convey("AddEquip", t, func(ctx convey.C) {
var (
arg = &model.PendantEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
n, err := d.AddEquip(c, arg)
ctx.Convey("Then err should be nil.n should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(n, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantTxUpdatePackageInfo(t *testing.T) {
convey.Convey("TxUpdatePackageInfo", t, func(ctx convey.C) {
var (
arg = &model.PendantPackage{Mid: 88888929, Pid: 2, Status: 1}
tx, _ = d.BeginTran(c)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
n, err := d.TxUpdatePackageInfo(c, arg, tx)
ctx.Convey("Then err should be nil.n should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(n, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantCheckPackageExpire(t *testing.T) {
convey.Convey("CheckPackageExpire", t, func(ctx convey.C) {
var (
mid = int64(650454)
expires = int64(2147483647)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.CheckPackageExpire(c, mid, expires)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantBeginTran(t *testing.T) {
convey.Convey("BeginTran", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.BeginTran(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantTxAddPackage(t *testing.T) {
convey.Convey("TxAddPackage", t, func(ctx convey.C) {
var (
arg = &model.PendantPackage{Mid: time.Now().Unix(), Pid: 4}
tx, _ = d.BeginTran(c)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.TxAddPackage(c, arg, tx)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantTxAddHistory(t *testing.T) {
convey.Convey("TxAddHistory", t, func(ctx convey.C) {
var (
arg = &model.PendantHistory{}
tx, _ = d.BeginTran(c)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.TxAddHistory(c, arg, tx)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,31 @@
package pendant
import (
"context"
"net/url"
"go-common/library/ecode"
"go-common/library/log"
)
// PayBcoin pay coin
func (d *Dao) PayBcoin(c context.Context, params url.Values, ip string) (orderNo, casherURL string, err error) {
var res struct {
Code int `json:"code"`
Ts string `json:"ts"`
OrderNum string `json:"order_no"`
CasherURL string `json:"cashier_url"`
}
if err = d.client.Post(c, d.payURL, ip, params, &res); err != nil {
log.Error("dao.client.Post(%s) error(%v)", d.payURL, err)
return
}
if res.Code != 0 {
log.Error("dao.client.Post(%s) error(%v)", d.payURL, res)
err = ecode.Int(res.Code)
return
}
orderNo = res.OrderNum
casherURL = res.CasherURL
return
}

View File

@@ -0,0 +1,41 @@
package pendant
import (
"fmt"
"net/url"
"strconv"
"testing"
"github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
func TestPendantPayBcoin(t *testing.T) {
convey.Convey("PayBcoin", t, func(ctx convey.C) {
var (
params = url.Values{}
ip = ""
_subject = "头像挂件"
)
params.Set("mid", "109228")
params.Set("out_trade_no", "2016050614625209018624230766")
params.Set("money", strconv.FormatFloat(2, 'f', 2, 64))
params.Set("subject", _subject)
params.Set("remark", fmt.Sprintf(_subject+" - %s%s个月", strconv.FormatInt(4, 10), strconv.FormatInt(1234, 10)))
params.Set("merchant_id", d.c.PayInfo.MerchantID)
params.Set("merchant_product_id", d.c.PayInfo.MerchantProductID)
params.Set("platform_type", "3")
params.Set("iap_pay_type", "0")
params.Set("notify_url", d.c.PayInfo.CallBackURL)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
defer gock.OffAll()
httpMock("Post", d.payURL).Reply(0).JSON(`{"code":0}`)
orderNo, casherURL, err := d.PayBcoin(c, params, ip)
ctx.Convey("Then err should be nil.orderNo,casherURL should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(casherURL, convey.ShouldNotBeNil)
ctx.So(orderNo, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,286 @@
package pendant
import (
"context"
"strconv"
"encoding/json"
"go-common/app/service/main/usersuit/model"
"go-common/library/cache/redis"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_pendantPKG = "pkg_" // key of
_pendantEquip = "pe_"
)
func keyEquip(mid int64) string {
return _pendantEquip + strconv.FormatInt(mid, 10)
}
// encode
func (d *Dao) encode(mid, pid, expires, tp int64, status, isVIP int32, pendant *model.Pendant) (res []byte, err error) {
ft := &model.PendantPackage{Mid: mid, Pid: pid, Expires: expires, Type: tp, Status: status, IsVIP: isVIP, Pendant: pendant}
return json.Marshal(ft)
}
// decode
func (d *Dao) decode(src []byte, v *model.PendantPackage) (err error) {
return json.Unmarshal(src, v)
}
// AddPKGCache set package cache.
func (d *Dao) AddPKGCache(c context.Context, mid int64, info []*model.PendantPackage) (err error) {
var (
key = _pendantPKG + strconv.FormatInt(mid, 10)
args = redis.Args{}.Add(key)
)
for i := 0; i < len(info); i++ {
var ef []byte
if ef, err = d.encode(info[i].Mid, info[i].Pid, info[i].Expires, info[i].Type, info[i].Status, info[i].IsVIP, info[i].Pendant); err != nil {
return
}
args = args.Add(i, ef)
}
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("DEL", key); err != nil {
log.Error("conn.Send(DEL, %s) error(%v)", key, err)
return
}
if err = conn.Send("HMSET", args...); err != nil {
log.Error("conn.Send(HMSET, %s) error(%v)", key, err)
return
}
if err = conn.Send("EXPIRE", key, d.pendantExpire); err != nil {
log.Error("conn.Send(EXPIRE, %s) error(%v)", key, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() error(%v)", err)
return
}
for i := 0; i < 3; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() %d error(%v)", i+1, err)
break
}
}
return
}
// PKGCache get package cache.
func (d *Dao) PKGCache(c context.Context, mid int64) (info []*model.PendantPackage, err error) {
var (
key = _pendantPKG + strconv.FormatInt(mid, 10)
tmp = make(map[string]string, len(info))
)
conn := d.redis.Get(c)
defer conn.Close()
if tmp, err = redis.StringMap(conn.Do("HGETALL", key)); err != nil {
return
}
if err == nil && len(tmp) > 0 {
for i := 0; i < len(tmp); i++ {
s := strconv.FormatInt(int64(i), 10)
vf := &model.PendantPackage{}
vf.Pendant = &model.Pendant{}
if err = d.decode([]byte(tmp[s]), vf); err != nil {
return
}
info = append(info, &model.PendantPackage{
Mid: vf.Mid,
Pid: vf.Pid,
Expires: vf.Expires,
Type: vf.Type,
Status: vf.Status,
IsVIP: vf.IsVIP,
Pendant: vf.Pendant,
})
}
}
return
}
// DelPKGCache del package cache
func (d *Dao) DelPKGCache(c context.Context, mid int64) (err error) {
key := _pendantPKG + strconv.FormatInt(mid, 10)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("DEL", key); err != nil {
log.Error("conn.Send(DEL, %s) error(%v)", key, err)
return
}
return
}
// equipCache return pendant info cache
func (d *Dao) equipCache(c context.Context, mid int64) (info *model.PendantEquip, err error) {
var (
item []byte
conn = d.redis.Get(c)
)
defer conn.Close()
if item, err = redis.Bytes(conn.Do("GET", keyEquip(mid))); err != nil {
if err == redis.ErrNil {
err = nil
}
return
}
if err = json.Unmarshal(item, &info); err != nil {
log.Error("json.Unmarshal(%v) err(%v)", item, err)
}
return
}
// equipsCache obtain equips from redis .
func (d *Dao) equipsCache(c context.Context, mids []int64) (map[int64]*model.PendantEquip, []int64, error) {
var (
err error
bss [][]byte
key string
args = redis.Args{}
conn = d.redis.Get(c)
)
for _, v := range mids {
key = keyEquip(v)
args = args.Add(key)
}
defer conn.Close()
if bss, err = redis.ByteSlices(conn.Do("MGET", args...)); err != nil {
if err == redis.ErrNil {
return nil, nil, nil
}
log.Error("Failed mget equip: keys: %+v: %+v", args, err)
return nil, nil, err
}
info := make(map[int64]*model.PendantEquip, len(mids))
for _, bs := range bss {
if bs == nil {
continue
}
pe := &model.PendantEquip{}
if err = json.Unmarshal(bs, pe); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", string(bs), err)
err = nil
continue
}
info[pe.Mid] = pe
}
missed := make([]int64, 0, len(mids))
for _, mid := range mids {
if _, ok := info[mid]; !ok {
missed = append(missed, mid)
}
}
return info, missed, nil
}
// AddEquipCache set pendant info cache
func (d *Dao) AddEquipCache(c context.Context, mid int64, info *model.PendantEquip) (err error) {
var (
key = keyEquip(mid)
values []byte
conn = d.redis.Get(c)
)
defer conn.Close()
if values, err = json.Marshal(info); err != nil {
return
}
if err = conn.Send("SET", keyEquip(mid), values); err != nil {
log.Error("conn.Send(SET, %s, %d) error(%v)", key, values, err)
return
}
if err = conn.Send("EXPIRE", key, d.pendantExpire); err != nil {
log.Error("conn.Send(Expire, %s, %d) error(%v)", key, d.pendantExpire, err)
return
}
if err = conn.Flush(); err != nil {
err = errors.Wrap(err, "conn.Send Flush")
return
}
for i := 0; i < 2; i++ {
if _, err = conn.Receive(); err != nil {
err = errors.Wrap(err, "conn.Send conn.Receive()")
return
}
}
return
}
// AddEquipsCache mset equips info to caache .
func (d *Dao) AddEquipsCache(c context.Context, equips map[int64]*model.PendantEquip) (err error) {
var (
bs []byte
key string
keys []string
argsMid = redis.Args{}
conn = d.redis.Get(c)
)
defer conn.Close()
for _, v := range equips {
if bs, err = json.Marshal(v); err != nil {
log.Error("json.Marshal err(%v)", err)
continue
}
key = keyEquip(v.Mid)
keys = append(keys, key)
argsMid = argsMid.Add(key).Add(string(bs))
}
if err = conn.Send("MSET", argsMid...); err != nil {
err = errors.Wrap(err, "conn.Send(MSET) error")
return
}
count := 1
for _, v := range keys {
count++
if err = conn.Send("EXPIRE", v, d.pendantExpire); err != nil {
err = errors.Wrap(err, "conn.Send error")
return
}
}
if err = conn.Flush(); err != nil {
err = errors.Wrap(err, "conn.Send Flush")
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
err = errors.Wrap(err, "conn.Send conn.Receive()")
return
}
}
return
}
// DelEquipCache set pendant info cache
func (d *Dao) DelEquipCache(c context.Context, mid int64) (err error) {
key := keyEquip(mid)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("DEL", key); err != nil {
log.Error("conn.Do(DEL, %s) error(%v)", key, err)
}
return
}
// DelEquipsCache del batch equip cache .
func (d *Dao) DelEquipsCache(c context.Context, mids []int64) (err error) {
var (
args = redis.Args{}
conn = d.redis.Get(c)
)
defer conn.Close()
for _, v := range mids {
args = args.Add(keyEquip(v))
}
if _, err = conn.Do("DEL", args...); err != nil {
log.Error("conn.Do(DEL, %s) error(%v)", args, err)
}
return
}

View File

@@ -0,0 +1,180 @@
package pendant
import (
"fmt"
"go-common/app/service/main/usersuit/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestPendantkeyEquip(t *testing.T) {
convey.Convey("keyEquip", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyEquip(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantencode(t *testing.T) {
convey.Convey("encode", t, func(ctx convey.C) {
var (
mid = int64(650454)
pid = int64(1)
expires = int64(1535970125)
tp = int64(0)
status = int32(1)
isVIP = int32(1)
pendant = &model.Pendant{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.encode(mid, pid, expires, tp, status, isVIP, pendant)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
fmt.Println(string(res))
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantdecode(t *testing.T) {
convey.Convey("decode", t, func(ctx convey.C) {
var (
src = []byte("\u007b\u0022\u0069\u0064\u0022\u003a\u0030\u002c\u0022\u006d\u0069\u0064\u0022\u003a\u0036\u0035\u0030\u0034\u0035\u0034\u002c\u0022\u0070\u0069\u0064\u0022\u003a\u0031\u002c\u0022\u0065\u0078\u0070\u0069\u0072\u0065\u0022\u003a\u0031\u0035\u0033\u0035\u0039\u0037\u0030\u0031\u0032\u0035\u002c\u0022\u0074\u0079\u0070\u0065\u0022\u003a\u0030\u002c\u0022\u0073\u0074\u0061\u0074\u0075\u0073\u0022\u003a\u0031\u002c\u0022\u0069\u0073\u0056\u0049\u0050\u0022\u003a\u0031\u002c\u0022\u0070\u0065\u006e\u0064\u0061\u006e\u0074\u0022\u003a\u007b\u0022\u0070\u0069\u0064\u0022\u003a\u0030\u002c\u0022\u006e\u0061\u006d\u0065\u0022\u003a\u0022\u0022\u002c\u0022\u0069\u006d\u0061\u0067\u0065\u0022\u003a\u0022\u0022\u002c\u0022\u0069\u006d\u0061\u0067\u0065\u005f\u006d\u006f\u0064\u0065\u006c\u0022\u003a\u0022\u0022\u002c\u0022\u0073\u0074\u0061\u0074\u0075\u0073\u0022\u003a\u0030\u002c\u0022\u0063\u006f\u0069\u006e\u0022\u003a\u0030\u002c\u0022\u0070\u006f\u0069\u006e\u0074\u0022\u003a\u0030\u002c\u0022\u0062\u0063\u006f\u0069\u006e\u0022\u003a\u0030\u002c\u0022\u0065\u0078\u0070\u0069\u0072\u0065\u0022\u003a\u0030\u002c\u0022\u0067\u0069\u0064\u0022\u003a\u0030\u002c\u0022\u0072\u0061\u006e\u006b\u0022\u003a\u0030\u007d\u007d")
v = &model.PendantPackage{Mid: 650454, Pid: 1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.decode(src, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantAddPKGCache(t *testing.T) {
convey.Convey("AddPKGCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
info = []*model.PendantPackage{}
pp = &model.PendantPackage{Mid: mid, Pid: int64(1)}
)
info = append(info, pp)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddPKGCache(c, mid, info)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantPKGCache(t *testing.T) {
convey.Convey("PKGCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
info, err := d.PKGCache(c, mid)
ctx.Convey("Then err should be nil.info should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(info, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantDelPKGCache(t *testing.T) {
convey.Convey("DelPKGCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelPKGCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantAddEquipCache(t *testing.T) {
convey.Convey("AddEquipCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
info = &model.PendantEquip{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddEquipCache(c, mid, info)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantequipCache(t *testing.T) {
convey.Convey("equipCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
info, err := d.equipCache(c, mid)
ctx.Convey("Then err should be nil.info should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(info, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantDelEquipCache(t *testing.T) {
convey.Convey("DelEquipCache", t, func(ctx convey.C) {
var (
mid = int64(650454)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelEquipCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestPendantequipsCache(t *testing.T) {
convey.Convey("equipsCache", t, func(ctx convey.C) {
var (
mids = []int64{650454}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.equipsCache(c, mids)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestPendantDelEquipsCache(t *testing.T) {
convey.Convey("DelEquipsCache", t, func(ctx convey.C) {
var (
mids = []int64{650454, 1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelEquipsCache(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,28 @@
package pendant
import (
"context"
"strconv"
"go-common/app/service/main/usersuit/model"
"go-common/library/ecode"
"github.com/pkg/errors"
)
// VipInfo get identify info by calling api.
func (d *Dao) VipInfo(c context.Context, mid int64, ip string) (idt *model.VipInfo, err error) {
var res struct {
Code int
Data *model.VipInfo
}
if err = d.client.Get(c, d.vipInfoURL+strconv.FormatInt(mid, 10), ip, nil, &res); err != nil {
return
}
if res.Code != 0 {
err = errors.WithStack(ecode.Int(res.Code))
return
}
idt = res.Data
return
}

View File

@@ -0,0 +1,23 @@
package pendant
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestPendantVipInfo(t *testing.T) {
convey.Convey("VipInfo", t, func(ctx convey.C) {
var (
mid = int64(650454)
ip = "127.0.0.1"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
idt, err := d.VipInfo(c, mid, ip)
ctx.Convey("Then err should be nil.idt should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(idt, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"http.go",
"medal.go",
"pendant.go",
],
importpath = "go-common/app/service/main/usersuit/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//app/service/main/usersuit/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/metadata: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,79 @@
package http
import (
"net/http"
"go-common/app/service/main/usersuit/conf"
"go-common/app/service/main/usersuit/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
usersuitSvc *service.Service
vfySvc *verify.Verify
)
// Init init http sever instance.
func Init(c *conf.Config, s *service.Service) {
usersuitSvc = s
vfySvc = verify.New(c.Verify)
// init inner router
innerEngine := bm.DefaultServer(c.BM)
innerRouter(innerEngine)
// init inner server
if err := innerEngine.Start(); err != nil {
log.Error("xhttp.Serve inner error(%v)", err)
panic(err)
}
}
// innerRouter init inner router.
func innerRouter(e *bm.Engine) {
// health check
e.Ping(ping)
e.Register(register)
group := e.Group("/x/internal/pendant", vfySvc.Verify)
{
group.GET("/groupInfo", groupInfo)
group.GET("/groupInfoByID", groupInfoByID)
group.GET("/pendantByID", pendantByID)
group.GET("/vipGroup", vipGroup)
group.GET("/entryGroup", entryGroup)
group.GET("/pointRecommend", pointRecommend)
group.GET("/package", packageInfo)
group.GET("/equipment", equipment)
group.GET("/orderHistory", orderHistory)
group.POST("/order", order)
group.POST("/equip", equip)
group.POST("/multiGrantByMid", multiGrantByMid)
group.POST("/multiGrantByPid", multiGrantByPid)
}
e.POST("/x/internal/pendant/callback", pendantCallback)
medal := e.Group("/x/internal/medal", vfySvc.Verify)
{
medal.GET("/my", medalMy)
medal.GET("/all", medalAllInfo)
medal.GET("/info", medalInfo)
medal.GET("/popup", medalPopup)
medal.GET("/user", medalUser)
medal.GET("/check", medalCheck)
medal.GET("/activated", medalActivated)
medal.POST("/install", medalInstall)
medal.POST("/grant", medalGet)
}
}
// ping check server ok.
func ping(c *bm.Context) {
if err := usersuitSvc.Ping(c); err != nil {
log.Error("usersuit-service service ping error (%+v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
// register check server ok.
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}

View File

@@ -0,0 +1,120 @@
package http
import (
"go-common/app/service/main/usersuit/model"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
)
func medalInfo(c *bm.Context) {
var (
err error
info *model.MedalInfo
arg = new(model.ArgMIDNID)
)
if err = c.Bind(arg); err != nil {
return
}
if info, err = usersuitSvc.MedalInfo(c, arg.MID, arg.NID); err != nil {
c.JSON(nil, err)
return
}
c.JSON(info, nil)
}
func medalGet(c *bm.Context) {
var (
err error
arg = new(model.ArgMIDNID)
)
if err = c.Bind(arg); err != nil {
return
}
if err = usersuitSvc.MedalGet(c, arg.MID, arg.NID); err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func medalCheck(c *bm.Context) {
var (
err error
info *model.MedalCheck
arg = new(model.ArgMIDNID)
)
if err = c.Bind(arg); err != nil {
return
}
if info, err = usersuitSvc.MedalCheck(c, arg.MID, arg.NID); err != nil {
c.JSON(nil, err)
return
}
c.JSON(info, nil)
}
func medalActivated(c *bm.Context) {
var (
err error
arg = new(model.ArgMID)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(usersuitSvc.MedalActivated(c, arg.MID))
}
func medalMy(c *bm.Context) {
var (
err error
arg = new(model.ArgMID)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(usersuitSvc.MedalMyInfo(c, arg.MID))
}
func medalAllInfo(c *bm.Context) {
var (
err error
arg = new(model.ArgMID)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(usersuitSvc.MedalAllInfo(c, arg.MID))
}
func medalPopup(c *bm.Context) {
var (
err error
arg = new(model.ArgMID)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(usersuitSvc.MedalPopup(c, arg.MID))
}
func medalInstall(c *bm.Context) {
var (
err error
arg = new(model.ArgMedalInstall)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(nil, usersuitSvc.MedalInstall(c, arg.Mid, arg.Nid, arg.IsActivated))
}
func medalUser(c *bm.Context) {
var (
err error
arg = new(model.ArgMID)
)
if err = c.Bind(arg); err != nil {
return
}
c.JSON(usersuitSvc.MedalUserInfo(c, arg.MID, metadata.String(c, metadata.RemoteIP)))
}

View File

@@ -0,0 +1,399 @@
package http
import (
"strconv"
"go-common/app/service/main/usersuit/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
const (
_paySuccess = "1"
)
// groupInfo get group info
func groupInfo(c *bm.Context) {
c.JSON(usersuitSvc.GroupInfo(c))
}
// groupInfoByID get group info
func groupInfoByID(c *bm.Context) {
var (
err error
gid int64
params = c.Request.Form
gidstr = params.Get("gid")
)
if gidstr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if gid, err = strconv.ParseInt(gidstr, 10, 64); err != nil || gid < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(usersuitSvc.GroupInfoByID(c, gid))
}
// vipGroup return vip group info
func vipGroup(c *bm.Context) {
c.JSON(usersuitSvc.GroupInfoByID(c, 31))
}
// entryGroup return vip group info
func entryGroup(c *bm.Context) {
c.JSON(usersuitSvc.GroupInfoByID(c, 30))
}
// pointRecommend get pendant which has point pay type
func pointRecommend(c *bm.Context) {
var (
err error
mid int64
params = c.Request.Form
midstr = params.Get("mid")
)
if midstr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midstr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(usersuitSvc.PendantPoint(c, mid))
}
// pendantByID return pendant info
func pendantByID(c *bm.Context) {
var (
err error
pid int64
params = c.Request.Form
pidstr = params.Get("pid")
)
if pidstr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if pid, err = strconv.ParseInt(pidstr, 10, 64); err != nil || pid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(usersuitSvc.PendantInfo(c, pid))
}
// orderHistory query order history
func orderHistory(c *bm.Context) {
var (
err error
t int64
history *model.ArgOrderHistory
data []*model.PendantOrderInfo
count map[string]int64
req = c.Request
params = req.Form
midStr = params.Get("mid")
pidStr = params.Get("pid")
orderID = params.Get("orderID")
payID = params.Get("payID")
payType = params.Get("payType")
status = params.Get("status")
startTime = params.Get("start_time")
endTime = params.Get("end_time")
page = params.Get("page")
)
if midStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
history = new(model.ArgOrderHistory)
if history.Mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || history.Mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if pidStr != "" {
if t, err = strconv.ParseInt(pidStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
history.Pid = t
}
if orderID != "" {
history.OrderID = orderID
}
if payID != "" {
history.PayID = payID
}
if payType != "" {
if t, err = strconv.ParseInt(payType, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
history.PayType = int32(t)
}
if status != "" {
if t, err = strconv.ParseInt(status, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
history.Status = int32(t)
}
if startTime != "" {
if t, err = strconv.ParseInt(startTime, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
history.StartTime = t
}
if endTime != "" {
if t, err = strconv.ParseInt(endTime, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
history.EndTime = t
}
if page != "" {
if t, err = strconv.ParseInt(page, 10, 64); err != nil {
history.Page = 1
}
history.Page = t
} else {
history.Page = 1
}
if data, count, err = usersuitSvc.OrderHistory(c, history); err != nil {
log.Error("usersuitSvc.History error(%v)", err)
c.JSON(nil, err)
return
}
c.JSONMap(map[string]interface{}{
"data": data,
"count": count,
}, nil)
}
// packageInfo get user package
func packageInfo(c *bm.Context) {
var (
err error
mid int64
req = c.Request
midStr = req.Form.Get("mid")
)
if midStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(usersuitSvc.PackageInfo(c, mid))
}
// equipment get current equipped pendant
func equipment(c *bm.Context) {
var (
err error
mid int64
req = c.Request
params = req.Form
midStr = params.Get("mid")
)
if midStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(usersuitSvc.Equipment(c, mid))
}
// order order pendant
func order(c *bm.Context) {
var (
err error
mid, pid, expires, tp int64
orderData *model.PayInfo
req = c.Request
params = req.Form
midStr = params.Get("mid")
pidStr = params.Get("pid")
expiresStr = params.Get("expires")
typeStr = params.Get("moneytype")
)
if midStr == "" || pidStr == "" || expiresStr == "" || typeStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if pid, err = strconv.ParseInt(pidStr, 10, 64); err != nil || pid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if expires, err = strconv.ParseInt(expiresStr, 10, 64); err != nil || expires < 1 {
c.JSON(nil, ecode.RequestErr)
return
}
if tp, err = strconv.ParseInt(typeStr, 10, 64); err != nil || tp < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if orderData, err = usersuitSvc.OrderPendant(c, mid, pid, expires, tp); err != nil {
c.JSON(nil, err)
return
}
if orderData != nil && orderData.PayURL != "" {
c.JSON(orderData, nil)
}
}
// multiGrant batch grant pendant
func multiGrantByMid(c *bm.Context) {
var (
err error
pid, expire int64
mids []int64
req = c.Request
params = req.Form
midStr = params.Get("mids")
pidStr = params.Get("pid")
expireStr = params.Get("expire")
)
if midStr == "" || pidStr == "" || expireStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mids, err = xstr.SplitInts(midStr); err != nil || len(mids) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if pid, err = strconv.ParseInt(pidStr, 10, 64); err != nil || pid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if expire, err = strconv.ParseInt(expireStr, 10, 64); err != nil || expire <= 0 || expire > 3650 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, usersuitSvc.BatchGrantPendantByMid(c, pid, expire, mids))
}
// multiGrant batch grant pendant
func multiGrantByPid(c *bm.Context) {
var (
err error
mid int64
pids, expires []int64
req = c.Request
params = req.Form
midStr = params.Get("mid")
pidStr = params.Get("pids")
expireStr = params.Get("expires")
)
if midStr == "" || pidStr == "" || expireStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if pids, err = xstr.SplitInts(pidStr); err != nil || len(pids) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if expires, err = xstr.SplitInts(expireStr); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, usersuitSvc.BatchGrantPendantByPid(c, mid, expires, pids))
}
// equip euqip pendant
func equip(c *bm.Context) {
var (
err error
mid, pid, status, source int64
req = c.Request
params = req.Form
midStr = params.Get("mid")
pidStr = params.Get("pid")
statusStr = params.Get("status")
sourceStr = params.Get("source") // 挂件来源可选默认00 未知1背包2大会员
)
if midStr == "" || pidStr == "" || statusStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil || mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if pid, err = strconv.ParseInt(pidStr, 10, 64); err != nil || pid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if status, err = strconv.ParseInt(statusStr, 10, 64); err != nil || (status != 1 && status != 2) {
c.JSON(nil, ecode.RequestErr)
return
}
source = model.ParseSource(sourceStr)
c.JSON(nil, usersuitSvc.EquipPendant(c, mid, pid, int8(status), source))
}
// pendantCallback pay callback func
func pendantCallback(c *bm.Context) {
var (
err error
sFlag int64
arg *model.PendantOrderInfo
req = c.Request
params = req.Form
success = params.Get("isSuccess") // NOTE : how to fixed 1
orderID = params.Get("out_trade_no")
payID = params.Get("trade_no")
status = params.Get("trade_status")
)
if success == "" || orderID == "" || payID == "" || status == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if status != _paySuccess {
c.JSON(nil, nil)
return
}
arg = &model.PendantOrderInfo{OrderID: orderID, PayID: payID}
if sFlag, err = strconv.ParseInt(status, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
arg.Stauts = int32(sFlag)
arg.IsCallback = 1
c.JSON(nil, usersuitSvc.PendantCallback(c, arg))
}

View File

@@ -0,0 +1,66 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
proto_library(
name = "model_proto",
srcs = ["model.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "model_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_proto"],
importpath = "go-common/app/service/main/usersuit/model",
proto = ":model_proto",
tags = ["automanaged"],
deps = [
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"invite.go",
"medal.go",
"model.go",
"notify.go",
"pendant_state.go",
"rpc.go",
],
embed = [":model_go_proto"],
importpath = "go-common/app/service/main/usersuit/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/log:go_default_library",
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto: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,81 @@
package model
import (
xtime "go-common/library/time"
)
const (
// StatusOk ok
StatusOk = 0
// StatusUsed used
StatusUsed = 1
// StatusExpires expire
StatusExpires = 2
)
// Invite invaite
type Invite struct {
Status int64 `json:"status"`
Mid int64 `json:"mid"`
Code string `json:"invite_code"`
IP uint32 `json:"-"` // legacy IP field
IPng []byte `json:"-"`
Ctime xtime.Time `json:"buy_time"`
Expires int64 `json:"expires"`
Imid int64 `json:"invited_mid,omitempty"`
UsedAt int64 `json:"used_at,omitempty"`
Mtime xtime.Time `json:"-"`
}
// FillStatus fill status
func (inv *Invite) FillStatus(now int64) {
if inv.Used() {
inv.Status = StatusUsed
return
}
if inv.Expired(now) {
inv.Status = StatusExpires
return
}
inv.Status = StatusOk
}
// Used use
func (inv *Invite) Used() bool {
return inv.UsedAt > 0 && inv.Imid > 0
}
// Expired expire
func (inv *Invite) Expired(now int64) bool {
return now > inv.Expires
}
// SortInvitesByCtimeDesc sort
type SortInvitesByCtimeDesc []*Invite
// Len len
func (invs SortInvitesByCtimeDesc) Len() int {
return len(invs)
}
// Less less
func (invs SortInvitesByCtimeDesc) Less(i, j int) bool {
return int64(invs[i].Ctime) > int64(invs[j].Ctime)
}
// Swap swap
func (invs SortInvitesByCtimeDesc) Swap(i, j int) {
tmp := invs[i]
invs[i] = invs[j]
invs[j] = tmp
}
// InviteStat stat
type InviteStat struct {
Mid int64 `json:"mid"`
CurrentLimit int64 `json:"current_limit"`
CurrentBought int64 `json:"current_bought"`
TotalBought int64 `json:"total_bought"`
TotalUsed int64 `json:"total_used"`
InviteCodes []*Invite `json:"invite_codes"`
}

View File

@@ -0,0 +1,75 @@
package model
import (
"fmt"
"math/rand"
xtime "go-common/library/time"
)
const (
// OwnerInstall is_activated=1.
OwnerInstall = 1
// OwnerUninstall is_activated=0.
OwnerUninstall = 0
// Level1 medal_info.level 普通.
Level1 = int32(1)
// Level2 medal_info.level 高级.
Level2 = int32(2)
// Level3 medal_info.level 稀有.
Level3 = int32(3)
// IsGet medal has get.
IsGet = int32(1)
// NotGet medal not get.
NotGet = int32(0)
)
var medalLevel = map[int32]string{Level1: "普通勋章", Level2: "高级勋章", Level3: "稀有勋章"}
// MedalOwner struct.
type MedalOwner struct {
ID int64 `json:"id"`
MID int64 `json:"mid"`
NID int64 `json:"nid"`
IsActivated int8 `json:"is_activated"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// MedalGroup struct.
type MedalGroup struct {
ID int64 `json:"id"`
Name string `json:"name"`
PID int64 `json:"pid"`
Rank int8 `json:"rank"`
IsOnline int8 `josn:"is_online"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// MedalMsg struct.
type MedalMsg struct {
ID int64 `json:"id"`
MID int64 `json:"mid"`
NID int64 `json:"nid"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// MedalCheck struct.
type MedalCheck struct {
Has int32 `json:"has"`
Info interface{} `json:"info"`
}
// Build build image and level info.
func (mi *MedalInfo) Build() {
mi.Image = getImageURL(mi.Image)
mi.ImageSmall = getImageURL(mi.ImageSmall)
mi.LevelDesc = medalLevel[mi.Level]
}
// getImageUrl get image from BFS.
func getImageURL(imgSrc string) (imgURL string) {
return fmt.Sprintf("http://i%d.hdslb.com%s", rand.Int63n(3), imgSrc)
}

View File

@@ -0,0 +1,33 @@
package model
// Pendant event const.
const (
PendantPickOff int64 = iota + 1
PendantPutOn
)
// VipInfo .
type VipInfo struct {
Mid int64 `json:"mid"`
VipType int64 `json:"vipType"`
VipStatus int64 `json:"vipStatus"`
VipDueDate int64 `json:"vipDueDate"`
}
// ArgMIDNID struct.
type ArgMIDNID struct {
MID int64 `form:"mid" validate:"gt=0,required"`
NID int64 `form:"nid" validate:"gt=0,required"`
}
// ArgMID struct.
type ArgMID struct {
MID int64 `form:"mid" validate:"gt=0,required"`
}
// AccountNotify .
type AccountNotify struct {
UID int64 `json:"mid"`
Type string `json:"type"`
Action string `json:"action"`
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
syntax = "proto3";
package account.service.usersuit;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "model";
message ArgPendantHistory {
int64 mid =1 [(gogoproto.jsontag) = "mid"];
int32 source_type =2 [(gogoproto.jsontag) = "sourceType"];
int64 start_time =3 [(gogoproto.jsontag) = "startTime"];
int64 end_time =4 [(gogoproto.jsontag) = "endTime"];
int64 page =5 [(gogoproto.jsontag) = "page"];
}
message ArgOrderHistory {
string order_id = 1 [(gogoproto.jsontag) = "oderID", (gogoproto.customname) = "OrderID"];
string pay_id = 2 [(gogoproto.jsontag) = "payID", (gogoproto.customname) = "PayID"];
int64 mid = 3 [(gogoproto.jsontag) = "mid"];
int64 pid = 4 [(gogoproto.jsontag) = "pid"];
int32 status = 5 [(gogoproto.jsontag) = "status"];
int32 pay_type =6 [(gogoproto.jsontag) = "payType"];
int64 start_time =7 [(gogoproto.jsontag) = "startTime"];
int64 end_time =8 [(gogoproto.jsontag) = "endTime"];
int64 page =9 [(gogoproto.jsontag) = "page"];
}
message PendantGroupInfo {
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.customname) = "ID"];
string name = 2 [(gogoproto.jsontag) = "group_name"];
int32 rank = 3 [(gogoproto.jsontag) = "rank"];
int32 status = 4 [(gogoproto.jsontag) = "status"];
string image =5 [(gogoproto.jsontag) = "image"];
string image_model =6 [(gogoproto.jsontag) = "image_model"];
int32 frequency_limit =7 [(gogoproto.jsontag) = "-"];
int32 time_limit =8 [(gogoproto.jsontag) = "-"];
int64 number =9 [(gogoproto.jsontag) = "group_count"];
repeated Pendant sub_pendant = 10 [(gogoproto.jsontag) = "pendant_info"];
}
message PendantGroup {
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.customname) = "ID"];
int64 gid = 2 [(gogoproto.jsontag) = "gid"];
int64 pid = 3 [(gogoproto.jsontag) = "pid"];
}
message PendantPrice {
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.customname) = "ID"];
int64 pid = 2 [(gogoproto.jsontag) = "pid"];
int64 type = 3 [(gogoproto.jsontag) = "type"];
int64 price = 4 [(gogoproto.jsontag) = "price"];
}
message PendantHistory {
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.customname) = "ID"];
int64 mid = 2 [(gogoproto.jsontag) = "mid"];
int64 pid = 3 [(gogoproto.jsontag) = "pid"];
int32 source_type = 4 [(gogoproto.jsontag) = "source_type"];
int64 expire = 5 [(gogoproto.jsontag) = "expire"];
string operator_name = 6 [(gogoproto.jsontag) = "opeartor_name"];
int32 operator_action = 7 [(gogoproto.jsontag) = "opeartor_action"];
}
message PendantOrderInfo {
int64 id =1 [(gogoproto.jsontag) = "_", (gogoproto.customname) = "ID"];
int64 mid = 2 [(gogoproto.jsontag) = "mid"];
string order_id = 3 [(gogoproto.jsontag) = "order_id", (gogoproto.customname) = "OrderID"];
string pay_id = 4 [(gogoproto.jsontag) = "pay_id", (gogoproto.customname) = "PayID"];
int64 pay_type = 5 [(gogoproto.jsontag) = "pay_type"];
double pay_price = 6 [(gogoproto.jsontag) = "pay_price"];
int64 app_id = 7 [(gogoproto.jsontag) = "app_id", (gogoproto.customname) = "AppID"];
int32 stauts = 8 [(gogoproto.jsontag) = "status"];
int64 pid = 9 [(gogoproto.jsontag) = "pid"];
int64 time_length = 10 [(gogoproto.jsontag) = "time_length"];
string cost = 11 [(gogoproto.jsontag) = "cost"];
int64 buy_time = 12 [(gogoproto.jsontag) = "buy_time"];
int32 is_callback = 13 [(gogoproto.jsontag) = "is_callback"];
int64 callback_time = 14 [(gogoproto.jsontag) = "callback_time"];
string image = 15 [(gogoproto.jsontag) = "image"];
string name = 16 [(gogoproto.jsontag) = "name"];
}
message PayInfo {
string order_id = 1 [(gogoproto.jsontag) = "order_id", (gogoproto.customname) = "OrderID"];
string order_num = 2 [(gogoproto.jsontag) = "order_no"];
string pay_url = 3 [(gogoproto.jsontag) = "pay_url", (gogoproto.customname) = "PayURL"];
}
message Pendant {
int64 id =1 [(gogoproto.jsontag) = "pid", (gogoproto.customname) = "ID"];
string name = 2 [(gogoproto.jsontag) = "name"];
string image = 3 [(gogoproto.jsontag) = "image"];
string image_model =4 [(gogoproto.jsontag) = "image_model"];
int32 status =5 [(gogoproto.jsontag) = "status"];
int64 coin =6 [(gogoproto.jsontag) = "coin"];
int64 point =7 [(gogoproto.jsontag) = "point"];
int64 bcoin =8 [(gogoproto.jsontag) = "bcoin", (gogoproto.customname) = "BCoin"];
int64 expires =9 [(gogoproto.jsontag) = "expire"];
int64 gid =10 [(gogoproto.jsontag) = "gid"];
int32 rank = 11 [(gogoproto.jsontag) = "rank"];
}
message PendantPackage {
int64 id = 1 [(gogoproto.jsontag) = "id", (gogoproto.customname) = "ID"];
int64 mid = 2 [(gogoproto.jsontag) = "mid"];
int64 pid = 3 [(gogoproto.jsontag) = "pid"];
int64 expires =4 [(gogoproto.jsontag) = "expire"];
int64 type =5 [(gogoproto.jsontag) = "type"];
int32 status =6 [(gogoproto.jsontag) = "status"];
int32 is_vip =7 [(gogoproto.jsontag) = "isVIP", (gogoproto.customname) = "IsVIP"];
Pendant pendant =8 [(gogoproto.jsontag) = "pendant"];
}
message PendantEquip {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
int64 pid = 2 [(gogoproto.jsontag) = "pid"];
int64 expires =3 [(gogoproto.jsontag) = "expire"];
Pendant pendant =4 [(gogoproto.jsontag) = "pendant"];
}
message GroupPendantList {
int64 pkgExpires = 1 [(gogoproto.jsontag) = "pkg_expires"];
int32 pkgStatus = 2 [(gogoproto.jsontag) = "pkg_status"];
Pendant pendant = 3 [(gogoproto.embed) = true, (gogoproto.jsontag) = ""];
}
message MedalInfo {
int64 ID = 1 [(gogoproto.jsontag) = "nid"];
string Name = 2 [(gogoproto.jsontag) = "name"];
string Description = 3 [(gogoproto.jsontag) = "description"];
string Image = 4 [(gogoproto.jsontag) = "image"];
string ImageSmall = 5 [(gogoproto.jsontag) = "image_small"];
string Condition = 6 [(gogoproto.jsontag) = "condition"];
int64 GID = 7 [(gogoproto.jsontag) = "gid"];
int32 Level = 8 [(gogoproto.jsontag) = "level,omitempty"];
string LevelRank = 9 [(gogoproto.jsontag) = "level_rank,omitempty"];
string LevelDesc = 10 [(gogoproto.jsontag) = "level_desc,omitempty"];
int32 Sort = 11 [(gogoproto.jsontag) = "sort,omitempty"];
int32 IsOnline = 12 [(gogoproto.jsontag) = "is_online,omitempty"];
int32 IsGet = 13 [(gogoproto.jsontag) = "is_get,omitempty"];
int64 Ctime = 14 [(gogoproto.jsontag) = "ctime,omitempty", (gogoproto.casttype) = "go-common/library/time.Time"];
int64 Mtime = 15 [(gogoproto.jsontag) = "mtime,omitempty", (gogoproto.casttype) = "go-common/library/time.Time"];
}
message MedalHomeInfo {
string Description = 1 [(gogoproto.jsontag) = "description"];
string Image = 2 [(gogoproto.jsontag) = "image"];
string ImageSmall = 3 [(gogoproto.jsontag) = "image_small"];
int32 IsActivated = 4 [(gogoproto.jsontag) = "is_activated"];
string Level = 5 [(gogoproto.jsontag) = "level"];
string Name = 6 [(gogoproto.jsontag) = "name"];
int64 NID = 7 [(gogoproto.jsontag) = "nid"];
}
message MedalUserInfo {
string Name = 1 [(gogoproto.jsontag) = "name"];
string Face = 2 [(gogoproto.jsontag) = "face"];
int32 Level = 3 [(gogoproto.jsontag) = "level"];
bool TrueLove = 4 [(gogoproto.jsontag) = "truelove"];
int64 NID = 5 [(gogoproto.jsontag) = "nid"];
string ImageSmall = 6 [(gogoproto.jsontag) = "image_small"];
}
message MedalPopup {
int64 NID = 1 [(gogoproto.jsontag) = "nid"];
string Name = 2 [(gogoproto.jsontag) = "name"];
string Image = 3 [(gogoproto.jsontag) = "image"];
}
message MedalMyInfo{
MedalInfo MedalInfo = 1 [(gogoproto.embed) = true, (gogoproto.jsontag) = ""];
int32 IsActivated = 2 [(gogoproto.jsontag) = "is_activated"];
int32 IsNewGet = 3 [(gogoproto.jsontag) = "is_new_get"];
int64 GetTime = 4 [(gogoproto.jsontag) = "get_time,omitempty", (gogoproto.casttype) = "go-common/library/time.Time"];
}
message MedalMyInfos{
int32 Count = 1 [(gogoproto.jsontag) = "count"];
string Name = 2 [(gogoproto.jsontag) = "name"];
repeated MedalMyInfo List = 3 [(gogoproto.jsontag) = "list"];
}
message MedalAllInfos{
int64 HasActivated = 1 [(gogoproto.jsontag) = "has_activated"];
bool RedPoint = 2 [(gogoproto.jsontag) = "is_show_red_point"];
repeated int64 HasGet = 3 [(gogoproto.jsontag) = "has_get"];
map<int64, MedalCategoryInfo> List = 4 [(gogoproto.jsontag) = "list"];
}
message MedalCategoryInfo{
int32 Count = 1 [(gogoproto.jsontag) = "count"];
string Name = 2 [(gogoproto.jsontag) = "name"];
repeated MedalItemInfo Data = 3 [(gogoproto.jsontag) = "data"];
}
message MedalItemInfo{
MedalInfo Left = 1 [(gogoproto.jsontag) = "left"];
int32 Count = 2 [(gogoproto.jsontag) = "count"];
repeated MedalInfo Right = 3 [(gogoproto.jsontag) = "right,omitempty"];
}
message PointFlag{
bool Pendant = 1 [(gogoproto.jsontag) = "pendant"];
bool Medal = 2 [(gogoproto.jsontag) = "medal"];
}

View File

@@ -0,0 +1,7 @@
package model
//const
const (
AccountNotifyUpdatePendant = "updatePendant"
AccountNotifyUpdateMedal = "updateMedal"
)

View File

@@ -0,0 +1,61 @@
package model
import (
"strconv"
"go-common/library/log"
)
// const .
const (
// pendant status
PendantStatusON = 1
PendantStatusOFF = 0
// group status
GroupStatusON = 1
GroupStatusOFF = 0
// packpage status
InvalidPendantPKG = int32(0)
ValidPendantPKG = int32(1)
EquipPendantPKG = int32(2)
// pendant equip
PendantEquipOFF = int8(1)
PendantEquipON = int8(2)
// pendant source
UnknownEquipSource = 0
EquipFromPackage = 1
EquipFromVIP = 2
)
// IsValidSource 挂件来源是否合法 合法true,无效false
func IsValidSource(source int64) bool {
if source != EquipFromPackage && source != EquipFromVIP && source != UnknownEquipSource {
log.Error("IsValidSource souce=%v is not correct value", source)
return false
}
return true
}
// ParseSource c处理挂件来源
func ParseSource(sourceStr string) int64 {
// 没有传值,则设置为未知挂件
if sourceStr == "" {
return UnknownEquipSource
}
// 有传递参数,但是没有按照要求传值,也设置为未知挂件
source, err := strconv.ParseInt(sourceStr, 10, 64)
if err != nil {
log.Error("ParseSource err(%+v)", err)
return UnknownEquipSource
}
// 没有按照要求传值,也设置为未知挂件
if source != EquipFromPackage && source != EquipFromVIP && source != UnknownEquipSource {
log.Error("ParseSource souce=%v is not correct value", source)
return UnknownEquipSource
}
return source
}

View File

@@ -0,0 +1,95 @@
package model
// ArgBuy buy
type ArgBuy struct {
Mid int64
Num int64
IP string
}
// ArgApply apply
type ArgApply struct {
Mid int64
Code string
Cookie string
IP string
}
// ArgStat stat
type ArgStat struct {
Mid int64
IP string
}
// ArgGenerate generator
type ArgGenerate struct {
Mid int64
Num int64
ExpireDay int64
IP string
}
// ArgList list
type ArgList struct {
Mid int64
Start, End int64
}
// ArgEquipment rpc pendant arg .
type ArgEquipment struct {
Mid int64
IP string
}
// ArgEquipments rpc equipment arg .
type ArgEquipments struct {
Mids []int64
IP string
}
// ArgEquip rpc equip arg.
type ArgEquip struct {
Mid int64
Pid int64
Status int8
IP string
Source int64
}
// ArgMid struct.
type ArgMid struct {
Mid int64
}
// ArgMids struct.
type ArgMids struct {
Mids []int64
}
// ArgMedalUserInfo struct.
type ArgMedalUserInfo struct {
Mid int64
Cookie string
IP string
}
// ArgMedalInstall struct.
type ArgMedalInstall struct {
Mid int64 `form:"mid" validate:"gt=0,required"`
Nid int64 `form:"nid" validate:"gt=0,required"`
IsActivated int8 `form:"isActivated"`
}
// ArgGrantByMids one pendant give to multiple users.
type ArgGrantByMids struct {
BatchNo string
Mids []int64
Pid int64
Expire int64
}
// ArgGPMID .
type ArgGPMID struct {
MID int64
GID int64
}

View File

@@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["pendant_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"client.go",
"common.go",
"medal.go",
"pendant.go",
],
importpath = "go-common/app/service/main/usersuit/rpc/client",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/model:go_default_library",
"//library/net/rpc: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,92 @@
package client
import (
"context"
"go-common/app/service/main/usersuit/model"
"go-common/library/net/rpc"
)
const (
_appid = "account.service.usersuit"
)
var (
_noRes = &struct{}{}
)
// Service2 struct
type Service2 struct {
client *rpc.Client2
}
// New Service2 init
func New(c *rpc.ClientConfig) (s *Service2) {
s = &Service2{}
s.client = rpc.NewDiscoveryCli(_appid, c)
return
}
const (
_buy = "RPC.Buy"
_apply = "RPC.Apply"
_stat = "RPC.Stat"
_generate = "RPC.Generate"
_list = "RPC.List"
_equip = "RPC.Equip"
_grantByMids = "RPC.GrantByMids"
_groupPendantMid = "RPC.GroupPendantMid"
)
// Buy buy invite
func (s *Service2) Buy(c context.Context, arg *model.ArgBuy) (res []*model.Invite, err error) {
res = make([]*model.Invite, 0)
err = s.client.Call(c, _buy, arg, &res)
return
}
// Apply apply
func (s *Service2) Apply(c context.Context, arg *model.ArgApply) (err error) {
err = s.client.Call(c, _apply, arg, _noRes)
return
}
// Stat stat
func (s *Service2) Stat(c context.Context, arg *model.ArgStat) (res *model.InviteStat, err error) {
res = new(model.InviteStat)
err = s.client.Call(c, _stat, arg, res)
return
}
// Generate generator
func (s *Service2) Generate(c context.Context, arg *model.ArgGenerate) (res []*model.Invite, err error) {
res = make([]*model.Invite, 0)
err = s.client.Call(c, _generate, arg, &res)
return
}
// List list
func (s *Service2) List(c context.Context, arg *model.ArgList) (res []*model.Invite, err error) {
res = make([]*model.Invite, 0)
err = s.client.Call(c, _list, arg, &res)
return
}
// Equip pendant equip.
func (s *Service2) Equip(c context.Context, arg *model.ArgEquip) (err error) {
err = s.client.Call(c, _equip, arg, _noRes)
return
}
// GrantByMids one pendant give to multiple users.
func (s *Service2) GrantByMids(c context.Context, arg *model.ArgGrantByMids) (err error) {
err = s.client.Call(c, _grantByMids, arg, _noRes)
return
}
// GroupPendantMid get share group pendant by mid
func (s *Service2) GroupPendantMid(c context.Context, arg *model.ArgGPMID) (res []*model.GroupPendantList, err error) {
res = make([]*model.GroupPendantList, 0)
err = s.client.Call(c, _groupPendantMid, arg, &res)
return
}

View File

@@ -0,0 +1,18 @@
package client
import (
"context"
"go-common/app/service/main/usersuit/model"
)
const (
_pointFlag = "RPC.PointFlag"
)
// PointFlag obtain new pendant noify.
func (s *Service2) PointFlag(c context.Context, arg *model.ArgMID) (res *model.PointFlag, err error) {
res = new(model.PointFlag)
err = s.client.Call(c, _pointFlag, arg, res)
return
}

View File

@@ -0,0 +1,75 @@
package client
import (
"context"
"go-common/app/service/main/usersuit/model"
)
const (
_medalHomeInfo = "RPC.MedalHomeInfo"
_medalUserInfo = "RPC.MedalUserInfo"
_medalInstall = "RPC.MedalInstall"
_medalPopup = "RPC.MedalPopup"
_medalMyInfo = "RPC.MedalMyInfo"
_medalAllInfo = "RPC.MedalAllInfo"
_medalGrant = "RPC.MedalGrant"
_medalActivated = "RPC.MedalActivated"
_medalActivatedMulti = "RPC.MedalActivatedMulti"
)
// MedalHomeInfo return user mdeal home info.
func (s *Service2) MedalHomeInfo(c context.Context, arg *model.ArgMid) (res []*model.MedalHomeInfo, err error) {
err = s.client.Call(c, _medalHomeInfo, arg, &res)
return
}
// MedalUserInfo return medal user info.
func (s *Service2) MedalUserInfo(c context.Context, arg *model.ArgMedalUserInfo) (res *model.MedalUserInfo, err error) {
res = new(model.MedalUserInfo)
err = s.client.Call(c, _medalUserInfo, arg, res)
return
}
// MedalInstall install or uninstall medal.
func (s *Service2) MedalInstall(c context.Context, arg *model.ArgMedalInstall) (err error) {
err = s.client.Call(c, _medalInstall, arg, _noRes)
return
}
// MedalPopup return medal popup.
func (s *Service2) MedalPopup(c context.Context, arg *model.ArgMid) (res *model.MedalPopup, err error) {
res = new(model.MedalPopup)
err = s.client.Call(c, _medalPopup, arg, res)
return
}
// MedalMyInfo return medal my info.
func (s *Service2) MedalMyInfo(c context.Context, arg *model.ArgMid) (res []*model.MedalMyInfos, err error) {
err = s.client.Call(c, _medalMyInfo, arg, &res)
return
}
// MedalAllInfo return medal all info.
func (s *Service2) MedalAllInfo(c context.Context, arg *model.ArgMid) (res *model.MedalAllInfos, err error) {
err = s.client.Call(c, _medalAllInfo, arg, &res)
return
}
// MedalGrant send a medal to user.
func (s *Service2) MedalGrant(c context.Context, arg *model.ArgMIDNID) (err error) {
err = s.client.Call(c, _medalGrant, arg, _noRes)
return
}
// MedalActivated get the user activated medal info.
func (s *Service2) MedalActivated(c context.Context, arg *model.ArgMid) (res *model.MedalInfo, err error) {
err = s.client.Call(c, _medalActivated, arg, &res)
return
}
// MedalActivatedMulti Multi get the user activated medal info(at most 50).
func (s *Service2) MedalActivatedMulti(c context.Context, arg *model.ArgMids) (res map[int64]*model.MedalInfo, err error) {
err = s.client.Call(c, _medalActivatedMulti, arg, &res)
return
}

View File

@@ -0,0 +1,24 @@
package client
import (
"context"
"go-common/app/service/main/usersuit/model"
)
const (
_pendantEquip = "RPC.Equipment"
_pendantEquips = "RPC.Equipments"
)
// Equipment obtian equipment by mid
func (s *Service2) Equipment(c context.Context, arg *model.ArgEquipment) (res *model.PendantEquip, err error) {
res = new(model.PendantEquip)
err = s.client.Call(c, _pendantEquip, arg.Mid, res)
return
}
// Equipments obtian equipment by mids
func (s *Service2) Equipments(c context.Context, arg *model.ArgEquipments) (res map[int64]*model.PendantEquip, err error) {
err = s.client.Call(c, _pendantEquips, arg.Mids, &res)
return
}

View File

@@ -0,0 +1,10 @@
package client
import (
"testing"
)
// TestRPC_Equipment test
func TestRPC_Equipment(t *testing.T) {
}

View File

@@ -0,0 +1,58 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"pendant_test.go",
"rpc_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//app/service/main/usersuit/service:go_default_library",
"//library/log:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"common.go",
"medal.go",
"pendant.go",
"rpc.go",
],
importpath = "go-common/app/service/main/usersuit/rpc/server",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//app/service/main/usersuit/service:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/context: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 server
import (
"go-common/app/service/main/usersuit/model"
"go-common/library/net/rpc/context"
)
// PointFlag obtain new pendant noify.
func (r *RPC) PointFlag(c context.Context, arg *model.ArgMID, res *model.PointFlag) (err error) {
var pf *model.PointFlag
if pf, err = r.s.PointFlag(c, arg); err == nil && pf != nil {
*res = *pf
}
return
}

View File

@@ -0,0 +1,72 @@
package server
import (
"go-common/app/service/main/usersuit/model"
"go-common/library/net/rpc/context"
)
// MedalHomeInfo return user mdeal home info.
func (r *RPC) MedalHomeInfo(c context.Context, arg *model.ArgMid, res *[]*model.MedalHomeInfo) (err error) {
*res, err = r.s.MedalHomeInfo(c, arg.Mid)
return
}
// MedalUserInfo return medal user info.
func (r *RPC) MedalUserInfo(c context.Context, arg *model.ArgMedalUserInfo, res *model.MedalUserInfo) (err error) {
var mui *model.MedalUserInfo
if mui, err = r.s.MedalUserInfo(c, arg.Mid, arg.IP); err == nil && mui != nil {
*res = *mui
}
return
}
// MedalInstall install or uninstall medal.
func (r *RPC) MedalInstall(c context.Context, arg *model.ArgMedalInstall, res *struct{}) (err error) {
err = r.s.MedalInstall(c, arg.Mid, arg.Nid, arg.IsActivated)
return
}
// MedalPopup return medal popup.
func (r *RPC) MedalPopup(c context.Context, arg *model.ArgMid, res *model.MedalPopup) (err error) {
var mp *model.MedalPopup
if mp, err = r.s.MedalPopup(c, arg.Mid); err == nil && mp != nil {
*res = *mp
}
return
}
// MedalMyInfo return medal my info.
func (r *RPC) MedalMyInfo(c context.Context, arg *model.ArgMid, res *[]*model.MedalMyInfos) (err error) {
*res, err = r.s.MedalMyInfo(c, arg.Mid)
return
}
// MedalAllInfo return medal all info.
func (r *RPC) MedalAllInfo(c context.Context, arg *model.ArgMid, res *model.MedalAllInfos) (err error) {
var mai *model.MedalAllInfos
if mai, err = r.s.MedalAllInfo(c, arg.Mid); err == nil && mai != nil {
*res = *mai
}
return
}
// MedalGrant send a medal to user.
func (r *RPC) MedalGrant(c context.Context, arg *model.ArgMIDNID, res *struct{}) (err error) {
err = r.s.MedalGet(c, arg.MID, arg.NID)
return
}
// MedalActivated get the user activated medal info.
func (r *RPC) MedalActivated(c context.Context, arg *model.ArgMid, res *model.MedalInfo) (err error) {
var ma *model.MedalInfo
if ma, err = r.s.MedalActivated(c, arg.Mid); err == nil && ma != nil {
*res = *ma
}
return
}
// MedalActivatedMulti Multi get get the user activated medal info(at most 50).
func (r *RPC) MedalActivatedMulti(c context.Context, arg *model.ArgMids, res *map[int64]*model.MedalInfo) (err error) {
*res, err = r.s.MedalActivatedMulti(c, arg.Mids)
return
}

View File

@@ -0,0 +1,21 @@
package server
import (
"go-common/app/service/main/usersuit/model"
"go-common/library/net/rpc/context"
)
// Equipment obtain Equipment by mid .
func (r *RPC) Equipment(c context.Context, mid int64, res *model.PendantEquip) (err error) {
var pe *model.PendantEquip
if pe, err = r.s.Equipment(c, mid); err == nil && pe != nil {
*res = *pe
}
return
}
// Equipments obtain equipments by mids .
func (r *RPC) Equipments(c context.Context, mids []int64, res *map[int64]*model.PendantEquip) (err error) {
*res, err = r.s.Equipments(c, mids)
return
}

View File

@@ -0,0 +1,45 @@
package server
import (
"go-common/app/service/main/usersuit/model"
"testing"
)
const (
_pendantEquip = "RPC.Equipment"
_pendantEquips = "RPC.Equipments"
)
// TestRPC_Equipment test
func TestRPC_Equipment(t *testing.T) {
var (
res = new(model.PendantEquip)
err error
)
once.Do(startServer)
arg := &model.ArgEquipment{
Mid: 27515240,
}
if err = client.Call(_pendantEquip, arg.Mid, &res); err != nil {
t.Errorf("client.Call(%s) error(%v)", _pendantEquip, err)
t.FailNow()
}
t.Logf("res (%v)", res)
}
// TestRPC_Equipment test
func TestRPC_Equipments(t *testing.T) {
var (
res = make(map[int64]*model.PendantEquip)
err error
)
once.Do(startServer)
arg := &model.ArgEquipments{
Mids: []int64{27515240, 100},
}
if err = client.Call(_pendantEquips, arg.Mids, &res); err != nil {
t.Errorf("client.Call(%s) error(%v)", _pendantEquips, err)
t.FailNow()
}
t.Logf("res (%v)", res)
}

View File

@@ -0,0 +1,68 @@
package server
import (
"go-common/app/service/main/usersuit/conf"
"go-common/app/service/main/usersuit/model"
"go-common/app/service/main/usersuit/service"
"go-common/library/net/rpc"
"go-common/library/net/rpc/context"
)
// RPC server struct
type RPC struct {
s *service.Service
}
// New new rpc server.
func New(c *conf.Config, s *service.Service) (svr *rpc.Server) {
r := &RPC{s: s}
svr = rpc.NewServer(c.RPCServer)
if err := svr.Register(r); err != nil {
panic(err)
}
return
}
// Ping check connection success.
func (r *RPC) Ping(c context.Context, arg *struct{}, res *struct{}) (err error) {
return
}
// Buy buy invite code
func (r *RPC) Buy(c context.Context, arg *model.ArgBuy, res *[]*model.Invite) (err error) {
*res, err = r.s.BuyInvite(c, arg.Mid, arg.Num, arg.IP)
return
}
// Apply apply invite code
func (r *RPC) Apply(c context.Context, arg *model.ArgApply, res *struct{}) (err error) {
err = r.s.ApplyInvite(c, arg.Mid, arg.Code, arg.Cookie, arg.IP)
return
}
// Stat stat code
func (r *RPC) Stat(c context.Context, arg *model.ArgStat, res *model.InviteStat) (err error) {
var stat *model.InviteStat
if stat, err = r.s.Stat(c, arg.Mid, arg.IP); err == nil && stat != nil {
*res = *stat
}
return
}
// Equip pendant equip.
func (r *RPC) Equip(c context.Context, arg *model.ArgEquip, res *struct{}) (err error) {
err = r.s.EquipPendant(c, arg.Mid, arg.Pid, arg.Status, arg.Source)
return
}
// GrantByMids one pendant give to multiple users.
func (r *RPC) GrantByMids(c context.Context, arg *model.ArgGrantByMids, res *struct{}) (err error) {
err = r.s.BatchGrantPendantByMid(c, arg.Pid, arg.Expire, arg.Mids)
return
}
// GroupPendantMid get group pendant by mid and gid
func (r *RPC) GroupPendantMid(c context.Context, arg *model.ArgGPMID, res *[]*model.GroupPendantList) (err error) {
*res, err = r.s.GroupPendantMid(c, arg)
return
}

View File

@@ -0,0 +1,47 @@
package server
import (
"net/rpc"
"sync"
"testing"
"time"
"go-common/app/service/main/usersuit/conf"
"go-common/app/service/main/usersuit/service"
"go-common/library/log"
)
const (
addr = "127.0.0.1:7269"
_testPing = "RPC.Ping"
)
var (
_noArg = &struct{}{}
client *rpc.Client
once sync.Once
)
func startServer() {
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Xlog)
defer log.Close()
svr := service.New(conf.Conf)
New(conf.Conf, svr)
time.Sleep(time.Second * 3)
var err error
client, err = rpc.Dial("tcp", addr)
if err != nil {
panic(err)
}
}
func TestRPC_Ping(t *testing.T) {
once.Do(startServer)
if err := client.Call(_testPing, &_noArg, &_noArg); err != nil {
t.Errorf("client.Call(%s) error(%v)", _testPing, err)
t.FailNow()
}
}

View File

@@ -0,0 +1,83 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"invite_test.go",
"medal_test.go",
"notify_test.go",
"pendant_test.go",
"service_test.go",
"third_pendant_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/dao/pendant:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/bouk/monkey:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"invite.go",
"medal.go",
"notify.go",
"pendant.go",
"point_flag.go",
"service.go",
"third_pendant.go",
],
importpath = "go-common/app/service/main/usersuit/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/coin/api/gorpc:go_default_library",
"//app/service/main/coin/model:go_default_library",
"//app/service/main/member/api/gorpc:go_default_library",
"//app/service/main/member/model:go_default_library",
"//app/service/main/member/model/block:go_default_library",
"//app/service/main/point/model:go_default_library",
"//app/service/main/point/rpc/client:go_default_library",
"//app/service/main/usersuit/conf:go_default_library",
"//app/service/main/usersuit/dao/invite:go_default_library",
"//app/service/main/usersuit/dao/medal:go_default_library",
"//app/service/main/usersuit/dao/pendant:go_default_library",
"//app/service/main/usersuit/model:go_default_library",
"//app/service/main/vip/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus:go_default_library",
"//library/sync/errgroup: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,328 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"math/rand"
"net"
"sort"
"time"
coinmdl "go-common/app/service/main/coin/model"
memmdl "go-common/app/service/main/member/model"
blkmdl "go-common/app/service/main/member/model/block"
"go-common/app/service/main/usersuit/model"
xsql "go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
xtime "go-common/library/time"
)
var (
_buyFlag = "1"
_applyFlag = "1"
_emptyInvites = make([]*model.Invite, 0)
)
const (
_inviteCodeExpireSeconds = int64(86400 * 3)
_checkZero = int8(1) // 硬币可为负数
_level5BuyLimit = 1
_level6BuyLimit = 2
)
// BuyInvite buy invite
func (s *Service) BuyInvite(c context.Context, mid int64, num int64, ip string) (res []*model.Invite, err error) {
var (
levelInfo *memmdl.LevelInfo
moralInfo *memmdl.Moral
blockInfo *blkmdl.RPCResInfo
coins float64
)
eg, errCtx := errgroup.WithContext(c)
eg.Go(func() (err error) {
if levelInfo, err = s.memRPC.Level(errCtx, &memmdl.ArgMid2{Mid: mid, RealIP: ip}); err != nil {
log.Error("s.memRPC.Level(%+v) error(%v)", &memmdl.ArgMid2{Mid: mid, RealIP: ip}, err)
}
return
})
eg.Go(func() (err error) {
if moralInfo, err = s.memRPC.Moral(errCtx, &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}); err != nil {
log.Error("s.memRPC.Moral(%+v) error(%v)", &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}, err)
}
return
})
eg.Go(func() (err error) {
if blockInfo, err = s.memRPC.BlockInfo(errCtx, &blkmdl.RPCArgInfo{MID: mid}); err != nil {
log.Error("s.memRPC.BlockInfo(%d) error(%v)", mid, err)
}
return
})
eg.Go(func() (err error) {
if coins, err = s.coinRPC.UserCoins(errCtx, &coinmdl.ArgCoinInfo{Mid: mid}); err != nil {
log.Error("d.coinRP.UserCoins(%d) err(%+v)", mid, err)
}
return
})
if err = eg.Wait(); err != nil {
return
}
switch {
case levelInfo == nil:
err = ecode.UsersuitInviteLevelLow
return
case moralInfo == nil:
err = ecode.LackOfScores
return
case blockInfo == nil:
err = ecode.UserDisabled
return
}
if blockInfo.BlockStatus != 0 {
err = ecode.UserDisabled
return
}
if moralInfo.Moral/100 < 60 {
err = ecode.LackOfScores
return
}
cost := float64(num * 50)
if coins < cost {
err = ecode.LackOfCoins
return
}
level := levelInfo.Cur
var limit int64
var expireSeconds int64
switch level {
case 5:
limit = _level5BuyLimit
expireSeconds = _inviteCodeExpireSeconds
case 6:
limit = _level6BuyLimit
expireSeconds = _inviteCodeExpireSeconds
default:
err = ecode.UsersuitInviteLevelLow
return
}
var ok bool
if ok, err = s.d.SetBuyFlagCache(c, mid, _buyFlag); err != nil {
return
}
if !ok {
err = ecode.ServiceUnavailable
return
}
defer func() {
s.d.DelBuyFlagCache(context.Background(), mid)
}()
now := time.Now()
start, end := rangeMonth(now)
var count int64
if count, err = s.d.CurrentCount(c, mid, start, end); err != nil {
return
}
if count+num > limit {
err = ecode.UsersuitInviteReachCurrentMonthLimit
return
}
arg := &coinmdl.ArgModifyCoin{Mid: mid, Count: -cost, Reason: fmt.Sprintf("购买邀请码%d个", num), CheckZero: _checkZero, IP: ip}
if _, err = s.coinRPC.ModifyCoin(c, arg); err != nil {
return
}
nowTs := now.Unix()
codem := make(map[string]int)
for int64(len(codem)) < num {
codem[geneInviteCode(mid, nowTs)] = 1
}
invs := make([]*model.Invite, 0)
buyIP := net.ParseIP(ip)
for code := range codem {
invs = append(invs, &model.Invite{
Status: model.StatusOk,
Mid: mid,
Code: code,
IP: IPv4toN(buyIP),
IPng: buyIP,
Expires: nowTs + expireSeconds,
Ctime: xtime.Time(nowTs),
})
}
var tx *xsql.Tx
if tx, err = s.d.Begin(c); err != nil {
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback() error(%v)", err1)
}
log.Error("发放邀请码失败用户ID%d已扣减硬币数%.2f,未发放成功数量:%d用户IP%s", mid, cost, num, ip)
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit() error(%v)", err)
log.Error("发放邀请码失败用户ID%d已扣减硬币数%.2f,未发放成功数量:%d用户IP%s", mid, cost, num, ip)
}
}()
for _, inv := range invs {
if _, err = s.d.TxAddInvite(c, tx, inv); err != nil {
return
}
}
res = invs
return
}
func geneInviteCode(mid int64, ts int64) string {
data := md5.Sum([]byte(fmt.Sprintf("%d,%d,%d", ts, mid, rand.Int63())))
h := hex.EncodeToString(data[:])
return h[8:24]
}
func rangeMonth(now time.Time) (start, end time.Time) {
year := now.Year()
month := now.Month()
loc := now.Location()
start = time.Date(year, month, 1, 0, 0, 0, 0, loc)
end = time.Date(year, month+1, 0, 23, 59, 59, 0, loc)
return
}
// ApplyInvite apply invite
func (s *Service) ApplyInvite(c context.Context, mid int64, code string, cookie, ip string) (err error) {
var memInfo *memmdl.BaseInfo
if memInfo, err = s.memRPC.Base(c, &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}); err != nil {
log.Error("s.memRPC.Level(%+v) error(%v)", &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}, err)
return
}
if memInfo == nil || memInfo.Rank >= 10000 {
err = ecode.UsersuitInviteAlreadyFormal
return
}
var ok bool
if ok, err = s.d.SetApplyFlagCache(c, code, _applyFlag); err != nil {
return
}
if !ok {
err = ecode.ServiceUnavailable
return
}
defer func() {
s.d.DelApplyFlagCache(context.Background(), code)
}()
var inv *model.Invite
if inv, err = s.d.Invite(c, code); err != nil {
return
}
if inv == nil {
err = ecode.UsersuitInviteCodeNotExists
return
}
if inv.Used() {
err = ecode.UsersuitInviteCodeUsed
return
}
nowTs := time.Now().Unix()
if inv.Expired(nowTs) {
err = ecode.UsersuitInviteCodeExpired
return
}
if _, err = s.d.UpdateInvite(c, mid, nowTs, code); err != nil {
return
}
if err = s.beFormal(c, mid, cookie, ip); err != nil {
log.Error("service.beFormal(%s, %d) error(%v)", code, mid, err)
log.Error("使用邀请码转正失败用户ID%d用户IP%s邀请码%+v", mid, ip, inv)
}
return
}
func (s *Service) beFormal(c context.Context, mid int64, cookie, ip string) (err error) {
for i := 0; i < 3; i++ {
if err = s.d.BeFormal(c, mid, cookie, ip); err == nil {
return
}
}
return
}
// Stat stat
func (s *Service) Stat(c context.Context, mid int64, ip string) (res *model.InviteStat, err error) {
var (
level int32
levelInfo *memmdl.LevelInfo
)
if levelInfo, err = s.memRPC.Level(c, &memmdl.ArgMid2{Mid: mid, RealIP: ip}); err != nil {
log.Error("s.memRPC.Level(%+v) error(%v)", &memmdl.ArgMid2{Mid: mid, RealIP: ip}, err)
return
}
if levelInfo == nil {
level = 0
} else {
level = levelInfo.Cur
}
var limit int64
switch level {
case 5:
limit = _level5BuyLimit
case 6:
limit = _level6BuyLimit
default:
res = &model.InviteStat{
Mid: mid,
CurrentLimit: 0,
CurrentBought: 0,
TotalUsed: 0,
TotalBought: 0,
InviteCodes: _emptyInvites,
}
return
}
var invs []*model.Invite
if invs, err = s.d.Invites(c, mid); err != nil {
return
}
now := time.Now()
nowTs := now.Unix()
sort.Sort(model.SortInvitesByCtimeDesc(invs))
start, end := rangeMonth(now)
startTs, endTs := start.Unix(), end.Unix()
curBought := int64(0)
totalUsed := int64(0)
for _, inv := range invs {
inv.FillStatus(nowTs)
buyTs := int64(inv.Ctime)
if buyTs >= startTs && buyTs <= endTs {
curBought++
}
if inv.Status == model.StatusUsed {
totalUsed++
}
}
res = &model.InviteStat{
Mid: mid,
CurrentLimit: limit,
CurrentBought: curBought,
TotalBought: int64(len(invs)),
TotalUsed: totalUsed,
InviteCodes: invs,
}
return
}
// IPv4toN is
func IPv4toN(ip net.IP) (sum uint32) {
v4 := ip.To4()
if v4 == nil {
return
}
sum += uint32(v4[0]) << 24
sum += uint32(v4[1]) << 16
sum += uint32(v4[2]) << 8
sum += uint32(v4[3])
return sum
}

View File

@@ -0,0 +1,70 @@
package service
import (
"context"
"testing"
"time"
"go-common/library/ecode"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_BuyInvite(t *testing.T) {
time.Sleep(time.Second * 2)
Convey("buy invite", t, func() {
Convey("when cookie is nil the return err should be NoLogin", func() {
mid := int64(88888970)
num := int64(2)
_, err := s.BuyInvite(context.Background(), mid, num, "127.0.0.1")
So(ecode.Cause(err), ShouldEqual, ecode.LackOfCoins)
})
})
}
func TestService_RangeMonth(t *testing.T) {
const (
_format = "2006-01-02 15:04:05"
)
type Range struct {
start string
end string
}
m := map[string]*Range{
"2017-12-01 00:00:00": {
"2017-12-01 00:00:00",
"2017-12-31 23:59:59",
},
"2017-12-24 12:05:00": {
"2017-12-01 00:00:00",
"2017-12-31 23:59:59",
},
"2017-12-24 23:59:59": {
"2017-12-01 00:00:00",
"2017-12-31 23:59:59",
},
}
Convey("Range month", t, func() {
for k, v := range m {
t, err := time.Parse(_format, k)
So(err, ShouldBeNil)
start, end := rangeMonth(t)
So(start.Format(_format), ShouldEqual, v.start)
So(end.Format(_format), ShouldEqual, v.end)
}
})
}
func TestService_GeneInviteCode(t *testing.T) {
Convey("Generate 10 codes in batch", t, func() {
mid := int64(88888970)
num := int64(1000)
ts := time.Now().Unix()
m := make(map[string]struct{})
for i := int64(0); i < num; i++ {
code := geneInviteCode(mid, ts)
m[code] = struct{}{}
}
So(len(m), ShouldEqual, num)
})
}

View File

@@ -0,0 +1,506 @@
package service
import (
"context"
"fmt"
"sort"
memmdl "go-common/app/service/main/member/model"
"go-common/app/service/main/usersuit/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_MedalGetTitle = "滋···叮!%s勋章合成完毕"
_MedalGetContext = "恭喜!你已成功领取“%s”勋章想赶紧把它装备起来吗请猛戳 ゚∀゚)σ #{成就勋章}{\"https://account.bilibili.com/site/nameplate.html\"}"
)
var (
medalLevel = map[int32]string{model.Level1: "普通勋章", model.Level2: "高级勋章", model.Level3: "稀有勋章"}
_emptyOwner = make([]*model.MedalOwner, 0)
)
// MedalInfo return medal info by mid and nid.
func (s *Service) MedalInfo(c context.Context, mid, nid int64) (res *model.MedalInfo, err error) {
var (
nos []*model.MedalOwner
)
if nos, err = s.OwnerMedal(c, mid); err != nil {
log.Error("s.medalDao.MedalOwnersCache(%d) error(%v)", mid, err)
err = nil
}
if _, ok := s.medalInfoAll[nid]; !ok {
err = ecode.MedalNotFound
return
}
res = s.medalInfoAll[nid]
for _, v := range nos {
if v.NID == nid {
res.IsGet = model.IsGet
break
}
}
return
}
// MedalGet get medal.
func (s *Service) MedalGet(c context.Context, mid, nid int64) (err error) {
var (
nos []*model.MedalOwner
mi *model.MedalInfo
ok bool
)
mia := s.medalInfoAll
if mi, ok = mia[nid]; !ok {
err = ecode.MedalNotFound
return
}
if nos, err = s.OwnerMedal(c, mid); err != nil {
log.Error("s.medalDao.MedalOwnersCache(%d) error(%v)", mid, err)
return
}
for _, no := range nos {
if no.NID == nid {
err = ecode.MedalHasGet
return
}
}
if err = s.medalDao.AddMedalOwner(c, mid, nid); err != nil {
return
}
s.medalDao.DelMedalOwnersCache(c, mid)
s.medalDao.SetPopupCache(c, mid, nid)
s.medalDao.SetRedPointCache(c, mid, nid)
s.medalDao.SendMsg(c, mid, fmt.Sprintf(_MedalGetTitle, mi.Name), fmt.Sprintf(_MedalGetContext, mi.Name))
return
}
// MedalCheck check user is get medal.
func (s *Service) MedalCheck(c context.Context, mid, nid int64) (res *model.MedalCheck, err error) {
res = &model.MedalCheck{}
mos, _ := s.OwnerMedal(c, mid)
if len(mos) != 0 {
for _, mo := range mos {
if mo.NID == nid {
res.Has = model.IsGet
res.Info = mo
return
}
}
res.Info = struct{}{}
res.Has = model.NotGet
return
}
mo, err := s.medalDao.OwnerBYNidMid(c, mid, nid)
if err != nil {
return
}
if mo == nil {
res.Info = struct{}{}
res.Has = model.NotGet
return
}
res.Info = mo
res.Has = model.IsGet
return
}
// MedalHomeInfo return user mdeal home info.
func (s *Service) MedalHomeInfo(c context.Context, mid int64) (res []*model.MedalHomeInfo, err error) {
var (
nos []*model.MedalOwner
mia = s.medalInfoAll
activatedNid int64
)
res = make([]*model.MedalHomeInfo, 0)
if nos, err = s.OwnerMedal(c, mid); err != nil {
log.Error("s.medalDao.MedalOwnersCache(%d) error(%v)", mid, err)
return
}
if activatedNid, err = s.ActivatedMedalID(c, mid); err != nil {
log.Error("s.medalDao.MedalActivatedCache(%d) error(%v)", mid, err)
return
}
if (len(nos)) == 0 {
return
}
if len(nos) > 4 {
nos = nos[0:4]
}
for _, no := range nos {
mhi := &model.MedalHomeInfo{}
mhi.NID = no.NID
if _, ok := mia[no.NID]; ok {
mhi.Name = mia[no.NID].Name
mhi.Description = mia[no.NID].Description
mhi.Image = mia[no.NID].Image
mhi.Level = mia[no.NID].LevelDesc
mhi.ImageSmall = mia[no.NID].ImageSmall
}
if activatedNid == no.NID {
mhi.IsActivated = model.OwnerInstall
}
res = append(res, mhi)
}
return
}
// MedalUserInfo return medal user info.
func (s *Service) MedalUserInfo(c context.Context, mid int64, ip string) (res *model.MedalUserInfo, err error) {
var (
levelInfo *memmdl.LevelInfo
memInfo *memmdl.BaseInfo
nid int64
trueLove bool
)
if levelInfo, err = s.memRPC.Level(c, &memmdl.ArgMid2{Mid: mid, RealIP: ip}); err != nil || levelInfo == nil {
log.Error("s.memRPC.Level(%+v) error(%v)", &memmdl.ArgMid2{Mid: mid, RealIP: ip}, err)
return
}
if memInfo, err = s.memRPC.Base(c, &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}); err != nil || memInfo == nil {
log.Error("s.memRPC.Base(%+v) error(%v)", &memmdl.ArgMemberMid{Mid: mid, RemoteIP: ip}, err)
return
}
if nid, err = s.ActivatedMedalID(c, mid); err != nil {
log.Error("MedalUserInfo s.medalDao.MedalActivatedCache(%d) error(%v)", mid, err)
err = nil
}
res = &model.MedalUserInfo{
Name: memInfo.Name,
Face: memInfo.Face,
Level: levelInfo.Cur,
NID: nid,
}
if mi, ok := s.medalInfoAll[nid]; ok {
res.ImageSmall = mi.ImageSmall
}
if trueLove, err = s.medalDao.GetWearedfansMedal(c, mid, 2); err != nil {
log.Warn("s.medalDao.GetWearedfansMedal(%d), err(%+v)", mid, err)
}
res.TrueLove = trueLove
return
}
// MedalInstall install or uninstall medal.
func (s *Service) MedalInstall(c context.Context, mid, nid int64, isActivated int8) (err error) {
var (
nos []*model.MedalOwner
isGet bool
)
if nos, err = s.OwnerMedal(c, mid); err != nil {
log.Error("s.medalDao.MedalOwnersCache(%d) error(%v)", mid, err)
return
}
for _, no := range nos {
if no.NID == nid {
isGet = true
break
}
}
if !isGet {
err = ecode.MedalNotGet
return
}
switch isActivated {
case model.OwnerUninstall:
if err = s.medalDao.UninstallMedalOwner(c, mid, nid); err != nil {
err = errors.WithStack(err)
return
}
s.medalDao.DelMedalActivatedCache(c, mid)
case model.OwnerInstall:
if err = s.medalDao.UninstallAllMedalOwner(c, mid, nid); err != nil {
err = errors.WithStack(err)
return
}
if err = s.medalDao.InstallMedalOwner(c, mid, nid); err != nil {
err = errors.WithStack(err)
return
}
s.medalDao.SetMedalActivatedCache(c, mid, nid)
default:
log.Error("unkonw action(%d)", isActivated)
}
s.addNotify(func() {
s.accNotify(context.Background(), mid, model.AccountNotifyUpdateMedal)
})
return
}
// MedalPopup return medal popup.
func (s *Service) MedalPopup(c context.Context, mid int64) (res *model.MedalPopup, err error) {
nid, err := s.medalDao.PopupCache(c, mid)
if err != nil {
return
}
if nid <= 0 {
return
}
mi, ok := s.medalInfoAll[nid]
if !ok {
return
}
res = &model.MedalPopup{
NID: mi.ID,
Name: mi.Name,
Image: mi.Image,
}
s.medalDao.DelPopupCache(c, mid)
return
}
// MedalMyInfo return medal my info.
func (s *Service) MedalMyInfo(c context.Context, mid int64) (res []*model.MedalMyInfos, err error) {
var nos []*model.MedalOwner
res = make([]*model.MedalMyInfos, 0)
if nos, err = s.OwnerMedal(c, mid); err != nil {
log.Error("s.medalDao.MedalOwnersCache(%d) error(%v)", mid, err)
return
}
if len(nos) == 0 {
return
}
mia := s.medalInfoAll
activatedNid, _ := s.ActivatedMedalID(c, mid)
popNid, _ := s.medalDao.PopupCache(c, mid)
s.medalDao.DelPopupCache(c, mid)
list1 := make([]*model.MedalMyInfo, 0)
list2 := make([]*model.MedalMyInfo, 0)
list3 := make([]*model.MedalMyInfo, 0)
for _, no := range nos {
mi, ok := mia[no.NID]
if !ok {
return
}
li := &model.MedalMyInfo{}
if activatedNid == no.NID {
li.IsActivated = 1
}
if popNid == no.NID {
li.IsNewGet = 1
}
li.MedalInfo = mi
li.GetTime = no.CTime
switch mi.Level {
case model.Level1:
list1 = append(list1, li)
case model.Level2:
list2 = append(list2, li)
case model.Level3:
list3 = append(list3, li)
}
}
for i := int32(1); i <= 3; i++ {
re := &model.MedalMyInfos{}
re.Name = medalLevel[i]
switch i {
case model.Level1:
re.List = list1
case model.Level2:
re.List = list2
case model.Level3:
re.List = list3
}
if len(re.List) > 0 {
re.Count = int32(len(re.List))
res = append(res, re)
}
}
return
}
// MedalAllInfo return medal all info.
func (s *Service) MedalAllInfo(c context.Context, mid int64) (res *model.MedalAllInfos, err error) {
rnid, _ := s.medalDao.RedPointCache(c, mid)
anid, _ := s.ActivatedMedalID(c, mid)
res = &model.MedalAllInfos{
List: make(map[int64]*model.MedalCategoryInfo),
RedPoint: rnid > 0,
HasActivated: anid,
}
var getMap = make(map[int64]bool)
mos, _ := s.OwnerMedal(c, mid)
for _, mo := range mos {
res.HasGet = append(res.HasGet, mo.NID)
getMap[mo.NID] = true
}
var allGids = make(map[int64]*model.MedalGroup)
for i := 0; i < len(s.medalGroupAll); i++ {
allGids[s.medalGroupAll[i].ID] = s.medalGroupAll[i]
}
var medals = make([][]*model.MedalInfo, len(s.medalInfoAll))
for _, mi := range s.medalInfoAll {
medals[mi.GID] = append(medals[mi.GID], mi)
}
var tmpPidNotZeros = make(map[int64][]*model.MedalItemInfo)
var pids []int64
for _, agi := range allGids {
if agi.PID == 0 {
pids = append(pids, agi.ID)
continue
}
if medals[agi.ID] == nil {
continue
}
ms := medals[agi.ID]
sort.Slice(ms, func(i, j int) bool {
return ms[i].Sort < ms[j].Sort
})
left := ms[len(ms)-1]
tmpPidNotZero := &model.MedalItemInfo{
Left: left,
Count: int32(len(ms)),
Right: ms,
}
for i := 0; i < len(ms); i++ {
if _, ok := getMap[ms[i].ID]; !ok {
tmpPidNotZero.Left = ms[i]
break
}
}
tmpPidNotZeros[agi.PID] = append(tmpPidNotZeros[agi.PID], tmpPidNotZero)
}
for _, pid := range pids {
var medalCategory = &model.MedalCategoryInfo{}
if category, ok := tmpPidNotZeros[pid]; ok {
medalCategory.Count = int32(len(category))
medalCategory.Data = category
} else {
var pidIsZeros = make([]*model.MedalItemInfo, 0)
for _, m := range medals[pid] {
var pidIsZero = &model.MedalItemInfo{Left: m, Count: 1}
pidIsZeros = append(pidIsZeros, pidIsZero)
}
medalCategory.Count = int32(len(pidIsZeros))
medalCategory.Data = pidIsZeros
}
medalCategory.Name = allGids[pid].Name
res.List[int64(allGids[pid].Rank)] = medalCategory
}
return
}
func (s *Service) loadMedal() {
medalInfoAll, err := s.medalDao.MedalInfoAll(context.TODO())
if err != nil {
log.Error("s.medalDao.MedalInfoAll error(%v)", err)
return
}
medalGroupAll, err := s.medalDao.MedalGroupAll(context.TODO())
if err != nil {
log.Error("s.medalDao.MedalGroupAll error(%v)", err)
return
}
s.medalInfoAll = medalInfoAll
s.medalGroupAll = medalGroupAll
}
// MedalActivated get user actived medal.
func (s *Service) MedalActivated(c context.Context, mid int64) (res *model.MedalInfo, err error) {
var nid int64
if nid, err = s.ActivatedMedalID(c, mid); err != nil {
err = errors.WithStack(err)
return
}
mia := s.medalInfoAll
for id, m := range mia {
if id == nid {
res = m
break
}
}
return
}
// MedalActivatedMulti Multi get the user activated medal info(at most 50).
func (s *Service) MedalActivatedMulti(c context.Context, mids []int64) (res map[int64]*model.MedalInfo, err error) {
if len(mids) > 50 {
err = ecode.RequestErr
return
}
res = make(map[int64]*model.MedalInfo)
var nids = make(map[int64]int64)
var miss = make([]int64, 0)
if nids, miss, err = s.medalDao.MedalsActivatedCache(c, mids); err != nil {
err = errors.WithStack(err)
return
}
for _, mismid := range miss {
mismid := mismid
var (
nid int64
canCache = true
)
if nid, err = s.medalDao.ActivatedOwnerByMid(c, mismid); err != nil {
err = errors.WithStack(err)
return
}
nids[mismid] = nid
if canCache {
s.addCache(func() {
s.medalDao.SetMedalActivatedCache(context.Background(), mismid, nid)
})
}
}
for mid, nid := range nids {
if mi, ok := s.medalInfoAll[nid]; ok {
res[mid] = mi
}
}
return
}
// ActivatedMedalID get user activated medal id.
func (s *Service) ActivatedMedalID(c context.Context, mid int64) (nid int64, err error) {
var (
notFound bool
canCache = true
)
if nid, notFound, err = s.medalDao.MedalActivatedCache(c, mid); err != nil {
err = errors.WithStack(err)
return
}
if notFound {
if nid, err = s.medalDao.ActivatedOwnerByMid(c, mid); err != nil {
err = errors.WithStack(err)
return
}
if canCache {
s.addCache(func() {
s.medalDao.SetMedalActivatedCache(context.Background(), mid, nid)
})
}
}
return
}
// OwnerMedal get user Owner medal.
func (s *Service) OwnerMedal(c context.Context, mid int64) (res []*model.MedalOwner, err error) {
var (
notFound bool
canCache = true
)
if res, notFound, err = s.medalDao.MedalOwnersCache(c, mid); err != nil {
err = errors.WithStack(err)
return
}
if notFound {
if res, err = s.medalDao.MedalOwnerByMid(c, mid); err != nil {
err = errors.WithStack(err)
return
}
if len(res) == 0 {
res = _emptyOwner
}
if canCache {
s.addCache(func() {
s.medalDao.SetMedalOwnersache(context.Background(), mid, res)
})
}
}
return
}

View File

@@ -0,0 +1,46 @@
package service
import (
"context"
"fmt"
"testing"
"go-common/app/service/main/usersuit/model"
. "github.com/smartystreets/goconvey/convey"
)
// TestService_MedalAllInfo
func TestService_MedalAllInfo(t *testing.T) {
var (
res = &model.MedalAllInfos{}
err error
)
Convey("need return something", t, func() {
res, err = s.MedalAllInfo(context.Background(), 1)
for _, re := range res.List {
fmt.Printf("Name:%+v \n", re.Name)
fmt.Printf("Count:%+v \n", re.Count)
fmt.Printf("re:%+v \n", re.Data)
}
fmt.Printf("err:%+v \n", err)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestService_MedalActivatedMulti(t *testing.T) {
var (
res = make(map[int64]*model.MedalInfo)
err error
)
Convey("need return something", t, func() {
res, err = s.MedalActivatedMulti(context.Background(), []int64{650460, 4780461, 14082, 631053})
for k, re := range res {
fmt.Printf("key:%v re:%+v \n", k, re)
}
fmt.Printf("err:%+v \n", err)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,18 @@
package service
import (
"context"
"strconv"
"go-common/app/service/main/usersuit/model"
"github.com/pkg/errors"
)
func (s *Service) accNotify(c context.Context, mid int64, action string) (err error) {
msg := &model.AccountNotify{UID: mid, Type: "updateUsersuit", Action: action}
if err = s.accountNotifyPub.Send(c, strconv.FormatInt(msg.UID, 10), msg); err != nil {
err = errors.Errorf("mid(%d) s.accountNotifyPub.Send(%+v) error(%+v)", msg.UID, msg, err)
}
return
}

View File

@@ -0,0 +1,15 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_accNotify(t *testing.T) {
Convey("should return err be nil", t, func() {
err := s.accNotify(context.Background(), 1, "updateMedal")
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,972 @@
package service
import (
"bytes"
"context"
"fmt"
"math/rand"
"net/url"
"strconv"
"time"
coinmdl "go-common/app/service/main/coin/model"
pointmdl "go-common/app/service/main/point/model"
"go-common/app/service/main/usersuit/model"
vipmdl "go-common/app/service/main/vip/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
const (
_gtbig int64 = 31
_entryID int64 = 30
_dayTime int64 = 86400
_expires int64 = 31 * 86400
_coin int64 = 0
_point int64 = 2
_bcoin int64 = 1
_subject = "头像挂件"
_reason = "购买挂件"
_ctype = "硬币"
_ptype = "积分"
_btype = "B币"
)
var (
_emptyPendant = &model.PendantEquip{Pid: 0}
)
type orderParam struct {
Mid, Pid, Expires, Price, Tp int64
Cost, IP, OrderID string
Pendant *model.Pendant
}
// GroupInfo get group info for web.
func (s *Service) GroupInfo(c context.Context) (res []*model.PendantGroupInfo, err error) {
return s.groupInfo, nil
}
// AllGroupInfo get all group information
func (s *Service) AllGroupInfo(c context.Context) (res []*model.PendantGroupInfo, err error) {
var (
pendant []*model.Pendant
temp []*model.PendantGroupInfo
)
if temp, err = s.pendantDao.PendantGroupInfo(c); err != nil || temp == nil {
err = ecode.PendantNotFound
return
}
res = make([]*model.PendantGroupInfo, 0)
for i, x := range temp {
if pendant, err = s.pendantInfoByGid(c, x.ID); err != nil {
return
}
if len(pendant) != 0 {
pgi := temp[i]
pgi.SubPendant = pendant
pgi.Number = int64(len(pendant))
res = append(res, pgi)
}
}
return
}
// GroupInfoByID get group info by gid
func (s *Service) GroupInfoByID(c context.Context, gid int64) (res *model.PendantGroupInfo, err error) {
var ok bool
if res, ok = s.groupMap[gid]; ok {
return
}
return
}
// PendantAll get all pendant info
func (s *Service) PendantAll(c context.Context) (res []*model.Pendant, err error) {
if res, err = s.pendantDao.PendantList(c); err != nil || res == nil {
err = ecode.PendantNotFound
return
}
for _, v := range res {
if err = s.assemblePendant(c, v); err != nil {
return
}
}
return
}
// PendantInfo get pendant info by id
func (s *Service) PendantInfo(c context.Context, pid int64) (res *model.Pendant, err error) {
var ok bool
if res, ok = s.pendantMap[pid]; !ok {
err = ecode.PendantNotFound
}
return
}
// PendantPoint return pendant which has point pay type
func (s *Service) PendantPoint(c context.Context, mid int64) (res []*model.Pendant, err error) {
var (
pkg []*model.PendantPackage
tmp = make(map[int64]int64)
)
if pkg, err = s.PackageInfo(c, mid); err != nil {
return
}
for _, v := range pkg {
tmp[v.Pid] = v.Expires
}
all := s.pendantInfo
res = make([]*model.Pendant, 0)
for _, v := range all {
if v.Status == model.PendantStatusOFF {
continue
}
if _, ok := tmp[v.ID]; ok {
v.Expires = tmp[v.ID]
}
if v.Point > 0 && v.Gid != _gtbig {
res = append(res, v)
}
}
l := len(res)
for i := 0; i < l; i++ {
now := res[i]
var nowGRank = &model.PendantGroupInfo{}
if nowGRank, err = s.GroupInfoByID(c, now.Gid); err != nil || nowGRank == nil {
log.Error("PendantPoint nowGRank(%+v) err(%+v)", nowGRank, err)
return
}
for j := i + 1; j < l-1; j++ {
next := res[j]
if now.Rank == next.Rank {
var nextGRank = &model.PendantGroupInfo{}
if nextGRank, err = s.GroupInfoByID(c, next.Gid); err != nil || nextGRank == nil {
log.Error("PendantPoint nextGRank(%+v) err(%+v)", nextGRank, err)
return
}
if nowGRank.Rank > nextGRank.Rank {
tp := res[i]
res[i] = res[j]
res[j] = tp
}
}
}
}
return
}
// OrderHistory get order info.
func (s *Service) OrderHistory(c context.Context, arg *model.ArgOrderHistory) (res []*model.PendantOrderInfo, count map[string]int64, err error) {
var (
p *model.Pendant
ct int64
)
if res, ct, err = s.pendantDao.OrderInfo(c, arg); err != nil {
return
}
for i, r := range res {
res[i].TimeLength = r.TimeLength / (_dayTime)
if p, err = s.PendantInfo(c, res[i].Pid); err != nil {
return
}
res[i].Name = p.Name
res[i].Image = p.Image
}
count = make(map[string]int64, 3)
count["page_current"] = arg.Page
count["page_size"] = 20
count["result_count"] = ct
return
}
// PackageInfo get package by mid
func (s *Service) PackageInfo(c context.Context, mid int64) (res []*model.PendantPackage, err error) {
defer func() {
if err == nil && res != nil && len(res) != 0 {
if err = s.pendantDao.DelRedPointCache(c, mid); err != nil {
log.Error("s.pendantDao.DelRedPointCache(%d) error(%+v)", mid, err)
err = nil
}
}
}()
var (
cache = false
t = time.Now().Unix()
)
if res, err = s.pendantDao.PKGCache(c, mid); err != nil {
return
}
for _, v := range res {
if v.Expires < t {
cache = true
res = nil
break
}
}
if !cache && res != nil {
return
}
cache = true
if res, err = s.pendantDao.PackageByMid(c, mid); err != nil {
return
}
if len(res) == 0 {
s.pendantDao.DelPKGCache(c, mid)
return
}
for k, v := range res {
if res[k].Pendant, err = s.PendantInfo(c, v.Pid); err != nil {
return
}
}
if cache && len(res) > 0 {
s.addCache(func() {
s.pendantDao.AddPKGCache(context.Background(), mid, res)
})
}
return
}
// Equipment return equipped pendant.
func (s *Service) Equipment(c context.Context, mid int64) (res *model.PendantEquip, err error) {
defer func() {
if err == nil && res != nil && res.Pid != 0 {
if res.Pendant, err = s.PendantInfo(c, res.Pid); err != nil {
log.Error("can not found user(%d) equip(%d) error(%+v)", mid, res.Pid, err)
err = nil
}
}
}()
var (
cache = true
noRow = false
t = time.Now().Unix()
)
res = &model.PendantEquip{}
if res, err = s.pendantDao.EquipCache(c, mid); err != nil {
log.Error("s.pendantDao.EquipCache error(%v)", err)
err = nil
cache = false
}
if res != nil {
if res.Pid == 0 {
res = nil
return
}
if t < res.Expires {
return
}
log.Info("user(%d) equip expire.", mid)
s.pendantDao.DelEquipCache(c, mid)
}
if res, noRow, err = s.pendantDao.EquipByMid(c, mid, t); err != nil {
log.Error("s.pendantDao.EquipByMid(%d), err(%+v)", mid, err)
return
}
if noRow {
res = _emptyPendant
}
if cache {
cres := &model.PendantEquip{
Mid: res.Mid,
Pid: res.Pid,
Expires: res.Expires,
Pendant: nil,
}
s.addCache(func() {
s.pendantDao.AddEquipCache(context.Background(), mid, cres)
})
}
return
}
// Equipments obtain equipemt from cache or db .
func (s *Service) Equipments(c context.Context, mids []int64) (res map[int64]*model.PendantEquip, err error) {
defer func() {
// err == nil && res != nil && res.Pid != 0
if err != nil || res == nil {
return
}
for _, e := range res {
if e.Pid == 0 {
continue
}
if e.Pendant, err = s.PendantInfo(c, e.Pid); err != nil {
log.Error("can not found user(%d) equip(%d) error(%+v)", e.Mid, e.Pid, err)
err = nil
}
}
}()
var (
cache = true
missedMID []int64
t = time.Now().Unix()
)
if len(mids) > 100 {
err = ecode.RequestErr
return
}
if res, missedMID, err = s.pendantDao.EquipsCache(c, mids); err != nil {
log.Error("s.pendantDao.EquipCache error(%v)", err)
err = nil
cache = false
}
for _, v := range res {
if t >= v.Expires {
delete(res, v.Mid)
}
}
if len(missedMID) == 0 {
return
}
equipMap, err := s.pendantDao.EquipByMids(c, missedMID, t)
if err != nil {
return
}
if len(equipMap) == 0 {
return
}
var cleanMids []int64
for _, v := range equipMap {
if t >= v.Expires {
cleanMids = append(cleanMids, v.Mid)
delete(equipMap, v.Mid)
continue
}
cv := &model.PendantEquip{
Mid: v.Mid,
Pid: v.Pid,
Expires: v.Expires,
Pendant: nil,
}
res[v.Mid] = cv
}
if len(cleanMids) > 0 {
s.pendantDao.DelEquipsCache(c, cleanMids)
}
if cache {
s.addCache(func() {
s.pendantDao.AddEquipsCache(context.Background(), equipMap)
})
}
return
}
// PackageByID get package by mid and pid
func (s *Service) PackageByID(c context.Context, mid, pid int64) (res *model.PendantPackage, err error) {
return s.pendantDao.PackageByID(c, mid, pid)
}
// OrderPendant order pendant
func (s *Service) OrderPendant(c context.Context, mid, pid, expires, tp int64) (res *model.PayInfo, err error) {
var pendant *model.Pendant
if pendant, err = s.PendantInfo(c, pid); err != nil || pendant == nil {
err = ecode.PendantNotFound
return
}
if pendant.Status == model.PendantStatusOFF {
err = ecode.PendantNotFound
return
}
// if pendant.Gid == _gtbig {
// return s.vipOrder(c, mid, pid, pendant)
// }
// common order
orderInfo := &orderParam{Mid: mid, Pid: pid, Expires: expires, Tp: tp, IP: metadata.String(c, metadata.RemoteIP), Pendant: pendant}
switch tp {
case 0:
return s.coinOrder(c, mid, pid, expires, orderInfo, pendant)
case 2:
return s.pointOrder(c, mid, pid, expires, orderInfo, pendant)
case 1:
return s.bcoinOrder(c, mid, pid, expires, orderInfo, pendant)
default:
err = ecode.RequestErr
return
}
}
// BatchGrantPendantByMid batch grant pendant.
func (s *Service) BatchGrantPendantByMid(c context.Context, pid, expire int64, mids []int64) (err error) {
var pendant *model.Pendant
if pendant, err = s.PendantInfo(c, pid); err != nil {
return
}
if pendant == nil {
err = ecode.PendantNotFound
return
}
s.addNotify(func() {
s.processBatchByMid(context.Background(), pid, expire, mids)
})
return
}
// BatchGrantPendantByPid batch grant pendant
func (s *Service) BatchGrantPendantByPid(c context.Context, mid int64, expires, pids []int64) (err error) {
s.addNotify(func() {
s.processBatchByPid(context.Background(), mid, expires, pids)
})
return
}
// PendantCallback pay call back func
func (s *Service) PendantCallback(c context.Context, arg *model.PendantOrderInfo) (err error) {
var (
order, o *model.PendantOrderInfo
tx *sql.Tx
argPkg, r *model.PendantPackage
)
if order, err = s.pendantDao.OrderInfoByID(c, arg.OrderID); err != nil {
return
} else if order == nil {
err = ecode.PendantOrderNotFound
return
}
if tx, err = s.pendantDao.BeginTran(c); err != nil {
return
}
argPkg = &model.PendantPackage{Mid: order.Mid, Pid: order.Pid, Expires: order.TimeLength, Status: 1, Type: 1}
if r, err = s.PackageByID(c, order.Mid, order.Pid); err != nil {
return
}
if r != nil {
if err = s.txUpdatePackagInfo(c, argPkg, r, tx); err != nil {
tx.Rollback()
log.Error("s.TxUpdatePackagInfo error %v", err)
return
}
} else {
t := time.Now().Unix()
argPkg.Expires += t
if _, err = s.pendantDao.TxAddPackage(c, argPkg, tx); err != nil {
tx.Rollback()
return
}
}
o = &model.PendantOrderInfo{OrderID: order.OrderID, Stauts: 1, PayID: order.PayID, IsCallback: 1, CallbackTime: time.Now().Unix()}
if _, err = s.pendantDao.TxUpdateOrderInfo(c, o, tx); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
return
}
s.pendantDao.DelPKGCache(c, order.Mid)
s.addNotify(func() {
if err = s.pendantDao.SetRedPointCache(context.Background(), order.Mid, order.Pid); err != nil {
log.Error("s.pendantDao.SetRedPointCache(%d,%d) error(%+v)", order.Mid, order.Pid, err)
err = nil
}
})
return
}
// EquipPendant equip pendant
func (s *Service) EquipPendant(c context.Context, mid, pid int64, status int8, source int64) error {
switch status {
case model.PendantEquipOFF:
if err := s.TakeOffPendant(c, mid); err != nil {
return err
}
case model.PendantEquipON:
if err := s.WearPendant(c, mid, pid, source); err != nil {
return err
}
default:
log.Warn("mid(%d) pid(%d) not exist status(%d)", mid, pid, status)
return ecode.RequestErr
}
// 操作成功,通知
s.addNotify(func() {
s.accNotify(context.Background(), mid, model.AccountNotifyUpdatePendant)
})
return nil
}
// TakeOffPendant 卸下挂件,并删除缓存
func (s *Service) TakeOffPendant(c context.Context, mid int64) error {
if _, err := s.pendantDao.UpEquipMID(c, mid); err != nil {
log.Error("s.pendantDao.UpEquipMID(%d) error(%+v)", mid, err)
return err
}
if cacheErr := s.pendantDao.DelEquipCache(c, mid); cacheErr != nil {
log.Error("s.pendantDao.DelEquipCache(%d) error(%+v)", mid, cacheErr)
}
return nil
}
// WearPendant 佩戴挂件
func (s *Service) WearPendant(c context.Context, mid, pid, source int64) error {
pd, err := s.PendantInfo(c, pid)
if err != nil || pd == nil {
return ecode.PendantNotFound
}
arg := &model.PendantEquip{
Mid: mid,
Pid: pid,
}
log.Info("wearPendant req params: mid=%v,pid=%v,source=%v", mid, pid, source)
source = func(source int64) int64 {
// 未按照规则传的挂件来源,设置为未知来源
if !model.IsValidSource(source) {
return model.UnknownEquipSource
}
// 不知道挂件来源,根据挂件属性组进行判断
if source == model.UnknownEquipSource {
source = model.EquipFromPackage
if pd.Gid == _gtbig {
return model.EquipFromVIP
}
}
return source
}(source)
// 背包挂件:在背包里面则直接佩戴
equipPkgPendant := func() error {
// 从背包里取这个挂件出来
var pkg *model.PendantPackage
pkg, err = s.PackageByID(c, mid, pid)
if err != nil {
return err
}
// 挂件不在背包里面
if pkg == nil {
return ecode.PendantPackageNotFound
}
// 挂件在背包里,则直接佩戴
arg.Expires = pkg.Expires
_, err = s.pendantDao.AddEquip(c, arg)
return err
}
// 大会员挂件:根据大会员是否过期判断是否佩戴
equipVipPendant := func() error {
// 判断是否是大会员挂件
if pd.Gid != _gtbig {
return ecode.PendantPackageNotFound
}
// 视用户大会员情况而定
var vi *model.VipInfo
vi, err = s.pendantDao.VipInfo(c, mid, metadata.String(c, metadata.RemoteIP))
if err != nil {
return err
}
if vi == nil {
return ecode.PendantGetVIPErr
}
if vi.VipStatus != vipmdl.VipStatusNotOverTime {
return ecode.VipUserInfoNotExit
}
arg.Expires = vi.VipDueDate / 1000
_, err = s.pendantDao.AddEquip(c, arg)
return err
}
// 佩戴过程
switch source {
case model.EquipFromPackage:
if err := equipPkgPendant(); err != nil {
log.Error("equipPkgPendant mid=%v,pid=%v,source=%v, err(%+v)", mid, pid, source, err)
return err
}
case model.EquipFromVIP:
if err := equipVipPendant(); err != nil {
log.Error("equipVipPendant mid=%v,pid=%v,source=%v, err(%+v)", mid, pid, source, err)
return err
}
default:
log.Warn("mid=%v,pid=%v,source=%v", mid, pid, source)
return ecode.RequestErr
}
// 佩戴成功删缓存
if delCacheErr := s.pendantDao.DelEquipCache(c, mid); delCacheErr != nil {
log.Error("s.pendantDao.DelEquipCache(%d) error(%+v)", mid, delCacheErr)
}
if addCacheErr := s.pendantDao.AddEquipCache(c, mid, arg); addCacheErr != nil {
log.Error("s.pendantDao.AddEquipCache(%d) error(%+v)", mid, addCacheErr)
}
return nil
}
func (s *Service) assemblePendant(c context.Context, pendant *model.Pendant) (err error) {
var (
ok bool
gid int64
price map[int64]*model.PendantPrice
)
if price, err = s.pendantDao.PendantPrice(c, pendant.ID); err != nil {
return
}
for _, v := range price {
if v.Type == _coin {
pendant.Coin = v.Price
}
if v.Type == _point {
pendant.Point = v.Price
}
if v.Type == _bcoin {
pendant.BCoin = v.Price
}
}
if gid, ok = s.pidMap[pendant.ID]; !ok {
log.Warn("s.pidMap pid(%d)", pendant.ID)
return
}
pendant.Gid = gid
return
}
func (s *Service) pendantInfoByGid(c context.Context, gid int64) (info []*model.Pendant, err error) {
var (
ok bool
ids []int64
temp []*model.Pendant
)
if ids, ok = s.gidMap[gid]; !ok || len(ids) == 0 {
log.Warn("s.gidMap gid(%d)", gid)
return
}
if temp, err = s.pendantDao.Pendants(c, ids); err != nil {
return
}
for i := range temp {
rp := temp[i]
if err = s.assemblePendant(c, rp); err != nil {
return
}
if rp.Gid == _gtbig || rp.Gid == _entryID {
info = append(info, rp)
} else if rp.BCoin != 0 || rp.Point != 0 || rp.Coin != 0 {
info = append(info, rp)
}
}
return
}
func (s *Service) grantOrderID() string {
var b bytes.Buffer
b.WriteString(time.Now().Format("20060102"))
b.WriteString(strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
b.WriteString(strconv.FormatInt(rand.Int63n(999999), 10))
return b.String()
}
func (s *Service) processOrder(c context.Context, p *orderParam) (err error) {
var (
order *model.PendantOrderInfo
tx *sql.Tx
arg, r *model.PendantPackage
)
if r, err = s.PackageByID(c, p.Mid, p.Pid); err != nil {
return
}
if tx, err = s.pendantDao.BeginTran(c); err != nil {
return
}
arg = &model.PendantPackage{Mid: p.Mid, Pid: p.Pid, Expires: p.Expires, Status: 1, Type: p.Tp}
if r != nil {
if err = s.txUpdatePackagInfo(c, arg, r, tx); err != nil {
tx.Rollback()
return
}
} else {
t := time.Now().Unix()
arg.Expires += t
if _, err = s.pendantDao.TxAddPackage(c, arg, tx); err != nil {
tx.Rollback()
return
}
}
order = &model.PendantOrderInfo{Mid: p.Mid, OrderID: p.OrderID, PayType: p.Tp, PayPrice: float64(p.Price), Stauts: 1, Pid: p.Pid, TimeLength: p.Expires, Cost: p.Cost, BuyTime: time.Now().Unix()}
if _, err = s.pendantDao.TxAddOrderInfo(c, order, tx); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("OrderPendant tx.Commit(), error(%v)", err)
return
}
s.pendantDao.DelPKGCache(c, p.Mid)
s.addNotify(func() {
if err = s.pendantDao.SetRedPointCache(context.Background(), p.Mid, p.Pid); err != nil {
log.Error("s.pendantDao.SetRedPointCache(%d,%d) error(%+v)", p.Mid, p.Pid, err)
err = nil
}
})
return
}
// processBatch a routine process batch
func (s *Service) processBatchByPid(c context.Context, mid int64, expires, pids []int64) {
var (
pendant *model.Pendant
err error
)
for i, v := range pids {
if expires[i] <= 0 || expires[i] > 3650 {
log.Error("mid:%v pid:%v expire:%v error %v", mid, v, expires[i], err)
continue
}
if pendant, err = s.PendantInfo(c, v); err != nil || pendant == nil {
continue
}
s.grantPendant(c, v, expires[i], mid)
}
}
func (s *Service) processBatchByMid(c context.Context, pid, expire int64, mids []int64) {
for _, mid := range mids {
s.grantPendant(c, pid, expire, mid)
}
}
func (s *Service) grantPendant(c context.Context, pid, expire, mid int64) {
var (
tx *sql.Tx
p *model.Pendant
arg, r *model.PendantPackage
history *model.PendantHistory
err error
)
if r, err = s.PackageByID(c, mid, pid); err != nil {
return
}
if p, err = s.PendantInfo(c, pid); err != nil {
return
}
if tx, err = s.pendantDao.BeginTran(c); err != nil {
return
}
expire *= _dayTime
arg = &model.PendantPackage{Mid: mid, Pid: pid, Expires: expire, Status: 1, Type: 5}
if p.Gid == _gtbig {
arg.IsVIP = 1
}
if r != nil {
if err = s.txUpdatePackagInfo(c, arg, r, tx); err != nil {
tx.Rollback()
return
}
} else {
t := time.Now().Unix()
arg.Expires += t
if _, err = s.pendantDao.TxAddPackage(c, arg, tx); err != nil {
tx.Rollback()
return
}
}
history = &model.PendantHistory{Mid: mid, Pid: pid, SourceType: 5, Expire: expire, OperatorName: "system", OperatorAction: 0}
if _, err = s.pendantDao.TxAddHistory(c, history, tx); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
return
}
s.pendantDao.DelPKGCache(c, mid)
s.addNotify(func() {
if err = s.pendantDao.SetRedPointCache(context.Background(), mid, pid); err != nil {
log.Error("s.pendantDao.SetRedPointCache(%d,%d) error(%+v)", mid, pid, err)
}
})
}
func (s *Service) txUpdatePackagInfo(c context.Context, arg *model.PendantPackage, item *model.PendantPackage, tx *sql.Tx) (err error) {
var (
n int64
t = time.Now().Unix()
)
if item.Expires < t {
arg.Expires += t
} else {
arg.Expires += item.Expires
}
if n, err = s.pendantDao.TxUpdatePackageInfo(c, arg, tx); err != nil {
return
} else if n <= 0 {
err = ecode.PendantPackageNotFound
return
}
return
}
// func (s *Service) vipOrder(c context.Context, mid, pid int64, pendantInfo *model.Pendant) (res *model.PayInfo, err error) {
// var (
// vipInfo *model.VipInfo
// r *model.PendantPackage
// pkg []*model.PendantPackage
// tx *sql.Tx
// )
// if vipInfo, err = s.pendantDao.VipInfo(c, mid, metadata.String(c, metadata.RemoteIP)); err != nil {
// return
// }
// switch vipInfo.VipType {
// case 0:
// case 2:
// err = ecode.PendantCanNotBuy
// return
// case 1:
// if vipInfo.VipStatus != 1 {
// err = ecode.PendantVIPOverdue
// return
// }
// if pkg, err = s.PackageInfo(c, mid); err != nil {
// log.Error("s.PackageInfo error(%v)", err)
// return
// }
// for _, x := range pkg {
// if (x.Pendant.Gid == _gtbig) && x.Type != 4 && x.Type != 5 {
// err = ecode.PendantAlreadyGet
// return
// }
// }
// if r, err = s.PackageByID(c, mid, pid); err != nil {
// return
// }
// if tx, err = s.pendantDao.BeginTran(c); err != nil {
// return
// }
// arg := &model.PendantPackage{Mid: mid, Pid: pid, Expires: _expires, Status: 1, Type: 3}
// if r != nil { // 续期
// if err = s.txUpdatePackagInfo(c, arg, r, tx); err != nil {
// tx.Rollback()
// return
// }
// } else {
// arg.IsVIP = 1
// arg.Expires += time.Now().Unix()
// if _, err = s.pendantDao.TxAddPackage(c, arg, tx); err != nil {
// tx.Rollback()
// return
// }
// }
// OrderID := s.grantOrderID()
// order := &model.PendantOrderInfo{Mid: mid, OrderID: OrderID, PayType: 3, Stauts: 1, Pid: pid, TimeLength: _expires, BuyTime: time.Now().Unix()}
// if _, err = s.pendantDao.TxAddOrderInfo(c, order, tx); err != nil {
// tx.Rollback()
// return
// }
// if err = tx.Commit(); err != nil {
// return
// }
// eq := &model.PendantEquip{
// Mid: mid,
// Pid: pid,
// Expires: arg.Expires,
// Pendant: pendantInfo,
// }
// if _, err = s.pendantDao.AddEquip(c, eq); err != nil {
// return
// }
// s.pendantDao.DelPKGCache(c, mid)
// s.pendantDao.DelEquipCache(c, mid)
// s.pendantDao.AddEquipCache(c, mid, eq)
// s.addNotify(func() {
// s.accNotify(context.Background(), mid, model.AccountNotifyUpdatePendant)
// if err = s.pendantDao.SetRedPointCache(context.Background(), mid, pid); err != nil {
// log.Error("s.pendantDao.SetRedPointCache(%d,%d) error(%+v)", mid, pid, err)
// err = nil
// }
// })
// return
// default:
// break
// }
// return
// }
func (s *Service) bcoinOrder(c context.Context, mid, pid, expires int64, orderInfo *orderParam, pendant *model.Pendant) (res *model.PayInfo, err error) {
var b bytes.Buffer
if pendant.BCoin <= 0 {
err = ecode.PendantPayTypeErr
return
}
var payID, cashURL string
bprice := float64(pendant.BCoin) / 100 * float64(expires)
orderInfo.Price = pendant.BCoin * expires
orderInfo.Expires = expires * _expires
b.WriteString(strconv.FormatFloat(bprice, 'f', 2, 64))
b.WriteString(_btype)
orderInfo.Cost = b.String()
orderInfo.OrderID = s.grantOrderID()
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("out_trade_no", orderInfo.OrderID)
params.Set("money", strconv.FormatFloat(bprice, 'f', 2, 64))
params.Set("subject", _subject)
params.Set("remark", fmt.Sprintf(_subject+" - %s%s个月", strconv.FormatInt(pid, 10), strconv.FormatInt(expires, 10)))
params.Set("merchant_id", s.merchantID)
params.Set("merchant_product_id", s.merchantProductID)
params.Set("platform_type", "3")
params.Set("iap_pay_type", "0")
params.Set("notify_url", s.callBackURL)
if payID, cashURL, err = s.pendantDao.PayBcoin(c, params, orderInfo.IP); err != nil {
return
}
order := &model.PendantOrderInfo{Mid: orderInfo.Mid, OrderID: orderInfo.OrderID, PayID: payID, PayType: orderInfo.Tp, PayPrice: float64(orderInfo.Price), Stauts: 2, Pid: orderInfo.Pid, TimeLength: orderInfo.Expires, Cost: orderInfo.Cost, BuyTime: time.Now().Unix()}
if _, err = s.pendantDao.AddOrderInfo(c, order); err != nil {
return
}
res = &model.PayInfo{OrderID: orderInfo.OrderID, OrderNum: payID, PayURL: cashURL}
return
}
func (s *Service) pointOrder(c context.Context, mid, pid, expires int64, orderInfo *orderParam, pendant *model.Pendant) (res *model.PayInfo, err error) {
var (
status int8
b bytes.Buffer
)
if pendant.Point <= 0 {
err = ecode.PendantPayTypeErr
return
}
orderInfo.Price = pendant.Point * expires
orderInfo.Expires = expires * _expires
b.WriteString(strconv.FormatInt(orderInfo.Price, 10))
b.WriteString(_ptype)
orderInfo.Cost = b.String()
orderInfo.OrderID = s.grantOrderID()
arg := &pointmdl.ArgPointConsume{Mid: mid, ChangeType: 6, RelationID: strconv.FormatInt(pid, 10), Point: orderInfo.Price, Remark: fmt.Sprintf("兑换挂件(%s)", strconv.FormatInt(pid, 10))}
if status, err = s.pointRPC.ConsumePoint(c, arg); err != nil {
return
}
if status != pointmdl.SUCCESS {
log.Warn("mid(%d) not consume point", mid)
return
}
err = s.processOrder(c, orderInfo)
return
}
func (s *Service) coinOrder(c context.Context, mid, pid, expires int64, orderInfo *orderParam, pendant *model.Pendant) (res *model.PayInfo, err error) {
var b bytes.Buffer
if pendant.Coin <= 0 {
err = ecode.PendantPayTypeErr
return
}
orderInfo.Price = pendant.Coin * expires
orderInfo.Expires = expires * _expires
b.WriteString(strconv.FormatInt(orderInfo.Price, 10))
b.WriteString(_ctype)
orderInfo.Cost = b.String()
orderInfo.OrderID = s.grantOrderID()
arg := &coinmdl.ArgModifyCoin{Mid: mid, Count: -float64(orderInfo.Price), Reason: _reason, CheckZero: 1}
if s.coinRPC.ModifyCoin(c, arg); err != nil {
return
}
err = s.processOrder(c, orderInfo)
return
}

View File

@@ -0,0 +1,740 @@
package service
import (
"context"
"github.com/bouk/monkey"
"reflect"
"testing"
"time"
"go-common/app/service/main/usersuit/dao/pendant"
"go-common/app/service/main/usersuit/model"
"go-common/library/ecode"
. "github.com/smartystreets/goconvey/convey"
)
func TestNew(t *testing.T) {
now := time.Now()
t.Logf("%d, %d", now.Unix(), now.UnixNano()/1e6)
var x = []string{"A", "B", "C"}
for index, value := range x {
t.Logf("index is (%+v) ,value is (%+v)", index, value)
}
}
// TestService_PendantAll test get all pendant infomartion
func TestService_PendantAll(t *testing.T) {
var (
res = make([]*model.Pendant, 0)
err error
)
Convey("need return something", t, func() {
res, err = s.PendantAll(context.Background())
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestService_PendantInfoByID(t *testing.T) {
var (
pid = int64(1)
res *model.Pendant
err error
)
Convey("need return something", t, func() {
res, err = s.PendantInfo(context.Background(), pid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
// TestService_PendantPoint test PendantPoint
func TestService_PendantPoint(t *testing.T) {
var (
mid int64 = 20606508
err error
res = make([]*model.Pendant, 0)
)
Convey("need return something", t, func() {
res, err = s.PendantPoint(context.Background(), mid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
// TestService_OrderHistory test order info
func TestService_OrderHistory(t *testing.T) {
var (
arg = &model.ArgOrderHistory{
OrderID: "201711141510654974657997844",
PayID: "201711141822545083995814",
Mid: 650454,
Pid: 17,
Status: 2,
PayType: 1,
StartTime: 1500311684,
EndTime: 1520311684,
Page: 1,
}
res = make([]*model.PendantOrderInfo, 0)
count = make(map[string]int64)
err error
)
Convey("need return something", t, func() {
res, count, err = s.OrderHistory(context.Background(), arg)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
So(count, ShouldNotBeNil)
})
}
// TestService_PackageByMid test package
func TestService_PackageByMid(t *testing.T) {
var (
mid int64 = 88889021
res = make([]*model.PendantPackage, 0)
err error
)
Convey("need return something", t, func() {
res, err = s.PackageInfo(context.Background(), mid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestService_Equipment(t *testing.T) {
var (
mid int64 = 111001965
vipPendant = &model.Pendant{
ID: 102,
Name: "文豪",
Status: 1, // 挂件状态:0.下线;1.在线
Gid: 1, // 挂件所在组
Rank: 1,
}
pendantEquip = &model.PendantEquip{
Mid: 111001965,
Pid: 102,
Expires: 1594076284,
Pendant: vipPendant,
}
)
Convey("need return something", t, func() {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "EquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) (*model.PendantEquip, error) {
return pendantEquip, nil
})
defer guard.Unpatch()
res, err := s.Equipment(context.Background(), mid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestService_GroupInfo(t *testing.T) {
var (
res []*model.PendantGroupInfo
err error
)
Convey("need return something .", t, func() {
res, err = s.GroupInfo(context.Background())
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
// OrderPendant(c context.Context, mid, pid, expires, tp int64, ip string)
func TestService_OrderPendant(t *testing.T) {
var (
mid int64 = 88889021
pid int64 = 10
res *model.PayInfo
err error
)
Convey("need return something", t, func() {
res, err = s.OrderPendant(context.Background(), mid, pid, time.Now().Unix(), 1)
So(err, ShouldNotBeNil)
So(res, ShouldBeNil)
})
}
func TestService_TakeOffPendant(t *testing.T) {
var (
mid int64 = 111001965
err error
)
Convey("need return something", t, func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "UpEquipMID", func(_ *pendant.Dao, _ context.Context, _ int64) (int64, error) {
return 0, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err = s.TakeOffPendant(context.Background(), mid)
So(err, ShouldBeNil)
})
}
func TestService_PendantInfo(t *testing.T) {
var (
pid int64 = 171
)
Convey("when everything goes well", t, func() {
res, err := s.PendantInfo(context.Background(), pid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
Convey("when occur an err", t, func() {
res, err := s.PendantInfo(context.Background(), 102)
So(err, ShouldNotBeNil)
So(res, ShouldBeNil)
})
}
func TestService_PackageByID(t *testing.T) {
var (
mid int64 = 111001965
pid int64 = 102
pendantPkg = &model.PendantPackage{
ID: 16281,
Mid: 111001965,
Pid: 98,
Expires: 1594076284,
Type: 0,
Status: 1,
IsVIP: 0,
Pendant: &model.Pendant{
ID: 1,
Name: "文豪",
Image: "/bfs/face/2e699edf6b51f61fc501247d4e826c97eebfb4fe.png",
ImageModel: "/bfs/face/95aa23fa00e619330a10e55cff328a7cace08e32.png",
Status: 1, // 挂件状态:0.下线;1.在线
Gid: 30, // 挂件所在组
Rank: 1,
},
}
)
Convey("when everything goes well", t, func() {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return pendantPkg, nil
})
defer guard.Unpatch()
res, err := s.PackageByID(context.Background(), mid, pid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
Convey("when occur an err", t, func() {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return pendantPkg, nil
})
defer guard.Unpatch()
res, err := s.PackageByID(context.Background(), mid, pid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
//func TestService_PendantInfoAA(t *testing.T) {
// var (
// pid int64 = 102
// )
// Convey("when everything goes well", t, func() {
// guard := monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
// return nil, nil
// })
// defer guard.Unpatch()
// res, err := s.PendantInfoAA(context.Background(), pid)
// println(err == nil)
// println(res == nil)
// })
//}
func TestService_WearPendant(t *testing.T) {
mid, vipPendant, vipPkg, pkgPendant, notVipPkg, vipInfo := preEquipPendant()
Convey("test WearPendant", t, func() {
Convey("when everything goes well", func() {
// 穿戴挂件与来源一致 vip挂件挂件在背包里面
Convey("1) test vip pendent", func() {
// 1查询挂件信息 //pendants,err := s.PendantInfo(context.Background(),pid)
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return vipPendant, nil
})
// 2根据挂件ID查询挂件所在背包的信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return vipPkg, nil
})
// 3) 穿戴
// vip挂件需要查询vip信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "VipInfo", func(_ *pendant.Dao, _ context.Context, _ int64, _ string) (*model.VipInfo, error) {
return vipInfo, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
// 4穿戴成功 删除缓存
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
// 5重新添加缓存
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 206, model.EquipFromVIP)
Convey("pendant(vip) source(EquipFromVIP) ,then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴挂件与来源一致 vip挂件挂件不在背包里面
Convey("2) test vip pendant and this pendant not exist in package", func() {
// 1查询挂件信息 //pendants,err := s.PendantInfo(context.Background(),pid)
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return vipPendant, nil
})
// 2根据挂件ID查询挂件所在背包的信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return nil, ecode.PendantPackageNotFound
})
// 3) 穿戴
// vip挂件需要查询vip信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "VipInfo", func(_ *pendant.Dao, _ context.Context, _ int64, _ string) (*model.VipInfo, error) {
return vipInfo, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
// 4穿戴成功 删除缓存
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
// 5重新添加缓存
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.EquipFromVIP)
Convey("pendant(vip) source(EquipFromVIP), but allow it not exist in pkg, then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴挂件与来源一致 :背包挂件(挂件必须在背包中)
Convey("3) test pkg pendent", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return pkgPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.EquipFromPackage)
Convey("pendant(pkg) source(EquipFromPackage) ,then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 来源未知 :是背包挂件(挂件必须在背包中)
Convey("4) test pkg pendent", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return pkgPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.UnknownEquipSource)
Convey("pendant(pkg) source(UnknownEquipSource)->source(EquipFromPackage) ,then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 来源未知 是vip挂件(挂件在背包里面)
Convey("5) test vip pendent", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return vipPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return vipPkg, nil
})
// vip挂件需要查询vip信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "VipInfo", func(_ *pendant.Dao, _ context.Context, _ int64, _ string) (*model.VipInfo, error) {
return vipInfo, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.UnknownEquipSource)
Convey("pendant(vip) source(UnknownEquipSource)->source(EquipFromVIP) ,then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 来源未知 是vip挂件(挂件不在背包里面)
Convey("6) test vip pendent", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return vipPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return vipPkg, ecode.PendantPackageNotFound
})
// vip挂件需要查询vip信息
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "VipInfo", func(_ *pendant.Dao, _ context.Context, _ int64, _ string) (*model.VipInfo, error) {
return vipInfo, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.UnknownEquipSource)
Convey("pendant(vip) source(UnknownEquipSource)->source(EquipFromVIP), vip pendant allow not exist in package, then err should be nil", func() {
So(err, ShouldBeNil)
})
})
})
Convey("when occur an err", func() {
// 穿戴一个背包挂件非vip挂件挂件来源是vip报错
Convey("1) test pkg pendent and source(EquipFromVIP)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return pkgPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.EquipFromVIP)
Convey("pendant(pkg) source(EquipFromPackage), then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 穿戴一个背包挂件非vip挂件挂件来源是背包但是挂件不在背包里报错
Convey("2) test pkg pendent and source(pkg), but not exist in package", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return pkgPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, ecode.PendantPackageNotFound
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.EquipFromPackage)
Convey("pendant(pkg) source(EquipFromPackage), but not exist in package, then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 穿戴一个vip挂件挂件来源是背包但是挂件不在背包里报错
Convey("3) test vip pendent and source(pkg), but not exist in package", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return vipPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, ecode.PendantPackageNotFound
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.EquipFromPackage)
Convey("pendant(vip) source(EquipFromPackage), but not exist in package, then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 来源未知 :是背包挂件(挂件不在背包里面),报错
Convey("4) test pkg pendent and source(pkg), but not exist in package", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "PendantInfo", func(_ *Service, _ context.Context, _ int64) (*model.Pendant, error) {
return pkgPendant, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "PackageByID", func(_ *pendant.Dao, _ context.Context, _ int64, _ int64) (*model.PendantPackage, error) {
return notVipPkg, ecode.PendantPackageNotFound
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquip", func(_ *pendant.Dao, _ context.Context, _ *model.PendantEquip) (int64, error) {
return 1, nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "DelEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64) error {
return nil
})
monkey.PatchInstanceMethod(reflect.TypeOf(s.pendantDao), "AddEquipCache", func(_ *pendant.Dao, _ context.Context, _ int64, _ *model.PendantEquip) error {
return nil
})
defer monkey.UnpatchAll()
err := s.WearPendant(context.Background(), mid, 0, model.UnknownEquipSource)
Convey("pendant(pkg) source(UnknownEquipSource)->source(EquipFromPackage), but not exist in package, then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
})
})
}
func preEquipPendant() (mid int64, vipPendant *model.Pendant, vipPkg *model.PendantPackage, pkgPendant *model.Pendant, notVipPkg *model.PendantPackage, vipInfo *model.VipInfo) {
mid = 111001965
vipPendant = &model.Pendant{
ID: 102,
Name: "文豪",
Status: 1, // 挂件状态:0.下线;1.在线
Gid: 31, // 挂件所在组
Rank: 1,
}
pkgPendant = &model.Pendant{
ID: 98,
Name: "bilibili春",
Status: 1, // 挂件状态:0.下线;1.在线
Gid: 30, // 挂件所在组
Rank: 1,
}
vipPkg = &model.PendantPackage{
ID: 16281,
Mid: 111001965,
Pid: 98,
Expires: 1594076284,
Type: 0,
Status: 1,
IsVIP: 0,
Pendant: pkgPendant,
}
notVipPkg = &model.PendantPackage{
ID: 16282,
Mid: 111001965,
Pid: 98,
Expires: 1594076284,
Type: 0,
Status: 1,
IsVIP: 0,
Pendant: vipPendant,
}
vipInfo = &model.VipInfo{
Mid: 111001965,
VipType: 2,
VipStatus: 1,
VipDueDate: 1594076284,
}
return mid, vipPendant, vipPkg, pkgPendant, notVipPkg, vipInfo
}
//func TestService_EquipPendant(t *testing.T) {
// var (
// mid int64 = 111001965
// pid int64 = 10
// err error
// )
// Convey("need return something", func() {
// err = s.EquipPendant(context.Background(), mid, pid, 1, 1)
// So(err, ShouldNotBeNil)
// })
//}
func TestService_EquipPendant(t *testing.T) {
var (
mid int64 = 111001965
vipPid int64 = 102
notVipPid int64 = 98
wear int8 = 2 //1卸载 2装备
takeOff int8 = 1 //1卸载 2装备
)
Convey("test EquipPendant", t, func() {
Convey("when everything goes well", func() {
Convey("1) takeOff pendant(vip) source(EquipFromVIP)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "TakeOffPendant", func(_ *Service, _ context.Context, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, vipPid, takeOff, model.EquipFromVIP)
Convey("takeOff pendant(vip) source(EquipFromVIP),then err should be nil", func() {
So(err, ShouldBeNil)
})
})
Convey("2) takeOff pendant(pkg) source(EquipFromPackage)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "TakeOffPendant", func(_ *Service, _ context.Context, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, takeOff, model.EquipFromPackage)
Convey("takeOff pendant(vip) source(EquipFromVIP),then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴一个vip挂件挂件来源是vip正确
Convey("3) wear pendant(vip) source(EquipFromVIP)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, vipPid, wear, model.EquipFromVIP)
Convey("pendant(vip) source(EquipFromVIP), then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴一个vip挂件挂件来源是背包来源是背包的挂件必须存在背包中正确
Convey("4) wear pendant(vip) source(EquipFromPackage)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, vipPid, wear, model.EquipFromPackage)
Convey("pendant(vip) source(EquipFromPackage), then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴一个背包挂件非vip挂件挂件来源是背包正确
Convey("5) wear pendant(pkg) source(EquipFromPackage)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, wear, model.EquipFromPackage)
Convey("pendant(pkg) source(EquipFromPackage), then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴一个vip挂件挂件来源是 未知,正确
Convey("6) wear pendant(vip) source(UnknownEquipSource)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, vipPid, wear, model.UnknownEquipSource)
Convey("pendant(vip) source(UnknownEquipSource)->source(EquipFromVIP), then err should be nil", func() {
So(err, ShouldBeNil)
})
})
// 穿戴一个背包挂件(存在背包里面),挂件来源是 未知,正确
Convey("7) wear pendant(pkg) source(UnknownEquipSource)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return nil
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, wear, model.UnknownEquipSource)
Convey("pendant(pkg) source(UnknownEquipSource)->source(EquipFromPackage), then err should be nil", func() {
So(err, ShouldBeNil)
})
})
})
Convey("wear pendant when occur an err", func() {
// 穿戴一个背包挂件非vip挂件挂件来源是vip报错
Convey("1) wear pendant(pkg) source(EquipFromVIP)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return ecode.PendantPackageNotFound
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, wear, model.EquipFromVIP)
Convey("pendant(pkg) source(EquipFromVIP),then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 穿戴一个背包挂件非vip挂件挂件来源是背包但是挂件不在背包里报错
Convey("2) wear pendant(pkg) source(EquipFromPackage)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return ecode.PendantPackageNotFound
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, wear, model.EquipFromPackage)
Convey("pendant(pkg) source(EquipFromPackage),but it not exist in package, then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 穿戴一个vip挂件挂件来源是背包但是挂件不在背包里报错
Convey("3) wear pendant(vip) source(EquipFromPackage)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return ecode.PendantPackageNotFound
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, vipPid, wear, model.EquipFromPackage)
Convey("pendant(vip) source(EquipFromPackage), then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
// 来源未知 :是背包挂件(挂件不在背包里面),报错
Convey("4) wear pendant(pkg) source(UnknownEquipSource)", func() {
monkey.PatchInstanceMethod(reflect.TypeOf(s), "WearPendant", func(_ *Service, _ context.Context, _ int64, _ int64, _ int64) error {
return ecode.PendantPackageNotFound
})
defer monkey.UnpatchAll()
err := s.EquipPendant(context.Background(), mid, notVipPid, wear, model.UnknownEquipSource)
Convey("pendant(pkg) source(UnknownEquipSource)->source(EquipFromPackage), but it not exist in package, then err should not be nil", func() {
So(err, ShouldNotBeNil)
})
})
})
})
}

View File

@@ -0,0 +1,23 @@
package service
import (
"context"
"go-common/app/service/main/usersuit/model"
)
// PointFlag .
func (s *Service) PointFlag(c context.Context, arg *model.ArgMID) (pf *model.PointFlag, err error) {
var pid, nid int64
if pid, err = s.pendantDao.RedPointCache(c, arg.MID); err != nil {
return
}
if nid, err = s.medalDao.RedPointCache(c, arg.MID); err != nil {
return
}
pf = &model.PointFlag{
Pendant: pid > 0,
Medal: nid > 0,
}
return
}

View File

@@ -0,0 +1,194 @@
package service
import (
"context"
"time"
coinrpc "go-common/app/service/main/coin/api/gorpc"
memrpc "go-common/app/service/main/member/api/gorpc"
pointrpc "go-common/app/service/main/point/rpc/client"
"go-common/app/service/main/usersuit/conf"
inviteDao "go-common/app/service/main/usersuit/dao/invite"
medalDao "go-common/app/service/main/usersuit/dao/medal"
pendantDao "go-common/app/service/main/usersuit/dao/pendant"
"go-common/app/service/main/usersuit/model"
"go-common/library/log"
"go-common/library/queue/databus"
)
// Service struct of service.
type Service struct {
d *inviteDao.Dao
pendantDao *pendantDao.Dao
medalDao *medalDao.Dao
// conf
c *conf.Config
missch chan func()
notifych chan func()
memRPC *memrpc.Service
coinRPC *coinrpc.Service
pointRPC *pointrpc.Service
merchantID string
merchantProductID string
callBackURL string
groupInfo []*model.PendantGroupInfo
pendantInfo []*model.Pendant
groupMap map[int64]*model.PendantGroupInfo
pendantMap map[int64]*model.Pendant
medalInfoAll map[int64]*model.MedalInfo
medalGroupAll []*model.MedalGroup
accountNotifyPub *databus.Databus
gidMap map[int64][]int64
pidMap map[int64]int64
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
d: inviteDao.New(c),
pendantDao: pendantDao.New(c),
medalDao: medalDao.New(c),
missch: make(chan func(), 10240),
notifych: make(chan func(), 1024000),
memRPC: memrpc.New(c.GORPCClient.Member),
coinRPC: coinrpc.New(c.GORPCClient.Coin),
pointRPC: pointrpc.New(c.GORPCClient.Point),
merchantID: c.PayInfo.MerchantID,
merchantProductID: c.PayInfo.MerchantProductID,
callBackURL: c.PayInfo.CallBackURL,
pendantMap: make(map[int64]*model.Pendant),
accountNotifyPub: databus.New(conf.Conf.AccountNotify),
gidMap: make(map[int64][]int64),
pidMap: make(map[int64]int64),
}
s.loadGidRefPid()
s.loadMedal()
s.refreshGroup(context.TODO())
go s.loadgidrefpidproc()
go s.cacheproc()
go s.refreshGroup(context.TODO())
go s.taskproc()
go s.notifyproc()
go s.loadmedalproc()
return
}
func (s *Service) addCache(f func()) {
select {
case s.missch <- f:
default:
log.Warn("cacheproc chan full")
}
}
// cacheproc is a routine for executing closure.
func (s *Service) cacheproc() {
for {
f := <-s.missch
f()
}
}
func (s *Service) addNotify(f func()) {
select {
case s.notifych <- f:
default:
log.Warn("addNotify chan full")
}
}
// notifyproc nofity clear cache
func (s *Service) notifyproc() {
for {
f := <-s.notifych
f()
}
}
// Close dao.
func (s *Service) Close() {
s.d.Close()
s.pendantDao.Close()
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
if err = s.d.Ping(c); err != nil {
return
}
return
}
// Task regular get appkey info
func (s *Service) taskproc() {
for {
time.Sleep(2 * time.Minute)
s.refreshGroup(context.TODO())
}
}
// rerefreshGroup when group info changed
func (s *Service) refreshGroup(c context.Context) {
var err error
s.groupInfo, err = s.AllGroupInfo(c)
if err != nil {
log.Error("s.AllGroupInfo error(%+v)", err)
return
}
s.pendantInfo, err = s.PendantAll(c)
if err != nil {
log.Error("s.PendantAll error(%+v)", err)
return
}
groupMap := make(map[int64]*model.PendantGroupInfo)
pendantMap := make(map[int64]*model.Pendant)
for _, v := range s.groupInfo {
if _, ok := groupMap[v.ID]; !ok {
groupMap[v.ID] = v
}
}
for _, v := range s.pendantInfo {
if _, ok := pendantMap[v.ID]; !ok {
pendantMap[v.ID] = v
}
}
s.pendantMap = pendantMap
s.groupMap = groupMap
}
func (s *Service) loadGidRefPid() {
var (
err error
c = context.TODO()
pidMap map[int64]int64
gidMap map[int64][]int64
)
if gidMap, pidMap, err = s.pendantDao.GIDRefPID(c); err != nil {
log.Error("s.pendantDao.GIDRefPID error(%+v)", err)
return
}
s.gidMap = gidMap
s.pidMap = pidMap
}
func (s *Service) loadmedalproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.loadMedal panic(%v)", x)
go s.loadMedal()
log.Info("service.loadMedal recover")
}
}()
for {
time.Sleep(2 * time.Minute)
s.loadMedal()
}
}
func (s *Service) loadgidrefpidproc() {
for {
time.Sleep(2 * time.Minute)
s.loadGidRefPid()
}
}

View File

@@ -0,0 +1,19 @@
package service
import (
"flag"
"path/filepath"
"go-common/app/service/main/usersuit/conf"
)
var (
s *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/convey-test.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"sort"
"go-common/app/service/main/usersuit/model"
"go-common/library/log"
"go-common/library/net/metadata"
)
// GroupPendantMid .
func (s *Service) GroupPendantMid(c context.Context, arg *model.ArgGPMID) (res []*model.GroupPendantList, err error) {
var (
ok bool
pids []int64
pe = &model.PendantEquip{}
pps = []*model.PendantPackage{}
mpp = map[int64]*model.PendantPackage{}
)
if pids, ok = s.gidMap[arg.GID]; !ok {
log.Warn("mid(%d) gid(%d) ip(%s) group pendants is empty", arg.MID, arg.GID, metadata.String(c, metadata.RemoteIP))
return
}
if arg.MID != 0 {
if pps, err = s.PackageInfo(c, arg.MID); err != nil {
return
}
if pe, err = s.Equipment(c, arg.MID); err != nil {
return
}
mpp = make(map[int64]*model.PendantPackage, len(pps))
for _, v := range pps {
mpp[v.Pid] = v
}
}
for _, v := range pids {
var (
status int32
expires int64
)
if pp, exists := mpp[v]; exists {
status = pp.Status
expires = pp.Expires
if pe != nil && pe.Pid == pp.Pid {
status = model.EquipPendantPKG
}
}
pendant, exists := s.pendantMap[v]
if !exists {
log.Warn("gid(%d) pid(%d) pendant is empty", arg.GID, v)
continue
}
psg := &model.GroupPendantList{
PkgStatus: status,
PkgExpires: expires,
Pendant: pendant,
}
res = append(res, psg)
}
sort.Slice(res, func(i int, j int) bool {
return res[i].Pendant.Rank < res[j].Pendant.Rank
})
return
}

View File

@@ -0,0 +1,23 @@
package service
import (
"context"
"testing"
"go-common/app/service/main/usersuit/model"
. "github.com/smartystreets/goconvey/convey"
)
// TestServiceGroupPendantMid .
func TestServiceGroupPendantMid(t *testing.T) {
arg := &model.ArgGPMID{
GID: 30,
MID: 1,
}
Convey("need return something", t, func() {
res, err := s.GroupPendantMid(context.Background(), arg)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}