Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/openplatform/abtest:all-srcs",
"//app/service/openplatform/anti-fraud:all-srcs",
"//app/service/openplatform/pgc-season:all-srcs",
"//app/service/openplatform/ticket-item:all-srcs",
"//app/service/openplatform/ticket-sales:all-srcs",
],
tags = ["automanaged"],
)

View File

@@ -0,0 +1,9 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- haoguanwei
- maojian
labels:
- new-project
- openplatform
- service

View File

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

View File

@@ -0,0 +1,11 @@
##### Version 0.1.0
> 1. 使用sync map优化读取性能
##### Version 0.0.2
> 1. 内部自身调用,去除鉴权服务
> 2. 添加abtest管理端的分组功能
##### Version 0.0.1
> 1. A/B 实验 后台服务

View File

@@ -0,0 +1,10 @@
# Owner
lijiadong
qiuliang
# Author
lijiadong
luhao
# Reviewer
qiuliang

View File

@@ -0,0 +1,16 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- lijiadong
- luhao
- qiuliang
labels:
- openplatform
- service
- service/openplatform/abtest
options:
no_parent_owners: true
reviewers:
- lijiadong
- luhao
- qiuliang

View File

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

View File

@@ -0,0 +1,42 @@
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 = ["open-abtest.toml"],
importpath = "go-common/app/service/openplatform/abtest/cmd",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/http:go_default_library",
"//app/service/openplatform/abtest/service:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

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

View File

@@ -0,0 +1,96 @@
# This is a TOML document. Boom.
[log]
dir = "/data/log/abtest"
[db]
[db.ab]
addr = "172.16.33.203:3306"
dsn = "root:123456@tcp(172.16.33.203:3306)/tickets?timeout=5s&readTimeout=30s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "2s"
[db.ab.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[bm]
[bm.inner]
addr = "0.0.0.0:8801"
timeout = "2s"
[bm.outer]
addr = "0.0.0.0:8802"
timeout = "2s"
[redis]
name = "abtest-service"
proto = "tcp"
addr = "172.16.33.203:6379"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
verifyCdTimes = "2h"
[identify]
whiteAccessKey = ""
whiteMid = 0
csrfOn = false
[identify.app]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
[identify.host]
auth = "http://passport.bilibili.co"
secret = "http://open.bilibili.co"
[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"}
[httpClient]
[httpClient.read]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "4s"
keepAlive = "60s"
timer = 1000
[httpClient.read.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[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

View File

@@ -0,0 +1,37 @@
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/openplatform/abtest/conf",
tags = ["automanaged"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/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,115 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/trace"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
// Conf global variable.
var (
Conf = &Config{}
client *conf.Client
confPath string
)
// Config struct of conf.
type Config struct {
// base
// log
Log *log.Config
// db
DB *DB
// redis
Redis *Redis
// http
BM *HTTPServers
// http client
HTTPClient HTTPClient
// tracer
Tracer *trace.Config
//stat
Stat int
Stra int
}
// DB db config.
type DB struct {
Ab *sql.Config
}
// Redis conf.
type Redis struct {
*redis.Config
Expire xtime.Duration
VerifyCdTimes xtime.Duration
}
// HTTPServers Http Servers
type HTTPServers struct {
Inner *bm.ServerConfig
Outer *bm.ServerConfig
}
// HTTPClient config
type HTTPClient struct {
Read *bm.ClientConfig
Write *bm.ClientConfig
}
func outer() (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 event")
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Value("open-abtest.toml"); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init int config
func Init() error {
if confPath != "" {
return outer()
}
return remote()
}

View File

@@ -0,0 +1,60 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"abtest_test.go",
"dao_test.go",
"group_test.go",
"redis_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/model:go_default_library",
"//library/cache/redis:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"abtest.go",
"dao.go",
"group.go",
"redis.go",
],
importpath = "go-common/app/service/openplatform/abtest/dao",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,225 @@
package dao
import (
"context"
xsql "database/sql"
"encoding/json"
"fmt"
"go-common/app/service/openplatform/abtest/model"
"go-common/library/cache/redis"
"go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_selAllByStatus = "SELECT `id`,`name`,`desc`,`stra`,`seed`,`result`,`status`,`version`,`group`,`author_name`,`modifier_name`,`ctime`,`mtime` FROM `abtest` WHERE `deleted`=0 AND `status` IN (%s) ORDER BY mtime DESC LIMIT %d, %d"
_selAllByStatusAndGroup = "SELECT `id`,`name`,`desc`,`stra`,`seed`,`result`,`status`,`version`,`group`,`author_name`,`modifier_name`,`ctime`,`mtime` FROM `abtest` WHERE `group`=? AND `deleted`=0 AND `status` IN (%s) ORDER BY mtime DESC LIMIT %d, %d"
_selActByGroup = "SELECT `id`,`name`,`desc`,`stra`,`seed`,`result`,`status`,`version`,`group`,`author_name`,`modifier_name`,`ctime`,`mtime` FROM `abtest` WHERE `group`=? AND `status`=1 AND `deleted`=0"
_selCnt = "SELECT count(*) as `count` FROM `abtest` WHERE `deleted`=0 AND `status` IN (%s)"
_selCntByGroup = "SELECT count(*) as `count` FROM `abtest` WHERE `group`=? AND `deleted`=0 AND `status` IN (%s)"
_selByID = "SELECT `id`,`name`,`desc`,`stra`,`seed`,`result`,`status`,`version`,`group`,`author_name`,`modifier_name`,`ctime`,`mtime` FROM `abtest` WHERE `id`=? AND `deleted`=0"
_selByIDAndGroup = "SELECT `id`,`name`,`desc`,`stra`,`seed`,`result`,`status`,`version`,`group`,`author_name`,`modifier_name`,`ctime`,`mtime` FROM `abtest` WHERE `id`=? AND `group`=? AND `deleted`=0"
_insAB = "INSERT INTO `abtest` (`name`,`desc`,`stra`,`seed`,`result`,`status`,`group`,`author_name`,`modifier_name`) VALUES(?,?,?,?,?,0,?,?,?)"
_upAB = "UPDATE `abtest` SET `name`=?,`desc`=?,`stra`=?,`result`=?,`modifier_name`=?,`version`=? WHERE `id`=?"
_upStatus = "UPDATE `abtest` SET `status`=?,`modifier_name`=? WHERE `id`=?"
_delAB = "UPDATE `abtest` SET `deleted`=1 WHERE `id`=? AND `status`!=1"
)
//ActByGroup 根据group获取当前激活项目
func (d *Dao) ActByGroup(c context.Context, group int) (res []*model.AB, err error) {
var (
rows *sql.Rows
straStr string
)
if rows, err = d.db.Query(c, _selActByGroup, group); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ActByGroup] d.db.Query err: %v", err))
return
}
defer rows.Close()
res = make([]*model.AB, 0)
for rows.Next() {
ele := &model.AB{}
if err = rows.Scan(&ele.ID, &ele.Name, &ele.Desc, &straStr, &ele.Seed, &ele.Result, &ele.Status, &ele.Version, &ele.Group, &ele.Author, &ele.Modifier, &ele.CreateTime, &ele.ModifyTime); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ActByGroup] rows.Scan err: %v", err))
return
}
if err = json.Unmarshal([]byte(straStr), &ele.Stra); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ActByGroup] json.Unmarshal err: %v", err))
return
}
res = append(res, ele)
}
return
}
//Ab 获取单个测试
func (d *Dao) Ab(c context.Context, id int) (res *model.AB, err error) {
var straStr string
row := d.db.QueryRow(c, _selByID, id)
res = &model.AB{}
if err = row.Scan(&res.ID, &res.Name, &res.Desc, &straStr, &res.Seed, &res.Result, &res.Status, &res.Version, &res.Group, &res.Author, &res.Modifier, &res.CreateTime, &res.ModifyTime); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|Ab] row.Scan err: %v", err))
return
}
err = json.Unmarshal([]byte(straStr), &res.Stra)
return
}
//AbByIDAndGroup 获取单个测试
func (d *Dao) AbByIDAndGroup(c context.Context, id int, group int) (res *model.AB, err error) {
var (
straStr string
row *sql.Row
)
row = d.db.QueryRow(c, _selByIDAndGroup, id, group)
res = &model.AB{}
if err = row.Scan(&res.ID, &res.Name, &res.Desc, &straStr, &res.Seed, &res.Result, &res.Status, &res.Version, &res.Group, &res.Author, &res.Modifier, &res.CreateTime, &res.ModifyTime); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|AbByIDAndGroup] row.Scan err: %v", err))
return
}
err = json.Unmarshal([]byte(straStr), &res.Stra)
return
}
//CountAb 获取测试数量
func (d *Dao) CountAb(c context.Context, mstatus string) (count int, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selCnt, mstatus))
if err = row.Scan(&count); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|CountAb] row.Scan err: %v", err))
return
}
return
}
//CountAbByGroup 获取测试数量
func (d *Dao) CountAbByGroup(c context.Context, mstatus string, group int) (count int, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selCntByGroup, mstatus), group)
if err = row.Scan(&count); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|CountAbByGroup] row.Scan err: %v", err))
return
}
return
}
//ListAb 分页获取所有测试列表
func (d *Dao) ListAb(c context.Context, offset, size int, mstatus string) (res []*model.AB, count int, err error) {
var (
rows *sql.Rows
straStr string
)
if rows, err = d.db.Query(c, fmt.Sprintf(_selAllByStatus, mstatus, offset, size)); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAb] d.db.Query err: %v", err))
return
}
defer rows.Close()
res = make([]*model.AB, 0)
for rows.Next() {
ele := &model.AB{}
if err = rows.Scan(&ele.ID, &ele.Name, &ele.Desc, &straStr, &ele.Seed, &ele.Result, &ele.Status, &ele.Version, &ele.Group, &ele.Author, &ele.Modifier, &ele.CreateTime, &ele.ModifyTime); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAb] rows.Scan err: %v", err))
return
}
err = json.Unmarshal([]byte(straStr), &ele.Stra)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAb] json.Unmarshal err: %v", err))
continue
}
res = append(res, ele)
}
count, err = d.CountAb(c, mstatus)
return
}
//ListAbByGroup 分页获取分组测试列表
func (d *Dao) ListAbByGroup(c context.Context, offset, size int, mstatus string, group int) (res []*model.AB, count int, err error) {
var (
rows *sql.Rows
straStr string
)
if rows, err = d.db.Query(c, fmt.Sprintf(_selAllByStatusAndGroup, mstatus, offset, size), group); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAbByGroup] d.db.Query err: %v", err))
return
}
defer rows.Close()
res = make([]*model.AB, 0)
for rows.Next() {
ele := &model.AB{}
if err = rows.Scan(&ele.ID, &ele.Name, &ele.Desc, &straStr, &ele.Seed, &ele.Result, &ele.Status, &ele.Version, &ele.Group, &ele.Author, &ele.Modifier, &ele.CreateTime, &ele.ModifyTime); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAbByGroup] rows.Scan err: %v", err))
return
}
err = json.Unmarshal([]byte(straStr), &ele.Stra)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.abtest|ListAbByGroup] json.Unmarshal err: %v", err))
continue
}
res = append(res, ele)
}
count, err = d.CountAbByGroup(c, mstatus, group)
return
}
//AddAb 添加AB实验
func (d *Dao) AddAb(c context.Context, name, desc string, stra string, seed, result, group int, username string) (newID int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _insAB, name, desc, stra, seed, result, group, username, username); err != nil {
return
}
newID, err = res.LastInsertId()
return
}
//DelAb 删除AB实验
func (d *Dao) DelAb(c context.Context, id int) (rowsAffected int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _delAB, id); err != nil {
return
}
return res.RowsAffected()
}
//UpAb 更新AB实验
func (d *Dao) UpAb(c context.Context, id int, name, desc string, stra string, result int, username string, newVersion, status, group int) (rowsAffected int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _upAB, name, desc, stra, result, username, newVersion, id); err != nil {
return
}
return res.RowsAffected()
}
//UpStatus 更新AB实验状态
func (d *Dao) UpStatus(c context.Context, id, status int, username string, group int) (rowsAffected int64, err error) {
var res xsql.Result
if res, err = d.db.Exec(c, _upStatus, status, username, id); err != nil {
return
}
return res.RowsAffected()
}
//Incr stat result, save to redis
func (d *Dao) Incr(c context.Context, key string) {
conn := d.redis.Get(c)
defer conn.Close()
conn.Do("incr", key)
conn.Do("expire", key, 86400*7)
}
//Move backup key
func (d *Dao) Move(c context.Context, key string) {
conn := d.redis.Get(c)
defer conn.Close()
i, _ := redis.Int(conn.Do("get", key))
if _, err := conn.Do("setex", "O:"+key, 86400*7, i); err == nil {
conn.Do("del", key)
}
}
// GetFromRedis .
func (d *Dao) GetFromRedis(c context.Context, key string) (value int, err error) {
conn := d.redis.Get(c)
defer conn.Close()
value, err = redis.Int(conn.Do("get", key))
return
}

View File

@@ -0,0 +1,86 @@
package dao
import (
"context"
"flag"
"fmt"
"math/rand"
"testing"
"go-common/app/service/openplatform/abtest/conf"
"go-common/library/cache/redis"
. "github.com/smartystreets/goconvey/convey"
)
var testID int
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
d = New(conf.Conf)
}
func TestActByGroup(t *testing.T) {
Convey("TestActByGroup: ", t, func() {
_, err := d.ActByGroup(context.TODO(), 1)
So(err, ShouldBeNil)
})
}
func TestListAb(t *testing.T) {
Convey("TestListAb: ", t, func() {
_, _, err := d.ListAb(context.TODO(), 1, 2, "0,1,2")
So(err, ShouldBeNil)
})
}
var testCase = "testxx"
func TestAddAb(t *testing.T) {
Convey("TestAddAb: ", t, func() {
var err error
testID, err = redis.Int(d.AddAb(context.TODO(), "test", "test", `{"precision":100,"ratio":[80,20]}`, rand.Intn(10000000), 0, 1, "test"))
So(err, ShouldBeNil)
So(testID, ShouldNotEqual, 0)
})
}
func TestUpAb(t *testing.T) {
Convey("TestUpAb: ", t, func() {
res, err := d.UpAb(context.TODO(), testID, "test", testCase, `{"precision":100,"ratio":[80,20]}`, 0, "update", 1, 0, 1)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}
func TestAb(t *testing.T) {
Convey("TestAb: ", t, func() {
res, err := d.Ab(context.TODO(), testID)
So(err, ShouldBeNil)
So(res.Desc, ShouldEqual, testCase)
})
}
func TestUpStatus(t *testing.T) {
Convey("TestUpSta: ", t, func() {
res, err := d.UpStatus(context.TODO(), testID, 1, "test", 1)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
Convey("TestUpSta: ", t, func() {
res, err := d.UpStatus(context.TODO(), testID, 0, "test", 1)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}
func TestDelStatus(t *testing.T) {
Convey("TestDelSta: ", t, func() {
res, err := d.DelAb(context.TODO(), testID)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"time"
"go-common/app/service/openplatform/abtest/conf"
"go-common/library/cache/redis"
"go-common/library/database/sql"
"go-common/library/log"
)
// Dao struct answer history of Dao
type Dao struct {
c *conf.Config
// db
db *sql.DB
// redis
redis *redis.Pool
expire int
verifyExpire int
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.Ab),
redis: redis.NewPool(c.Redis.Config),
expire: int(time.Duration(c.Redis.Expire) / time.Second),
verifyExpire: int(time.Duration(c.Redis.VerifyCdTimes) / time.Second),
}
return
}
// Close close connections.
func (d *Dao) Close() {
if d.db != nil {
d.db.Close()
}
if d.redis != nil {
d.redis.Close()
}
}
// Ping ping health.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
log.Error("PingDb error(%v)", err)
return
}
if err = d.PingRedis(c); err != nil {
log.Error("PingRedis error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,17 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var d *Dao
func TestPing(t *testing.T) {
Convey("TestPing: ", t, func() {
err := d.Ping(context.TODO())
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,82 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"go-common/app/service/openplatform/abtest/model"
"go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_addGroup = "INSERT INTO abtest_group (`name`,`desc`,`is_deleted`)VALUES(?, ?, 0)"
_listGroup = "SELECT `id`, `name`, `desc` FROM abtest_group WHERE `is_deleted` = 0 ORDER BY id"
_updateGroup = "UPDATE abtest_group set `name` = ?, `desc` = ? where id = ?"
_deleteGroup = "UPDATE abtest_group set `is_deleted` = 1 WHERE id = ?"
)
//AddGroup add a new group
func (d *Dao) AddGroup(c context.Context, g model.Group) (i int, err error) {
var (
res xsql.Result
)
if res, err = d.db.Exec(c, _addGroup, g.Name, g.Desc); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.group|AddGroup] d.db.Exec err: %v", err))
return
}
return intConv(res.LastInsertId())
}
//UpdateGroup update group by id
func (d *Dao) UpdateGroup(c context.Context, g model.Group) (i int, err error) {
var (
res xsql.Result
)
if res, err = d.db.Exec(c, _updateGroup, g.Name, g.Desc, g.ID); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.group|UpdateGroup] d.db.Exec err: %v", err))
return
}
return intConv(res.RowsAffected())
}
//DeleteGroup delete the group by id
func (d *Dao) DeleteGroup(c context.Context, id int) (i int, err error) {
var (
res xsql.Result
)
if res, err = d.db.Exec(c, _deleteGroup, id); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.group|DeleteGroup] d.db.Exec err: %v", err))
return
}
return intConv(res.RowsAffected())
}
//ListGroup list all groups
func (d *Dao) ListGroup(c context.Context) (res []*model.Group, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(c, _listGroup); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.group|ListGroup] d.db.Query err: %v", err))
return
}
defer rows.Close()
for rows.Next() {
g := &model.Group{}
if err = rows.Scan(&g.ID, &g.Name, &g.Desc); err != nil {
err = errors.Wrap(err, fmt.Sprintf("[dao.group|ListGroup] d.db.Query err: %v", err))
return
}
res = append(res, g)
}
return
}
func intConv(i int64, err error) (int, error) {
if err != nil {
return 0, err
}
return int(i), nil
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"testing"
"go-common/app/service/openplatform/abtest/model"
. "github.com/smartystreets/goconvey/convey"
)
var g = model.Group{
Name: "test",
Desc: "test add",
}
var testDesc = "test update"
func TestAddGroup(t *testing.T) {
Convey("TestAddGroup: ", t, func() {
var err error
testID, err = d.AddGroup(context.TODO(), g)
So(err, ShouldBeNil)
})
}
func TestUpdateGroup(t *testing.T) {
g.Desc = testDesc
g.ID = testID
Convey("TestUpdateGroup: ", t, func() {
res, err := d.UpdateGroup(context.TODO(), g)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}
func TestListGroup(t *testing.T) {
Convey("TestListGroup: ", t, func() {
res, err := d.ListGroup(context.TODO())
So(err, ShouldBeNil)
x := res[len(res)-1]
So(x.Name, ShouldEqual, g.Name)
})
}
func TestDeleteGroup(t *testing.T) {
Convey("TestDeleteGroup: ", t, func() {
res, err := d.DeleteGroup(context.TODO(), testID)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}

View File

@@ -0,0 +1,44 @@
package dao
import (
"context"
"fmt"
"go-common/library/cache/redis"
)
const (
_keyVersionID = "abtest:versionid:%d"
)
// PingRedis check redis connection
func (d *Dao) PingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("PING")
return
}
//RedisVersionID 获取redis中的分组版本
func (d *Dao) RedisVersionID(c context.Context, group int) (ver int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
ver, err = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, group)))
return
}
//SetnxRedisVersionID 使用v设置redis中的版本号
func (d *Dao) SetnxRedisVersionID(c context.Context, group int, v int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("SETNX", fmt.Sprintf(_keyVersionID, group), v)
return
}
//UpdateRedisVersionID 使用v更新redis中的分组版本
func (d *Dao) UpdateRedisVersionID(c context.Context, group int, v int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("SETEX", fmt.Sprintf(_keyVersionID, group), d.verifyExpire, v)
return
}

View File

@@ -0,0 +1,99 @@
package dao
import (
"context"
"fmt"
"testing"
"go-common/library/cache/redis"
. "github.com/smartystreets/goconvey/convey"
)
func TestPingRedis(t *testing.T) {
Convey("TestPingRedis: ", t, func() {
err := d.PingRedis(context.TODO())
So(err, ShouldBeNil)
})
}
func TestGetRedisVersionID(t *testing.T) {
var (
ver int64
err error
)
ctx := context.TODO()
conn := d.redis.Get(ctx)
defer conn.Close()
Convey("TestGetRedisVersionID: ", t, func() {
if ver, _ = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1))); ver > 0 {
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
}
conn.Do("SET", fmt.Sprintf(_keyVersionID, 1), 123)
ver, err = d.RedisVersionID(ctx, 1)
So(ver, ShouldEqual, 123)
So(err, ShouldBeNil)
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
})
}
func TestSetnxRedisVersionID(t *testing.T) {
var (
ver int64
err error
)
ctx := context.TODO()
conn := d.redis.Get(ctx)
defer conn.Close()
Convey("TestSetnxRedisVersionID: ", t, func() {
if ver, _ = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1))); ver > 0 {
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
}
err = d.SetnxRedisVersionID(ctx, 1, 146)
So(err, ShouldBeNil)
ver, _ = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1)))
So(ver, ShouldEqual, 146)
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
})
}
func TestUpdateRedisVersionID(t *testing.T) {
var (
ver int64
err error
)
ctx := context.TODO()
conn := d.redis.Get(ctx)
defer conn.Close()
Convey("TestUpdateRedisVersionID: ", t, func() {
if ver, err = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1))); err == nil {
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
So(err, ShouldBeNil)
} else {
So(err, ShouldEqual, redis.ErrNil)
}
ver, err = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1)))
fmt.Println(ver)
So(err, ShouldEqual, redis.ErrNil)
err = d.UpdateRedisVersionID(ctx, 1, 123)
So(err, ShouldBeNil)
ver, err = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1)))
So(err, ShouldBeNil)
So(ver, ShouldEqual, 123)
err = d.UpdateRedisVersionID(ctx, 1, 132)
So(err, ShouldBeNil)
ver, err = redis.Int64(conn.Do("GET", fmt.Sprintf(_keyVersionID, 1)))
So(ver, ShouldEqual, 132)
So(err, ShouldBeNil)
_, err = conn.Do("DEL", fmt.Sprintf(_keyVersionID, 1))
So(err, ShouldBeNil)
})
}
func TestEnd(t *testing.T) {
d.Close()
}

View File

@@ -0,0 +1,60 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"group_test.go",
"http_test.go",
"stra_bench_test.go",
"stra_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/service:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"group.go",
"http.go",
"stra.go",
],
importpath = "go-common/app/service/openplatform/abtest/http",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/model:go_default_library",
"//app/service/openplatform/abtest/model/validator:go_default_library",
"//app/service/openplatform/abtest/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,48 @@
package http
import (
"go-common/app/service/openplatform/abtest/model"
"go-common/app/service/openplatform/abtest/model/validator"
bm "go-common/library/net/http/blademaster"
)
//add group
func addGroup(c *bm.Context) {
params := new(validator.AddGroupParams)
if err := c.Bind(params); err != nil {
return
}
g := model.Group{
Name: params.Name,
Desc: params.Desc,
}
c.JSON(abSvr.AddGroup(c, g))
}
//list group
func listGroup(c *bm.Context) {
c.JSON(abSvr.ListGroup(c))
}
//update group
func updateGroup(c *bm.Context) {
params := new(validator.UpdateGroupParams)
if err := c.Bind(params); err != nil {
return
}
g := model.Group{
ID: params.ID,
Name: params.Name,
Desc: params.Desc,
}
c.JSON(abSvr.UpdateGroup(c, g))
}
//delete group
func deleteGroup(c *bm.Context) {
params := new(validator.DeleteGroupParams)
if err := c.Bind(params); err != nil {
return
}
c.JSON(abSvr.DeleteGroup(c, params.ID))
}

View File

