Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

84
app/service/main/BUILD Normal file
View File

@@ -0,0 +1,84 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account:all-srcs",
"//app/service/main/account-recovery:all-srcs",
"//app/service/main/antispam:all-srcs",
"//app/service/main/archive:all-srcs",
"//app/service/main/assist:all-srcs",
"//app/service/main/bns:all-srcs",
"//app/service/main/broadcast:all-srcs",
"//app/service/main/card/api/grpc/v1:all-srcs",
"//app/service/main/card/cmd:all-srcs",
"//app/service/main/card/conf:all-srcs",
"//app/service/main/card/dao:all-srcs",
"//app/service/main/card/model:all-srcs",
"//app/service/main/card/server/grpc:all-srcs",
"//app/service/main/card/server/http:all-srcs",
"//app/service/main/card/service:all-srcs",
"//app/service/main/coin:all-srcs",
"//app/service/main/coupon:all-srcs",
"//app/service/main/dapper:all-srcs",
"//app/service/main/dapper-query:all-srcs",
"//app/service/main/dynamic:all-srcs",
"//app/service/main/favorite:all-srcs",
"//app/service/main/feed:all-srcs",
"//app/service/main/figure:all-srcs",
"//app/service/main/filter:all-srcs",
"//app/service/main/history:all-srcs",
"//app/service/main/identify:all-srcs",
"//app/service/main/identify-game:all-srcs",
"//app/service/main/location:all-srcs",
"//app/service/main/member:all-srcs",
"//app/service/main/msm:all-srcs",
"//app/service/main/open:all-srcs",
"//app/service/main/passport:all-srcs",
"//app/service/main/passport-auth:all-srcs",
"//app/service/main/passport-game:all-srcs",
"//app/service/main/passport-sns:all-srcs",
"//app/service/main/point:all-srcs",
"//app/service/main/push:all-srcs",
"//app/service/main/push-strategy:all-srcs",
"//app/service/main/rank:all-srcs",
"//app/service/main/relation:all-srcs",
"//app/service/main/reply-feed:all-srcs",
"//app/service/main/resource:all-srcs",
"//app/service/main/riot-search:all-srcs",
"//app/service/main/search:all-srcs",
"//app/service/main/secure:all-srcs",
"//app/service/main/seq-server:all-srcs",
"//app/service/main/share:all-srcs",
"//app/service/main/sms:all-srcs",
"//app/service/main/spy:all-srcs",
"//app/service/main/tag:all-srcs",
"//app/service/main/thumbup:all-srcs",
"//app/service/main/tv/api:all-srcs",
"//app/service/main/tv/cmd:all-srcs",
"//app/service/main/tv/internal/conf:all-srcs",
"//app/service/main/tv/internal/dao:all-srcs",
"//app/service/main/tv/internal/model:all-srcs",
"//app/service/main/tv/internal/pkg:all-srcs",
"//app/service/main/tv/internal/server/grpc:all-srcs",
"//app/service/main/tv/internal/server/http:all-srcs",
"//app/service/main/tv/internal/service:all-srcs",
"//app/service/main/ugcpay:all-srcs",
"//app/service/main/ugcpay-rank:all-srcs",
"//app/service/main/up:all-srcs",
"//app/service/main/upcredit:all-srcs",
"//app/service/main/usersuit:all-srcs",
"//app/service/main/videoup:all-srcs",
"//app/service/main/vip:all-srcs",
"//app/service/main/vipinfo:all-srcs",
"//app/service/main/workflow:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

6
app/service/main/OWNERS Normal file
View File

@@ -0,0 +1,6 @@
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- main
- new-main-service-project
- service

View File

@@ -0,0 +1,21 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account-recovery/cmd:all-srcs",
"//app/service/main/account-recovery/conf:all-srcs",
"//app/service/main/account-recovery/dao:all-srcs",
"//app/service/main/account-recovery/http:all-srcs",
"//app/service/main/account-recovery/model:all-srcs",
"//app/service/main/account-recovery/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,35 @@
## account-recovery
#### v1.3.5
> 1. 使用 grpc 请求account-service
#### v1.3.4
> 1. 修正 waitgroup 的用法
#### v1.3.3
> 1. 上次成功找回的提交时间以及成功找回次数保存到数据库
#### v1.3.2
> 1. 上次成功找回的申诉时间
#### v1.3.1
> 1. bfs key
#### v1.3.0
> 1.接入游戏账号找回
> 2.邮件日志记录
> 3.实名认证对比逻辑
#### v1.2.1
> 1.手机邮箱历史记录数据修复
#### v1.2.0
> 1.简化分页
#### v1.1.0
> 1.修改邮件发送内容(昵称改为用户名,账号找回链接)
> 2.修改台湾区为台湾
> 3.fix昵称校验
#### v1.0.0
1. 账号申诉找回

View File

@@ -0,0 +1,8 @@
# Owner
wanghuan01
zhoujiahui
# Author
huangyangyang
# Reviewer

View File

@@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- huangyangyang
- wanghuan01
- zhoujiahui
labels:
- main
- service
- service/main/account-recovery
options:
no_parent_owners: true
reviewers:
- huangyangyang

View File

