Create & Init Project...
This commit is contained in:
23
app/service/main/figure/BUILD
Normal file
23
app/service/main/figure/BUILD
Normal file
@ -0,0 +1,23 @@
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//app/service/main/figure/cmd:all-srcs",
|
||||
"//app/service/main/figure/conf:all-srcs",
|
||||
"//app/service/main/figure/dao:all-srcs",
|
||||
"//app/service/main/figure/http:all-srcs",
|
||||
"//app/service/main/figure/model:all-srcs",
|
||||
"//app/service/main/figure/rpc/client:all-srcs",
|
||||
"//app/service/main/figure/rpc/server:all-srcs",
|
||||
"//app/service/main/figure/service:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
50
app/service/main/figure/CHANGELOG.md
Normal file
50
app/service/main/figure/CHANGELOG.md
Normal file
@ -0,0 +1,50 @@
|
||||
信用积分项目-服务
|
||||
===============
|
||||
|
||||
v1.3.0
|
||||
1. add /dao ut
|
||||
|
||||
v1.2.0
|
||||
1. new server && new verify or auth
|
||||
|
||||
v1.1.3
|
||||
1. refine api /infos
|
||||
|
||||
v1.1.2
|
||||
1. add /infos api
|
||||
|
||||
v1.1.1
|
||||
1. fix http info return
|
||||
|
||||
v1.1.0
|
||||
1. http router to bm
|
||||
|
||||
v1.0.9
|
||||
1. add register
|
||||
|
||||
v1.0.8
|
||||
1. update path
|
||||
|
||||
v1.0.7
|
||||
1. update figure query
|
||||
|
||||
v1.0.6
|
||||
1. remove stastd
|
||||
|
||||
v1.0.5
|
||||
1. 优化identify
|
||||
|
||||
v1.0.4
|
||||
1. fix redis expire
|
||||
|
||||
v1.0.3
|
||||
1. 增加rpc接口
|
||||
|
||||
v1.0.2
|
||||
1. 增加prom监控
|
||||
|
||||
v1.0.1
|
||||
1. 修改接口,增加分值段位信息
|
||||
|
||||
v1.0.0
|
||||
1. 初始化项目,更新依赖
|
10
app/service/main/figure/CONTRIBUTORS.md
Normal file
10
app/service/main/figure/CONTRIBUTORS.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Owner
|
||||
zhaogangtao
|
||||
|
||||
# Author
|
||||
zhaogangtao
|
||||
muyang
|
||||
|
||||
# Reviewer
|
||||
zhaogangtao
|
||||
linmiao
|
15
app/service/main/figure/OWNERS
Normal file
15
app/service/main/figure/OWNERS
Normal file
@ -0,0 +1,15 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- muyang
|
||||
- zhaogangtao
|
||||
labels:
|
||||
- main
|
||||
- service
|
||||
- service/main/figure
|
||||
options:
|
||||
no_parent_owners: true
|
||||
reviewers:
|
||||
- linmiao
|
||||
- muyang
|
||||
- zhaogangtao
|
13
app/service/main/figure/README.md
Normal file
13
app/service/main/figure/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
#### figure-service
|
||||
|
||||
##### 项目简介
|
||||
> 1.用户信用分
|
||||
|
||||
##### 编译环境
|
||||
> 请只用golang v1.8.x以上版本编译执行。
|
||||
|
||||
##### 依赖包
|
||||
> 1.公共包go-common
|
||||
|
||||
##### 特别说明
|
||||
> 1.model目录可能会被其他项目引用,请谨慎请改并通知各方。
|
46
app/service/main/figure/cmd/BUILD
Normal file
46
app/service/main/figure/cmd/BUILD
Normal file
@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "cmd",
|
||||
embed = [":go_default_library"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["main.go"],
|
||||
data = ["figure-service-test.toml"],
|
||||
importpath = "go-common/app/service/main/figure/cmd",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/http:go_default_library",
|
||||
"//app/service/main/figure/rpc/server:go_default_library",
|
||||
"//app/service/main/figure/service:go_default_library",
|
||||
"//library/log:go_default_library",
|
||||
"//library/net/trace:go_default_library",
|
||||
"//library/os/signal:go_default_library",
|
||||
"//library/syscall: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"],
|
||||
)
|
43
app/service/main/figure/cmd/figure-service-test.toml
Normal file
43
app/service/main/figure/cmd/figure-service-test.toml
Normal file
@ -0,0 +1,43 @@
|
||||
version = "1.0.3"
|
||||
|
||||
[log]
|
||||
dir = "/data/log/figure-service"
|
||||
|
||||
[bm]
|
||||
addr = "0.0.0.0:7062"
|
||||
timeout = "1s"
|
||||
maxListen = 100
|
||||
|
||||
[mysql]
|
||||
addr = "172.16.33.54:3306"
|
||||
dsn = "test:test@tcp(172.16.33.54:3306)/bilibili_figure?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
|
||||
active = 5
|
||||
idle = 2
|
||||
queryTimeout = "100ms"
|
||||
execTimeout = "100ms"
|
||||
tranTimeout = "200ms"
|
||||
[mysql.breaker]
|
||||
window = "3s"
|
||||
sleep = "100ms"
|
||||
bucket = 10
|
||||
ratio = 0.5
|
||||
request = 100
|
||||
|
||||
[redis]
|
||||
name = "figure-service"
|
||||
proto = "tcp"
|
||||
addr = "172.16.33.54:6379"
|
||||
idle = 100
|
||||
active = 100
|
||||
dialTimeout = "1s"
|
||||
readTimeout = "1s"
|
||||
writeTimeout = "1s"
|
||||
idleTimeout = "10s"
|
||||
expire = "720h"
|
||||
|
||||
[rpcServer]
|
||||
addr = "0.0.0.0:7069"
|
||||
proto = "tcp"
|
||||
|
||||
[property]
|
||||
loadRankPeriod = "1m"
|
53
app/service/main/figure/cmd/main.go
Normal file
53
app/service/main/figure/cmd/main.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
"go-common/app/service/main/figure/http"
|
||||
rpc "go-common/app/service/main/figure/rpc/server"
|
||||
"go-common/app/service/main/figure/service"
|
||||
"go-common/library/log"
|
||||
"go-common/library/net/trace"
|
||||
"go-common/library/os/signal"
|
||||
"go-common/library/syscall"
|
||||
)
|
||||
|
||||
var svr *service.Service
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if err := conf.Init(); err != nil {
|
||||
log.Error("conf.Init() error(%v)", err)
|
||||
panic(err)
|
||||
}
|
||||
log.Init(conf.Conf.Log)
|
||||
defer log.Close()
|
||||
trace.Init(conf.Conf.Tracer)
|
||||
defer trace.Close()
|
||||
// service init
|
||||
svr = service.New(conf.Conf)
|
||||
http.Init(svr)
|
||||
rpcSvr := rpc.New(conf.Conf, svr)
|
||||
log.Info("figure-service start")
|
||||
// init signal
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
|
||||
for {
|
||||
s := <-c
|
||||
log.Info("figure-service get a signal %s", s.String())
|
||||
switch s {
|
||||
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
|
||||
rpcSvr.Close()
|
||||
time.Sleep(time.Second * 2)
|
||||
log.Info("figure-service exit")
|
||||
return
|
||||
case syscall.SIGHUP:
|
||||
// TODO reload
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
3
app/service/main/figure/conf.toml
Normal file
3
app/service/main/figure/conf.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[property]
|
||||
# 加载分值段信息周期
|
||||
loadRankPeriod = "1m"
|
49
app/service/main/figure/conf/BUILD
Normal file
49
app/service/main/figure/conf/BUILD
Normal file
@ -0,0 +1,49 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["conf_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["conf.go"],
|
||||
importpath = "go-common/app/service/main/figure/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/net/http/blademaster/middleware/verify:go_default_library",
|
||||
"//library/net/rpc: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"],
|
||||
)
|
105
app/service/main/figure/conf/conf.go
Normal file
105
app/service/main/figure/conf/conf.go
Normal file
@ -0,0 +1,105 @@
|
||||
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/http/blademaster/middleware/verify"
|
||||
"go-common/library/net/rpc"
|
||||
"go-common/library/net/trace"
|
||||
xtime "go-common/library/time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// var .
|
||||
var (
|
||||
confPath string
|
||||
client *conf.Client
|
||||
// config
|
||||
Conf = &Config{}
|
||||
)
|
||||
|
||||
// Config def.
|
||||
type Config struct {
|
||||
// base
|
||||
// log
|
||||
Log *log.Config
|
||||
// tracer
|
||||
Tracer *trace.Config
|
||||
//app
|
||||
Verify *verify.Config
|
||||
// http
|
||||
BM *bm.ServerConfig
|
||||
// db
|
||||
Mysql *sql.Config
|
||||
// redis
|
||||
Redis *Redis
|
||||
// RPC
|
||||
RPCServer *rpc.ServerConfig
|
||||
// property
|
||||
Property *Property
|
||||
}
|
||||
|
||||
// Redis redis.
|
||||
type Redis struct {
|
||||
*redis.Config
|
||||
Expire xtime.Duration
|
||||
}
|
||||
|
||||
// Property .
|
||||
type Property struct {
|
||||
LoadRankPeriod xtime.Duration
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&confPath, "conf", "", "config path")
|
||||
}
|
||||
|
||||
// Init init conf.
|
||||
func Init() (err error) {
|
||||
if confPath == "" {
|
||||
return configCenter()
|
||||
}
|
||||
_, err = toml.DecodeFile(confPath, &Conf)
|
||||
return
|
||||
}
|
||||
|
||||
func configCenter() (err error) {
|
||||
if client, err = conf.New(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = load(); err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for range client.Event() {
|
||||
log.Info("config reload")
|
||||
if load() != nil {
|
||||
log.Error("config reload error (%v)", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func load() (err error) {
|
||||
var (
|
||||
s string
|
||||
ok bool
|
||||
tmpConf *Config
|
||||
)
|
||||
if s, ok = client.Toml2(); !ok {
|
||||
return errors.New("load config center error")
|
||||
}
|
||||
if _, err = toml.Decode(s, &tmpConf); err != nil {
|
||||
return errors.New("could not decode config")
|
||||
}
|
||||
*Conf = *tmpConf
|
||||
return
|
||||
}
|
23
app/service/main/figure/conf/conf_test.go
Normal file
23
app/service/main/figure/conf/conf_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
flag.Set("conf", "../cmd/figure-service-test.toml")
|
||||
var err error
|
||||
if err = Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if Conf == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
if Conf.Verify == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
if Conf.Verify.HTTPClient == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
58
app/service/main/figure/dao/BUILD
Normal file
58
app/service/main/figure/dao/BUILD
Normal file
@ -0,0 +1,58 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"dao_test.go",
|
||||
"mysql_test.go",
|
||||
"redis_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/model:go_default_library",
|
||||
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dao.go",
|
||||
"mysql.go",
|
||||
"redis.go",
|
||||
],
|
||||
importpath = "go-common/app/service/main/figure/dao",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/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"],
|
||||
)
|
44
app/service/main/figure/dao/dao.go
Normal file
44
app/service/main/figure/dao/dao.go
Normal file
@ -0,0 +1,44 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
"go-common/library/cache/redis"
|
||||
"go-common/library/database/sql"
|
||||
)
|
||||
|
||||
// Dao figure DAO
|
||||
type Dao struct {
|
||||
c *conf.Config
|
||||
db *sql.DB
|
||||
redis *redis.Pool
|
||||
redisExpire int32
|
||||
}
|
||||
|
||||
// New new a figure DAO
|
||||
func New(c *conf.Config) (d *Dao) {
|
||||
d = &Dao{
|
||||
c: c,
|
||||
db: sql.NewMySQL(c.Mysql),
|
||||
redis: redis.NewPool(c.Redis.Config),
|
||||
redisExpire: int32(time.Duration(c.Redis.Expire) / time.Second),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Ping check service health
|
||||
func (d *Dao) Ping(c context.Context) (err error) {
|
||||
if err = d.PingRedis(c); err != nil {
|
||||
return
|
||||
}
|
||||
return d.db.Ping(c)
|
||||
}
|
||||
|
||||
// Close close all dao.
|
||||
func (d *Dao) Close() {
|
||||
if d.db != nil {
|
||||
d.db.Close()
|
||||
}
|
||||
}
|
36
app/service/main/figure/dao/dao_test.go
Normal file
36
app/service/main/figure/dao/dao_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
)
|
||||
|
||||
var (
|
||||
d *Dao
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if os.Getenv("DEPLOY_ENV") != "" {
|
||||
flag.Set("app_id", "main.account-law.figure-service")
|
||||
flag.Set("conf_token", "9b960afb4badef680ae468698b4efd1a")
|
||||
flag.Set("tree_id", "5632")
|
||||
flag.Set("conf_version", "docker-1")
|
||||
flag.Set("deploy_env", "uat")
|
||||
flag.Set("conf_host", "config.bilibili.co")
|
||||
flag.Set("conf_path", "/tmp")
|
||||
flag.Set("region", "sh")
|
||||
flag.Set("zone", "sh001")
|
||||
} else {
|
||||
flag.Set("conf", "../cmd/figure-service-test.toml")
|
||||
}
|
||||
flag.Parse()
|
||||
if err := conf.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d = New(conf.Conf)
|
||||
m.Run()
|
||||
os.Exit(0)
|
||||
}
|
57
app/service/main/figure/dao/mysql.go
Normal file
57
app/service/main/figure/dao/mysql.go
Normal file
@ -0,0 +1,57 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
xsql "go-common/library/database/sql"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
_shard = 100
|
||||
_figureInfo = "SELECT id, mid, score, lawful_score, wide_score, friendly_score, bounty_score, creativity_score, ver, ctime, mtime FROM figure_user_%02d WHERE mid=? ORDER BY id DESC LIMIT 1"
|
||||
_rank = `SELECT score_from,score_to,percentage FROM figure_rank ORDER BY percentage ASC`
|
||||
)
|
||||
|
||||
func hit(mid int64) int64 {
|
||||
return mid % _shard
|
||||
}
|
||||
|
||||
// FigureInfo get user figure info
|
||||
func (d *Dao) FigureInfo(c context.Context, mid int64) (res *model.Figure, err error) {
|
||||
row := d.db.QueryRow(c, fmt.Sprintf(_figureInfo, hit(mid)), mid)
|
||||
res = &model.Figure{}
|
||||
if err = row.Scan(&res.ID, &res.Mid, &res.Score, &res.LawfulScore, &res.WideScore, &res.FriendlyScore, &res.BountyScore, &res.CreativityScore, &res.Ver, &res.Ctime, &res.Mtime); err != nil {
|
||||
if err == xsql.ErrNoRows {
|
||||
err = nil
|
||||
res = nil
|
||||
return
|
||||
}
|
||||
err = errors.WithStack(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Ranks get figure score rank by ver
|
||||
func (d *Dao) Ranks(c context.Context) (ranks []*model.Rank, err error) {
|
||||
var (
|
||||
rows *xsql.Rows
|
||||
)
|
||||
if rows, err = d.db.Query(c, _rank); err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var rank = &model.Rank{}
|
||||
if err = rows.Scan(&rank.ScoreFrom, &rank.ScoreTo, &rank.Percentage); err != nil {
|
||||
ranks = nil
|
||||
return
|
||||
}
|
||||
ranks = append(ranks, rank)
|
||||
}
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
53
app/service/main/figure/dao/mysql_test.go
Normal file
53
app/service/main/figure/dao/mysql_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestDaohit(t *testing.T) {
|
||||
convey.Convey("hit", t, func(ctx convey.C) {
|
||||
var (
|
||||
mid = int64(46333)
|
||||
)
|
||||
ctx.Convey("When everything right.", func(ctx convey.C) {
|
||||
shard := hit(mid)
|
||||
ctx.Convey("Then shard should not be mid % 100.", func(ctx convey.C) {
|
||||
ctx.So(shard, convey.ShouldEqual, 33)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoFigureInfo(t *testing.T) {
|
||||
convey.Convey("FigureInfo", t, func(ctx convey.C) {
|
||||
var (
|
||||
c = context.Background()
|
||||
mid = int64(20606508)
|
||||
)
|
||||
ctx.Convey("When everything right.", func(ctx convey.C) {
|
||||
res, err := d.FigureInfo(c, mid)
|
||||
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
ctx.So(res, convey.ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoRanks(t *testing.T) {
|
||||
convey.Convey("Ranks", t, func(ctx convey.C) {
|
||||
var (
|
||||
c = context.Background()
|
||||
)
|
||||
ctx.Convey("When everything right.", func(ctx convey.C) {
|
||||
ranks, err := d.Ranks(c)
|
||||
ctx.Convey("Then err should be nil.ranks should have length 100.", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
ctx.So(ranks, convey.ShouldHaveLength, 100)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
112
app/service/main/figure/dao/redis.go
Normal file
112
app/service/main/figure/dao/redis.go
Normal file
@ -0,0 +1,112 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/library/cache/redis"
|
||||
"go-common/library/log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
_figureKey = "f:%d"
|
||||
)
|
||||
|
||||
func figureKey(mid int64) string {
|
||||
return fmt.Sprintf(_figureKey, mid)
|
||||
}
|
||||
|
||||
// PingRedis check redis connection
|
||||
func (d *Dao) PingRedis(c context.Context) (err error) {
|
||||
conn := d.redis.Get(c)
|
||||
defer conn.Close()
|
||||
if _, err = conn.Do("SET", "PING", "PONG"); err != nil {
|
||||
err = errors.WithStack(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddFigureInfoCache put figure to redis
|
||||
func (d *Dao) AddFigureInfoCache(c context.Context, f *model.Figure) (err error) {
|
||||
var (
|
||||
key = figureKey(f.Mid)
|
||||
conn = d.redis.Get(c)
|
||||
values []byte
|
||||
)
|
||||
defer conn.Close()
|
||||
if values, err = json.Marshal(f); err != nil {
|
||||
err = errors.Wrapf(err, "%+v", f)
|
||||
return
|
||||
}
|
||||
if err = conn.Send("SET", key, values); err != nil {
|
||||
err = errors.Wrapf(err, "conn.Send(SET, %s, %d)", key, values)
|
||||
return
|
||||
}
|
||||
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
|
||||
err = errors.Wrapf(err, "conn.Send(Expire, %s, %d)", key, d.redisExpire)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FigureInfoCache get user figure info from redis
|
||||
func (d *Dao) FigureInfoCache(c context.Context, mid int64) (f *model.Figure, err error) {
|
||||
key := figureKey(mid)
|
||||
conn := d.redis.Get(c)
|
||||
defer conn.Close()
|
||||
item, err := redis.Bytes(conn.Do("GET", key))
|
||||
if err != nil {
|
||||
if err == redis.ErrNil {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(item, &f); err != nil {
|
||||
log.Error("json.Unmarshal(%v) err(%v)", item, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FigureBatchInfoCache ...
|
||||
func (d *Dao) FigureBatchInfoCache(c context.Context, mids []int64) (fs []*model.Figure, missIndex []int, err error) {
|
||||
if len(mids) == 0 {
|
||||
return
|
||||
}
|
||||
fs = make([]*model.Figure, len(mids))
|
||||
var (
|
||||
conn = d.redis.Get(c)
|
||||
valueBytes [][]byte
|
||||
keys []interface{}
|
||||
)
|
||||
defer conn.Close()
|
||||
for _, mid := range mids {
|
||||
keys = append(keys, figureKey(mid))
|
||||
}
|
||||
if valueBytes, err = redis.ByteSlices(conn.Do("MGET", keys...)); err != nil {
|
||||
if err == redis.ErrNil {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
err = errors.WithStack(err)
|
||||
return
|
||||
}
|
||||
for i, value := range valueBytes {
|
||||
if value == nil {
|
||||
missIndex = append(missIndex, i)
|
||||
continue
|
||||
}
|
||||
f := &model.Figure{}
|
||||
if err = json.Unmarshal(value, &f); err != nil {
|
||||
log.Error("%+v", errors.Wrapf(err, "json.Unmarshal(%s)", value))
|
||||
err = nil
|
||||
missIndex = append(missIndex, i)
|
||||
continue
|
||||
}
|
||||
fs[i] = f
|
||||
}
|
||||
return
|
||||
}
|
77
app/service/main/figure/dao/redis_test.go
Normal file
77
app/service/main/figure/dao/redis_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
"go-common/app/service/main/figure/model"
|
||||
)
|
||||
|
||||
func TestDaofigureKey(t *testing.T) {
|
||||
convey.Convey("figureKey", t, func(ctx convey.C) {
|
||||
var (
|
||||
mid = int64(46333)
|
||||
)
|
||||
ctx.Convey("When everything right.", func(ctx convey.C) {
|
||||
key := figureKey(mid)
|
||||
ctx.Convey("Then key should equal f:key.", func(ctx convey.C) {
|
||||
ctx.So(key, convey.ShouldEqual, "f:46333")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoPingRedis(t *testing.T) {
|
||||
convey.Convey("PingRedis", t, func(ctx convey.C) {
|
||||
var (
|
||||
c = context.Background()
|
||||
)
|
||||
ctx.Convey("When everything right.", func(ctx convey.C) {
|
||||
err := d.PingRedis(c)
|
||||
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoAddFigureInfoCache(t *testing.T) {
|
||||
convey.Convey("AddFigureInfoCache", t, func(ctx convey.C) {
|
||||
var (
|
||||
c = context.Background()
|
||||
figure = &model.Figure{
|
||||
Mid: 46333,
|
||||
Score: 2333,
|
||||
LawfulScore: 123,
|
||||
WideScore: 321,
|
||||
FriendlyScore: 19999,
|
||||
BountyScore: 1,
|
||||
CreativityScore: 0,
|
||||
Ver: 2333,
|
||||
}
|
||||
)
|
||||
ctx.Convey("When add FigureInfoCache.", func(ctx convey.C) {
|
||||
err := d.AddFigureInfoCache(c, figure)
|
||||
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
ctx.Convey("When get FigureInfoCache.", func(ctx convey.C) {
|
||||
figure2, err := d.FigureInfoCache(c, figure.Mid)
|
||||
ctx.Convey("Then err should be nil.figure2 should resemble figure.", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
ctx.So(figure2, convey.ShouldResemble, figure)
|
||||
})
|
||||
})
|
||||
ctx.Convey("When get FigureBatchInfoCache.", func(ctx convey.C) {
|
||||
figures, missIndex, err := d.FigureBatchInfoCache(c, []int64{figure.Mid})
|
||||
ctx.Convey("Then err should be nil.missIndex should be empty.figures should have length 1.figuers[0] should resemble figure", func(ctx convey.C) {
|
||||
ctx.So(err, convey.ShouldBeNil)
|
||||
ctx.So(missIndex, convey.ShouldBeEmpty)
|
||||
ctx.So(figures, convey.ShouldHaveLength, 1)
|
||||
ctx.So(figures[0], convey.ShouldResemble, figure)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
40
app/service/main/figure/http/BUILD
Normal file
40
app/service/main/figure/http/BUILD
Normal file
@ -0,0 +1,40 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"figure.go",
|
||||
"http.go",
|
||||
],
|
||||
importpath = "go-common/app/service/main/figure/http",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/model:go_default_library",
|
||||
"//app/service/main/figure/service:go_default_library",
|
||||
"//library/ecode:go_default_library",
|
||||
"//library/log:go_default_library",
|
||||
"//library/net/http/blademaster:go_default_library",
|
||||
"//library/net/http/blademaster/middleware/verify:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
48
app/service/main/figure/http/figure.go
Normal file
48
app/service/main/figure/http/figure.go
Normal file
@ -0,0 +1,48 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/library/ecode"
|
||||
"go-common/library/log"
|
||||
bm "go-common/library/net/http/blademaster"
|
||||
)
|
||||
|
||||
func figureInfo(c *bm.Context) {
|
||||
var (
|
||||
err error
|
||||
params = c.Request.Form
|
||||
|
||||
midStr = params.Get("mid")
|
||||
)
|
||||
mid, err := strconv.ParseInt(midStr, 10, 64)
|
||||
if err != nil {
|
||||
log.Error("strconv.ParseInt(%s) error(%+v)", midStr, err)
|
||||
c.JSON(nil, ecode.RequestErr)
|
||||
return
|
||||
}
|
||||
c.JSON(svc.FigureWithRank(c, mid))
|
||||
}
|
||||
|
||||
func figureInfos(c *bm.Context) {
|
||||
var (
|
||||
err error
|
||||
v = &model.ParamBatchInfo{}
|
||||
)
|
||||
if err = c.Bind(v); err != nil {
|
||||
return
|
||||
}
|
||||
if len(v.MIDs) > 50 {
|
||||
err = ecode.RequestErr
|
||||
return
|
||||
}
|
||||
var (
|
||||
frs []*model.FigureWithRank
|
||||
)
|
||||
frs, err = svc.BatchFigureWithRank(c, v.MIDs)
|
||||
if frs == nil {
|
||||
frs = make([]*model.FigureWithRank, 0)
|
||||
}
|
||||
c.JSON(frs, err)
|
||||
}
|
58
app/service/main/figure/http/http.go
Normal file
58
app/service/main/figure/http/http.go
Normal file
@ -0,0 +1,58 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
"go-common/app/service/main/figure/service"
|
||||
"go-common/library/log"
|
||||
bm "go-common/library/net/http/blademaster"
|
||||
"go-common/library/net/http/blademaster/middleware/verify"
|
||||
)
|
||||
|
||||
var (
|
||||
svc *service.Service
|
||||
verSvc *verify.Verify
|
||||
)
|
||||
|
||||
// Init new a http server
|
||||
func Init(s *service.Service) {
|
||||
initService(s)
|
||||
// init router.
|
||||
e := bm.DefaultServer(conf.Conf.BM)
|
||||
internalRouter(e)
|
||||
if err := e.Start(); err != nil {
|
||||
log.Error("e.Start() error(%v)", err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initService(s *service.Service) {
|
||||
svc = s
|
||||
verSvc = verify.New(conf.Conf.Verify)
|
||||
}
|
||||
|
||||
// internalRouter init inner router.
|
||||
func internalRouter(e *bm.Engine) {
|
||||
e.Ping(ping)
|
||||
e.Register(register)
|
||||
|
||||
group := e.Group("/x/internal/figure", verSvc.Verify)
|
||||
{
|
||||
group.GET("/info", figureInfo)
|
||||
group.POST("/infos", figureInfos)
|
||||
}
|
||||
}
|
||||
|
||||
// ping check server ok.
|
||||
func ping(c *bm.Context) {
|
||||
if err := svc.Ping(c); err != nil {
|
||||
log.Error("figure-service service ping error (%+v)", err)
|
||||
c.AbortWithStatus(http.StatusServiceUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
// register check server ok.
|
||||
func register(c *bm.Context) {
|
||||
c.JSON(map[string]interface{}{}, nil)
|
||||
}
|
32
app/service/main/figure/model/BUILD
Normal file
32
app/service/main/figure/model/BUILD
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"http.go",
|
||||
"model.go",
|
||||
"rpc.go",
|
||||
],
|
||||
importpath = "go-common/app/service/main/figure/model",
|
||||
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"],
|
||||
)
|
5
app/service/main/figure/model/http.go
Normal file
5
app/service/main/figure/model/http.go
Normal file
@ -0,0 +1,5 @@
|
||||
package model
|
||||
|
||||
type ParamBatchInfo struct {
|
||||
MIDs []int64 `form:"mids,split"`
|
||||
}
|
30
app/service/main/figure/model/model.go
Normal file
30
app/service/main/figure/model/model.go
Normal file
@ -0,0 +1,30 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// Figure user figure model
|
||||
type Figure struct {
|
||||
ID int32 `json:"-"`
|
||||
Mid int64 `json:"mid"`
|
||||
Score int32 `json:"score"`
|
||||
LawfulScore int32 `json:"lawful_score"`
|
||||
WideScore int32 `json:"wide_score"`
|
||||
FriendlyScore int32 `json:"friendly_score"`
|
||||
BountyScore int32 `json:"bounty_score"`
|
||||
CreativityScore int32 `json:"creativity_score"`
|
||||
Ver int32 `json:"ver"`
|
||||
Ctime time.Time `json:"-"`
|
||||
Mtime time.Time `json:"-"`
|
||||
}
|
||||
|
||||
type FigureWithRank struct {
|
||||
*Figure
|
||||
Percentage int8 `json:"percentage"`
|
||||
}
|
||||
|
||||
// Rank user rank model
|
||||
type Rank struct {
|
||||
ScoreFrom int32 `json:"score_from"`
|
||||
ScoreTo int32 `json:"score_to"`
|
||||
Percentage int8 `json:"percentage"`
|
||||
}
|
5
app/service/main/figure/model/rpc.go
Normal file
5
app/service/main/figure/model/rpc.go
Normal file
@ -0,0 +1,5 @@
|
||||
package model
|
||||
|
||||
type ArgUserFigure struct {
|
||||
Mid int64
|
||||
}
|
42
app/service/main/figure/rpc/client/BUILD
Normal file
42
app/service/main/figure/rpc/client/BUILD
Normal file
@ -0,0 +1,42 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["figure_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//app/service/main/figure/model:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["figure.go"],
|
||||
importpath = "go-common/app/service/main/figure/rpc/client",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/model:go_default_library",
|
||||
"//library/net/rpc:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
39
app/service/main/figure/rpc/client/figure.go
Normal file
39
app/service/main/figure/rpc/client/figure.go
Normal file
@ -0,0 +1,39 @@
|
||||
package figure
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/library/net/rpc"
|
||||
)
|
||||
|
||||
const (
|
||||
_userFigure = "RPC.UserFigure"
|
||||
)
|
||||
|
||||
const (
|
||||
_appid = "account.service.figure"
|
||||
)
|
||||
|
||||
var (
|
||||
_noRes = &struct{}{}
|
||||
)
|
||||
|
||||
// Service struct info.
|
||||
type Service struct {
|
||||
client *rpc.Client2
|
||||
}
|
||||
|
||||
// New create instance of service and return.
|
||||
func New(c *rpc.ClientConfig) (s *Service) {
|
||||
s = &Service{}
|
||||
s.client = rpc.NewDiscoveryCli(_appid, c)
|
||||
return
|
||||
}
|
||||
|
||||
// UserFigure get user figure & figure rank info.
|
||||
func (s *Service) UserFigure(c context.Context, arg *model.ArgUserFigure) (res *model.FigureWithRank, err error) {
|
||||
res = &model.FigureWithRank{}
|
||||
err = s.client.Call(c, _userFigure, arg, res)
|
||||
return
|
||||
}
|
24
app/service/main/figure/rpc/client/figure_test.go
Normal file
24
app/service/main/figure/rpc/client/figure_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package figure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
)
|
||||
|
||||
var (
|
||||
s *Service
|
||||
)
|
||||
|
||||
func init() {
|
||||
s = New(nil)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestUserFigure(t *testing.T) {
|
||||
res, _ := s.UserFigure(context.TODO(), &model.ArgUserFigure{Mid: 27515628})
|
||||
fmt.Println(res)
|
||||
}
|
35
app/service/main/figure/rpc/server/BUILD
Normal file
35
app/service/main/figure/rpc/server/BUILD
Normal file
@ -0,0 +1,35 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["rpc.go"],
|
||||
importpath = "go-common/app/service/main/figure/rpc/server",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/model:go_default_library",
|
||||
"//app/service/main/figure/service:go_default_library",
|
||||
"//library/net/rpc:go_default_library",
|
||||
"//library/net/rpc/context:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
38
app/service/main/figure/rpc/server/rpc.go
Normal file
38
app/service/main/figure/rpc/server/rpc.go
Normal file
@ -0,0 +1,38 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"go-common/app/service/main/figure/conf"
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/app/service/main/figure/service"
|
||||
"go-common/library/net/rpc"
|
||||
"go-common/library/net/rpc/context"
|
||||
)
|
||||
|
||||
// RPC server def.
|
||||
type RPC struct {
|
||||
s *service.Service
|
||||
}
|
||||
|
||||
// New init rpc.
|
||||
func New(c *conf.Config, s *service.Service) (svr *rpc.Server) {
|
||||
r := &RPC{s: s}
|
||||
svr = rpc.NewServer(c.RPCServer)
|
||||
if err := svr.Register(r); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Ping check rpc server health.
|
||||
func (r *RPC) Ping(c context.Context, arg *struct{}, res *struct{}) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// UserFigure get user figure & rank info.
|
||||
func (r *RPC) UserFigure(c context.Context, arg *model.ArgUserFigure, res *model.FigureWithRank) (err error) {
|
||||
var fr *model.FigureWithRank
|
||||
if fr, err = r.s.FigureWithRank(c, arg.Mid); fr != nil {
|
||||
*res = *fr
|
||||
}
|
||||
return
|
||||
}
|
52
app/service/main/figure/service/BUILD
Normal file
52
app/service/main/figure/service/BUILD
Normal file
@ -0,0 +1,52 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["service_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"figure.go",
|
||||
"rank.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "go-common/app/service/main/figure/service",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//app/service/main/figure/conf:go_default_library",
|
||||
"//app/service/main/figure/dao:go_default_library",
|
||||
"//app/service/main/figure/model:go_default_library",
|
||||
"//library/ecode: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"],
|
||||
)
|
45
app/service/main/figure/service/figure.go
Normal file
45
app/service/main/figure/service/figure.go
Normal file
@ -0,0 +1,45 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/library/log"
|
||||
)
|
||||
|
||||
func (s *Service) FigureBatchInfo(c context.Context, mids []int64) (fs []*model.Figure, err error) {
|
||||
if len(mids) == 0 {
|
||||
return
|
||||
}
|
||||
var (
|
||||
cache = true
|
||||
missIndex []int
|
||||
)
|
||||
if fs, missIndex, err = s.dao.FigureBatchInfoCache(c, mids); err != nil {
|
||||
cache = false
|
||||
log.Error("%+v", err)
|
||||
}
|
||||
if len(missIndex) == 0 {
|
||||
return
|
||||
}
|
||||
for _, i := range missIndex {
|
||||
if fs[i], err = s.dao.FigureInfo(c, mids[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if cache {
|
||||
s.addMission(func() {
|
||||
var cerr error
|
||||
for _, i := range missIndex {
|
||||
if fs[i] == nil {
|
||||
continue
|
||||
}
|
||||
if cerr = s.dao.AddFigureInfoCache(context.TODO(), fs[i]); err != nil {
|
||||
log.Error("%+v", cerr)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
89
app/service/main/figure/service/rank.go
Normal file
89
app/service/main/figure/service/rank.go
Normal file
@ -0,0 +1,89 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go-common/app/service/main/figure/model"
|
||||
"go-common/library/ecode"
|
||||
"go-common/library/log"
|
||||
)
|
||||
|
||||
var (
|
||||
rs = &ranks{}
|
||||
)
|
||||
|
||||
type ranks struct {
|
||||
rs []*model.Rank
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (r *ranks) load(datas []*model.Rank) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.rs = make([]*model.Rank, 0, 100)
|
||||
r.rs = append(r.rs, datas...)
|
||||
}
|
||||
|
||||
func (r *ranks) find(score int32) (percentage int8) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
for _, r := range r.rs {
|
||||
if r.ScoreFrom <= score && r.ScoreTo >= score {
|
||||
return r.Percentage
|
||||
}
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
func (s *Service) loadRank(c context.Context) {
|
||||
var (
|
||||
ranks []*model.Rank
|
||||
err error
|
||||
)
|
||||
if ranks, err = s.dao.Ranks(c); err != nil {
|
||||
log.Error("%+v", err)
|
||||
return
|
||||
}
|
||||
rs.load(ranks)
|
||||
}
|
||||
|
||||
func (s *Service) Rank(c context.Context, score int32) (percentage int8) {
|
||||
percentage = rs.find(score)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) FigureWithRank(c context.Context, mid int64) (fr *model.FigureWithRank, err error) {
|
||||
var fs []*model.Figure
|
||||
if fs, err = s.FigureBatchInfo(c, []int64{mid}); err != nil {
|
||||
return
|
||||
}
|
||||
if len(fs) != 1 || fs[0] == nil {
|
||||
err = ecode.FigureNotFound
|
||||
return
|
||||
}
|
||||
fr = s.generateFigureWithRank(c, fs[0])
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) BatchFigureWithRank(c context.Context, mids []int64) (frs []*model.FigureWithRank, err error) {
|
||||
if len(mids) == 0 {
|
||||
return
|
||||
}
|
||||
var fs []*model.Figure
|
||||
if fs, err = s.FigureBatchInfo(c, mids); err != nil {
|
||||
return
|
||||
}
|
||||
for _, f := range fs {
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
frs = append(frs, s.generateFigureWithRank(c, f))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) generateFigureWithRank(c context.Context, f *model.Figure) (fr *model.FigureWithRank) {
|
||||
fr = &model.FigureWithRank{Figure: f, Percentage: s.Rank(c, f.Score)}
|
||||
return
|
||||
}
|
62
app/service/main/figure/service/service.go
Normal file
62
app/service/main/figure/service/service.go
Normal file
@ -0,0 +1,62 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
figureDao "go-common/app/service/main/figure/dao"
|
||||
"go-common/library/log"
|
||||
)
|
||||
|
||||
// Service biz service def.
|
||||
type Service struct {
|
||||
c *conf.Config
|
||||
dao *figureDao.Dao
|
||||
missch chan func()
|
||||
}
|
||||
|
||||
// New new a Service and return.
|
||||
func New(c *conf.Config) (s *Service) {
|
||||
s = &Service{
|
||||
c: c,
|
||||
dao: figureDao.New(c),
|
||||
missch: make(chan func(), 1024),
|
||||
}
|
||||
go s.cacheproc()
|
||||
go s.rankproc()
|
||||
return s
|
||||
}
|
||||
|
||||
// Ping check dao health.
|
||||
func (s *Service) Ping(c context.Context) (err error) {
|
||||
return s.dao.Ping(c)
|
||||
}
|
||||
|
||||
// Close close all dao.
|
||||
func (s *Service) Close() {
|
||||
s.dao.Close()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) addMission(f func()) {
|
||||
select {
|
||||
case s.missch <- f:
|
||||
default:
|
||||
log.Warn("cacheproc chan full")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) rankproc() {
|
||||
for {
|
||||
s.loadRank(context.TODO())
|
||||
time.Sleep(time.Duration(s.c.Property.LoadRankPeriod))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) cacheproc() {
|
||||
for {
|
||||
f := <-s.missch
|
||||
f()
|
||||
}
|
||||
}
|
49
app/service/main/figure/service/service_test.go
Normal file
49
app/service/main/figure/service/service_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"go-common/app/service/main/figure/conf"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
var (
|
||||
svr *Service
|
||||
ctx = context.TODO()
|
||||
mid int64 = 7593623
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Set("conf", "../cmd/figure-service-test.toml")
|
||||
err := conf.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
svr = New(conf.Conf)
|
||||
}
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
Convey("TestService", t, func() {
|
||||
err := svr.Ping(context.TODO())
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_FigureInfo(t *testing.T) {
|
||||
Convey("TestService_FigureInfo", t, func() {
|
||||
f, err := svr.BatchFigureWithRank(ctx, []int64{mid})
|
||||
So(err, ShouldBeNil)
|
||||
So(f, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_FigureWithRank(t *testing.T) {
|
||||
Convey("TestService_FigureWithRank", t, func() {
|
||||
f, err := svr.FigureWithRank(ctx, mid)
|
||||
So(err, ShouldBeNil)
|
||||
So(f, ShouldNotBeNil)
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user