@@ -0,0 +1,118 @@
package http
import (
"context"
"net/url"
"strconv"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
const (
_getGroupListURL = "http://localhost:8801/openplatform/admin/abtest/group/list"
_AddGroupURL = "http://localhost:8801/openplatform/admin/abtest/group/add"
_DelGroupURL = "http://localhost:8801/openplatform/admin/abtest/group/delete"
_UpdateGroupURL = "http://localhost:8801/openplatform/admin/abtest/group/update"
)
var agcs = []TestCase{
TestCase{tag: "TestAddGroup: valid parameters", testData: TestData{"name": "test", "desc": "test add"}, should: Shoulds{0}},
TestCase{tag: "TestAddGroup: empty parameters", testData: TestData{"name": "", "desc": ""}, should: Shoulds{-400}},
TestCase{tag: "TestAddGroup: no parameters", testData: TestData{}, should: Shoulds{-400}},
}
func TestAddGroup(t *testing.T) {
for _, td := range agcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _AddGroupURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data int `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
if res.Code == 0 {
testID = res.Data
}
})
}
}
func TestUpdateGroup(t *testing.T) {
var ugcs = []TestCase{
TestCase{tag: "TestUpdateGroup: valid parameters", testData: TestData{"id": strconv.Itoa(testID), "name": "test", "desc": "test update"}, should: Shoulds{0}},
TestCase{tag: "TestUpdateGroup: empty parameters", testData: TestData{"id": "0", "name": "", "desc": ""}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateGroup: no parameters", testData: TestData{}, should: Shoulds{-400}},
}
for _, td := range ugcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _UpdateGroupURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
func TestDeleteGroup(t *testing.T) {
var dgcs = []TestCase{
TestCase{tag: "TestDeleteGroup: valid parameters", testData: TestData{"id": strconv.Itoa(testID), "name": "test", "desc": "test delete"}, should: Shoulds{0}},
TestCase{tag: "TestDeleteGroup: empty parameters", testData: TestData{"id": "0", "name": "", "desc": ""}, should: Shoulds{-400}},
TestCase{tag: "TestDeleteGroup: no parameters", testData: TestData{}, should: Shoulds{-400}},
}
for _, td := range dgcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _DelGroupURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
func TestListGroup(t *testing.T) {
Convey("TestListGroup: ", t, func() {
params := url.Values{}
req, _ := client.NewRequest("GET", _getGroupListURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, 0)
})
}

View File

@@ -0,0 +1,80 @@
package http
import (
"net/http"
"go-common/app/service/openplatform/abtest/conf"
"go-common/app/service/openplatform/abtest/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
abSvr *service.Service
)
// Init init account service.
func Init(c *conf.Config, s *service.Service) {
abSvr = s
engineInner := bm.DefaultServer(c.BM.Inner)
innerRouter(engineInner)
// init Inner serve
if err := engineInner.Start(); err != nil {
log.Error("bm.DefaultServer error(%v)", err)
panic(err)
}
engineOuter := bm.DefaultServer(c.BM.Outer)
outerRouter(engineOuter)
// init Outer serve
if err := engineOuter.Start(); err != nil {
log.Error("bm.DefaultServer error(%v)", err)
panic(err)
}
}
// innerRouter init inner router api path.
func innerRouter(e *bm.Engine) {
e.Ping(ping)
group := e.Group("/openplatform/internal/abtest")
{
//common
group.GET("/versionid", versionID)
group.GET("/version", version)
}
group = e.Group("/openplatform/admin/abtest")
{
//mng abtest
group.GET("/list", listAb)
group.GET("/add", addAb)
group.GET("/update", updateAb)
group.GET("/status", updateStatus)
group.GET("/delete", deleteAb)
}
group = e.Group("/openplatform/admin/abtest/group")
{
//mng group
group.GET("/add", addGroup)
group.GET("/list", listGroup)
group.GET("/update", updateGroup)
group.GET("/delete", deleteGroup)
}
group = e.Group("/openplatform/admin/abtest/stat")
{
group.GET("/total", total)
}
}
// outerRouter init outer router.
func outerRouter(e *bm.Engine) {
}
// ping check server ok.
func ping(c *bm.Context) {
if err := abSvr.Ping(c); err != nil {
log.Error("open-abtest http service ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,24 @@
package http
import (
"flag"
"fmt"
"go-common/app/service/openplatform/abtest/conf"
"go-common/app/service/openplatform/abtest/service"
httpx "go-common/library/net/http/blademaster"
_ "github.com/smartystreets/goconvey/convey"
)
var client *httpx.Client
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
svr := service.New(conf.Conf)
client = httpx.NewClient(conf.Conf.HTTPClient.Read)
Init(conf.Conf, svr)
}

View File

@@ -0,0 +1,197 @@
package http
import (
"encoding/json"
"math/rand"
"strconv"
"strings"
"time"
"go-common/app/service/openplatform/abtest/model"
"go-common/app/service/openplatform/abtest/model/validator"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
func versionID(c *bm.Context) {
params := new(validator.VerionParams)
if err := c.Bind(params); err != nil {
return
}
c.JSON(abSvr.VersionID(c, params.Group))
}
func version(c *bm.Context) {
var (
key = c.Request.Form.Get("key")
verStr = c.Request.Form.Get("version")
ver = &model.Version{}
groupStr = c.Request.Form.Get("group")
appKey = c.Request.Form.Get("appkey")
group int
err error
)
if verStr != "" {
if err = json.Unmarshal([]byte(verStr), ver); err != nil {
log.Warn("[http.stra|version] json.Unmarshal(%s) err: %v", verStr, err)
}
}
if groupStr != "" {
if group, err = strconv.Atoi(groupStr); err != nil {
log.Warn("[http.stra|version] strconv.Atoi(group:%s) err: %v", groupStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
c.JSON(abSvr.Version(c, group, key, ver, appKey))
}
func listAb(c *bm.Context) {
var (
params = new(validator.ListParams)
data []*model.AB
total int
err error
)
if err = c.Bind(params); err != nil {
return
}
if err = checkStatus(params.Mstatus); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if data, total, err = abSvr.ListAb(c, params.Pn, params.Ps, params.Mstatus, params.Group); err != nil {
log.Error("[http.stra|version] abSvr.ListAb(%d,%d) err: %v", params.Pn, params.Ps, err)
c.JSON(nil, err)
return
}
c.JSON(map[string]interface{}{
"result": data,
"total": total,
}, nil)
}
func addAb(c *bm.Context) {
var (
params = new(validator.AddAbParams)
ab = &model.AB{}
err error
)
if err = c.Bind(params); err != nil {
return
}
// params
if err = json.Unmarshal([]byte(params.Data), ab); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if ab.Group == 0 {
ab.Group = params.Group
}
if ab.Group == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if params.Group != 0 && ab.Group != params.Group {
c.JSON(nil, ecode.RequestErr)
return
}
if !ab.Stra.Check() {
c.JSON(nil, ecode.RequestErr)
return
}
rand.Seed(time.Now().Unix())
c.JSON(abSvr.AddAb(c, ab))
}
func updateAb(c *bm.Context) {
var (
err error
ab *model.AB
srcAb model.AB
)
params := new(validator.UpdateAbParams)
if err = c.Bind(params); err != nil {
return
}
if ab, err = abSvr.Ab(c, params.ID, params.Group); err != nil {
c.JSON(nil, err)
return
}
if params.Group != 0 && ab.Group != params.Group {
c.JSON(nil, ecode.RequestErr)
return
}
srcAb = *ab
if err = json.Unmarshal([]byte(params.Data), ab); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if !ab.Stra.Check() {
c.JSON(nil, ecode.RequestErr)
return
}
ab.Version = srcAb.Version
ab.Status = srcAb.Status
ab.Group = srcAb.Group
c.JSON(abSvr.UpdateAb(c, params.ID, ab))
}
func updateStatus(c *bm.Context) {
var (
ab *model.AB
err error
)
params := new(validator.UpdateStatusAbParams)
if err = c.Bind(params); err != nil {
return
}
if params.Status > 3 || params.Status < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if ab, err = abSvr.Ab(c, params.ID, params.Group); err != nil {
c.JSON(nil, err)
return
}
if params.Group != 0 && ab.Group != params.Group {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(abSvr.UpdateStatus(c, params.ID, params.Status, params.Modifier, ab.Group))
}
func deleteAb(c *bm.Context) {
var (
ab *model.AB
err error
)
params := new(validator.DelAbParams)
if err = c.Bind(params); err != nil {
return
}
if ab, err = abSvr.Ab(c, params.ID, params.Group); err != nil {
c.JSON(nil, err)
return
}
if params.Group != 0 && ab.Group != params.Group {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(abSvr.DeleteAb(c, params.ID))
}
func checkStatus(s string) (err error) {
strs := strings.Split(s, ",")
for _, a := range strs {
if _, err = strconv.Atoi(a); err != nil {
return
}
}
return
}
func total(c *bm.Context) {
c.JSON(abSvr.Total(c))
}

View File

@@ -0,0 +1,29 @@
package http
import (
"context"
"fmt"
"net/url"
"testing"
)
func BenchmarkVersion(b *testing.B) {
for i := 0; i < b.N; i++ {
params := url.Values{}
for k, v := range guscs[0].testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _getVersionURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data struct {
V int `json:"v"`
D interface{} `json:"d"`
} `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
fmt.Println(err)
}
}
}

View File