@@ -0,0 +1,12 @@
# account-recovery-service
# 项目简介
1.
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,43 @@
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 = ["account-recovery-test.toml"],
importpath = "go-common/app/service/main/account-recovery/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/http:go_default_library",
"//library/ecode/tip: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,188 @@
# This is a TOML document. Boom
version = "1.0.0"
user = "nobody"
pid = "/tmp/account-recovery-service.pid"
dir = "./"
[log]
stdout = true
dir = "/data/log/account-recovery"
[mysql]
addr = "172.16.33.205"
dsn = "account:wx2U1MwXRyWEuURw@tcp(172.16.33.205:3306)/account?timeout=500ms&readTimeout=500ms&writeTimeout=500ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["account:wx2U1MwXRyWEuURw@tcp(172.16.33.205:3306)/account?timeout=500ms&readTimeout=500ms&writeTimeout=500ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
#readDSN = ["test:test@tcp(127.0.0.2:3306)/test? timeout=200ms&readTimeout=200ms&writeTimeout=200ms&parseTime=true&loc=Local&charset=utf8,utf8mb4","test:test@tcp(127.0.0.3:3306)/test?timeout=200ms&readTimeout=200ms&writeTimeout=200ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "1000ms"
execTimeout = "500ms"
tranTimeout = "2000ms"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[redis]
name = "account-recovery-service"
proto = "tcp"
addr = "127.0.0.1:6379"
idle = 10
active = 10
dialTimeout = "100ms"
readTimeout = "350ms"
writeTimeout = "350ms"
idleTimeout = "10s"
expire = "1m"
[AccRecover]
MidInfoURL = "http://passport.bilibili.co/intranet/acc/midinfo/byCon"
UpPwdURL = "http://passport.bilibili.co/intranet/acc/midinfo/pwd"
UpBatchPwdURL = "http://passport.bilibili.co/intranet/acc/midinfo/batchpwd"
CheckSafeURL = "http://passport.bilibili.co/intranet/acc/midinfo/check/safe"
GameURL = "http://interface.biligame.com/api/internal/activity.games"
CheckRegURL = "http://passport.bilibili.co/intranet/acc/midinfo/check/reg"
CheckUserURL = "http://manager.bilibili.co/x/admin/passport/userBindLog"
CheckCardURL = "http://api.bilibili.co/x/internal/member/realname/check"
CheckPwdURL = "http://api.bilibili.co/x/internal/passport/history/pwd/check"
GetLoginIPURL = "http://api.bilibili.co/x/internal/passport/records/loginlog"
GetUserInfoURL = "http://passport.bilibili.co/intranet/acc/detail"
[HttpClientConfig]
key = "e74eaed756f64154"
secret = "testSecret"
dial = "100ms"
timeout = "10s"
keepAlive = "60s"
timer = 1024
[HttpClientConfig.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 1.0
request = 100
[MailConf]
host = "smtp.exmail.qq.com"
port = 465
username = "manager@bilibili.com"
password = ""
[CaptchaConf]
TokenBID = "account"
tokenURL = "http://api.bilibili.co/x/internal/v1/captcha/token"
verifyURL = "http://api.bilibili.co/x/internal/v1/captcha/verify"
[AccountRPC]
timeout = "1s"
[locationrpc]
timeout = "1s"
[memberGRPC]
timeout = "1s"
[chanSize]
MailMsg = 999
[bm]
addr = "0.0.0.0:8000"
Timeout = "2s"
readTimeout = "10s"
writeTimeout = "10s"
[auth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://uat-dashboard-mng.bilibili.co"
dashboardCaller = "manager-go"
[auth.DsHTTPClient]
key = "manager-go"
secret = "949bbb2dd3178252638c2407578bc7ad"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[AESEncode]
salt = ""
aesKey = "0123456789abcdef"
[elastic]
host = "http://uat-manager.bilibili.co"
#host = "http://127.0.0.1:8080"
[elastic.HTTPClient]
key = "3c4e41f926e51656"
secret = "26a2095b60c24154521d24ae62b885bb"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[elastic.HTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[bfs]
key = "2wf7m69kng83cl5h"
secret = "jr0po1k2e9mtai5l8g3shn7dqxwc4u"
addr = "http://uat-bfs.bilibili.co/bfs/account/recovery"
bucket = "account"
timeout = "10s"
[databus]
[databus.UserActLog]
key = "2511663d546f1413"
secret = "cde3b480836cc76df3d635470f991caa"
group = "LogUserAction-MainSearch-P"
topic = "LogUserAction-T"
action = "pub"
buffer = 10240
name = "log-user-action/log-sub"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"

View File

@@ -0,0 +1,42 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/service/main/account-recovery/conf"
"go-common/app/service/main/account-recovery/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info(" account recovery start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
http.Init(conf.Conf)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("account recovery exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/service/main/account-recovery/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/elastic:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden: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,178 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/elastic"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/queue/databus"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Verify *verify.Config
Tracer *trace.Config
Redis *redis.Config
MySQL *sql.Config
Ecode *ecode.Config
// HTTPClientConfig
HTTPClientConfig *bm.ClientConfig
// AccRecover request URL info
AccRecover *AccRecover
// MailConfig
MailConf *Mail
// CaptchaConf
CaptchaConf *Captcha
// RPC config
LocationRPC *rpc.ClientConfig
// grpc
MemberGRPC *warden.ClientConfig
AccountGRPC *warden.ClientConfig
// ChanSize
ChanSize *ChanSize
// Auth
Auth *permit.Config
AESEncode *AESEncode
// elastic config
Elastic *elastic.Config
// Bfs
Bfs *Bfs
// DataBus databus
DataBus *DataBus
}
// ChanSize mail send channel size.
type ChanSize struct {
MailMsg int64
}
// AccRecover is a url config to request java api
type AccRecover struct {
MidInfoURL string
UpPwdURL string
UpBatchPwdURL string
CheckSafeURL string
GameURL string
CheckRegURL string
CheckUserURL string
CheckCardStatusURL string
CheckCardURL string
CheckPwdURL string
GetLoginIPURL string
GetUserInfoURL string
}
// Mail 邮件配置
type Mail struct {
Host string
Port int
Username, Password string
}
// Captcha 验证码配置
type Captcha struct {
TokenBID string
TokenURL string
VerifyURL string
}
// AESEncode aes encode
type AESEncode struct {
AesKey string
Salt string
}
// Bfs Bfs.
type Bfs struct {
Timeout xtime.Duration
Bucket string
Addr string
Key string
Secret string
}
// DataBus is
type DataBus struct {
UserActLog *databus.Config
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
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
}

View File

@@ -0,0 +1,86 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"aes.go",
"captcha.go",
"dao.go",
"email.go",
"mid_info.go",
"mysql.go",
"redis.go",
"req_rpc.go",
"user_act_log.go",
],
importpath = "go-common/app/service/main/account-recovery/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/dao/sqlbuilder:go_default_library",
"//app/service/main/account-recovery/model:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/location/model:go_default_library",
"//app/service/main/location/rpc/client:go_default_library",
"//app/service/main/member/api:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/elastic: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/net/metadata:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/gopkg.in/gomail.v2:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account-recovery/dao/sqlbuilder:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"captcha_test.go",
"dao_test.go",
"email_test.go",
"mid_info_test.go",
"mysql_test.go",
"redis_test.go",
"req_rpc_test.go",
"user_act_log_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/model:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,63 @@
package dao
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
)
//func pad(src []byte) []byte {
// padding := aes.BlockSize - len(src)%aes.BlockSize
// padText := bytes.Repeat([]byte{byte(padding)}, padding)
// return append(src, padText...)
//}
func unpad(src []byte) ([]byte, error) {
length := len(src)
unpadding := int(src[length-1])
if unpadding > length {
return nil, errors.New("unpad error. This could happen when incorrect encryption key is used")
}
return src[:(length - unpadding)], nil
}
//func (s *Service) encrypt(text string) (string, error) {
// msg := pad([]byte(text))
// cipherText := make([]byte, aes.BlockSize+len(msg))
// iv := cipherText[:aes.BlockSize]
// if _, err := io.ReadFull(rand.Reader, iv); err != nil {
// return "", err
// }
//
// cfb := cipher.NewCFBEncrypter(s.AESBlock, iv)
// cfb.XORKeyStream(cipherText[aes.BlockSize:], []byte(msg))
// finalMsg := base64.URLEncoding.EncodeToString(cipherText)
// return finalMsg, nil
//}
func (d *Dao) decrypt(text string) (string, error) {
decodedMsg, err := base64.URLEncoding.DecodeString(text)
if err != nil {
return "", err
}
if (len(decodedMsg) % aes.BlockSize) != 0 {
return "", errors.New("blocksize must be multipe of decoded message length")
}
iv := decodedMsg[:aes.BlockSize]
msg := decodedMsg[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(d.AESBlock, iv)
cfb.XORKeyStream(msg, msg)
unpadMsg, err := unpad(msg)
if err != nil {
return "", err
}
return string(unpadMsg), nil
}

View File

@@ -0,0 +1,47 @@
package dao
import (
"context"
"net/url"
"go-common/app/service/main/account-recovery/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
// GetToken get open token.
func (d *Dao) GetToken(c context.Context, bid string) (res *model.TokenResq, err error) {
params := url.Values{}
params.Add("bid", bid)
if err = d.httpClient.Get(c, d.c.CaptchaConf.TokenURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("GetToken HTTP request err(%v)", err)
return
}
if res.Code != 0 {
log.Error("GetToken service return err(%v)", res.Code)
err = ecode.Int(int(res.Code))
return
}
return
}
// Verify verify code.
func (d *Dao) Verify(c context.Context, code, token string) (ok bool, err error) {
params := url.Values{}
params.Add("token", token)
params.Add("code", code)
res := new(struct {
Code int `json:"code"`
})
if err = d.httpClient.Post(c, d.c.CaptchaConf.VerifyURL, metadata.String(c, metadata.RemoteIP), params, res); err != nil {
log.Error("Verify HTTP request err(%v)", err)
return
}
if res.Code != 0 {
log.Error("Verify service return err(%v)", res.Code)
err = ecode.Int(res.Code)
return
}
return true, nil
}

View File

@@ -0,0 +1,22 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoGetToken(t *testing.T) {
var (
c = context.Background()
bid = "account"
)
convey.Convey("GetToken", t, func(ctx convey.C) {
res, err := d.GetToken(c, bid)
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,90 @@
package dao
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/tls"
"go-common/app/service/main/account-recovery/conf"
account "go-common/app/service/main/account/api"
location "go-common/app/service/main/location/rpc/client"
member "go-common/app/service/main/member/api"
"go-common/library/cache/redis"
"go-common/library/database/elastic"
xsql "go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"gopkg.in/gomail.v2"
)
// Dao dao
type Dao struct {
c *conf.Config
redis *redis.Pool
db *xsql.DB
// httpClient
httpClient *bm.Client
// email
email *gomail.Dialer
es *elastic.Elastic
// rpc
locRPC *location.Service
// grpc
memberClient member.MemberClient
accountClient account.AccountClient
hashSalt []byte
AESBlock cipher.Block
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
redis: redis.NewPool(c.Redis),
db: xsql.NewMySQL(c.MySQL),
// httpClient
httpClient: bm.NewClient(c.HTTPClientConfig),
email: gomail.NewDialer(c.MailConf.Host, c.MailConf.Port, c.MailConf.Username, c.MailConf.Password),
es: elastic.NewElastic(c.Elastic),
locRPC: location.New(c.LocationRPC),
hashSalt: []byte(c.AESEncode.Salt),
}
dao.email.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
dao.AESBlock, _ = aes.NewCipher([]byte(c.AESEncode.AesKey))
var err error
if dao.memberClient, err = member.NewClient(c.MemberGRPC); err != nil {
panic(err)
}
if dao.accountClient, err = account.NewClient(c.AccountGRPC); err != nil {
panic(err)
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
return
}
if err = d.PingRedis(c); err != nil {
return
}
// TODO: if you need use mc,redis, please add
return
}

View File

@@ -0,0 +1,46 @@
package dao
import (
"flag"
"os"
"strings"
"testing"
"go-common/app/service/main/account-recovery/conf"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.account-recovery")
flag.Set("conf_token", "5fe12bcbf11eb0c368ee0c2d1f567184")
flag.Set("tree_id", "55382")
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/account-recovery-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.httpClient.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,23 @@
package dao
import (
"go-common/app/service/main/account-recovery/conf"
"go-common/library/log"
"gopkg.in/gomail.v2"
)
// SendMail send the email.
func (d *Dao) SendMail(body string, subject string, send ...string) (err error) {
log.Info("send mail send:%v", send)
msg := gomail.NewMessage()
msg.SetHeader("From", conf.Conf.MailConf.Username)
msg.SetHeader("To", send...)
msg.SetHeader("Subject", subject)
msg.SetBody("text/html", body, gomail.SetPartEncoding(gomail.Base64))
if err = d.email.DialAndSend(msg); err != nil {
log.Error("s.email.DialAndSend error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,23 @@
package dao
import (
"math/rand"
"strconv"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSendMail(t *testing.T) {
var (
body = strconv.Itoa(rand.Intn(100))
subject = "邮件测试hyy"
send = "2459593393@qq.com"
)
convey.Convey("SendMail", t, func(ctx convey.C) {
err := d.SendMail(body, subject, send)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,272 @@
package dao
import (
"context"
"net/url"
"strconv"
"strings"
"go-common/app/service/main/account-recovery/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
// GetMidInfo get mid info by more condition
func (d *Dao) GetMidInfo(c context.Context, qType string, qKey string) (v *model.MIDInfo, err error) {
params := url.Values{}
params.Set("q_type", qType)
params.Set("q_key", qKey)
res := new(struct {
Code int `json:"code"`
Data model.MIDInfo `json:"data"`
})
if err = d.httpClient.Get(c, d.c.AccRecover.MidInfoURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("GetMidInfo HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("GetMidInfo server err_code %d,params: qType=%s,qKey=%s", res.Code, qType, qKey)
err = ecode.ServerErr
return
}
log.Info("GetMidInfo url=%v, params: qType=%s,qKey=%s, res: %+v", d.c.AccRecover.MidInfoURL, qType, qKey, res)
return &res.Data, nil
}
// GetUserInfo get user info by mid
func (d *Dao) GetUserInfo(c context.Context, mid int64) (v *model.UserInfo, err error) {
params := url.Values{}
params.Add("mid", strconv.Itoa(int(mid)))
res := new(struct {
Code int `json:"code"`
Data model.UserInfo `json:"data"`
})
if err = d.httpClient.Get(c, d.c.AccRecover.GetUserInfoURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("GetUserInfo HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("GetUserInfo server err_code %d,params: mid=%d", res.Code, mid)
err = ecode.ServerErr
return
}
log.Info("GetUserInfo url=%v, params: mid=%d, res: %+v", d.c.AccRecover.GetUserInfoURL, mid, res)
return &res.Data, nil
}
// UpdatePwd update password
func (d *Dao) UpdatePwd(c context.Context, mid int64, operator string) (user *model.User, err error) {
params := url.Values{}
params.Set("mid", strconv.Itoa(int(mid)))
params.Set("operator", operator)
res := new(struct {
Code int `json:"code"`
Data model.User `json:"data"`
})
if err = d.httpClient.Post(c, d.c.AccRecover.UpPwdURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("UpdatePwd HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("UpdatePwd server err_code %d,params: mid=%d", res.Code, mid)
err = ecode.Int(res.Code)
return
}
log.Info("UpdatePwd url=%v, params: mid=%d,operator=%s, res: %+v", d.c.AccRecover.UpPwdURL, mid, operator, res)
return &res.Data, nil
}
// CheckSafe safe info
func (d *Dao) CheckSafe(c context.Context, mid int64, question int8, answer string) (check *model.Check, err error) {
params := url.Values{}
params.Add("mid", strconv.Itoa(int(mid)))
params.Add("question", strconv.Itoa(int(question)))
params.Add("answer", answer)
res := new(struct {
Code int `json:"code"`
Data model.Check `json:"data"`
})
if err = d.httpClient.Post(c, d.c.AccRecover.CheckSafeURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("CheckSafe HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("CheckSafe server err_code %d,params: mid=%d,question=%d,answer=%s", res.Code, mid, question, answer)
err = ecode.Int(res.Code)
return
}
log.Info("CheckSafe url=%v, params: mid=%d,question=%d,answer=%s, res: %+v", d.c.AccRecover.CheckSafeURL, mid, question, answer, res)
return &res.Data, nil
}
// GetUserType get user_type
func (d *Dao) GetUserType(c context.Context, mid int64) (gams []*model.Game, err error) {
params := url.Values{}
params.Add("mid", strconv.Itoa(int(mid)))
res := new(struct {
Code int `json:"code"`
Data []*model.Game `json:"items"`
})
if err = d.httpClient.Get(c, d.c.AccRecover.GameURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("GetUserType HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("GetUserType server err_code %d,params: mid=%d", res.Code, mid)
err = ecode.Int(res.Code)
return
}
log.Info("GetUserType url=%v, params: mid=%d, res: %+v", d.c.AccRecover.GameURL, mid, res)
return res.Data, nil
}
// CheckReg check reg info
func (d *Dao) CheckReg(c context.Context, mid int64, regTime int64, regType int8, regAddr string) (v *model.Check, err error) {
params := url.Values{}
params.Add("mid", strconv.Itoa(int(mid)))
params.Add("reg_time", strconv.FormatInt(regTime, 10))
params.Add("reg_type", strconv.Itoa(int(regType)))
params.Add("reg_addr", regAddr)
res := new(struct {
Code int `json:"code"`
Data model.Check `json:"data"`
})
if err = d.httpClient.Post(c, d.c.AccRecover.CheckRegURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("CheckReg HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("CheckReg server err_code %d,params: mid=%d,regTime=%d,regType=%d,regAddr=%s", res.Code, mid, regTime, regType, regAddr)
err = ecode.Int(res.Code)
return
}
log.Info("CheckReg url=%v, params: mid=%d,regTime=%d,regType=%d,regAddr=%s, res: %+v", d.c.AccRecover.CheckRegURL, mid, regTime, regType, regAddr, res)
return &res.Data, nil
}
// UpdateBatchPwd batch update password
func (d *Dao) UpdateBatchPwd(c context.Context, mids string, operator string) (userMap map[string]*model.User, err error) {
params := url.Values{}
params.Set("mids", mids)
params.Set("operator", operator)
res := new(struct {
Code int `json:"code"`
Data map[string]*model.User `json:"data"`
})
if err = d.httpClient.Post(c, d.c.AccRecover.UpBatchPwdURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("UpdateBatchPwd HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("UpdateBatchPwd server err_code %d,params: mids=%s", res.Code, mids)
err = ecode.Int(res.Code)
return
}
log.Info("UpdateBatchPwd url=%v, params: mid=%s,operator=%s, res: %+v", d.c.AccRecover.UpBatchPwdURL, mids, operator, res)
return res.Data, nil
}
// CheckCard check card
func (d *Dao) CheckCard(c context.Context, mid int64, cardType int8, cardCode string) (ok bool, err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("card_type", strconv.Itoa(int(cardType)))
params.Set("card_code", cardCode)
res := new(struct {
Code int `json:"code"`
Data bool `json:"data"`
})
if err = d.httpClient.Get(c, d.c.AccRecover.CheckCardURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("CheckCard HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("CheckCard server err_code %d,params: mid=%d,cardType=%d,cardCode=%s", res.Code, mid, cardType, cardCode)
err = ecode.Int(res.Code)
return
}
log.Info("CheckCard url=%v, params: mid=%d,cardType=%d,cardCode=%s, res: %+v", d.c.AccRecover.CheckCardURL, mid, cardType, cardCode, res)
return res.Data, nil
}
// CheckPwds check pwd
func (d *Dao) CheckPwds(c context.Context, mid int64, pwds string) (v string, err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("pwd", pwds)
res := new(struct {
Code int `json:"code"`
Data string `json:"data"`
})
if err = d.httpClient.Post(c, d.c.AccRecover.CheckPwdURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("CheckPwds HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("CheckPwds server err_code %d,params: mid=%d,pwds=%s", res.Code, mid, pwds)
err = ecode.Int(res.Code)
return
}
log.Info("CheckPwds url=%v, params: mid=%d,pwds=%s, res: %+v", d.c.AccRecover.CheckPwdURL, mid, pwds, res)
return res.Data, nil
}
// GetLoginIPs get login ip
func (d *Dao) GetLoginIPs(c context.Context, mid int64, limit int64) (ipInfo []*model.LoginIPInfo, err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("limit", strconv.FormatInt(limit, 10))
res := new(struct {
Code int `json:"code"`
Data []*model.LoginIPInfo `json:"data"`
})
if err = d.httpClient.Get(c, d.c.AccRecover.GetLoginIPURL, metadata.String(c, metadata.RemoteIP), params, &res); err != nil {
log.Error("GetLoginIPs HTTP request err %+v", err)
return
}
if res.Code != 0 {
log.Error("GetLoginIPs server err_code %d,params: mid=%d,limit=%d", res.Code, mid, limit)
err = ecode.ServerErr
return
}
log.Info("GetLoginIPs url=%v, params: mid=%d,limit=%d, res: %+v", d.c.AccRecover.GetLoginIPURL, mid, limit, res)
return res.Data, nil
}
// GetAddrByIP get addr by ip
func (d *Dao) GetAddrByIP(c context.Context, mid int64, limit int64) (addrs string, err error) {
ipInfo, err := d.GetLoginIPs(c, mid, limit)
if err != nil || len(ipInfo) == 0 {
return
}
var ipLen = len(ipInfo)
ips := make([]string, 0, ipLen)
//ip去重复和空串
for i := 0; i < ipLen; i++ {
if (i > 0 && ipInfo[i-1].LoginIP == ipInfo[i].LoginIP) || len(ipInfo[i].LoginIP) == 0 {
continue
}
ips = append(ips, ipInfo[i].LoginIP)
}
ipMap, err := d.Infos(c, ips)
i := 0
for _, loc := range ipMap {
if loc.Country != "" {
addrs += loc.Country + "-"
}
if loc.Province != "" {
addrs += loc.Province + "-"
}
if loc.City != "" {
addrs += loc.City + "-"
}
addrs = strings.TrimRight(addrs, "-") + ","
i++
if i >= 3 {
break
}
}
addrs = strings.TrimRight(addrs, ",")
return
}

View File

@@ -0,0 +1,182 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
func TestDaoGetMidInfo(t *testing.T) {
var (
c = context.Background()
qType = "1"
qKey = "silg@yahoo.cn"
)
convey.Convey("GetMidInfo", t, func(ctx convey.C) {
v, err := d.GetMidInfo(c, qType, qKey)
ctx.Convey("Then err should be nil.v should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(v, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetUserInfo(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
)
convey.Convey("GetUserInfo", t, func(ctx convey.C) {
defer gock.OffAll()
httpMock("GET", d.c.AccRecover.GetUserInfoURL).Reply(200).JSON(`{"code":0,"data":{"mid":21,"email":"raiden131@yahoo.cn","telphone":"","join_time":1245902140}}`)
v, err := d.GetUserInfo(c, mid)
ctx.Convey("Then err should be nil.v should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(v, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdatePwd(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
)
convey.Convey("UpdatePwd", t, func(ctx convey.C) {
defer gock.OffAll()
httpMock("POST", d.c.AccRecover.UpPwdURL).Reply(200).JSON(`{"code": 0, "data":{"pwd":"d4txsunbb1","userid":"minorin"}}`)
user, err := d.UpdatePwd(c, mid, "账号找回服务")
ctx.So(err, convey.ShouldBeNil)
ctx.So(user, convey.ShouldNotBeNil)
})
}
func TestDaoCheckSafe(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
question = int8(0)
answer = "1"
)
convey.Convey("CheckSafe", t, func(ctx convey.C) {
check, err := d.CheckSafe(c, mid, question, answer)
ctx.Convey("Then err should be nil.check should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(check, convey.ShouldNotBeNil)
})
})
}
//func httpMock(method, url string) *gock.Request {
// r := gock.New(url)
// r.Method = strings.ToUpper(method)
// return r
//}
//func TestDaoGetUserType(t *testing.T) {
// var (
// c = context.Background()
// mid = int64(2)
// )
// convey.Convey("When http request gets code != 0", t, func(ctx convey.C) {
// defer gock.OffAll()
// httpMock("GET", d.c.AccRecover.GameURL).Reply(0).JSON(`{"requestId":"0def8d70b7ef11e8a395fa163e01a2e9","ts":"1535440592","code":0,"items":[{"id":14,"name":"SDK测试2","lastLogin":"1500969010"}]}`)
// games, err := d.GetUserType(c, mid)
// ctx.So(err, convey.ShouldBeNil)
// ctx.So(games, convey.ShouldNotBeNil)
// })
//}
func TestDaoCheckReg(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
regTime = int64(1532441644)
regType = int8(0)
regAddr = "中国_上海"
)
convey.Convey("CheckReg", t, func(ctx convey.C) {
v, err := d.CheckReg(c, mid, regTime, regType, regAddr)
ctx.Convey("Then err should be nil.v should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(v, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdateBatchPwd(t *testing.T) {
var (
c = context.Background()
mids = "1,2"
)
convey.Convey("UpdateBatchPwd", t, func(ctx convey.C) {
defer gock.OffAll()
httpMock("POST", d.c.AccRecover.UpBatchPwdURL).Reply(200).JSON(`{"code":0,"data":{"6":{"pwd":"tgs52r1st9","userid":"腹黑君"},"7":{"pwd":"g20ahzrf7j","userid":"Tzwcard"}}}`)
userMap, err := d.UpdateBatchPwd(c, mids, "账号找回服务")
ctx.So(err, convey.ShouldBeNil)
ctx.So(userMap, convey.ShouldNotBeNil)
})
}
func TestDaoCheckCard(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
cardType = int8(1)
cardCode = "123"
)
convey.Convey("CheckCard", t, func(ctx convey.C) {
ok, err := d.CheckCard(c, mid, cardType, cardCode)
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 TestDaoCheckPwds(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
pwds = "123"
)
convey.Convey("CheckPwds", t, func(ctx convey.C) {
v, err := d.CheckPwds(c, mid, pwds)
ctx.Convey("Then err should be nil.v should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(v, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetLoginIPs(t *testing.T) {
var (
c = context.Background()
mid = int64(2)
limit = int64(10)
)
convey.Convey("GetLoginIPs", t, func(ctx convey.C) {
ipInfo, err := d.GetLoginIPs(c, mid, limit)
ctx.Convey("Then err should be nil.ipInfo should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ipInfo, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetAddrByIP(t *testing.T) {
var (
c = context.Background()
mid = int64(111001254)
limit = int64(10)
)
convey.Convey("GetAddrByIP", t, func(ctx convey.C) {
addrs, err := d.GetAddrByIP(c, mid, limit)
ctx.Convey("Then err should be nil.addrs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(addrs, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,475 @@
package dao
import (
"context"
rsql "database/sql"
"fmt"
"strings"
"go-common/app/service/main/account-recovery/dao/sqlbuilder"
"go-common/app/service/main/account-recovery/model"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
"go-common/library/xstr"
)
const (
_selectCountRecoveryInfo = "select count(rid) from account_recovery_info"
_selectRecoveryInfoLimit = "select rid,mid,user_type,status,login_addrs,unames,reg_time,reg_type,reg_addr,pwds,phones,emails,safe_question,safe_answer,card_type,card_id," +
"sys_login_addrs,sys_reg,sys_unames,sys_pwds,sys_phones,sys_emails,sys_safe,sys_card," +
"link_email,operator,opt_time,remark,ctime,business from account_recovery_info %s"
_getSuccessCount = "SELECT count FROM account_recovery_success WHERE mid=?"
_batchGetRecoverySuccess = "SELECT mid,count,ctime,mtime FROM account_recovery_success WHERE mid in (%s)"
_updateSuccessCount = "INSERT INTO account_recovery_success (mid, count) VALUES (?, 1) ON DUPLICATE KEY UPDATE count = count + 1"
_batchUpdateSuccessCount = "INSERT INTO account_recovery_success (mid, count) VALUES %s ON DUPLICATE KEY UPDATE count = count + 1"
_updateStatus = "UPDATE account_recovery_info SET status=?,operator=?,opt_time=?,remark=? WHERE rid = ? AND `status`=0"
_getNoDeal = "SELECT COUNT(1) FROM account_recovery_info WHERE mid=? AND `status`=0"
_updateUserType = "UPDATE account_recovery_info SET user_type=? WHERE rid = ?"
_insertRecoveryInfo = "INSERT INTO account_recovery_info(login_addrs,unames,reg_time,reg_type,reg_addr,pwds,phones,emails,safe_question,safe_answer,card_type,card_id,link_email,mid,business,last_suc_count,last_suc_ctime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
_updateSysInfo = "UPDATE account_recovery_info SET sys_login_addrs=?,sys_reg=?,sys_unames=?,sys_pwds=?,sys_phones=?,sys_emails=?,sys_safe=?,sys_card=?,user_type=? WHERE rid=?"
_getUinfoByRid = "SELECT mid,link_email,ctime FROM account_recovery_info WHERE rid=? LIMIT 1"
_getUinfoByRidMore = "SELECT rid,mid,link_email,ctime FROM account_recovery_info WHERE rid in (%s)"
_selectUnCheckInfo = "SELECT mid,login_addrs,unames,reg_time,reg_type,reg_addr,pwds,phones,emails,safe_question,safe_answer,card_type,card_id FROM account_recovery_info WHERE rid=? AND `status`=0 AND sys_card=''"
_getStatusByRid = "SELECT `status` FROM account_recovery_info WHERE rid=?"
_getMailStatus = "SELECT mail_status FROM account_recovery_info WHERE rid=?"
_updateMailStatus = "UPDATE account_recovery_info SET mail_status=1 WHERE rid=?"
_insertRecoveryAddit = "INSERT INTO account_recovery_addit(`rid`, `files`, `extra`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE files=VALUES(files),extra=VALUES(extra)"
_updateRecoveryAddit = "UPDATE account_recovery_addit SET `files` = ?,`extra` = ? WHERE rid = ?"
_getRecoveryAddit = "SELECT rid, `files`,`extra`, ctime, mtime FROM account_recovery_addit WHERE rid= ?"
_batchRecoveryAAdit = "SELECT rid, `files`, `extra`, ctime, mtime FROM account_recovery_addit WHERE rid in (%s)"
_batchGetLastSuccess = "SELECT mid,max(ctime) FROM account_recovery_info WHERE mid in (%s) AND `status`=1 GROUP BY mid"
_getLastSuccess = "SELECT mid,max(ctime) FROM account_recovery_info WHERE mid = ? AND `status`=1"
)
// GetStatusByRid get status by rid
func (dao *Dao) GetStatusByRid(c context.Context, rid int64) (status int64, err error) {
res := dao.db.Prepared(_getStatusByRid).QueryRow(c, rid)
if err = res.Scan(&status); err != nil {
if err == sql.ErrNoRows {
status = -1
err = nil
} else {
log.Error("GetStatusByRid row.Scan error(%v)", err)
}
}
return
}
// GetSuccessCount get success count
func (dao *Dao) GetSuccessCount(c context.Context, mid int64) (count int64, err error) {
res := dao.db.Prepared(_getSuccessCount).QueryRow(c, mid)
if err = res.Scan(&count); err != nil {
if err == sql.ErrNoRows {
count = 0
err = nil
} else {
log.Error("GetSuccessCount row.Scan error(%v)", err)
}
}
return
}
// BatchGetRecoverySuccess batch get recovery success info
func (dao *Dao) BatchGetRecoverySuccess(c context.Context, mids []int64) (successMap map[int64]*model.RecoverySuccess, err error) {
rows, err := dao.db.Query(c, fmt.Sprintf(_batchGetRecoverySuccess, xstr.JoinInts(mids)))
if err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("BatchGetRecoverySuccess d.db.Query error(%v)", err)
return
}
successMap = make(map[int64]*model.RecoverySuccess)
for rows.Next() {
r := new(model.RecoverySuccess)
if err = rows.Scan(&r.SuccessMID, &r.SuccessCount, &r.FirstSuccessTime, &r.LastSuccessTime); err != nil {
log.Error("BatchGetRecoverySuccess rows.Scan error(%v)", err)
continue
}
successMap[r.SuccessMID] = r
}
return
}
// UpdateSuccessCount insert or update success count
func (dao *Dao) UpdateSuccessCount(c context.Context, mid int64) (err error) {
_, err = dao.db.Exec(c, _updateSuccessCount, mid)
return
}
// BatchUpdateSuccessCount batch insert or update success count
func (dao *Dao) BatchUpdateSuccessCount(c context.Context, mids string) (err error) {
var s string
midArr := strings.Split(mids, ",")
for _, mid := range midArr {
s = s + fmt.Sprintf(",(%s, 1)", mid)
}
_, err = dao.db.Exec(c, fmt.Sprintf(_batchUpdateSuccessCount, s[1:]))
return
}
// GetNoDeal get no deal record
func (dao *Dao) GetNoDeal(c context.Context, mid int64) (count int64, err error) {
res := dao.db.Prepared(_getNoDeal).QueryRow(c, mid)
if err = res.Scan(&count); err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("GetNoDeal row.Scan error(%v)", err)
return
}
return
}
// UpdateStatus update field status.
func (dao *Dao) UpdateStatus(c context.Context, status int64, rid int64, operator string, optTime xtime.Time, remark string) (err error) {
_, err = dao.db.Exec(c, _updateStatus, status, operator, optTime, remark, rid)
return
}
// UpdateUserType update field user_type.
func (dao *Dao) UpdateUserType(c context.Context, status int64, rid int64) (err error) {
if _, err = dao.db.Exec(c, _updateUserType, status, rid); err != nil {
log.Error("dao.db.Exec(%s, %d, %d) error(%v)", _updateUserType, status, rid, err)
}
return
}
// InsertRecoveryInfo insert data
func (dao *Dao) InsertRecoveryInfo(c context.Context, uinfo *model.UserInfoReq) (lastID int64, err error) {
var res rsql.Result
if res, err = dao.db.Exec(c, _insertRecoveryInfo, uinfo.LoginAddrs, uinfo.Unames, uinfo.RegTime, uinfo.RegType, uinfo.RegAddr,
uinfo.Pwds, uinfo.Phones, uinfo.Emails, uinfo.SafeQuestion, uinfo.SafeAnswer, uinfo.CardType, uinfo.CardID, uinfo.LinkMail, uinfo.Mid, uinfo.Business, uinfo.LastSucCount, uinfo.LastSucCTime); err != nil {
log.Error("dao.db.Exec(%s, %v) error(%v)", _insertRecoveryInfo, uinfo, err)
return
}
return res.LastInsertId()
}
// UpdateSysInfo update sysinfo and user_type
func (dao *Dao) UpdateSysInfo(c context.Context, sys *model.SysInfo, userType int64, rid int64) (err error) {
if _, err = dao.db.Exec(c, _updateSysInfo, &sys.SysLoginAddrs, &sys.SysReg, &sys.SysUNames, &sys.SysPwds, &sys.SysPhones,
&sys.SysEmails, &sys.SysSafe, &sys.SysCard, userType, rid); err != nil {
log.Error("dao.db.Exec(%s, %v) error(%v)", _updateSysInfo, sys, err)
}
return
}
// GetAllByCon get a pageData by more condition
func (dao *Dao) GetAllByCon(c context.Context, aq *model.QueryRecoveryInfoReq) ([]*model.AccountRecoveryInfo, int64, error) {
query := sqlbuilder.NewSelectBuilder().Select("rid,mid,user_type,status,login_addrs,unames,reg_time,reg_type,reg_addr,pwds,phones,emails,safe_question,safe_answer,card_type,card_id,sys_login_addrs,sys_reg,sys_unames,sys_pwds,sys_phones,sys_emails,sys_safe,sys_card,link_email,operator,opt_time,remark,ctime,business,last_suc_count,last_suc_ctime").From("account_recovery_info")
if aq.Bussiness != "" {
query = query.Where(query.Equal("business", aq.Bussiness))
}
if aq.Status != nil {
query = query.Where(fmt.Sprintf("status=%d", *aq.Status))
}
if aq.Game != nil {
query = query.Where(fmt.Sprintf("user_type=%d", *aq.Game))
}
if aq.UID != 0 {
query = query.Where(fmt.Sprintf("mid=%d", aq.UID))
}
if aq.RID != 0 {
query = query.Where(fmt.Sprintf("rid=%d", aq.RID))
}
if aq.StartTime != 0 {
query = query.Where(query.GE("ctime", aq.StartTime.Time()))
}
if aq.EndTime != 0 {
query = query.Where(query.LE("ctime", aq.EndTime.Time()))
}
totalSQL, totalArg := query.Copy().Select("count(1)").Build()
log.Info("Build GetAllByCon total count SQL: %s", totalSQL)
page := aq.Page
if page == 0 {
page = 1
}
size := aq.Size
if size == 0 {
size = 50
}
query = query.Limit(int(size)).Offset(int(size * (page - 1))).OrderBy("rid DESC")
rawSQL, rawArg := query.Build()
log.Info("Build GetAllByCon SQL: %s", rawSQL)
total := int64(0)
row := dao.db.QueryRow(c, totalSQL, totalArg...)
if err := row.Scan(&total); err != nil {
return nil, 0, err
}
rows, err := dao.db.Query(c, rawSQL, rawArg...)
if err != nil {
return nil, 0, err
}
defer rows.Close()
resultData := make([]*model.AccountRecoveryInfo, 0)
for rows.Next() {
r := new(model.AccountRecoveryInfo)
if err = rows.Scan(&r.Rid, &r.Mid, &r.UserType, &r.Status, &r.LoginAddr, &r.UNames, &r.RegTime, &r.RegType, &r.RegAddr,
&r.Pwd, &r.Phones, &r.Emails, &r.SafeQuestion, &r.SafeAnswer, &r.CardType, &r.CardID,
&r.SysLoginAddr, &r.SysReg, &r.SysUNames, &r.SysPwds, &r.SysPhones, &r.SysEmails, &r.SysSafe, &r.SysCard,
&r.LinkEmail, &r.Operator, &r.OptTime, &r.Remark, &r.CTime, &r.Bussiness, &r.LastSucCount, &r.LastSucCTime); err != nil {
log.Error("GetAllByCon error (%+v)", err)
continue
}
resultData = append(resultData, r)
}
return resultData, total, err
}
// QueryByID query by rid
func (dao *Dao) QueryByID(c context.Context, rid int64, fromTime, endTime xtime.Time) (res *model.AccountRecoveryInfo, err error) {
sql1 := "select rid,mid,user_type,status,login_addrs,unames,reg_time,reg_type,reg_addr,pwds,phones,emails,safe_question,safe_answer,card_type,card_id," +
"sys_login_addrs,sys_reg,sys_unames,sys_pwds,sys_phones,sys_emails,sys_safe,sys_card," +
"link_email,operator,opt_time,remark,ctime,business from account_recovery_info where ctime between ? and ? and rid = ?"
res = new(model.AccountRecoveryInfo)
row := dao.db.QueryRow(c, sql1, fromTime, endTime, rid)
if err = row.Scan(&res.Rid, &res.Mid, &res.UserType, &res.Status, &res.LoginAddr, &res.UNames, &res.RegTime, &res.RegType, &res.RegAddr,
&res.Pwd, &res.Phones, &res.Emails, &res.SafeQuestion, &res.SafeAnswer, &res.CardType, &res.CardID,
&res.SysLoginAddr, &res.SysReg, &res.SysUNames, &res.SysPwds, &res.SysPhones, &res.SysEmails, &res.SysSafe, &res.SysCard,
&res.LinkEmail, &res.Operator, &res.OptTime, &res.Remark, &res.CTime, &res.Bussiness); err != nil {
if err == sql.ErrNoRows {
err = nil
res = nil
return
}
log.Error("QueryByID(%d) error(%v)", rid, err)
return
}
return
}
//QueryInfoByLimit page query through limit m,n
func (dao *Dao) QueryInfoByLimit(c context.Context, req *model.DBRecoveryInfoParams) (res []*model.AccountRecoveryInfo, total int64, err error) {
p := make([]interface{}, 0)
s := " where ctime between ? and ?"
p = append(p, req.StartTime)
p = append(p, req.EndTime)
if req.ExistGame {
s = s + " and user_type = ?"
p = append(p, req.Game)
}
if req.ExistStatus {
s = s + " and status = ?"
p = append(p, req.Status)
}
if req.ExistMid {
s = s + " and mid = ?"
p = append(p, req.Mid)
}
var s2 = s + " order by rid desc limit ?,?"
p2 := p
p2 = append(p2, (req.CurrPage-1)*req.Size, req.Size)
var rows *sql.Rows
rows, err = dao.db.Query(c, fmt.Sprintf(_selectRecoveryInfoLimit, s2), p2...)
if err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("QueryInfo err: d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.AccountRecoveryInfo, 0, req.Size)
for rows.Next() {
r := new(model.AccountRecoveryInfo)
if err = rows.Scan(&r.Rid, &r.Mid, &r.UserType, &r.Status, &r.LoginAddr, &r.UNames, &r.RegTime, &r.RegType, &r.RegAddr,
&r.Pwd, &r.Phones, &r.Emails, &r.SafeQuestion, &r.SafeAnswer, &r.CardType, &r.CardID,
&r.SysLoginAddr, &r.SysReg, &r.SysUNames, &r.SysPwds, &r.SysPhones, &r.SysEmails, &r.SysSafe, &r.SysCard,
&r.LinkEmail, &r.Operator, &r.OptTime, &r.Remark, &r.CTime, &r.Bussiness); err != nil {
log.Error("QueryInfo (%v) error (%v)", req, err)
return
}
res = append(res, r)
}
row := dao.db.QueryRow(c, _selectCountRecoveryInfo+s, p...)
if err = row.Scan(&total); err != nil {
log.Error("QueryInfo total error (%v)", err)
return
}
return
}
// GetUinfoByRid get mid,linkMail by rid
func (dao *Dao) GetUinfoByRid(c context.Context, rid int64) (mid int64, linkMail string, ctime string, err error) {
res := dao.db.Prepared(_getUinfoByRid).QueryRow(c, rid)
req := new(struct {
Mid int64
LinKMail string
Ctime xtime.Time
})
if err = res.Scan(&req.Mid, &req.LinKMail, &req.Ctime); err != nil {
if err == sql.ErrNoRows {
req.Mid = 0
err = nil
} else {
log.Error("GetUinfoByRid row.Scan error(%v)", err)
}
}
mid = req.Mid
linkMail = req.LinKMail
ctime = req.Ctime.Time().Format("2006-01-02 15:04:05")
return
}
// GetUinfoByRidMore get list of BatchAppeal by rid
func (dao *Dao) GetUinfoByRidMore(c context.Context, ridsStr string) (bathRes []*model.BatchAppeal, err error) {
rows, err := dao.db.Prepared(fmt.Sprintf(_getUinfoByRidMore, ridsStr)).Query(c)
if err != nil {
return nil, err
}
defer rows.Close()
bathRes = make([]*model.BatchAppeal, 0, len(strings.Split(ridsStr, ",")))
for rows.Next() {
req := &model.BatchAppeal{}
if err = rows.Scan(&req.Rid, &req.Mid, &req.LinkMail, &req.Ctime); err != nil {
return
}
bathRes = append(bathRes, req)
}
return
}
// GetUnCheckInfo get uncheck info
func (dao *Dao) GetUnCheckInfo(c context.Context, rid int64) (r *model.UserInfoReq, err error) {
row := dao.db.QueryRow(c, _selectUnCheckInfo, rid)
r = new(model.UserInfoReq)
if err = row.Scan(&r.Mid, &r.LoginAddrs, &r.Unames, &r.RegTime, &r.RegType, &r.RegAddr,
&r.Pwds, &r.Phones, &r.Emails, &r.SafeQuestion, &r.SafeAnswer, &r.CardType, &r.CardID); err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("GetUnCheckInfo (%v) error (%v)", rid, err)
}
return
}
//BeginTran begin transaction
func (dao *Dao) BeginTran(ctx context.Context) (tx *sql.Tx, err error) {
if tx, err = dao.db.Begin(ctx); err != nil {
log.Error("db: begintran BeginTran d.db.Begin error(%v)", err)
}
return
}
// GetMailStatus get mail_status by rid
func (dao *Dao) GetMailStatus(c context.Context, rid int64) (mailStatus int64, err error) {
res := dao.db.Prepared(_getMailStatus).QueryRow(c, rid)
if err = res.Scan(&mailStatus); err != nil {
if err == sql.ErrNoRows {
mailStatus = -1
err = nil
} else {
log.Error("GetStatusByRid row.Scan error(%v)", err)
}
}
return
}
// UpdateMailStatus update mail_status.
func (dao *Dao) UpdateMailStatus(c context.Context, rid int64) (err error) {
_, err = dao.db.Exec(c, _updateMailStatus, rid)
return
}
// UpdateRecoveryAddit is
func (dao *Dao) UpdateRecoveryAddit(c context.Context, rid int64, files []string, extra string) (err error) {
_, err = dao.db.Exec(c, _updateRecoveryAddit, strings.Join(files, ","), extra, rid)
return
}
// GetRecoveryAddit is
func (dao *Dao) GetRecoveryAddit(c context.Context, rid int64) (addit *model.DBAccountRecoveryAddit, err error) {
row := dao.db.QueryRow(c, _getRecoveryAddit, rid)
addit = new(model.DBAccountRecoveryAddit)
if err = row.Scan(&addit.Rid, &addit.Files, &addit.Extra, &addit.Ctime, &addit.Mtime); err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("GetRecoveryAddit (%v) error (%v)", rid, err)
}
return
}
// InsertRecoveryAddit is
func (dao *Dao) InsertRecoveryAddit(c context.Context, rid int64, files, extra string) (err error) {
_, err = dao.db.Exec(c, _insertRecoveryAddit, rid, files, extra)
return
}
//BatchGetRecoveryAddit is
func (dao *Dao) BatchGetRecoveryAddit(c context.Context, rids []int64) (addits map[int64]*model.DBAccountRecoveryAddit, err error) {
rows, err := dao.db.Query(c, fmt.Sprintf(_batchRecoveryAAdit, xstr.JoinInts(rids)))
if err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("BatchGetRecoveryAddit d.db.Query error(%v)", err)
return
}
defer rows.Close()
addits = make(map[int64]*model.DBAccountRecoveryAddit)
for rows.Next() {
var addit = new(model.DBAccountRecoveryAddit)
if err = rows.Scan(&addit.Rid, &addit.Files, &addit.Extra, &addit.Ctime, &addit.Mtime); err != nil {
log.Error("BatchGetRecoveryAddit rows.Scan error(%v)", err)
continue
}
addits[addit.Rid] = addit
}
return
}
// BatchGetLastSuccess batch get last find success info
func (dao *Dao) BatchGetLastSuccess(c context.Context, mids []int64) (lastSuccessMap map[int64]*model.LastSuccessData, err error) {
rows, err := dao.db.Query(c, fmt.Sprintf(_batchGetLastSuccess, xstr.JoinInts(mids)))
if err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("BatchGetLastSuccess d.db.Query error(%v)", err)
return
}
defer rows.Close()
lastSuccessMap = make(map[int64]*model.LastSuccessData)
for rows.Next() {
r := new(model.LastSuccessData)
if err = rows.Scan(&r.LastApplyMID, &r.LastApplyTime); err != nil {
log.Error("BatchGetLastSuccess rows.Scan error(%v)", err)
continue
}
lastSuccessMap[r.LastApplyMID] = r
}
return
}
// GetLastSuccess get last find success info
func (dao *Dao) GetLastSuccess(c context.Context, mid int64) (lastSuc *model.LastSuccessData, err error) {
row := dao.db.QueryRow(c, _getLastSuccess, mid)
lastSuc = new(model.LastSuccessData)
if err = row.Scan(&lastSuc.LastApplyMID, &lastSuc.LastApplyTime); err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("GetRecoveryAddit (%v) error (%v)", mid, err)
}
return
}

View File

@@ -0,0 +1,412 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/account-recovery/model"
xtime "go-common/library/time"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoGetStatusByRid(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("GetStatusByRid", t, func(ctx convey.C) {
status, err := d.GetStatusByRid(c, rid)
ctx.Convey("Then err should be nil.status should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(status, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetSuccessCount(t *testing.T) {
var (
c = context.Background()
mid = int64(1234)
)
convey.Convey("GetSuccessCount", t, func(ctx convey.C) {
count, err := d.GetSuccessCount(c, mid)
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 TestDaoBatchGetRecoverySuccess(t *testing.T) {
var (
c = context.Background()
mids = []int64{1234}
)
convey.Convey("BatchGetRecoverySuccess", t, func(ctx convey.C) {
countMap, err := d.BatchGetRecoverySuccess(c, mids)
ctx.Convey("Then err should be nil.countMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(countMap, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdateSuccessCount(t *testing.T) {
var (
c = context.Background()
mid = int64(1234)
)
convey.Convey("UpdateSuccessCount", t, func(ctx convey.C) {
err := d.UpdateSuccessCount(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoBatchUpdateSuccessCount(t *testing.T) {
var (
c = context.Background()
mids = "1234"
)
convey.Convey("BatchUpdateSuccessCount", t, func(ctx convey.C) {
err := d.BatchUpdateSuccessCount(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoGetNoDeal(t *testing.T) {
var (
c = context.Background()
mid = int64(1234)
)
convey.Convey("GetNoDeal", t, func(ctx convey.C) {
count, err := d.GetNoDeal(c, mid)
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 TestDaoUpdateStatus(t *testing.T) {
var (
c = context.Background()
status = int64(1)
rid = int64(1)
operator = "abcd"
optTime xtime.Time
remark = ""
)
convey.Convey("UpdateStatus", t, func(ctx convey.C) {
err := d.UpdateStatus(c, status, rid, operator, optTime, remark)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoUpdateUserType(t *testing.T) {
var (
c = context.Background()
status = int64(1)
rid = int64(1)
)
convey.Convey("UpdateUserType", t, func(ctx convey.C) {
err := d.UpdateUserType(c, status, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoInsertRecoveryInfo(t *testing.T) {
var (
c = context.Background()
uinfo = &model.UserInfoReq{
LoginAddrs: "中国-福州,中国-上海,澳大利亚",
//RegTime:timeS, //变成2018
RegTime: 1533206284, //变成2018 //数据库设置为int(11),so数据库必须设置为tiimestamp
RegType: int8(1),
RegAddr: "中国上海",
Unames: "昵称AA,昵称BB,昵称CC",
Pwds: "密码1,密码2",
Phones: "12345678901,54321678923",
Emails: "2456@sina.com,789@qq.com",
SafeQuestion: int8(1),
SafeAnswer: "心态呀",
CardID: "ISN-1234567890-0987",
CardType: int8(1),
LinkMail: "345678@qq.com",
Mid: 1234,
}
)
convey.Convey("InsertRecoveryInfo", t, func(ctx convey.C) {
lastID, err := d.InsertRecoveryInfo(c, uinfo)
ctx.Convey("Then err should be nil.lastID should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(lastID, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdateSysInfo(t *testing.T) {
var (
c = context.Background()
sys = &model.SysInfo{
SysLoginAddrs: "中国-福州,中国-上海,澳大利亚",
SysReg: "对",
SysUNames: "对,错,错",
SysPwds: "对,错",
SysPhones: "对,错",
SysEmails: "对,错",
SysSafe: "对",
SysCard: "对",
}
userType = int64(1)
rid = int64(1)
)
convey.Convey("UpdateSysInfo", t, func(ctx convey.C) {
err := d.UpdateSysInfo(c, sys, userType, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoQueryByID(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
fromTime xtime.Time = 1533120949
endTime xtime.Time = 1535636392
)
convey.Convey("QueryByID", t, func(ctx convey.C) {
res, err := d.QueryByID(c, rid, fromTime, endTime)
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 TestDaoQueryInfoByLimit(t *testing.T) {
var (
c = context.Background()
req = &model.DBRecoveryInfoParams{
ExistGame: false,
ExistStatus: false,
ExistMid: false,
Mid: 0,
Game: 0,
Status: 1,
FirstRid: 20,
LastRid: 0,
Size: 2,
StartTime: 1533120949,
EndTime: 1535636392,
SubNum: 1,
CurrPage: 1,
}
)
convey.Convey("QueryInfoByLimit", t, func(ctx convey.C) {
res, total, err := d.QueryInfoByLimit(c, req)
ctx.Convey("Then err should be nil.res,total should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(total, convey.ShouldNotBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetUinfoByRid(t *testing.T) {
var (
c = context.Background()
rid = int64(240)
)
convey.Convey("GetUinfoByRid", t, func(ctx convey.C) {
mid, linkMail, ctime, err := d.GetUinfoByRid(c, rid)
ctx.Convey("Then err should be nil.mid,linkMail,ctime should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ctime, convey.ShouldNotBeNil)
ctx.So(linkMail, convey.ShouldNotBeNil)
ctx.So(mid, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetUinfoByRidMore(t *testing.T) {
var (
c = context.Background()
ridsStr = "1,2"
)
convey.Convey("GetUinfoByRidMore", t, func(ctx convey.C) {
bathRes, err := d.GetUinfoByRidMore(c, ridsStr)
ctx.Convey("Then err should be nil.bathRes should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(bathRes, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetUnCheckInfo(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("GetUnCheckInfo", t, func(ctx convey.C) {
r, err := d.GetUnCheckInfo(c, rid)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetMailStatus(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("GetMailStatus", t, func(ctx convey.C) {
mailStatus, err := d.GetMailStatus(c, rid)
ctx.Convey("Then err should be nil.mailStatus should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(mailStatus, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpdateMailStatus(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("UpdateMailStatus", t, func(ctx convey.C) {
err := d.UpdateMailStatus(c, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoGetAllByCon(t *testing.T) {
var (
defint int64
c = context.Background()
aq = &model.QueryRecoveryInfoReq{
//RID: 1,
//UID:2,
Status: &defint,
Game: &defint,
Size: 10,
Page: 1,
StartTime: 1533052800,
EndTime: 1536924163,
//StartTime time.Time `json:"start_time" form:"start_time"`
//EndTime time.Time `json:"end_time" form:"end_time"`
//IsAdvanced bool `json:"-"`
//Page int64 `form:"page"`
}
)
convey.Convey("UpdateMailStatus", t, func(ctx convey.C) {
resultData, total, err := d.GetAllByCon(c, aq)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(resultData, convey.ShouldNotBeNil)
ctx.So(total, convey.ShouldNotBeNil)
ctx.So(err, convey.ShouldBeNil)
ctx.Println(total, " len=", len(resultData))
})
})
}
func TestDaoInsertRecoveryAddit(t *testing.T) {
var (
c = context.Background()
rid int64 = 1
files = "http://uat-i0.hdslb.com/bfs/account/recovery/bca2.zip,http://uat-i0.hdslb.com/bfs/account/recovery/abcd.zip"
extra = `{"GameArea":"ios-A服","GameNames":"崩坏3","GamePlay":"1"}`
)
convey.Convey("InsertRecoveryAddit", t, func(ctx convey.C) {
err := d.InsertRecoveryAddit(c, rid, files, extra)
err = d.InsertRecoveryAddit(c, 2, files, extra)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoUpdateRecoveryAddit(t *testing.T) {
var (
c = context.Background()
rid int64 = 1
files = []string{"http://uat-i0.hdslb.com/bfs/aaaa.zip", "http://uat-i0.hdslb.com/bfs/dddd.zip"}
extra = `{"GameArea":"ios-A服","GameNames":"崩坏3","GamePlay":"1"}`
)
convey.Convey("UpdateMailStatus", t, func(ctx convey.C) {
err := d.UpdateRecoveryAddit(c, rid, files, extra)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoGetRecoveryAddit(t *testing.T) {
var (
c = context.Background()
rid int64 = 1
)
convey.Convey("UpdateMailStatus", t, func(ctx convey.C) {
addit, err := d.GetRecoveryAddit(c, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.Println(addit, convey.ShouldNotBeNil)
})
})
}
func TestDaoBatchGetRecoveryAddit(t *testing.T) {
var (
c = context.Background()
rids = []int64{1, 2}
)
convey.Convey("BatchGetRecoveryAddit", t, func(ctx convey.C) {
addits, err := d.BatchGetRecoveryAddit(c, rids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(addits, convey.ShouldNotBeNil)
})
})
}
func TestBatchGetLastSuccess(t *testing.T) {
var (
c = context.Background()
mids = []int64{1234}
)
convey.Convey("BatchGetLastSuccess", t, func(ctx convey.C) {
lastSuccessMap, err := d.BatchGetLastSuccess(c, mids)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(lastSuccessMap, convey.ShouldNotBeNil)
})
})
}
func TestDaoGetLastSuccess(t *testing.T) {
var (
c = context.Background()
mid = int64(1234)
)
convey.Convey("GetLastSuccess", t, func(ctx convey.C) {
res, err := d.GetLastSuccess(c, mid)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,92 @@
package dao
import (
"context"
"strconv"
"time"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_expire = 30 * 60 // 30 minutes
_prefixCaptcha = "recovery:ca_"
)
// SetLinkMailCount set linkMail expire time.
func (d *Dao) SetLinkMailCount(c context.Context, linkMail string) (state int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
//用时间戳去减去时间 当天0点过期第二天又可以发送10封邮件
i, _ := redis.Int(conn.Do("incr", linkMail))
conn.Do("expire", linkMail, getSubtime())
if i >= 11 { //第10封邮件之后
state = 10 //当天邮件发送到达最大次数
return
}
return
}
func getSubtime() (subtime int64) {
timeStr := time.Now().Format("2006-01-02")
t, _ := time.Parse("2006-01-02", timeStr)
t2 := t.AddDate(0, 0, 1).Unix()
curr := time.Now().Unix() //当前时间
subtime = t2 - curr
return
}
// keyCaptcha
func keyCaptcha(mid int64, linkMail string) string {
return _prefixCaptcha + strconv.FormatInt(mid, 10) + "_" + linkMail
}
// SetCaptcha set linkMail expire time.
func (d *Dao) SetCaptcha(c context.Context, code string, mid int64, linkMail string) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := keyCaptcha(mid, linkMail)
//验证码30分钟内有效
if _, err = conn.Do("SETEX", key, _expire, code); err != nil {
log.Error("conn.Do(SETEX, %d, %v, %s) error(%v)", mid, _expire, code, err)
}
return
}
// GetEMailCode get captcha from redis
func (d *Dao) GetEMailCode(c context.Context, mid int64, linkMail string) (code string, err error) {
key := keyCaptcha(mid, linkMail)
conn := d.redis.Get(c)
defer conn.Close()
code, err = redis.String(conn.Do("GET", key))
if err != nil {
if err == redis.ErrNil {
err = nil
return
}
log.Error("conn.Do(GET, %s, ), err (%v)", key, err)
return
}
return
}
// DelEMailCode del captcha from redis 提交:校验验证之后就删除验证码(保证只能提交一次)
func (d *Dao) DelEMailCode(c context.Context, mid int64, linkMail string) (err error) {
key := keyCaptcha(mid, linkMail)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("DEL", key); err != nil {
log.Error("conn.Do(DEL, %s, ), err (%v)", key, err)
return
}
return
}
// PingRedis check connection success.
func (d *Dao) PingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("GET", "PING")
return
}

View File

@@ -0,0 +1,100 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSetLinkMailCount(t *testing.T) {
var (
c = context.Background()
linkMail = "2459593393@qq.com"
)
convey.Convey("SetLinkMailCount", t, func(ctx convey.C) {
state, err := d.SetLinkMailCount(c, linkMail)
ctx.Convey("Then err should be nil.state should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(state, convey.ShouldNotBeNil)
})
})
}
func TestDaogetSubtime(t *testing.T) {
convey.Convey("getSubtime", t, func(ctx convey.C) {
subtime := getSubtime()
ctx.Convey("Then subtime should not be nil.", func(ctx convey.C) {
ctx.So(subtime, convey.ShouldNotBeNil)
})
})
}
func TestDaokeyCaptcha(t *testing.T) {
var (
mid = int64(0)
linkMail = "2459593393@qq.com"
)
convey.Convey("keyCaptcha", t, func(ctx convey.C) {
p1 := keyCaptcha(mid, linkMail)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoSetCaptcha(t *testing.T) {
var (
c = context.Background()
code = "1234"
mid = int64(1)
linkMail = "2459593393@qq.com"
)
convey.Convey("SetCaptcha", t, func(ctx convey.C) {
err := d.SetCaptcha(c, code, mid, linkMail)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoGetEMailCode(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
linkMail = "2459593393@qq.com"
)
convey.Convey("GetEMailCode", t, func(ctx convey.C) {
code, err := d.GetEMailCode(c, mid, linkMail)
ctx.Convey("Then err should be nil.code should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(code, convey.ShouldNotBeNil)
})
})
}
func TestDaoDelEMailCode(t *testing.T) {
var (
c = context.Background()
mid = int64(1)
linkMail = "2459593393@qq.com"
)
convey.Convey("GetEMailCode", t, func(ctx convey.C) {
err := d.DelEMailCode(c, mid, linkMail)
ctx.Convey("Then err should be nil.code should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoPingRedis(t *testing.T) {
var (
c = context.Background()
)
convey.Convey("PingRedis", t, func(ctx convey.C) {
err := d.PingRedis(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,49 @@
package dao
import (
"context"
account "go-common/app/service/main/account/api"
location "go-common/app/service/main/location/model"
member "go-common/app/service/main/member/api"
"go-common/library/log"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// Info3 get info by mid
func (d *Dao) Info3(c context.Context, mid int64) (info *account.Info, err error) {
var (
arg = &account.MidReq{
Mid: mid,
RealIp: metadata.String(c, metadata.RemoteIP),
}
res *account.InfoReply
)
if res, err = d.accountClient.Info3(c, arg); err != nil {
err = errors.Wrapf(err, "%v", arg)
return nil, err
}
return res.Info, nil
}
// Infos get the ips info.
func (d *Dao) Infos(c context.Context, ipList []string) (res map[string]*location.Info, err error) {
if res, err = d.locRPC.Infos(c, ipList); err != nil {
log.Error("s.locaRPC err(%v)", err)
}
return
}
// CheckRealnameStatus realname status
func (d *Dao) CheckRealnameStatus(c context.Context, mid int64) (status int8, err error) {
var (
relnameStatus *member.RealnameStatusReply
)
if relnameStatus, err = d.memberClient.RealnameStatus(c, &member.MemberMidReq{Mid: mid, RemoteIP: metadata.String(c, metadata.RemoteIP)}); err != nil {
log.Error("s.memberSvr.RealnameStatus err(%v)", err)
return
}
return relnameStatus.RealnameStatus, nil
}

View File

@@ -0,0 +1,50 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoInfo3(t *testing.T) {
var (
c = context.Background()
mid = int64(2)
)
convey.Convey("Info3", t, func(ctx convey.C) {
res, err := d.Info3(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 TestDaoInfos(t *testing.T) {
var (
c = context.Background()
ipList = []string{"127.0.0.1"}
)
convey.Convey("Infos", t, func(ctx convey.C) {
res, err := d.Infos(c, ipList)
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 TestDaoCheckRealnameStatus(t *testing.T) {
var (
c = context.Background()
mid int64 = 1
)
convey.Convey("CheckRealnameStatus", t, func(ctx convey.C) {
res, err := d.CheckRealnameStatus(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)
})
})
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"args_test.go",
"builder_test.go",
"cond_test.go",
"flavor_test.go",
"modifiers_test.go",
"select_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"args.go",
"builder.go",
"cond.go",
"flavor.go",
"modifiers.go",
"select.go",
],
importpath = "go-common/app/service/main/account-recovery/dao/sqlbuilder",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
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,236 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"bytes"
"database/sql"
"fmt"
"sort"
"strconv"
"strings"
)
// Args stores arguments associated with a SQL.
type Args struct {
// The default flavor used by `Args#Compile`
Flavor Flavor
args []interface{}
namedArgs map[string]int
sqlNamedArgs map[string]int
onlyNamed bool
}
// Add adds an arg to Args and returns a placeholder.
func (args *Args) Add(arg interface{}) string {
return fmt.Sprintf("$%v", args.add(arg))
}
func (args *Args) add(arg interface{}) int {
idx := len(args.args)
switch a := arg.(type) {
case sql.NamedArg:
if args.sqlNamedArgs == nil {
args.sqlNamedArgs = map[string]int{}
}
if p, ok := args.sqlNamedArgs[a.Name]; ok {
arg = args.args[p]
break
}
args.sqlNamedArgs[a.Name] = idx
case namedArgs:
if args.namedArgs == nil {
args.namedArgs = map[string]int{}
}
if p, ok := args.namedArgs[a.name]; ok {
arg = args.args[p]
break
}
// Find out the real arg and add it to args.
idx = args.add(a.arg)
args.namedArgs[a.name] = idx
return idx
}
args.args = append(args.args, arg)
return idx
}
// Compile compiles builder's format to standard sql and returns associated args.
//
// The format string uses a special syntax to represent arguments.
//
// $? refers successive arguments passed in the call. It works similar as `%v` in `fmt.Sprintf`.
// $0 $1 ... $n refers nth-argument passed in the call. Next $? will use arguments n+1.
// ${name} refers a named argument created by `Named` with `name`.
// $$ is a "$" string.
func (args *Args) Compile(format string, intialValue ...interface{}) (query string, values []interface{}) {
return args.CompileWithFlavor(format, args.Flavor, intialValue...)
}
// CompileWithFlavor compiles builder's format to standard sql with flavor and returns associated args.
//
// See doc for `Compile` to learn details.
func (args *Args) CompileWithFlavor(format string, flavor Flavor, intialValue ...interface{}) (query string, values []interface{}) {
buf := &bytes.Buffer{}
idx := strings.IndexRune(format, '$')
offset := 0
values = intialValue
if flavor == invalidFlavor {
flavor = DefaultFlavor
}
for idx >= 0 && len(format) > 0 {
if idx > 0 {
buf.WriteString(format[:idx])
}
format = format[idx+1:]
// Should not happen.
if len(format) == 0 {
break
}
if format[0] == '$' {
buf.WriteRune('$')
format = format[1:]
} else if format[0] == '{' {
format, values = args.compileNamed(buf, flavor, format, values)
} else if !args.onlyNamed && '0' <= format[0] && format[0] <= '9' {
format, values, offset = args.compileDigits(buf, flavor, format, values, offset)
} else if !args.onlyNamed && format[0] == '?' {
format, values, offset = args.compileSuccessive(buf, flavor, format[1:], values, offset)
}
idx = strings.IndexRune(format, '$')
}
if len(format) > 0 {
buf.WriteString(format)
}
query = buf.String()
if len(args.sqlNamedArgs) > 0 {
// Stabilize the sequence to make it easier to write test cases.
ints := make([]int, 0, len(args.sqlNamedArgs))
for _, p := range args.sqlNamedArgs {
ints = append(ints, p)
}
sort.Ints(ints)
for _, i := range ints {
values = append(values, args.args[i])
}
}
return
}
func (args *Args) compileNamed(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
i := 1
for ; i < len(format) && format[i] != '}'; i++ {
// Nothing.
}
// Invalid $ format. Ignore it.
if i == len(format) {
return format, values
}
name := format[1:i]
format = format[i+1:]
if p, ok := args.namedArgs[name]; ok {
format, values, _ = args.compileSuccessive(buf, flavor, format, values, p)
}
return format, values
}
func (args *Args) compileDigits(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
i := 1
for ; i < len(format) && '0' <= format[i] && format[i] <= '9'; i++ {
// Nothing.
}
digits := format[:i]
format = format[i:]
if pointer, err := strconv.Atoi(digits); err == nil {
return args.compileSuccessive(buf, flavor, format, values, pointer)
}
return format, values, offset
}
func (args *Args) compileSuccessive(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
if offset >= len(args.args) {
return format, values, offset
}
arg := args.args[offset]
values = args.compileArg(buf, flavor, values, arg)
return format, values, offset + 1
}
func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
switch a := arg.(type) {
case Builder:
var s string
s, values = a.BuildWithFlavor(flavor, values...)
buf.WriteString(s)
case sql.NamedArg:
buf.WriteRune('@')
buf.WriteString(a.Name)
case rawArgs:
buf.WriteString(a.expr)
case listArgs:
if len(a.args) > 0 {
values = args.compileArg(buf, flavor, values, a.args[0])
}
for i := 1; i < len(a.args); i++ {
buf.WriteString(", ")
values = args.compileArg(buf, flavor, values, a.args[i])
}
default:
switch flavor {
case MySQL:
buf.WriteRune('?')
case PostgreSQL:
fmt.Fprintf(buf, "$%v", len(values)+1)
default:
panic(fmt.Errorf("Args.CompileWithFlavor: invalid flavor %v (%v)", flavor, int(flavor)))
}
values = append(values, arg)
}
return values
}
// Copy is
func (args *Args) Copy() *Args {
return &Args{
Flavor: args.Flavor,
args: args.args,
namedArgs: args.namedArgs,
sqlNamedArgs: args.sqlNamedArgs,
onlyNamed: args.onlyNamed,
}
}

View File

@@ -0,0 +1,76 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"bytes"
"fmt"
"strings"
"testing"
)
func TestArgs(t *testing.T) {
cases := map[string][]interface{}{
"abc ? def\n[123]": {"abc $? def", 123},
"abc ? def\n[456]": {"abc $0 def", 456},
"abc def\n[]": {"abc $1 def", 123},
"abc ? def\n[789]": {"abc ${s} def", Named("s", 789)},
"abc def \n[]": {"abc ${unknown} def ", 123},
"abc $ def\n[]": {"abc $$ def", 123},
"abcdef\n[]": {"abcdef$", 123},
"abc ? ? ? ? def\n[123 456 123 456]": {"abc $? $? $0 $? def", 123, 456, 789},
"abc ? raw ? raw def\n[123 123]": {"abc $? $? $0 $? def", 123, Raw("raw"), 789},
}
for expected, c := range cases {
args := new(Args)
for i := 1; i < len(c); i++ {
args.Add(c[i])
}
sql, values := args.Compile(c[0].(string))
actual := fmt.Sprintf("%v\n%v", sql, values)
if actual != expected {
t.Fatalf("invalid compile result. [expected:%v] [actual:%v]", expected, actual)
}
}
old := DefaultFlavor
DefaultFlavor = PostgreSQL
defer func() {
DefaultFlavor = old
}()
// PostgreSQL flavor compiled sql.
for expected, c := range cases {
args := new(Args)
for i := 1; i < len(c); i++ {
args.Add(c[i])
}
sql, values := args.Compile(c[0].(string))
actual := fmt.Sprintf("%v\n%v", sql, values)
expected = toPostgreSQL(expected)
if actual != expected {
t.Fatalf("invalid compile result. [expected:%v] [actual:%v]", expected, actual)
}
}
}
func toPostgreSQL(sql string) string {
parts := strings.Split(sql, "?")
buf := &bytes.Buffer{}
buf.WriteString(parts[0])
for i, p := range parts[1:] {
fmt.Fprintf(buf, "$%v", i+1)
buf.WriteString(p)
}
return buf.String()
}

View File

@@ -0,0 +1,105 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"fmt"
)
// Builder is a general SQL builder.
// It's used by Args to create nested SQL like the `IN` expression in
// `SELECT * FROM t1 WHERE id IN (SELECT id FROM t2)`.
type Builder interface {
Build() (sql string, args []interface{})
BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{})
}
type compiledBuilder struct {
args *Args
format string
}
func (cb *compiledBuilder) Build() (sql string, args []interface{}) {
return cb.args.Compile(cb.format)
}
func (cb *compiledBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
return cb.args.CompileWithFlavor(cb.format, flavor, initialArg...)
}
type flavoredBuilder struct {
builder Builder
flavor Flavor
}
func (fb *flavoredBuilder) Build() (sql string, args []interface{}) {
return fb.builder.BuildWithFlavor(fb.flavor)
}
func (fb *flavoredBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
return fb.builder.BuildWithFlavor(flavor, initialArg...)
}
// WithFlavor creates a new Builder based on builder with a default flavor.
func WithFlavor(builder Builder, flavor Flavor) Builder {
return &flavoredBuilder{
builder: builder,
flavor: flavor,
}
}
// Buildf creates a Builder from a format string using `fmt.Sprintf`-like syntax.
// As all arguments will be converted to a string internally, e.g. "$0",
// only `%v` and `%s` are valid.
func Buildf(format string, arg ...interface{}) Builder {
args := &Args{
Flavor: DefaultFlavor,
}
vars := make([]interface{}, 0, len(arg))
for _, a := range arg {
vars = append(vars, args.Add(a))
}
return &compiledBuilder{
args: args,
format: fmt.Sprintf(Escape(format), vars...),
}
}
// Build creates a Builder from a format string.
// The format string uses special syntax to represent arguments.
// See doc in `Args#Compile` for syntax details.
func Build(format string, arg ...interface{}) Builder {
args := &Args{
Flavor: DefaultFlavor,
}
for _, a := range arg {
args.Add(a)
}
return &compiledBuilder{
args: args,
format: format,
}
}
// BuildNamed creates a Builder from a format string.
// The format string uses `${key}` to refer the value of named by key.
func BuildNamed(format string, named map[string]interface{}) Builder {
args := &Args{
Flavor: DefaultFlavor,
onlyNamed: true,
}
for n, v := range named {
args.Add(Named(n, v))
}
return &compiledBuilder{
args: args,
format: format,
}
}

View File

@@ -0,0 +1,113 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"database/sql"
"fmt"
"reflect"
"testing"
)
func ExampleBuildf() {
sb := NewSelectBuilder()
sb.Select("id").From("user")
explain := Buildf("EXPLAIN %v LEFT JOIN SELECT * FROM banned WHERE state IN (%v, %v)", sb, 1, 2)
sql, args := explain.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// EXPLAIN SELECT id FROM user LEFT JOIN SELECT * FROM banned WHERE state IN (?, ?)
// [1 2]
}
func ExampleBuild() {
sb := NewSelectBuilder()
sb.Select("id").From("user").Where(sb.In("status", 1, 2))
b := Build("EXPLAIN $? LEFT JOIN SELECT * FROM $? WHERE created_at > $? AND state IN (${states}) AND modified_at BETWEEN $2 AND $?",
sb, Raw("banned"), 1514458225, 1514544625, Named("states", List([]int{3, 4, 5})))
sql, args := b.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// EXPLAIN SELECT id FROM user WHERE status IN (?, ?) LEFT JOIN SELECT * FROM banned WHERE created_at > ? AND state IN (?, ?, ?) AND modified_at BETWEEN ? AND ?
// [1 2 1514458225 3 4 5 1514458225 1514544625]
}
func ExampleBuildNamed() {
b := BuildNamed("SELECT * FROM ${table} WHERE status IN (${status}) AND name LIKE ${name} AND created_at > ${time} AND modified_at < ${time} + 86400",
map[string]interface{}{
"time": sql.Named("start", 1234567890),
"status": List([]int{1, 2, 5}),
"name": "Huan%",
"table": Raw("user"),
})
sql, args := b.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// SELECT * FROM user WHERE status IN (?, ?, ?) AND name LIKE ? AND created_at > @start AND modified_at < @start + 86400
// [1 2 5 Huan% {{} start 1234567890}]
}
func ExampleWithFlavor() {
sql, args := WithFlavor(Buildf("SELECT * FROM foo WHERE id = %v", 1234), PostgreSQL).Build()
fmt.Println(sql)
fmt.Println(args)
// Explicitly use MySQL as the flavor.
sql, args = WithFlavor(Buildf("SELECT * FROM foo WHERE id = %v", 1234), PostgreSQL).BuildWithFlavor(MySQL)
fmt.Println(sql)
fmt.Println(args)
// Output:
// SELECT * FROM foo WHERE id = $1
// [1234]
// SELECT * FROM foo WHERE id = ?
// [1234]
}
func TestBuildWithPostgreSQL(t *testing.T) {
sb1 := PostgreSQL.NewSelectBuilder()
sb1.Select("col1", "col2").From("t1").Where(sb1.E("id", 1234), sb1.G("level", 2))
sb2 := PostgreSQL.NewSelectBuilder()
sb2.Select("col3", "col4").From("t2").Where(sb2.E("id", 4567), sb2.LE("level", 5))
// Use DefaultFlavor (MySQL) instead of PostgreSQL.
sql, args := Build("SELECT $1 AS col5 LEFT JOIN $0 LEFT JOIN $2", sb1, 7890, sb2).Build()
if expected := "SELECT ? AS col5 LEFT JOIN SELECT col1, col2 FROM t1 WHERE id = ? AND level > ? LEFT JOIN SELECT col3, col4 FROM t2 WHERE id = ? AND level <= ?"; sql != expected {
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql)
}
if expected := []interface{}{7890, 1234, 2, 4567, 5}; !reflect.DeepEqual(args, expected) {
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
}
old := DefaultFlavor
DefaultFlavor = PostgreSQL
defer func() {
DefaultFlavor = old
}()
sql, args = Build("SELECT $1 AS col5 LEFT JOIN $0 LEFT JOIN $2", sb1, 7890, sb2).Build()
if expected := "SELECT $1 AS col5 LEFT JOIN SELECT col1, col2 FROM t1 WHERE id = $2 AND level > $3 LEFT JOIN SELECT col3, col4 FROM t2 WHERE id = $4 AND level <= $5"; sql != expected {
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql)
}
if expected := []interface{}{7890, 1234, 2, 4567, 5}; !reflect.DeepEqual(args, expected) {
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
}
}

View File

@@ -0,0 +1,136 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"fmt"
"strings"
)
// Cond provides several helper methods to build conditions.
type Cond struct {
Args *Args
}
// Equal represents "field = value".
func (c *Cond) Equal(field string, value interface{}) string {
return fmt.Sprintf("%v = %v", Escape(field), c.Args.Add(value))
}
// E is an alias of Equal.
func (c *Cond) E(field string, value interface{}) string {
return c.Equal(field, value)
}
// NotEqual represents "field != value".
func (c *Cond) NotEqual(field string, value interface{}) string {
return fmt.Sprintf("%v <> %v", Escape(field), c.Args.Add(value))
}
// NE is an alias of NotEqual.
func (c *Cond) NE(field string, value interface{}) string {
return c.NotEqual(field, value)
}
// GreaterThan represents "field > value".
func (c *Cond) GreaterThan(field string, value interface{}) string {
return fmt.Sprintf("%v > %v", Escape(field), c.Args.Add(value))
}
// G is an alias of GreaterThan.
func (c *Cond) G(field string, value interface{}) string {
return c.GreaterThan(field, value)
}
// GreaterEqualThan represents "field >= value".
func (c *Cond) GreaterEqualThan(field string, value interface{}) string {
return fmt.Sprintf("%v >= %v", Escape(field), c.Args.Add(value))
}
// GE is an alias of GreaterEqualThan.
func (c *Cond) GE(field string, value interface{}) string {
return c.GreaterEqualThan(field, value)
}
// LessThan represents "field < value".
func (c *Cond) LessThan(field string, value interface{}) string {
return fmt.Sprintf("%v < %v", Escape(field), c.Args.Add(value))
}
// L is an alias of LessThan.
func (c *Cond) L(field string, value interface{}) string {
return c.LessThan(field, value)
}
// LessEqualThan represents "field <= value".
func (c *Cond) LessEqualThan(field string, value interface{}) string {
return fmt.Sprintf("%v <= %v", Escape(field), c.Args.Add(value))
}
// LE is an alias of LessEqualThan.
func (c *Cond) LE(field string, value interface{}) string {
return c.LessEqualThan(field, value)
}
// In represents "field IN (value...)".
func (c *Cond) In(field string, value ...interface{}) string {
vs := make([]string, 0, len(value))
for _, v := range value {
vs = append(vs, c.Args.Add(v))
}
return fmt.Sprintf("%v IN (%v)", Escape(field), strings.Join(vs, ", "))
}
// NotIn represents "field NOT IN (value...)".
func (c *Cond) NotIn(field string, value ...interface{}) string {
vs := make([]string, 0, len(value))
for _, v := range value {
vs = append(vs, c.Args.Add(v))
}
return fmt.Sprintf("%v NOT IN (%v)", Escape(field), strings.Join(vs, ", "))
}
// Like represents "field LIKE value".
func (c *Cond) Like(field string, value interface{}) string {
return fmt.Sprintf("%v LIKE %v", Escape(field), c.Args.Add(value))
}
// NotLike represents "field NOT LIKE value".
func (c *Cond) NotLike(field string, value interface{}) string {
return fmt.Sprintf("%v NOT LIKE %v", Escape(field), c.Args.Add(value))
}
// IsNull represents "field IS NULL".
func (c *Cond) IsNull(field string) string {
return fmt.Sprintf("%v IS NULL", Escape(field))
}
// IsNotNull represents "field IS NOT NULL".
func (c *Cond) IsNotNull(field string) string {
return fmt.Sprintf("%v IS NOT NULL", Escape(field))
}
// Between represents "field BETWEEN lower AND upper".
func (c *Cond) Between(field string, lower, upper interface{}) string {
return fmt.Sprintf("%v BETWEEN %v AND %v", Escape(field), c.Args.Add(lower), c.Args.Add(upper))
}
// NotBetween represents "field NOT BETWEEN lower AND upper".
func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
return fmt.Sprintf("%v NOT BETWEEN %v AND %v", Escape(field), c.Args.Add(lower), c.Args.Add(upper))
}
// Or represents OR logic like "expr1 OR expr2 OR expr3".
func (c *Cond) Or(orExpr ...string) string {
return fmt.Sprintf("(%v)", strings.Join(orExpr, " OR "))
}
// Var returns a placeholder for value.
func (c *Cond) Var(value interface{}) string {
return c.Args.Add(value)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"testing"
)
func TestCond(t *testing.T) {
cases := map[string]func() string{
"$$a = $0": func() string { return newTestCond().Equal("$a", 123) },
"$$b = $0": func() string { return newTestCond().E("$b", 123) },
"$$a <> $0": func() string { return newTestCond().NotEqual("$a", 123) },
"$$b <> $0": func() string { return newTestCond().NE("$b", 123) },
"$$a > $0": func() string { return newTestCond().GreaterThan("$a", 123) },
"$$b > $0": func() string { return newTestCond().G("$b", 123) },
"$$a >= $0": func() string { return newTestCond().GreaterEqualThan("$a", 123) },
"$$b >= $0": func() string { return newTestCond().GE("$b", 123) },
"$$a < $0": func() string { return newTestCond().LessThan("$a", 123) },
"$$b < $0": func() string { return newTestCond().L("$b", 123) },
"$$a <= $0": func() string { return newTestCond().LessEqualThan("$a", 123) },
"$$b <= $0": func() string { return newTestCond().LE("$b", 123) },
"$$a IN ($0, $1, $2)": func() string { return newTestCond().In("$a", 1, 2, 3) },
"$$a NOT IN ($0, $1, $2)": func() string { return newTestCond().NotIn("$a", 1, 2, 3) },
"$$a LIKE $0": func() string { return newTestCond().Like("$a", "%Huan%") },
"$$a NOT LIKE $0": func() string { return newTestCond().NotLike("$a", "%Huan%") },
"$$a IS NULL": func() string { return newTestCond().IsNull("$a") },
"$$a IS NOT NULL": func() string { return newTestCond().IsNotNull("$a") },
"$$a BETWEEN $0 AND $1": func() string { return newTestCond().Between("$a", 123, 456) },
"$$a NOT BETWEEN $0 AND $1": func() string { return newTestCond().NotBetween("$a", 123, 456) },
"(1 = 1 OR 2 = 2 OR 3 = 3)": func() string { return newTestCond().Or("1 = 1", "2 = 2", "3 = 3") },
"$0": func() string { return newTestCond().Var(123) },
}
for expected, f := range cases {
if actual := f(); expected != actual {
t.Fatalf("invalid result. [expected:%v] [actual:%v]", expected, actual)
}
}
}
func newTestCond() *Cond {
return &Cond{
Args: &Args{},
}
}

View File

@@ -0,0 +1,57 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import "fmt"
// Supported flavors.
const (
invalidFlavor Flavor = iota
MySQL
PostgreSQL
)
var (
// DefaultFlavor is the default flavor for all builders.
DefaultFlavor = MySQL
)
// Flavor is the flag to control the format of compiled sql.
type Flavor int
// String returns the name of f.
func (f Flavor) String() string {
switch f {
case MySQL:
return "MySQL"
case PostgreSQL:
return "PostgreSQL"
}
return "<invalid>"
}
// NewSelectBuilder creates a new SELECT builder with flavor.
func (f Flavor) NewSelectBuilder() *SelectBuilder {
b := newSelectBuilder()
b.SetFlavor(f)
return b
}
// Quote adds quote for name to make sure the name can be used safely
// as table name or field name.
//
// * For MySQL, use back quote (`) to quote name;
// * For PostgreSQL, use double quote (") to quote name.
func (f Flavor) Quote(name string) string {
switch f {
case MySQL:
return fmt.Sprintf("`%v`", name)
case PostgreSQL:
return fmt.Sprintf(`"%v"`, name)
}
return name
}

View File

@@ -0,0 +1,40 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"fmt"
"testing"
)
func TestFlavor(t *testing.T) {
cases := map[Flavor]string{
0: "<invalid>",
MySQL: "MySQL",
PostgreSQL: "PostgreSQL",
}
for f, expected := range cases {
if actual := f.String(); actual != expected {
t.Fatalf("invalid flavor name. [expected:%v] [actual:%v]", expected, actual)
}
}
}
func ExampleFlavor() {
// Create a flavored builder.
sb := PostgreSQL.NewSelectBuilder()
sb.Select("name").From("user").Where(
sb.E("id", 1234),
sb.G("rank", 3),
)
sql, args := sb.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// SELECT name FROM user WHERE id = $1 AND rank > $2
// [1234 3]
}

View File

@@ -0,0 +1,98 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"reflect"
"strings"
)
// Escape replaces `$` with `$$` in ident.
func Escape(ident string) string {
return strings.Replace(ident, "$", "$$", -1)
}
// EscapeAll replaces `$` with `$$` in all strings of ident.
func EscapeAll(ident ...string) []string {
escaped := make([]string, 0, len(ident))
for _, i := range ident {
escaped = append(escaped, Escape(i))
}
return escaped
}
// Flatten recursively extracts values in slices and returns
// a flattened []interface{} with all values.
// If slices is not a slice, return `[]interface{}{slices}`.
func Flatten(slices interface{}) (flattened []interface{}) {
v := reflect.ValueOf(slices)
slices, flattened = flatten(v)
if slices != nil {
return []interface{}{slices}
}
return flattened
}
func flatten(v reflect.Value) (elem interface{}, flattened []interface{}) {
k := v.Kind()
for k == reflect.Interface {
v = v.Elem()
k = v.Kind()
}
if k != reflect.Slice && k != reflect.Array {
return v.Interface(), nil
}
for i, l := 0, v.Len(); i < l; i++ {
e, f := flatten(v.Index(i))
if e == nil {
flattened = append(flattened, f...)
} else {
flattened = append(flattened, e)
}
}
return
}
type rawArgs struct {
expr string
}
// Raw marks the expr as a raw value which will not be added to args.
func Raw(expr string) interface{} {
return rawArgs{expr}
}
type listArgs struct {
args []interface{}
}
// List marks arg as a list of data.
// If arg is `[]int{1, 2, 3}`, it will be compiled to `?, ?, ?` with args `[1 2 3]`.
func List(arg interface{}) interface{} {
return listArgs{Flatten(arg)}
}
type namedArgs struct {
name string
arg interface{}
}
// Named creates a named argument.
// Unlike `sql.Named`, this named argument works only with `Build` or `BuildNamed` for convenience
// and will be replaced to a `?` after `Compile`.
func Named(name string, arg interface{}) interface{} {
return namedArgs{
name: name,
arg: arg,
}
}

View File

@@ -0,0 +1,59 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"reflect"
"testing"
)
func TestEscape(t *testing.T) {
cases := map[string]string{
"foo": "foo",
"$foo": "$$foo",
"$$$": "$$$$$$",
}
var inputs, expects []string
for s, expected := range cases {
inputs = append(inputs, s)
expects = append(expects, expected)
if actual := Escape(s); actual != expected {
t.Fatalf("invalid escape result. [expected:%v] [actual:%v]", expected, actual)
}
}
actuals := EscapeAll(inputs...)
if !reflect.DeepEqual(expects, actuals) {
t.Fatalf("invalid escape result. [expected:%v] [actual:%v]", expects, actuals)
}
}
func TestFlatten(t *testing.T) {
cases := [][2]interface{}{
{
"foo",
[]interface{}{"foo"},
},
{
[]int{1, 2, 3},
[]interface{}{1, 2, 3},
},
{
[]interface{}{"abc", []int{1, 2, 3}, [3]string{"def", "ghi"}},
[]interface{}{"abc", 1, 2, 3, "def", "ghi", ""},
},
}
for _, c := range cases {
input, expected := c[0], c[1]
actual := Flatten(input)
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("invalid flatten result. [expected:%v] [actual:%v]", expected, actual)
}
}
}

View File

@@ -0,0 +1,212 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"bytes"
"fmt"
"strconv"
"strings"
)
// NewSelectBuilder creates a new SELECT builder.
func NewSelectBuilder() *SelectBuilder {
return DefaultFlavor.NewSelectBuilder()
}
func newSelectBuilder() *SelectBuilder {
args := &Args{}
return &SelectBuilder{
Cond: Cond{
Args: args,
},
limit: -1,
offset: -1,
args: args,
}
}
// SelectBuilder is a builder to build SELECT.
type SelectBuilder struct {
Cond
distinct bool
tables []string
selectCols []string
whereExprs []string
havingExprs []string
groupByCols []string
orderByCols []string
order string
limit int
offset int
args *Args
}
// Distinct marks this SELECT as DISTINCT.
func (sb *SelectBuilder) Distinct() *SelectBuilder {
sb.distinct = true
return sb
}
// Select sets columns in SELECT.
func (sb *SelectBuilder) Select(col ...string) *SelectBuilder {
sb.selectCols = EscapeAll(col...)
return sb
}
// From sets table names in SELECT.
func (sb *SelectBuilder) From(table ...string) *SelectBuilder {
sb.tables = table
return sb
}
// Where sets expressions of WHERE in SELECT.
func (sb *SelectBuilder) Where(andExpr ...string) *SelectBuilder {
sb.whereExprs = append(sb.whereExprs, andExpr...)
return sb
}
// Having sets expressions of HAVING in SELECT.
func (sb *SelectBuilder) Having(andExpr ...string) *SelectBuilder {
sb.havingExprs = append(sb.havingExprs, andExpr...)
return sb
}
// GroupBy sets columns of GROUP BY in SELECT.
func (sb *SelectBuilder) GroupBy(col ...string) *SelectBuilder {
sb.groupByCols = EscapeAll(col...)
return sb
}
// OrderBy sets columns of ORDER BY in SELECT.
func (sb *SelectBuilder) OrderBy(col ...string) *SelectBuilder {
sb.orderByCols = EscapeAll(col...)
return sb
}
// Asc sets order of ORDER BY to ASC.
func (sb *SelectBuilder) Asc() *SelectBuilder {
sb.order = "ASC"
return sb
}
// Desc sets order of ORDER BY to DESC.
func (sb *SelectBuilder) Desc() *SelectBuilder {
sb.order = "DESC"
return sb
}
// Limit sets the LIMIT in SELECT.
func (sb *SelectBuilder) Limit(limit int) *SelectBuilder {
sb.limit = limit
return sb
}
// Offset sets the LIMIT offset in SELECT.
func (sb *SelectBuilder) Offset(offset int) *SelectBuilder {
sb.offset = offset
return sb
}
// As returns an AS expression.
func (sb *SelectBuilder) As(col, alias string) string {
return fmt.Sprintf("%v AS %v", col, Escape(alias))
}
// BuilderAs returns an AS expression wrapping a complex SQL.
// According to SQL syntax, SQL built by builder is surrounded by parens.
func (sb *SelectBuilder) BuilderAs(builder Builder, alias string) string {
return fmt.Sprintf("(%v) AS %v", sb.Var(builder), Escape(alias))
}
// String returns the compiled SELECT string.
func (sb *SelectBuilder) String() string {
s, _ := sb.Build()
return s
}
// Build returns compiled SELECT string and args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (sb *SelectBuilder) Build() (sql string, args []interface{}) {
return sb.BuildWithFlavor(sb.args.Flavor)
}
// BuildWithFlavor returns compiled SELECT string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &bytes.Buffer{}
buf.WriteString("SELECT ")
if sb.distinct {
buf.WriteString("DISTINCT ")
}
buf.WriteString(strings.Join(sb.selectCols, ", "))
buf.WriteString(" FROM ")
buf.WriteString(strings.Join(sb.tables, ", "))
if len(sb.whereExprs) > 0 {
buf.WriteString(" WHERE ")
buf.WriteString(strings.Join(sb.whereExprs, " AND "))
}
if len(sb.groupByCols) > 0 {
buf.WriteString(" GROUP BY ")
buf.WriteString(strings.Join(sb.groupByCols, ", "))
if len(sb.havingExprs) > 0 {
buf.WriteString(" HAVING ")
buf.WriteString(strings.Join(sb.havingExprs, " AND "))
}
}
if len(sb.orderByCols) > 0 {
buf.WriteString(" ORDER BY ")
buf.WriteString(strings.Join(sb.orderByCols, ", "))
if sb.order != "" {
buf.WriteRune(' ')
buf.WriteString(sb.order)
}
}
if sb.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteString(strconv.Itoa(sb.limit))
if sb.offset >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteString(strconv.Itoa(sb.offset))
}
}
return sb.Args.CompileWithFlavor(buf.String(), flavor, initialArg...)
}
// SetFlavor sets the flavor of compiled sql.
func (sb *SelectBuilder) SetFlavor(flavor Flavor) (old Flavor) {
old = sb.args.Flavor
sb.args.Flavor = flavor
return
}
// Copy the builder
func (sb *SelectBuilder) Copy() *SelectBuilder {
return &SelectBuilder{
Cond: sb.Cond,
distinct: sb.distinct,
tables: sb.tables,
selectCols: sb.selectCols,
whereExprs: sb.whereExprs,
havingExprs: sb.havingExprs,
groupByCols: sb.groupByCols,
orderByCols: sb.orderByCols,
order: sb.order,
limit: sb.limit,
offset: sb.offset,
args: sb.args.Copy(),
}
}

View File

@@ -0,0 +1,67 @@
// Copyright 2018 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"database/sql"
"fmt"
)
func ExampleSelectBuilder() {
sb := NewSelectBuilder()
sb.Distinct().Select("id", "name", sb.As("COUNT(*)", "t"))
sb.From("demo.user")
sb.Where(
sb.GreaterThan("id", 1234),
sb.Like("name", "%Du"),
sb.Or(
sb.IsNull("id_card"),
sb.In("status", 1, 2, 5),
),
sb.NotIn(
"id",
NewSelectBuilder().Select("id").From("banned"),
), // Nested SELECT.
"modified_at > created_at + "+sb.Var(86400), // It's allowed to write arbitrary SQL.
)
sb.GroupBy("status").Having(sb.NotIn("status", 4, 5))
sb.OrderBy("modified_at").Asc()
sb.Limit(10).Offset(5)
sql, args := sb.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// SELECT DISTINCT id, name, COUNT(*) AS t FROM demo.user WHERE id > ? AND name LIKE ? AND (id_card IS NULL OR status IN (?, ?, ?)) AND id NOT IN (SELECT id FROM banned) AND modified_at > created_at + ? GROUP BY status HAVING status NOT IN (?, ?) ORDER BY modified_at ASC LIMIT 10 OFFSET 5
// [1234 %Du 1 2 5 86400 4 5]
}
func ExampleSelectBuilder_advancedUsage() {
sb := NewSelectBuilder()
innerSb := NewSelectBuilder()
sb.Select("id", "name")
sb.From(
sb.BuilderAs(innerSb, "user"),
)
sb.Where(
sb.In("status", Flatten([]int{1, 2, 3})...),
sb.Between("created_at", sql.Named("start", 1234567890), sql.Named("end", 1234599999)),
)
innerSb.Select("*")
innerSb.From("banned")
innerSb.Where(
innerSb.NotIn("name", Flatten([]string{"Huan Du", "Charmy Liu"})...),
)
sql, args := sb.Build()
fmt.Println(sql)
fmt.Println(args)
// Output:
// SELECT id, name FROM (SELECT * FROM banned WHERE name NOT IN (?, ?)) AS user WHERE status IN (?, ?, ?) AND created_at BETWEEN @start AND @end
// [Huan Du Charmy Liu 1 2 3 {{} start 1234567890} {{} end 1234599999}]
}

View File

@@ -0,0 +1,140 @@
package dao
import (
"context"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"strconv"
"time"
"go-common/app/service/main/account-recovery/model"
"go-common/library/database/elastic"
"go-common/library/log"
)
// NickNameLog NickNameLog
func (d *Dao) NickNameLog(c context.Context, nickNameReq *model.NickNameReq) (res *model.NickNameLogRes, err error) {
nowYear := time.Now().Year()
index1 := "log_user_action_14_" + strconv.Itoa(nowYear)
index2 := "log_user_action_14_" + strconv.Itoa(nowYear-1)
r := d.es.NewRequest("log_user_action").Fields("str_0", "str_1").Index(index1, index2)
r.Order("ctime", elastic.OrderDesc).Order("mid", elastic.OrderDesc).Pn(nickNameReq.Page).Ps(nickNameReq.Size)
if nickNameReq.Mid != 0 {
r.WhereEq("mid", nickNameReq.Mid)
}
if nickNameReq.From != 0 && nickNameReq.To != 0 {
ftm := time.Unix(nickNameReq.From, 0)
sf := ftm.Format("2006-01-02 15:04:05")
ttm := time.Unix(nickNameReq.To, 0)
tf := ttm.Format("2006-01-02 15:04:05")
r.WhereRange("ctime", sf, tf, elastic.RangeScopeLoRo)
}
esres := new(model.NickESRes)
if err = r.Scan(context.TODO(), &esres); err != nil {
log.Error("nickNameLog search error(%v)", err)
return
}
var nickNames = make([]*model.NickNameInfo, 0)
for _, value := range esres.Result {
ulr := model.NickNameInfo{OldName: value.OldName, NewName: value.NewName}
nickNames = append(nickNames, &ulr)
}
res = &model.NickNameLogRes{Page: esres.Page, Result: nickNames}
return
}
type userLogsExtra struct {
EncryptTel string `json:"tel"`
EncryptEmail string `json:"email"`
}
// UserBindLog User bind log
func (d *Dao) UserBindLog(c context.Context, userActLogReq *model.UserBindLogReq) (res *model.UserBindLogRes, err error) {
e := d.es
nowYear := time.Now().Year()
var count = 2 //默认查询两年
//2016年就有了手机历史记录此处需要循环建立索引 , 2018年才有邮箱这个功能
if userActLogReq.Action == "telBindLog" {
count = nowYear - 2015
}
if userActLogReq.Action == "emailBindLog" {
count = nowYear - 2017
}
indexs := make([]string, count)
for i := 0; i < count; i++ {
indexs[i] = "log_user_action_54_" + strconv.Itoa(nowYear-i)
}
r := e.NewRequest("log_user_action").Fields("mid", "str_0", "extra_data", "ctime").Index(indexs...)
r.Order("ctime", elastic.OrderDesc).Order("mid", elastic.OrderDesc).Pn(userActLogReq.Page).Ps(userActLogReq.Size)
if userActLogReq.Mid != 0 {
r.WhereEq("mid", userActLogReq.Mid)
}
if userActLogReq.Query != "" {
hash := sha1.New()
hash.Write([]byte(userActLogReq.Query))
telHash := base64.StdEncoding.EncodeToString(hash.Sum(d.hashSalt))
r.WhereEq("str_0", telHash)
}
if userActLogReq.Action != "" {
r.WhereEq("action", userActLogReq.Action)
}
if userActLogReq.From != 0 && userActLogReq.To != 0 {
ftm := time.Unix(userActLogReq.From, 0)
sf := ftm.Format("2006-01-02 15:04:05")
ttm := time.Unix(userActLogReq.To, 0)
tf := ttm.Format("2006-01-02 15:04:05")
r.WhereRange("ctime", sf, tf, elastic.RangeScopeLoRo)
}
esres := new(model.EsRes)
if err = r.Scan(context.Background(), &esres); err != nil {
log.Error("userActLogs search error(%v)", err)
return
}
var userBindLogs = make([]*model.UserBindLog, 0)
for _, value := range esres.Result {
var email, tel string
//var model.UserBindLog
userLogExtra := userLogsExtra{}
err = json.Unmarshal([]byte(value.ExtraData), &userLogExtra)
if err != nil {
log.Error("cannot convert json(%s) to struct,err(%+v) ", value.ExtraData, err)
continue
}
if userLogExtra.EncryptEmail != "" {
email, err = d.decrypt(userLogExtra.EncryptEmail)
if err != nil {
log.Error("EncryptEmail decode err(%v)", err)
continue
}
}
if userLogExtra.EncryptTel != "" {
tel, err = d.decrypt(userLogExtra.EncryptTel)
if err != nil {
log.Error("EncryptTel decode err(%v)", err)
continue
}
}
ulr := model.UserBindLog{Mid: value.Mid, Email: email, Phone: tel, Time: value.CTime}
userBindLogs = append(userBindLogs, &ulr)
}
res = &model.UserBindLogRes{Page: esres.Page, Result: userBindLogs}
return
}

View File

@@ -0,0 +1,42 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/account-recovery/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoNickNameLog(t *testing.T) {
var (
c = context.Background()
nickNameReq = &model.NickNameReq{Mid: 111001254}
)
convey.Convey("NickNameLog", t, func(ctx convey.C) {
res, err := d.NickNameLog(c, nickNameReq)
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 TestDaoUserBindLog(t *testing.T) {
var (
c = context.Background()
emailReq = &model.UserBindLogReq{
Action: "emailBindLog",
Mid: 23,
Size: 100,
}
)
convey.Convey("UserBindLog", t, func(ctx convey.C) {
res, err := d.UserBindLog(c, emailReq)
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,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"appeal.go",
"bfs.go",
"http.go",
],
importpath = "go-common/app/service/main/account-recovery/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/model:go_default_library",
"//app/service/main/account-recovery/service:go_default_library",
"//library/database/bfs: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/permit:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//vendor/github.com/google/uuid: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,201 @@
package http
import (
"strconv"
"strings"
"go-common/app/service/main/account-recovery/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
)
// queryAccount is verify account is exist
func queryAccount(c *bm.Context) {
params := new(model.QueryInfoReq)
if err := c.Bind(params); err != nil {
return
}
c.JSON(srv.QueryAccount(c, params))
}
// commitInfo is commit appeal info
func commitInfo(c *bm.Context) {
uinfo := new(model.UserInfoReq)
if err := c.Bind(uinfo); err != nil {
return
}
uinfo.LoginAddrs = strings.TrimSpace(uinfo.LoginAddrs)
uinfo.RegAddr = strings.TrimSpace(uinfo.RegAddr)
uinfo.Unames = strings.TrimSpace(uinfo.Unames)
uinfo.Pwds = strings.TrimSpace(uinfo.Pwds)
uinfo.Phones = strings.TrimSpace(uinfo.Phones)
uinfo.Emails = strings.TrimSpace(uinfo.Emails)
uinfo.SafeAnswer = strings.TrimSpace(uinfo.SafeAnswer)
uinfo.LinkMail = strings.TrimSpace(uinfo.LinkMail)
uinfo.Captcha = strings.TrimSpace(uinfo.Captcha)
uinfo.CardID = strings.TrimSpace(uinfo.CardID)
uinfo.Business = strings.TrimSpace(uinfo.Business)
bizMap := make(map[string]string)
req := c.Request.Form
uinfo.BusinessMap = bizMap
extraArgs := model.BusinessExtraArgs(uinfo.Business)
for _, k := range extraArgs {
bizMap[k] = req.Get(k)
}
c.JSON(nil, srv.CommitInfo(c, uinfo))
}
// queryConWithBusiness query with business
func queryConWithBusiness(business string) func(*bm.Context) {
return func(c *bm.Context) {
params := new(model.QueryRecoveryInfoReq)
if err := c.Bind(params); err != nil {
return
}
params.Bussiness = business
if perms, ok := c.Get(permit.CtxPermissions); ok {
for _, p := range perms.([]string) {
if p == "ACCOUNT_RECOVERY_ADVANCED" {
params.IsAdvanced = true
}
}
}
req := c.Request
if inStatus := req.Form.Get("status"); inStatus != "" {
status, err := strconv.ParseInt(inStatus, 10, 64)
if err != nil {
log.Error("Invalid status: %s: %+v", inStatus, err)
status = 0
}
params.Status = &status
}
if inGame := req.Form.Get("game"); inGame != "" {
game, err := strconv.ParseInt(inGame, 10, 64)
if err != nil {
log.Error("Invalid game: %s: %+v", inGame, err)
game = 0
}
params.Game = &game
}
if params.Size <= 0 {
params.Size = 50
}
c.JSON(srv.QueryCon(c, params))
}
}
// judge is reject or agree one operation
func judge(c *bm.Context) {
req := new(model.JudgeReq)
if err := c.Bind(req); err != nil {
return
}
c.JSON(nil, srv.Judge(c, req))
}
// batchJudge reject or agree more operation
func batchJudge(c *bm.Context) {
req := new(model.BatchJudgeReq)
if err := c.Bind(req); err != nil {
return
}
split := strings.Split(req.Rids, ",")
rids := make([]int64, 0, len(split))
for _, v := range split {
rid, err := strconv.ParseInt(v, 10, 64)
if err != nil {
c.JSON(nil, err)
return
}
rids = append(rids, rid)
}
req.RidsAry = rids
c.JSON(nil, srv.BatchJudge(c, req))
}
// getCaptchaMail
func getCaptchaMail(c *bm.Context) {
req := new(model.CaptchaMailReq)
if err := c.Bind(req); err != nil {
return
}
if !strings.Contains(req.LinkMail, "@") {
c.JSON(nil, ecode.RequestErr)
return
}
if req.Mid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
var err error
state, err := srv.GetCaptchaMail(c, req)
c.JSON(map[string]int64{
"state": state,
}, err)
}
func parseMid(midStr string) (mid int64) {
if len(midStr) == 0 {
return 0
}
mid, err := strconv.ParseInt(midStr, 10, 64)
if err != nil {
return 0
}
return
}
func verifyCode(c *bm.Context) {
var err error
arg := new(struct {
Token string `form:"token" validate:"required"`
Code string `form:"code" validate:"required"` //验证码
})
if err = c.Bind(arg); err != nil {
return
}
if err = srv.Verify(c, arg.Token, arg.Code); err != nil {
c.JSON(nil, ecode.CreativeGeetestErr)
return
}
c.JSON(nil, err)
}
func webToken(c *bm.Context) {
c.JSON(srv.WebToken(c))
}
func compareInfo(c *bm.Context) {
rid := parseMid(c.Request.Form.Get("rid"))
if rid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, srv.CompareInfo(c, rid))
}
func sendMail(c *bm.Context) {
req := new(model.SendMailReq)
if err := c.Bind(req); err != nil {
return
}
if req.RID <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, srv.SendMail(c, req))
}
// gameList game list
func gameList(c *bm.Context) {
mids := c.Request.Form.Get("mids")
if mids == "" {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(srv.GameList(c, mids))
}

View File

@@ -0,0 +1,89 @@
package http
import (
"fmt"
"io/ioutil"
"path/filepath"
"go-common/app/service/main/account-recovery/model"
"go-common/library/database/bfs"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/google/uuid"
"github.com/pkg/errors"
)
var _allowedExt = map[string]struct{}{
".zip": {},
".rar": {},
".7z": {},
}
func fileUpload(c *bm.Context) {
defer c.Request.Form.Del("file") // 防止日志不出现
c.Request.ParseMultipartForm(32 << 20)
recoveryFile, fileName, err := func() ([]byte, string, error) {
f, fh, err := c.Request.FormFile("file")
if err != nil {
return nil, "", errors.Wrapf(err, "parse file form file: ")
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, "", errors.Wrapf(err, "read file form file:")
}
if len(data) <= 0 {
return nil, "", errors.Wrapf(err, "form file data: length: %d", len(data))
}
log.Info("Succeeded to parse file from form file: length: %d", len(data))
return data, fh.Filename, nil
}()
if err != nil {
log.Error("Failed to parse file file: %+v", err)
c.JSON(nil, ecode.RequestErr)
return
}
log.Info("Succeeded to parse recoveryFile data: recoveryFile-length: %d", len(recoveryFile))
//限制文件大小
if len(recoveryFile) > 10*1024*1024 {
log.Error("account-recovery: file is to large(%v)", len(recoveryFile))
c.JSON(nil, ecode.FileTooLarge)
return
}
//限制文件类型 *.zip, *.rar, *.7z
ext := filepath.Ext(fileName)
_, allowed := _allowedExt[ext]
if !allowed {
c.JSON(nil, ecode.BfsUploadFileContentTypeIllegal)
return
}
request := &bfs.Request{
Bucket: "account",
Dir: "recovery",
File: recoveryFile,
Filename: fmt.Sprintf("%s%s", uuid4(), ext),
}
bfsClient := bfs.New(nil)
location, err := bfsClient.Upload(c, request)
if err != nil {
log.Error("err(%+v)", err)
c.JSON(nil, err)
return
}
fileURL := model.BuildFileURL(location)
data := map[string]interface{}{
"url": location,
"fileURL": fileURL,
}
c.JSON(data, nil)
}
func uuid4() string {
return uuid.New().String()
}

View File

@@ -0,0 +1,72 @@
package http
import (
"net/http"
"go-common/app/service/main/account-recovery/conf"
"go-common/app/service/main/account-recovery/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
srv *service.Service
vfy *verify.Verify
authSvr *permit.Permit
)
// Init init
func Init(c *conf.Config) {
srv = service.New(c)
vfy = verify.New(c.Verify)
authSvr = permit.New(c.Auth)
engine := bm.DefaultServer(c.BM)
router(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start() error(%v)", err)
panic(err)
}
}
func router(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/account-recovery")
{
g.POST("/query", queryAccount)
g.POST("/commit", commitInfo)
g.POST("/getCaptchaMail", getCaptchaMail)
g.GET("/captcha/token", webToken)
g.POST("/captcha/verify", verifyCode)
g.POST("/file/upload", bm.CORS(), fileUpload)
}
adminGroup := e.Group("/x/admin/account-recovery", authSvr.Permit("ACCOUNT_RECOVERY_NORMAL"))
{
adminGroup.GET("/queryCon", queryConWithBusiness(""))
adminGroup.GET("/queryCon/account", authSvr.Permit("ACCOUNT_RECOVERY_ACCOUNT"), queryConWithBusiness("account"))
adminGroup.GET("/queryCon/game", authSvr.Permit("ACCOUNT_RECOVERY_GAME"), queryConWithBusiness("game"))
// todo judge,batchJudge也需要分配权限
adminGroup.POST("/judge", judge)
adminGroup.POST("/batchJudge", batchJudge)
adminGroup.POST("/gameList", gameList)
}
internalGroup := e.Group("/x/internal/account-recovery", vfy.Verify)
{
internalGroup.POST("/compare", compareInfo)
internalGroup.POST("/sendMail", sendMail)
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/service/main/account-recovery/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//library/log:go_default_library",
"//library/time: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,525 @@
package model
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"math/rand"
"net/url"
"strings"
gotime "time"
"go-common/app/service/main/account-recovery/conf"
"go-common/library/log"
"go-common/library/time"
)
// QueryInfoReq query mid info req
type QueryInfoReq struct {
QType string `form:"q_type" validate:"required"`
QValue string `form:"q_value" validate:"required"`
CToken string `form:"token" validate:"required"`
Code string `form:"code" validate:"required"`
}
// QueryInfoResp query info response
type QueryInfoResp struct {
Status int64 `json:"status"`
UID int64 `json:"uid"`
}
// MIDInfo mid info data
type MIDInfo struct {
Mids string `json:"mids"`
Count int64 `json:"count"`
}
// User user info data
type User struct {
UserID string `json:"userid"`
Pwd string `json:"pwd"`
}
// Check check safe: Flag , reg: CheckInfo
type Check struct {
CheckInfo string `json:"checkInfo"`
}
// UserInfoReq 用户申诉提交信息
type UserInfoReq struct {
LoginAddrs string `json:"login_addrs" form:"login_addrs" validate:"required,min=1"`
RegTime time.Time `json:"reg_time" form:"reg_time" validate:"required"`
RegType int8 `json:"reg_type" form:"reg_type" validate:"required"`
RegAddr string `json:"reg_addr" form:"reg_addr" validate:"required"`
Unames string `json:"unames" form:"unames"`
Pwds string `json:"pwds" form:"pwds" validate:"required"`
Phones string `json:"phones" form:"phones"`
Emails string `json:"emails" form:"emails"`
SafeQuestion int8 `json:"safe_question" form:"safe_question" default:"99"`
SafeAnswer string `json:"safe_answer" form:"safe_answer"`
CardID string `json:"card_id" form:"card_id"`
CardType int8 `json:"card_type" form:"card_type" default:"99"`
Captcha string `json:"captcha" form:"captcha" validate:"required"`
LinkMail string `json:"link_mail" form:"link_mail" validate:"required,email"`
Mid int64 `json:"mid" form:"mid" validate:"required,min=1"`
Business string `json:"business" form:"business" default:"account"`
BusinessMap map[string]string
Files []string `json:"files" form:"files,split"`
LastSucCount int64 //成功找回的总次数
LastSucCTime time.Time //上次成功找回的提交时间
}
// DBAccountRecoveryAddit is
type DBAccountRecoveryAddit struct {
Rid int64 `json:"rid"`
Files string `json:"files"`
Extra string `json:"extra"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}
// AsRecoveryAddit parse DBAccountRecoveryAddit to RecoveryAddit
func (dbAddit *DBAccountRecoveryAddit) AsRecoveryAddit() *RecoveryAddit {
addit := &RecoveryAddit{
Files: []string{},
Extra: map[string]interface{}{},
}
if err := json.Unmarshal([]byte(dbAddit.Extra), &addit.Extra); err != nil {
log.Error("QueryRecoveryAddit: json.Unmarshal(%s) error(%v)", addit.Extra, err)
}
addit.Files = strings.Split(dbAddit.Files, ",")
for i, v := range addit.Files {
addit.Files[i] = BuildFileURL(v)
}
return addit
}
// BuildFileURL build bfs download url
func BuildFileURL(raw string) string {
if raw == "" {
return ""
}
ori, err := url.Parse(raw)
if err != nil {
return raw
}
if strings.HasPrefix(ori.Path, "/bfs/account") {
//println("filename=====",bfsFilename(ori.Path, "account"))
token := authorize(conf.Conf.Bfs.Key, conf.Conf.Bfs.Secret, "GET", "account", bfsFilename(ori.Path, "account"), gotime.Now().Unix())
p := url.Values{}
p.Set("token", token)
ori.RawQuery = p.Encode()
}
if ori.Hostname() == "" {
ori.Host = fmt.Sprintf("i%d.hdslb.com", rand.Int63n(3))
ori.Scheme = "http"
}
return ori.String()
}
// authorize returns authorization for upload file to bfs
func authorize(key, secret, method, bucket, filename string, expire int64) string {
content := fmt.Sprintf("%s\n%s\n%s\n%d\n", method, bucket, filename, expire)
mac := hmac.New(sha1.New, []byte(secret))
mac.Write([]byte(content))
signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return fmt.Sprintf("%s:%s:%d", key, signature, expire)
}
func bfsFilename(path string, bucket string) string {
return strings.TrimLeft(path, fmt.Sprintf("/bfs/%s", bucket))
}
// QueryRecoveryInfoReq 信息查询请求
type QueryRecoveryInfoReq struct {
// FirstRid int64 `json:"first_rid" form:"first_rid"`
// LastRid int64 `json:"last_rid" form:"last_rid"`
// RID account recovery info id
RID int64 `json:"rid" form:"rid"`
// UID user id
UID int64 `json:"uid" form:"uid"`
// Status account recovery status
// status value, example: default "0", thought "1", reject "2"
Status *int64 `json:"status" form:"-"`
// Game is game user
// game value, example: "1"/"0"
Game *int64 `json:"game" form:"-"`
// Size default size 10
Size int64 `json:"size" form:"size"`
StartTime time.Time `json:"start_time" form:"start_time"`
EndTime time.Time `json:"end_time" form:"end_time"`
IsAdvanced bool `json:"-"`
// CurrPage int64 `form:"curr_page"`
Page int64 `form:"page"`
Bussiness string `json:"-"`
}
// DBRecoveryInfoParams DBRecoveryInfoParams
type DBRecoveryInfoParams struct {
ExistGame bool
ExistStatus bool
ExistMid bool
Mid int64
Game int64
Status int64
FirstRid int64
LastRid int64
Size int64
StartTime time.Time
EndTime time.Time
SubNum int64
CurrPage int64
}
// AccountRecoveryInfo account recovery db info
type AccountRecoveryInfo struct {
Rid int64
Mid int64
UserType int64
Status int64
LoginAddr string
UNames string
RegTime time.Time `json:"-"`
RegTimeStr string `json:"RegTime"`
RegType int64 `json:"-"`
RegTypeStr string `json:"RegType"`
RegAddr string
Pwd string
Phones string
Emails string
SafeQuestion int64 `json:"-"`
SafeQuestionStr string `json:"SafeQuestion"`
SafeAnswer string
CardType int64 `json:"-"`
CardTypeStr string `json:"CardType"`
CardID string
SysLoginAddr string
SysReg string
SysUNames string
SysPwds string
SysPhones string
SysEmails string
SysSafe string
SysCard string
LinkEmail string
Operator string
OptTime time.Time
Remark string
CTime time.Time
MTime time.Time
Bussiness string
LastSucCount int64
LastSucCTime time.Time
}
// RecoveryResInfo RecoveryResInfo
type RecoveryResInfo struct {
AccountRecoveryInfo
RecoverySuccess
RecoveryAddit
LastSuccessData
}
// RecoverySuccess recovery success info
type RecoverySuccess struct {
SuccessMID int64 `json:"-"`
SuccessCount int64
FirstSuccessTime time.Time
LastSuccessTime time.Time
}
// LastSuccessData last recovery success info
type LastSuccessData struct {
LastApplyMID int64 `json:"-"`
LastApplyTime time.Time
}
// RecoveryAddit common business field
type RecoveryAddit struct {
Files []string
Extra map[string]interface{}
}
// MultiQueryRes MultiQueryRes
type MultiQueryRes struct {
Info []*RecoveryResInfo
Page *Page
}
// UserInfoRes response of userInfo
type UserInfoRes struct {
LoginAddrs string `json:"login_addrs"`
Unames string `json:"unames"`
Pwds string `json:"pwds"`
Phones string `json:"phones"`
Emails string `json:"emails"`
RegInfo *RegInfo `json:"reg_info"`
SafeInfo *SafeInfo `json:"safe_info"`
CardInfo *CardInfo `json:"card_info"`
}
// RegInfo reg info
type RegInfo struct {
RegTime time.Time `json:"reg_time"`
RegType int64 `json:"reg_type"`
RegAddr string `json:"reg_addr"`
}
// SafeInfo safe info
type SafeInfo struct {
SafeQuestion int8 `json:"safe_question"`
SafeAnswer string `json:"safe_answer"`
}
// CardInfo card info
type CardInfo struct {
CardID string `json:"card_id"`
CardType int8 `json:"card_type"`
}
// SysInfo sys check info
type SysInfo struct {
SysLoginAddrs string `json:"sys_login_addrs"`
SysReg string `json:"sys_reg"`
SysUNames string `json:"sys_nick"`
SysPwds string `json:"sys_pwds"`
SysPhones string `json:"sys_phones"`
SysEmails string `json:"sys_emails"`
SysSafe string `json:"sys_safe"`
SysCard string `json:"sys_card"`
}
// OperInfo operate info
type OperInfo struct {
Operator string `json:"operator"`
OperTime time.Time `json:"oper_time"`
}
// AppealRes 信息查询返回结果
type AppealRes struct {
Rid int64 `json:"rid"`
Mid int64 `json:"uid"`
Ctime time.Time `json:"ctime"`
Count int64 `json:"count"`
LinkEmail string `json:"link_email"`
Remark string `json:"remark"`
Status int64 `json:"status"`
UserInfoRes *UserInfoRes `json:"user_info"`
SysInfo *SysInfo `json:"sys_info"`
OperInfo *OperInfo `json:"oper_info"`
}
// Page page
type Page struct {
//Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
}
//JudgeReq appeal judge
type JudgeReq struct {
Status int64 `form:"status" validate:"required"`
Rid int64 `form:"rid" validate:"required"`
Operator string `form:"operator" validate:"required"`
OptTime time.Time `form:"opt_time" validate:"required"`
Remark string `form:"remark"`
}
//BatchJudgeReq appeal judge
type BatchJudgeReq struct {
Status int64 `form:"status" validate:"required"`
Rids string `form:"rids" validate:"required"`
Operator string `form:"operator" validate:"required"`
OptTime time.Time `form:"opt_time" validate:"required"`
Remark string `form:"remark"`
RidsAry []int64
}
// CommonResq common response
type CommonResq struct {
Code int64 `json:"code"`
TS int64 `json:"ts"`
Message string `json:"message"`
}
// Token captcha token
type Token struct {
Token string `json:"token"`
URL string `json:"url"`
}
// TokenResq response of capthca
type TokenResq struct {
CommonResq
Data *Token `json:"data"`
}
// Game data of game
type Game struct {
GameBaseID int `json:"id"`
GameName string `json:"name"`
LastLogin string `json:"lastLogin"`
}
// GameListRes game list res
type GameListRes struct {
Mid int64 `json:"mid"`
Items []*Game `json:"items"`
}
// UserBindLogRes UserBindLogRes
type UserBindLogRes struct {
Page Page `json:"page"`
Result []*UserBindLog `json:"result"`
}
// UserBindLog UserBindLog
type UserBindLog struct {
Mid int64 `json:"mid"`
Phone string `json:"phone"`
Email string `json:"email"`
Time string `json:"time"`
}
// BatchAppeal some of appeal info
type BatchAppeal struct {
Rid string
Mid string
LinkMail string
Ctime time.Time
}
// NickNameReq request es params
type NickNameReq struct {
Mid int64 `form:"mid"`
Page int `form:"page"`
Size int `form:"size"`
From int64 `form:"from"`
To int64 `form:"to"`
}
// NickNameLogEs query field form es
type NickNameLogEs struct {
OldName string `json:"str_0"`
NewName string `json:"str_1"`
}
// NickESRes the result of es query
type NickESRes struct {
Page Page `json:"page"`
Result []*NickNameLogEs `json:"result"`
}
// NickNameLogRes make NickESRes result become we need
type NickNameLogRes struct {
Page Page `json:"page"`
Result []*NickNameInfo `json:"result"`
}
// NickNameInfo nickName
type NickNameInfo struct {
OldName string `json:"old_name"`
NewName string `json:"new_name"`
}
// CheckEmailPhone check email and phone
type CheckEmailPhone struct {
PhonesCheck string
EmailCheck string
}
//CaptchaMailReq get mail captcha
type CaptchaMailReq struct {
Mid int64 `form:"mid" validate:"required"`
LinkMail string `form:"link_mail" validate:"required"`
}
// AddrInfo AddrInfo
type AddrInfo struct {
OftenAddrs string `json:"oftenAddrs"`
}
// SendMailReq send mail according to status and rid
type SendMailReq struct {
RID int64 `form:"rid" validate:"required"`
Status int64 `form:"status" validate:"required"`
}
// LoginIPInfo login ip info
type LoginIPInfo struct {
LoginIP string `json:"loginip"`
}
// UserInfo user detail info
type UserInfo struct {
Mid int64 `json:"mid"`
Phone string `json:"telphone"`
Email string `json:"email"`
JoinTime time.Time `json:"join_time"`
}
// UserBindLogReq UserBindLogReq
type UserBindLogReq struct {
// Action value : telBindLog or emailBindLog
Action string `form:"action"`
Mid int64 `form:"mid"`
//Query search tel or email
Query string `form:"query"`
Page int `form:"page"`
Size int `form:"size"`
From int64 `form:"from"`
To int64 `form:"to"`
}
// EsRes EsRes
type EsRes struct {
Page Page `json:"page"`
Result []*UserActLogEs `json:"result"`
}
// UserActLogEs UserActLogEs
type UserActLogEs struct {
Mid int64 `json:"mid"`
Str0 string `json:"str_0"`
ExtraData string `json:"extra_data"`
CTime string `json:"ctime"`
}
const (
// VerifyMail verify mail
VerifyMail = 1 //验证码邮件
// CommitMail commit mail
CommitMail = 2 //申诉信息提交邮件
// RejectMail reject mail
RejectMail = 3 //账号申诉驳回邮件
// AgreeMail agree mail
AgreeMail = 4 //账号审核通过邮件
// DOAgree agree this appeal
DOAgree = 1 //申诉通过
// DOReject reject this appeal
DOReject = 2 //申诉驳回
// HIDEALL hide all
HIDEALL = "*********"
// BizGame game
BizGame = "game"
)
// BusinessExtraArgs business args
func BusinessExtraArgs(business string) []string {
switch business {
case BizGame:
return []string{"GamePlay", "GameArea", "GameRoleCTime", "GameRoleCreatePhoneType", "GameUsedPhoneType", "GameNames"}
}
return []string{}
}

View File

@@ -0,0 +1,66 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"appeal.go",
"data_check.go",
"send_email.go",
"service.go",
],
importpath = "go-common/app/service/main/account-recovery/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/dao:go_default_library",
"//app/service/main/account-recovery/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/microcosm-cc/bluemonday:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"appeal_test.go",
"data_check_test.go",
"send_email_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/account-recovery/conf:go_default_library",
"//app/service/main/account-recovery/model:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,746 @@
package service
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"strconv"
"strings"
"time"
"go-common/app/service/main/account-recovery/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
xtime "go-common/library/time"
)
// QueryAccount is verify account is exist
func (s *Service) QueryAccount(c context.Context, req *model.QueryInfoReq) (res *model.QueryInfoResp, err error) {
if req.CToken == "" || req.Code == "" {
err = ecode.RequestErr
}
if req.QType == "" {
err = ecode.RequestErr
return
}
if _, err = s.d.Verify(c, req.Code, req.CToken); err != nil {
log.Error("Verify code error(%v)", err)
err = ecode.CaptchaErr
return
}
res = new(model.QueryInfoResp)
var v *model.MIDInfo
if v, err = s.d.GetMidInfo(c, req.QType, req.QValue); err != nil {
return
}
log.Info("QueryAccount:GetMidInfo req: %+v, res: %+v", req, v)
if v == nil || v.Count == 0 {
// 用户不存在
res.Status = 2
return
}
if v != nil {
// 用户存在多个
if v.Count > 1 {
res.Status = 3
return
}
if v.Count == 1 {
res.Status = 1
}
}
if res.UID, err = strconv.ParseInt(v.Mids, 10, 64); err != nil {
log.Error("QueryAccount strconv ParseInt err(%v)", err)
return
}
var count int64
//查询该账号是否在申诉中
if count, err = s.d.GetNoDeal(c, res.UID); err != nil {
return
} else if count != 0 {
res.Status = 4
} else if count == 0 {
res.Status = 1
}
return
}
// CommitInfo is commit appeal info
func (s *Service) CommitInfo(c context.Context, uinfo *model.UserInfoReq) (err error) {
if uinfo.Captcha == "" {
err = ecode.CaptchaErr
return
}
var code string
retry := 0
for {
if retry > 3 {
break
}
if code, err = s.d.GetEMailCode(c, uinfo.Mid, uinfo.LinkMail); err != nil {
retry = retry + 1
continue
}
break
}
if err != nil || code == "" || code != uinfo.Captcha {
err = ecode.CaptchaErr
return
}
//验证通过,删除验证码
s.d.DelEMailCode(c, uinfo.Mid, uinfo.LinkMail)
// params check
regType := uinfo.RegType
if uinfo.RegTime == 0 {
err = ecode.RequestErr
if err != nil {
return
}
}
if uinfo.LoginAddrs == "" {
err = ecode.RequestErr
if err != nil {
return
}
}
if !(regType == 1 || regType == 2 || regType == 3) {
err = ecode.RequestErr
if err != nil {
return
}
}
if uinfo.RegAddr == "" {
err = ecode.RequestErr
if err != nil {
return
}
}
//字符传切割成数组后的长度校验
if err = checkCountStr(uinfo.Unames, 3); err != nil {
return
}
if err = checkCountStr(uinfo.LoginAddrs, 3); err != nil {
return
}
if err = checkCountStr(uinfo.Emails, 3); err != nil {
return
}
if uinfo.Phones != "" {
phoneAddr := strings.Split(uinfo.Phones, ";")
if err = checkCountStr(string(phoneAddr[0]), 3); err != nil {
return
}
if len(phoneAddr) > 1 {
if err = checkCountStr(string(phoneAddr[1]), 3); err != nil {
return
}
}
}
if uinfo.Emails != "" {
split := strings.Split(uinfo.Emails, ",")
for _, email := range split {
if !strings.Contains(email, "@") {
err = ecode.RequestErr
return
}
}
}
if !strings.Contains(uinfo.LinkMail, "@") {
err = ecode.RequestErr
return
}
if checkCountStr(uinfo.Pwds, 3) != nil {
err = ecode.RequestErr
if err != nil {
return
}
}
//要传递,则两个都必须有值 默认不传设置为99
if uinfo.CardID != "" {
if uinfo.CardType == 99 {
err = ecode.RequestErr
if err != nil {
return
}
}
} else {
if uinfo.CardType != 99 {
err = ecode.RequestErr
if err != nil {
return
}
}
}
if uinfo.SafeAnswer != "" {
if uinfo.SafeQuestion == 99 {
err = ecode.RequestErr
if err != nil {
return
}
}
} else {
if uinfo.SafeQuestion != 99 {
err = ecode.RequestErr
if err != nil {
return
}
}
}
log.Info("CommitInfo uinfo: %+v", uinfo)
//检查mid是否存在
_, err = s.d.Info3(c, uinfo.Mid)
if err != nil {
log.Error("s.d.Info3 err(%v)", err)
return
}
// 查询mid对应的上次成功找回的案件的信息成功次数提交时间
sucCount, lastSucCtime, err := s.getlastSuc(c, uinfo.Mid)
if err != nil {
return
}
uinfo.LastSucCount = sucCount
uinfo.LastSucCTime = lastSucCtime
rid, err := s.d.InsertRecoveryInfo(c, uinfo)
if err != nil {
log.Error("InsertRecoveryInfo err(%v)", err)
return
}
if err = s.InsertRecoveryAddit(c, rid, uinfo.Files, uinfo.BusinessMap); err != nil {
return
}
//整个提交通过后,验证通过,删除验证码
s.d.DelEMailCode(c, uinfo.Mid, uinfo.LinkMail)
return
}
func (s *Service) getlastSuc(c context.Context, mid int64) (int64, xtime.Time, error) {
// 查询成功找回次数
sucCount, err := s.d.GetSuccessCount(c, mid)
if err != nil {
log.Error("GetSuccessCount err(%+v)", err)
return 0, 0, err
}
if sucCount == 0 {
return 0, 0, nil
}
// 查询上次成功找回的提交时间无记录则默认0即可
lastSuc, err := s.d.GetLastSuccess(c, mid)
if err != nil {
log.Error("GetLastSuccess err(%+v)", err)
return 0, 0, err
}
return sucCount, lastSuc.LastApplyTime, nil
}
// QueryCon is Multi conditional combinatorial query
func (s *Service) QueryCon(c context.Context, aq *model.QueryRecoveryInfoReq) (res *model.MultiQueryRes, err error) {
var (
infos []*model.AccountRecoveryInfo
total int64
)
res = new(model.MultiQueryRes)
infos, total, err = s.d.GetAllByCon(c, aq)
rids := make([]int64, 0, len(infos))
mids := make([]int64, 0, len(infos))
m := make(map[int64]*model.AccountRecoveryInfo)
successMap := make(map[int64]*model.RecoverySuccess, len(infos))
lastSuccessMap := make(map[int64]*model.LastSuccessData, len(infos))
for _, i := range infos {
m[i.Rid] = i
rids = append(rids, i.Rid)
mids = append(mids, i.Mid)
}
if len(infos) > 0 {
if successMap, err = s.d.BatchGetRecoverySuccess(c, mids); err != nil {
return
}
if lastSuccessMap, err = s.d.BatchGetLastSuccess(c, mids); err != nil {
return
}
}
if len(rids) == 0 {
return
}
bizData, err := s.QueryRecoveryAddit(c, rids)
if err != nil {
log.Error("QueryRecoveryAddit err(%+v)", err)
return
}
res.Info = make([]*model.RecoveryResInfo, 0, len(infos))
for _, rid := range rids {
addit, ok := bizData[rid]
if !ok {
addit = &model.RecoveryAddit{
Files: []string{},
Extra: map[string]interface{}{},
}
}
res.Info = append(res.Info, &model.RecoveryResInfo{
AccountRecoveryInfo: *m[rid],
RecoveryAddit: *addit,
})
}
hideQueryResInfo(res.Info, aq.IsAdvanced)
for _, info := range res.Info {
suc, ok := successMap[info.Mid]
if !ok {
suc = new(model.RecoverySuccess)
}
info.RecoverySuccess = *suc
lastSuc, ok := lastSuccessMap[info.Mid]
if !ok {
lastSuc = new(model.LastSuccessData)
}
info.LastSuccessData = *lastSuc
}
res.Page = &model.Page{
Size: aq.Size,
Total: total,
}
return
}
// QueryRecoveryAddit query recovery addit
func (s *Service) QueryRecoveryAddit(c context.Context, rids []int64) (map[int64]*model.RecoveryAddit, error) {
log.Info("QueryRecoveryAddit rids=%v", rids)
addits, err := s.d.BatchGetRecoveryAddit(c, rids)
if err != nil {
return nil, err
}
recoveryAddits := make(map[int64]*model.RecoveryAddit, len(rids))
for _, addit := range addits {
recoveryAddit := addit.AsRecoveryAddit()
recoveryAddits[addit.Rid] = recoveryAddit
}
return recoveryAddits, nil
}
// Judge is reject or agree one operation
func (s *Service) Judge(c context.Context, req *model.JudgeReq) (err error) {
if req.Status == 1 || req.Status == 2 { // 1通过 2驳回
if err = s.d.UpdateStatus(c, req.Status, req.Rid, req.Operator, req.OptTime, req.Remark); err != nil {
log.Error("UpdateStatus status=%d (1 agree,2 reject) error(%v)", req.Status, err)
return
}
}
return
}
// BatchJudge is reject or agree more operation.
func (s *Service) BatchJudge(c context.Context, req *model.BatchJudgeReq) (err error) {
if req.Status == 1 || req.Status == 2 { // 1通过 2驳回
if err = s.batchJudge(c, req.Status, req.RidsAry, req.Operator, req.OptTime, req.Remark); err != nil {
log.Error("batchJudge update status fail")
return
}
}
return
}
// GetCaptchaMail send captcha mail.
func (s *Service) GetCaptchaMail(c context.Context, req *model.CaptchaMailReq) (state int64, err error) {
//邮件次数是否到达最大值
state, _ = s.d.SetLinkMailCount(c, req.LinkMail)
if state == 10 {
return
}
go func() {
vcode := randCode()
if err1 := s.SendMailM(c, model.VerifyMail, req.LinkMail, vcode); err1 == nil {
s.d.SetCaptcha(c, vcode, req.Mid, req.LinkMail)
s.SendMailLog(c, req.Mid, model.VerifyMail, req.LinkMail, vcode)
} else {
log.Error("SendMailM VerifyMail fail")
}
}()
state = 1
return
}
// SendMail reject more.
func (s *Service) SendMail(c context.Context, req *model.SendMailReq) (err error) {
log.Info("SendMail rid=%d,status=%d", req.RID, req.Status)
mailStatus, err := s.d.GetMailStatus(c, req.RID)
if err != nil {
log.Error("GetMailStatus no record rid=%d,status=%d", req.RID, req.Status)
return
}
if mailStatus == 0 {
log.Info("SendMail rid=%d,status=%d", req.RID, req.Status)
switch req.Status {
case model.DOAgree:
s.AddMailch(func() {
s.agree(context.Background(), req.RID, "账号找回服务")
})
case model.DOReject:
s.AddMailch(func() {
s.reject(context.Background(), req.RID)
})
}
err = s.d.UpdateMailStatus(c, req.RID)
if err != nil {
log.Error("UpdateMailStatus no record rid=%d,status=%d,err: %+v", req.RID, req.Status, err)
return
}
}
return
}
func (s *Service) batchJudge(c context.Context, status int64, rids []int64, operator string, optTime xtime.Time, remark string) (err error) {
var tx *sql.Tx
//开启事务
if tx, err = s.d.BeginTran(c); err != nil {
return
}
for _, rid := range rids {
if err = s.d.UpdateStatus(c, status, rid, operator, optTime, remark); err != nil {
tx.Rollback()
return
}
}
//提交
err = tx.Commit()
return
}
// deal deal user appeal
func (s *Service) deal(c context.Context, rid int64) (err error) {
mid, linkMail, ctime, err := s.d.GetUinfoByRid(c, rid)
if mid <= 0 || err != nil {
log.Error("deal mid error,no record in account_recovery_info; rid=%d, err(%v)", rid, err)
return
}
uid := strconv.FormatInt(mid, 10)
rid1 := strconv.FormatInt(rid, 10)
err = s.SendMailM(c, model.CommitMail, linkMail, hideUID(uid), ctime, rid1)
if err != nil {
log.Error("deal SendMailM rid=%d, err(%v)", rid, err)
return
}
s.SendMailLog(c, mid, model.CommitMail, linkMail, hideUID(uid), ctime, rid1)
return
}
// agree use appeal is agree
func (s *Service) agree(c context.Context, rid int64, operator string) (err error) {
mid, linkMail, ctime, err := s.d.GetUinfoByRid(c, rid)
if mid <= 0 || err != nil {
log.Error("agree mid error,no record in account_recovery_info; rid=%d, err(%v)", rid, err)
return
}
if err = s.d.UpdateSuccessCount(c, mid); err != nil {
log.Error("UpdateSuccessCount error(%v)", err)
return
}
user, err := s.d.UpdatePwd(c, mid, operator)
if err != nil {
return
}
uid := strconv.FormatInt(mid, 10)
rid1 := strconv.FormatInt(rid, 10)
userID := user.UserID
pwd := user.Pwd
err = s.SendMailM(c, model.AgreeMail, linkMail, hideUID(uid), ctime, rid1, userID, pwd)
if err != nil {
log.Error("agree SendMailM rid=%d, err(%v)", rid, err)
return
}
s.SendMailLog(c, mid, model.AgreeMail, linkMail, hideUID(uid), ctime, rid1, userID, pwd)
return
}
// reject use appeal is reject
func (s *Service) reject(c context.Context, rid int64) (err error) {
mid, linkMail, ctime, err := s.d.GetUinfoByRid(c, rid)
if mid <= 0 || err != nil {
log.Error("reject mid error,no record in account_recovery_info; rid=%d, err(%v)", rid, err)
return
}
uid := strconv.FormatInt(mid, 10)
rid1 := strconv.FormatInt(rid, 10)
err = s.SendMailM(c, model.RejectMail, linkMail, hideUID(uid), ctime, rid1)
if err != nil {
log.Error("reject SendMailM rid=%d, err(%v)", rid, err)
return
}
s.SendMailLog(c, mid, model.RejectMail, linkMail, hideUID(uid), ctime, rid1)
return
}
// agreeMore agree more. todo 暂时未使用
func (s *Service) agreeMore(c context.Context, ridsStr string, operator string) (err error) {
batchRes, err := s.d.GetUinfoByRidMore(c, ridsStr)
if err != nil {
log.Error("GetUinfoByRidMore err(%v)", err)
}
var mids string
for _, res := range batchRes {
mids += "," + res.Mid
}
if err = s.d.BatchUpdateSuccessCount(c, mids[1:]); err != nil {
log.Error("BatchUpdateSuccessCount error(%v)", err)
return
}
res, err := s.d.UpdateBatchPwd(c, mids[1:], operator)
if err != nil {
return
}
s.SendMailMany(c, model.AgreeMail, batchRes, res)
return
}
// rejectMore reject more. todo 暂时未使用
func (s *Service) rejectMore(c context.Context, ridsStr string) (err error) {
batchRes, err := s.d.GetUinfoByRidMore(c, ridsStr)
if err != nil {
log.Error("s.d.GetUinfoByRidMore err(%v)", err)
}
s.SendMailMany(c, model.RejectMail, batchRes, nil)
return
}
// WebToken get captcha token
func (s *Service) WebToken(c context.Context) (token *model.Token, err error) {
var (
tokenReq *model.TokenResq
)
if tokenReq, err = s.d.GetToken(c, s.c.CaptchaConf.TokenBID); err != nil {
log.Error("GetToken error(%v)", err)
return
}
token = tokenReq.Data
return
}
// Verify verify captcha
func (s *Service) Verify(c context.Context, token, code string) (err error) {
if _, err = s.d.Verify(c, code, token); err != nil {
log.Error("Verify error(%v)", err)
return
}
return
}
// hideUID
func hideUID(mid string) (uid string) {
if len(mid) >= 3 {
uid = mid[:1] + "****" + mid[len(mid)-1:]
} else {
uid = mid[:1] + "****"
}
return
}
// randCode
func randCode() (vcode string) {
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
vcode = fmt.Sprintf("%06v", rnd.Int31n(1000000))
return
}
// checkCountStr 校验常用登陆地址,密码,手机,邮箱,昵称的个数
func checkCountStr(str string, count int) (err error) {
if str != "" {
split := strings.Split(str, ",")
if len(split) > count {
err = ecode.RequestErr
}
}
return
}
// hideQueryResInfo hide response info
func hideQueryResInfo(res []*model.RecoveryResInfo, IsAdvanced bool) {
for _, resInfo := range res {
//其他1 手机注册2 邮箱注册3
if resInfo.RegType == 1 {
resInfo.RegTypeStr = "其他注册"
} else if resInfo.RegType == 2 {
resInfo.RegTypeStr = "手机注册"
} else if resInfo.RegType == 3 {
resInfo.RegTypeStr = "邮箱注册"
}
cardMap := make(map[int64]string)
cardMap[99] = "没有绑定证件类型"
cardMap[0] = "身份证"
cardMap[1] = "护照(境外签发)"
cardMap[2] = "港澳居民来往内地通行证"
cardMap[3] = "台湾居民来往大陆通行证"
cardMap[4] = "护照(中国签发)"
cardMap[5] = "外国人永久居留证"
cardMap[6] = "其他国家或地区身份证"
safeMap := make(map[int64]string)
safeMap[99] = "没安全提示问题"
safeMap[0] = "没安全提示问题"
safeMap[1] = "你最喜欢的格言什么?"
safeMap[2] = "你家乡的名称是什么?"
safeMap[3] = "你读的小学叫什么?"
safeMap[4] = "你的父亲叫什么名字?"
safeMap[5] = "你的母亲叫什么名字?"
safeMap[6] = "你最喜欢的偶像是谁?"
safeMap[7] = "你最喜欢的歌曲是什么?"
resInfo.CardTypeStr = cardMap[resInfo.CardType]
if IsAdvanced {
resInfo.SafeQuestionStr = safeMap[resInfo.SafeQuestion]
resInfo.RegTimeStr = resInfo.RegTime.Time().Format("2006年01月")
} else {
resInfo.SafeAnswer = model.HIDEALL
resInfo.SafeQuestionStr = model.HIDEALL
resInfo.CardID = hideCardID(resInfo.CardID)
resInfo.Emails = hideEmails(resInfo.Emails)
resInfo.RegTypeStr = "**注册"
resInfo.RegAddr = "**_**_**"
resInfo.RegTimeStr = "**年**月"
}
//电话地址单独处理
resInfo.Phones = hidePhones(resInfo.Phones, IsAdvanced)
//数据缺失(没有设置密保,没有绑定实名认证)则不打码 99表示默认不传
if resInfo.SysSafe == "null" || resInfo.SafeQuestion == 99 {
resInfo.SafeAnswer = "没安全提示问题"
resInfo.SafeQuestionStr = "无"
}
if resInfo.CardType == 99 {
resInfo.CardTypeStr = "没有绑定证件类型"
resInfo.CardID = "无"
}
if resInfo.Phones == "" {
resInfo.Phones = "无"
}
if resInfo.Emails == "" {
resInfo.Emails = "无"
}
if resInfo.UNames == "" {
resInfo.UNames = "无"
}
//密码所有人均不可见
resInfo.Pwd = hidePwds(resInfo.Pwd)
}
}
// hidePhones
func hidePhones(phones string, IsAdvanced bool) (phoneStr string) {
if phones != "" {
phoneAddr := strings.Split(phones, ";")
phoneInfo := strings.Split(string(phoneAddr[0]), ",")
var addrInfo []string
if len(phoneAddr) > 1 {
addrInfo = strings.Split(string(phoneAddr[1]), ",")
}
for i, phone := range phoneInfo {
if !IsAdvanced {
phoneStr += "," + phone[:3] + "*****" + phone[len(phone)-3:]
} else {
phoneStr += "," + phone
}
if i < len(addrInfo) {
phoneStr += "(" + addrInfo[i] + ")"
}
}
phoneStr = strings.TrimLeft(phoneStr, ",")
return
}
return phones
}
// hideEmails
func hideEmails(emails string) (emailStr string) {
if emails != "" {
split := strings.Split(emails, ",")
for _, email := range split {
index := strings.Index(email, "@")
emailStr += "," + email[:3] + "*****" + email[index:]
}
emailStr = strings.TrimLeft(emailStr, ",")
return
}
return emails
}
// hidePwds
func hidePwds(pwds string) (pwdStr string) {
if pwds != "" {
split := strings.Split(pwds, ",")
m := len(split)
for i := 0; i < m; i++ {
pwdStr += ",*******"
}
pwdStr = strings.TrimLeft(pwdStr, ",")
return
}
return "********"
}
// hideCardID
func hideCardID(cardID string) (cardIDStr string) {
if cardID != "" && len(cardID) > 3 {
cardIDStr = cardID[:2] + "********" + cardID[len(cardID)-2:]
return
}
return cardID
}
// GameList GameList.
func (s *Service) GameList(c context.Context, mids string) (res []*model.GameListRes, err error) {
res = make([]*model.GameListRes, 0)
midArr := strings.Split(mids, ",")
eg, _ := errgroup.WithContext(c)
for _, midStr := range midArr {
var mid int64
mid, err = strconv.ParseInt(midStr, 10, 64)
if err != nil {
err = ecode.RequestErr
return
}
eg.Go(func() (e error) {
var items []*model.Game
items, err = s.d.GetUserType(c, mid)
if items == nil {
items = make([]*model.Game, 0)
}
gameRes := &model.GameListRes{
Mid: mid,
Items: items,
}
res = append(res, gameRes)
return
})
}
if err = eg.Wait(); err != nil {
return
}
return
}
// InsertRecoveryAddit insert addit for commit info
func (s *Service) InsertRecoveryAddit(c context.Context, rid int64, Files []string, BusinessMap map[string]string) (err error) {
log.Info("InsertRecoveryAddit rid=%v, BusinessMap=%v", rid, BusinessMap)
bizFiles := strings.Join(Files, ",")
bizExtra := BusinessMap
extra, err := json.Marshal(bizExtra)
if err != nil {
log.Error("json.Unmarshal(%s) error(%+v)", string(extra), err)
return
}
if err = s.d.InsertRecoveryAddit(c, rid, bizFiles, string(extra)); err != nil {
log.Error("InsertRecoveryAddit rid=%v, extra=%v, error(%+v)", rid, string(extra), err)
return
}
return nil
}

View File

@@ -0,0 +1,289 @@
package service
import (
"context"
"testing"
"time"
"go-common/app/service/main/account-recovery/model"
xtime "go-common/library/time"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceQueryAccount(t *testing.T) {
var (
c = context.Background()
req = &model.QueryInfoReq{
QType: "uid",
QValue: "1",
CToken: "f0bd2dd65c444697b7b1330bb757bf3d",
Code: "gp74",
}
)
convey.Convey("QueryAccount", t, func(ctx convey.C) {
res, err := s.QueryAccount(c, req)
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 TestServiceCommitInfo(t *testing.T) {
var (
c = context.Background()
uinfo = &model.UserInfoReq{
LoginAddrs: "地址1,地址2",
RegTime: xtime.Time(time.Now().Unix()),
RegType: 1,
RegAddr: "注册地址",
Unames: "昵称1",
Pwds: "密码1",
Phones: "手机1",
Emails: "邮件1",
SafeQuestion: 0,
SafeAnswer: "",
CardID: "ISN-367890",
CardType: 1,
Captcha: "6789",
LinkMail: "2459593393@qq.com",
Mid: 8,
}
)
convey.Convey("CommitInfo", t, func(ctx convey.C) {
err := s.CommitInfo(c, uinfo)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceQueryCon(t *testing.T) {
var (
defint int64
c = context.Background()
aq = &model.QueryRecoveryInfoReq{
RID: 1,
UID: 0,
Status: &defint,
Game: &defint,
Size: 2,
StartTime: 1533206284,
EndTime: 1539206284,
IsAdvanced: false,
Page: 1,
}
)
convey.Convey("QueryCon", t, func(ctx convey.C) {
res, err := s.QueryCon(c, aq)
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 TestServiceJudge(t *testing.T) {
var (
c = context.Background()
req = &model.JudgeReq{
Rid: 1,
Status: 1,
Operator: "hyy",
OptTime: xtime.Time(time.Now().Unix()),
Remark: "申诉通过",
}
)
convey.Convey("Judge", t, func(ctx convey.C) {
err := s.Judge(c, req)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceBatchJudge(t *testing.T) {
var (
c = context.Background()
ridsAry = []int64{1, 2}
req = &model.BatchJudgeReq{
Rids: "1,2",
Status: 1,
Operator: "hyy",
OptTime: xtime.Time(time.Now().Unix()),
Remark: "批量申诉驳回",
RidsAry: ridsAry,
}
)
convey.Convey("BatchJudge", t, func(ctx convey.C) {
err := s.BatchJudge(c, req)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceGetCaptchaMail(t *testing.T) {
var (
c = context.Background()
req = &model.CaptchaMailReq{Mid: 1, LinkMail: "2459593393@qq.com"}
)
convey.Convey("GetCaptchaMail", t, func(ctx convey.C) {
state, err := s.GetCaptchaMail(c, req)
ctx.Convey("Then err should be nil.state should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(state, convey.ShouldNotBeNil)
})
})
}
func TestServiceSendMail(t *testing.T) {
var (
c = context.Background()
req = &model.SendMailReq{RID: 1, Status: 1}
)
convey.Convey("SendMail", t, func(ctx convey.C) {
err := s.SendMail(c, req)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicebatchJudge(t *testing.T) {
var (
c = context.Background()
status = int64(1)
rids = []int64{1, 2}
operator = "abcd"
optTime xtime.Time
remark = "通过"
)
convey.Convey("batchJudge", t, func(ctx convey.C) {
err := s.batchJudge(c, status, rids, operator, optTime, remark)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicedeal(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("deal", t, func(ctx convey.C) {
err := s.deal(c, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceagree(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("agree", t, func(ctx convey.C) {
err := s.agree(c, rid, "账号找回服务")
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicereject(t *testing.T) {
var (
c = context.Background()
rid = int64(1)
)
convey.Convey("reject", t, func(ctx convey.C) {
err := s.reject(c, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceagreeMore(t *testing.T) {
var (
c = context.Background()
ridsStr = "1,2"
)
convey.Convey("agreeMore", t, func(ctx convey.C) {
err := s.agreeMore(c, ridsStr, "账号找回服务")
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServicerejectMore(t *testing.T) {
var (
c = context.Background()
ridsStr = "1,2"
)
convey.Convey("rejectMore", t, func(ctx convey.C) {
err := s.rejectMore(c, ridsStr)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceWebToken(t *testing.T) {
var (
c = context.Background()
)
convey.Convey("WebToken", t, func(ctx convey.C) {
token, err := s.WebToken(c)
ctx.Convey("Then err should be nil.token should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(token, convey.ShouldNotBeNil)
})
})
}
func TestServicehideUID(t *testing.T) {
var (
mid = "1"
)
convey.Convey("hideUID", t, func(ctx convey.C) {
uid := hideUID(mid)
ctx.Convey("Then uid should not be nil.", func(ctx convey.C) {
ctx.So(uid, convey.ShouldNotBeNil)
})
})
}
func TestServiceGameList(t *testing.T) {
var (
c = context.Background()
mids = "1,2"
)
convey.Convey("GameList", t, func(ctx convey.C) {
res, err := s.GameList(c, mids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestServiceQueryRecoveryAddit(t *testing.T) {
convey.Convey("QueryRecoveryAddit", t, func(ctx convey.C) {
var (
c = context.Background()
rids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
bizData, err := s.QueryRecoveryAddit(c, rids)
ctx.Convey("Then err should be nil.bizData should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(bizData, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,315 @@
package service
import (
"context"
"strings"
"go-common/app/service/main/account-recovery/model"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// CompareInfo compare user_info with sys_info
func (s *Service) CompareInfo(c context.Context, rid int64) (err error) {
var (
games []*model.Game
sysiInfo = &model.SysInfo{}
emailphone = &model.CheckEmailPhone{}
sysRegCheck string
sysUnamesCheck string
sysSafeCheck string
sysCardCheck string
sysPwdsCheck string
sysLoginAddrCheck string
userType int64
)
uinfo, err := s.d.GetUnCheckInfo(c, rid)
if err != nil || uinfo == nil {
return
}
if err == nil && uinfo.Mid == 0 {
return
}
eg, _ := errgroup.WithContext(c)
eg.Go(func() (e error) {
games, e = s.d.GetUserType(c, uinfo.Mid)
return
})
eg.Go(func() (e error) {
//前端只能以"台湾地区"展示给用户,所以在这里做一下转换
if uinfo.RegAddr == "台湾地区" {
uinfo.RegAddr = "台湾"
}
sysRegCheck, e = s.checkRegInfo(c, uinfo)
return
})
eg.Go(func() (e error) {
sysUnamesCheck, e = s.checkUnames(c, uinfo)
return
})
eg.Go(func() (e error) {
phoneAddr := strings.Split(uinfo.Phones, ";")
//手机去掉第一个0进行对比
phones := strings.Split(string(phoneAddr[0]), ",")
phoneDeal := ""
for _, p := range phones {
if strings.Index(p, "0") == 0 && len(p) > 1 {
p = p[1:]
}
phoneDeal += "," + p
}
uinfo.Phones = strings.TrimLeft(phoneDeal, ",")
emailphone, e = s.checkPhonesAndEmails(c, uinfo)
return
})
eg.Go(func() (e error) {
sysSafeCheck, e = s.checkSafe(c, uinfo)
return
})
eg.Go(func() (e error) {
sysCardCheck, e = s.checkCard(c, uinfo)
return
})
eg.Go(func() (e error) {
sysPwdsCheck, e = s.checkPwds(c, uinfo)
return
})
eg.Go(func() (e error) {
sysLoginAddrCheck, e = s.getLoginAddrs(c, uinfo) //调用java得到ip再去通过rpc调用得到地址
return
})
if err = eg.Wait(); err != nil {
log.Error("CompareInfo err(%v)", err)
return
}
if len(games) == 0 {
userType = 0
} else {
userType = 1
}
sysiInfo.SysReg = sysRegCheck
sysiInfo.SysUNames = sysUnamesCheck
sysiInfo.SysPhones = emailphone.PhonesCheck
sysiInfo.SysEmails = emailphone.EmailCheck
sysiInfo.SysSafe = sysSafeCheck
sysiInfo.SysCard = sysCardCheck
sysiInfo.SysPwds = sysPwdsCheck
sysiInfo.SysLoginAddrs = sysLoginAddrCheck
err = s.d.UpdateSysInfo(c, sysiInfo, userType, rid)
if err != nil {
return
}
s.AddMailch(func() {
s.deal(context.Background(), rid)
})
return
}
// checkRegInfo 注册信息校验
func (s *Service) checkRegInfo(c context.Context, uinfo *model.UserInfoReq) (sysRegCheck string, err error) {
reg, err := s.d.CheckReg(c, uinfo.Mid, uinfo.RegTime.Time().Unix(), uinfo.RegType, uinfo.RegAddr)
if err != nil {
log.Error("checkRegInfo error(%v)", err)
return
}
log.Info("checkRegInfo req uinfo:%+v, reg: %+v", uinfo, reg)
sysRegCheck = reg.CheckInfo
return
}
// checkUnames 昵称列表信息校验
func (s *Service) checkUnames(c context.Context, uinfo *model.UserInfoReq) (sysUnamesCheck string, err error) {
var req = &model.NickNameReq{Mid: uinfo.Mid, Size: 100}
var res *model.NickNameLogRes
res, err = s.d.NickNameLog(c, req)
if err != nil {
return
}
info, err1 := s.d.Info3(c, uinfo.Mid)
if err1 != nil {
return "", err1
}
names := make(map[string]bool)
if info != nil {
names[info.Name] = true
}
if len(res.Result) != 0 {
for _, r := range res.Result {
names[r.OldName] = true
names[r.NewName] = true
}
}
if uinfo.Unames == "" {
sysUnamesCheck = "0"
if len(names) == 0 {
sysUnamesCheck = "1"
}
return
}
unames := strings.Split(uinfo.Unames, ",")
unameCheck := make([]string, len(unames))
for i, uname := range unames {
unameCheck[i] = "0"
if names[uname] {
unameCheck[i] = "1"
}
}
log.Info("checkUnames req uinfo:%+v, NickNameLog: %+v, Info: %+v, sysUnamesCheck:%+v", uinfo, res, info, sysUnamesCheck)
sysUnamesCheck = strings.Join(unameCheck, ",")
return
}
// checkPhonesAndEmails 手机and邮箱列表信息校验
func (s *Service) checkPhonesAndEmails(c context.Context, uinfo *model.UserInfoReq) (result *model.CheckEmailPhone, err error) {
var resPhones = &model.UserBindLogRes{}
var resEmails = &model.UserBindLogRes{}
phoneReq := &model.UserBindLogReq{
Action: "telBindLog",
Mid: uinfo.Mid,
Size: 100,
}
if resPhones, err = s.d.UserBindLog(c, phoneReq); err != nil {
return
}
emailReq := &model.UserBindLogReq{
Action: "emailBindLog",
Mid: uinfo.Mid,
Size: 100,
}
if resEmails, err = s.d.UserBindLog(c, emailReq); err != nil {
return
}
var userInfo *model.UserInfo
if userInfo, err = s.d.GetUserInfo(c, uinfo.Mid); err != nil {
return
}
tels := make(map[string]bool)
emails := make(map[string]bool)
if userInfo.Phone != "" {
tels[userInfo.Phone] = true
}
if userInfo.Email != "" {
emails[userInfo.Email] = true
}
for _, item := range resPhones.Result {
tels[item.Phone] = true
}
for _, item := range resEmails.Result {
emails[item.Email] = true
}
result = new(model.CheckEmailPhone)
result.PhonesCheck = checkTel(uinfo.Phones, tels)
result.EmailCheck = checkEmail(uinfo.Emails, emails)
log.Info("checkPhonesAndEmails req uinfo:%+v, resPhones: %+v, resEmails: %+v, userInfo: %+v,result: %+v", uinfo, resPhones, resEmails, userInfo, result)
return
}
func checkEmail(ues string, emails map[string]bool) (result string) {
if ues == "" {
result = "0"
if len(emails) == 0 {
result = "1"
}
return
}
es := strings.Split(ues, ",")
emailCheck := make([]string, len(es))
for i, e := range es {
emailCheck[i] = "0"
if emails[e] {
emailCheck[i] = "1"
}
}
result = strings.Join(emailCheck, ",")
return
}
func checkTel(phones string, tels map[string]bool) (result string) {
if phones == "" {
result = "0"
if len(tels) == 0 {
result = "1"
}
return
}
ps := strings.Split(phones, ",")
phoneCheck := make([]string, len(ps))
for i, phone := range ps {
phoneCheck[i] = "0"
if tels[phone] {
phoneCheck[i] = "1"
}
}
result = strings.Join(phoneCheck, ",")
return
}
// checkSafe 密保信息校验
func (s *Service) checkSafe(c context.Context, uinfo *model.UserInfoReq) (sysSafeCheck string, err error) {
//没有绑定密保
if uinfo.SafeQuestion == 99 {
uinfo.SafeQuestion = 0
}
safe, err := s.d.CheckSafe(c, uinfo.Mid, uinfo.SafeQuestion, uinfo.SafeAnswer)
if err != nil {
log.Error("dao method CheckSafe error(%v)", err)
}
sysSafeCheck = safe.CheckInfo
return
}
// checkCard 校验证件信息
func (s *Service) checkCard(c context.Context, uinfo *model.UserInfoReq) (sysCardCheck string, err error) {
sysCardCheck = "0"
//1如果用户在系统里账号本身没有设置没记录则不论用户找回时填写如否系统资料对比列显示记为 null。-->请求status查看是否认证
//2如果用户在系统里账号有设置有记录则用户在找回时填写错误或没填写则比对标红记录 错;填写比对正确,则为 对。-->请求check查看是否输入正确
//me: 调用接口失败,统一判断为错误
status, err := s.d.CheckRealnameStatus(c, uinfo.Mid)
if err != nil {
return
}
//只要系统没有记录认证信息均返回null
if status == 0 {
return "null", nil
}
//用户没有填写信息,但是系统有记录认证信息
if uinfo.CardID == "" || uinfo.CardType == 99 {
return "0", nil
}
//用户填写了信息,且已经认证,检查认证信息
flag, err := s.d.CheckCard(c, uinfo.Mid, uinfo.CardType, uinfo.CardID)
if err != nil {
return
}
if flag {
sysCardCheck = "1"
}
return
}
// checkPwds 校验历史密码信息
func (s *Service) checkPwds(c context.Context, uinfo *model.UserInfoReq) (sysPwdsCheck string, err error) {
if sysPwdsCheck, err = s.d.CheckPwds(c, uinfo.Mid, uinfo.Pwds); err != nil {
return
}
return
}
// getLoginAddrs 获取登陆地
func (s *Service) getLoginAddrs(c context.Context, uinfo *model.UserInfoReq) (addrs string, err error) {
addrs, err = s.d.GetAddrByIP(c, uinfo.Mid, 100)
if err != nil {
log.Error("GetAddrByIP error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,21 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceCompareInfo(t *testing.T) {
var (
c = context.Background()
rid = int64(240)
)
convey.Convey("CompareInfo", t, func(ctx convey.C) {
err := s.CompareInfo(c, rid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,220 @@
package service
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"go-common/app/service/main/account-recovery/model"
"go-common/library/log"
"go-common/library/net/metadata"
"github.com/microcosm-cc/bluemonday"
)
type userLog struct {
Business int `json:"business"`
Type int64 `json:"type"`
IP string `json:"ip"`
CTime string `json:"ctime"`
Str0 string `json:"str_0"`
ExtraData string `json:"extra_data"`
Mid int64 `json:"mid"`
}
type mailExtra struct {
Subject string `json:"subject"`
Content string `json:"content"`
}
// SendMailLog send mailLog
func (s *Service) SendMailLog(c context.Context, mid int64, mailType int, linkMail string, params ...string) (err error) {
subject, content, err := buildMailData(true, mailType, params...)
if err != nil {
log.Error("SendMailLog err(%v)", err)
return
}
mailData := &mailExtra{
Subject: subject,
Content: content,
}
mailDataBytes, err := json.Marshal(mailData)
if err != nil {
log.Error("mailData (%v) json marshal err(%v)", mailDataBytes, err)
return
}
uLog := userLog{
Mid: mid,
Business: 51,
Type: 1,
IP: metadata.String(c, metadata.RemoteIP),
CTime: time.Now().Format("2006-01-02 15:04:05"),
Str0: linkMail,
ExtraData: string(mailDataBytes),
}
for {
if err = s.userActLogPub.Send(c, linkMail, uLog); err != nil {
log.Error("databus send(%v) error(%v)", uLog, err)
time.Sleep(100 * time.Millisecond)
continue
}
log.Info("SendMailLog success uLog: %+v", uLog)
break
}
return
}
// SendMailM send more type mail
func (s *Service) SendMailM(c context.Context, mailType int, linkMail string, params ...string) (err error) {
subject, body, err := buildMailData(false, mailType, params...)
if err != nil {
log.Error("SendMailLog err(%v)", err)
return
}
content := fmt.Sprintf("<html><body>"+
"</body>"+
" %s "+
"</html>",
body,
)
if s.d.SendMail(content, subject, linkMail); err != nil {
err = errors.New("send mial fail")
log.Error("SendMail fail,mailType=%d", mailType)
}
return
}
func buildMailData(hide bool, mailType int, params ...string) (subject, content string, err error) {
switch mailType {
case model.VerifyMail:
subject = "【哔哩哔哩】账号找回-验证邮件(点开查看,无需回复)"
// 验证码params[0]
content = generateHTML(mailType, params...)
if hide {
content = generateHTML(mailType, params[0][:2]+strings.Repeat("*", 4))
}
case model.CommitMail:
subject = "【哔哩哔哩】账号找回-申述已受理(点开查看,无需回复)"
// 打码后的UID params[0] , 提交时间params[1] , 案件IDparams[2]
content = generateHTML(mailType, params...)
case model.RejectMail:
subject = "【哔哩哔哩】账号找回-申诉结果(点开查看,无需回复)"
// 打码后的UID params[0] , 提交时间params[1] , 案件IDparams[2]
content = generateHTML(mailType, params...)
case model.AgreeMail:
subject = "【哔哩哔哩】账号找回-申诉结果(点开查看,无需回复)"
// 打码后的UID params[0] , 提交时间params[1] , 案件IDparams[2], 用户名params[3] , 初始密码params[4]
content = generateHTML(mailType, params...)
if hide {
content = generateHTML(mailType, params[0], params[1], params[2], params[3], model.HIDEALL)
}
default:
err = errors.New("没有该类型的邮件")
}
if hide {
content = xssFilter(content)
}
return subject, content, err
}
// generateHTML generate mail body
func generateHTML(mailType int, params ...string) (content string) {
var body bytes.Buffer
switch mailType {
case model.VerifyMail:
body.WriteString("<div>尊敬的用户,你好:<br />")
body.WriteString("你正在哔哩哔哩进行相应账号找回申诉,本次请求的邮件验证码为:%s,<br />")
body.WriteString("本验证码30分钟内有效请及时输入。<br />")
body.WriteString("如非本人操作,请忽略该邮件。<br />")
body.WriteString("祝在哔哩哔哩收获愉快!<br /></div>")
content = fmt.Sprintf(body.String(), params[0])
case model.CommitMail:
body.WriteString("<div>尊敬的用户,你好:<br />")
body.WriteString("账号【UID%s】的找回申诉已受理提交时间%s案件ID为【%s】请耐心等待结果将在最长7个工作日内发送到本邮箱请注意查收。")
body.WriteString("(这是一封自动发送的邮件,请不要直接回复)<br /></div>")
content = fmt.Sprintf(body.String(), params[0], params[1], params[2])
case model.RejectMail:
body.WriteString("<div>尊敬的用户,你好:<br />")
body.WriteString("账号【UID%s】的找回申诉已处理完毕提交时间%s案件ID为【%s】由于你提供的信息与此账号信息不一致申诉未能通过审核。请补充提供更准确完整的资料再行尝试。")
body.WriteString("<a href='https://account.bilibili.com/appeal/home.html#/find'>点此重新申诉</a><br /><br />")
body.WriteString("祝在哔哩哔哩收获愉快!<br /></div>")
content = fmt.Sprintf(body.String(), params[0], params[1], params[2])
case model.AgreeMail:
body.WriteString("<div>尊敬的用户,你好:<br />")
body.WriteString("账号【UID%s】的找回申诉已处理完毕提交时间%s案件ID为【%s】审核通过密码已重置请使用如下用户名与初始密码登录<br />")
body.WriteString("用户名:%s <br />")
body.WriteString("初始密码:%s <br />")
body.WriteString("为了你的账号安全,请及时修改密码与相应密保工具。 <br />")
body.WriteString("(这是一封自动发送的邮件,请不要直接回复)<br /></div>")
content = fmt.Sprintf(body.String(), params[0], params[1], params[2], params[3], params[4])
}
return content
}
// SendMailMany send more type mail
func (s *Service) SendMailMany(c context.Context, mailType int, batchRes []*model.BatchAppeal, userMap map[string]*model.User) (err error) {
var (
content = ""
subject = ""
)
switch mailType {
case model.RejectMail:
subject = "【哔哩哔哩】账号找回-申诉结果(点开查看,无需回复)"
case model.AgreeMail:
subject = "【哔哩哔哩】账号找回-申诉结果(点开查看,无需回复)"
default:
err = errors.New("没有该类型的邮件")
}
if err != nil {
log.Error("SendMailMany err(%v)", err)
return
}
//循环发送多封邮件
for _, appealInfo := range batchRes {
params := make([]string, 0)
rid := appealInfo.Rid
mid := appealInfo.Mid
linkMail := appealInfo.LinkMail
ctime := appealInfo.Ctime.Time().Format("2006-01-02 15:04:05")
params = append(params, hideUID(mid), ctime, rid)
if model.AgreeMail == mailType {
userID := userMap[mid].UserID
pwd := userMap[mid].Pwd
params = append(params, userID, pwd)
}
content = fmt.Sprintf("<html><body>"+
"</body>"+
" %s "+
"</html>",
generateHTML(mailType, params...),
)
if s.d.SendMail(content, subject, linkMail); err != nil {
err = errors.New("send mial fail")
log.Error("SendMailMany SendMail fail,mailType=%d", mailType)
}
midInt, _ := strconv.ParseInt(mid, 10, 64)
//发送邮件日志
s.SendMailLog(c, midInt, mailType, linkMail, params...)
}
return
}
// xss filter
func xssFilter(content string) string {
p := bluemonday.StrictPolicy()
return p.Sanitize(content)
}

View File

@@ -0,0 +1,77 @@
package service
import (
"context"
"testing"
"time"
"go-common/app/service/main/account-recovery/model"
xtime "go-common/library/time"
"github.com/smartystreets/goconvey/convey"
)
func TestServiceSendMailM(t *testing.T) {
var (
c = context.Background()
mailType = int(1)
params = "1234"
linkMail = "2459593393@qq.com"
)
convey.Convey("SendMailM", t, func(ctx convey.C) {
err := s.SendMailM(c, mailType, linkMail, params)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceSendMailMany(t *testing.T) {
var (
c = context.Background()
mailType = int(0)
endDate = time.Now()
timeS = xtime.Time(endDate.Unix())
batchRes = []*model.BatchAppeal{
{
Rid: "1",
Mid: "1",
LinkMail: "2459593393@qq.com",
Ctime: timeS,
},
{
Rid: "2",
Mid: "2",
LinkMail: "1772968069@qq.com",
Ctime: timeS,
},
}
userMap = make(map[string]*model.User)
user1 = model.User{UserID: "mid=1", Pwd: "mid=1的pwd"}
user2 = model.User{UserID: "mid=2", Pwd: "mid=2的pwd"}
)
userMap["1"] = &user1
userMap["2"] = &user2
convey.Convey("SendMailMany", t, func(ctx convey.C) {
err := s.SendMailMany(c, mailType, batchRes, userMap)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestServiceSendMailLog(t *testing.T) {
var (
c = context.Background()
mid int64 = 1
mailType = int(1)
params = "1234"
linkMail = "2459593393@qq.com"
)
convey.Convey("SendMailLog", t, func(ctx convey.C) {
err := s.SendMailLog(c, mid, mailType, linkMail, params)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,74 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/service/main/account-recovery/conf"
"go-common/app/service/main/account-recovery/dao"
"go-common/library/log"
"go-common/library/queue/databus"
)
// Service struct
type Service struct {
c *conf.Config
//用户申诉dao
d *dao.Dao
mailch chan func()
// wait
wg sync.WaitGroup
userActLogPub *databus.Databus
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
d: dao.New(c),
mailch: make(chan func(), c.ChanSize.MailMsg),
userActLogPub: databus.New(c.DataBus.UserActLog),
}
s.wg.Add(1)
go s.mailproc()
return s
}
// AddMailch .
func (s *Service) AddMailch(f func()) {
select {
case s.mailch <- f:
default:
log.Warn("AddMailch chan full")
}
}
// mailproc send mail
func (s *Service) mailproc() {
defer s.wg.Done()
for {
f, ok := <-s.mailch
if !ok {
log.Warn("s.mailch chan is close")
return
}
f()
}
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.d.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.d.Close()
close(s.mailch)
time.Sleep(1 * time.Second)
s.wg.Wait()
}

View File

@@ -0,0 +1,36 @@
package service
import (
"flag"
"os"
"testing"
"go-common/app/service/main/account-recovery/conf"
)
var (
s *Service
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.account-recovery")
flag.Set("conf_token", "5fe12bcbf11eb0c368ee0c2d1f567184")
flag.Set("tree_id", "55382")
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/account-recovery-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
m.Run()
os.Exit(0)
}

View File

@@ -0,0 +1,24 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account/api:all-srcs",
"//app/service/main/account/cmd:all-srcs",
"//app/service/main/account/conf:all-srcs",
"//app/service/main/account/dao:all-srcs",
"//app/service/main/account/model:all-srcs",
"//app/service/main/account/rpc:all-srcs",
"//app/service/main/account/server/grpc:all-srcs",
"//app/service/main/account/server/http:all-srcs",
"//app/service/main/account/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,489 @@
### account service
### Version 7.17.0
> 1. 修复 mc sdk
### Version 7.15.0
> 1. profile 增加 is_tourist 字段
> 2. 移除 account-notify-job
### Version 7.14.2
> 1. 修正 gRPC 节操修改逻辑
### Version 7.14.1
> 1. goRpc方法添加过期备注
### Version 7.14.0
> 1. gRPC 重构
### Version 7.13.0
> 1. 增加 VIP 相关 gRPC 接口
### Version 7.12.1
> 1. 使用优先队列延迟删除缓存
> 2. 增加队列长度统计
### Version 7.11.0
> 1. gRPC服务迁移到v1
### Version 7.10.3
> 1. add vip decide method
> 2. 实名认证空数据
> 3. 实名认证空指针检查
### Version 7.10.1
> 1. separate privacy http client
### Version 7.10.0
> 1. 增加 /privacy api
### Version 7.9.0
> 1. rpc.ProfileWithStat3 vip信息增加VipPayType
### Version 7.8.6
> 1. 封禁信息改为调用member
### Version 7.8.5
> 1. 删缓存错误以 cause 为准
### Version 7.8.4
> 1. 整理删缓存日志
### Version 7.8.3
> 1. remove cache save
### Version 7.8.2
> 1. logging delete cache error
### Version 7.8.1
> 1. 删除缓存日志
### Version 7.8.0
> 1. 去除显式 ip 参数传递
### Version 7.7.0
> 1. 修改部分基础库
### Version 7.6.0
> 1. 通过名字搜索用户信息走搜索接口
### Version 7.5.0
> 1. cards和myinfo接口调用go相关服务
### Version 7.4.0
> 1. 经验节操修改调用member
### Version 7.3.10
> 1.使用新版 warden
> 2.去除无用 error wrap
### Version 7.3.0
> 1.使用新版 warden
### Version 7.2.0
> 1.添加 GRPC 接口
#### Version 7.1.0
> 1.集成新的官方认证
> 1.官方认证兼容老接口结构
#### Version 7.0.0
> 1.删除大量无用代码
> 2.规范和统一字段名
> 3.使用最新bm和cache gen
#### Version 6.21.2
> 1.minify wallet cache duration
> 2.remove statsd
#### Version 6.21.1
> 1.reduce wallet cache duration
#### Version 6.21.0
> 1.call account thin APIs for info, infos, myinfo, userinfo, profile.
> 2.parallel call dependencies.
#### Version 6.20.0
> 1.add userinfo rpc
#### Version 6.19.0
> 1.del cache for action cleanAccessKey
#### Version 6.18.0
> 1.添加cardbyname接口
#### Version 6.17.0
> 1.del unused code
#### Version 6.16.0
> 1.add member proxy config
#### Version 6.16.0
> 1.add member proxy mid end with 92
#### Version 6.15.0
> 1.profile 硬币信息直接读取coin-service
#### Version 6.14.0
> 1.更新b+缓存
#### Version 6.14.0
> 1.添加userinfo api接口
#### Version 6.13.0
> 1.删除relation代码
#### Version 6.12.0
> 1.mid获取手机号及用户关系
#### Version 6.11.0
> 1.add thin info rpc
#### Version 6.10.4
> 1.fix wallet cache expiration
#### Version 6.10.3
> 1.add vip info cache2 del
#### Version 6.10.2
> 1.add vip info cache del
#### Version 6.10.1
> 1.升级基础库
#### Version 6.10.0
> 1.update http client conf and usage
#### Version 6.9.0
> 1.支持分组缓存清理
#### Version 6.8.0
> 1.myinfo接口调用account-java的获取用户信息myinfo内网接口
#### Version 6.7.0
> 1.迁移info card 到member-service
#### Version 6.6.0
> 1.add rpc token and group
#### Version 6.5.0
> 1.add login empty cache
#### Version 6.4.0
> 1.add http method get vip info
#### Version 6.3.0
> 1.add rpc service method info3
#### Version 6.2.0
> 1.adjust internal http router group
#### Version 6.1.0
> 1.add new mc identify cluster
#### Version 6.0.0
> 1.merge account-service into kratos
#### Version 5.16.0
> 1.add unit tests for dao and service
#### Version 5.15.0
> 1.add new mc identify cluster
#### Version 5.14.0
> 1.更新mc基础包缓存
#### Version 5.13.0
> 1.删除relation redis代码
#### Version 5.12.5
> 1. 修复syslog panic error
#### Version 5.12.4
> 1.修复go-common memcache stat err
#### Version 5.12.3
> 1.添加memcache错误监控
#### Version 5.12.2
> 1.修复profile -404错误
#### Version 5.12.1
> 1.修复wallet的缓存错误
#### Version 5.12.0
> 1.http接口改为内网接口
#### Version 5.11.7
> 1.修复relation关注升级
#### Version 5.11.6
> 1.平滑发版
> 2.fix RESTFul vipinfo
#### Version 5.11.5
> 1.add mc statsd
#### Version 5.11.4
> 1.fix rpc server stat init
#### Version 5.11.3
> 1.fix rpc stat init
#### Version 5.11.2
> 1.升级RESTFul Get
#### Version 5.11.1
> 1.prom监控bugfix
#### Version 5.11.0
> 1.接入prom监控
#### Version 5.10.0
> 1.添加attention返回所有关注列表包括悄悄关注
#### Version 5.9.0
> 1.增加黑名单rpc
> 2.更新vendor
#### Version 5.8.0
> 1.封装relation-service接口
#### Version 5.7.5
> 1.fixed monitor ping
> 2.修改配置文件加载错误提示
#### Version 5.7.4
> 1.接入新的配置中心
> 2.接入rpc DiscoverOff配置
> 3.优化清除access cache逻辑
#### Version 5.7.3
> 1.account 添加用户auditInfo rpc接口
#### Version 5.7.2
> 1.接入新的配置中心
> 2.rpc 注册配置修改
#### Version 5.7.1
> 1.no change
#### Version 5.7.0
> 1.cache callback优化
> 2.升级dao层使用Get2
> 3.memcahed err 不再回写缓存
> 4.拆分account.go
> 5.去掉service请求dao的异常日志
> 6.删除不再使用的代码
#### Version 5.6.2
> 1.profile兼容逻辑
#### Version 5.6.1
> 1.no commit(docker file missing!!!)
#### Version 5.6.0
> 1.tw-proxy local deploy
#### Version 5.5.0
> 1.cal/del 维护新的mc集群
> 2.删除thrift rpc代码
#### Version 5.4.0
> 1.go-common stat小包合并大包
> 2.go-common修复base64编码bug
#### Version 5.3.0
> 1.更新最新的net/rpc
#### Version 5.2.1
> 1.go-common修复trace race bug
#### Version 5.2.0
> 1.支持优先从本地加载配置
> 2.更新vendor支持最新的rpc库
#### Version 5.1.4
> 1.AccessInfo接口cache缓存失败return改为回源帐号
> 2.Secret接口走内存cache
#### Version 5.1.3
> 1.profile接口支持VerifyOrUserGet
#### Version 5.1.2
> 1.wallet过期时间改为CouponDueTime
> 2.修复access删除bug
#### Version 5.1.1
> 1.修复wallet清缓存bug
> 2.更新vendor
#### Version 5.1.0
> 1.支持平滑切流量和重启
#### Version 5.0.0
> 1.net/rpc改为为golang/rpcx
#### Version 4.5.0
> 1.接入配置中心
#### Version 4.3.3
> 1.wallet字段json格式修正
#### Version 4.3.2
> 1.idfSvc改成service实现,并支持两个新方法Access和Verify
> 2.升级vendor
#### Version 4.3.1
> 1.修复ak获取到用户存缓存0的bug
#### Version 4.3.0
> 1.新增支持accesskey白名单
> 2.支持b币新接口
#### Version 4.2.1
> 1.去掉帐号错误码转换
#### Version 4.2.0
> 1.添加accessInfo接口和获取secret的http接口
#### Version 4.1.1
> 1.升级zk的包版本到最新
#### Version 4.1.0
> 1.增加thrift的auth调用
#### Version 4.0.1
> 1.更新所有匿名rpc client为默认user
#### Version 4.0.0
> 1.引入vendor
> 2.rpc修改syslog和上报
#### Version 3.7.0
> 1.新增verify和secret的rpc接口用于identify
#### Version 3.6.1
> 1.新增获取移动端头图的接口
#### Version 3.6.0
> 1.VIP信息聚合
> 2.新增名片信息和空间信息的聚合接口
> 3.新增查询用户信息和是否关注up主的聚合接口
#### Version 3.5.1
> 1.新增profile http接口
> 2.新增批量获取relation
#### Version 3.5.0
> 1.新增获取up主和粉丝的关系rpc接口
#### Version 3.4.0
> 1.新增批量获取up主和粉丝的关系
> 2.cache更新优化
#### Version 3.3.1
> 1.fix relation bug
#### Version 3.3.0
> 1.增加用户是否关注up主的http接口
> 2.增加批量获取用户信息接口的http接口
##### Version 3.2.2
> 1.trace v2
##### Version 3.2.1
> 1.http client读写超时设置分开
##### Version 3.2.0
> 1.修复elk日志
> 2.支持trace v2
##### Version 3.1.0
> 1.使用go-common/xlog
> 2.修改删除cache接口为get
##### Version 3.0.1
> 1.修复relation接口返回nil
##### Version 3.0.0
> 1.context使用官方接口
> 2.添加coin rpc
##### Version 2.4.0
> 1.添加昵称查询card rpc接口
##### Version 2.3.0
> 1.添加服务发现
##### Version 2.2.2
> 1.修改配置信息,更合理
##### Version 2.2.1
> 1.修复rpc返回指针赋值
##### Version 2.2.0
> 1.优化
> 2.add elk
> 3.add trace id
> 4.modify appkey
> 5.add async update cache
> 6.add friend relation
##### Version 2.1.0
> 1.add tracer
##### Version 1.1.0
> 1.基于go-common重构
##### Version 1.0.0
> 1.初始化完成用户信息基础查询功能

View File

@@ -0,0 +1,17 @@
# Owner
wanghuan01
zhoujiahui
# Author
wanghuan01
zhoujiahui
zhoujixiang
chenshangqiang
# Reviewer
wanghuan01
zhoujiahui
zhoujixiang
chenshangqiang
linmiao
haoguawnei

View File

@@ -0,0 +1,20 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- chenshangqiang
- wanghuan01
- zhoujiahui
- zhoujixiang
labels:
- main
- service
- service/main/account
options:
no_parent_owners: true
reviewers:
- chenshangqiang
- haoguawnei
- linmiao
- wanghuan01
- zhoujiahui
- zhoujixiang

View File

@@ -0,0 +1,14 @@
#### account-service
`account-service`主要是为主站各个业务提供一个大的cache和rpc接口。
* 聚合了账号多个业务(`passport` `account` `big` `pay` `relation-service`)的缓存
* 提供了rpc服务
1. 为没有rpc服务的业务(`passport` `account` `big` `pay`)封装rpc接口
2. 已rpc服务的业务(`relation-service`)提供接口封装透传rpc服务
##### 依赖环境
Go 1.7.5或更高版本
##### API文档
TODO example code api

View File

@@ -0,0 +1,71 @@
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 = "api_proto",
srcs = ["api.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "api_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/service/main/account/api",
proto = ":api_proto",
tags = ["automanaged"],
deps = [
"//library/time:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"client.go",
"copy_autogenerated.go",
"mock.go",
"vip.go",
],
embed = [":api_go_proto"],
importpath = "go-common/app/service/main/account/api",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/member/model:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/golang/mock/gomock:go_default_library",
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account/api/gorpc:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
// +bili:type=service
syntax = "proto3";
package account.service;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "api";
message Card {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
string name = 2 [(gogoproto.jsontag) = "name"];
string sex = 3 [(gogoproto.jsontag) = "sex"];
string face = 4 [(gogoproto.jsontag) = "face"];
string sign = 5 [(gogoproto.jsontag) = "sign"];
int32 rank = 6 [(gogoproto.jsontag) = "rank"];
int32 level = 7 [(gogoproto.jsontag) = "level"];
int32 silence = 8 [(gogoproto.jsontag) = "silence"];
VipInfo vip = 9 [(gogoproto.jsontag) = "vip", (gogoproto.nullable) = false];
PendantInfo pendant = 10
[(gogoproto.jsontag) = "pendant", (gogoproto.nullable) = false];
NameplateInfo nameplate = 11
[(gogoproto.jsontag) = "nameplate", (gogoproto.nullable) = false];
OfficialInfo official = 12
[(gogoproto.jsontag) = "official", (gogoproto.nullable) = false];
}
message Info {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
string name = 2 [(gogoproto.jsontag) = "name"];
string sex = 3 [(gogoproto.jsontag) = "sex"];
string face = 4 [(gogoproto.jsontag) = "face"];
string sign = 5 [(gogoproto.jsontag) = "sign"];
int32 rank = 6 [(gogoproto.jsontag) = "rank"];
}
message Profile {
int64 mid = 1 [(gogoproto.jsontag) = "mid"];
string name = 2 [(gogoproto.jsontag) = "name"];
string sex = 3 [(gogoproto.jsontag) = "sex"];
string face = 4 [(gogoproto.jsontag) = "face"];
string sign = 5 [(gogoproto.jsontag) = "sign"];
int32 rank = 6 [(gogoproto.jsontag) = "rank"];
int32 level = 7 [(gogoproto.jsontag) = "level"];
int32 join_time = 8 [(gogoproto.jsontag) = "jointime"];
int32 moral = 9 [(gogoproto.jsontag) = "moral"];
int32 silence = 10 [(gogoproto.jsontag) = "silence"];
int32 email_status = 11 [(gogoproto.jsontag) = "email_status"];
int32 tel_status = 12 [(gogoproto.jsontag) = "tel_status"];
int32 identification = 13 [(gogoproto.jsontag) = "identification"];
VipInfo vip = 14 [(gogoproto.jsontag) = "vip", (gogoproto.nullable) = false];
PendantInfo pendant = 15
[(gogoproto.jsontag) = "pendant", (gogoproto.nullable) = false];
NameplateInfo nameplate = 16
[(gogoproto.jsontag) = "nameplate", (gogoproto.nullable) = false];
OfficialInfo official = 17
[(gogoproto.jsontag) = "official", (gogoproto.nullable) = false];
int64 birthday = 18 [
(gogoproto.jsontag) = "birthday",
(gogoproto.casttype) = "go-common/library/time.Time"
];
int32 is_tourist = 19 [(gogoproto.jsontag) = "is_tourist"];
}
// +bili:deepcopy-gen=true
// +bili:deepcopy-gen:structs=go-common/app/service/main/member/model.LevelInfo
message LevelInfo {
int32 cur = 1 [(gogoproto.jsontag) = "current_level"];
int32 min = 2 [(gogoproto.jsontag) = "current_min"];
int32 now_exp = 3 [(gogoproto.jsontag) = "current_exp"];
int32 next_exp = 4 [(gogoproto.jsontag) = "next_exp"];
}
message VipInfo {
int32 type = 1 [(gogoproto.jsontag) = "type"];
int32 status = 2 [(gogoproto.jsontag) = "status"];
int64 due_date = 3 [(gogoproto.jsontag) = "due_date"];
int32 vip_pay_type = 4 [(gogoproto.jsontag) = "vip_pay_type"];
}
message PendantInfo {
int32 pid = 1 [(gogoproto.jsontag) = "pid", (gogoproto.casttype) = "int"];
string name = 2 [(gogoproto.jsontag) = "name"];
string image = 3 [(gogoproto.jsontag) = "image"];
int64 expire = 4
[(gogoproto.jsontag) = "expire", (gogoproto.casttype) = "int"];
}
message NameplateInfo {
int32 nid = 1 [(gogoproto.jsontag) = "nid", (gogoproto.casttype) = "int"];
string name = 2 [(gogoproto.jsontag) = "name"];
string image = 3 [(gogoproto.jsontag) = "image"];
string image_small = 4 [(gogoproto.jsontag) = "image_small"];
string level = 5 [(gogoproto.jsontag) = "level"];
string condition = 6 [(gogoproto.jsontag) = "condition"];
}
// +bili:deepcopy-gen=true
// +bili:deepcopy-gen:structs=go-common/app/service/main/member/model.OfficialInfo
message OfficialInfo {
int32 role = 1 [(gogoproto.jsontag) = "role", (gogoproto.casttype) = "int8"];
string title = 2 [(gogoproto.jsontag) = "title"];
string desc = 3 [(gogoproto.jsontag) = "desc"];
}
message MidReq {
int64 mid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
string real_ip = 2;
}
message MidsReq {
repeated int64 mids = 1
[(gogoproto.moretags) = "validate:\"gt=0,dive,gt=0\""];
string real_ip = 2;
}
message NamesReq {
repeated string names = 1
[(gogoproto.moretags) = "validate:\"gt=0,dive,gt=0\""];
string real_ip = 2;
}
message ExpReq {
int64 mid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
double exp = 2;
string operater = 3;
string operate = 4;
string reason = 5;
string real_ip = 6;
}
message MoralReq {
int64 mid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
double moral = 2;
string oper = 3;
string reason = 4;
string remark = 5;
string real_ip = 6;
}
message RelationReq {
int64 mid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
int64 owner = 2;
string real_ip = 3;
}
message RelationsReq {
int64 mid = 1 [(gogoproto.moretags) = "validate:\"gt=0,required\""];
repeated int64 owners = 2;
string real_ip = 3;
}
message RichRelationReq {
int64 owner = 1;
repeated int64 mids = 2
[(gogoproto.moretags) = "validate:\"gt=0,dive,gt=0\""];
string real_ip = 3;
}
message InfoReply {
Info info = 1;
}
message InfosReply {
map<int64, Info> infos = 1;
}
message CardReply {
Card card = 1;
}
message CardsReply {
map<int64, Card> cards = 1;
}
message ProfileReply {
Profile profile = 1;
}
message ProfileStatReply {
Profile profile = 1;
LevelInfo level_info = 2 [(gogoproto.nullable) = false];
double coins = 3;
int64 following = 4;
int64 follower = 5;
}
message RelationReply {
bool following = 1;
}
message AttentionsReply {
repeated int64 attentions = 1;
}
message BlacksReply {
map<int64, bool> black_list = 1;
}
message RelationsReply {
map<int64, RelationReply> relations = 1;
}
message RichRelationsReply {
map<int64, int32> rich_relations = 1;
}
// +bili:deepcopy-gen=true
// +bili:deepcopy-gen:structs=go-common/app/service/main/account/api.VipInfo
message VipReply {
int32 type = 1;
int32 status = 2;
int64 due_date = 3;
int32 vip_pay_type = 4;
}
message VipsReply {
map<int64, VipReply> vips = 1;
}
message ExpReply {}
message MoralReply {}
service Account {
rpc Info3(MidReq) returns (InfoReply);
rpc Infos3(MidsReq) returns (InfosReply);
rpc InfosByName3(NamesReq) returns (InfosReply);
rpc Card3(MidReq) returns (CardReply);
rpc Cards3(MidsReq) returns (CardsReply);
rpc Profile3(MidReq) returns (ProfileReply);
rpc ProfileWithStat3(MidReq) returns (ProfileStatReply);
rpc AddExp3(ExpReq) returns (ExpReply);
rpc AddMoral3(MoralReq) returns (MoralReply);
rpc Relation3(RelationReq) returns (RelationReply);
rpc Attentions3(MidReq) returns (AttentionsReply);
rpc Blacks3(MidReq) returns (BlacksReply);
rpc Relations3(RelationsReq) returns (RelationsReply);
rpc RichRelations3(RichRelationReq) returns (RichRelationsReply);
rpc Vip3(MidReq) returns (VipReply);
rpc Vips3(MidsReq) returns (VipsReply);
}

View File

@@ -0,0 +1,22 @@
package api
import (
"context"
"go-common/library/net/rpc/warden"
"google.golang.org/grpc"
)
// AppID unique app id for service discovery
const AppID = "account.service"
// NewClient new member grpc client
func NewClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (AccountClient, error) {
client := warden.NewClient(cfg, opts...)
conn, err := client.Dial(context.Background(), "discovery://default/"+AppID)
if err != nil {
return nil, err
}
return NewAccountClient(conn), nil
}

View File

@@ -0,0 +1,137 @@
// Code generated by deepcopy-gen. DO NOT EDIT.
package api
import (
model "go-common/app/service/main/member/model"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LevelInfo) DeepCopyInto(out *LevelInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LevelInfo.
func (in *LevelInfo) DeepCopy() *LevelInfo {
if in == nil {
return nil
}
out := new(LevelInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyAsIntoLevelInfo is an autogenerated deepcopy function, copying the receiver, writing into model.LevelInfo.
func (in *LevelInfo) DeepCopyAsIntoLevelInfo(out *model.LevelInfo) {
out.Cur = in.Cur
out.Min = in.Min
out.NowExp = in.NowExp
out.NextExp = in.NextExp
return
}
// DeepCopyFromLevelInfo is an autogenerated deepcopy function, copying the receiver, writing into model.LevelInfo.
func (out *LevelInfo) DeepCopyFromLevelInfo(in *model.LevelInfo) {
out.Cur = in.Cur
out.Min = in.Min
out.NowExp = in.NowExp
out.NextExp = in.NextExp
return
}
// DeepCopyAsLevelInfo is an autogenerated deepcopy function, copying the receiver, creating a new model.LevelInfo.
func (in *LevelInfo) DeepCopyAsLevelInfo() *model.LevelInfo {
if in == nil {
return nil
}
out := new(model.LevelInfo)
in.DeepCopyAsIntoLevelInfo(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OfficialInfo) DeepCopyInto(out *OfficialInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OfficialInfo.
func (in *OfficialInfo) DeepCopy() *OfficialInfo {
if in == nil {
return nil
}
out := new(OfficialInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyAsIntoOfficialInfo is an autogenerated deepcopy function, copying the receiver, writing into model.OfficialInfo.
func (in *OfficialInfo) DeepCopyAsIntoOfficialInfo(out *model.OfficialInfo) {
out.Role = in.Role
out.Title = in.Title
out.Desc = in.Desc
return
}
// DeepCopyFromOfficialInfo is an autogenerated deepcopy function, copying the receiver, writing into model.OfficialInfo.
func (out *OfficialInfo) DeepCopyFromOfficialInfo(in *model.OfficialInfo) {
out.Role = in.Role
out.Title = in.Title
out.Desc = in.Desc
return
}
// DeepCopyAsOfficialInfo is an autogenerated deepcopy function, copying the receiver, creating a new model.OfficialInfo.
func (in *OfficialInfo) DeepCopyAsOfficialInfo() *model.OfficialInfo {
if in == nil {
return nil
}
out := new(model.OfficialInfo)
in.DeepCopyAsIntoOfficialInfo(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VipReply) DeepCopyInto(out *VipReply) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VipReply.
func (in *VipReply) DeepCopy() *VipReply {
if in == nil {
return nil
}
out := new(VipReply)
in.DeepCopyInto(out)
return out
}
// DeepCopyAsIntoVipInfo is an autogenerated deepcopy function, copying the receiver, writing into VipInfo.
func (in *VipReply) DeepCopyAsIntoVipInfo(out *VipInfo) {
out.Type = in.Type
out.Status = in.Status
out.DueDate = in.DueDate
out.VipPayType = in.VipPayType
return
}
// DeepCopyFromVipInfo is an autogenerated deepcopy function, copying the receiver, writing into VipInfo.
func (out *VipReply) DeepCopyFromVipInfo(in *VipInfo) {
out.Type = in.Type
out.Status = in.Status
out.DueDate = in.DueDate
out.VipPayType = in.VipPayType
return
}
// DeepCopyAsVipInfo is an autogenerated deepcopy function, copying the receiver, creating a new VipInfo.
func (in *VipReply) DeepCopyAsVipInfo() *VipInfo {
if in == nil {
return nil
}
out := new(VipInfo)
in.DeepCopyAsIntoVipInfo(out)
return out
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["account3.go"],
importpath = "go-common/app/service/main/account/api/gorpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/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,152 @@
package client
import (
"context"
v1 "go-common/app/service/main/account/api"
"go-common/app/service/main/account/model"
"go-common/library/net/rpc"
)
const (
_info3 = "RPC.Info3"
_card3 = "RPC.Card3"
_infos3 = "RPC.Infos3"
_infosByName3 = "RPC.InfosByName3"
_cards3 = "RPC.Cards3"
_profile3 = "RPC.Profile3"
_profileStat3 = "RPC.ProfileWithStat3"
_addExp3 = "RPC.AddExp3"
_addMoral3 = "RPC.AddMoral3"
_relation3 = "RPC.Relation3"
_relations3 = "RPC.Relations3"
_attentions3 = "RPC.Attentions3"
_blacks3 = "RPC.Blacks3"
_richRelations3 = "RPC.RichRelations3"
)
const (
_appid = "account.service"
)
var (
_noArg = &struct{}{}
)
// Service3 for server client3
type Service3 struct {
client *rpc.Client2
}
// New3 for new struct Service2 obj
// DEPRECATED: Please use gRPC service instead.
func New3(c *rpc.ClientConfig) (s *Service3) {
s = &Service3{}
s.client = rpc.NewDiscoveryCli(_appid, c)
return
}
// Info3 receive ArgMid contains mid and real ip, then init user info.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Info3(c context.Context, arg *model.ArgMid) (res *v1.Info, err error) {
res = new(v1.Info)
err = s.client.Call(c, _info3, arg, res)
return
}
// Card3 receive ArgMid contains mid and real ip, then init user card.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Card3(c context.Context, arg *model.ArgMid) (res *v1.Card, err error) {
res = new(v1.Card)
err = s.client.Call(c, _card3, arg, res)
return
}
// Infos3 receive ArgMids contains mid and real ip, then init user info.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Infos3(c context.Context, arg *model.ArgMids) (res map[int64]*v1.Info, err error) {
err = s.client.Call(c, _infos3, arg, &res)
return
}
// InfosByName3 receive ArgMids contains mid and real ip, then init user info.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) InfosByName3(c context.Context, arg *model.ArgNames) (res map[int64]*v1.Info, err error) {
err = s.client.Call(c, _infosByName3, arg, &res)
return
}
// Cards3 receive ArgMids contains mid and real ip, then init user card.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Cards3(c context.Context, arg *model.ArgMids) (res map[int64]*v1.Card, err error) {
err = s.client.Call(c, _cards3, arg, &res)
return
}
// Profile3 get user profile.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Profile3(c context.Context, arg *model.ArgMid) (res *v1.Profile, err error) {
res = new(v1.Profile)
err = s.client.Call(c, _profile3, arg, res)
return
}
// ProfileWithStat3 get user profile.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) ProfileWithStat3(c context.Context, arg *model.ArgMid) (res *model.ProfileStat, err error) {
res = new(model.ProfileStat)
err = s.client.Call(c, _profileStat3, arg, res)
return
}
// AddExp3 receive ArgExp contains mid, money and reason, then add exp for user.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) AddExp3(c context.Context, arg *model.ArgExp) (err error) {
err = s.client.Call(c, _addExp3, arg, _noArg)
return
}
// AddMoral3 receive ArgMoral contains mid, moral, oper, reason and remark, then add moral for user.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) AddMoral3(c context.Context, arg *model.ArgMoral) (err error) {
err = s.client.Call(c, _addMoral3, arg, _noArg)
return
}
// Relation3 get user friend relation.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Relation3(c context.Context, arg *model.ArgRelation) (res *model.Relation, err error) {
res = new(model.Relation)
err = s.client.Call(c, _relation3, arg, res)
return
}
// Relations3 batch get user friend relation.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Relations3(c context.Context, arg *model.ArgRelations) (res map[int64]*model.Relation, err error) {
err = s.client.Call(c, _relations3, arg, &res)
return
}
// Attentions3 get user attentions ,include followings and whispers.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Attentions3(c context.Context, arg *model.ArgMid) (res []int64, err error) {
err = s.client.Call(c, _attentions3, arg, &res)
return
}
// Blacks3 get user black list.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) Blacks3(c context.Context, arg *model.ArgMid) (res map[int64]struct{}, err error) {
err = s.client.Call(c, _blacks3, arg, &res)
return
}
// RichRelations3 get relation between owner and mids.
// DEPRECATED: Please use gRPC service instead.
func (s *Service3) RichRelations3(c context.Context, arg *model.ArgRichRelation) (res map[int64]int, err error) {
err = s.client.Call(c, _richRelations3, arg, &res)
return
}

View File

@@ -0,0 +1,554 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: api.pb.go
// Package api is a generated GoMock package.
package api
import (
gomock "github.com/golang/mock/gomock"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
reflect "reflect"
)
// MockAccountClient is a mock of AccountClient interface
type MockAccountClient struct {
ctrl *gomock.Controller
recorder *MockAccountClientMockRecorder
}
// MockAccountClientMockRecorder is the mock recorder for MockAccountClient
type MockAccountClientMockRecorder struct {
mock *MockAccountClient
}
// NewMockAccountClient creates a new mock instance
func NewMockAccountClient(ctrl *gomock.Controller) *MockAccountClient {
mock := &MockAccountClient{ctrl: ctrl}
mock.recorder = &MockAccountClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAccountClient) EXPECT() *MockAccountClientMockRecorder {
return m.recorder
}
// Info3 mocks base method
func (m *MockAccountClient) Info3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*InfoReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Info3", varargs...)
ret0, _ := ret[0].(*InfoReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Info3 indicates an expected call of Info3
func (mr *MockAccountClientMockRecorder) Info3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info3", reflect.TypeOf((*MockAccountClient)(nil).Info3), varargs...)
}
// Infos3 mocks base method
func (m *MockAccountClient) Infos3(ctx context.Context, in *MidsReq, opts ...grpc.CallOption) (*InfosReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Infos3", varargs...)
ret0, _ := ret[0].(*InfosReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Infos3 indicates an expected call of Infos3
func (mr *MockAccountClientMockRecorder) Infos3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infos3", reflect.TypeOf((*MockAccountClient)(nil).Infos3), varargs...)
}
// InfosByName3 mocks base method
func (m *MockAccountClient) InfosByName3(ctx context.Context, in *NamesReq, opts ...grpc.CallOption) (*InfosReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "InfosByName3", varargs...)
ret0, _ := ret[0].(*InfosReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InfosByName3 indicates an expected call of InfosByName3
func (mr *MockAccountClientMockRecorder) InfosByName3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InfosByName3", reflect.TypeOf((*MockAccountClient)(nil).InfosByName3), varargs...)
}
// Card3 mocks base method
func (m *MockAccountClient) Card3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*CardReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Card3", varargs...)
ret0, _ := ret[0].(*CardReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Card3 indicates an expected call of Card3
func (mr *MockAccountClientMockRecorder) Card3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Card3", reflect.TypeOf((*MockAccountClient)(nil).Card3), varargs...)
}
// Cards3 mocks base method
func (m *MockAccountClient) Cards3(ctx context.Context, in *MidsReq, opts ...grpc.CallOption) (*CardsReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Cards3", varargs...)
ret0, _ := ret[0].(*CardsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Cards3 indicates an expected call of Cards3
func (mr *MockAccountClientMockRecorder) Cards3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cards3", reflect.TypeOf((*MockAccountClient)(nil).Cards3), varargs...)
}
// Profile3 mocks base method
func (m *MockAccountClient) Profile3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*ProfileReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Profile3", varargs...)
ret0, _ := ret[0].(*ProfileReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Profile3 indicates an expected call of Profile3
func (mr *MockAccountClientMockRecorder) Profile3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Profile3", reflect.TypeOf((*MockAccountClient)(nil).Profile3), varargs...)
}
// ProfileWithStat3 mocks base method
func (m *MockAccountClient) ProfileWithStat3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*ProfileStatReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "ProfileWithStat3", varargs...)
ret0, _ := ret[0].(*ProfileStatReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ProfileWithStat3 indicates an expected call of ProfileWithStat3
func (mr *MockAccountClientMockRecorder) ProfileWithStat3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProfileWithStat3", reflect.TypeOf((*MockAccountClient)(nil).ProfileWithStat3), varargs...)
}
// AddExp3 mocks base method
func (m *MockAccountClient) AddExp3(ctx context.Context, in *ExpReq, opts ...grpc.CallOption) (*ExpReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "AddExp3", varargs...)
ret0, _ := ret[0].(*ExpReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddExp3 indicates an expected call of AddExp3
func (mr *MockAccountClientMockRecorder) AddExp3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddExp3", reflect.TypeOf((*MockAccountClient)(nil).AddExp3), varargs...)
}
// AddMoral3 mocks base method
func (m *MockAccountClient) AddMoral3(ctx context.Context, in *MoralReq, opts ...grpc.CallOption) (*MoralReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "AddMoral3", varargs...)
ret0, _ := ret[0].(*MoralReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddMoral3 indicates an expected call of AddMoral3
func (mr *MockAccountClientMockRecorder) AddMoral3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddMoral3", reflect.TypeOf((*MockAccountClient)(nil).AddMoral3), varargs...)
}
// Relation3 mocks base method
func (m *MockAccountClient) Relation3(ctx context.Context, in *RelationReq, opts ...grpc.CallOption) (*RelationReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Relation3", varargs...)
ret0, _ := ret[0].(*RelationReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Relation3 indicates an expected call of Relation3
func (mr *MockAccountClientMockRecorder) Relation3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Relation3", reflect.TypeOf((*MockAccountClient)(nil).Relation3), varargs...)
}
// Attentions3 mocks base method
func (m *MockAccountClient) Attentions3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*AttentionsReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Attentions3", varargs...)
ret0, _ := ret[0].(*AttentionsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Attentions3 indicates an expected call of Attentions3
func (mr *MockAccountClientMockRecorder) Attentions3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attentions3", reflect.TypeOf((*MockAccountClient)(nil).Attentions3), varargs...)
}
// Blacks3 mocks base method
func (m *MockAccountClient) Blacks3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*BlacksReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Blacks3", varargs...)
ret0, _ := ret[0].(*BlacksReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Blacks3 indicates an expected call of Blacks3
func (mr *MockAccountClientMockRecorder) Blacks3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Blacks3", reflect.TypeOf((*MockAccountClient)(nil).Blacks3), varargs...)
}
// Relations3 mocks base method
func (m *MockAccountClient) Relations3(ctx context.Context, in *RelationsReq, opts ...grpc.CallOption) (*RelationsReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Relations3", varargs...)
ret0, _ := ret[0].(*RelationsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Relations3 indicates an expected call of Relations3
func (mr *MockAccountClientMockRecorder) Relations3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Relations3", reflect.TypeOf((*MockAccountClient)(nil).Relations3), varargs...)
}
// RichRelations3 mocks base method
func (m *MockAccountClient) RichRelations3(ctx context.Context, in *RichRelationReq, opts ...grpc.CallOption) (*RichRelationsReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "RichRelations3", varargs...)
ret0, _ := ret[0].(*RichRelationsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// RichRelations3 indicates an expected call of RichRelations3
func (mr *MockAccountClientMockRecorder) RichRelations3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RichRelations3", reflect.TypeOf((*MockAccountClient)(nil).RichRelations3), varargs...)
}
// Vip3 mocks base method
func (m *MockAccountClient) Vip3(ctx context.Context, in *MidReq, opts ...grpc.CallOption) (*VipReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Vip3", varargs...)
ret0, _ := ret[0].(*VipReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Vip3 indicates an expected call of Vip3
func (mr *MockAccountClientMockRecorder) Vip3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Vip3", reflect.TypeOf((*MockAccountClient)(nil).Vip3), varargs...)
}
// Vips3 mocks base method
func (m *MockAccountClient) Vips3(ctx context.Context, in *MidsReq, opts ...grpc.CallOption) (*VipsReply, error) {
varargs := []interface{}{ctx, in}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Vips3", varargs...)
ret0, _ := ret[0].(*VipsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Vips3 indicates an expected call of Vips3
func (mr *MockAccountClientMockRecorder) Vips3(ctx, in interface{}, opts ...interface{}) *gomock.Call {
varargs := append([]interface{}{ctx, in}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Vips3", reflect.TypeOf((*MockAccountClient)(nil).Vips3), varargs...)
}
// MockAccountServer is a mock of AccountServer interface
type MockAccountServer struct {
ctrl *gomock.Controller
recorder *MockAccountServerMockRecorder
}
// MockAccountServerMockRecorder is the mock recorder for MockAccountServer
type MockAccountServerMockRecorder struct {
mock *MockAccountServer
}
// NewMockAccountServer creates a new mock instance
func NewMockAccountServer(ctrl *gomock.Controller) *MockAccountServer {
mock := &MockAccountServer{ctrl: ctrl}
mock.recorder = &MockAccountServerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAccountServer) EXPECT() *MockAccountServerMockRecorder {
return m.recorder
}
// Info3 mocks base method
func (m *MockAccountServer) Info3(arg0 context.Context, arg1 *MidReq) (*InfoReply, error) {
ret := m.ctrl.Call(m, "Info3", arg0, arg1)
ret0, _ := ret[0].(*InfoReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Info3 indicates an expected call of Info3
func (mr *MockAccountServerMockRecorder) Info3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info3", reflect.TypeOf((*MockAccountServer)(nil).Info3), arg0, arg1)
}
// Infos3 mocks base method
func (m *MockAccountServer) Infos3(arg0 context.Context, arg1 *MidsReq) (*InfosReply, error) {
ret := m.ctrl.Call(m, "Infos3", arg0, arg1)
ret0, _ := ret[0].(*InfosReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Infos3 indicates an expected call of Infos3
func (mr *MockAccountServerMockRecorder) Infos3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infos3", reflect.TypeOf((*MockAccountServer)(nil).Infos3), arg0, arg1)
}
// InfosByName3 mocks base method
func (m *MockAccountServer) InfosByName3(arg0 context.Context, arg1 *NamesReq) (*InfosReply, error) {
ret := m.ctrl.Call(m, "InfosByName3", arg0, arg1)
ret0, _ := ret[0].(*InfosReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InfosByName3 indicates an expected call of InfosByName3
func (mr *MockAccountServerMockRecorder) InfosByName3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InfosByName3", reflect.TypeOf((*MockAccountServer)(nil).InfosByName3), arg0, arg1)
}
// Card3 mocks base method
func (m *MockAccountServer) Card3(arg0 context.Context, arg1 *MidReq) (*CardReply, error) {
ret := m.ctrl.Call(m, "Card3", arg0, arg1)
ret0, _ := ret[0].(*CardReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Card3 indicates an expected call of Card3
func (mr *MockAccountServerMockRecorder) Card3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Card3", reflect.TypeOf((*MockAccountServer)(nil).Card3), arg0, arg1)
}
// Cards3 mocks base method
func (m *MockAccountServer) Cards3(arg0 context.Context, arg1 *MidsReq) (*CardsReply, error) {
ret := m.ctrl.Call(m, "Cards3", arg0, arg1)
ret0, _ := ret[0].(*CardsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Cards3 indicates an expected call of Cards3
func (mr *MockAccountServerMockRecorder) Cards3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cards3", reflect.TypeOf((*MockAccountServer)(nil).Cards3), arg0, arg1)
}
// Profile3 mocks base method
func (m *MockAccountServer) Profile3(arg0 context.Context, arg1 *MidReq) (*ProfileReply, error) {
ret := m.ctrl.Call(m, "Profile3", arg0, arg1)
ret0, _ := ret[0].(*ProfileReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Profile3 indicates an expected call of Profile3
func (mr *MockAccountServerMockRecorder) Profile3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Profile3", reflect.TypeOf((*MockAccountServer)(nil).Profile3), arg0, arg1)
}
// ProfileWithStat3 mocks base method
func (m *MockAccountServer) ProfileWithStat3(arg0 context.Context, arg1 *MidReq) (*ProfileStatReply, error) {
ret := m.ctrl.Call(m, "ProfileWithStat3", arg0, arg1)
ret0, _ := ret[0].(*ProfileStatReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ProfileWithStat3 indicates an expected call of ProfileWithStat3
func (mr *MockAccountServerMockRecorder) ProfileWithStat3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProfileWithStat3", reflect.TypeOf((*MockAccountServer)(nil).ProfileWithStat3), arg0, arg1)
}
// AddExp3 mocks base method
func (m *MockAccountServer) AddExp3(arg0 context.Context, arg1 *ExpReq) (*ExpReply, error) {
ret := m.ctrl.Call(m, "AddExp3", arg0, arg1)
ret0, _ := ret[0].(*ExpReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddExp3 indicates an expected call of AddExp3
func (mr *MockAccountServerMockRecorder) AddExp3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddExp3", reflect.TypeOf((*MockAccountServer)(nil).AddExp3), arg0, arg1)
}
// AddMoral3 mocks base method
func (m *MockAccountServer) AddMoral3(arg0 context.Context, arg1 *MoralReq) (*MoralReply, error) {
ret := m.ctrl.Call(m, "AddMoral3", arg0, arg1)
ret0, _ := ret[0].(*MoralReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddMoral3 indicates an expected call of AddMoral3
func (mr *MockAccountServerMockRecorder) AddMoral3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddMoral3", reflect.TypeOf((*MockAccountServer)(nil).AddMoral3), arg0, arg1)
}
// Relation3 mocks base method
func (m *MockAccountServer) Relation3(arg0 context.Context, arg1 *RelationReq) (*RelationReply, error) {
ret := m.ctrl.Call(m, "Relation3", arg0, arg1)
ret0, _ := ret[0].(*RelationReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Relation3 indicates an expected call of Relation3
func (mr *MockAccountServerMockRecorder) Relation3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Relation3", reflect.TypeOf((*MockAccountServer)(nil).Relation3), arg0, arg1)
}
// Attentions3 mocks base method
func (m *MockAccountServer) Attentions3(arg0 context.Context, arg1 *MidReq) (*AttentionsReply, error) {
ret := m.ctrl.Call(m, "Attentions3", arg0, arg1)
ret0, _ := ret[0].(*AttentionsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Attentions3 indicates an expected call of Attentions3
func (mr *MockAccountServerMockRecorder) Attentions3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attentions3", reflect.TypeOf((*MockAccountServer)(nil).Attentions3), arg0, arg1)
}
// Blacks3 mocks base method
func (m *MockAccountServer) Blacks3(arg0 context.Context, arg1 *MidReq) (*BlacksReply, error) {
ret := m.ctrl.Call(m, "Blacks3", arg0, arg1)
ret0, _ := ret[0].(*BlacksReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Blacks3 indicates an expected call of Blacks3
func (mr *MockAccountServerMockRecorder) Blacks3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Blacks3", reflect.TypeOf((*MockAccountServer)(nil).Blacks3), arg0, arg1)
}
// Relations3 mocks base method
func (m *MockAccountServer) Relations3(arg0 context.Context, arg1 *RelationsReq) (*RelationsReply, error) {
ret := m.ctrl.Call(m, "Relations3", arg0, arg1)
ret0, _ := ret[0].(*RelationsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Relations3 indicates an expected call of Relations3
func (mr *MockAccountServerMockRecorder) Relations3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Relations3", reflect.TypeOf((*MockAccountServer)(nil).Relations3), arg0, arg1)
}
// RichRelations3 mocks base method
func (m *MockAccountServer) RichRelations3(arg0 context.Context, arg1 *RichRelationReq) (*RichRelationsReply, error) {
ret := m.ctrl.Call(m, "RichRelations3", arg0, arg1)
ret0, _ := ret[0].(*RichRelationsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// RichRelations3 indicates an expected call of RichRelations3
func (mr *MockAccountServerMockRecorder) RichRelations3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RichRelations3", reflect.TypeOf((*MockAccountServer)(nil).RichRelations3), arg0, arg1)
}
// Vip3 mocks base method
func (m *MockAccountServer) Vip3(arg0 context.Context, arg1 *MidReq) (*VipReply, error) {
ret := m.ctrl.Call(m, "Vip3", arg0, arg1)
ret0, _ := ret[0].(*VipReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Vip3 indicates an expected call of Vip3
func (mr *MockAccountServerMockRecorder) Vip3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Vip3", reflect.TypeOf((*MockAccountServer)(nil).Vip3), arg0, arg1)
}
// Vips3 mocks base method
func (m *MockAccountServer) Vips3(arg0 context.Context, arg1 *MidsReq) (*VipsReply, error) {
ret := m.ctrl.Call(m, "Vips3", arg0, arg1)
ret0, _ := ret[0].(*VipsReply)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Vips3 indicates an expected call of Vips3
func (mr *MockAccountServerMockRecorder) Vips3(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Vips3", reflect.TypeOf((*MockAccountServer)(nil).Vips3), arg0, arg1)
}

View File

@@ -0,0 +1,19 @@
package api
const (
_normalVip = 1 //月度大会员
_annualVip = 2 //年度会员
_statusAvailable = 1 //未过期
_statusFrozen = 2 //冻结
)
// IsValid decide the user is valid vip or not.
func (v *VipInfo) IsValid() bool {
return v.Status == _statusAvailable && (v.Type == _normalVip || v.Type == _annualVip)
}
// IsFrozen decide the user is frozen vip or not.
func (v *VipInfo) IsFrozen() bool {
return v.Status == _statusFrozen
}

View File

@@ -0,0 +1,46 @@
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 = ["account-service-example.toml"],
importpath = "go-common/app/service/main/account/cmd",
tags = ["automanaged"],
deps = [
"//app/service/main/account/conf:go_default_library",
"//app/service/main/account/rpc/server:go_default_library",
"//app/service/main/account/server/grpc:go_default_library",
"//app/service/main/account/server/http:go_default_library",
"//app/service/main/account/service:go_default_library",
"//library/log:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/rpc/warden/resolver/livezk: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,145 @@
# This is a TOML document. Boom.
version = "3.0.0"
user = "nobody"
pid = "/tmp/account-service.pid"
dir = "./"
perf = "127.0.0.1:6070"
[host]
passportURI = "http://uat-passport.bilibili.co"
vipURI = "http://uat-vip.bilibili.co"
[wardenServer]
addr = "0.0.0.0:6077"
timeout = "1s"
[liveZK]
addrs = ["172.18.33.131:2181", "172.18.33.168:2181", "172.18.33.169:2181"]
timeout = "1s"
[log]
dir = "/data/log/account-service/"
[log.agent]
taskID = "000069"
proto = "unixgram"
addr = "/var/run/lancer/collector.sock"
chan = 10240
[tracer]
family = "account-service"
proto = "unixgram"
addr = "/var/run/dapper-collect/dapper-collect.sock"
[bm]
[bm.inner]
addr = "0.0.0.0:6071"
timeout = "1s"
[bm.local]
addr = "0.0.0.0:6072"
timeout = "1s"
[rpcServer2]
discoverOff = false
token = "123456"
[[rpcServer2.servers]]
proto = "tcp"
addr = "0.0.0.0:6079"
weight = 10
group = "test"
[rpcServer2.zookeeper]
root = "/microservice/account-service-2/"
addrs = ["172.18.33.172:2181"]
timeout = "1s"
[httpClient]
[httpClient.read]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[httpClient.read.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.read.url]
"http://api.bilibili.co/x/internal/v3/account/privacy" = {key = "7c7ac0db1aa05587", secret = "9a6d62d93290c5f771ad381e9ca23f26"}
[httpClient.write]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[httpClient.write.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.privacy]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[httpClient.privacy.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify]
[identify.host]
auth = "http://passport.bilibili.com"
secret = "http://open.bilibili.com"
[identify.httpClient]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "30ms"
timeout = "100ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[memcache]
accountExpire = "3s"
[memcache.account]
name = "account-service/account"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[relationRPC]
timeout = "1s"
[memberRPC]
timeout = "1s"
[coinRPC]
timeout = "1s"
[suitRPC]
timeout = "1s"
[appkeyFilter]
whiteKeys = ["all"]

View File

@@ -0,0 +1,78 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/main/account/conf"
rpc "go-common/app/service/main/account/rpc/server"
"go-common/app/service/main/account/server/grpc"
"go-common/app/service/main/account/server/http"
"go-common/app/service/main/account/service"
"go-common/library/log"
"go-common/library/net/rpc/warden"
"go-common/library/net/rpc/warden/resolver/livezk"
"go-common/library/net/trace"
)
const (
discoveryID = "account.service"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
trace.Init(conf.Conf.Tracer)
defer trace.Close()
log.Info("account-service start")
// service init
svr := service.New(conf.Conf)
rpcSvr := rpc.New(conf.Conf, svr)
// warden init
var wardensvr *warden.Server
if conf.Conf.WardenServer != nil {
var err error
if wardensvr, err = grpc.Start(conf.Conf, svr); err != nil {
panic(fmt.Sprintf("start warden server fail! %s", err))
}
cancel, err := livezk.Register(conf.Conf.LiveZK, conf.Conf.WardenServer.Addr, discoveryID)
if err != nil {
panic(fmt.Sprintf("register grpc service into live zookeeper error: %s", err))
}
defer cancel()
}
http.Init(conf.Conf, svr)
// signal handler
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("account-service get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("account-service exit")
rpcSvr.Close()
if wardensvr != nil {
wardensvr.Shutdown(context.Background())
}
time.Sleep(time.Second)
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,41 @@
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/account/conf",
tags = ["automanaged"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/conf:go_default_library",
"//library/database/elastic:go_default_library",
"//library/log:go_default_library",
"//library/naming/livezk: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/rpc/warden:go_default_library",
"//library/net/trace: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,131 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/elastic"
"go-common/library/log"
"go-common/library/naming/livezk"
bm "go-common/library/net/http/blademaster"
v "go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
var (
confPath string
// Conf common conf
Conf = &Config{}
client *conf.Client
)
//Config config struct
type Config struct {
Host *Host
// log
Log *log.Config
// http
BM *bm.ServerConfig
RPCServer *rpc.ServerConfig
// http client
HTTPClient HTTPClient
// identify
// Identify *identify.Config
// mc
Memcache *Memcache
// tracer
Tracer *trace.Config
// rpc
MemberRPC *rpc.ClientConfig
MemberGRPC *warden.ClientConfig
RelationRPC *rpc.ClientConfig
CoinRPC *rpc.ClientConfig
SuitRPC *rpc.ClientConfig
BlockRPC *rpc.ClientConfig
// warden
WardenServer *warden.ServerConfig
LiveZK *livezk.Zookeeper
// Elastic config
Elastic *elastic.Config
Verify *v.Config
AppkeyFilter *AppkeyFilter
}
// AppkeyFilter is.
type AppkeyFilter struct {
Privacy []string
}
// Host host.
type Host struct {
AccountURI string
VipURI string
PassportURI string
}
// HTTPClient config
type HTTPClient struct {
Read *bm.ClientConfig
Write *bm.ClientConfig
Privacy *bm.ClientConfig
}
// Memcache config
type Memcache struct {
Account *memcache.Config
AccountExpire time.Duration
}
func configCenter() (err error) {
if client, err = conf.New(); err != nil {
panic(err)
}
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("<account-service.toml> is not exists")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config, maybe <account-service.toml> file err")
}
*Conf = *tmpConf
return
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init config
func Init() (err error) {
if confPath == "" {
return configCenter()
}
_, err = toml.DecodeFile(confPath, &Conf)
return
}

View File

@@ -0,0 +1,83 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.cache.go",
"dao.go",
"mc.go",
"passport.go",
"raw.go",
"realname.go",
"search.go",
"vip.go",
],
importpath = "go-common/app/service/main/account/dao",
tags = ["automanaged"],
deps = [
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/conf:go_default_library",
"//app/service/main/account/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/usersuit/model:go_default_library",
"//app/service/main/usersuit/rpc/client:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/elastic:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao.cache_test.go",
"dao_test.go",
"mc_test.go",
"passport_test.go",
"raw_test.go",
"search_test.go",
"vip_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/account/conf:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/member/model:go_default_library",
"//app/service/main/member/model/block:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,376 @@
// Code generated by $GOPATH/src/go-common/app/tool/cache/gen. DO NOT EDIT.
/*
Package dao is a generated cache proxy package.
It is generated from:
type _cache interface {
Info(c context.Context, key int64) (*v1.Info, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Infos(c context.Context, keys []int64) (map[int64]*v1.Info, error)
Card(c context.Context, key int64) (*v1.Card, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Cards(c context.Context, keys []int64) (map[int64]*v1.Card, error)
Vip(c context.Context, key int64) (*v1.VipInfo, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Vips(c context.Context, keys []int64) (map[int64]*v1.VipInfo, error)
Profile(c context.Context, key int64) (*v1.Profile, error)
}
*/
package dao
import (
"context"
"sync"
v1 "go-common/app/service/main/account/api"
"go-common/library/stat/prom"
"go-common/library/sync/errgroup"
)
var _ _cache
// Info get data from cache if miss will call source method, then add to cache.
func (d *Dao) Info(c context.Context, id int64) (res *v1.Info, err error) {
addCache := true
res, err = d.CacheInfo(c, id)
if err != nil {
addCache = false
err = nil
}
if res != nil {
prom.CacheHit.Incr("Info")
return
}
prom.CacheMiss.Incr("Info")
res, err = d.RawInfo(c, id)
if err != nil {
return
}
miss := res
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheInfo(ctx, id, miss)
})
return
}
// Infos get data from cache if miss will call source method, then add to cache.
func (d *Dao) Infos(c context.Context, keys []int64) (res map[int64]*v1.Info, err error) {
if len(keys) == 0 {
return
}
addCache := true
res, err = d.CacheInfos(c, keys)
if err != nil {
addCache = false
res = nil
err = nil
}
var miss []int64
for _, key := range keys {
if (res == nil) || (res[key] == nil) {
miss = append(miss, key)
}
}
prom.CacheHit.Add("Infos", int64(len(keys)-len(miss)))
if len(miss) == 0 {
return
}
var missData map[int64]*v1.Info
missLen := len(miss)
prom.CacheMiss.Add("Infos", int64(missLen))
mutex := sync.Mutex{}
for i := 0; i < missLen; i += 50 * 10 {
var subKeys []int64
group := &errgroup.Group{}
ctx := c
if (i + 50*10) > missLen {
subKeys = miss[i:]
} else {
subKeys = miss[i : i+50*10]
}
missSubLen := len(subKeys)
for j := 0; j < missSubLen; j += 50 {
var ks []int64
if (j + 50) > missSubLen {
ks = subKeys[j:]
} else {
ks = subKeys[j : j+50]
}
group.Go(func() (err error) {
data, err := d.RawInfos(ctx, ks)
mutex.Lock()
for k, v := range data {
if missData == nil {
missData = make(map[int64]*v1.Info, len(keys))
}
missData[k] = v
}
mutex.Unlock()
return
})
}
err1 := group.Wait()
if err1 != nil {
err = err1
}
}
if res == nil {
res = make(map[int64]*v1.Info)
}
for k, v := range missData {
res[k] = v
}
if err != nil {
return
}
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheInfos(ctx, missData)
})
return
}
// Card cache: -batch=50 -max_group=10 -batch_err=continue
func (d *Dao) Card(c context.Context, id int64) (res *v1.Card, err error) {
addCache := true
res, err = d.CacheCard(c, id)
if err != nil {
addCache = false
err = nil
}
if res != nil {
prom.CacheHit.Incr("Card")
return
}
prom.CacheMiss.Incr("Card")
res, err = d.RawCard(c, id)
if err != nil {
return
}
miss := res
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheCard(ctx, id, miss)
})
return
}
// Cards get data from cache if miss will call source method, then add to cache.
func (d *Dao) Cards(c context.Context, keys []int64) (res map[int64]*v1.Card, err error) {
if len(keys) == 0 {
return
}
addCache := true
res, err = d.CacheCards(c, keys)
if err != nil {
addCache = false
res = nil
err = nil
}
var miss []int64
for _, key := range keys {
if (res == nil) || (res[key] == nil) {
miss = append(miss, key)
}
}
prom.CacheHit.Add("Cards", int64(len(keys)-len(miss)))
if len(miss) == 0 {
return
}
var missData map[int64]*v1.Card
missLen := len(miss)
prom.CacheMiss.Add("Cards", int64(missLen))
mutex := sync.Mutex{}
for i := 0; i < missLen; i += 50 * 10 {
var subKeys []int64
group := &errgroup.Group{}
ctx := c
if (i + 50*10) > missLen {
subKeys = miss[i:]
} else {
subKeys = miss[i : i+50*10]
}
missSubLen := len(subKeys)
for j := 0; j < missSubLen; j += 50 {
var ks []int64
if (j + 50) > missSubLen {
ks = subKeys[j:]
} else {
ks = subKeys[j : j+50]
}
group.Go(func() (err error) {
data, err := d.RawCards(ctx, ks)
mutex.Lock()
for k, v := range data {
if missData == nil {
missData = make(map[int64]*v1.Card, len(keys))
}
missData[k] = v
}
mutex.Unlock()
return
})
}
err1 := group.Wait()
if err1 != nil {
err = err1
}
}
if res == nil {
res = make(map[int64]*v1.Card)
}
for k, v := range missData {
res[k] = v
}
if err != nil {
return
}
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheCards(ctx, missData)
})
return
}
// Vip cache: -batch=50 -max_group=10 -batch_err=continue
func (d *Dao) Vip(c context.Context, id int64) (res *v1.VipInfo, err error) {
addCache := true
res, err = d.CacheVip(c, id)
if err != nil {
addCache = false
err = nil
}
if res != nil {
prom.CacheHit.Incr("Vip")
return
}
prom.CacheMiss.Incr("Vip")
res, err = d.RawVip(c, id)
if err != nil {
return
}
miss := res
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheVip(ctx, id, miss)
})
return
}
// Vips get data from cache if miss will call source method, then add to cache.
func (d *Dao) Vips(c context.Context, keys []int64) (res map[int64]*v1.VipInfo, err error) {
if len(keys) == 0 {
return
}
addCache := true
res, err = d.CacheVips(c, keys)
if err != nil {
addCache = false
res = nil
err = nil
}
var miss []int64
for _, key := range keys {
if (res == nil) || (res[key] == nil) {
miss = append(miss, key)
}
}
prom.CacheHit.Add("Vips", int64(len(keys)-len(miss)))
if len(miss) == 0 {
return
}
var missData map[int64]*v1.VipInfo
missLen := len(miss)
prom.CacheMiss.Add("Vips", int64(missLen))
mutex := sync.Mutex{}
for i := 0; i < missLen; i += 50 * 10 {
var subKeys []int64
group := &errgroup.Group{}
ctx := c
if (i + 50*10) > missLen {
subKeys = miss[i:]
} else {
subKeys = miss[i : i+50*10]
}
missSubLen := len(subKeys)
for j := 0; j < missSubLen; j += 50 {
var ks []int64
if (j + 50) > missSubLen {
ks = subKeys[j:]
} else {
ks = subKeys[j : j+50]
}
group.Go(func() (err error) {
data, err := d.RawVips(ctx, ks)
mutex.Lock()
for k, v := range data {
if missData == nil {
missData = make(map[int64]*v1.VipInfo, len(keys))
}
missData[k] = v
}
mutex.Unlock()
return
})
}
err1 := group.Wait()
if err1 != nil {
err = err1
}
}
if res == nil {
res = make(map[int64]*v1.VipInfo)
}
for k, v := range missData {
res[k] = v
}
if err != nil {
return
}
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheVips(ctx, missData)
})
return
}
// Profile cache: -batch=50 -max_group=10 -batch_err=continue
func (d *Dao) Profile(c context.Context, id int64) (res *v1.Profile, err error) {
addCache := true
res, err = d.CacheProfile(c, id)
if err != nil {
addCache = false
err = nil
}
if res != nil {
prom.CacheHit.Incr("Profile")
return
}
prom.CacheMiss.Incr("Profile")
res, err = d.RawProfile(c, id)
if err != nil {
return
}
miss := res
if !addCache {
return
}
d.cache.Do(c, func(ctx context.Context) {
d.AddCacheProfile(ctx, id, miss)
})
return
}

View File

@@ -0,0 +1,126 @@
package dao
import (
"context"
"testing"
"go-common/library/ecode"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoInfo(t *testing.T) {
var (
c = context.TODO()
id = int64(1405)
)
convey.Convey("Get base-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Info(c, id)
ctx.Convey("Then err should be nil and res should be not nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoInfos(t *testing.T) {
var (
c = context.TODO()
keys = []int64{110020171, 110019841}
)
convey.Convey("Batch get base-infos from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Infos(c, keys)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoCard(t *testing.T) {
var (
c = context.TODO()
id = int64(110020171)
)
convey.Convey("Get card-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Card(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoCards(t *testing.T) {
var (
c = context.TODO()
keys = []int64{110020171, 110019841}
)
convey.Convey("Batch get card-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Cards(c, keys)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoVip(t *testing.T) {
var (
c = context.TODO()
id = int64(110018881)
)
convey.Convey("Get vip-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Vip(c, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAutoRenewVip(t *testing.T) {
var (
c = context.TODO()
autoRenewMid = int64(27515232)
)
convey.Convey("Get vip-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Vip(c, autoRenewMid)
t.Logf("data(%+v)", res)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoVips(t *testing.T) {
var (
c = context.TODO()
keys = []int64{1405, 2205}
)
convey.Convey("Batch get vip-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Vips(c, keys)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoProfile(t *testing.T) {
var (
c = context.TODO()
id = int64(111003471)
)
convey.Convey("Get profile-info from cache if miss will call source method", t, func(ctx convey.C) {
res, err := d.Profile(c, id)
if err == ecode.Degrade {
err = nil
}
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,138 @@
package dao
import (
"context"
"fmt"
"strings"
"time"
v1 "go-common/app/service/main/account/api"
"go-common/app/service/main/account/conf"
member "go-common/app/service/main/member/api/gorpc"
mmodel "go-common/app/service/main/member/model"
usersuit "go-common/app/service/main/usersuit/rpc/client"
"go-common/library/cache/memcache"
"go-common/library/database/elastic"
bm "go-common/library/net/http/blademaster"
"go-common/library/sync/pipeline/fanout"
)
//go:generate $GOPATH/src/go-common/app/tool/cache/gen
type _cache interface {
Info(c context.Context, key int64) (*v1.Info, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Infos(c context.Context, keys []int64) (map[int64]*v1.Info, error)
Card(c context.Context, key int64) (*v1.Card, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Cards(c context.Context, keys []int64) (map[int64]*v1.Card, error)
Vip(c context.Context, key int64) (*v1.VipInfo, error)
//cache: -batch=50 -max_group=10 -batch_err=continue
Vips(c context.Context, keys []int64) (map[int64]*v1.VipInfo, error)
Profile(c context.Context, key int64) (*v1.Profile, error)
}
const (
_nameURL = "/api/member/getInfoByName"
_vipInfoURL = "/internal/v1/user/%d"
_vipMultiInfoURL = "/internal/v1/user/list"
_passportDetailURL = "/intranet/acc/detail"
_passportProfile = "/intranet/acc/queryByMid"
)
// Dao dao.
type Dao struct {
// memcache
mc *memcache.Pool
mcExpire int32
// cache async save
cache *fanout.Fanout
// rpc
mRPC *member.Service
suitRPC *usersuit.Service2
// http
httpR *bm.Client
httpW *bm.Client
httpP *bm.Client
// api
detailURI string
profileURI string
nameURI string
// vip api
vipInfoURI string
vipMultiInfoURI string
//es
es *elastic.Elastic
}
// New new a dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
// account memcache
mc: memcache.NewPool(c.Memcache.Account),
mcExpire: int32(time.Duration(c.Memcache.AccountExpire) / time.Second),
// cache chan
cache: fanout.New("accountServiceCache", fanout.Worker(1), fanout.Buffer(1024)),
// rpc
// mRPC: member.New(c.MemberRPC),
mRPC: member.New(c.MemberRPC),
suitRPC: usersuit.New(c.SuitRPC),
// http read&write client
httpR: bm.NewClient(c.HTTPClient.Read),
httpW: bm.NewClient(c.HTTPClient.Write),
httpP: bm.NewClient(c.HTTPClient.Privacy),
es: elastic.NewElastic(c.Elastic),
// api
nameURI: c.Host.AccountURI + _nameURL,
// vip api
vipInfoURI: c.Host.VipURI + _vipInfoURL,
vipMultiInfoURI: c.Host.VipURI + _vipMultiInfoURL,
//passport
detailURI: c.Host.PassportURI + _passportDetailURL,
profileURI: c.Host.PassportURI + _passportProfile,
}
return
}
// LevelExp get member level exp.
func (d *Dao) LevelExp(c context.Context, mid int64) (lexp *mmodel.LevelInfo, err error) {
lexp, err = d.mRPC.Exp(c, &mmodel.ArgMid2{Mid: mid})
return
}
// AddMoral add moral.
func (d *Dao) AddMoral(c context.Context, arg *mmodel.ArgUpdateMoral) (err error) {
return d.mRPC.AddMoral(c, arg)
}
// UpdateExp update exp.
func (d *Dao) UpdateExp(c context.Context, arg *mmodel.ArgAddExp) error {
return d.mRPC.UpdateExp(c, arg)
}
// Ping check connection success.
func (d *Dao) Ping(c context.Context) (err error) {
conn := d.mc.Get(c)
err = conn.Set(&memcache.Item{
Key: "ping",
Value: []byte("pong"),
})
conn.Close()
return
}
// Close close memcache resource.
func (d *Dao) Close() {
if d.mc != nil {
d.mc.Close()
}
}
func fullImage(mid int64, image string) string {
if len(image) == 0 {
return ""
}
if strings.HasPrefix(image, "http://") {
return image
}
return fmt.Sprintf("http://i%d.hdslb.com%s", mid%3, image)
}

View File

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

View File

@@ -0,0 +1,364 @@
package dao
import (
"context"
"strconv"
"github.com/pkg/errors"
v1 "go-common/app/service/main/account/api"
mc "go-common/library/cache/memcache"
)
const (
_prefixInfo = "i3_"
_prefixCard = "c3_"
_prefixVip = "v3_"
_prefixProfile = "p3_"
)
func keyInfo(mid int64) string {
return _prefixInfo + strconv.FormatInt(mid, 10)
}
func keyCard(mid int64) string {
return _prefixCard + strconv.FormatInt(mid, 10)
}
func keyVip(mid int64) string {
return _prefixVip + strconv.FormatInt(mid, 10)
}
func keyProfile(mid int64) string {
return _prefixProfile + strconv.FormatInt(mid, 10)
}
// CacheInfo get account info from cache.
func (d *Dao) CacheInfo(c context.Context, mid int64) (v *v1.Info, err error) {
key := keyInfo(mid)
conn := d.mc.Get(c)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao cache info")
return
}
v = &v1.Info{}
if err = conn.Scan(r, v); err != nil {
err = errors.Wrap(err, "dao cache scan info")
}
return
}
// AddCacheInfo set account info into cache.
func (d *Dao) AddCacheInfo(c context.Context, mid int64, v *v1.Info) (err error) {
item := &mc.Item{
Key: keyInfo(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
conn := d.mc.Get(c)
err = conn.Set(item)
conn.Close()
if err != nil {
err = errors.Wrap(err, "dao add info cache")
}
return
}
// CacheInfos multi get account info from cache.
func (d *Dao) CacheInfos(c context.Context, mids []int64) (res map[int64]*v1.Info, err error) {
keys := make([]string, 0, len(mids))
keyMidMap := make(map[string]int64, len(mids))
for _, mid := range mids {
key := keyInfo(mid)
if _, ok := keyMidMap[key]; !ok {
// duplicate mid
keyMidMap[key] = mid
keys = append(keys, key)
}
}
conn := d.mc.Get(c)
defer conn.Close()
rs, err := conn.GetMulti(keys)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao infos cache")
return
}
res = make(map[int64]*v1.Info, len(mids))
for _, r := range rs {
ai := &v1.Info{}
conn.Scan(r, ai)
res[ai.Mid] = ai
}
return
}
// AddCacheInfos set account infos cache.
func (d *Dao) AddCacheInfos(c context.Context, im map[int64]*v1.Info) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for _, i := range im {
item := &mc.Item{
Key: keyInfo(i.Mid),
Object: i,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
err = conn.Set(item)
if err != nil {
err = errors.Wrap(err, "dao add infos cache")
}
}
return
}
// CacheCard get account card from cache.
func (d *Dao) CacheCard(c context.Context, mid int64) (v *v1.Card, err error) {
key := keyCard(mid)
conn := d.mc.Get(c)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao cache card")
return
}
v = &v1.Card{}
if err = conn.Scan(r, v); err != nil {
err = errors.Wrap(err, "dao cache scan card")
}
return
}
// AddCacheCard set account card into cache.
func (d *Dao) AddCacheCard(c context.Context, mid int64, v *v1.Card) (err error) {
item := &mc.Item{
Key: keyCard(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
conn := d.mc.Get(c)
err = conn.Set(item)
conn.Close()
if err != nil {
err = errors.Wrap(err, "dao add card cache")
}
return
}
// CacheCards multi get account cards from cache.
func (d *Dao) CacheCards(c context.Context, mids []int64) (res map[int64]*v1.Card, err error) {
keys := make([]string, 0, len(mids))
keyMidMap := make(map[string]int64, len(mids))
for _, mid := range mids {
key := keyCard(mid)
if _, ok := keyMidMap[key]; !ok {
// duplicate mid
keyMidMap[key] = mid
keys = append(keys, key)
}
}
conn := d.mc.Get(c)
defer conn.Close()
rs, err := conn.GetMulti(keys)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao cards cache")
return
}
res = make(map[int64]*v1.Card, len(mids))
for _, r := range rs {
ai := &v1.Card{}
conn.Scan(r, ai)
res[ai.Mid] = ai
}
return
}
// AddCacheCards set account cards cache.
func (d *Dao) AddCacheCards(c context.Context, cm map[int64]*v1.Card) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for _, card := range cm {
item := &mc.Item{
Key: keyCard(card.Mid),
Object: card,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
err = conn.Set(item)
if err != nil {
err = errors.Wrap(err, "dao add cards cache")
}
}
return
}
// CacheVip get vip cache.
func (d *Dao) CacheVip(c context.Context, mid int64) (v *v1.VipInfo, err error) {
key := keyVip(mid)
conn := d.mc.Get(c)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao vip cache")
return
}
v = new(v1.VipInfo)
if err = conn.Scan(r, v); err != nil {
err = errors.Wrap(err, "dao vip cache scan")
}
return
}
// AddCacheVip set vip cache.
func (d *Dao) AddCacheVip(c context.Context, mid int64, v *v1.VipInfo) (err error) {
conn := d.mc.Get(c)
conn.Set(&mc.Item{
Key: keyVip(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
})
conn.Close()
if err != nil {
err = errors.Wrap(err, "dao vip add cache")
}
return
}
// CacheVips multi get account cards from cache.
func (d *Dao) CacheVips(c context.Context, mids []int64) (res map[int64]*v1.VipInfo, err error) {
keys := make([]string, 0, len(mids))
keyMidMap := make(map[string]int64, len(mids))
for _, mid := range mids {
key := keyVip(mid)
if _, ok := keyMidMap[key]; !ok {
// duplicate mid
keyMidMap[key] = mid
keys = append(keys, key)
}
}
conn := d.mc.Get(c)
defer conn.Close()
rs, err := conn.GetMulti(keys)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao vips cache")
return
}
res = make(map[int64]*v1.VipInfo, len(mids))
for _, r := range rs {
ai := &v1.VipInfo{}
conn.Scan(r, ai)
res[keyMidMap[r.Key]] = ai
}
return
}
// AddCacheVips set account vips cache.
func (d *Dao) AddCacheVips(c context.Context, vm map[int64]*v1.VipInfo) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for mid, v := range vm {
item := &mc.Item{
Key: keyVip(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
}
err = conn.Set(item)
if err != nil {
err = errors.Wrap(err, "dao add vips cache")
}
}
return
}
// CacheProfile get profile cache.
func (d *Dao) CacheProfile(c context.Context, mid int64) (v *v1.Profile, err error) {
key := keyProfile(mid)
conn := d.mc.Get(c)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == mc.ErrNotFound {
err = nil
return
}
err = errors.Wrap(err, "dao profile cache")
return
}
v = new(v1.Profile)
if err = conn.Scan(r, v); err != nil {
err = errors.Wrap(err, "dao profile cache scan")
}
return
}
// AddCacheProfile set profile cache.
func (d *Dao) AddCacheProfile(c context.Context, mid int64, v *v1.Profile) (err error) {
conn := d.mc.Get(c)
conn.Set(&mc.Item{
Key: keyProfile(mid),
Object: v,
Flags: mc.FlagProtobuf,
Expiration: d.mcExpire,
})
conn.Close()
if err != nil {
err = errors.Wrap(err, "dao profile add cache")
}
return
}
// DelCache delete cache.
func (d *Dao) DelCache(c context.Context, mid int64) []error {
conn := d.mc.Get(c)
errs := make([]error, 0, 5)
if err := conn.Delete(keyInfo(mid)); err != nil {
errs = append(errs, errors.Wrap(err, keyInfo(mid)))
}
if err := conn.Delete(keyCard(mid)); err != nil {
errs = append(errs, errors.Wrap(err, keyCard(mid)))
}
if err := conn.Delete(keyVip(mid)); err != nil {
errs = append(errs, errors.Wrap(err, keyVip(mid)))
}
if err := conn.Delete(keyProfile(mid)); err != nil {
errs = append(errs, errors.Wrap(err, keyProfile(mid)))
}
if err := conn.Close(); err != nil {
errs = append(errs, errors.Wrap(err, "conn close"))
}
d.cache.Do(c, func(ctx context.Context) {
d.Info(ctx, mid)
d.Card(ctx, mid)
d.Vip(ctx, mid)
d.Profile(ctx, mid)
})
return errs
}

View File

@@ -0,0 +1,397 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/account/model"
mc "go-common/library/cache/memcache"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyInfo(t *testing.T) {
var (
mid = int64(2205)
)
convey.Convey("Generate info-key", t, func(ctx convey.C) {
p1 := keyInfo(mid)
ctx.Convey("Then info-key should contains info prefix.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldContainSubstring, _prefixInfo)
})
})
}
func TestDaokeyCard(t *testing.T) {
var (
mid = int64(2205)
)
convey.Convey("Generate card-info-key", t, func(ctx convey.C) {
p1 := keyCard(mid)
ctx.Convey("Then card-info-key should contains card prefix.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldContainSubstring, _prefixCard)
})
})
}
func TestDaokeyVip(t *testing.T) {
var (
mid = int64(2205)
)
convey.Convey("Generate vip-info-key", t, func(ctx convey.C) {
p1 := keyVip(mid)
ctx.Convey("Then vip-info-key should contains vip prefix.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldContainSubstring, _prefixVip)
})
})
}
func TestDaokeyProfile(t *testing.T) {
var (
mid = int64(2205)
)
convey.Convey("Generate profile-key", t, func(ctx convey.C) {
p1 := keyProfile(mid)
ctx.Convey("Then profile-key should contains profile prefix.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldContainSubstring, _prefixProfile)
})
})
}
func TestDaoCacheInfo(t *testing.T) {
var (
c = context.TODO()
mid = int64(2205)
)
convey.Convey("Get member base-info from cache", t, func(ctx convey.C) {
_, err := d.CacheInfo(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddCacheInfo(t *testing.T) {
var (
c = context.TODO()
mid = int64(2205)
v = &model.Info{
Mid: 2205,
Name: "Haha",
Sex: "男",
Face: "http://i1.hdslb.com/bfs/face/4b12a3e65d344e31a11e6425767863019738c7bc.jpg",
Sign: "来电只是",
Rank: 500,
}
)
convey.Convey("Add member base-info to cache", t, func(ctx convey.C) {
err := d.AddCacheInfo(c, mid, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheInfos(t *testing.T) {
var (
c = context.TODO()
mids = []int64{2205, 2805}
)
convey.Convey("Batch get members' base-info", t, func(ctx convey.C) {
res, err := d.CacheInfos(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddCacheInfos(t *testing.T) {
var (
c = context.TODO()
im = map[int64]*model.Info{
2205: {
Mid: 2205,
Name: "板桥真菜",
Sex: "2",
Face: "/bfs/face/e93098c3aa8c18b24001740e707ebe2df180f5f7.jpg",
Sign: "没有",
Rank: 10000,
},
3305: {
Mid: 3305,
Name: "FGNB",
Sex: "1",
Face: "/bfs/face/e93098c3aa8c18b24001740e707ebe2df180f5f7.jpg",
Sign: "啦啦",
Rank: 5000,
},
}
)
convey.Convey("Batch set members' base-info to cache", t, func(ctx convey.C) {
err := d.AddCacheInfos(c, im)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheCard(t *testing.T) {
var (
c = context.TODO()
mid = int64(2805)
)
convey.Convey("Get card-info from cache", t, func(ctx convey.C) {
_, err := d.CacheCard(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddCacheCard(t *testing.T) {
var (
c = context.TODO()
mid = int64(2805)
v = &model.Card{
Mid: 10920044,
Name: "冠冠爱看书",
Sex: "男",
Face: "http://i1.hdslb.com/bfs/face/4b12a3e65d344e31a11e6425767863019738c7bc.jpg",
Sign: "来点字",
Rank: 10000,
Level: 5, //等级
Silence: 0,
Vip: model.VipInfo{
Type: 2,
Status: 1,
DueDate: 162930240,
},
Pendant: model.PendantInfo{
Pid: 159,
Name: "2018拜年祭",
Image: "http://i2.hdslb.com/bfs/face/aace621fa64a698f2ca94d13645a26e9a7a99ed2.png",
Expire: 1566367231,
},
Nameplate: model.NameplateInfo{
Nid: 7,
Name: "见习搬运工",
Image: "http://i1.hdslb.com/bfs/face/8478fb7c54026cd47f09daa493a1b1683113a90d.png",
ImageSmall: "http://i0.hdslb.com/bfs/face/50eef47c3a30a75659d3cc298cfb09031d1a2ce5.png",
Level: "普通勋章",
Condition: "转载视频",
},
}
)
convey.Convey("Add card-info to cache", t, func(ctx convey.C) {
err := d.AddCacheCard(c, mid, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheCards(t *testing.T) {
var (
c = context.TODO()
mids = []int64{110017381, 110019061, 110020081}
)
convey.Convey("Batch get card-info from cache", t, func(ctx convey.C) {
res, err := d.CacheCards(c, mids)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddCacheCards(t *testing.T) {
var (
c = context.TODO()
card1 = &model.Card{
Mid: 10920044,
Name: "冠冠爱看书",
Sex: "男",
Face: "http://i1.hdslb.com/bfs/face/4b12a3e65d344e31a11e6425767863019738c7bc.jpg",
Sign: "来点字",
Rank: 10000,
Level: 5, //等级
Silence: 0,
Vip: model.VipInfo{
Type: 2,
Status: 1,
DueDate: 162930240,
},
Pendant: model.PendantInfo{
Pid: 159,
Name: "2018拜年祭",
Image: "http://i2.hdslb.com/bfs/face/aace621fa64a698f2ca94d13645a26e9a7a99ed2.png",
Expire: 1566367231,
},
Nameplate: model.NameplateInfo{
Nid: 7,
Name: "见习搬运工",
Image: "http://i1.hdslb.com/bfs/face/8478fb7c54026cd47f09daa493a1b1683113a90d.png",
ImageSmall: "http://i0.hdslb.com/bfs/face/50eef47c3a30a75659d3cc298cfb09031d1a2ce5.png",
Level: "普通勋章",
Condition: "转载视频",
},
}
cm = map[int64]*model.Card{
card1.Mid: card1,
}
)
convey.Convey("Batch set card-info to cache", t, func(ctx convey.C) {
err := d.AddCacheCards(c, cm)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheVip(t *testing.T) {
var (
c = context.TODO()
mid = int64(110003731)
)
convey.Convey("Get vip-info from cache", t, func(ctx convey.C) {
_, err := d.CacheVip(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddCacheVip(t *testing.T) {
var (
c = context.TODO()
mid = int64(110003731)
v = &model.VipInfo{
Type: 2,
Status: 1,
DueDate: 162930240,
}
)
convey.Convey("Set vip-cache to cache", t, func(ctx convey.C) {
err := d.AddCacheVip(c, mid, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheVips(t *testing.T) {
var (
c = context.TODO()
mids = []int64{110002741, 110004601, 110006251}
)
convey.Convey("Batch get vip-infos from cache", t, func(ctx convey.C) {
res, err := d.CacheVips(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoAddCacheVips(t *testing.T) {
var (
c = context.TODO()
vm = map[int64]*model.VipInfo{
110007391: {
Type: 2,
Status: 1,
DueDate: 162930240,
},
110010271: {
Type: 2,
Status: 1,
DueDate: 162930240,
},
}
)
convey.Convey("Batch set vip-infos to cache", t, func(ctx convey.C) {
err := d.AddCacheVips(c, vm)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoCacheProfile(t *testing.T) {
var (
c = context.TODO()
mid = int64(110011831)
)
convey.Convey("Get profile-info from cache", t, func(ctx convey.C) {
_, err := d.CacheProfile(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoAddCacheProfile(t *testing.T) {
var (
c = context.TODO()
mid = int64(110011951)
v = &model.Profile{
Mid: 10920044,
Name: "冠冠爱看书",
Sex: "男",
Face: "http://i1.hdslb.com/bfs/face/4b12a3e65d344e31a11e6425767863019738c7bc.jpg",
Sign: "来点字",
Rank: 10000,
Level: 5,
JoinTime: 1503296503,
Moral: 71,
Silence: 0,
EmailStatus: 1,
TelStatus: 1,
Identification: 0,
Vip: model.VipInfo{
Type: 2,
Status: 1,
DueDate: 1629302400000,
},
Pendant: model.PendantInfo{
Pid: 159,
Name: "2018拜年祭",
Image: "http://i2.hdslb.com/bfs/face/aace621fa64a698f2ca94d13645a26e9a7a99ed2.png",
Expire: 1551413548,
},
Nameplate: model.NameplateInfo{
Nid: 7,
Name: "见习搬运工",
Image: "http://i1.hdslb.com/bfs/face/8478fb7c54026cd47f09daa493a1b1683113a90d.png",
ImageSmall: "http://i0.hdslb.com/bfs/face/50eef47c3a30a75659d3cc298cfb09031d1a2ce5.png",
Level: "普通勋章",
Condition: "转载视频投稿通过总数>=10",
},
}
)
convey.Convey("Set profile-info to cache", t, func(ctx convey.C) {
err := d.AddCacheProfile(c, mid, v)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoDelCache(t *testing.T) {
var (
c = context.TODO()
mid = int64(110014081)
)
convey.Convey("Delete member's cache", t, func(ctx convey.C) {
errs := d.DelCache(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
for _, e := range errs {
if e != mc.ErrNotFound {
ctx.So(e, convey.ShouldBeNil)
}
}
})
})
}

View File

@@ -0,0 +1,64 @@
package dao
import (
"context"
"net/url"
"strconv"
"go-common/app/service/main/account/model"
"go-common/library/ecode"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// PassportDetail get detail.
func (d *Dao) PassportDetail(c context.Context, mid int64) (res *model.PassportDetail, err error) {
ip := metadata.String(c, metadata.RemoteIP)
params := url.Values{}
// params.Set("access_key", accessKey)
params.Set("mid", strconv.FormatInt(mid, 10))
var resp struct {
Code int `json:"code"`
Info *model.PassportDetail `json:"data"`
}
req, err := d.httpR.NewRequest("GET", d.detailURI, ip, params)
if err != nil {
err = errors.Wrap(err, "dao passport detail")
return
}
// req.Header.Set("Cookie", cookie)
req.Header.Set("X-BACKEND-BILI-REAL-IP", ip)
if err = d.httpR.Do(c, req, &resp); err != nil {
err = errors.Wrap(err, "dao passport detail")
return
}
if resp.Code != 0 {
err = ecode.Int(resp.Code)
err = errors.Wrap(err, "dao passport detail")
return
}
res = resp.Info
return
}
// PassportProfile is.
func (d *Dao) PassportProfile(c context.Context, mid int64, ip string) (res *model.PassportProfile, err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
var resp struct {
Code int `json:"code"`
Data *model.PassportProfile `json:"data"`
}
if err = d.httpP.Get(c, d.profileURI, ip, params, &resp); err != nil {
err = errors.Wrap(err, "dao passport profile")
return nil, err
}
if resp.Code != 0 {
err = ecode.Int(resp.Code)
err = errors.WithStack(err)
return
}
res = resp.Data
return
}

View File

@@ -0,0 +1,22 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoPassportDetail(t *testing.T) {
var (
c = context.TODO()
mid = int64(110016931)
)
convey.Convey("Get passport detail", t, func(ctx convey.C) {
res, err := d.PassportDetail(c, mid)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,389 @@
package dao
import (
"context"
v1 "go-common/app/service/main/account/api"
"go-common/app/service/main/account/model"
mml "go-common/app/service/main/member/model"
bml "go-common/app/service/main/member/model/block"
sml "go-common/app/service/main/usersuit/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
"github.com/pkg/errors"
)
// RawInfo raw info.
func (d *Dao) RawInfo(c context.Context, mid int64) (res *v1.Info, err error) {
var base *mml.BaseInfo
if base, err = d.mRPC.Base(c, &mml.ArgMemberMid{Mid: mid}); err != nil {
// err = errors.Wrap(err, "dao raw info")
// err = errors.WithStack(err)
return
}
res = &v1.Info{
Mid: base.Mid,
Name: base.Name,
Sex: base.SexStr(),
Face: base.Face,
Sign: base.Sign,
Rank: int32(base.Rank),
}
return
}
// RawInfos raw infos.
func (d *Dao) RawInfos(c context.Context, mids []int64) (res map[int64]*v1.Info, err error) {
var bm map[int64]*mml.BaseInfo
if bm, err = d.mRPC.Bases(c, &mml.ArgMemberMids{Mids: mids}); err != nil {
// err = errors.Wrap(err, "dao raw info")
err = errors.WithStack(err)
return
}
res = map[int64]*v1.Info{}
for _, base := range bm {
i := &v1.Info{
Mid: base.Mid,
Name: base.Name,
Sex: base.SexStr(),
Face: base.Face,
Sign: base.Sign,
Rank: int32(base.Rank),
}
res[i.Mid] = i
}
return
}
// RawCard get card by mid.
func (d *Dao) RawCard(c context.Context, mid int64) (res *v1.Card, err error) {
eg, _ := errgroup.WithContext(c)
var mb *mml.Member
eg.Go(func() (e error) {
if mb, e = d.mRPC.Member(c, &mml.ArgMemberMid{Mid: mid}); e != nil {
log.Error("d.mRPC.Member(%d) err(%v)", mid, e)
// e = ecode.Degrade
}
return
})
var medal *sml.MedalInfo
eg.Go(func() (e error) {
if medal, e = d.suitRPC.MedalActivated(c, &sml.ArgMid{Mid: mid}); e != nil {
log.Error("s.suitRPC.MedalActivated(%d) err %v", mid, e)
e = ecode.Degrade
}
return
})
var block *bml.RPCResInfo
eg.Go(func() (e error) {
if block, e = d.mRPC.BlockInfo(c, &bml.RPCArgInfo{MID: mid}); e != nil {
log.Error("d.block.BlockInfo(%d) err %v", mid, e)
e = ecode.Degrade
}
return
})
var vip *v1.VipInfo
eg.Go(func() (e error) {
if vip, e = d.Vip(c, mid); e != nil {
log.Error("d.Vip(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
var pendant *sml.PendantEquip
eg.Go(func() (e error) {
if pendant, e = d.suitRPC.Equipment(c, &sml.ArgEquipment{Mid: mid}); e != nil {
log.Error("d.suitRPC.Equipment(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
if err = eg.Wait(); err != nil && err != ecode.Degrade {
return
}
card := &v1.Card{Mid: mid}
if medal != nil {
card.Nameplate.Nid = int(medal.ID)
card.Nameplate.Name = medal.Name
card.Nameplate.Image = medal.Image
card.Nameplate.ImageSmall = medal.ImageSmall
card.Nameplate.Level = medal.LevelDesc
card.Nameplate.Condition = medal.Condition
}
if block != nil {
card.Silence = blockStatusToSilence(block.BlockStatus)
}
if mb != nil {
card.Name = mb.Name
card.Sign = mb.Sign
card.Sex = mb.SexStr()
card.Rank = int32(mb.Rank)
card.Face = mb.Face
if mb.OfficialInfo != nil {
// card.Official = *mb.OfficialInfo
card.Official.DeepCopyFromOfficialInfo(mb.OfficialInfo)
}
card.Level = mb.Cur
}
if vip != nil {
card.Vip = *vip
}
if pendant != nil {
card.Pendant.Pid = int(pendant.Pid)
card.Pendant.Expire = int(pendant.Expires)
if pendant.Pendant != nil {
card.Pendant.Name = pendant.Pendant.Name
card.Pendant.Image = fullImage(mid, pendant.Pendant.Image)
}
}
res = card
return
}
// RawCards get card by mid.
func (d *Dao) RawCards(c context.Context, mids []int64) (res map[int64]*v1.Card, err error) {
eg, _ := errgroup.WithContext(c)
var medals map[int64]*sml.MedalInfo
eg.Go(func() (e error) {
if medals, e = d.suitRPC.MedalActivatedMulti(c, &sml.ArgMids{Mids: mids}); e != nil {
log.Error("s.suitRPC.MedalActivatedMulti(%v) err %v", mids, e)
e = ecode.Degrade
}
return
})
var blocks map[int64]*bml.RPCResInfo
eg.Go(func() (e error) {
var bs []*bml.RPCResInfo
if bs, e = d.mRPC.BlockBatchInfo(c, &bml.RPCArgBatchInfo{MIDs: mids}); e != nil {
log.Error("d.block.BlockBatchInfo(%v) err %v", mids, e)
e = ecode.Degrade
}
blocks = make(map[int64]*bml.RPCResInfo, len(bs))
for _, block := range bs {
blocks[block.MID] = block
}
return
})
var mbs map[int64]*mml.Member
eg.Go(func() (e error) {
if mbs, e = d.mRPC.Members(c, &mml.ArgMemberMids{Mids: mids}); e != nil {
log.Error("d.mRPC.Members(%v) err(%v)", mids, e)
// e = ecode.Degrade
}
return
})
var vipm map[int64]*v1.VipInfo
eg.Go(func() (e error) {
if vipm, e = d.Vips(c, mids); e != nil {
log.Error("d.CpVips(%v) err(%v)", mids, e)
e = ecode.Degrade
}
return
})
var pendantm map[int64]*sml.PendantEquip
eg.Go(func() (e error) {
if pendantm, e = d.suitRPC.Equipments(c, &sml.ArgEquipments{Mids: mids}); e != nil {
log.Error("d.suitRPC.Equipments(%v) err(%v)", mids, e)
e = ecode.Degrade
}
return
})
if err = eg.Wait(); err != nil && err != ecode.Degrade {
return
}
res = map[int64]*v1.Card{}
for _, mid := range mids {
card := &v1.Card{Mid: mid}
if mb, ok := mbs[mid]; ok && mb != nil {
card.Name = mb.Name
card.Sign = mb.Sign
card.Sex = mb.SexStr()
card.Rank = int32(mb.Rank)
card.Face = mb.Face
if mb.OfficialInfo != nil {
// card.Official = *mb.OfficialInfo
card.Official.DeepCopyFromOfficialInfo(mb.OfficialInfo)
}
card.Level = mb.Cur
} else {
continue
}
if block, ok := blocks[mid]; ok && block != nil {
card.Silence = blockStatusToSilence(block.BlockStatus)
}
if medal, ok := medals[mid]; ok && medal != nil {
card.Nameplate.Nid = int(medal.ID)
card.Nameplate.Name = medal.Name
card.Nameplate.Image = medal.Image
card.Nameplate.ImageSmall = medal.ImageSmall
card.Nameplate.Level = medal.LevelDesc
card.Nameplate.Condition = medal.Condition
}
if vip, ok := vipm[mid]; ok && vip != nil {
card.Vip = *vip
}
if pendant, ok := pendantm[mid]; ok && pendant != nil {
card.Pendant.Pid = int(pendant.Pid)
card.Pendant.Expire = int(pendant.Expires)
if pendant.Pendant != nil {
card.Pendant.Name = pendant.Pendant.Name
card.Pendant.Image = fullImage(mid, pendant.Pendant.Image)
}
}
res[mid] = card
}
return
}
// RawProfile get profile by mid.
func (d *Dao) RawProfile(c context.Context, mid int64) (res *v1.Profile, err error) {
eg, _ := errgroup.WithContext(c)
var detail *model.PassportDetail
eg.Go(func() (e error) {
if detail, e = d.PassportDetail(c, mid); e != nil {
log.Error("d.PassPortDetail(%d) err %v", mid, e)
// e = ecode.Degrade
}
return
})
var mb *mml.Member
eg.Go(func() (e error) {
if mb, e = d.mRPC.Member(c, &mml.ArgMemberMid{Mid: mid}); e != nil {
log.Error("d.mRPC.Member(%d) err(%v)", mid, e)
// e = ecode.Degrade
}
return
})
var moral *mml.Moral
eg.Go(func() (e error) {
if moral, e = d.mRPC.Moral(c, &mml.ArgMemberMid{Mid: mid}); e != nil {
log.Error("d.mRPC.Member(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
var realNameStatus *mml.RealnameStatus
eg.Go(func() (e error) {
if realNameStatus, e = d.mRPC.RealnameStatus(c, &mml.ArgMemberMid{Mid: mid}); e != nil {
log.Error("d.mRPC.RealnameStatus(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
var medal *sml.MedalInfo
eg.Go(func() (e error) {
if medal, e = d.suitRPC.MedalActivated(c, &sml.ArgMid{Mid: mid}); e != nil {
log.Error("s.suitRPC.MedalActivated(%d) err %v", mid, e)
e = ecode.Degrade
}
return
})
var block *bml.RPCResInfo
eg.Go(func() (e error) {
if block, e = d.mRPC.BlockInfo(c, &bml.RPCArgInfo{MID: mid}); e != nil {
log.Error("s.dao.BlockInfo(%d) err %v", mid, e)
e = ecode.Degrade
}
return
})
var vip *v1.VipInfo
eg.Go(func() (e error) {
if vip, e = d.Vip(c, mid); e != nil {
log.Error("d.Vip(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
var pendant *sml.PendantEquip
eg.Go(func() (e error) {
if pendant, e = d.suitRPC.Equipment(c, &sml.ArgEquipment{Mid: mid}); e != nil {
log.Error("d.suitRPC.Equipment(%d) err(%v)", mid, e)
e = ecode.Degrade
}
return
})
if err = eg.Wait(); err != nil && err != ecode.Degrade {
return
}
pfl := &v1.Profile{Mid: mid}
if mb != nil {
pfl.Name = mb.Name
pfl.Sign = mb.Sign
pfl.Sex = mb.SexStr()
pfl.Rank = int32(mb.Rank)
pfl.Face = mb.Face
if mb.OfficialInfo != nil {
// pfl.Official = *mb.OfficialInfo
pfl.Official.DeepCopyFromOfficialInfo(mb.OfficialInfo)
}
pfl.Level = mb.Cur
pfl.Birthday = mb.Birthday
}
if block != nil {
pfl.Silence = blockStatusToSilence(block.BlockStatus)
}
if detail != nil {
pfl.JoinTime = int32(detail.JoinTime)
pfl.EmailStatus = bindEmailStatus(detail.Email, detail.Spacesta)
pfl.TelStatus = bindPhoneStatus(detail.Phone)
pfl.IsTourist = boolToInt32(detail.IsTourist)
}
if realNameStatus != nil {
pfl.Identification = identificationStatus(*realNameStatus)
}
pfl.Moral = parseMoral(moral)
if medal != nil {
pfl.Nameplate.Nid = int(medal.ID)
pfl.Nameplate.Name = medal.Name
pfl.Nameplate.Image = medal.Image
pfl.Nameplate.ImageSmall = medal.ImageSmall
pfl.Nameplate.Level = medal.LevelDesc
pfl.Nameplate.Condition = medal.Condition
}
if vip != nil {
pfl.Vip = *vip
}
if pendant != nil {
pfl.Pendant.Pid = int(pendant.Pid)
pfl.Pendant.Expire = int(pendant.Expires)
if pendant.Pendant != nil {
pfl.Pendant.Name = pendant.Pendant.Name
pfl.Pendant.Image = fullImage(mid, pendant.Pendant.Image)
}
}
res = pfl
return
}
func blockStatusToSilence(status bml.BlockStatus) int32 {
return boolToInt32(status == bml.BlockStatusForever || status == bml.BlockStatusLimit)
}
func bindEmailStatus(email string, spacesta int8) int32 {
return boolToInt32(spacesta > -10 && len(email) > 0)
}
func bindPhoneStatus(phone string) int32 {
return boolToInt32(len(phone) > 0)
}
func parseMoral(moral *mml.Moral) int32 {
m := int32(mml.DefaultMoral)
if moral != nil {
m = int32(moral.Moral)
}
return m / 100
}
func identificationStatus(realNameStatus mml.RealnameStatus) int32 {
return boolToInt32(realNameStatus == mml.RealnameStatusTrue)
}
func boolToInt32(b bool) int32 {
if b {
return 1
}
return 0
}

View File

@@ -0,0 +1,163 @@
package dao
import (
"context"
"testing"
mml "go-common/app/service/main/member/model"
bml "go-common/app/service/main/member/model/block"
"go-common/library/ecode"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRawInfo(t *testing.T) {
var (
c = context.TODO()
mid = int64(110016481)
)
convey.Convey("Get base-info from member-rpc", t, func(ctx convey.C) {
res, err := d.RawInfo(c, mid)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawInfos(t *testing.T) {
var (
c = context.TODO()
mids = []int64{110016811, 110017441}
)
convey.Convey("Batch get base-info from member-rpc", t, func(ctx convey.C) {
res, err := d.RawInfos(c, mids)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawCard(t *testing.T) {
var (
c = context.TODO()
mid = int64(110018101)
)
convey.Convey("Get card-info from member-rpc", t, func(ctx convey.C) {
res, err := d.RawCard(c, mid)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawCards(t *testing.T) {
var (
c = context.TODO()
mids = []int64{110019691, 110019241}
)
convey.Convey("Batch get card-info from member-rpc", t, func(ctx convey.C) {
res, err := d.RawCards(c, mids)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawProfile(t *testing.T) {
var (
c = context.TODO()
mid = int64(110019691)
)
convey.Convey("Get profile-info from member-rpc", t, func(ctx convey.C) {
res, err := d.RawProfile(c, mid)
if err == ecode.Degrade {
err = nil
}
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestDaoblockStatusToSilence(t *testing.T) {
var (
status bml.BlockStatus
)
convey.Convey("Check whether member in block status ", t, func(ctx convey.C) {
p1 := blockStatusToSilence(status)
ctx.Convey("Then p1 should be 0 or 1.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldBeIn, []int32{0, 1})
})
})
}
func TestDaobindEmailStatus(t *testing.T) {
var (
email = "zxfd43622@qq.com"
spacesta = int8(0)
)
convey.Convey("Get member bind-email status", t, func(ctx convey.C) {
p1 := bindEmailStatus(email, spacesta)
ctx.Convey("Then p1 should be 0 or 1.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldBeIn, []int32{0, 1})
})
})
}
func TestDaobindPhoneStatus(t *testing.T) {
var (
phone = "13849920122"
)
convey.Convey("Get member bind-phone status", t, func(ctx convey.C) {
p1 := bindPhoneStatus(phone)
ctx.Convey("Then p1 should be 0 or 1.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldBeIn, []int32{0, 1})
})
})
}
func TestDaoparseMoral(t *testing.T) {
var (
moral = &mml.Moral{
Mid: 3441,
Moral: 7100,
Added: 100,
Deducted: 0,
}
)
convey.Convey("Parse moral value", t, func(ctx convey.C) {
p1 := parseMoral(moral)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoidentificationStatus(t *testing.T) {
var (
realNameStatus = mml.RealnameStatusTrue
)
convey.Convey("Get identification status", t, func(ctx convey.C) {
p1 := identificationStatus(realNameStatus)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldEqual, 1)
})
})
}
func TestDaoboolToInt32(t *testing.T) {
var (
b = true
)
convey.Convey("Convert true to int", t, func(ctx convey.C) {
p1 := boolToInt32(b)
ctx.Convey("Then p1 should be 1.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldEqual, 1)
})
})
}

View File

@@ -0,0 +1,16 @@
package dao
import (
"context"
member "go-common/app/service/main/member/model"
)
// RealnameDetail is.
func (d *Dao) RealnameDetail(c context.Context, mid int64) (detail *member.RealnameDetail, err error) {
req := &member.ArgMemberMid{Mid: mid}
if detail, err = d.mRPC.RealnameDetail(c, req); err != nil {
return
}
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"go-common/app/service/main/account/model"
"github.com/pkg/errors"
)
// MidsByName is.
func (d *Dao) MidsByName(ctx context.Context, names []string) ([]int64, error) {
r := d.es.NewRequest("member_user").
Fields("mid").
Index("user_base").
WhereIn("kwname", names).
Ps(len(names)).
Pn(1)
result := &model.SearchMemberResult{}
if err := r.Scan(ctx, &result); err != nil {
return nil, errors.WithStack(err)
}
return result.Mids(), nil
}

View File

@@ -0,0 +1,22 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoMidsByName(t *testing.T) {
var (
c = context.TODO()
names = []string{"", "一"}
)
convey.Convey("Search mids by name", t, func(ctx convey.C) {
p1, err := d.MidsByName(c, names)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,89 @@
package dao
import (
"context"
"net/url"
v1 "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/net/metadata"
"go-common/library/xstr"
"github.com/pkg/errors"
)
var (
_emptyVipInfos3 = map[int64]*v1.VipInfo{}
)
// RawVip get mid's vip info from account center by vip API.
func (d *Dao) RawVip(c context.Context, mid int64) (vip *v1.VipInfo, err error) {
params := url.Values{}
var res struct {
Code int `json:"code"`
Data struct {
Type int32 `json:"vipType"`
Status int32 `json:"vipStatus"`
DueDate int64 `json:"vipDueDate"`
VipPayType int32 `json:"isAutoRenew"`
} `json:"data"`
}
err = d.httpR.RESTfulGet(c, d.vipInfoURI, metadata.String(c, metadata.RemoteIP), params, &res, mid)
if err != nil {
err = errors.Wrap(err, "dao vip")
return
}
if res.Code != 0 {
err = ecode.Int(res.Code)
err = errors.Wrap(err, "dao vip")
return
}
vip = &v1.VipInfo{
Type: res.Data.Type,
Status: res.Data.Status,
DueDate: res.Data.DueDate,
VipPayType: res.Data.VipPayType,
}
return
}
// RawVips get multi mid's vip info from account center by vip API.
func (d *Dao) RawVips(c context.Context, mids []int64) (res map[int64]*v1.VipInfo, err error) {
params := url.Values{}
params.Set("idList", xstr.JoinInts(mids))
var info struct {
Code int `json:"code"`
Data map[int64]*struct {
Type int32 `json:"vipType"`
Status int32 `json:"vipStatus"`
DueDate int64 `json:"vipDueDate"`
} `json:"data"`
}
err = d.httpR.Get(c, d.vipMultiInfoURI, "", params, &info)
if err != nil {
err = errors.Wrap(err, "dao vip")
return
}
if info.Code != 0 {
err = ecode.Int(info.Code)
err = errors.Wrap(err, "dao vip")
return
}
if len(info.Data) == 0 {
res = _emptyVipInfos3
return
}
res = make(map[int64]*v1.VipInfo, len(info.Data))
for mid, v := range info.Data {
if v == nil {
continue
}
vip := &v1.VipInfo{
Type: v.Type,
Status: v.Status,
DueDate: v.DueDate,
}
res[mid] = vip
}
return
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRawVip(t *testing.T) {
var (
c = context.TODO()
mid = int64(110018671)
)
convey.Convey("Get vip-info from vip-rpc", t, func(ctx convey.C) {
vip, err := d.RawVip(c, mid)
ctx.Convey("Then err should be nil and vip should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(vip, convey.ShouldNotBeNil)
})
})
}
func TestDaoRawVips(t *testing.T) {
var (
c = context.TODO()
mids = []int64{110016841, 110018671}
)
convey.Convey("Batch get vip-infos from vip-rpc", t, func(ctx convey.C) {
res, err := d.RawVips(c, mids)
ctx.Convey("Then err should be nil and res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"acc.go",
"model.go",
"param.go",
"passport.go",
"rpc.go",
],
importpath = "go-common/app/service/main/account/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account/api:go_default_library",
"//app/service/main/member/model:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/account/model/queue:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,130 @@
package model
import (
"strconv"
v1 "go-common/app/service/main/account/api"
mmodel "go-common/app/service/main/member/model"
)
// AccJavaInfo thin infomartion
type AccJavaInfo struct {
Mid int64 `json:"mid"`
Scores int32 `json:"scores"`
JoinTime int32 `json:"jointime"`
Silence int32 `json:"silence"`
EmailStatus int32 `json:"email_status"`
TelStatus int32 `json:"tel_status"`
Identification int32 `json:"identification"`
Moral int32 `json:"moral"`
Nameplate struct {
Nid int `json:"nid"`
Name string `json:"name"`
Image string `json:"image"`
ImageSmall string `json:"image_small"`
Level string `json:"level"`
Condition string `json:"condition"`
} `json:"nameplate"`
}
// OldInfo old info.
type OldInfo struct {
Mid string `json:"mid"`
Name string `json:"uname"`
Sex string `json:"sex"`
Sign string `json:"sign"`
Avatar string `json:"avatar"`
Rank string `json:"rank"`
DisplayRank string `json:"DisplayRank"`
LevelInfo mmodel.LevelInfo `json:"level_info"`
Official OldOfficial `json:"official_verify"`
Vip v1.VipInfo `json:"vip"`
}
// OldOfficial old official.
type OldOfficial struct {
Type int8 `json:"type"`
Desc string `json:"desc"`
}
// CvtOfficial is used to convert to old official.
func CvtOfficial(o v1.OfficialInfo) OldOfficial {
old := OldOfficial{}
if o.Role == 0 {
old.Type = -1
} else {
if o.Role <= 2 {
old.Type = 0
} else {
old.Type = 1
}
old.Desc = o.Title
}
return old
}
// Info old info -> info.
func (oi *OldInfo) Info() *v1.Info {
mid, _ := strconv.ParseInt(oi.Mid, 10, 64)
rank, _ := strconv.ParseInt(oi.Rank, 10, 64)
i := &v1.Info{
Mid: mid,
Name: oi.Name,
Sex: oi.Sex,
Face: oi.Avatar,
Sign: oi.Sign,
Rank: int32(rank),
}
return i
}
// Relation relation.
type Relation struct {
Following bool `json:"following"`
}
// ProfileStat profile with stat.
type ProfileStat struct {
*v1.Profile
LevelExp mmodel.LevelInfo `json:"level_exp"`
Coins float64 `json:"coins"`
Following int64 `json:"following"`
Follower int64 `json:"follower"`
}
// SearchMemberResult is.
type SearchMemberResult struct {
Order string `json:"order"`
Sort string `json:"sort"`
Result []struct {
Mid int64 `json:"mid"`
} `json:"result"`
Page Page `json:"page"`
}
// Privacy .
type Privacy struct {
Realname string `json:"realname"`
IdentityCard string `json:"identity_card"`
IdentitySex string `json:"identity_sex"`
Tel string `json:"tel"`
RegIP string `json:"reg_ip"`
RegTS int64 `json:"reg_ts"`
HandIMG string `json:"hand_img"`
}
// Page page.
type Page struct {
Num int `json:"num"`
Size int `json:"size"`
Total int `json:"total"`
}
// Mids is.
func (r *SearchMemberResult) Mids() []int64 {
mids := make([]int64, 0, len(r.Result))
for _, r := range r.Result {
mids = append(mids, r.Mid)
}
return mids
}

View File

@@ -0,0 +1,33 @@
package model
import (
v1 "go-common/app/service/main/account/api"
)
// VipInfo is the type alias v1.VipInfo
// DEPRECATED: using v1.VipInfo
type VipInfo = v1.VipInfo
// Info is the type alias v1.Info
// DEPRECATED: using v1.Info
type Info = v1.Info
// Profile is the type alias v1.Profile
// DEPRECATED: using v1.Profile
type Profile = v1.Profile
// Card is the type alias v1.Card
// DEPRECATED: using v1.Card
type Card = v1.Card
// PendantInfo is the type alias v1.PendantInfo
// DEPRECATED: using v1.PendantInfo
type PendantInfo = v1.PendantInfo
// NameplateInfo is the type alias v1.NameplateInfo
// DEPRECATED: using v1.NameplateInfo
type NameplateInfo = v1.NameplateInfo
// OfficialInfo is the type alias v1.OfficialInfo
// DEPRECATED: using v1.OfficialInfo
type OfficialInfo = v1.OfficialInfo

View File

@@ -0,0 +1,28 @@
package model
// ParamMid is.
type ParamMid struct {
Mid int64 `form:"mid" validate:"gt=0,required"`
}
// ParamMids is.
type ParamMids struct {
Mids []int64 `form:"mids,split" validate:"gt=0,dive,gt=0"`
}
// ParamNames is.
type ParamNames struct {
Names []string `form:"names,split" validate:"gt=0,dive,gt=0"`
}
// ParamModify is.
type ParamModify struct {
Mid int64 `form:"mid" validate:"gt=0,required"`
ModifiedAttr string `form:"modifiedAttr" validate:"gt=0,required"`
}
// ParamMsg is.
type ParamMsg struct {
// by notify
Msg string `form:"msg"`
}

View File

@@ -0,0 +1,33 @@
package model
import (
xtime "go-common/library/time"
)
//PassportDetail detail.
type PassportDetail struct {
Mid int64 `json:"mid"`
Email string `json:"email"`
Phone string `json:"telphone"`
Spacesta int8 `json:"spacesta"`
JoinTime int64 `json:"join_time"`
IsTourist bool `json:"is_tourist"`
}
//PassportProfile .
type PassportProfile struct {
Mid int64 `json:"mid"`
UName string `json:"uname"`
UserID string `json:"user_id"`
Telphone string `json:"telphone"`
NickLock int `json:"nick_lock"`
BindQQ bool `json:"bind_qq"`
BindSina bool `json:"bind_sina"`
SpaceSta int `json:"spacesta"`
LoginTime xtime.Time `json:"login_time"`
LoginIP string `json:"login_ip"`
JoinIP string `json:"join_ip"`
JoinTime xtime.Time `json:"join_time"`
SafeQuestion int `json:"safe_question"`
CountryCode int64 `json:"country_code"`
}

View File

@@ -0,0 +1,5 @@
#! /bin/sh
# proto.sh https://github.com/google/protobuf/releases 下载release包解压后将include中的文件夹拖到/usr/local/include即可
gopath=$GOPATH/src
gogopath=$GOPATH/src/go-common/vendor/github.com/gogo/protobuf
protoc --gofast_out=. --proto_path=/usr/local/include:$gopath:$gogopath:. vip.proto info.proto card.proto profile.proto usersuit.proto

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"error.go",
"priority_queue.go",
"queue.go",
"ring.go",
],
importpath = "go-common/app/service/main/account/model/queue",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

Some files were not shown because too many files have changed in this diff Show More