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,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"],
)

View 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. 初始化项目,更新依赖

View File

@ -0,0 +1,10 @@
# Owner
zhaogangtao
# Author
zhaogangtao
muyang
# Reviewer
zhaogangtao
linmiao

View 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

View File

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

View File

@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["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"],
)

View 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"

View 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
}
}
}

View File

@ -0,0 +1,3 @@
[property]
# 加载分值段信息周期
loadRankPeriod = "1m"

View 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"],
)

View 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
}

View 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()
}
}

View File

@ -0,0 +1,58 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"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"],
)

View 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()
}
}

View 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)
}

View 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
}

View 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)
})
})
})
}

View 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
}

View 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)
})
})
})
})
})
}

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"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"],
)

View 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)
}

View 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)
}

View 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"],
)

View File

@ -0,0 +1,5 @@
package model
type ParamBatchInfo struct {
MIDs []int64 `form:"mids,split"`
}

View 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"`
}

View File

@ -0,0 +1,5 @@
package model
type ArgUserFigure struct {
Mid int64
}

View 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"],
)

View 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
}

View 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)
}

View File

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["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"],
)

View 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
}

View 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"],
)

View 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
}

View 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
}

View 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()
}
}

View 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)
})
}