@@ -0,0 +1,246 @@
package http
import (
"context"
"net/url"
"strconv"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
const (
_getListURL = "http://localhost:8801/openplatform/admin/abtest/list"
_getVersionIDURL = "http://localhost:8801/openplatform/internal/abtest/versionid"
_getVersionURL = "http://localhost:8801/openplatform/internal/abtest/version"
_AddURL = "http://localhost:8801/openplatform/admin/abtest/add"
_DelURL = "http://localhost:8801/openplatform/admin/abtest/delete"
_UpdateURL = "http://localhost:8801/openplatform/admin/abtest/update"
_UpdateStatusURL = "http://localhost:8801/openplatform/admin/abtest/status"
)
type TestData map[string]string
type Shoulds []interface{}
type TestCase struct {
tag string
testData TestData
should Shoulds
}
var gvcs = []TestCase{
TestCase{tag: "TestGetVersionID: valid parameters", testData: TestData{"group": "1"}, should: Shoulds{0}},
TestCase{tag: "TestGetVersionID: empty parameters", testData: TestData{"group": ""}, should: Shoulds{-400}},
TestCase{tag: "TestGetVersionID: invalid parameters", testData: TestData{"group": "asd"}, should: Shoulds{-400}},
TestCase{tag: "TestGetVersionID: no parameters", testData: TestData{}, should: Shoulds{-400}},
}
func TestGetVersionID(t *testing.T) {
for _, td := range gvcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _getVersionIDURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var guscs = []TestCase{
TestCase{tag: "TestGetVersion: valid parameters", testData: TestData{"group": "1", "key": "23232", "version": "{}"}, should: Shoulds{0}},
TestCase{tag: "TestGetVersion: no version", testData: TestData{"group": "1", "key": ""}, should: Shoulds{0}},
TestCase{tag: "TestGetVersion: no key", testData: TestData{"group": "1", "version": "{}"}, should: Shoulds{0}},
TestCase{tag: "TestGetVersion: no group", testData: TestData{"group": "1", "version": "{}"}, should: Shoulds{0}},
}
func TestGetVersion(t *testing.T) {
for _, td := range guscs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _getVersionURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data struct {
V int `json:"v"`
D interface{} `json:"d"`
} `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var glcs = []TestCase{
TestCase{tag: "TestGetListAb: valid parameters", testData: TestData{"pn": "1", "ps": "20", "mstatus": "1,2,0"}, should: Shoulds{0}},
TestCase{tag: "TestGetListAb: no pn", testData: TestData{"ps": "1", "mstatus": "1,2,0"}, should: Shoulds{-400}},
TestCase{tag: "TestGetListAb: no ps", testData: TestData{"pn": "1", "mstatus": "1,2,0"}, should: Shoulds{-400}},
TestCase{tag: "TestGetListAb: no mstatus", testData: TestData{"pn": "1", "ps": "10"}, should: Shoulds{-400}},
TestCase{tag: "TestGetListAb: invalid pn", testData: TestData{"pn": "a", "ps": "20", "mstatus": "1,2,0"}, should: Shoulds{-400}},
TestCase{tag: "TestGetListAb: invalid ps", testData: TestData{"pn": "1", "ps": "a", "mstatus": "1,2,0"}, should: Shoulds{-400}},
TestCase{tag: "TestGetListAb: invalid mstatus", testData: TestData{"pn": "1", "ps": "20", "mstatus": "a"}, should: Shoulds{-400}},
}
func TestGetListAb(t *testing.T) {
for _, td := range glcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _getListURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data struct {
Result interface{} `json:"result"`
Total interface{} `json:"total"`
} `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var testID int
var adcs = []TestCase{
TestCase{tag: "TestAddAb: valid json", testData: TestData{"data": `{"name":"test1","desc":"test","stra":{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`}, should: Shoulds{0}},
TestCase{tag: "TestAddAb: no permission", testData: TestData{"data": `{"name":"test1","desc":"test","stra":{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`, "group": "2"}, should: Shoulds{-400}},
TestCase{tag: "TestAddAb: not json", testData: TestData{"data": `{"name":"test2","desc""test""stra":"{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`}, should: Shoulds{-400}},
TestCase{tag: "TestAddAb: invalid stra", testData: TestData{"data": `{"name":"test3","desc":"test","stra":{"precision":100,"ratio":[20,70]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`}, should: Shoulds{-400}},
TestCase{tag: "TestAddAb: no data", testData: TestData{}, should: Shoulds{-400}},
}
func TestAddAb(t *testing.T) {
for _, td := range adcs {
Convey(td.tag, t, func() {
var res struct {
Code int `json:"code"`
Data struct {
Newid int `json:"newid"`
} `json:"data"`
}
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _AddURL, "127.0.0.1", params)
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
if res.Code == 0 {
testID = res.Data.Newid
}
})
}
}
func TestUpdateAb(t *testing.T) {
var upcs = []TestCase{
TestCase{tag: "TestUpdateAb: valid params", testData: TestData{"id": strconv.Itoa(testID), "data": `{"name":"test","desc":"update","stra":{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`}, should: Shoulds{0}},
TestCase{tag: "TestUpdateAb: no permission", testData: TestData{"id": strconv.Itoa(testID), "data": `{"name":"test1","desc":"test","stra":{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":2,"author":"test","modifer":"test"}`, "group": "2"}, should: Shoulds{-500}},
TestCase{tag: "TestUpdateAb: invalid params", testData: TestData{}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateAb: invalid id", testData: TestData{"id": "11111", "data": "aa"}, should: Shoulds{-500}},
TestCase{tag: "TestUpdateAb: invalid data", testData: TestData{"id": strconv.Itoa(testID), "data": "aa"}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateAb: valid stra", testData: TestData{"id": strconv.Itoa(testID), "data": `{"name":"test","desc":"update","stra":{"precision":100,"ratio":[20,81]},"result":1,"status":0,"group":1,"author":"test","modifer":"test"}`}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateAb: valid params group 0", testData: TestData{"id": strconv.Itoa(testID), "data": `{"name":"test","desc":"update2","stra":{"precision":100,"ratio":[20,80]},"result":1,"status":0,"group":0,"author":"test","modifer":"test"}`}, should: Shoulds{0}},
}
for _, td := range upcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _UpdateURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
func TestUpdateStatusAb(t *testing.T) {
var upscs = []TestCase{
TestCase{tag: "TestUpdateStatusAb: valid params", testData: TestData{"id": strconv.Itoa(testID), "status": "1", "modifier": "test2"}, should: Shoulds{0}},
TestCase{tag: "TestUpdateStatusAb: no permission", testData: TestData{"id": strconv.Itoa(testID), "status": "1", "modifier": "test2", "group": "2"}, should: Shoulds{-500}},
TestCase{tag: "TestUpdateStatusAb: invalid params", testData: TestData{}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateStatusAb: invalid id", testData: TestData{"id": "11111", "data": "aa"}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateStatusAb: invalid status", testData: TestData{"id": strconv.Itoa(testID), "status": "4", "modifier": "test2"}, should: Shoulds{-400}},
TestCase{tag: "TestUpdateStatusAb: valid params", testData: TestData{"id": strconv.Itoa(testID), "status": "3", "modifier": "test2"}, should: Shoulds{0}},
}
for _, td := range upscs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _UpdateStatusURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
func TestDelAb(t *testing.T) {
var dacs = []TestCase{
TestCase{tag: "TestDelAb: no permission", testData: TestData{"id": strconv.Itoa(testID), "group": "2"}, should: Shoulds{-500}},
TestCase{tag: "TestDelAb: valid id", testData: TestData{"id": strconv.Itoa(testID)}, should: Shoulds{0}},
TestCase{tag: "TestDelAb: invalid id", testData: TestData{"id": "x"}, should: Shoulds{-400}},
}
for _, td := range dacs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _DelURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}

View File

@@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["stra_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"abtest.go",
"group.go",
"stra.go",
"version.go",
],
importpath = "go-common/app/service/openplatform/abtest/model",
tags = ["automanaged"],
deps = [
"//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",
"//app/service/openplatform/abtest/model/jump:all-srcs",
"//app/service/openplatform/abtest/model/validator:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,31 @@
package model
import (
"go-common/library/time"
)
// AB AB测试实验
type AB struct {
ID int `json:"id"`
Name string `json:"name"`
Desc string `json:"desc"`
Stra Stra `json:"stra"`
Seed int `json:"seed"`
Result int `json:"result"`
Status int `json:"status"`
Version int `json:"version"`
Group int `json:"group"`
Author string `json:"author"`
Modifier string `json:"modifier"`
CreateTime time.Time `json:"ctime"`
ModifyTime time.Time `json:"mtime"`
}
// Stat .
type Stat struct {
New map[int]map[int]int `json:"now"`
Old map[int]map[int]int `json:"last"`
}
// Empty .
type Empty struct{}

View File

@@ -0,0 +1,8 @@
package model
//Group info of abtest
type Group struct {
ID int
Name string
Desc string
}

View File

@@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["jump_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["jump.go"],
importpath = "go-common/app/service/openplatform/abtest/model/jump",
tags = ["automanaged"],
)
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,30 @@
package jump
import (
"bytes"
"crypto/md5"
"encoding/binary"
)
//Hash get result by hash
func Hash(key uint64, numBuckets int) int32 {
var b int64 = -1
var j int64
for j < int64(numBuckets) {
b = j
key = key*2862933555777941757 + 1
j = int64(float64(b+1) * (float64(int64(1)<<31) / float64((key>>33)+1)))
}
return int32(b)
}
//Md5 get result by Md5
func Md5(key string) uint64 {
var x uint64
s := md5.Sum([]byte(key))
b := bytes.NewBuffer(s[:])
binary.Read(b, binary.BigEndian, &x)
return x
}

View File

@@ -0,0 +1,40 @@
package jump
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
type hashStruct struct {
value uint64
exp int32
}
func Test_Hash(t *testing.T) {
Convey("Test_Hash: ", t, func() {
h := hashStruct{
value: uint64(314978625),
exp: int32(18),
}
bucket := 100
v := Hash(h.value, bucket)
So(v, ShouldEqual, h.exp)
})
}
type md5Struct struct {
value string
exp uint64
}
func Test_Md5(t *testing.T) {
Convey("Test_Hash: ", t, func() {
h := md5Struct{
value: "987654321",
exp: uint64(7979946199622949865),
}
v := Md5(h.value)
So(v, ShouldEqual, h.exp)
})
}

View File

@@ -0,0 +1,53 @@
package model
import (
"errors"
"go-common/library/log"
)
//Stra 实验策略
type Stra struct {
//精度
Precision int `json:"precision"`
//依次比例
Ratio []int `json:"ratio"`
}
func (s *Stra) check() (isValid bool) {
sum := 0
for _, r := range s.Ratio {
sum += r
}
isValid = (sum == s.Precision)
return
}
//Check ensure stra valid
func (s *Stra) Check() (isValid bool) {
return s.check()
}
//Version calculate version by score
func (s *Stra) Version(score int) (version int, err error) {
if !s.check() {
err = errors.New("the sum of ratio is not equal to precision")
log.Error("[model.stra|Version] s.check failed")
return
}
if score >= s.Precision || score < 0 {
err = errors.New("score should between 0 and s.Precision")
return
}
for i, r := range s.Ratio {
if score >= r {
score -= r
} else {
version = i
break
}
}
return
}

View File

@@ -0,0 +1,40 @@
package model
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var stra = []Stra{
Stra{Precision: 100, Ratio: []int{10, 90}},
Stra{Precision: 100, Ratio: []int{10, 9}},
}
func TestCheck(t *testing.T) {
Convey("TestCheck: ", t, func() {
var checks = []bool{true, false}
for i, s := range stra {
got := s.Check()
So(got, ShouldEqual, checks[i])
}
})
}
func TestVersion(t *testing.T) {
testCase := map[int]int{9: 0, 20: 1}
s := stra[0]
Convey("TestVersion: ", t, func() {
for j, k := range testCase {
got, _ := s.Version(j)
So(got, ShouldEqual, k)
}
_, err := s.Version(101)
So(err, ShouldNotBeNil)
_, err = stra[1].Version(101)
So(err, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,30 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"group.go",
"stra.go",
],
importpath = "go-common/app/service/openplatform/abtest/model/validator",
tags = ["automanaged"],
)
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,19 @@
package validator
//AddGroupParams add group params validate
type AddGroupParams struct {
Name string `form:"name" validate:"required"`
Desc string `form:"desc" validate:"required"`
}
//UpdateGroupParams update group params validate
type UpdateGroupParams struct {
ID int `form:"id" validate:"required"`
Name string `form:"name" validate:"required"`
Desc string `form:"desc" validate:"required"`
}
//DeleteGroupParams delete group params validate
type DeleteGroupParams struct {
ID int `form:"id" validate:"required"`
}

View File

@@ -0,0 +1,41 @@
package validator
//VerionParams presents version params
type VerionParams struct {
Group int `form:"group" validate:"min=0,required"`
}
//ListParams presents listAb params
type ListParams struct {
Pn int `form:"pn" validate:"min=1,required"`
Ps int `form:"ps" validate:"min=1,required"`
Mstatus string `form:"mstatus" validate:"required"`
Group int `form:"group"`
}
//AddAbParams presents addAbtest params
type AddAbParams struct {
Data string `form:"data" validate:"required"`
Group int `form:"group"`
}
//UpdateAbParams presents updateAbtest params
type UpdateAbParams struct {
ID int `form:"id" validate:"required"`
Data string `form:"data" validate:"required"`
Group int `form:"group"`
}
//DelAbParams presents deleteAbtest params
type DelAbParams struct {
ID int `form:"id" validate:"required"`
Group int `form:"group"`
}
//UpdateStatusAbParams presents updateStatusAbtest params
type UpdateStatusAbParams struct {
ID int `form:"id" validate:"required"`
Status int `form:"status"`
Modifier string `form:"modifier" validate:"required"`
Group int `form:"group"`
}

View File

@@ -0,0 +1,7 @@
package model
// Version Cookie中保存的版本信息
type Version struct {
VersionID int64 `json:"v"`
Data map[int]int `json:"d"`
}

View File

@@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"abtest_test.go",
"cache_test.go",
"group_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"abtest.go",
"cache.go",
"group.go",
"service.go",
],
importpath = "go-common/app/service/openplatform/abtest/service",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/abtest/conf:go_default_library",
"//app/service/openplatform/abtest/dao:go_default_library",
"//app/service/openplatform/abtest/model:go_default_library",
"//app/service/openplatform/abtest/model/jump:go_default_library",
"//library/cache/redis:go_default_library",
"//library/log: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,250 @@
package service
import (
"context"
"encoding/json"
"math/rand"
"strconv"
"strings"
"time"
"go-common/app/service/openplatform/abtest/model"
"go-common/app/service/openplatform/abtest/model/jump"
"go-common/library/cache/redis"
"go-common/library/log"
)
//resetGroupCache 使用数据库信息重置分组缓存
func (s *Service) resetGroupCache(c context.Context, group int, ver int64) (err error) {
var src []*model.AB
if src, err = s.d.ActByGroup(c, group); err != nil {
return
}
err = s.setGroupCache(c, group, src, ver)
return
}
//groupCache 获取分组缓存
func (s *Service) groupCache(c context.Context, group int) (cache map[int]*model.AB, err error) {
var (
ok bool
ver int64
)
for cache, ok = s.readGroupCache(c, group); !ok; cache, ok = s.readGroupCache(c, group) {
if ver, err = s.d.RedisVersionID(c, group); err != nil {
if err == redis.ErrNil {
if err = s.d.SetnxRedisVersionID(c, group, time.Now().Unix()); err != nil {
log.Error("[service.abtest|groupCache] s.d.SetnxRedisVersionID err: %v", err)
return
}
} else {
log.Error("[service.abtest|groupCache] s.d.RedisVersionID err: %v", err)
return
}
}
s.resetGroupCache(c, group, ver)
}
return
}
func syncStart(s *Service) {
ctx := context.TODO()
for {
s.SyncVersionID(ctx)
time.Sleep(time.Second * 30)
}
}
//SyncVersionID 同步版本
func (s *Service) SyncVersionID(c context.Context) {
var (
err error
redisVer int64
cacheVerList map[int]int64
)
if cacheVerList, err = s.VersionIDListCache(c); err != nil {
log.Error("[service.abtest|SyncVersionID] VersionIDListCache err: %v", err)
return
}
for group, cacheVer := range cacheVerList {
if redisVer, err = s.d.RedisVersionID(c, group); err != nil {
if err == redis.ErrNil {
if err = s.d.SetnxRedisVersionID(c, group, time.Now().Unix()); err != nil {
log.Error("[service.abtest|groupCache] s.d.SetnxRedisVersionID err: %v", err)
return
}
} else {
log.Error("[service.abtest|groupCache] s.d.RedisVersionID err: %v", err)
return
}
}
if redisVer > cacheVer {
if err = s.resetGroupCache(c, group, redisVer); err != nil {
log.Error("[service.abtest|SyncVersionID] resetGroupCache(group:%d) err: %v", group, err)
continue
}
} else if redisVer < cacheVer {
s.d.UpdateRedisVersionID(c, group, cacheVer)
}
}
}
// versionByKey 根据key选择现行AB测试配置
func (s *Service) versionByKey(c context.Context, group int, key string) (abMap map[int]int, err error) {
var cache map[int]*model.AB
abMap = make(map[int]int)
if cache, err = s.groupCache(c, group); err != nil {
log.Error("[service.abtest|versionByKey] s.groupCache err: %v", err)
return
}
for _, ele := range cache {
if ele.Status != 1 || ele.Group != group {
continue
}
if key != "" {
//key不为空时散列后计算得到result
score := int(jump.Hash(jump.Md5(strings.Join([]string{key, string(ele.Seed)}, "")), ele.Stra.Precision))
if abMap[ele.ID], err = ele.Stra.Version(score); err != nil {
log.Error("[service.abtest|versionByKey] stra.Version err: %v", err)
return
}
} else {
abMap[ele.ID] = ele.Result
}
}
return
}
//VersionID returns current version ID
func (s *Service) VersionID(c context.Context, group int) (ver int64, err error) {
return s.versionID(c, group), nil
}
//Version 获取用户AB测试配置
func (s *Service) Version(c context.Context, group int, key string, ver *model.Version, appkey string) (res *model.Version, err error) {
res = ver
nVer := s.versionID(c, group)
if nVer > ver.VersionID {
res.VersionID = nVer
res.Data, err = s.versionByKey(c, group, key)
}
if s.c.Stat == 1 {
go s.stat(c, res, appkey)
// log.Info("key: %s, result: %s", key, res.Data[s.c.Stra])
}
return
}
//Ab 获取单个实验
func (s *Service) Ab(c context.Context, id int, group int) (res *model.AB, err error) {
if group == 0 {
return s.d.Ab(c, id)
}
return s.d.AbByIDAndGroup(c, id, group)
}
//ListAb 获取实验列表
func (s *Service) ListAb(c context.Context, page, pageSize int, mstatus string, group int) (res []*model.AB, total int, err error) {
offset := (page - 1) * pageSize
if group == 0 {
return s.d.ListAb(c, offset, pageSize, mstatus)
}
return s.d.ListAbByGroup(c, offset, pageSize, mstatus, group)
}
//AddAb 添加实验
func (s *Service) AddAb(c context.Context, ab *model.AB) (res map[string]interface{}, err error) {
var (
newID int64
seed int
stra []byte
)
seed = rand.Intn(1000000000)
if stra, err = json.Marshal(ab.Stra); err != nil {
return
}
if newID, err = s.d.AddAb(c, ab.Name, ab.Desc, string(stra), seed, ab.Result, ab.Group, ab.Author); err != nil {
return
}
res = map[string]interface{}{
"newid": newID,
}
return
}
//UpdateAb 更新实验
func (s *Service) UpdateAb(c context.Context, id int, ab *model.AB) (res bool, err error) {
var stra []byte
if stra, err = json.Marshal(ab.Stra); err != nil {
return
}
if _, err = s.d.UpAb(c, id, ab.Name, ab.Desc, string(stra), ab.Result, ab.Modifier, ab.Version+1, ab.Status, ab.Group); err != nil {
return
}
if err = s.resetGroupCache(c, ab.Group, time.Now().Unix()); err != nil {
return
}
res = true
go s.statU(c)
return
}
//UpdateStatus 更新实验状态
func (s *Service) UpdateStatus(c context.Context, id, status int, username string, group int) (res bool, err error) {
if _, err = s.d.UpStatus(c, id, status, username, group); err != nil {
return
}
if err = s.resetGroupCache(c, group, time.Now().Unix()); err != nil {
return
}
res = true
return
}
//DeleteAb 删除实验
func (s *Service) DeleteAb(c context.Context, id int) (res bool, err error) {
var row int64
if row, err = s.d.DelAb(c, id); err != nil {
return
}
if row != 0 {
res = true
}
return
}
func (s *Service) stat(c context.Context, data *model.Version, appkey string) {
for k, v := range data.Data {
key := "STAT:" + appkey + ":" + strconv.Itoa(k) + ":" + strconv.Itoa(v)
if _, ok := s.keyList.Load(key); !ok {
s.keyList.Store(key, model.Empty{})
}
s.d.Incr(c, key)
}
}
func (s *Service) statU(c context.Context) {
s.keyList.Range(func(key interface{}, value interface{}) bool {
s.d.Move(c, key.(string))
return true
})
}
//Total total of abtest result
func (s *Service) Total(c context.Context) (res map[string]map[string]int, err error) {
res0 := make(map[string]int)
res1 := make(map[string]int)
res = make(map[string]map[string]int)
s.keyList.Range(func(key interface{}, value interface{}) bool {
if v, err := s.d.GetFromRedis(c, key.(string)); err == nil {
res0[key.(string)] = v
}
if v, err := s.d.GetFromRedis(c, "O:"+key.(string)); err == nil {
res1[key.(string)] = v
}
res["current"] = res0
res["last"] = res1
return true
})
return
}

View File

@@ -0,0 +1,99 @@
package service
import (
"context"
"math/rand"
"testing"
"go-common/app/service/openplatform/abtest/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
testVer int64
testID int
testAb *model.AB
)
func TestSyncVersionID(t *testing.T) {
Convey("TestSyncVersionID: ", t, func() {
svr.SyncVersionID(context.TODO())
})
}
func TestVersionID(t *testing.T) {
var err error
Convey("TestVersionID: ", t, func() {
testVer, err = svr.VersionID(nil, 1)
So(err, ShouldBeNil)
})
}
func TestVersion(t *testing.T) {
Convey("TestVersion: ", t, func() {
over := &model.Version{}
_, err := svr.Version(context.TODO(), 1, "key", over, "1")
So(err, ShouldBeNil)
})
}
func TestListAb(t *testing.T) {
Convey("TestListAb: ", t, func() {
_, _, err := svr.ListAb(context.TODO(), 1, 10, "0,1,2", 1)
So(err, ShouldBeNil)
})
}
func TestAddAb(t *testing.T) {
var nab = &model.AB{
Name: "test",
Desc: "desc",
Stra: model.Stra{Precision: 100, Ratio: []int{80, 20}},
Seed: rand.Intn(1000000000),
Result: 0,
Group: 1,
Author: "test",
}
Convey("TestAddAb: ", t, func() {
res, err := svr.AddAb(context.TODO(), nab)
So(err, ShouldBeNil)
testID = int(res["newid"].(int64))
})
}
func TestAb(t *testing.T) {
var err error
Convey("TestAb: ", t, func() {
testAb, err = svr.Ab(context.TODO(), testID, 1)
So(err, ShouldBeNil)
})
}
func TestUpdateAb(t *testing.T) {
Convey("TestUpdateAb: ", t, func() {
testAb.Desc = "testUpdate"
res, err := svr.UpdateAb(context.TODO(), testID, testAb)
So(err, ShouldBeNil)
So(res, ShouldEqual, true)
})
}
func TestUpdateStatus(t *testing.T) {
Convey("TestUpdateStatus: ", t, func() {
res, err := svr.UpdateStatus(context.TODO(), testID, 1, "update", 1)
So(err, ShouldBeNil)
So(res, ShouldEqual, true)
svr.UpdateStatus(context.TODO(), testID, 0, "update", 1)
})
}
func TestDeleteAb(t *testing.T) {
Convey("TestDeleteAb: ", t, func() {
res, err := svr.DeleteAb(context.TODO(), testID)
So(err, ShouldBeNil)
So(res, ShouldEqual, true)
res, _ = svr.DeleteAb(context.TODO(), 1111)
So(res, ShouldEqual, false)
})
}

View File

@@ -0,0 +1,64 @@
package service
import (
"context"
"time"
"go-common/app/service/openplatform/abtest/model"
)
//setGroupCache 使用src重置分组缓存
func (s *Service) setGroupCache(c context.Context, group int, src []*model.AB, ver int64) (err error) {
m := make(map[int]*model.AB)
for _, ab := range src {
m[ab.ID] = ab
}
// s.mutex.Lock()
// s.abCache[group] = m
// s._versionID[group] = ver
// s.mutex.Unlock()
s.abCache.Store(group, m)
s._versionID.Store(group, ver)
return
}
//readGroupCache 获取分组缓存不存在则ok返回false
func (s *Service) readGroupCache(c context.Context, group int) (res map[int]*model.AB, ok bool) {
// s.mutex.RLock()
// res, ok = s.abCache[group]
// s.mutex.RUnlock()
var v interface{}
if v, ok = s.abCache.Load(group); ok {
res = v.(map[int]*model.AB)
}
return
}
//VersionIDListCache 获取缓存版本列表
func (s *Service) VersionIDListCache(c context.Context) (res map[int]int64, err error) {
res = make(map[int]int64)
// s.mutex.RLock()
// for k, v := range s._versionID {
// res[k] = v
// }
// s.mutex.RUnlock()
s._versionID.Range(func(key, value interface{}) bool {
res[key.(int)] = value.(int64)
return true
})
return
}
//versionID get A/B test version ID by group
func (s *Service) versionID(c context.Context, group int) (ver int64) {
v, ok := s._versionID.Load(group)
// s.mutex.RLock()
// ver, ok = s._versionID[group]
// s.mutex.RUnlock()
if ok {
ver = v.(int64)
return
}
ver = time.Now().Unix()
return
}

View File

@@ -0,0 +1,34 @@
package service
import (
"context"
"testing"
"go-common/app/service/openplatform/abtest/model"
. "github.com/smartystreets/goconvey/convey"
)
var ab = model.AB{ID: 1828, Group: 1, Status: 1, Name: "n@m3"}
func TestSetGroupCache(t *testing.T) {
var src = []*model.AB{&ab}
Convey("TestSetGroupCache: ", t, func() {
err := svr.setGroupCache(context.TODO(), 1, src, 123)
So(err, ShouldBeNil)
d, ok := svr.readGroupCache(context.TODO(), 1)
So(ok, ShouldBeTrue)
So(d[1828], ShouldEqual, &ab)
})
}
func TestVersionIDListCache(t *testing.T) {
var src = []*model.AB{&ab}
svr.setGroupCache(context.TODO(), 1, src, 123)
Convey("TestVersionIDListCache: ", t, func() {
verList, err := svr.VersionIDListCache(context.TODO())
So(err, ShouldBeNil)
_, ok := verList[1]
So(ok, ShouldBeTrue)
})
}

View File

@@ -0,0 +1,27 @@
package service
import (
"context"
"go-common/app/service/openplatform/abtest/model"
)
//AddGroup add a new group
func (s *Service) AddGroup(c context.Context, g model.Group) (id int, err error) {
return s.d.AddGroup(c, g)
}
//UpdateGroup update group by id
func (s *Service) UpdateGroup(c context.Context, g model.Group) (id int, err error) {
return s.d.UpdateGroup(c, g)
}
//ListGroup list all groups
func (s *Service) ListGroup(c context.Context) (m []*model.Group, err error) {
return s.d.ListGroup(c)
}
//DeleteGroup delete group by id
func (s *Service) DeleteGroup(c context.Context, id int) (r int, err error) {
return s.d.DeleteGroup(c, id)
}

View File

@@ -0,0 +1,51 @@
package service
import (
"context"
"testing"
"go-common/app/service/openplatform/abtest/model"
. "github.com/smartystreets/goconvey/convey"
)
var g = model.Group{
Name: "test",
Desc: "test add",
}
var testDesc = "test update"
func TestAddGroup(t *testing.T) {
Convey("TestAddGroup: ", t, func() {
var err error
testID, err = svr.AddGroup(context.TODO(), g)
So(err, ShouldBeNil)
})
}
func TestUpdateGroup(t *testing.T) {
g.Desc = testDesc
g.ID = testID
Convey("TestUpdateGroup: ", t, func() {
res, err := svr.UpdateGroup(context.TODO(), g)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}
func TestListGroup(t *testing.T) {
Convey("TestListGroup: ", t, func() {
res, err := svr.ListGroup(context.TODO())
So(err, ShouldBeNil)
x := res[len(res)-1]
So(x.Name, ShouldEqual, g.Name)
})
}
func TestDeleteGroup(t *testing.T) {
Convey("TestDeleteGroup: ", t, func() {
res, err := svr.DeleteGroup(context.TODO(), testID)
So(err, ShouldBeNil)
So(res, ShouldEqual, 1)
})
}

View File

@@ -0,0 +1,50 @@
package service
import (
"context"
"sync"
"go-common/app/service/openplatform/abtest/conf"
"go-common/app/service/openplatform/abtest/dao"
)
// Service struct of service.
type Service struct {
d *dao.Dao
// conf
c *conf.Config
// groupId => AB.id => AB
// abCache map[int]map[int]*model.AB
// _versionID map[int]int64
// mutex sync.RWMutex
abCache sync.Map
_versionID sync.Map
keyList sync.Map
// statCacheO map[int]map[int]int
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
d: dao.New(c),
}
// s.statCache = make(map[int]map[int]int)
// s.statCacheO = make(map[int]map[int]int)
go syncStart(s)
return
}
// Close dao.
func (s *Service) Close() {
s.d.Close()
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
if err = s.d.Ping(c); err != nil {
return
}
return
}

View File

@@ -0,0 +1,35 @@
package service
import (
"context"
"flag"
"fmt"
"testing"
"go-common/app/service/openplatform/abtest/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
svr *Service
)
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
svr = New(conf.Conf)
}
func TestPing(t *testing.T) {
Convey("TestPing: ", t, func() {
err := svr.Ping(context.TODO())
So(err, ShouldBeNil)
})
}
func TestEnd(t *testing.T) {
svr.Close()
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/openplatform/anti-fraud/api/grpc/v1:all-srcs",
"//app/service/openplatform/anti-fraud/api/http/v1:all-srcs",
"//app/service/openplatform/anti-fraud/cmd:all-srcs",
"//app/service/openplatform/anti-fraud/conf:all-srcs",
"//app/service/openplatform/anti-fraud/dao:all-srcs",
"//app/service/openplatform/anti-fraud/model:all-srcs",
"//app/service/openplatform/anti-fraud/server/grpc:all-srcs",
"//app/service/openplatform/anti-fraud/server/http:all-srcs",
"//app/service/openplatform/anti-fraud/service:all-srcs",
],
tags = ["automanaged"],
)

View File

@@ -0,0 +1,36 @@
##### Version 0.0.7
> 1. 增加业务方配置
> 2. 数据同步到支付风控
> 3. 日志记录
> 4. goroutine换成chan
##### Version 0.0.6
> 1. 去除ip的验证
##### Version 0.0.5
> 1. ipv6的兼容
##### Version 0.0.4
> 1. 风险检控接口
> 2. 限制改成可配置的
> 3. 兼容redis设置失败的问题
> 4. 简化参数,同时增加返回服务可用状态
##### Version 0.0.3
> 1. fix ID大小写
##### Version 0.0.2
> 1.获取题目添加redis缓存
> 2.更新题目的时候删除缓存
> 3.修复getObj
> 4.压测优化代码
> 5.队列代码优化
##### Version 0.0.1
> 1.初始化服务结构
> 2.防刷系统初步创建
> 3.优化绑定操作

View File

@@ -0,0 +1,10 @@
# Owner
liuzhan
# Author
wanglitao
# Reviewer
qiuliang

View File

@@ -0,0 +1,13 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- liuzhan
labels:
- openplatform
- service
- service/openplatform/anti-fraud
options:
no_parent_owners: true
reviewers:
- qiuliang
- wanglitao

View File

@@ -0,0 +1,18 @@
#### openplatform-service
`antifraud-service`主要是为开放平台票务各个业务提供一个综合的http接口。
* 聚合了票务多个模块(`题库` `题目` `答题` `获取题目`)的服务接口
* 提供了http服务
1 获取题库列表
2 获取题库详情a
3 更新题库
4 添加题库
4 删除题库
6 答题校验
##### 依赖环境
Go 1.7.5或更高版本
##### API文档
http://info.bilibili.co/pages/viewpage.action?pageId=8741024

View File

@@ -0,0 +1,56 @@
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
proto_library(
name = "v1_proto",
srcs = [
"graph.proto",
"risk.proto",
],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "v1_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/service/openplatform/anti-fraud/api/grpc/v1",
proto = ":v1_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [],
embed = [":v1_go_proto"],
importpath = "go-common/app/service/openplatform/anti-fraud/api/grpc/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"@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"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
//存放api使用的模型
syntax = "proto3";
package open.service.antifraud.v1;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "v1";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
message GraphPrepareRequest{
int64 mid = 1 [(gogoproto.jsontag) = "mid", (gogoproto.customname) = "MID",(gogoproto.moretags) = "form:\"mid\" validate:\"required\""];
string ip = 2 [(gogoproto.jsontag) = "ip", (gogoproto.customname) = "IP",(gogoproto.moretags) = "form:\"ip\" validate:\"required\""];
int64 customer = 3 [(gogoproto.jsontag) = "customer", (gogoproto.moretags) = "form:\"customer\" validate:\"required\""];
int64 item_id = 4 [(gogoproto.jsontag) = "item_id", (gogoproto.customname) = "ItemID", (gogoproto.moretags) = "form:\"item_id\" validate:\"required\""];
string client_type = 5 [(gogoproto.jsontag) = "client_type", (gogoproto.moretags) = "form:\"client_type\" validate:\"required\""];
}
message GraphPrepareResponse{
int64 success = 1 [(gogoproto.jsontag) = "success"];
string captcha_id = 2 [(gogoproto.jsontag) = "captcha_id", (gogoproto.customname) = "CaptchaID"];
string challenge = 3 [(gogoproto.jsontag) = "challenge"];
int64 new_captcha = 4 [(gogoproto.jsontag) = "new_captcha"];
string voucher = 5 [(gogoproto.jsontag) = "voucher"];
}
message GraphCheckRequest{
int64 mid = 1 [(gogoproto.jsontag) = "mid", (gogoproto.customname) = "MID",(gogoproto.moretags) = "form:\"mid\" validate:\"required\""];
string ip = 2 [(gogoproto.jsontag) = "ip", (gogoproto.customname) = "IP",(gogoproto.moretags) = "form:\"ip\" validate:\"required\""];
int64 customer = 3 [(gogoproto.jsontag) = "customer", (gogoproto.moretags) = "form:\"customer\" validate:\"required\""];
int64 item_id = 4 [(gogoproto.jsontag) = "item_id", (gogoproto.customname) = "ItemID",(gogoproto.moretags) = "form:\"item_id\" validate:\"required\""];
string voucher = 5 [(gogoproto.jsontag) = "voucher", (gogoproto.moretags) = "form:\"voucher\" validate:\"required\""];
string challenge = 6 [(gogoproto.jsontag) = "challenge", (gogoproto.moretags) = "form:\"challenge\" validate:\"required\""];
string validate = 7 [(gogoproto.jsontag) = "validate", (gogoproto.moretags) = "form:\"validate\" validate:\"required\""];
string seccode = 8 [(gogoproto.jsontag) = "seccode", (gogoproto.moretags) = "form:\"seccode\" validate:\"required\""];
string client_type = 9 [(gogoproto.jsontag) = "client_type", (gogoproto.moretags) = "form:\"client_type\" validate:\"required\""];
int64 success = 10 [(gogoproto.jsontag) = "success", (gogoproto.moretags) = "form:\"success\" validate:\"required\""];
}
message GraphCheckResponse{
string new_voucher = 1 [(gogoproto.jsontag) = "new_voucher"];
}
service Graph {
// 拉起极验
rpc GraphPrepare(GraphPrepareRequest) returns (GraphPrepareResponse);
// 极验验证
rpc GraphCheck(GraphCheckRequest) returns (GraphCheckResponse);
}

View File

@@ -0,0 +1,917 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: app/service/openplatform/anti-fraud/api/grpc/v1/risk.proto
package v1
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
import strings "strings"
import reflect "reflect"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type RiskCheckRequest struct {
MID int64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid" form:"mid" validate:"required"`
IP string `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip" form:"ip" validate:"required"`
ItemID int64 `protobuf:"varint,3,opt,name=item_id,json=itemId,proto3" json:"item_id" form:"item_id" validate:"required"`
Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path" form:"path" validate:"required"`
Customer int64 `protobuf:"varint,5,opt,name=customer,proto3" json:"customer" form:"customer" validate:"required"`
OpenTime int64 `protobuf:"varint,6,opt,name=open_time,json=openTime,proto3" json:"open_time" form:"open_time" validate:"required"`
Voucher string `protobuf:"bytes,7,opt,name=voucher,proto3" json:"voucher" form:"voucher"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RiskCheckRequest) Reset() { *m = RiskCheckRequest{} }
func (*RiskCheckRequest) ProtoMessage() {}
func (*RiskCheckRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_risk_5894149e138ee66f, []int{0}
}
func (m *RiskCheckRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RiskCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RiskCheckRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RiskCheckRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RiskCheckRequest.Merge(dst, src)
}
func (m *RiskCheckRequest) XXX_Size() int {
return m.Size()
}
func (m *RiskCheckRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RiskCheckRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RiskCheckRequest proto.InternalMessageInfo
type RiskCheckResponse struct {
Success int64 `protobuf:"varint,1,opt,name=success,proto3" json:"success"`
Rank int64 `protobuf:"varint,2,opt,name=rank,proto3" json:"rank"`
Method int64 `protobuf:"varint,3,opt,name=method,proto3" json:"method"`
Desc string `protobuf:"bytes,4,opt,name=desc,proto3" json:"desc"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RiskCheckResponse) Reset() { *m = RiskCheckResponse{} }
func (*RiskCheckResponse) ProtoMessage() {}
func (*RiskCheckResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_risk_5894149e138ee66f, []int{1}
}
func (m *RiskCheckResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RiskCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RiskCheckResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RiskCheckResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RiskCheckResponse.Merge(dst, src)
}
func (m *RiskCheckResponse) XXX_Size() int {
return m.Size()
}
func (m *RiskCheckResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RiskCheckResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RiskCheckResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*RiskCheckRequest)(nil), "open.service.antifraud.v1.RiskCheckRequest")
proto.RegisterType((*RiskCheckResponse)(nil), "open.service.antifraud.v1.RiskCheckResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// RiskClient is the client API for Risk service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RiskClient interface {
// 风险检查
RiskCheck(ctx context.Context, in *RiskCheckRequest, opts ...grpc.CallOption) (*RiskCheckResponse, error)
}
type riskClient struct {
cc *grpc.ClientConn
}
func NewRiskClient(cc *grpc.ClientConn) RiskClient {
return &riskClient{cc}
}
func (c *riskClient) RiskCheck(ctx context.Context, in *RiskCheckRequest, opts ...grpc.CallOption) (*RiskCheckResponse, error) {
out := new(RiskCheckResponse)
err := c.cc.Invoke(ctx, "/open.service.antifraud.v1.Risk/RiskCheck", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RiskServer is the server API for Risk service.
type RiskServer interface {
// 风险检查
RiskCheck(context.Context, *RiskCheckRequest) (*RiskCheckResponse, error)
}
func RegisterRiskServer(s *grpc.Server, srv RiskServer) {
s.RegisterService(&_Risk_serviceDesc, srv)
}
func _Risk_RiskCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RiskCheckRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RiskServer).RiskCheck(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/open.service.antifraud.v1.Risk/RiskCheck",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RiskServer).RiskCheck(ctx, req.(*RiskCheckRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Risk_serviceDesc = grpc.ServiceDesc{
ServiceName: "open.service.antifraud.v1.Risk",
HandlerType: (*RiskServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "RiskCheck",
Handler: _Risk_RiskCheck_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/service/openplatform/anti-fraud/api/grpc/v1/risk.proto",
}
func (m *RiskCheckRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RiskCheckRequest) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.MID != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintRisk(dAtA, i, uint64(m.MID))
}
if len(m.IP) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintRisk(dAtA, i, uint64(len(m.IP)))
i += copy(dAtA[i:], m.IP)
}
if m.ItemID != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintRisk(dAtA, i, uint64(m.ItemID))
}
if len(m.Path) > 0 {
dAtA[i] = 0x22
i++
i = encodeVarintRisk(dAtA, i, uint64(len(m.Path)))
i += copy(dAtA[i:], m.Path)
}
if m.Customer != 0 {
dAtA[i] = 0x28
i++
i = encodeVarintRisk(dAtA, i, uint64(m.Customer))
}
if m.OpenTime != 0 {
dAtA[i] = 0x30
i++
i = encodeVarintRisk(dAtA, i, uint64(m.OpenTime))
}
if len(m.Voucher) > 0 {
dAtA[i] = 0x3a
i++
i = encodeVarintRisk(dAtA, i, uint64(len(m.Voucher)))
i += copy(dAtA[i:], m.Voucher)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *RiskCheckResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RiskCheckResponse) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Success != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintRisk(dAtA, i, uint64(m.Success))
}
if m.Rank != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintRisk(dAtA, i, uint64(m.Rank))
}
if m.Method != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintRisk(dAtA, i, uint64(m.Method))
}
if len(m.Desc) > 0 {
dAtA[i] = 0x22
i++
i = encodeVarintRisk(dAtA, i, uint64(len(m.Desc)))
i += copy(dAtA[i:], m.Desc)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeVarintRisk(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *RiskCheckRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.MID != 0 {
n += 1 + sovRisk(uint64(m.MID))
}
l = len(m.IP)
if l > 0 {
n += 1 + l + sovRisk(uint64(l))
}
if m.ItemID != 0 {
n += 1 + sovRisk(uint64(m.ItemID))
}
l = len(m.Path)
if l > 0 {
n += 1 + l + sovRisk(uint64(l))
}
if m.Customer != 0 {
n += 1 + sovRisk(uint64(m.Customer))
}
if m.OpenTime != 0 {
n += 1 + sovRisk(uint64(m.OpenTime))
}
l = len(m.Voucher)
if l > 0 {
n += 1 + l + sovRisk(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RiskCheckResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Success != 0 {
n += 1 + sovRisk(uint64(m.Success))
}
if m.Rank != 0 {
n += 1 + sovRisk(uint64(m.Rank))
}
if m.Method != 0 {
n += 1 + sovRisk(uint64(m.Method))
}
l = len(m.Desc)
if l > 0 {
n += 1 + l + sovRisk(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovRisk(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozRisk(x uint64) (n int) {
return sovRisk(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *RiskCheckRequest) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&RiskCheckRequest{`,
`MID:` + fmt.Sprintf("%v", this.MID) + `,`,
`IP:` + fmt.Sprintf("%v", this.IP) + `,`,
`ItemID:` + fmt.Sprintf("%v", this.ItemID) + `,`,
`Path:` + fmt.Sprintf("%v", this.Path) + `,`,
`Customer:` + fmt.Sprintf("%v", this.Customer) + `,`,
`OpenTime:` + fmt.Sprintf("%v", this.OpenTime) + `,`,
`Voucher:` + fmt.Sprintf("%v", this.Voucher) + `,`,
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
`}`,
}, "")
return s
}
func (this *RiskCheckResponse) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&RiskCheckResponse{`,
`Success:` + fmt.Sprintf("%v", this.Success) + `,`,
`Rank:` + fmt.Sprintf("%v", this.Rank) + `,`,
`Method:` + fmt.Sprintf("%v", this.Method) + `,`,
`Desc:` + fmt.Sprintf("%v", this.Desc) + `,`,
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
`}`,
}, "")
return s
}
func valueToStringRisk(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
return "nil"
}
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *RiskCheckRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RiskCheckRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RiskCheckRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MID", wireType)
}
m.MID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MID |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field IP", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRisk
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.IP = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ItemID", wireType)
}
m.ItemID = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ItemID |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRisk
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Path = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Customer", wireType)
}
m.Customer = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Customer |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field OpenTime", wireType)
}
m.OpenTime = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.OpenTime |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Voucher", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRisk
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Voucher = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRisk(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthRisk
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RiskCheckResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RiskCheckResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RiskCheckResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType)
}
m.Success = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Success |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Rank", wireType)
}
m.Rank = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Rank |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType)
}
m.Method = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Method |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Desc", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRisk
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRisk
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Desc = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipRisk(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthRisk
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipRisk(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRisk
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRisk
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRisk
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthRisk
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowRisk
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipRisk(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthRisk = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowRisk = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("app/service/openplatform/anti-fraud/api/grpc/v1/risk.proto", fileDescriptor_risk_5894149e138ee66f)
}
var fileDescriptor_risk_5894149e138ee66f = []byte{
// 541 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6f, 0xd3, 0x30,
0x14, 0xc7, 0x97, 0xb4, 0xb4, 0xab, 0x91, 0x10, 0xf8, 0x14, 0xa6, 0x2d, 0xae, 0x02, 0x48, 0x93,
0x46, 0x13, 0xca, 0x84, 0x90, 0x8a, 0x10, 0x52, 0xe1, 0xd2, 0x03, 0x02, 0x45, 0x3b, 0x71, 0x99,
0xd2, 0xc4, 0x6d, 0xad, 0xce, 0xb5, 0x67, 0x3b, 0x39, 0x73, 0xe6, 0x23, 0xf0, 0x89, 0x76, 0xe4,
0xc8, 0xc9, 0x62, 0x39, 0xe6, 0x98, 0x4f, 0x80, 0xec, 0xa4, 0x05, 0xa1, 0x4e, 0xe2, 0x92, 0xf8,
0xfd, 0xdf, 0x7b, 0x3f, 0xfd, 0xed, 0x67, 0x83, 0x49, 0xc2, 0x79, 0x24, 0xb1, 0x28, 0x48, 0x8a,
0x23, 0xc6, 0xf1, 0x86, 0x5f, 0x25, 0x6a, 0xc1, 0x04, 0x8d, 0x92, 0x8d, 0x22, 0xa3, 0x85, 0x48,
0xf2, 0x2c, 0x4a, 0x38, 0x89, 0x96, 0x82, 0xa7, 0x51, 0x31, 0x8e, 0x04, 0x91, 0xeb, 0x90, 0x0b,
0xa6, 0x18, 0x7c, 0x6c, 0xea, 0xc3, 0xb6, 0x39, 0x34, 0xf5, 0xb6, 0x3c, 0x2c, 0xc6, 0x47, 0xa3,
0x25, 0x51, 0xab, 0x7c, 0x1e, 0xa6, 0x8c, 0x46, 0x4b, 0xb6, 0x64, 0x91, 0xed, 0x98, 0xe7, 0x0b,
0x1b, 0xd9, 0xc0, 0xae, 0x1a, 0x52, 0xf0, 0xad, 0x0b, 0x1e, 0xc6, 0x44, 0xae, 0xdf, 0xaf, 0x70,
0xba, 0x8e, 0xf1, 0x75, 0x8e, 0xa5, 0x82, 0x53, 0xd0, 0xa1, 0x24, 0xf3, 0x9c, 0xa1, 0x73, 0xda,
0x99, 0xbe, 0x28, 0x35, 0xea, 0x7c, 0x9c, 0x7d, 0xa8, 0x34, 0x32, 0x6a, 0xad, 0x91, 0x6f, 0x5c,
0x4e, 0x02, 0x4a, 0xb2, 0x60, 0x58, 0x24, 0x57, 0x24, 0x4b, 0x14, 0x9e, 0x04, 0x02, 0x5f, 0xe7,
0x44, 0xe0, 0x2c, 0x88, 0x4d, 0x19, 0x7c, 0x0b, 0x5c, 0xc2, 0x3d, 0x77, 0xe8, 0x9c, 0x0e, 0xa6,
0xa3, 0x52, 0x23, 0x77, 0xf6, 0xb9, 0xd2, 0xc8, 0x25, 0xbc, 0xd6, 0xe8, 0xa4, 0x01, 0x10, 0xbe,
0xbf, 0xdf, 0x25, 0x1c, 0x5e, 0x80, 0x3e, 0x51, 0x98, 0x5e, 0x92, 0xcc, 0xeb, 0x58, 0x1b, 0x6f,
0x4a, 0x8d, 0x7a, 0x33, 0x85, 0xa9, 0x75, 0xb2, 0x4d, 0xd6, 0x1a, 0x05, 0x2d, 0xac, 0x11, 0xf6,
0x13, 0x7b, 0x26, 0x3b, 0xcb, 0xe0, 0x3b, 0xd0, 0xe5, 0x89, 0x5a, 0x79, 0x5d, 0x6b, 0xeb, 0xac,
0xd2, 0xc8, 0xc6, 0xb5, 0x46, 0xa8, 0xa1, 0x98, 0x68, 0x3f, 0xc2, 0x16, 0xc2, 0x4f, 0xe0, 0x30,
0xcd, 0xa5, 0x62, 0x14, 0x0b, 0xef, 0x9e, 0xf5, 0x75, 0x5e, 0x69, 0xb4, 0xd3, 0x6a, 0x8d, 0x9e,
0x34, 0xa0, 0xad, 0xb2, 0x1f, 0xb6, 0x6b, 0x80, 0x31, 0x18, 0x98, 0x59, 0x5e, 0x2a, 0x42, 0xb1,
0xd7, 0xb3, 0xc4, 0x57, 0x95, 0x46, 0x7f, 0xc4, 0x5a, 0xa3, 0xa7, 0x0d, 0x72, 0x27, 0xdd, 0xc1,
0x34, 0xf9, 0x0b, 0x42, 0x31, 0x7c, 0x0d, 0xfa, 0x05, 0xcb, 0xd3, 0x15, 0x16, 0x5e, 0xdf, 0x6e,
0xf4, 0xc4, 0x9c, 0x58, 0x2b, 0xd5, 0x1a, 0x3d, 0x68, 0x78, 0xad, 0x10, 0xc4, 0xdb, 0x54, 0xf0,
0xdd, 0x01, 0x8f, 0xfe, 0xba, 0x0c, 0x92, 0xb3, 0x8d, 0xc4, 0xf0, 0x19, 0xe8, 0xcb, 0x3c, 0x4d,
0xb1, 0x94, 0xed, 0x8d, 0xb8, 0x6f, 0x70, 0xad, 0x14, 0x6f, 0x17, 0xf0, 0x18, 0x74, 0x45, 0xb2,
0x59, 0xdb, 0x91, 0x77, 0xa6, 0x87, 0xe6, 0x6c, 0x4d, 0x1c, 0xdb, 0x2f, 0x0c, 0x40, 0x8f, 0x62,
0xb5, 0x62, 0xdb, 0x71, 0x82, 0x4a, 0xa3, 0x56, 0x89, 0xdb, 0xbf, 0x21, 0x64, 0x58, 0xa6, 0xed,
0x74, 0x2c, 0xc1, 0xc4, 0xb1, 0xfd, 0xbe, 0xdc, 0x80, 0xae, 0xf1, 0x06, 0x17, 0x60, 0xb0, 0xf3,
0x08, 0xcf, 0xc2, 0x3b, 0x5f, 0x42, 0xf8, 0xef, 0xb5, 0x3e, 0x7a, 0xfe, 0x7f, 0xc5, 0xcd, 0xb6,
0xa7, 0xc7, 0x37, 0xb7, 0xfe, 0xc1, 0xcf, 0x5b, 0xff, 0xe0, 0x6b, 0xe9, 0x3b, 0x37, 0xa5, 0xef,
0xfc, 0x28, 0x7d, 0xe7, 0x57, 0xe9, 0x3b, 0x5f, 0xdc, 0x62, 0x3c, 0xef, 0xd9, 0xe7, 0x73, 0xfe,
0x3b, 0x00, 0x00, 0xff, 0xff, 0x79, 0xb3, 0x6d, 0xb9, 0xc6, 0x03, 0x00, 0x00,
}

View File

@@ -0,0 +1,32 @@
//存放api使用的模型
syntax = "proto3";
package open.service.antifraud.v1;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "v1";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
message RiskCheckRequest{
int64 mid = 1 [(gogoproto.jsontag) = "mid", (gogoproto.customname) = "MID",(gogoproto.moretags) = "form:\"mid\" validate:\"required\""];
string ip = 2 [(gogoproto.jsontag) = "ip", (gogoproto.customname) = "IP",(gogoproto.moretags) = "form:\"ip\" validate:\"required\""];
int64 item_id = 3 [(gogoproto.jsontag) = "item_id", (gogoproto.customname) = "ItemID",(gogoproto.moretags) = "form:\"item_id\" validate:\"required\""];
string path = 4 [(gogoproto.jsontag) = "path",(gogoproto.moretags) = "form:\"path\" validate:\"required\""];
int64 customer = 5 [(gogoproto.jsontag) = "customer",(gogoproto.moretags) = "form:\"customer\" validate:\"required\""];
int64 open_time = 6 [(gogoproto.jsontag) = "open_time",(gogoproto.moretags) = "form:\"open_time\" validate:\"required\""];
string voucher = 7 [(gogoproto.jsontag) = "voucher",(gogoproto.moretags) = "form:\"voucher\""];
}
message RiskCheckResponse{
int64 success = 1 [(gogoproto.jsontag) = "success"];
int64 rank = 2 [(gogoproto.jsontag) = "rank"];
int64 method = 3 [(gogoproto.jsontag) = "method"];
string desc = 4 [(gogoproto.jsontag) = "desc"];
}
service Risk {
// 风险检查
rpc RiskCheck(RiskCheckRequest) returns (RiskCheckResponse);
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "go-common/app/service/openplatform/anti-fraud/api/http/v1",
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,107 @@
package v1
// RiskCheckV2Request .
type RiskCheckV2Request struct {
UID int64 `json:"uid" form:"uid" validate:"required"`
UserClientIP string `json:"userClientIp" form:"userClientIp" validate:"required"`
InterfaceName string `json:"interfaceName" form:"interfaceName" validate:"required"`
InterfaceDesc string `json:"interfaceDesc" form:"interfaceDesc" validate:"required"`
CustomerID int64 `json:"customerID" form:"customerId" validate:"required"`
DeviceInfo string `json:"deviceInfo" form:"deviceInfo" validate:"required"`
ItemInfo string `json:"itemInfo" form:"itemInfo" validate:"required"`
BuyerInfo string `json:"buyerInfo" form:"buyerInfo"`
AddrInfo string `json:"addrInfo" form:"addrInfo"`
Voucher string `json:"voucher" form:"voucher"`
ReqData string `json:"reqData" form:"reqData" validate:"required"`
ExtraData string `json:"extraData" form:"extraData"`
}
// RiskCheckV2Response .
type RiskCheckV2Response struct {
RiskID int64 `json:"riskId"`
RiskLevel int64 `json:"riskLevel"`
Method string `json:"method"`
Desc string `json:"desc"`
}
// IPListRequest .
type IPListRequest struct {
}
// IPListResponse .
type IPListResponse struct {
List []*IPListDetail `json:"list"`
}
// IPListDetail .
type IPListDetail struct {
IP string `json:"ip"`
Num int64 `json:"num"`
Timestamp int64 `json:"timestamp"`
}
// UIDListRequest .
type UIDListRequest struct {
}
// UIDListResponse .
type UIDListResponse struct {
List []*UIDListDetail `json:"list"`
}
// UIDListDetail .
type UIDListDetail struct {
UID string `json:"uid"`
Num int64 `json:"num"`
Timestamp int64 `json:"timestamp"`
}
// IPDetailRequest .
type IPDetailRequest struct {
IP string `json:"ip" form:"ip" validate:"required"`
Timestamp int64 `json:"timestamp" form:"timestamp" validate:"required"`
}
// IPDetailResponse .
type IPDetailResponse struct {
List []*ListDetail `json:"list"`
}
// UIDDetailRequest .
type UIDDetailRequest struct {
UID string `json:"uid" form:"uid" validate:"required"`
Timestamp int64 `json:"timestamp" form:"timestamp" validate:"required"`
}
// UIDDetailResponse .
type UIDDetailResponse struct {
List []*ListDetail `json:"list"`
}
// ListDetail .
type ListDetail struct {
UID string `json:"uid"`
IP string `json:"ip"`
}
// IPBlackRequest .
type IPBlackRequest struct {
IP string `json:"ip" form:"ip" validate:"required"`
CustomerID int64 `json:"customer_id" form:"customer_id" validate:"required"`
Minute int64 `json:"minute" form:"minute" validate:"required"`
}
// IPBlackResponse .
type IPBlackResponse struct {
}
// UIDBlackRequest .
type UIDBlackRequest struct {
UID string `json:"uid" form:"uid" validate:"required"`
CustomerID int64 `json:"customer_id" form:"customer_id" validate:"required"`
Minute int64 `json:"minute" form:"minute" validate:"required"`
}
// UIDBlackResponse .
type UIDBlackResponse struct {
}

View File

@@ -0,0 +1,42 @@
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 = ["anti-fraud.toml"],
importpath = "go-common/app/service/openplatform/anti-fraud/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/server/http: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,151 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
dir = "./"
debug = false
[log]
dir = "/tmp/log/anti-fraud"
[db]
[db.antiFraud]
addr = "172.16.33.203:3306"
dsn = "root:123456@tcp(172.16.33.203:3306)/tickets?timeout=5s&readTimeout=30s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "2s"
[db.antiFraud.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[db.payShield]
addr = "127.0.0.1:3306"
dsn = "root:123456@tcp(127.0.0.1:3306)/shield?timeout=5s&readTimeout=30s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "2s"
[db.payShield.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[db.payShield1]
addr = "172.22.33.156:3306"
dsn = "pay:QaJvNP7FGxDjpMI3tcVXUusZ80w6gmbk@tcp(172.22.33.156:3306)/shield?timeout=5s&readTimeout=30s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "2s"
[db.payShield1.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[redis]
name = "antifraud"
proto = "tcp"
addr = "172.16.33.203:9379"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
verifyCdTimes = "2h"
[httpClient]
[httpClient.read]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "4s"
keepAlive = "60s"
timer = 1000
[httpClient.read.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[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
[geetest]
#极验每小时拉起的总数 to do
count = 20
[limit]
Name = "ticket"
SaleTimeOut = 86400
#mid下单时间限制 to do
MIDCreateTimeOut = 3600
#mid最大下单次数 to do
MIDCreateMax = 10
#ip下单时间限制 to do
IPCreateTimeOut = 3600
#ip最大下单次数 to do
IPCreateMax = 20
#ip白名单
IPWhiteList = ["222.73.196.18"]
#ip修改时间间隔
IPChangeInterval = 2
[rule]
[rule.10001]
Name = "ticket"
SaleTimeOut = 86400
#mid下单时间限制 to do
MIDCreateTimeOut = 3600
#mid最大下单次数 to do
MIDCreateMax = 10
#ip下单时间限制 to do
IPCreateTimeOut = 3600
#ip最大下单次数 to do
IPCreateMax = 20
#ip白名单
IPWhiteList = ["222.73.196.18"]
#ip修改时间间隔
IPChangeInterval = 100
[rule.10002]
Name = "mall"
SaleTimeOut = 86400
#mid下单时间限制 to do
MIDCreateTimeOut = 3600
#mid最大下单次数 to do
MIDCreateMax = 10
#ip下单时间限制 to do
IPCreateTimeOut = 3600
#ip最大下单次数 to do
IPCreateMax = 20
#ip白名单
IPWhiteList = ["222.73.196.18"]
#ip修改时间间隔
IPChangeInterval = 2
[url]
shield = "http://10.23.144.107:8080/payplatform/shield/askasync"
[base]
shieldListTime = 8886400

View File

@@ -0,0 +1,47 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/service/openplatform/anti-fraud/conf"
"go-common/app/service/openplatform/anti-fraud/server/http"
"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()
trace.Init(nil)
defer trace.Close()
log.Info("anti_fraud start")
// http init
http.Init(conf.Conf)
// signal handler
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("anti-fraud get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
log.Info("anti-fraud exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,37 @@
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/openplatform/anti-fraud/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/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,139 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
// Conf global variable.
var (
Conf = &Config{}
client *conf.Client
confPath string
)
// Config struct of conf.
type Config struct {
// base
// log
Log *log.Config
// db
DB *DB
// redis
Redis *Redis
// HTTPClient
HTTPClient *HTTPClient
// limit
Limit *Limit
// geetest
Geetest *Geetest
// rule
Rule map[string]*Limit
// url
URL *URL
// base
Base *Base
}
// URL .
type URL struct {
Shield string
}
// Base .
type Base struct {
ShieldListTime int64
}
//Limit 限制
type Limit struct {
Name string
SaleTimeOut int64
MIDCreateTimeOut int64
MIDCreateMax int64
IPCreateTimeOut int64
IPCreateMax int64
IPChangeInterval int64
IPWhiteList []string
}
//Geetest 极验
type Geetest struct {
Count int64
}
// DB db config.
type DB struct {
AntiFraud *sql.Config
PayShield *sql.Config
}
// Redis conf.
type Redis struct {
*redis.Config
Expire xtime.Duration
VerifyCdTimes xtime.Duration
}
// HTTPClient config
type HTTPClient struct {
Read *bm.ClientConfig
Write *bm.ClientConfig
}
func outer() (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 event")
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Value("anti-fraud.toml"); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init int config
func Init() error {
if confPath != "" {
return outer()
}
return remote()
}

View File

@@ -0,0 +1,67 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"cache_test.go",
"dao_test.go",
"question_bank_bind_test.go",
"question_bank_test.go",
"question_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"dao.go",
"payShield.go",
"question.go",
"question_bank.go",
"question_bank_bind.go",
"risk.go",
],
importpath = "go-common/app/service/openplatform/anti-fraud/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/json-iterator/go: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,600 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"time"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/cache/redis"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_keyBankQuestions = "AntiFraud:BANK_%d:QUESTIONS"
_keyQuestionFetchTime = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_Date"
// 上次调起组件的id
_keyComponentID = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_COMID"
// 回答过的问题 id
_keyAnsweredIds = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_ANSWER_IDS"
// 组件内获取题目次数
_keyBindBank = "AntiFraud:BIND_BANK_ITEM_%s"
_keyComponentTimes = "AntiFraud:USER_%s:ITEM_%d_%d_%s_PLATFORM:%d_ANSWERTIMES"
_keyBankID = "AntiFraud:BANKID_%d"
//答案ids缓存
_keyAnswerIds = "AntiFraud:AnswerIds_%d"
// 图片id
_keyPicID = "AntiFraud:AnswerAllPic"
// 缓存所有图片id
_keyPicIds = "AntiFraud:AnswerPic_%d"
//默认100条数据
_limit = 100
// 缓存问题
_keyQusInfo = "AntiFraud:QusInfo_%d"
// 缓存图片
_keyAnswerPicID = "AntiFraud:USER_%s:ITEM_%d_%d_%s_AnswerPic:%d_COMID"
//答题日志list
_keyAddLog = "AntiFraud:AddLog"
//答案缓存
_keyAnswer = "AntiFraud:Answer_%d"
// 5分钟缓存
_fiveMinuts = 5 * time.Minute
)
// PingRedis check redis connection
func (d *Dao) PingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("PING")
return
}
// GetUserQuestionCache get
func (d *Dao) GetUserQuestionCache(c context.Context, args *model.ArgGetQuestion, bankID int64) {
key := fmt.Sprintf("USER_%s_ITEM_%d_%d_%s_PLATFORM_%d_BANK_%d", args.UID, args.Source, args.TargetItemType, args.TargetItem, args.Platform, bankID)
d.RedisDo(c, "GET", key)
}
// RedisDo redis cmd
func (d *Dao) RedisDo(c context.Context, cmd string, args ...interface{}) (reply interface{}, err error) {
conn := d.redis.Get(c)
defer conn.Close()
return conn.Do(cmd, args...)
}
// SetBankQuestionsCache 将题库下的全部问题存入缓存
func (d *Dao) SetBankQuestionsCache(c context.Context, bankID int64, ids []int64) (err error) {
key := fmt.Sprintf(_keyBankQuestions, bankID)
err = d.SetObj(c, key, ids, time.Hour)
if err != nil {
log.Error("d.SetBankQuestionsCache() error(%v)", err)
return
}
return
}
// GetBankQuestionsCache 从缓存获取题库下的全部问题
func (d *Dao) GetBankQuestionsCache(c context.Context, bankID int64) (ids []int64) {
key := fmt.Sprintf(_keyBankQuestions, bankID)
reply, err := redis.Bytes(d.RedisDo(c, "GET", key))
if err == redis.ErrNil {
return
}
if err != nil {
log.Error("查询 redis 出错 d.GetBankQuestionsCache(%d) error(%v)", bankID, err)
return
}
err = json.Unmarshal(reply, &ids)
if err != nil {
return
}
return
}
// QusFetchTime 上次题目拉取时间
func (d *Dao) QusFetchTime(c context.Context, args *model.ArgGetQuestion) (ts int64) {
ts, _ = redis.Int64(d.RedisDo(c, "GET", d.GetQusKey(_keyQuestionFetchTime, args)))
return
}
// SetQusFetchTime 设置上次题目拉取时间
func (d *Dao) SetQusFetchTime(c context.Context, args *model.ArgGetQuestion, ts int64) (err error) {
err = d.Setex(c, d.GetQusKey(_keyQuestionFetchTime, args), ts, _fiveMinuts)
if err != nil {
log.Error("d.SetQusFetchTime(%v, %d) error(%v)", args, ts, err)
}
return
}
// GetQusKey getkey
func (d *Dao) GetQusKey(format string, args *model.ArgGetQuestion) (s string) {
s = fmt.Sprintf(format, args.UID, args.Source, args.TargetItemType, args.TargetItem, args.Platform)
return
}
// Setex set
func (d *Dao) Setex(c context.Context, key string, data interface{}, exp time.Duration) error {
log.Info(" d.setex(%s, %v, %d)", key, data, exp)
_, err := d.RedisDo(c, "SETEX", key, int(exp/1e9), data)
if err != nil {
log.Error("d.Setex(%s, %v) error(%v)", key, data, err)
}
return err
}
// SetObj set
func (d *Dao) SetObj(c context.Context, key string, obj interface{}, exp time.Duration) error {
log.Info(" d.setex(%s, %v, %d)", key, obj, exp)
data, err := json.Marshal(obj)
if err != nil {
return err
}
_, err = d.RedisDo(c, "SETEX", key, int(exp/1e9), data)
if err != nil {
log.Error("d.Setex(%s, %v) error(%v)", key, string(data), err)
}
return err
}
//GetObj get
func (d *Dao) GetObj(c context.Context, key string, obj interface{}) (err error) {
reply, err := redis.Bytes(d.RedisDo(c, "GET", key))
if err != nil {
return
}
err = json.Unmarshal(reply, obj)
if err != nil {
return
}
return
}
// GetAnsweredID 获取已回答问题
func (d *Dao) GetAnsweredID(c context.Context, args *model.ArgGetQuestion) (ids []int64) {
key := d.GetQusKey(_keyAnsweredIds, args)
ids, err := redis.Int64s(d.RedisDo(c, "SMEMBERS", key))
if err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("d.GetAnsweredID(%v) error(%v)", args, err)
}
}
return
}
// SetAnsweredID 设置已回答问题
func (d *Dao) SetAnsweredID(c context.Context, args *model.ArgGetQuestion, questionID int64) (err error) {
key := d.GetQusKey(_keyAnsweredIds, args)
_, err = d.RedisDo(c, "SADD", key, questionID)
if err != nil {
log.Error("d.SetAnsweredID(%v, %d) error(%v)", args, questionID, err)
}
_, err = d.RedisDo(c, "EXPIRE", key, int(_fiveMinuts/1e9))
if err != nil {
log.Error("d.SetAnsweredID expire (%v, %d) error(%v)", args, questionID, err)
}
return
}
// RmAnsweredID 删除已回答问题
func (d *Dao) RmAnsweredID(c context.Context, args *model.ArgGetQuestion) (err error) {
key := d.GetQusKey(_keyAnsweredIds, args)
_, err = d.RedisDo(c, "DEL", key)
if err != nil {
log.Error("d.SetAnsweredID(%v, %d) error(%v)", args, err)
}
return
}
// GetComponentID 获取组件id
func (d *Dao) GetComponentID(c context.Context, args *model.ArgGetQuestion) (cID int, err error) {
key := d.GetQusKey(_keyComponentID, args)
if cID, err = redis.Int(d.RedisDo(c, "GET", key)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("d.GetComponentID(%v) error(%v)", args, err)
}
}
return
}
// SetComponentID 设置组件id
func (d *Dao) SetComponentID(c context.Context, args *model.ArgGetQuestion) (err error) {
key := d.GetQusKey(_keyComponentID, args)
err = d.Setex(c, key, args.ComponentID, _fiveMinuts)
if err != nil {
log.Error("d.SetComponentID(%v) error(%v)", args, err)
}
return
}
// GetComponentTimes 获取组件答题次数
func (d *Dao) GetComponentTimes(c context.Context, args *model.ArgGetQuestion) (cID int64, err error) {
key := d.GetQusKey(_keyComponentTimes, args)
if cID, err = redis.Int64(d.RedisDo(c, "GET", key)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("d.GetComponentTimes(%v) error(%v)", args, err)
}
}
return
}
// SetComponentTimes 设置组件答题次数
func (d *Dao) SetComponentTimes(c context.Context, args *model.ArgGetQuestion) (err error) {
key := d.GetQusKey(_keyComponentTimes, args)
err = d.Setex(c, key, 0, _fiveMinuts)
if err != nil {
log.Error("d.SetComponentID(%v) error(%v)", args, err)
}
return
}
// IncrComponentTimes 组件计数
func (d *Dao) IncrComponentTimes(c context.Context, args *model.ArgGetQuestion) (err error) {
key := d.GetQusKey(_keyComponentTimes, args)
_, err = d.RedisDo(c, "INCR", key)
if err != nil {
log.Error("d.GetComponentTimes(%v) error(%v)", args, err)
}
return
}
// GetQusBankInfoCache get
func (d *Dao) GetQusBankInfoCache(c context.Context, qbid int64) (oi *model.QuestionBank, err error) {
oi = &model.QuestionBank{}
key := fmt.Sprintf(_keyBankID, qbid)
err = d.GetObj(c, key, oi)
if err == redis.ErrNil {
err = nil
oi, err = d.GetQusBankInfo(c, qbid)
if err != nil {
log.Error("d.GetQusBankInfoCache error(%v)", err)
return
}
err = d.SetObj(c, key, oi, _fiveMinuts)
}
return
}
// GetBindBankInfo get
func (d *Dao) GetBindBankInfo(c context.Context, source, targetItemType int8, targetItem string) (bind *model.QuestionBankBind, err error) {
bind = &model.QuestionBankBind{}
key := fmt.Sprintf(_keyBindBank, targetItem)
err = d.GetObj(c, key, bind)
if err == redis.ErrNil {
//err = nil
binds, err1 := d.GetBindBank(c, source, targetItemType, []string{targetItem})
if err1 != nil {
log.Error("s.GetQuestion(%v) error(%v)", targetItem, err)
err = err1
return
}
if len(binds) < 1 {
log.Warn("s.GetQuestion(%v) 未找到题库绑定关系", targetItem)
err = ecode.BindBankNotFound
return
}
bind = binds[0]
if bind.QuestionBank == nil {
log.Error("s.GetQuestion(%v) 未找到已绑定的题库", targetItem)
err = ecode.QusbNotFound
return
}
err = d.SetObj(c, key, bind, time.Hour)
//return
}
return
}
// CorrectAnswerIds id
func (d *Dao) CorrectAnswerIds(c context.Context, qid int64) (ids []int64, err error) {
key := fmt.Sprintf(_keyAnswerIds, qid)
err = d.GetObj(c, key, &ids)
if err == redis.ErrNil {
err = nil
answers, err1 := d.GetAnswerList(c, qid)
for _, answer := range answers {
if answer.IsCorrect == 1 {
ids = append(ids, answer.AnswerID)
}
}
if err1 != nil {
err = err1
log.Error("d.GetQusBankInfoCache error(%v)", err)
return
}
err1 = d.SetObj(c, key, ids, time.Hour)
err = err1
return
}
return
}
// GetRandPic 背景图
func (d *Dao) GetRandPic(c context.Context, args *model.ArgGetQuestion) (oi *model.QuestBkPic, err error) {
key := _keyPicID
cnt, err := redis.Int(d.RedisDo(c, "LLen", key))
if err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("GetStatisticsCache do(RPOP, %s) error(%v)", key, err)
}
return
}
if cnt == 0 {
err1 := d.PushAllPic(c)
if err1 != nil {
if err1 == redis.ErrNil {
err = nil
} else {
log.Error("PushAllPic do(RPOP, %s) error(%v)", key, err)
}
return
}
}
id, err := redis.Int(d.RedisDo(c, "RPOP", key))
if err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("GetStatisticsCache do(RPOP, %s) error(%v)", key, err)
}
return
}
oi, err = d.GetPic(c, id)
if err != nil {
return
}
//缓存坐标
picKey := d.GetQusKey(_keyAnswerPicID, args)
err = d.SetObj(c, picKey, oi, time.Hour)
if err != nil {
return
}
return
}
// PushAllPic 事先放入redis list中
func (d *Dao) PushAllPic(c context.Context) (err error) {
cnt, err := d.GetPicCount(c)
limit := _limit
page := (int)(cnt / limit)
for i := 0; i <= page; i++ {
key := _keyPicID
ids, _ := d.GetAllPicIds(c, i*limit, limit)
a := make([]interface{}, 0)
a = append(a, key)
for _, id := range ids {
a = append(a, id)
}
if len(a) > 1 {
_, err := d.RedisDo(c, "LPUSH", a...)
if err != nil {
log.Error("[PushAllPic]conn.Do(lpush, %s) error(%v)", key, err)
}
}
}
return
}
// GetPic 获取背景图
func (d *Dao) GetPic(c context.Context, id int) (oi *model.QuestBkPic, err error) {
key := fmt.Sprintf(_keyPicIds, id)
oi = &model.QuestBkPic{}
err = d.GetObj(c, key, oi)
if err == redis.ErrNil {
err = nil
picInfo, err1 := d.GetRandomPic(c, id)
if err1 != nil {
err = err1
log.Error("d.GetPic error(%v)", err1)
return
}
err1 = d.SetObj(c, key, picInfo, time.Hour)
oi = picInfo
err = err1
return
}
return
}
// PushAnswer push
func (d *Dao) PushAnswer(c context.Context, answer *model.ArgCheckAnswer, isCorrect int8) (affect int64, err error) {
ids := xstr.JoinInts(answer.Answers)
obj := model.AddLog{
UID: answer.UID,
QsID: answer.QsID,
Platform: answer.Platform,
Source: answer.Source,
Ids: ids,
IsCorrect: isCorrect,
}
data, err := json.Marshal(obj)
if err != nil {
return
}
_, err = d.RedisDo(c, "LPUSH", _keyAddLog, data)
if err != nil {
log.Error("[PushAllPic]conn.Do(RPOP,%s) error(%v)", "_keyAddLog", err)
}
affect = obj.QsID
return
}
// PopAnswer pop
func (d *Dao) PopAnswer(c context.Context) {
for {
reply, err := redis.Bytes(d.RedisDo(c, "RPOP", _keyAddLog))
if len(reply) > 0 && err == nil {
answer := &model.AddLog{}
err = json.Unmarshal(reply, answer)
if err == nil {
_, err = d.db.Exec(c, _addUserAnswerSQL, answer.UID, answer.QsID, answer.Platform, answer.Source, answer.Ids, answer.IsCorrect)
if err != nil {
log.Error("d.PopAnswer error(%v)", err)
time.Sleep(time.Second * 1)
continue
}
}
}
time.Sleep(time.Second * 5)
}
}
// GetCacheQus get
func (d *Dao) GetCacheQus(c context.Context, id int64) (oi *model.Question, err error) {
key := fmt.Sprintf(_keyQusInfo, id)
oi = &model.Question{}
err = d.GetObj(c, key, oi)
if err == redis.ErrNil {
err = nil
info, err1 := d.GetQusInfo(c, id)
if err1 != nil {
err = err1
log.Error("d.GetPic error(%v)", err1)
return
}
err1 = d.SetObj(c, key, info, time.Hour)
oi = info
err = err1
return
}
return
}
// GetCacheAnswerPic get
func (d *Dao) GetCacheAnswerPic(c context.Context, args *model.ArgGetQuestion) (oi *model.QuestBkPic, err error) {
picKey := d.GetQusKey(_keyAnswerPicID, args)
oi = &model.QuestBkPic{}
err = d.GetObj(c, picKey, oi)
if err != nil {
return
}
return
}
// DelTargetItemBindCache del cache
func (d *Dao) DelTargetItemBindCache(c context.Context, skuID string) (err error) {
key := fmt.Sprintf(_keyBindBank, skuID)
_, err = d.RedisDo(c, "DEL", key)
if err != nil {
if err != redis.ErrNil {
log.Error("d.DelTargetItemBind(%v, %d) error(%v)", skuID, err)
return
}
err = nil
}
return
}
// DelQusCache del cache
func (d *Dao) DelQusCache(c context.Context, id int64) (err error) {
key := fmt.Sprintf(_keyQusInfo, id)
_, err = d.RedisDo(c, "DEL", key)
if err != nil {
if err != redis.ErrNil {
log.Error("d.DelQusCache(%v, %d) error(%v)", id, err)
return
}
err = nil
}
return
}
// GetAnswersByCache cache
func (d *Dao) GetAnswersByCache(c context.Context, id int64) (oi []*model.Answer, err error) {
key := fmt.Sprintf(_keyAnswer, id)
err = d.GetObj(c, key, &oi)
if err == redis.ErrNil {
err = nil
info, err1 := d.GetAnswerList(c, id)
if err1 != nil {
err = err1
log.Error("d.GetPic error(%v)", err1)
return
}
err1 = d.SetObj(c, key, info, time.Hour)
oi = info
err = err1
return
}
return
}
// DelAnswerCache del cache
func (d *Dao) DelAnswerCache(c context.Context, id int64) (err error) {
key := fmt.Sprintf(_keyAnswer, id)
_, err = d.RedisDo(c, "DEL", key)
if err != nil {
if err != redis.ErrNil {
log.Error("d.DelAnswerCache(%v, %d) error(%v)", id, err)
return
}
err = nil
}
return
}
// DelQusBankCache del cache
func (d *Dao) DelQusBankCache(c context.Context, id int64) (err error) {
key := fmt.Sprintf(_keyBankID, id)
_, err = d.RedisDo(c, "DEL", key)
if err != nil {
if err != redis.ErrNil {
log.Error("d.DelQusBankCache(%v, %d) error(%v)", id, err)
return
}
err = nil
}
return
}

View File

@@ -0,0 +1,145 @@
package dao
import (
"context"
"encoding/json"
"flag"
"fmt"
"testing"
"time"
"go-common/app/service/openplatform/anti-fraud/conf"
"go-common/app/service/openplatform/anti-fraud/model"
. "github.com/smartystreets/goconvey/convey"
)
const _key = "AntiFraud:BANKID_1527233672941"
var qustion = &model.ArgGetQuestion{UID: "1111", TargetItem: "1111", TargetItemType: 1, Source: 1, Platform: 1, ComponentID: 123}
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
d = New(conf.Conf)
}
func TestSetex(t *testing.T) {
Convey("TestSetex", t, func() {
err := d.Setex(context.TODO(), "BANK_1527061216377_QUESTIONS", 1, time.Hour)
So(err, ShouldBeNil)
})
}
func TestSetObj(t *testing.T) {
Convey("TestSetex", t, func() {
obj := &model.QuestionBank{}
reply := []byte(`{"qb_id":1527233672941,"qb_name":"wlt","cd_time":0,"max_retry_time":22,"is_deleted":0}`)
err := json.Unmarshal(reply, obj)
if err != nil {
return
}
d.GetObj(context.TODO(), _key, obj)
data, err := d.GetQusBankInfoCache(context.TODO(), 1527233672941)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
})
}
//
func TestGetObj(t *testing.T) {
Convey("TestSetex", t, func() {
data := &model.QuestionBank{}
err := d.GetObj(context.TODO(), _key, data)
So(err, ShouldBeNil)
})
}
func TestGetPic(t *testing.T) {
Convey("TestGetPic", t, func() {
oi, _ := d.GetPic(context.TODO(), 4)
So(oi, ShouldNotBeNil)
})
}
func TestPushAllPic(t *testing.T) {
Convey("TestGetPic", t, func() {
data := &model.ArgGetQuestion{
UID: "111111",
TargetItem: "1111",
TargetItemType: 1,
Source: 1,
Platform: 1,
ComponentID: 123,
}
_, err := d.GetRandPic(context.TODO(), data)
So(err, ShouldBeNil)
})
}
func TestGetCacheQus(t *testing.T) {
Convey("TestGetCacheQus", t, func() {
_, err := d.GetCacheQus(context.TODO(), 1527241107344)
So(err, ShouldBeNil)
})
}
func TestGetQusKey(t *testing.T) {
Convey("TestGetQusKey", t, func() {
oi := d.GetQusKey(_keyAnsweredIds, qustion)
So(oi, ShouldNotBeNil)
})
}
func TestQusFetchTime(t *testing.T) {
Convey("TestQusFetchTime", t, func() {
oi := d.QusFetchTime(context.TODO(), qustion)
So(oi, ShouldNotBeNil)
})
}
func TestSetComponentId(t *testing.T) {
Convey("TestSetComponentId", t, func() {
err := d.SetComponentID(context.TODO(), qustion)
So(err, ShouldBeNil)
})
}
func TestGetComponentId(t *testing.T) {
Convey("TestGetComponentId", t, func() {
oi, err := d.GetComponentID(context.TODO(), qustion)
So(oi, ShouldNotBeNil)
So(err, ShouldBeNil)
})
}
func TestIncrComponentTimes(t *testing.T) {
Convey("TestIncrComponentTimes", t, func() {
err := d.IncrComponentTimes(context.TODO(), qustion)
So(err, ShouldBeNil)
})
}
func TestGetRandPic(t *testing.T) {
Convey("TestGetRandPic", t, func() {
oi, err := d.GetRandPic(context.TODO(), qustion)
So(err, ShouldBeNil)
So(oi, ShouldNotBeNil)
})
}
func TestSetAnsweredID(t *testing.T) {
Convey("TestSetAnsweredID", t, func() {
_, err := d.RedisDo(context.TODO(), "SADD", "wlt", 1222)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,64 @@
package dao
import (
"context"
"go-common/app/service/openplatform/anti-fraud/conf"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/cache/redis"
"go-common/library/database/sql"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
// Dao init dao
type Dao struct {
c *conf.Config
db *sql.DB // db
payShieldDb *sql.DB // payShieldDb
redis *redis.Pool // redis
client *httpx.Client
payData chan *model.ShieldData
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.AntiFraud),
payShieldDb: sql.NewMySQL(c.DB.PayShield),
redis: redis.NewPool(c.Redis.Config),
client: httpx.NewClient(conf.Conf.HTTPClient.Read),
payData: make(chan *model.ShieldData, 2000),
}
go d.SyncPayShield(context.Background())
go d.PopAnswer(context.TODO())
return
}
// Close close connections.
func (d *Dao) Close() {
if d.db != nil {
d.db.Close()
}
if d.redis != nil {
d.redis.Close()
}
}
// Ping ping health.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
log.Error("PingDb error(%v)", err)
return
}
if err = d.PingRedis(c); err != nil {
log.Error("PingRedis error(%v)", err)
return
}
return
}
// BeginTran begin mysql transaction
func (d *Dao) BeginTran(c context.Context) (*sql.Tx, error) {
return d.db.Begin(c)
}

View File

@@ -0,0 +1,17 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var d *Dao
func TestPing(t *testing.T) {
Convey("TestPing: ", t, func() {
err := d.Ping(context.TODO())
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,102 @@
package dao
import (
"context"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/log"
)
const (
_ipList = "select ip,count(1) as num from shield_ip_log where mtime >= ? group by ip order by num desc limit 50"
_ipDetail = "select ip,uid from shield_ip_log where ip = ? and mtime >= ? and mtime <= ?"
_uidList = "select uid,count(1) as num from shield_user_log where mtime >= ? group by uid order by num desc limit 50"
_uidDetail = "select ip,uid from shield_user_log where uid = ? and mtime >= ? and mtime <= ?"
)
// ShieldIPList .
func (d *Dao) ShieldIPList(c context.Context, mtime string) (res []*model.IPListDetail, err error) {
res = make([]*model.IPListDetail, 0)
rows, err := d.payShieldDb.Query(c, _ipList, mtime)
if err != nil {
log.Warn("select err %s %v", _ipList, err)
return
}
for rows.Next() {
r := new(model.IPListDetail)
if err = rows.Scan(&r.IP, &r.Num); err != nil {
log.Warn("scan err %v", err)
return
}
res = append(res, r)
}
return
}
// ShieldIPDetail .
func (d *Dao) ShieldIPDetail(c context.Context, ip, stime, etime string) (res []*model.ListDetail, err error) {
res = make([]*model.ListDetail, 0)
rows, err := d.payShieldDb.Query(c, _ipDetail, ip, stime, etime)
if err != nil {
log.Warn("select err %s %v", _ipDetail, err)
return
}
for rows.Next() {
r := new(model.ListDetail)
if err = rows.Scan(&r.IP, &r.UID); err != nil {
log.Warn("scan err %v", err)
return
}
res = append(res, r)
}
return
}
// ShieldUIDList .
func (d *Dao) ShieldUIDList(c context.Context, mtime string) (res []*model.UIDListDetail, err error) {
res = make([]*model.UIDListDetail, 0)
rows, err := d.payShieldDb.Query(c, _uidList, mtime)
if err != nil {
log.Warn("select err %s %v", _uidList, err)
return
}
for rows.Next() {
r := new(model.UIDListDetail)
if err = rows.Scan(&r.UID, &r.Num); err != nil {
log.Warn("scan err %v", err)
return
}
res = append(res, r)
}
return
}
// ShieldUIDDetail .
func (d *Dao) ShieldUIDDetail(c context.Context, uid, stime, etime string) (res []*model.ListDetail, err error) {
res = make([]*model.ListDetail, 0)
rows, err := d.payShieldDb.Query(c, _uidDetail, uid, stime, etime)
if err != nil {
log.Warn("select err %s %v", _uidDetail, err)
return
}
for rows.Next() {
r := new(model.ListDetail)
if err = rows.Scan(&r.IP, &r.UID); err != nil {
log.Warn("scan err %v", err)
return
}
res = append(res, r)
}
return
}

View File

@@ -0,0 +1,299 @@
package dao
import (
"context"
"fmt"
"strings"
"time"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_getQusSQL = "SELECT question_id ,question_type,answer_type,question_name,question_bank_id,difficulty,is_deleted FROM question where question_id=? and is_deleted =0 "
_addQusSQL = "INSERT INTO question(question_id ,question_type,answer_type,question_name,question_bank_id,difficulty) VALUES(?,?,?,?,?,?)"
_getQuslistBywhereSQL = "SELECT question_id ,question_type,answer_type,question_name,question_bank_id,difficulty,is_deleted FROM question where is_deleted = 0 and question_bank_id = ? order by id desc limit ? ,? "
_getAllQusByBankID = "SELECT question_id FROM question WHERE question_bank_id = ? AND is_deleted = 0"
_getQuslistSQL = "SELECT question_id ,question_type,answer_type,question_name,question_bank_id,difficulty,is_deleted FROM question where is_deleted = ? limit ? ,? "
_getQusCntSQL = "SELECT COUNT(*) FROM question where is_deleted = 0 "
_delQusSQL = "UPDATE question set is_deleted = ? where question_id =? "
_updateQusSQL = "UPDATE question set question_type = ? ,answer_type=? ,question_name = ? , question_bank_id = ? ,difficulty = ? where question_id =? and is_deleted =0 "
_addAnswerSQL = "INSERT INTO question_answer(answer_id ,question_id,answer_content,is_correct ) VALUES(?,?,?,?)"
_multyAddAnswerSQL = "INSERT INTO question_answer(answer_id ,question_id,answer_content,is_correct ) VALUES %s "
_updateAnswerSQL = "UPDATE question_answer set answer_content = ? , is_correct =? , is_deleted =0 where answer_id = ? "
_delAnswerSQL = "UPDATE question_answer set is_deleted = 1 where question_id =? "
_getAnswerListSQL = "select answer_id ,question_id, answer_content,is_correct from question_answer where question_id =? and is_deleted =0 "
_addUserAnswerSQL = "INSERT INTO question_user_answer(uid ,question_id,platform,source,answers,is_correct ) VALUES(?,?,?,?,?,?)"
_checkAnswerSQL = "SELECT count(1) from question_answer where is_correct =1 and question_id = ? and answer_id IN(%s) "
_getRandomPicSQL = "SELECT x,y,src from question_verify_pic where id =? "
_getListPicSQL = "SELECT id from question_verify_pic limit ? ,? "
_getPicCntSQL = "SELECT COUNT(*) FROM question_verify_pic "
)
// GetQusInfo info
func (d *Dao) GetQusInfo(c context.Context, qid int64) (oi *model.Question, err error) {
oi = &model.Question{}
row := d.db.QueryRow(c, _getQusSQL, qid)
if err = row.Scan(&oi.QsID, &oi.QsType, &oi.AnswerType, &oi.QsName, &oi.QsBId, &oi.QsDif, &oi.IsDeleted); err != nil {
if err == sql.ErrNoRows {
oi = nil
err = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// InsertQus add
func (d *Dao) InsertQus(c context.Context, oi *model.Question) (lastID int64, err error) {
res, err := d.db.Exec(c, _addQusSQL, oi.QsID, oi.QsType, oi.AnswerType, oi.QsName, oi.QsBId, oi.QsDif)
if err != nil {
log.Error("[dao.question|GetQusList] d.db.Query err: %v", err)
return
}
lastID, err = res.LastInsertId()
return
}
// GetQusList list
func (d *Dao) GetQusList(c context.Context, offset int, limitnum int, qBid int64) (res []*model.Question, err error) {
res = make([]*model.Question, 0)
_sql := _getQuslistSQL
if qBid > 0 {
_sql = _getQuslistBywhereSQL
}
rows, err := d.db.Query(c, _sql, qBid, offset, limitnum)
if err != nil {
log.Error("[dao.question|GetQusList] d.db.Query err: %v %d,%d", err, offset, limitnum)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.Question{}
if err = rows.Scan(&oi.QsID, &oi.QsType, &oi.AnswerType, &oi.QsName, &oi.QsBId, &oi.QsDif, &oi.IsDeleted); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// GetQusIds ids
func (d *Dao) GetQusIds(c context.Context, bankID int64) (ids []int64, err error) {
if ids = d.GetBankQuestionsCache(c, bankID); len(ids) > 1 {
return
}
rows, err := d.db.Query(c, _getAllQusByBankID, bankID)
if err != nil {
log.Error("d.GetQusIds(%d) error(%v)", bankID, err)
return
}
defer rows.Close()
for rows.Next() {
var temp int64
if err = rows.Scan(&temp); err != nil {
log.Error("d.GetQusIds(%d) rows.Scan() error(%v)", bankID, err)
return
}
ids = append(ids, temp)
}
d.SetBankQuestionsCache(c, bankID, ids)
return
}
// DelQus del
func (d *Dao) DelQus(c context.Context, qid int64) (affect int64, err error) {
res, err := d.db.Exec(c, _delQusSQL, 1, qid)
if err != nil {
log.Error("d.DelQus(qbid:%d, dmid:%d) error(%v)", qid, 1, err)
return
}
return res.RowsAffected()
}
// UpdateQus update
func (d *Dao) UpdateQus(c context.Context, update *model.ArgUpdateQus, answers []model.Answer) (affect int64, err error) {
res, err := d.db.Exec(c, _updateQusSQL, update.Type, update.AnType, update.Name, update.BId, update.Dif, update.QsID)
if err != nil {
log.Error("d.UpdateQus(qbid:%d, dmid:%d) error(%v)", update.QsID, update.QsID, err)
return
}
return res.RowsAffected()
}
// GetQusCount cnt
func (d *Dao) GetQusCount(c context.Context, bid int64) (total int64, err error) {
var cntSQL string
if bid == 0 {
cntSQL = _getQusCntSQL
err = d.db.QueryRow(c, cntSQL).Scan(&total)
} else {
cntSQL = _getQusCntSQL + "and question_bank_id = ?"
err = d.db.QueryRow(c, cntSQL, bid).Scan(&total)
}
if err != nil {
log.Error("d.GetQusCount error(%v)", err)
return
}
return
}
// InserAnwser add
func (d *Dao) InserAnwser(c context.Context, answer *model.AnswerAdd) (affect int64, err error) {
res, err := d.db.Exec(c, _addAnswerSQL, answer.AnswerID, answer.QsID, answer.AnswerContent, answer.IsCorrect)
if err != nil {
log.Error("d.InserAnwser() error(%v)", err)
return
}
affect, err = res.LastInsertId()
return
}
// MultiAddAnwser add
func (d *Dao) MultiAddAnwser(c context.Context, answers []*model.AnswerAdd) (err error) {
length := len(answers)
if length == 0 {
return
}
values := strings.Trim(strings.Repeat("(?, ?, ?, ?),", length), ",")
args := make([]interface{}, 0)
for _, ins := range answers {
AnswerID := time.Now().UnixNano() / 1e6
time.Sleep(time.Millisecond)
args = append(args, AnswerID, ins.QsID, ins.AnswerContent, ins.IsCorrect)
}
_, err = d.db.Exec(c, fmt.Sprintf(_multyAddAnswerSQL, values), args...)
if err != nil {
log.Error("d.InserAnwser() error(%v)", err)
return
}
return
}
// UpdateAnwser upd
func (d *Dao) UpdateAnwser(c context.Context, answer *model.AnswerAdd) (affect int64, err error) {
res, err := d.db.Exec(c, _updateAnswerSQL, answer.AnswerContent, answer.IsCorrect, answer.AnswerID)
if err != nil {
log.Error("d.UpdateAnwser() error(%v)", err)
return
}
affect, err = res.RowsAffected()
return
}
// DelAnwser del
func (d *Dao) DelAnwser(c context.Context, qusID int64) (affect int64, err error) {
res, err := d.db.Exec(c, _delAnswerSQL, qusID)
if err != nil {
log.Error("d.UpdateAnwser() error(%v)", err)
return
}
affect, err = res.RowsAffected()
return
}
// GetAnswerList list
func (d *Dao) GetAnswerList(c context.Context, qusID int64) (res []*model.Answer, err error) {
res = make([]*model.Answer, 0)
rows, err := d.db.Query(c, _getAnswerListSQL, qusID)
if err != nil {
log.Error("[dao.GetAnswerList] d.db.Query err: %v", err)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.Answer{}
if err = rows.Scan(&oi.AnswerID, &oi.QsID, &oi.AnswerContent, &oi.IsCorrect); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// AddUserAnwser add
func (d *Dao) AddUserAnwser(c context.Context, answer *model.ArgCheckAnswer, isCorrect int8) (affect int64, err error) {
ids := xstr.JoinInts(answer.Answers)
res, err := d.db.Exec(c, _addUserAnswerSQL, answer.UID, answer.QsID, answer.Platform, answer.Source, ids, isCorrect)
if err != nil {
log.Error("d.InserAnwser() error(%v)", err)
return
}
affect, err = res.LastInsertId()
return
}
// CheckAnswer check
func (d *Dao) CheckAnswer(c context.Context, qsid int64, ids []int64) (total int, err error) {
err = d.db.QueryRow(c, fmt.Sprintf(_checkAnswerSQL, xstr.JoinInts(ids)), qsid).Scan(&total)
if err != nil {
log.Error("d.GetQusBankCount error(%v)", err)
return
}
return
}
// GetRandomPic get
func (d *Dao) GetRandomPic(c context.Context, id int) (oi *model.QuestBkPic, err error) {
oi = &model.QuestBkPic{}
row := d.db.QueryRow(c, _getRandomPicSQL, id)
if err = row.Scan(&oi.X, &oi.Y, &oi.Src); err != nil {
if err == sql.ErrNoRows {
oi = nil
err = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// GetAllPicIds ids
func (d *Dao) GetAllPicIds(c context.Context, offset int, limitnum int) (ids []int, err error) {
rows, err := d.db.Query(c, _getListPicSQL, offset, limitnum)
if err != nil {
log.Error("[dao.GetAllPicIds] d.db.Query err: %v %d,%d", err, offset, limitnum)
return
}
defer rows.Close()
for rows.Next() {
var oi int
if err = rows.Scan(&oi); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", ids)
return
}
ids = append(ids, oi)
}
return
}
// GetPicCount cnt
func (d *Dao) GetPicCount(c context.Context) (total int, err error) {
err = d.db.QueryRow(c, _getPicCntSQL).Scan(&total)
if err != nil {
log.Error("d.GetQusCount error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,256 @@
package dao
import (
"bytes"
"context"
"fmt"
"strconv"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_QusBankInfo = "SELECT qb_id ,qb_name,cd_time,max_retry_time,is_deleted FROM question_bank where qb_id= ? and is_deleted= 0 "
_addQusBank = "insert into question_bank(qb_id ,qb_name,cd_time,max_retry_time,is_deleted) values(?,?,?,?,?)"
_qusBanklist = "SELECT qb_id ,qb_name,cd_time,max_retry_time,is_deleted FROM question_bank where is_deleted= 0 and total_cnt < ? and qb_id IN(%s) order by id desc "
_qusBankcnt = "SELECT COUNT(*) FROM question_bank where is_deleted= 0 "
_delQusBank = "update question_bank set is_deleted = ? where qb_id =? "
_searchQusBank = "SELECT qb_id ,qb_name,cd_time,max_retry_time,is_deleted FROM question_bank where is_deleted= 0 and qb_name LIKE ? "
_updateQusBank = "update question_bank set qb_name = ? ,max_retry_time=? ,cd_time = ? where qb_id =? "
_qusBanklistByids = "SELECT qb_id, qb_name, cd_time, max_retry_time, is_deleted FROM question_bank WHERE is_deleted = 0 and qb_id IN(%s)"
_getStaticQusBankList = "SELECT id, qb_id,qb_name,cd_time,max_retry_time,is_deleted,total_cnt ,easy_cnt,normal_cnt,hard_cnt from question_bank where is_deleted =0 "
_getQbID = "SELECT qb_id FROM question_bank where id= ? and is_deleted= 0"
_getQusBankByID = "select a.qb_id,a.qb_name,a.cd_time,a.max_retry_time , a.is_deleted, count(if(b.is_deleted = 0,true,null)) total " +
"from question_bank a left join question b on a.qb_id = b.question_bank_id WHERE a.qb_id IN(%s) GROUP BY a.qb_id HAVING a.is_deleted = 0 "
_getQusBankCnt = "SELECT sum(case when difficulty = 1 then 1 else 0 end ) 'easy', sum(case when difficulty = 2 then 1 else 0 end ) 'normal'," +
"sum(case when difficulty = 3 then 1 else 0 end ) 'hard',count(id) 'total' from question WHERE is_deleted =0 and `question_bank_id`= ?"
_updateQusBankCnt = "update question_bank set easy_cnt = ? ,normal_cnt=? ,hard_cnt = ?,total_cnt = ? where qb_id =? "
)
// GetQusBankInfo info
func (d *Dao) GetQusBankInfo(c context.Context, qbid int64) (oi *model.QuestionBank, err error) {
oi = &model.QuestionBank{}
row := d.db.QueryRow(c, _QusBankInfo, qbid)
if err = row.Scan(&oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted); err != nil {
if err == sql.ErrNoRows {
oi = nil
err = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// InsertQusBank add
func (d *Dao) InsertQusBank(c context.Context, oi *model.QuestionBank) (lastID int64, err error) {
res, err := d.db.Exec(c, _addQusBank, oi.QsBId, oi.QBName, oi.CdTime, oi.MaxRetryTime, oi.IsDeleted)
if err != nil {
log.Error("[dao.question|GetQusBankList] d.db.Query err: %v", err)
return
}
lastID, err = res.LastInsertId()
return
}
// StatisticsQusBank 统计bank
func (d *Dao) StatisticsQusBank(c context.Context, offset int, limitnum int, name string) (res []*model.QusBankSt, err error) {
res = make([]*model.QusBankSt, 0)
var rows *sql.Rows
var _sql string
if name == "" {
_sql = _getStaticQusBankList + " order by id desc limit ? ,? "
rows, err = d.db.Query(c, _sql, offset, limitnum)
} else {
_sql = _getStaticQusBankList + " and qb_name LIKE ? order by id desc limit ? ,? "
name = "%" + name + "%"
rows, err = d.db.Query(c, _sql, name, offset, limitnum)
}
if err != nil {
log.Error("[dao.question|GetQusBankList] d.db.Query err: %v %d,%d", err, offset, limitnum)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.QusBankSt{}
if err = rows.Scan(&oi.ID, &oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted, &oi.TotalCnt, &oi.EasyCnt, &oi.NormalCnt, &oi.HardCnt); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// GetQusBankList list
func (d *Dao) GetQusBankList(c context.Context, cnt int, ids []int64) (res []*model.QuestionBank, err error) {
res = make([]*model.QuestionBank, 0)
rows, err := d.db.Query(c, fmt.Sprintf(_qusBanklist, xstr.JoinInts([]int64(ids))), cnt)
if err != nil {
log.Error("[dao.question|GetQusBankList] d.db.Query err: %v %d,%d", err)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.QuestionBank{}
if err = rows.Scan(&oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// DelQusBank del
func (d *Dao) DelQusBank(c context.Context, qbID int64, status int8) (affect int64, err error) {
res, err := d.db.Exec(c, _delQusBank, status, qbID)
if err != nil {
log.Error("d.UpdateQusBank(qbid:%d, dmid:%d) error(%v)", qbID, status, err)
return
}
s, e := res.RowsAffected()
log.Error("d.UpdateQusBank(qbxxxxxid:%d, dxxxxxmid:%d) error(%v)", qbID, s, e)
return res.RowsAffected()
}
// UpdateQusBank update
func (d *Dao) UpdateQusBank(c context.Context, qbID int64, name string, trytime int64, cdtime int64) (affect int64, err error) {
res, err := d.db.Exec(c, _updateQusBank, name, trytime, cdtime, qbID)
if err != nil {
log.Error("d.UpdateQusBank(qbid:%d, dmid:%d) error(%v)", qbID, trytime, err)
return
}
return res.RowsAffected()
}
// GetQusBankCount cnt
func (d *Dao) GetQusBankCount(c context.Context, name string) (total int64, err error) {
var cntSQL string
if name == "" {
cntSQL = _qusBankcnt
err = d.db.QueryRow(c, cntSQL).Scan(&total)
} else {
cntSQL = _qusBankcnt + " and qb_name LIKE ? "
name = "%" + name + "%"
err = d.db.QueryRow(c, cntSQL, name).Scan(&total)
}
if err != nil {
log.Error("d.GetQusBankCount error(%v)", err)
return
}
return
}
// BankSearch search
func (d *Dao) BankSearch(c context.Context, name string) (res []*model.QuestionBank, err error) {
res = make([]*model.QuestionBank, 0)
rows, err := d.db.Query(c, _searchQusBank, "%"+name+"%")
if err != nil {
log.Error("[dao.question|BankSearch] d.db.Query err: %v", err)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.QuestionBank{}
if err = rows.Scan(&oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// GetQusBankListByIds ids
func (d *Dao) GetQusBankListByIds(c context.Context, ids []int64) (res []*model.QuestionBank, err error) {
res = make([]*model.QuestionBank, 0)
rows, err := d.db.Query(c, fmt.Sprintf(_qusBanklistByids, xstr.JoinInts([]int64(ids))))
if err != nil {
log.Error("[dao.question|GetQusBankListByIds] d.db.Query err: %v", err)
return
}
defer rows.Close()
for rows.Next() {
oi := &model.QuestionBank{}
if err = rows.Scan(&oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted); err != nil {
log.Error("[dao.question|GetOrder] rows.Scan err: %v", res)
return
}
res = append(res, oi)
}
return
}
// GetQBId id
func (d *Dao) GetQBId(c context.Context, id int64) (qbID int64, err error) {
err = d.db.QueryRow(c, _getQbID, id).Scan(&qbID)
if err != nil {
log.Error("d.GetQBId error(%v)", err.Error())
return
}
return
}
// GetBankInfoByQBid byids
func (d *Dao) GetBankInfoByQBid(c context.Context, qbID map[int64]int64) (res []*model.QusBankSt, err error) {
rows, err := d.db.Query(c, fmt.Sprintf(_getQusBankByID, d.CoverStr(qbID)))
fmt.Println(fmt.Sprintf(_getQusBankByID, d.CoverStr(qbID)))
if err != nil {
log.Error(fmt.Sprintf("d.mysql.Query(%s) error(%+v)", _getQusBankByID, err.Error()))
return
}
defer rows.Close()
for rows.Next() {
oi := &model.QusBankSt{}
if err = rows.Scan(&oi.QsBId, &oi.QBName, &oi.CdTime, &oi.MaxRetryTime, &oi.IsDeleted, &oi.TotalCnt); err != nil {
log.Error(fmt.Sprintf("d.mysql.Query(%s) error(%+v)", _getQusBankByID, err.Error()))
return
}
res = append(res, oi)
}
return
}
// CoverStr str
func (d *Dao) CoverStr(strs map[int64]int64) string {
var buf = bytes.NewBuffer(nil)
for _, str := range strs {
buf.WriteString("'")
buf.WriteString(strconv.FormatInt(str, 10))
buf.WriteString("'")
buf.WriteString(",")
}
buf.Truncate(buf.Len() - 1)
return buf.String()
}
// UpdateQsBankCnt 更新数量
func (d *Dao) UpdateQsBankCnt(c context.Context, qid int64) (effid int64, err error) {
oi := &model.QusBankCnt{}
err = d.db.QueryRow(c, _getQusBankCnt, qid).Scan(&oi.EasyCnt, &oi.NormalCnt, &oi.HardCnt, &oi.TotalCnt)
if err != nil {
log.Error("d.UpdateQsBankCnt error(%v)", err.Error())
return
}
res, err := d.db.Exec(c, _updateQusBankCnt, &oi.EasyCnt, &oi.NormalCnt, &oi.HardCnt, &oi.TotalCnt, qid)
if err != nil {
log.Error("d._updateQusBankCnt(qbid:%d, dmid:%d) error(%v)", err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,243 @@
package dao
import (
"context"
"fmt"
"strconv"
"strings"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_addQuestionBankBind = "INSERT INTO question_bank_bind (target_item, target_item_type, bank_id, use_in_time, source, is_deleted) VALUES %s"
_getQuestionBankBindWithDeletedNew = " SELECT a.target_item,a.target_item_type,a.bank_id,a.use_in_time,b.id,b.qb_name" +
" FROM question_bank_bind as a join question_bank as b" +
" ON a.bank_id = b.qb_id" +
" where a.target_item IN(%s) and a.target_item_type = ? and a.source = ?"
_getQuestionBankBind = _getQuestionBankBindWithDeletedNew + " AND a.is_deleted = 0"
_updateQuestionBankBind = "UPDATE question_bank_bind set bank_id = ?, use_in_time = ?, is_deleted = 0" +
" WHERE source = ?" +
" AND target_item_type = ?" +
" AND target_item IN(%s)"
_deleteQuestionBankBind = "UPDATE question_bank_bind SET is_deleted = 1 WHERE target_item IN(%s)" +
" AND target_item_type = ?" +
" AND source = ?"
_getBindItem = "SELECT id, target_item, target_item_type, bank_id, use_in_time, source, is_deleted, ctime, mtime FROM question_bank_bind WHERE bank_id = ? AND is_deleted = 0 limit ?,?"
_countBindItem = "SELECT COUNT(1) as count FROM question_bank_bind WHERE bank_id = ? AND is_deleted = 0"
)
// AddBankBind add
func (d *Dao) AddBankBind(c context.Context, update []model.ArgQuestionBankBind, insert []model.ArgQuestionBankBind) (err error) {
lenUpdate := len(update)
lenInsert := len(insert)
if lenUpdate == 0 && lenInsert == 0 {
return
}
updateItems := map[int64]*model.ArgQuestionBankBindToDb{}
insertItems := map[int64]*model.ArgQuestionBankBindToDb{}
for _, v := range update {
if _, ok := updateItems[v.QsBId]; !ok {
updateItems[v.QsBId] = &model.ArgQuestionBankBindToDb{
QsBId: v.QsBId,
Source: v.Source,
TargetItemType: v.TargetItemType,
UseInTime: v.UseInTime,
TargetItems: []string{v.TargetItems},
}
} else {
updateItems[v.QsBId].TargetItems = append(updateItems[v.QsBId].TargetItems, v.TargetItems)
}
d.DelTargetItemBindCache(c, v.TargetItems)
}
for _, v := range insert {
if _, ok := insertItems[v.QsBId]; !ok {
insertItems[v.QsBId] = &model.ArgQuestionBankBindToDb{
QsBId: v.QsBId,
Source: v.Source,
TargetItemType: v.TargetItemType,
UseInTime: v.UseInTime,
TargetItems: []string{v.TargetItems},
}
} else {
insertItems[v.QsBId].TargetItems = append(insertItems[v.QsBId].TargetItems, v.TargetItems)
}
}
tx, err := d.db.Begin(c)
if err != nil {
log.Error("d.AddBankBind(%v) error(%v)", err)
return
}
defer func() {
if err != nil {
if err = tx.Rollback(); err != nil {
log.Error("tx.Rollback() error(%v)", err)
}
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit() error(%v)", err)
return
}
}()
if len(updateItems) > 0 {
for _, v := range updateItems {
sql := fmt.Sprintf(_updateQuestionBankBind, `"`+strings.Join(v.TargetItems, `","`)+`"`)
_, err = tx.Exec(sql, v.QsBId, v.UseInTime, v.Source, v.TargetItemType)
if err != nil {
log.Error("d.AddBankBind(%v, %v) error(%v)", update, insert, err)
return
}
}
}
if len(insertItems) > 0 {
for _, v := range insertItems {
lenInsert = len(v.TargetItems)
if lenInsert > 0 {
placeholder := strings.Trim(strings.Repeat("(?, ?, ?, ?, ?, ?),", lenInsert), ",")
values := make([]interface{}, 0)
for _, ins := range v.TargetItems {
values = append(values, ins, v.TargetItemType, v.QsBId, v.UseInTime, v.Source, 0)
}
_, err = tx.Exec(fmt.Sprintf(_addQuestionBankBind, placeholder), values...)
if err != nil {
log.Error("d.AddBankBind() tx.Exec(%s) error(%v)", fmt.Sprintf(_addQuestionBankBind, values), err)
return
}
}
}
}
return
}
// GetBankBind 查询绑定关系
func (d *Dao) GetBankBind(c context.Context, source, targetItemType int8, targetItem []string, withDeleted bool) (list []*model.QuestionBankBind, err error) {
list = make([]*model.QuestionBankBind, 0)
if len(targetItem) == 0 {
return
}
sql := _getQuestionBankBind
if withDeleted {
sql = _getQuestionBankBindWithDeletedNew
}
sql = fmt.Sprintf(sql, `"`+strings.Join(targetItem, `","`)+`"`)
rows, err := d.db.Query(c, sql, targetItemType, source)
if err != nil {
log.Error("d.GetBankBind(%v, %v, %v) db.Query() error(%v)", source, targetItemType, targetItem, err)
return
}
defer rows.Close()
for rows.Next() {
tmp := &model.QuestionBankBind{QuestionBank: new(model.QuestionBank)}
err = rows.Scan(&tmp.TargetItem, &tmp.TargetItemType, &tmp.QsBId, &tmp.UseInTime, &tmp.ID, &tmp.QuestionBank.QBName)
if err != nil {
return
}
list = append(list, tmp)
}
return
}
// GetBindBank 获取已绑定的题库信息
func (d *Dao) GetBindBank(c context.Context, source, targetItemType int8, targetItem []string) (binds []*model.QuestionBankBind, err error) {
// list = make([]*model.QuestionBankBind, 0)
binds, err = d.GetBankBind(c, source, targetItemType, targetItem, false)
if len(binds) == 0 {
return
}
var bankIds []int64
for _, bind := range binds {
bankIds = append(bankIds, bind.QsBId)
}
banks, err := d.GetQusBankListByIds(c, bankIds)
if err != nil {
return
}
for _, bind := range binds {
for _, bank := range banks {
if bind.QsBId == bank.QsBId {
bind.QuestionBank = bank
break
}
}
}
return
}
// CountBindItem cnt
func (d *Dao) CountBindItem(c context.Context, bankID int64) (count int64, err error) {
if err = d.db.QueryRow(c, _countBindItem, bankID).Scan(&count); err != nil {
log.Error("d.CountBindItem(%d) error(%v)", bankID, err)
}
return
}
// QuestionBankUnbind 解绑
func (d *Dao) QuestionBankUnbind(c context.Context, delIds []int64, targetType int8, source int8) (err error) {
if len(delIds) > 0 {
sql := fmt.Sprintf(_deleteQuestionBankBind, xstr.JoinInts(delIds))
_, err = d.db.Exec(c, sql, targetType, source)
if err != nil {
log.Error("d.QuestionBankUnbind() tx.Exec(%s) error(%v)", fmt.Sprintf(_deleteQuestionBankBind, xstr.JoinInts(delIds)), err)
return
}
}
for _, delID := range delIds {
d.DelTargetItemBindCache(c, strconv.FormatInt(delID, 10))
}
return
}
// GetBindItem itm
func (d *Dao) GetBindItem(c context.Context, bankID int64, page, pageSize int) (list []*model.QuestionBankBind, total int64, err error) {
list = make([]*model.QuestionBankBind, 0)
rows, err := d.db.Query(c, _getBindItem, bankID, (page-1)*pageSize, pageSize)
if err != nil {
log.Error("d.GetBindItem(%d) error(%v)", bankID, err)
return
}
defer rows.Close()
for rows.Next() {
tmp := &model.QuestionBankBind{}
err = rows.Scan(&tmp.ID, &tmp.TargetItem, &tmp.TargetItemType, &tmp.QsBId, &tmp.UseInTime, &tmp.Source, &tmp.IsDeleted, &tmp.Ctime, &tmp.Mtime)
if err != nil {
return
}
list = append(list, tmp)
}
total, err = d.CountBindItem(c, bankID)
if err != nil {
log.Error("d.GetBindItem(%d, %d, %d) error(%v)", bankID, page, pageSize, err)
return
}
return
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestGetBankBind(t *testing.T) {
Convey("TestGetBankBind: ", t, func() {
res, err := d.GetBankBind(context.TODO(), 1, 1, []string{"1111"}, true)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestGetBindBank(t *testing.T) {
Convey("TestGetBindBank: ", t, func() {
res, err := d.GetBindBank(context.TODO(), 1, 1, []string{"1111"})
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestCountBindItem(t *testing.T) {
Convey("TestCountBindItem: ", t, func() {
res, err := d.CountBindItem(context.TODO(), _qbid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestGetBindItem(t *testing.T) {
Convey("TestCountBindItem: ", t, func() {
res, cnt, err := d.GetBindItem(context.TODO(), _qbid, _page, _pagesize)
So(err, ShouldBeNil)
So(cnt, ShouldNotBeNil)
So(res, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,53 @@
package dao
import (
"context"
"flag"
"fmt"
"testing"
"go-common/app/service/openplatform/anti-fraud/conf"
. "github.com/smartystreets/goconvey/convey"
)
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
d = New(conf.Conf)
}
func TestGetQusBankListByIds(t *testing.T) {
Convey("TestGetQusBankListByIds: ", t, func() {
testIds := []int64{1527233672941, 2, 3}
res, err := d.GetQusBankListByIds(context.TODO(), testIds)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestBankSearch(t *testing.T) {
Convey("TestBankSearch: ", t, func() {
res, err := d.BankSearch(context.TODO(), "name")
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestGetQusBankCount(t *testing.T) {
Convey("GetQusBankCount: ", t, func() {
res, err := d.GetQusBankCount(context.TODO(), "wlt")
So(err, ShouldBeNil)
So(res, ShouldBeGreaterThanOrEqualTo, 0)
})
}
func TestUpdateQsBankCnt(t *testing.T) {
Convey("GetQusBankCount: ", t, func() {
res, err := d.UpdateQsBankCnt(context.TODO(), 1527233672941)
So(err, ShouldBeNil)
So(res, ShouldBeGreaterThanOrEqualTo, 0)
})
}

View File

@@ -0,0 +1,64 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
const (
_qid = 1527480165645
_qbid = 1527233672941
_page = 0
_pagesize = 10
)
func TestGetQusInfo(t *testing.T) {
Convey("TestBankSearch: ", t, func() {
res, err := d.GetQusInfo(context.TODO(), _qid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestGetQusList(t *testing.T) {
Convey("TestBankSearch: ", t, func() {
res, err := d.GetQusList(context.TODO(), _page, _pagesize, _qbid)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
})
}
func TestGetQusIds(t *testing.T) {
Convey("TestGetQusIds: ", t, func() {
res, err := d.GetQusIds(context.TODO(), _qbid)
length := len(res)
So(err, ShouldBeNil)
So(length, ShouldBeGreaterThan, 0)
})
}
func TestGetQusCount(t *testing.T) {
Convey("TestGetQusCount: ", t, func() {
cnt, err := d.GetQusCount(context.TODO(), _qbid)
So(err, ShouldBeNil)
So(cnt, ShouldBeGreaterThan, 0)
})
}
func TestGetAnswerList(t *testing.T) {
Convey("TestGetAnswerList: ", t, func() {
cnt, err := d.GetAnswerList(context.TODO(), _qid)
So(err, ShouldBeNil)
So(cnt, ShouldNotBeNil)
})
}
func TestGDelQus(t *testing.T) {
Convey("TestGetAnswerList: ", t, func() {
cnt, err := d.DelQus(context.TODO(), 1527241107344)
So(err, ShouldBeNil)
So(cnt, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,222 @@
package dao
import (
"bytes"
"context"
"crypto/md5"
"encoding/binary"
"encoding/hex"
"encoding/json"
"github.com/json-iterator/go"
"go-common/app/service/openplatform/anti-fraud/conf"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/cache/redis"
"go-common/library/ecode"
"go-common/library/log"
"net/http"
"time"
)
//CheckSalesTime 检查售卖时间
func (d *Dao) CheckSalesTime(c context.Context, mid, itemID, salesTime, saleTimeOut int64) (err error) {
key := model.GetSalesLimitKey(mid)
conn := d.redis.Get(c)
defer conn.Close()
var (
flag int64
data []byte
)
data, _ = redis.Bytes(conn.Do("GET", key))
json.Unmarshal(data, &flag)
if flag == 1 {
return ecode.AntiSalesTimeErr
}
if salesTime > time.Now().Unix() {
conn.Do("SET", key, 1, "EX", saleTimeOut)
return ecode.AntiSalesTimeErr
}
return nil
}
//CheckIPChange 检查用户ip变更
func (d *Dao) CheckIPChange(c context.Context, mid int64, ip string, changeTime int64) (err error) {
key := model.GetIPChangeKey(mid)
conn := d.redis.Get(c)
defer conn.Close()
var (
flag string
data []byte
)
data, _ = redis.Bytes(conn.Do("GET", key))
json.Unmarshal(data, &flag)
go func(c context.Context, key string, ip string) {
conn := d.redis.Get(c)
defer conn.Close()
conn.Do("SET", key, ip, "EX", changeTime)
}(context.Background(), key, ip)
if flag != "" && flag != ip {
return ecode.AntiIPChangeLimit
}
return nil
}
//CheckLimitNum 检查限制次数
func (d *Dao) CheckLimitNum(c context.Context, key string, num int64, pastTime int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
pastTime = pastTime * 1e9
currentTime := time.Now().UnixNano()
conn.Do("ZADD", key, currentTime, currentTime)
data, _ := redis.Int64(conn.Do("ZCOUNT", key, currentTime-pastTime, currentTime))
go func(c context.Context, key string, pastTime, currentTime int64, flag bool) {
conn := d.redis.Get(c)
defer conn.Close()
conn.Do("EXPIRE", key, pastTime/1e9)
if flag {
conn.Do("ZREMRANGEBYRANK", key, 0, 0)
}
}(context.Background(), key, pastTime, currentTime, data > num)
if data > num {
return ecode.AntiLimitNumUpper
}
return nil
}
//Voucher 凭证
func (d *Dao) Voucher(c context.Context, mid int64, ip string, itemID, customer, voucherType int64) (voucher string) {
s := make([]byte, 0)
buf := bytes.NewBuffer(s)
binary.Write(buf, binary.BigEndian, mid)
binary.Write(buf, binary.BigEndian, []byte(ip))
binary.Write(buf, binary.BigEndian, itemID)
binary.Write(buf, binary.BigEndian, customer)
binary.Write(buf, binary.BigEndian, voucherType)
binary.Write(buf, binary.BigEndian, time.Now().UnixNano())
digest := md5.Sum(buf.Bytes())
voucher = hex.EncodeToString(digest[:])
conn := d.redis.Get(c)
defer conn.Close()
key := model.GetUserVoucherKey(mid, voucher, voucherType)
conn.Do("SET", key, 1, "EX", model.RedisUserVoucherKeyTimeOut)
return
}
//CheckVoucher 验证用户凭证,一次性
func (d *Dao) CheckVoucher(c context.Context, mid int64, voucher string, voucherType int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := model.GetUserVoucherKey(mid, voucher, voucherType)
data, _ := redis.Int64(conn.Do("INCR", key))
conn.Do("DEL", key)
if data < 2 {
return ecode.AntiCheckVoucherErr
}
return nil
}
//IncrGeetestCount 统计一小时内极验的请求数
func (d *Dao) IncrGeetestCount(c context.Context) {
conn := d.redis.Get(c)
defer conn.Close()
key := model.GetGeetestCountKey()
conn.Do("INCR", key)
conn.Do("EXPIRE", key, model.RedisGeetestCountKeyTimeOut)
}
//CheckGeetestCount 检查极验总数是否达到上限
func (d *Dao) CheckGeetestCount(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := model.GetGeetestCountKey()
var (
data []byte
count int64
)
data, _ = redis.Bytes(conn.Do("GET", key))
json.Unmarshal(data, &count)
if count > d.c.Geetest.Count {
log.Info("极验总数达到上限")
return ecode.AntiGeetestCountUpper
}
return
}
// CheckBlack 检测黑名单
func (d *Dao) CheckBlack(c context.Context, customerId, mid int64, clientIP string) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
midKey := model.GetMIDBlackKey(customerId, mid)
ipKey := model.GetIPBlackKey(customerId, clientIP)
data, err := redis.Int64s(conn.Do("MGET", midKey, ipKey))
if err != nil {
log.Info("获取黑名单出错 %s %s", midKey, ipKey)
return nil
}
for _, value := range data {
if value == 1 {
return ecode.AntiBlackErr
}
}
return nil
}
// PayShield 支付风控
func (d *Dao) PayShield(c context.Context, data *model.ShieldData) {
var res struct {
errno int64
msg string
data interface{}
}
params, err := jsoniter.Marshal(data)
if err != nil {
log.Info("json marshal err %v", err)
return
}
log.Info("req pay shield params %s", string(params))
req, err := http.NewRequest("POST", conf.Conf.URL.Shield, bytes.NewBuffer(params))
if err != nil {
log.Warn("new request err url %s, err %v", conf.Conf.URL.Shield, err)
return
}
req.Header.Set("Content-Type", "application/json")
err = d.client.Do(c, req, &res)
if err != nil || res.errno != 0 {
log.Warn("client do err url %s, err %v", conf.Conf.URL.Shield, err)
return
}
}
// SetexRedisKey 设置redis key
func (d *Dao) SetexRedisKey(c context.Context, key string, timeout int64) {
conn := d.redis.Get(c)
defer conn.Close()
conn.Do("SET", key, 1, "EX", timeout)
}
// AddPayData .
func (d *Dao) AddPayData(data *model.ShieldData) {
d.payData <- data
}
// SyncPayShield .
func (d *Dao) SyncPayShield(c context.Context) {
for {
data := <-d.payData
d.PayShield(c, data)
}
}

View File

@@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"geetest.go",
"param.go",
"payShield.go",
"question.go",
"risk.go",
],
importpath = "go-common/app/service/openplatform/anti-fraud/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//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,76 @@
package model
import (
"fmt"
"time"
)
//缓存key
const (
//RedisSalesLimitKey 售卖时间限制,mid:SALESLIMIT:ANTI
RedisSalesLimitKey = "%d:SALESLIMIT:ANTI"
//RedisIPChangeKey 用户ip变更mid:IPCHANGELIMIT:ANTI
RedisIPChangeKey = "%d:IPCHANGELIMIT:ANTI"
//RedisCreateMIDLimitKey 同一mid下单次数限制,mid:itemId:MID:CREATELIMIT:ANTI
RedisCreateMIDLimitKey = "%d:%d:MID:CREATELIMIT:ANTI"
//RedisCreateIPLimitKey 同一mid下单次数限制,ip:itemId:IP:CREATELIMIT:ANTI
RedisCreateIPLimitKey = "%s|%d:IP:CREATELIMIT:ANTI"
//RedisUserVoucherKey 用户凭证keymid:voucher:voucherType:VOUCHER:ANTI
RedisUserVoucherKey = "%d:%s:%d:VOUCHER:ANTI"
RedisUserVoucherKeyTimeOut = 600
//RedisGeetestCountKey 拉起极验的总数
RedisGeetestCountKey = "%d:ANTI:GEETEST:COUNT"
RedisGeetestCountKeyTimeOut = 3600
//RedisMIDBlackKey mid黑名单key
RedisMIDBlackKey = "ANTI:MID:BLACK:%d:%d"
//RedisIPBlackKey ip黑名单key
RedisIPBlackKey = "ANTI:IP:BLACK:%d:%s"
)
//GetSalesLimitKey 获取售卖时间限制key
func GetSalesLimitKey(mid int64) (key string) {
return fmt.Sprintf(RedisSalesLimitKey, mid)
}
//GetIPChangeKey 获取用户ip变更key
func GetIPChangeKey(mid int64) (key string) {
return fmt.Sprintf(RedisIPChangeKey, mid)
}
//GetCreateMIDLimitKey 获取mid创单限制key
func GetCreateMIDLimitKey(mid int64, itemID int64) (key string) {
return fmt.Sprintf(RedisCreateMIDLimitKey, mid, itemID)
}
//GetCreateIPLimitKey 获取ip创单限制key
func GetCreateIPLimitKey(ip string, itemID int64) (key string) {
return fmt.Sprintf(RedisCreateIPLimitKey, ip, itemID)
}
//GetUserVoucherKey 获取用户凭证key
func GetUserVoucherKey(mid int64, voucher string, voucherType int64) (key string) {
return fmt.Sprintf(RedisUserVoucherKey, mid, voucher, voucherType)
}
//GetGeetestCountKey 获取极验总数key
func GetGeetestCountKey() (key string) {
current := time.Now().Unix()
return fmt.Sprintf(RedisGeetestCountKey, current/RedisGeetestCountKeyTimeOut)
}
//GetMIDBlackKey 获取mid黑名单key
func GetMIDBlackKey(customerId int64, mid int64) (key string) {
return fmt.Sprintf(RedisMIDBlackKey, customerId, mid)
}
//GetIPBlackKey 获取mid黑名单key
func GetIPBlackKey(customerId int64, clientIP string) (key string) {
return fmt.Sprintf(RedisIPBlackKey, customerId, clientIP)
}

View File

@@ -0,0 +1,8 @@
package model
const (
//GeetestCaptchaID 极验的id
GeetestCaptchaID = "3378262dc41a29fef92707dc5709d53d"
//GeetestPrivateKey 极验的key
GeetestPrivateKey = "de7f98dec5551fa0cca52c51b09bd1f1"
)

View File

@@ -0,0 +1,157 @@
package model
// ArgGetQusBank 题库
type ArgGetQusBank struct {
QsBId int64 `form:"qb_id" validate:"required"`
Stat int64 `form:"idel" validate:"min=0"`
}
// ArgAddQusBank 添加题库
type ArgAddQusBank struct {
QBName string `json:"qb_name" validate:"required"`
CdTime int64 `json:"cd_time" validate:"required,min=1,gte=1"`
MaxRetryTime int64 `json:"max_retry_time" validate:"required,min=1,gte=1"`
}
// ArgBaseBank 基本题库
type ArgBaseBank struct {
QsBId int64 `json:"qb_id" validate:"required"`
}
// ArgUpdateQusBank 更新题库
type ArgUpdateQusBank struct {
QsBId int64 `json:"qb_id" validate:"required"`
QBName string `json:"qb_name"`
CdTime int64 `json:"cd_time" validate:"min=1,gte=1"`
MaxRetryTime int64 `json:"max_retry_time" validate:"min=1,gte=1"`
}
// ArgPage 分页
type ArgPage struct {
PageNo int `form:"page" validate:"required,min=1,gte=1"`
PageSize int `form:"page_size" validate:"required,min=1,gte=1"`
}
// ArgBankList 题库列表
type ArgBankList struct {
ArgPage
Name string `form:"key"`
}
// ArgGetQus 题目
type ArgGetQus struct {
QsID int64 `form:"qid" validate:"required"`
Stat int64 `form:"status" validate:"min=0"`
}
// ArgBaseQus 基本题目信息
type ArgBaseQus struct {
Type int8 `json:"question_type" validate:"required,min=1,max=4"`
AnType int8 `json:"answer_type" validate:"required,min=1,max=4"`
Name string `json:"question_name" validate:"required,min=1,gte=1"`
BId int64 `json:"qb_id" validate:"required,min=1,gte=1"`
Dif int8 `json:"difficulty" validate:"required,min=1,max=3"`
Answer []Answer `json:"answer" validate:"required"`
}
// ArgAddQus 添加题目
type ArgAddQus struct {
ArgBaseQus
QsID string `json:"qid" validate:""`
}
// ArgUpdateQus 更新题目
type ArgUpdateQus struct {
ArgBaseQus
QsID int64 `json:"qid" validate:"required"`
}
// ArgQusList 题目列表
type ArgQusList struct {
ArgPage
QsBId int64 `form:"qb_id" validate:""`
}
// AddQus 添加题目
type AddQus struct {
QsID int64 `json:"qid" validate:""`
Type int8 `json:"question_type" validate:"required"`
AnType int8 `json:"answer_type" validate:"required,min=1,max=4"`
Name string `json:"question_name" validate:"required,min=1,gte=1"`
BId int64 `json:"qb_id" validate:"required,min=1,gte=1"`
Dif int8 `json:"difficulty" validate:"required,min=1,gte=1"`
Answers []Answer
}
// ArgQuestionBankBind 关联题库/修改关联
type ArgQuestionBankBind struct {
QsBId int64 `json:"qb_id" form:"qb_id" validate:"required"`
Source int8 `json:"source" validate:"required"`
TargetItemType int8 `json:"target_item_type" validate:"required"`
UseInTime int64 `json:"use_in_time" validate:"required"`
TargetItems string `json:"target_items" validate:"required"`
}
// ArgQuestionBankBinds to do
type ArgQuestionBankBinds struct {
BandBinds []ArgQuestionBankBind `json:"bind_info" validate:"required"`
}
// ArgQuestionBankBindToDb to do
type ArgQuestionBankBindToDb struct {
QsBId int64
Source int8
TargetItemType int8
UseInTime int64
TargetItems []string
}
// ArgQuestionBankUnbind 关联题库/修改关联
type ArgQuestionBankUnbind struct {
TargetItems []int64 `json:"target_items" validate:"required"`
TargetItemType int8 `json:"target_item_type" validate:"required"`
Source int8 `json:"source" validate:"required"`
}
// ArgGetBankBind 查询关联题库信息
type ArgGetBankBind struct {
TargetItems []string `json:"target_items" validate:"required"`
TargetItemType int8 `json:"target_item_type" validate:"required"`
Source int8 `json:"source" validate:"required"`
}
// ArgGetQuestion 随机获取一道题
type ArgGetQuestion struct {
UID string `form:"uid" json:"uid" validate:"required"`
TargetItem string `form:"target_item" json:"target_item" validate:"required"`
TargetItemType int8 `form:"target_item_type" json:"target_item_type" validate:"required"`
Source int8 `form:"source" json:"source" validate:"required"`
Platform int8 `form:"platform" json:"platform" validate:"required"`
ComponentID int `form:"component_id" json:"component_id" validate:"required"`
}
// ArgGetBindItems 绑定
type ArgGetBindItems struct {
ArgPage
QsBId int64 `form:"qb_id" validate:"required"`
}
// ArgBankSearch 搜索
type ArgBankSearch struct {
Name string `form:"name" validate:"required"`
}
// ArgCheckAnswer 答案检查
type ArgCheckAnswer struct {
ArgGetQuestion
QsID int64 `json:"qid" validate:"required"`
Answers []int64 `json:"answers"`
X int `json:"x"`
Y int `json:"y"`
}
// ArgCheckQus 题库检查
type ArgCheckQus struct {
QusIDs []int64 `json:"ids"`
Cnt int `json:"cnt"`
}

View File

@@ -0,0 +1,19 @@
package model
// IPListDetail .
type IPListDetail struct {
IP string
Num int64
}
// UIDListDetail .
type UIDListDetail struct {
UID string
Num int64
}
// ListDetail .
type ListDetail struct {
IP string
UID string
}

View File

@@ -0,0 +1,134 @@
package model
import (
"database/sql"
"go-common/library/time"
)
// 常量
const (
DeledStatus = 1 // 已删除
PAGESIZE = 20 // 每页条数
STARTINDEX = 20 // 开始页码
MULTIPLECHOICE = 2 //多选
)
// Question 题目返回数据
type Question struct {
QsID int64 `json:"qid"`
QsType int8 `json:"question_type"`
AnswerType int8 `json:"answer_type"`
QsName string `json:"question_name"`
QsDif int8 `json:"difficulty"`
QsBId int64 `json:"qb_id"`
IsDeleted uint8 `json:"is_deleted"`
//Ctime string `json:"ctime"`
//Mtime string `json:"mtime"`
}
// GetQuestionItem 获取题目接口返回数据
type GetQuestionItem struct {
*Question
Answers []*Answer `json:"answers"`
QuestBkPic *QuestBkPic `json:"qspic"`
AllCnt int64 `json:"total"`
AnTime int64 `json:"answer_cnt"`
}
// QuestBkPic 坐标
type QuestBkPic struct {
X int `json:"x"`
Y int `json:"y"`
Src string `json:"src"`
}
// QuestionAll 题目所有
type QuestionAll struct {
Question
AnswersList []*Answer `json:"answers"`
}
//QuestionBank stuct
type QuestionBank struct {
//ID int64 `json:"id"`
QsBId int64 `json:"qb_id"`
QBName string `json:"qb_name"`
CdTime int64 `json:"cd_time"`
MaxRetryTime int64 `json:"max_retry_time"`
IsDeleted int8 `json:"is_deleted"`
}
// QusBankSt 返回
type QusBankSt struct {
QuestionBank
ID int64 `json:"id"`
TotalCnt int64 `json:"total_cnt"`
EasyCnt int64 `json:"easy_cnt"`
NormalCnt int64 `json:"normal_cnt"`
HardCnt int64 `json:"hard_cnt"`
}
// QusBankCnt 统计类
type QusBankCnt struct {
ID int64 `json:"id"`
TotalCnt int64 `json:"total_cnt"`
EasyCnt sql.NullInt64 `json:"easy_cnt"`
NormalCnt sql.NullInt64 `json:"normal_cnt"`
HardCnt sql.NullInt64 `json:"hard_cnt"`
}
//Answer stuct
type Answer struct {
QsID int64 `json:"qid"`
AnswerContent string `json:"answer_content"`
IsCorrect int8 `json:"is_correct"`
AnswerID int64 `json:"answer_id"`
}
// AnswerAdd add
type AnswerAdd struct {
Answer
}
// AddReturn return
type AddReturn struct {
ID int64 `json:"id"`
}
// Page page
type Page struct {
Total int64 `json:"total"`
PageNo int `json:"page_no" default:"1"`
PageSize int `json:"page_size" default:"20"`
}
// QuestionBankBind 绑定题库字段
type QuestionBankBind struct {
ID int64 `json:"id"`
TargetItem string `json:"target_item" validate:"required"`
TargetItemType int8 `json:"target_item_type" validate:"required"`
QsBId int64 `json:"bank_id" validate:"required"`
UseInTime int64 `json:"use_in_time" validate:"required"`
Source int8 `json:"source" validate:"required"`
IsDeleted int8 `json:"is_deleted"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
QuestionBank *QuestionBank `json:"question_bank,omitempty"`
}
// RespList 返回
type RespList struct {
Page
Items interface{} `json:"items"`
}
// AddLog log
type AddLog struct {
UID string
QsID int64
Platform int8
Source int8
Ids string
IsCorrect int8
}

View File

@@ -0,0 +1,120 @@
package model
//风险常量
const (
//ServerOutage 服务不可用
ServerOutage = 0
//ServerNormal 服务正常
ServerNormal = 1
//RankNormal 正常
RankNormal = 0
//RankAbnormal 不正常
RankAbnormal = 1
//RankDoubt 可疑
RankDoubt = 2
//MethodPass 通过
MethodPass = 0
//MethodBan 禁止
MethodBan = 1
//MethodGeetest 极验
MethodGeetest = 2
//MethodQuestion 答题
MethodQuestion = 3
//VoucherTypePull 凭证拉起
VoucherTypePull = 1
//VoucherTypeCheck 凭证验证
VoucherTypeCheck = 2
CheckPass = "验证通过"
CheckSaleErr = "未到售卖时间"
CheckMidEnough = "mid下单次数达到上限"
CheckIPEnough = "IP下单次数达到上限"
CheckIPChange = "用户网络环境变更"
RiskLevelSuperHigh = 1
RiskLevelHigh = 2
RiskLevelMiddle = 3
RiskLevelLight = 4
RiskLevelNormal = 5
)
// DeviceInfo 设备信息
type DeviceInfo struct {
UA string `json:"ua"`
Info string `json:"info"`
Type string `json:"type"`
Platform string `json:"platform"`
Build string `json:"build"`
}
// ItemInfo 商品
type ItemInfo struct {
ID int64 `json:"id"`
Name string `json:"name"`
SaleTime int64 `json:"saleTime"`
Count int64 `json:"count"`
Money int64 `json:"money"`
}
// BuyerInfo 购买人
type BuyerInfo struct {
ID int64 `json:"id"`
Name string `json:"name"`
PersonalID string `json:"personalId"`
IDCardFront string `json:"idCardFront"`
IDCardBack string `json:"idCardBack"`
}
// AddrInfo 收货地址
type AddrInfo struct {
ID int64 `json:"id"`
Name string `json:"name"`
Phone string `json:"phone"`
ProvID int64 `json:"provId"`
Prov string `json:"prov"`
CityID int64 `json:"cityId"`
City string `json:"city"`
AreaID int64 `json:"areaId"`
Area string `json:"area"`
Addr string `json:"addr"`
}
// ShieldData .
type ShieldData struct {
CustomerID int64 `json:"customerId"`
UID string `json:"uid"`
TraceID string `json:"traceId"`
Timestamp int64 `json:"timestamp"`
UserClientIp string `json:"userClientIp"`
DeviceID string `json:"deviceId"`
SourceIP string `json:"sourceIp"`
InterfaceName string `json:"interfaceName"`
PayChannel string `json:"payChannel"`
ReqData *ReqData `json:"reqData"`
ExtShield *ExtShield `json:"extShield"`
}
// ReqData 业务方信息
type ReqData struct {
ItemID []int64 `json:"itemId"`
AddrID int64 `json:"addrId"`
BuyerID int64 `json:"buyerId"`
}
// ExtShield .
type ExtShield struct {
OrderID int64 `json:"orderId"`
RiskLevel int64 `json:"riskLevel"`
ShieldResult int64 `json:"shieldResult"`
ShieldMsg string `json:"shieldMsg"`
Source string `json:"source"`
}
// ShieldIPList .
type ShieldIPList struct {
IP string `json:"ip"`
Num string `json:"num"`
}

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 = ["server.go"],
importpath = "go-common/app/service/openplatform/anti-fraud/server/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/openplatform/anti-fraud/service:go_default_library",
"//library/net/rpc/warden:go_default_library",
"@org_golang_google_grpc//: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,27 @@
package grpc
import (
"context"
"go-common/app/service/openplatform/anti-fraud/service"
"go-common/library/net/rpc/warden"
"google.golang.org/grpc"
)
//New 生成rpc服务
func New(svc *service.Service) *warden.Server {
s := warden.NewServer(nil)
s.Use(middleware())
_, err := s.Start()
if err != nil {
panic("run server failed!" + err.Error())
}
return s
}
func middleware() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
//call chain
resp, err = handler(ctx, req)
return
}
}

View File

@@ -0,0 +1,64 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"http_test.go",
"question_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//library/ecode:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"graph.go",
"http.go",
"question.go",
"risk.go",
],
importpath = "go-common/app/service/openplatform/anti-fraud/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/openplatform/anti-fraud/api/grpc/v1:go_default_library",
"//app/service/openplatform/anti-fraud/api/http/v1:go_default_library",
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//app/service/openplatform/anti-fraud/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/verify: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,24 @@
package http
import (
"go-common/app/service/openplatform/anti-fraud/api/grpc/v1"
bm "go-common/library/net/http/blademaster"
)
//graphPrepare 拉起图形验证
func graphPrepare(c *bm.Context) {
params := new(v1.GraphPrepareRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.GraphPrepare(c, params))
}
//graphCheck 图形验证
func graphCheck(c *bm.Context) {
params := new(v1.GraphCheckRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.GraphCheck(c, params))
}

View File

@@ -0,0 +1,91 @@
package http
import (
"go-common/app/service/openplatform/anti-fraud/conf"
"go-common/app/service/openplatform/anti-fraud/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"net/http"
)
var (
vfySvc *verify.Verify
svc *service.Service
)
// Init init
func Init(c *conf.Config) {
initService(c)
// init router
engine := bm.DefaultServer(nil)
innerRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
// initService init services.
func initService(c *conf.Config) {
vfySvc = verify.New(nil)
svc = service.New(c)
}
// innerRouter init inner router api path.
func innerRouter(e *bm.Engine) {
e.Ping(ping)
//group := e.Group("/openplatform/internal/anti/fraud",idfSvc.Verify)
group := e.Group("/openplatform/internal/antifraud")
{
group.GET("/qusb/info", qusBankInfo) //题库单条信息
group.GET("/qusb/list", qusBankList) //题库列表
group.POST("/qusb/add", qusBankAdd) //题库添加
group.POST("/qusb/del", qusBankDel) //题库删除
group.POST("/qusb/update", qusBankEdit) //题库修改
group.POST("/qusb/check", qusBankCheck) // 答题
group.GET("/qs/info", qusInfo) //题目信息
group.GET("/qs/list", qusList) //题目列表
group.POST("/qs/add", qusAdd) //题目添加
group.POST("/qs/update", qusUpdate) //题目更新
group.POST("/qs/del", qusDel) //题目删除
group.GET("/qs/get", getQuestion) //题目获取
group.POST("/qs/answer", answerQuestion) // 答题
group.POST("/bind", questionBankBind) // 绑定题库
group.POST("/unbind", questionBankUnbind) // 解绑题库
group.POST("/bind/bank", getBankBind) // 查询已绑定的题库
group.GET("/bind/items", getBindItems) // 查询绑定到题库的 items
group.GET("/risk/check", riskCheck) //风险检验
group.POST("/risk/check/v2", riskCheckV2) //风险检验v2
group.GET("/graph/prepare", graphPrepare) //拉起图形验证
group.POST("/graph/check", graphCheck) //图形验证
}
group2 := e.Group("/openplatform/admin/antifraud/shield")
{
group2.GET("/risk/ip/list", ipList) //ip列表
group2.GET("/risk/ip/detail", ipDetail) //ip详情列表
group2.GET("/risk/uid/list", uidList) //uid列表
group2.GET("/risk/uid/detail", uidDetail) //uid详情列表
group2.POST("/risk/ip/black", ipBlack) //设置ip黑名单
group2.POST("/risk/uid/black", uidBlack) //设置uid黑名单
}
}
// outerRouter init outer router.
//func outerRouter(e *bm.Engine) {
//}
// ping check server ok.
func ping(c *bm.Context) {
if err := svc.Ping(c); err != nil {
log.Error("open-abtest http service ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,22 @@
package http
import (
"flag"
"fmt"
"go-common/app/service/openplatform/anti-fraud/conf"
httpx "go-common/library/net/http/blademaster"
_ "github.com/smartystreets/goconvey/convey"
)
var client *httpx.Client
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(fmt.Errorf("conf.Init() error(%v)", err))
}
client = httpx.NewClient(conf.Conf.HTTPClient.Read)
Init(conf.Conf)
}

View File

@@ -0,0 +1,245 @@
package http
import (
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// qusBankInfo 题库信息
func qusBankInfo(c *bm.Context) {
params := new(model.ArgGetQusBank)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.GetQusBankInfo(c, params.QsBId))
}
// qusBankList 题库列表
func qusBankList(c *bm.Context) {
params := new(model.ArgBankList)
if err := c.Bind(params); err != nil {
return
}
result := model.RespList{}
list, err := svc.GetQusBanklist(c, params.PageNo, params.PageSize, params.Name)
if err != nil {
c.JSON(nil, err)
return
}
total, err := svc.GetQusBankTotal(c, params.Name)
if err != nil {
c.JSON(nil, err)
return
}
result.Items = list
result.PageSize = params.PageSize
result.PageNo = params.PageNo
result.Total = total
c.JSON(result, nil)
}
// qusBankAdd 添加题库
func qusBankAdd(c *bm.Context) {
params := new(model.ArgAddQusBank)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.AddQusBank(c, params))
}
// qusBankEdit 更新题库
func qusBankEdit(c *bm.Context) {
params := new(model.ArgUpdateQusBank)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.UpdataQusBank(c, params.QsBId, params.QBName, params.MaxRetryTime, params.CdTime))
}
// qusBankDel 删除题库
func qusBankDel(c *bm.Context) {
params := new(model.ArgBaseBank)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.DelQusBank(c, params.QsBId, model.DeledStatus))
}
// qusInfo 题目详情
func qusInfo(c *bm.Context) {
params := new(model.ArgGetQus)
if err := c.Bind(params); err != nil {
return
}
data := &model.QuestionAll{}
answers, err := svc.GetAnswerList(c, params.QsID)
if err != nil {
c.JSON(nil, ecode.QusIDInvalid)
return
}
info, err := svc.GetQusInfo(c, params.QsID)
if err != nil || info == nil {
c.JSON(nil, ecode.QusIDInvalid)
return
}
data.Question = *info
data.AnswersList = answers
c.JSON(data, err)
}
// qusList 题目列表
func qusList(c *bm.Context) {
params := new(model.ArgQusList)
if err := c.Bind(params); err != nil {
return
}
result := model.RespList{}
list, err := svc.GetQuslist(c, params.PageNo, params.PageSize, params.QsBId)
if err != nil {
c.JSON(nil, err)
return
}
total, err := svc.GetQusTotal(c, params.QsBId)
if err != nil {
c.JSON(nil, err)
return
}
result.Items = list
result.PageSize = params.PageSize
result.PageNo = params.PageNo
result.Total = total
c.JSON(result, nil)
}
// qusAdd 题目添加
func qusAdd(c *bm.Context) {
params := new(model.ArgAddQus)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
anlist := params.Answer
msg, err := svc.CheckAnswer(c, 0, params.Type, anlist)
if err != nil {
c.JSON(msg, err)
return
}
addQus := &model.AddQus{
BId: params.BId,
Type: params.Type,
Name: params.Name,
Dif: params.Dif,
AnType: params.AnType,
Answers: anlist,
QsID: 0,
}
c.JSON(svc.AddQus(c, addQus, anlist))
}
// qusUpdate 题目更新
func qusUpdate(c *bm.Context) {
params := new(model.ArgUpdateQus)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
answers := params.Answer
msg, err := svc.CheckAnswer(c, params.QsID, params.Type, answers)
if err != nil {
c.JSON(msg, err)
return
}
c.JSON(svc.UpdateQus(c, params, answers))
}
// questionBankBind 项目关联题库
func questionBankBind(c *bm.Context) {
params := &model.ArgQuestionBankBinds{}
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
err := svc.QuestionBankBind(c, params)
if err != nil {
log.Error("questionBankBind(%v) error(%v)", params, err)
}
c.JSON(nil, err)
}
// questionBankUnbind 解绑
func questionBankUnbind(c *bm.Context) {
params := &model.ArgQuestionBankUnbind{}
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(nil, svc.QuestionBankUnbind(c, params))
}
// qusDel 删除题目
func qusDel(c *bm.Context) {
params := new(model.ArgGetQus)
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.DelQus(c, params.QsID))
}
// getBankBind 绑定关系
func getBankBind(c *bm.Context) {
params := &model.ArgGetBankBind{}
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.GetQuestionBankBind(c, params))
}
// getBindItems 查询绑定到题库的
func getBindItems(c *bm.Context) {
params := &model.ArgGetBindItems{}
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.GetBindItem(c, params))
}
// getQuestion 随机获取一个问题
func getQuestion(c *bm.Context) {
params := &model.ArgGetQuestion{}
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.GetQuestion(c, params))
}
// answerQuestion 答题
func answerQuestion(c *bm.Context) {
// 判断是否可答 返回冷却时间
params := &model.ArgCheckAnswer{}
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.UserAnswer(c, params))
}
// qusBankCheck 题库检查
func qusBankCheck(c *bm.Context) {
// 判断是否可答 返回冷却时间
params := &model.ArgCheckQus{}
if err := c.BindWith(params, binding.JSON); err != nil {
return
}
c.JSON(svc.QusBankCheck(c, params))
}

View File

@@ -0,0 +1,180 @@
package http
import (
"context"
"net/url"
"testing"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/ecode"
. "github.com/smartystreets/goconvey/convey"
)
const (
_qusBankInfoURL = "http://localhost:8801/openplatform/internal/antifraud/qusb/info?qbid=100"
_qusBanklistURL = "http://localhost:8801/openplatform/internal/antifraud/qusb/list"
_qslistURL = "http://localhost:8801/openplatform/internal/antifraud/qs/list"
_qsInfoURL = "http://localhost:8801/openplatform/internal/antifraud/qs/info"
_qsGetURL = "http://localhost:8801/openplatform/internal/antifraud/qs/get"
)
type TestData map[string]string
type Shoulds []interface{}
type TestCase struct {
tag string
testData TestData
should Shoulds
}
var glcs = []TestCase{
{tag: "TestQusBankList: valid parameters", testData: TestData{"page": "1", "page_size": "20"}, should: Shoulds{-0}},
{tag: "TestQusBankList: no page", testData: TestData{"page_size": "1"}, should: Shoulds{-400}},
{tag: "TestQusBankList: no page_size", testData: TestData{"page": "1"}, should: Shoulds{-400}},
{tag: "TestQusBankList: no mstatus", testData: TestData{"page": "a", "page_size": "b"}, should: Shoulds{-400}},
{tag: "TestQusBankList: invalid page", testData: TestData{"page": "a", "page_size": "20"}, should: Shoulds{-400}},
{tag: "TestQusBankList: invalid page_size", testData: TestData{"page": "1", "page_size": "a"}, should: Shoulds{-400}},
}
func TestQusBankList(t *testing.T) {
for _, td := range glcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _qusBanklistURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data struct {
Result interface{} `json:"result"`
Total interface{} `json:"total"`
PageNo interface{} `json:"page_no"`
PageSize interface{} `json:"page_size"`
Items interface{} `json:"items"`
} `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
func TestQusList(t *testing.T) {
for _, td := range glcs {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _qslistURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data struct {
Result interface{} `json:"result"`
Total interface{} `json:"total"`
PageNo interface{} `json:"page_no"`
PageSize interface{} `json:"page_size"`
Items interface{} `json:"items"`
} `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var argsBankInfo = []TestCase{
{tag: "TestQusBankInfo: valid parameters", testData: TestData{"qb_id": "1111"}, should: Shoulds{0}},
{tag: "TestQusBankInfo: no qb_id", testData: TestData{"qb_id": "1"}, should: Shoulds{0}},
{tag: "TestQusBankInfo: invalid qb_id", testData: TestData{"qb_id": "a"}, should: Shoulds{-400}},
}
func TestQusBankInfo(t *testing.T) {
for _, td := range argsBankInfo {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _qusBankInfoURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data model.QuestionBank `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var argsQusInfo = []TestCase{
{tag: "TestQusBankInfo: valid parameters", testData: TestData{"qid": "1111"}, should: Shoulds{20001005}},
{tag: "TestQusBankInfo: invalid qid", testData: TestData{"qid": "a"}, should: Shoulds{-400}},
}
func TestQusInfo(t *testing.T) {
for _, td := range argsQusInfo {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _qsInfoURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data model.QuestionBank `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldEqual, td.should[0])
})
}
}
var argsGetQuestion = []TestCase{
{tag: "TestQusBankInfo: valid parameters", testData: TestData{"uid": "1111", "target_item": "11111", "target_item_type": "1", "source": "1", "platform": "1", "component_id": "122"},
should: Shoulds{ecode.BindBankNotFound.Code(), ecode.GetComponentIDErr.Code(), ecode.SetComponentIDErr.Code(), 0}},
{tag: "TestQusBankInfo: invalid ", testData: TestData{"uid": "a"}, should: Shoulds{-400, -400}},
}
func TestGetQuestion(t *testing.T) {
for _, td := range argsGetQuestion {
Convey(td.tag, t, func() {
params := url.Values{}
for k, v := range td.testData {
params.Set(k, v)
}
req, _ := client.NewRequest("GET", _qsGetURL, "127.0.0.1", params)
var res struct {
Code int `json:"code"`
Data model.QuestionBank `json:"data"`
}
if err := client.Do(context.TODO(), req, &res); err != nil {
t.Errorf("client.Do() error(%v)", err)
t.FailNow()
}
So(res.Code, ShouldBeIn, td.should...)
})
}
}

View File

@@ -0,0 +1,97 @@
package http
import (
grpcv1 "go-common/app/service/openplatform/anti-fraud/api/grpc/v1"
"go-common/app/service/openplatform/anti-fraud/api/http/v1"
"go-common/app/service/openplatform/anti-fraud/model"
bm "go-common/library/net/http/blademaster"
)
//riskCheck 风险检查
func riskCheck(c *bm.Context) {
params := new(grpcv1.RiskCheckRequest)
if err := c.Bind(params); err != nil {
return
}
res, err := svc.RiskCheck(c, params)
svc.Ticket2PayShield(c, params, res)
if res.Rank == model.RankDoubt {
res.Rank = model.RankNormal
res.Method = model.MethodPass
res.Desc = model.CheckPass
}
c.JSON(res, err)
}
//riskCheckV2 风险检查
func riskCheckV2(c *bm.Context) {
params := new(v1.RiskCheckV2Request)
if err := c.Bind(params); err != nil {
return
}
res, err := svc.RiskCheckV2(c, params)
if err == nil {
svc.PayShield(c, params, res)
}
c.JSON(res, err)
}
// ipList IP列表接口
func ipList(c *bm.Context) {
params := new(v1.IPListRequest)
c.JSON(svc.IPList(c, params))
}
//ipDetail ip详情列表
func ipDetail(c *bm.Context) {
params := new(v1.IPDetailRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.IPDetail(c, params))
}
// uidList uid列表接口
func uidList(c *bm.Context) {
params := new(v1.UIDListRequest)
c.JSON(svc.UIDList(c, params))
}
//uidDetail uid详情列表
func uidDetail(c *bm.Context) {
params := new(v1.UIDDetailRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.UIDDetail(c, params))
}
// ipBlack ip黑名单
func ipBlack(c *bm.Context) {
params := new(v1.IPBlackRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.IPBlack(c, params))
}
// uidBlack uid黑名单
func uidBlack(c *bm.Context) {
params := new(v1.UIDBlackRequest)
if err := c.Bind(params); err != nil {
return
}
c.JSON(svc.UIDBlack(c, params))
}

View File

@@ -0,0 +1,70 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"question_bank_bind_test.go",
"question_bank_test.go",
"question_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"graph.go",
"question.go",
"question_bank.go",
"question_bank_bind.go",
"risk.go",
"service.go",
],
importpath = "go-common/app/service/openplatform/anti-fraud/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/common/openplatform/geetest/model:go_default_library",
"//app/common/openplatform/geetest/service:go_default_library",
"//app/common/openplatform/random:go_default_library",
"//app/service/openplatform/anti-fraud/api/grpc/v1:go_default_library",
"//app/service/openplatform/anti-fraud/api/http/v1:go_default_library",
"//app/service/openplatform/anti-fraud/conf:go_default_library",
"//app/service/openplatform/anti-fraud/dao:go_default_library",
"//app/service/openplatform/anti-fraud/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/json-iterator/go: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,40 @@
package service
import (
"context"
gtm "go-common/app/common/openplatform/geetest/model"
gtsvc "go-common/app/common/openplatform/geetest/service"
"go-common/app/service/openplatform/anti-fraud/api/grpc/v1"
"go-common/app/service/openplatform/anti-fraud/model"
"go-common/library/ecode"
)
//GraphPrepare 拉起图片验证
func (s *Service) GraphPrepare(c context.Context, req *v1.GraphPrepareRequest) (res *v1.GraphPrepareResponse, err error) {
go s.d.IncrGeetestCount(context.Background())
var data *gtm.ProcessRes
if data, err = gtsvc.PreProcess(c, req.MID, 1, req.IP, req.ClientType, model.GeetestCaptchaID, model.GeetestPrivateKey); err != nil {
return
}
res = new(v1.GraphPrepareResponse)
res.Success = int64(data.Success)
res.CaptchaID = data.CaptchaID
res.Challenge = data.Challenge
res.NewCaptcha = int64(data.NewCaptcha)
res.Voucher = s.d.Voucher(c, req.MID, req.IP, req.ItemID, req.Customer, model.VoucherTypePull)
return
}
//GraphCheck 图形验证
func (s *Service) GraphCheck(c context.Context, req *v1.GraphCheckRequest) (res *v1.GraphCheckResponse, err error) {
if err = s.d.CheckVoucher(c, req.MID, req.Voucher, model.VoucherTypePull); err != nil {
return
}
if status := gtsvc.Validate(c, req.Challenge, req.Validate, req.Seccode, req.ClientType, req.IP, model.GeetestCaptchaID, model.GeetestPrivateKey, int(req.Success), req.MID); !status {
err = ecode.AntiValidateFailed
return
}
res = new(v1.GraphCheckResponse)
res.NewVoucher = s.d.Voucher(c, req.MID, req.IP, req.ItemID, req.Customer, model.VoucherTypeCheck)
return res, nil
}

View File

@@ -0,0 +1,550 @@
package service
import (
"context"
"math"
"math/rand"
"time"
"go-common/app/service/openplatform/anti-fraud/model"
xsql "go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
)
func (s *Service) beginTran(c context.Context) (*xsql.Tx, error) {
return s.d.BeginTran(c)
}
// GetQusInfo 获取题目信息
func (s *Service) GetQusInfo(c context.Context, qsID int64) (res *model.Question, err error) {
if res, err = s.d.GetQusInfo(c, qsID); err != nil {
return
}
return
}
// GetAnswerList 答案列表
func (s *Service) GetAnswerList(c context.Context, qsID int64) (res []*model.Answer, err error) {
if res, err = s.d.GetAnswerList(c, qsID); err != nil {
return
}
return
}
// AddQus 添加题目
func (s *Service) AddQus(c context.Context, in *model.AddQus, answers []model.Answer) (res model.AddReturn, err error) {
tx, err := s.beginTran(c)
if err != nil {
log.Error("AddQusQusbeginTran error(%v)", err)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("tx.Rollback() error(%v)", err1)
}
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit() error(%v)", err)
}
}()
qb := &model.Question{
QsID: time.Now().UnixNano() / 1e6,
QsName: in.Name,
QsDif: in.Dif,
QsBId: in.BId,
QsType: in.Type,
AnswerType: in.AnType,
}
res.ID, err = s.d.InsertQus(c, qb)
if err != nil {
log.Error("s.dao.AddQusBank(%v) error(%v)", qb)
return
}
list := make([]*model.AnswerAdd, 0)
for _, item := range answers {
answer := &model.AnswerAdd{}
answer.IsCorrect = item.IsCorrect
answer.AnswerContent = item.AnswerContent
answer.QsID = qb.QsID
answer.AnswerID = time.Now().UnixNano() / 1e6
time.Sleep(time.Millisecond)
list = append(list, answer)
}
err = s.d.MultiAddAnwser(c, list)
if err != nil {
log.Error("s.dao.AnswerAdd error(%v)", err)
return
}
_, err = s.d.UpdateQsBankCnt(c, in.BId)
if err != nil {
log.Error("s.dao.UpdateQsBankCnt error(%v)", err)
return
}
return
}
// DelQus 删除题目
func (s *Service) DelQus(c context.Context, qid int64) (res bool, err error) {
res = false
tx, err := s.beginTran(c)
if err != nil {
log.Error("DelQus beginTran error(%v)", err)
return
}
var rows int64
info, err := s.GetQusInfo(c, qid)
if err != nil || info == nil {
err = ecode.QusIDInvalid
return
}
cnt, err := s.d.CountBindItem(c, info.QsBId)
if cnt > 0 || err != nil {
err = ecode.BankUsing
}
rows, err = s.d.DelQus(c, qid)
if err != nil || rows == 0 {
tx.Rollback()
log.Error("dao.Subject.DelQus error(%v) or rows==0", err)
err = ecode.ParamInvalid
return
}
rows, err = s.d.DelAnwser(c, qid)
if err != nil || rows == 0 {
tx.Rollback()
log.Error("dao.Subject.Del Answer error(%v) or rows==0", err)
err = ecode.ParamInvalid
return
}
if err = tx.Commit(); err != nil {
return
}
_, err = s.d.UpdateQsBankCnt(c, info.QsBId)
if err != nil {
log.Error("s.dao.UpdateQsBankCnt error(%v)", err)
return
}
res = true
return
}
// GetQuslist 题目列表
func (s *Service) GetQuslist(c context.Context, pageNo int, pageSize int, qBid int64) (res []*model.QuestionAll, err error) {
offset, limit := s.PageInfo(pageNo, pageSize)
list, err := s.d.GetQusList(c, offset, limit, qBid)
if err != nil {
return
}
for _, item := range list {
alist, err := s.d.GetAnswerList(c, item.QsID)
if err != nil {
break
}
data := &model.QuestionAll{
Question: *item,
AnswersList: alist,
}
res = append(res, data)
}
return
}
// GetQusTotal 题目列表
func (s *Service) GetQusTotal(c context.Context, bid int64) (res int64, err error) {
if res, err = s.d.GetQusCount(c, bid); err != nil {
return
}
return
}
// UpdateQus 更新题库
func (s *Service) UpdateQus(c context.Context, in *model.ArgUpdateQus, answers []model.Answer) (res bool, err error) {
res = false
info, _ := s.GetQusBankInfo(c, in.BId)
if info == nil {
err = ecode.QusbNotFound
return
}
var row int64
tx, err := s.beginTran(c)
if err != nil {
log.Error("Del Qus beginTran error(%v)", err)
return
}
if row, err = s.d.UpdateQus(c, in, answers); err != nil {
tx.Rollback()
log.Error("s.dao.DelQusBank(%v) error(%v)", err)
err = ecode.UpdateError
return
}
if row > 0 {
res = true
}
_, err = s.d.DelAnwser(c, in.QsID)
if err != nil {
tx.Rollback()
log.Error("s.dao.DelAnwser error(%v)", err)
err = ecode.UpdateError
return
}
list := make([]*model.AnswerAdd, 0)
for _, item := range answers {
answer := model.AnswerAdd{}
answer.Answer = item
answer.QsID = in.QsID
if item.AnswerID != 0 {
_, err = s.d.UpdateAnwser(c, &answer)
if err != nil {
tx.Rollback()
log.Error("s.dao.UpdateAnwser error(%v)", err)
return
}
} else {
list = append(list, &answer)
}
}
//批量插入
if len(list) > 0 {
err = s.d.MultiAddAnwser(c, list)
if err != nil {
tx.Rollback()
log.Error("s.dao.MultiAddAnwser error(%v)", err)
return
}
}
if err = tx.Commit(); err != nil {
return
}
res = true
_, err = s.d.UpdateQsBankCnt(c, in.BId)
if err != nil {
log.Error("s.dao.UpdateQsBankCnt error(%v)", err)
return
}
err = s.d.DelQusCache(c, in.QsID)
if err != nil {
log.Error("s.dao.DelQusCache error(%v)", err)
return
}
err = s.d.DelAnswerCache(c, in.QsID)
if err != nil {
log.Error("s.dao.DelAnswerCache error(%v)", err)
return
}
return
}
// CheckAnswer 检查答案
func (s *Service) CheckAnswer(c context.Context, qusID int64, qusType int8, anlist []model.Answer) (etag string, err error) {
var correct int8
for _, item := range anlist {
if item.AnswerContent == "" {
etag = "answer_content is empty"
err = ecode.ParamInvalid
return
}
if item.IsCorrect > 1 || item.IsCorrect < 0 {
etag = "invalid is_correct"
err = ecode.AnswerError
return
}
correct = item.IsCorrect + correct
if item.QsID > 0 {
if qusID == item.QsID {
if res, _ := s.d.GetQusInfo(c, item.QsID); res == nil {
etag = "invalid QsID"
err = ecode.QusIDInvalid
return
}
continue
} else {
etag = "invalid QsID"
err = ecode.QusIDInvalid
return
}
}
}
if qusType == model.MULTIPLECHOICE {
if correct < 2 {
etag = "invalid is_correct"
err = ecode.AnswerError
return
}
} else {
if correct != 1 {
etag = "invalid is_correct"
err = ecode.AnswerError
return
}
}
return
}
// GetQuestion 获取题目
func (s *Service) GetQuestion(c context.Context, args *model.ArgGetQuestion) (q *model.GetQuestionItem, err error) {
// 获取已绑定的题库
bind, err := s.d.GetBindBankInfo(c, args.Source, args.TargetItemType, args.TargetItem)
if err != nil {
return
}
// 判断是不是答题组件
cid, err := s.d.GetComponentID(c, args)
if err != nil {
log.Info("s.GetQuestion(%v) 两次获取题目间隔小于 cd 时间", args)
err = ecode.GetComponentIDErr
return
}
// 组件第一题答题
if cid == 0 || cid != args.ComponentID {
// 检查上次时间与这次的差值如果小于 cd 时间
//if time.Now().Unix()-s.d.QusFetchTime(c, args) < bind.QuestionBank.CdTime {
// log.Info("s.GetQuestion(%v) 两次获取题目间隔小于 cd 时间", args)
// err = ecode.AnswerIntervalInvalid
// return
//}
//缓存新的组件
if err = s.d.SetComponentID(c, args); err != nil {
err = ecode.SetComponentIDErr
return
}
// 把组件答题次数清零
err = s.d.SetComponentTimes(c, args)
if err != nil {
log.Info("s.GetQuestion(%v) 两次获取题目间隔小于 cd 时间", args)
err = ecode.SetComponentTimesErr
return
}
}
BankInfo, err := s.d.GetQusBankInfoCache(c, bind.QsBId)
if err != nil {
err = ecode.GetQusBankInfoCache
return
}
// 同一组件
times, err := s.d.GetComponentTimes(c, args)
if err != nil {
err = ecode.GetComponentTimesErr
return
}
if times == BankInfo.MaxRetryTime {
err = ecode.SameCompentErr
return
}
// 获取题库包含的问题
var qusIds []int64
qusIds, err = s.d.GetQusIds(c, BankInfo.QsBId)
if err != nil {
err = ecode.GetQusIDsErr
return
}
q = new(model.GetQuestionItem)
// 随机取一道
q.Question, err = s.randQuestion(c, qusIds, args)
if err != nil {
err = ecode.GetQusIDsErr
return
}
//记录答题次数
q.AllCnt = BankInfo.MaxRetryTime
q.AnTime = times + 1
//产生答题背景图片
if q.QsType == 1 {
q.QuestBkPic, err = s.d.GetRandPic(c, args)
}
if err != nil {
log.Error("s.GetRandPic(%v) error(%v)", args, err)
q = nil
return
}
q.Answers, err = s.d.GetAnswersByCache(c, q.QsID)
if err != nil {
log.Error("s.GetQuestion(%v) error(%v)", args, err)
q = nil
return
}
// 组件题目+1
err = s.d.IncrComponentTimes(c, args)
if err != nil {
q = nil
return
}
// 记录上次获取题目时间
err = s.d.SetQusFetchTime(c, args, time.Now().Unix())
if err != nil {
q = nil
return
}
return
}
// UserAnswer 回答
func (s *Service) UserAnswer(c context.Context, check *model.ArgCheckAnswer) (res bool, err error) {
// 检查题目合法不合法
args := check.ArgGetQuestion
bind, err := s.d.GetBindBankInfo(c, args.Source, args.TargetItemType, args.TargetItem)
if err != nil {
return
}
//答题记录
insertID, err := s.d.AddUserAnwser(c, check, 1)
if err != nil || insertID == 0 {
log.Error("s.dao.AddUser Answser error(%v)", err)
return
}
//检查这是第几次答题
times, err := s.d.GetComponentTimes(c, &check.ArgGetQuestion)
if err != nil {
err = ecode.GetComponentTimesErr
return
}
if times > bind.QuestionBank.MaxRetryTime {
err = ecode.SameCompentErr
return
}
qusInfo, err := s.d.GetCacheQus(c, check.QsID)
if err != nil {
err = ecode.AnswerError
return
}
//检查xy坐标
if qusInfo.QsType == 1 {
answerPic, err1 := s.d.GetCacheAnswerPic(c, &check.ArgGetQuestion)
if err1 != nil {
err = ecode.AnswerError
return
}
m := check.X - answerPic.X
n := check.X - answerPic.X
if int(math.Abs(float64(m))) > 75 || int(math.Abs(float64(n))) > 75 {
err = ecode.AnswerPoiError
return
}
}
//检查答案
ids, err := s.d.CorrectAnswerIds(c, check.QsID)
if len(ids) < 1 || len(ids) != len(check.Answers) || err != nil {
log.Info("s.CheckAnswer(%v) 答案错误", err)
err = ecode.AnswerError
return
}
num := 0
for _, id := range ids {
for _, answerid := range check.Answers {
if answerid == id {
num++
continue
}
}
}
if num != len(ids) {
log.Info("s.CheckAnswer(%v) 答案错误", err)
err = ecode.AnswerError
return
}
res = true
return
}
// randQuestion 随机获取题目
func (s *Service) randQuestion(c context.Context, qs []int64, args *model.ArgGetQuestion) (q *model.Question, err error) {
// 获取已经答过的题目 id
usedIds := s.d.GetAnsweredID(c, args)
var notUseQ []int64
for _, id := range qs {
used := false
for _, i := range usedIds {
if id == i {
used = true
break
}
}
if !used {
notUseQ = append(notUseQ, id)
}
}
// 全部使用过了就重新开始随机
if len(notUseQ) == 0 {
err = s.d.RmAnsweredID(c, args)
if err != nil {
log.Error("d.randQuestion(%v, %v) error(%v)", qs, args, err)
return
}
notUseQ = qs
}
qid := notUseQ[rand.Intn(len(notUseQ))]
q, err = s.d.GetCacheQus(c, qid)
if err != nil {
log.Error("d.GetQusInfo(%v) error(%v)", qid, err)
return
}
// 设置已经答过的题目 id
err = s.d.SetAnsweredID(c, args, qid)
if err != nil {
log.Error("d.randQuestion(%v, %v) s.d.SetAnsweredID(%d) error(%v)", qs, args, qid, err)
return
}
return
}

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