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 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/job/main/figure-timer/cmd:all-srcs",
"//app/job/main/figure-timer/conf:all-srcs",
"//app/job/main/figure-timer/dao:all-srcs",
"//app/job/main/figure-timer/http:all-srcs",
"//app/job/main/figure-timer/model:all-srcs",
"//app/job/main/figure-timer/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,70 @@
# v1.4.1
1. 优化ping , 去掉中间件ping
# v1.4.0
> 1.change hbase sdk
# v1.3.0
> 1.rebuild
# v1.2.0
> 1.http router to bm
# v1.1.6
> 1.update path
# v1.1.5
> 1.remove stastd
# v1.1.4
> 1.全量计算信用分
# v1.1.3
> 1.放慢figure user upset速度
# v1.1.2
> 1.修复重新计算record的能力
# v1.1.1
> 1.修复record值错误
> 2.增加重新计算record的能力
> 3.Dao interface for mock
# v1.1.0
> 1.计算框架支持支付,弹幕举报,评论举报
# v1.0.10
> 1.fix redis expire
# v1.0.9
> 1.hbase api兼容性修改
# v1.0.8
> 1.增加prom监控
# v1.0.7
> 1.增加自动计算分值段并落库
# v1.0.6
> 1.添加开始mid区间配置
# v1.0.5
> 1.vip类型变更
# v1.0.4
> 1.调整参数
> 2.修改spy分值初始化方式
# v1.0.3
> 1.修复hbase列类型
# v1.0.2
> 1.ActionCounters获取换成Get获取
# v1.0.1
> 1.增加log
> 2.修复hbase解码问题
# v1.0.0
> 1.版本初始化

View File

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

View File

@ -0,0 +1,15 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- muyang
- zhaogangtao
labels:
- job
- job/main/figure-timer
- main
options:
no_parent_owners: true
reviewers:
- linmiao
- muyang
- zhaogangtao

View File

@ -0,0 +1,10 @@
#### figure-timer
##### 项目简介
> 1.异步任务,周期结算用户信用分值
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common

View File

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["figure-timer-job-test.toml"],
importpath = "go-common/app/job/main/figure-timer/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/http:go_default_library",
"//app/job/main/figure-timer/service:go_default_library",
"//library/log: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,143 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
pid = "/tmp/figure-timer-job.pid"
dir = "./"
perf = "0.0.0.0:7080"
family = "figure-timer-job"
address = "172.16.0.148"
[log]
dir = "/data/log/figure-timer-job/"
[bm]
addr = "0.0.0.0:7082"
timeout = "1s"
maxListen = 100
[tracer]
proto = "udp"
addr = "172.16.33.46:5140"
tag = "platform/figure-timer-job"
[mysql]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_figure?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[hbase]
master = ""
meta = ""
dialTimeout = "1s"
readTimeout = "1s"
readsTimeout = "1s"
writeTimeout = "1s"
writesTimeout = "1s"
[hbase.zookeeper]
root = ""
addrs = ["10.23.66.16:2181"]
timeout = "30s"
[redis]
name = "figure-timer-job"
proto = "tcp"
addr = "172.16.33.54:6379"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "192h"
[property]
PendingMidStart = 0
PendingMidShard = 10000
PendingMidRetry = 3
CalcWeekOffset = 1
CycleCron = "0 0 0 * * 1"
ConcurrencySize = 16
FixRecord = true
CycleAll = true
CycleAllCron = "0 0 0 * * 1"
[property.calc]
K1 = 0.1
K2 = 0.1
K3 = 0.1
K4 = 0.1
K5 = 0.1
InitLawfulScore = 10000
InitWideScore = 0
InitFriendlyScore = 5000
InitBountyScore = 0
InitCreativityScore = 0
lawfulNegMax = 10000.0
lawfulPosMax = 10000.0
lawfulPosK = 0.002
lawfulNegK1 = 0.03
lawfulNegK2 = 0.02
lawfulPosL = 200.0
lawfulNegL = 200.0
lawfulPosC1 = 1.0
lawfulPosC2 = 1.0
lawfulPosC3 = 1.0
lawfulNegC1 = 1.0
lawfulNegC2 = 5.0
lawfulNegC3 = 5.0
lawfulPosQ1 = 1.0
lawfulPosQ2 = 1.0
lawfulPosQ3 = 1.0
lawfulNegQ1 = 1.0
lawfulNegQ2 = 1.0
lawfulNegQ3 = 1.0
widePosMax = 15000.0
widePosK = 0.004
wideC1 = 2.0
wideQ1 = 1.0
wideC2 = 0.01
wideQ2 = 1.0
friendlyPosMax = 15000.0
friendlyNegMax = 20000.0
friendlyPosK = 0.004
friendlyNegK = 0.004
friendlyPosL = 200.0
friendlyNegL = 200.0
friendlyPosQ1 = 1.0
friendlyPosC1 = 1.0
friendlyPosQ2 = 1.0
friendlyPosC2 = 2.0
friendlyPosQ3 = 1.0
friendlyPosC3 = 1.0
friendlyNegQ1 = 1.0
friendlyNegC1 = 150.0
friendlyNegQ2 = 1.0
friendlyNegC2 = 80.0
friendlyNegQ3 = 1.0
friendlyNegC3 = 10.0
friendlyNegQ4 = 1.0
friendlyNegC4 = 5.0
bountyMax = 15000.0
bountyPosL = 200.0
bountyK = 0.000015
bountyQ1 = 1.0
bountyC1 = 15.0
bountyQ2 = 2.5
bountyC2 = 1.0
bountyQ3 = 1.0
bountyC3 = 1.0
creativityPosMax = 7000.0
creativityPosK = 0.004
creativityPosL1 = 200.0
creativityQ1 = 1.0
creativityC1 = 0.1

View File

@ -0,0 +1,52 @@
package main
import (
"flag"
"os"
"os/signal"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/http"
"go-common/app/job/main/figure-timer/service"
"go-common/library/log"
"go-common/library/syscall"
)
var (
srv *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()
srv = service.New(conf.Conf)
http.Init(srv)
log.Info("figure-timer-job start")
signalHandler()
}
func signalHandler() {
var (
ch = make(chan os.Signal, 1)
)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGSTOP)
for {
si := <-ch
log.Info("figure-timer-job got a signal (%d)", si)
switch si {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
srv.Close()
srv.Wait()
log.Info("figure-timer-job exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@ -0,0 +1,78 @@
[property]
# 结算开始分片index方便debug
PendingMidStart = 0
# redis中用户列表分片数
PendingMidShard = 10000
# 从redis取出用户列表的重试次数防止一次失败导致本次结算不完整
PendingMidRetry = 3
# 计算周偏移0结算本周分值1计算上周分值...以此类推
CalcWeekOffset = 1
# 周期计算信用分的cron表达式
CycleCron = "0 0 0 * * 1"
# 计算分值并发数
ConcurrencySize = 16
[property.calc]
K1 = 0.1
K2 = 0.1
K3 = 0.1
K4 = 0.1
K5 = 0.1
InitLawfulScore = 10000
InitWideScore = 0
InitFriendlyScore = 5000
InitBountyScore = 0
InitCreativityScore = 0
lawfulNegMax = 10000.0
lawfulPosMax = 10000.0
lawfulPosK = 0.002
lawfulNegK1 = 0.03
lawfulNegK2 = 0.02
lawfulPosL = 200.0
lawfulNegL = 200.0
lawfulPosC1 = 1.0
lawfulPosC2 = 1.0
lawfulPosC3 = 1.0
lawfulNegC1 = 1.0
lawfulNegC2 = 5.0
lawfulNegC3 = 5.0
lawfulPosQ1 = 1.0
lawfulPosQ2 = 1.0
lawfulPosQ3 = 1.0
lawfulNegQ1 = 1.0
lawfulNegQ2 = 1.0
lawfulNegQ3 = 1.0
widePosMax = 15000.0
widePosK = 0.004
wideC1 = 2.0
wideQ1 = 1.0
wideC2 = 0.01
wideQ2 = 1.0
friendlyPosMax = 15000.0
friendlyNegMax = 20000.0
friendlyPosK = 0.004
friendlyNegK = 0.004
friendlyPosL = 200.0
friendlyNegL = 200.0
friendlyPosQ1 = 1.0
friendlyPosC1 = 1.0
friendlyPosQ2 = 1.0
friendlyPosC2 = 2.0
friendlyPosQ3 = 1.0
friendlyPosC3 = 1.0
friendlyNegQ1 = 1.0
friendlyNegC1 = 150.0
friendlyNegQ2 = 1.0
friendlyNegC2 = 80.0
friendlyNegQ3 = 1.0
friendlyNegC3 = 10.0
friendlyNegQ4 = 1.0
friendlyNegC4 = 5.0
bountyMax = 15000.0
bountyK = 0.005
bountyQ1 = 1.0
bountyC1 = 15.0
creativityPosMax = 7000.0
creativityPosK = 0.004
creativityPosL1 = 200.0
creativityQ1 = 1.0
creativityC1 = 0.1

View File

@ -0,0 +1,48 @@
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"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/job/main/figure-timer/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/hbase.v2: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,191 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/hbase.v2"
"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
// mysql
Mysql *sql.Config
// http
BM *bm.ServerConfig
// hbase
Hbase *HBaseConfig
// redis
Redis *Redis
// extra property
Property *Property
}
// HBaseConfig is.
type HBaseConfig struct {
*hbase.Config
ReadTimeout xtime.Duration
WriteTimeout xtime.Duration
}
// Redis redis.
type Redis struct {
*redis.Config
Expire xtime.Duration
}
// Property figure conf
type Property struct {
PendingMidStart int64
PendingMidShard int64
PendingMidRetry int64
CalcWeekOffset int64
CycleCron string
ConcurrencySize int64
FixRecord bool
CycleAll bool
CycleAllCron string
Calc *Calc
}
// Calc figure calc config
type Calc struct {
InitLawfulScore int64
InitWideScore int64
InitFriendlyScore int64
InitBountyScore int64
InitCreativityScore int64
K1 float64
K2 float64
K3 float64
K4 float64
K5 float64
LawfulNegMax float64
LawfulPosMax float64
LawfulPosK float64
LawfulNegK1 float64
LawfulNegK2 float64
LawfulPosL float64
LawfulNegL float64
LawfulPosC1 float64
LawfulPosC2 float64
LawfulPosC3 float64
LawfulNegC1 float64
LawfulNegC2 float64
LawfulNegC3 float64
LawfulPosQ1 float64
LawfulPosQ2 float64
LawfulPosQ3 float64
LawfulNegQ1 float64
LawfulNegQ2 float64
LawfulNegQ3 float64
WidePosMax float64
WidePosK float64
WideC1 float64
WideQ1 float64
WideC2 float64
WideQ2 float64
FriendlyPosMax float64
FriendlyNegMax float64
FriendlyPosK float64
FriendlyNegK float64
FriendlyPosL float64
FriendlyNegL float64
FriendlyPosQ1 float64
FriendlyPosC1 float64
FriendlyPosQ2 float64
FriendlyPosC2 float64
FriendlyPosQ3 float64
FriendlyPosC3 float64
FriendlyNegQ1 float64
FriendlyNegC1 float64
FriendlyNegQ2 float64
FriendlyNegC2 float64
FriendlyNegQ3 float64
FriendlyNegC3 float64
FriendlyNegQ4 float64
FriendlyNegC4 float64
BountyMax float64
BountyPosL float64
BountyK float64
BountyQ1 float64
BountyC1 float64
BountyQ2 float64
BountyC2 float64
BountyQ3 float64
BountyC3 float64
CreativityPosMax float64
CreativityPosK float64
CreativityPosL1 float64
CreativityQ1 float64
CreativityC1 float64
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init create config instance.
func Init() (err error) {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@ -0,0 +1,23 @@
package conf
import (
"flag"
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func init() {
flag.Set("conf", "../cmd/figure-timer-job-test.toml")
}
func TestInit(t *testing.T) {
Convey("TEST conf", t, func() {
err := Init()
So(err, ShouldBeNil)
So(Conf.Property, ShouldNotBeNil)
So(Conf.Property.Calc, ShouldNotBeNil)
fmt.Printf("%+v", Conf.Property.Calc)
})
}

View File

@ -0,0 +1,61 @@
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"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/model:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"hbase.go",
"mysql.go",
"redis.go",
],
importpath = "go-common/app/job/main/figure-timer/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/job/main/figure-timer/dao/mock_dao:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,94 @@
package dao
import (
"context"
"time"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/model"
"go-common/library/cache/redis"
"go-common/library/database/hbase.v2"
"go-common/library/database/sql"
)
// Int is dao interface
type Int interface {
HBase
Mysql
Redis
Close()
Ping(c context.Context) error
}
// HBase dao hbase interface
type HBase interface {
UserInfo(c context.Context, mid int64, weekVer int64) (userInfo *model.UserInfo, err error)
PutCalcRecord(c context.Context, record *model.FigureRecord, weekTS int64) (err error)
CalcRecords(c context.Context, mid int64, weekTSFrom, weekTSTo int64) (figureRecords []*model.FigureRecord, err error)
ActionCounter(c context.Context, mid int64, ts int64) (counter *model.ActionCounter, err error)
}
// Mysql dao mysql interface
type Mysql interface {
Figure(c context.Context, mid int64) (figure *model.Figure, err error)
Figures(c context.Context, fromMid int64, limit int) (figures []*model.Figure, end bool, err error)
UpsertFigure(c context.Context, figure *model.Figure) (id int64, err error)
InsertRankHistory(c context.Context, rank *model.Rank) (id int64, err error)
UpsertRank(c context.Context, rank *model.Rank) (id int64, err error)
}
// Redis dao redis interface
type Redis interface {
FigureCache(c context.Context, mid int64) (figure *model.Figure, err error)
SetFigureCache(c context.Context, figure *model.Figure) (err error)
PendingMidsCache(c context.Context, version int64, shard int64) (mids []int64, err error)
RemoveCache(c context.Context, mid int64) (err error)
}
// Dao struct info of Dao.
type Dao struct {
c *conf.Config
mysql *sql.DB
hbase *hbase.Client
redis *redis.Pool
redisExpire int32
}
// New new a Dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
mysql: sql.NewMySQL(c.Mysql),
hbase: hbase.NewClient(c.Hbase.Config),
redis: redis.NewPool(c.Redis.Config),
redisExpire: int32(time.Duration(c.Redis.Expire) / time.Second),
}
return
}
// Close close connections of mc, redis, db.
func (d *Dao) Close() {
if d.mysql != nil {
d.mysql.Close()
}
if d.redis != nil {
d.redis.Close()
}
if d.hbase != nil {
d.hbase.Close()
}
}
// Ping ping health of db.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.mysql.Ping(c); err != nil {
return
}
// if err = d.hbase.Ping(c); err != nil {
// return
// }
if err = d.PingRedis(c); err != nil {
return
}
return
}

View File

@ -0,0 +1,221 @@
package dao
import (
"context"
// "encoding/binary"
"flag"
"testing"
"time"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/model"
// "go-common/library/log"
"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
)
var (
dao *Dao
ctx = context.TODO()
)
func init() {
var err error
flag.Set("conf", "../cmd/figure-timer-job-test.toml")
if err = conf.Init(); err != nil {
panic(err)
}
dao = New(conf.Conf)
}
func TestInit(t *testing.T) {
Convey("TEST init", t, func() {
var err error
So(dao.c, ShouldNotBeNil)
So(dao.mysql, ShouldNotBeNil)
So(dao.hbase, ShouldNotBeNil)
So(dao.redis, ShouldNotBeNil)
err = dao.Ping(ctx)
So(err, ShouldBeNil)
})
}
func TestMysql(t *testing.T) {
Convey("TEST figure", t, func() {
var (
figure = &model.Figure{
Mid: 100,
Score: 100,
LawfulScore: 50,
WideScore: 0,
FriendlyScore: 23,
BountyScore: 250,
CreativityScore: 0,
Ver: 0,
}
figure2 *model.Figure
figureRank = &model.Rank{
ScoreFrom: 2000,
ScoreTo: 3000,
Percentage: 50,
Ver: 233,
}
err error
)
figure.ID, err = dao.UpsertFigure(ctx, figure)
So(err, ShouldBeNil)
figure2, err = dao.Figure(ctx, figure.Mid)
So(err, ShouldBeNil)
So(figure2, ShouldNotBeNil)
So(figure2.Ctime, ShouldNotBeEmpty)
So(figure2.Ctime, ShouldHappenBefore, time.Now().Add(time.Second))
So(figure2.Mtime, ShouldNotBeEmpty)
So(figure2.Mtime, ShouldHappenBefore, time.Now().Add(time.Second))
_, err = dao.InsertRankHistory(ctx, figureRank)
So(err, ShouldBeNil)
_, err = dao.UpsertRank(ctx, figureRank)
So(err, ShouldBeNil)
})
Convey("TEST figures", t, func() {
for shard := 0; shard < 100; shard++ {
var (
end bool
fromMid = int64(shard)
figures []*model.Figure
err error
)
for !end {
figures, end, err = dao.Figures(ctx, fromMid, 100)
So(err, ShouldBeNil)
for _, f := range figures {
So(f.Mid, ShouldBeGreaterThan, 0)
if fromMid < f.Mid {
fromMid = f.Mid
}
}
}
}
})
}
func TestRedis(t *testing.T) {
Convey("TEST figure", t, func() {
var (
figure = &model.Figure{
Mid: 23,
Score: 100,
LawfulScore: 50,
WideScore: 0,
FriendlyScore: 23,
BountyScore: 250,
CreativityScore: 0,
Ver: 0,
}
figure2 *model.Figure
err error
)
err = dao.SetFigureCache(ctx, figure)
So(err, ShouldBeNil)
figure2, err = dao.FigureCache(ctx, figure.Mid)
So(err, ShouldBeNil)
So(figure2, ShouldNotBeNil)
So(figure, ShouldResemble, figure2)
})
Convey("TEST pending mids", t, func() {
var (
mid int64 = 23
ver int64
shard = mid % dao.c.Property.PendingMidShard
mids []int64
err error
)
err = dao.setPendingMidCache(ctx, mid, ver)
So(err, ShouldBeNil)
mids, err = dao.PendingMidsCache(ctx, ver, shard)
So(err, ShouldBeNil)
So(mids, ShouldNotBeEmpty)
})
}
func (d *Dao) setPendingMidCache(c context.Context, mid int64, ver int64) (err error) {
var (
key = keyPendingMids(ver, mid%d.c.Property.PendingMidShard)
conn = d.redis.Get(c)
)
defer conn.Close()
if err = conn.Send("SADD", key, mid); err != nil {
err = errors.Wrapf(err, "conn.Send(SADD,%s,%d)", key, mid)
return
}
if err = conn.Send("EXPIRE", key, d); err != nil {
err = errors.Wrapf(err, "conn.Send(EXPIRE,%s,%v)", key, d)
return
}
if err = conn.Flush(); err != nil {
err = errors.WithStack(err)
return
}
if _, err = conn.Receive(); err != nil {
err = errors.WithStack(err)
return
}
return
}
// func testHbase(t *testing.T) {
// Convey("TEST figure_activity", t, func() {
// var (
// mid int64 = 15555180
// err error
// weekVer = time.Date(2017, 10, 2, 0, 0, 0, 0, time.Local).Unix()
// weekVerFrom = time.Date(2016, 10, 3, 0, 0, 0, 0, time.Local).Unix()
// weekVerTo = time.Date(2017, 9, 25, 0, 0, 0, 0, time.Local).Unix()
// userInfo *model.UserInfo
// figureRecord = &model.FigureRecord{
// Mid: mid,
// XPosLawful: 12,
// XNegLawful: 2,
// XPosWide: 12,
// XNegWide: 13,
// XPosFriendly: 233,
// XNegFriendly: 234,
// XPosCreativity: 0,
// XNegCreativity: 1,
// XPosBounty: -250,
// XNegBounty: -251,
// }
// figureRecords []*model.FigureRecord
// )
// err = dao.putSpyScore(ctx, mid, 100)
// So(err, ShouldBeNil)
// userInfo, err = dao.UserInfo(ctx, mid, weekVer)
// So(err, ShouldBeNil)
// So(userInfo, ShouldNotBeNil)
// err = dao.PutCalcRecord(ctx, figureRecord, time.Date(2016, 12, 3, 0, 0, 0, 0, time.Local).Unix())
// So(err, ShouldBeNil)
// figureRecords, err = dao.CalcRecords(ctx, mid, weekVerFrom, weekVerTo+1)
// So(err, ShouldBeNil)
// So(figureRecords, ShouldNotBeEmpty)
// })
// }
// // PutSpyScore add spy score info.
// func (d *Dao) putSpyScore(c context.Context, mid int64, score int8) (err error) {
// var (
// key = rowKeyUserInfo(mid)
// scoreB = make([]byte, 2)
// ctx, cancel = context.WithTimeout(c, time.Duration(conf.Conf.Hbase.WriteTimeout))
// )
// defer cancel()
// binary.BigEndian.PutUint16(scoreB, uint16(score))
// values := map[string]map[string][]byte{_hbaseUserFC: map[string][]byte{_hbaseUserQSpy: scoreB}}
// if _, err = d.hbase.PutStr(ctx, _hbaseUserTable, key, values); err != nil {
// log.Error("hbase.PutStr(%s, %s, %v) error(%v)", _hbaseUserTable, key, values, err)
// return
// }
// return
// }

View File

@ -0,0 +1,353 @@
package dao
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"io"
"math"
"strconv"
"strings"
"time"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
"github.com/pkg/errors"
"github.com/tsuna/gohbase/hrpc"
)
var (
// record
_hbaseRecordTable = "ugc:figurecalcrecord"
_hbaseRecordFC = "x"
_hbaseRecordQLawfulPosX = "pos_lawful"
_hbaseRecordQLawfulNegX = "neg_lawful"
_hbaseRecordQWidePosX = "pos_wide"
_hbaseRecordQWideNegX = "neg_wide"
_hbaseRecordQFriendlyPosX = "pos_friendly"
_hbaseRecordQFriendlyNegX = "neg_friendly"
_hbaseRecordQCreativityPosX = "pos_creativity"
_hbaseRecordQCreativityNegX = "neg_creativity"
_hbaseRecordQBountyPosX = "pos_bounty"
_hbaseRecordQBountyNegX = "neg_bounty"
// UserInfo
_hbaseUserTable = "ugc:figureuserstatus"
_hbaseUserFC = "user"
_hbaseUserQExp = "exp"
_hbaseUserQSpy = "spy_score"
_hbaseUserQArchiveViews = "archive_views"
_hbaseUserQVip = "vip_status"
_hbaseUserQDisciplineCommittee = "discipline_committee"
// ActionCounter
_hbaseActionTable = "ugc:figureactioncounter"
_hbaseActionFC = "user"
_hbaseActionQCoinCount = "coins"
_hbaseActionQReplyCount = "replies"
_hbaseActionQDanmakuCount = "danmaku"
_hbaseActionQCoinLowRisk = "coin_low_risk"
_hbaseActionQCoinHighRisk = "coin_high_risk"
_hbaseActionQReplyLowRisk = "reply_low_risk"
_hbaseActionQReplyHighRisk = "reply_high_risk"
_hbaseActionQReplyLiked = "reply_liked"
_hbaseActionQReplyUnLiked = "reply_hate"
_hbaseActionQReportReplyPassed = "report_reply_passed"
_hbaseActionQReportDanmakuPassed = "report_danmaku_passed"
_hbaseActionQPublishReplyDeleted = "publish_reply_deleted"
_hbaseActionQPublishDanmakuDeleted = "publish_danmaku_deleted"
_hbaseActionQPayMoney = "pay_money"
_hbaseActionQPayLiveMoney = "pay_live_money"
)
func rowKeyFigureRecord(mid int64, weekTS int64) (key string) {
return fmt.Sprintf("%d_%d", mid, weekTS)
}
func rowKeyUserInfo(mid int64) (key string) {
return fmt.Sprintf("%d", mid)
}
func rowKeyActionCounter(mid int64, dayTS int64) (key string) {
return fmt.Sprintf("%d_%d", mid, dayTS)
}
// UserInfo get it from hbase
func (d *Dao) UserInfo(c context.Context, mid int64, weekVer int64) (userInfo *model.UserInfo, err error) {
var (
result *hrpc.Result
key = rowKeyUserInfo(mid)
ctx, cancel = context.WithTimeout(c, time.Duration(d.c.Hbase.ReadTimeout))
)
defer cancel()
if result, err = d.hbase.GetStr(ctx, _hbaseUserTable, key); err != nil {
err = errors.Wrapf(err, "hbase.GetStr(%s,%s)", _hbaseUserTable, key)
return
}
userInfo = &model.UserInfo{Mid: mid, SpyScore: math.MaxUint64}
for _, c := range result.Cells {
if c == nil {
continue
}
if bytes.Equal([]byte(_hbaseUserFC), c.Family) {
switch string(c.Qualifier) {
case _hbaseUserQExp:
userInfo.Exp = binary.BigEndian.Uint64(c.Value)
case _hbaseUserQSpy:
userInfo.SpyScore = binary.BigEndian.Uint64(c.Value)
case _hbaseUserQArchiveViews:
userInfo.ArchiveViews = binary.BigEndian.Uint64(c.Value)
case _hbaseUserQVip:
userInfo.VIPStatus = binary.BigEndian.Uint64(c.Value)
case _hbaseUserQDisciplineCommittee:
userInfo.DisciplineCommittee = binary.BigEndian.Uint16(c.Value)
}
}
}
// 未获取到spy分值特殊处理
if userInfo.SpyScore == math.MaxUint64 {
userInfo.SpyScore = 100
}
log.Info("User Info [%+v]", userInfo)
return
}
// PutCalcRecord put record all the params x.
func (d *Dao) PutCalcRecord(c context.Context, record *model.FigureRecord, weekTS int64) (err error) {
var (
key = rowKeyFigureRecord(record.Mid, weekTS)
lawfulPosX = make([]byte, 8)
lawfulNegX = make([]byte, 8)
widePosX = make([]byte, 8)
wideNegX = make([]byte, 8)
friendlyPosX = make([]byte, 8)
friendlyNegX = make([]byte, 8)
creativityPosX = make([]byte, 8)
creativityNegX = make([]byte, 8)
bountyPosX = make([]byte, 8)
bountyNegX = make([]byte, 8)
ctx, cancel = context.WithTimeout(c, time.Duration(d.c.Hbase.WriteTimeout))
)
defer cancel()
binary.BigEndian.PutUint64(lawfulPosX, uint64(record.XPosLawful))
binary.BigEndian.PutUint64(lawfulNegX, uint64(record.XNegLawful))
binary.BigEndian.PutUint64(widePosX, uint64(record.XPosWide))
binary.BigEndian.PutUint64(wideNegX, uint64(record.XNegWide))
binary.BigEndian.PutUint64(friendlyPosX, uint64(record.XPosFriendly))
binary.BigEndian.PutUint64(friendlyNegX, uint64(record.XNegFriendly))
binary.BigEndian.PutUint64(creativityPosX, uint64(record.XPosCreativity))
binary.BigEndian.PutUint64(creativityNegX, uint64(record.XNegCreativity))
binary.BigEndian.PutUint64(bountyPosX, uint64(record.XPosBounty))
binary.BigEndian.PutUint64(bountyNegX, uint64(record.XNegBounty))
values := map[string]map[string][]byte{_hbaseRecordFC: {
_hbaseRecordQLawfulPosX: lawfulPosX,
_hbaseRecordQLawfulNegX: lawfulNegX,
_hbaseRecordQWideNegX: wideNegX,
_hbaseRecordQFriendlyPosX: friendlyPosX,
_hbaseRecordQFriendlyNegX: friendlyNegX,
_hbaseRecordQCreativityPosX: creativityPosX,
_hbaseRecordQCreativityNegX: creativityNegX,
_hbaseRecordQBountyPosX: bountyPosX,
_hbaseRecordQBountyNegX: bountyNegX,
}}
if _, err = d.hbase.PutStr(ctx, _hbaseRecordTable, key, values); err != nil {
err = errors.Wrapf(err, "hbase.Put(%s,%s,%+v) error(%v)", _hbaseRecordTable, key, values, err)
}
return
}
// CalcRecords get it from hbase
func (d *Dao) CalcRecords(c context.Context, mid int64, weekTSFrom, weekTSTo int64) (figureRecords []*model.FigureRecord, err error) {
var (
scanner hrpc.Scanner
keyFrom = rowKeyFigureRecord(mid, weekTSFrom)
keyTo = rowKeyFigureRecord(mid, weekTSTo)
ctx, cancel = context.WithTimeout(c, time.Duration(d.c.Hbase.ReadTimeout))
)
defer cancel()
if scanner, err = d.hbase.ScanRangeStr(ctx, _hbaseRecordTable, keyFrom, keyTo); err != nil {
err = errors.Wrapf(err, "hbase.ScanRangeStr(%s,%s,%s)", _hbaseRecordTable, keyFrom, keyTo)
return
}
for {
res, err := scanner.Next()
if err != nil {
if err != io.EOF {
err = errors.WithStack(err)
return nil, err
}
break
}
figureRecord := &model.FigureRecord{Mid: mid}
for i, c := range res.Cells {
if c == nil {
continue
}
if bytes.Equal([]byte(_hbaseRecordFC), c.Family) {
switch string(c.Qualifier) {
case _hbaseRecordQLawfulPosX:
figureRecord.XPosLawful = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQLawfulNegX:
figureRecord.XNegLawful = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQWidePosX:
figureRecord.XPosWide = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQWideNegX:
figureRecord.XNegWide = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQFriendlyPosX:
figureRecord.XPosFriendly = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQFriendlyNegX:
figureRecord.XNegFriendly = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQCreativityPosX:
figureRecord.XPosCreativity = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQCreativityNegX:
figureRecord.XNegCreativity = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQBountyPosX:
figureRecord.XPosBounty = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseRecordQBountyNegX:
figureRecord.XNegBounty = int64(binary.BigEndian.Uint64(c.Value))
}
}
if i == 0 {
figureRecord.Version = time.Unix(parseVersion(string(c.Row), c.Timestamp), 0)
}
}
log.Info("User figure record [%+v]", figureRecord)
figureRecords = append(figureRecords, figureRecord)
}
return
}
// ActionCounter .
func (d *Dao) ActionCounter(c context.Context, mid int64, ts int64) (actionCounter *model.ActionCounter, err error) {
var (
result *hrpc.Result
key = rowKeyActionCounter(mid, ts)
ctx, cancel = context.WithTimeout(c, time.Duration(d.c.Hbase.ReadTimeout))
)
defer cancel()
log.Info("Get mid [%d] key [%s]", mid, key)
if result, err = d.hbase.GetStr(ctx, _hbaseActionTable, key); err != nil {
err = errors.Wrapf(err, "hbase.GetStr(%s,%s)", _hbaseActionTable, key)
return
}
actionCounter = &model.ActionCounter{Mid: mid}
for i, c := range result.Cells {
if c == nil {
continue
}
if bytes.Equal([]byte(_hbaseActionFC), c.Family) {
switch string(c.Qualifier) {
case _hbaseActionQCoinCount:
actionCounter.CoinCount = binary.BigEndian.Uint64(c.Value)
case _hbaseActionQReplyCount:
actionCounter.ReplyCount = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQDanmakuCount:
actionCounter.DanmakuCount = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQCoinLowRisk:
actionCounter.CoinLowRisk = binary.BigEndian.Uint64(c.Value)
case _hbaseActionQCoinHighRisk:
actionCounter.CoinHighRisk = binary.BigEndian.Uint64(c.Value)
case _hbaseActionQReplyLowRisk:
actionCounter.ReplyLowRisk = binary.BigEndian.Uint64(c.Value)
case _hbaseActionQReplyHighRisk:
actionCounter.ReplyHighRisk = binary.BigEndian.Uint64(c.Value)
case _hbaseActionQReplyLiked:
actionCounter.ReplyLiked = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQReplyUnLiked:
actionCounter.ReplyUnliked = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQReportReplyPassed:
actionCounter.ReportReplyPassed = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQReportDanmakuPassed:
actionCounter.ReportDanmakuPassed = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQPublishReplyDeleted:
actionCounter.PublishReplyDeleted = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQPublishDanmakuDeleted:
actionCounter.PublishDanmakuDeleted = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQPayMoney:
actionCounter.PayMoney = int64(binary.BigEndian.Uint64(c.Value))
case _hbaseActionQPayLiveMoney:
actionCounter.PayLiveMoney = int64(binary.BigEndian.Uint64(c.Value))
}
}
if i == 0 {
actionCounter.Version = time.Unix(parseVersion(string(c.Row), c.Timestamp), 0)
}
}
log.Info("User action counter [%+v]", actionCounter)
return
}
// // ActionCounters .
// func (d *Dao) ActionCounters(c context.Context, mid int64, tsfrom int64, tsto int64) (actionCounters []*model.ActionCounter, err error) {
// var (
// scan *hrpc.Scan
// results []*hrpc.Result
// keyFrom = rowKeyActionCounter(mid, tsfrom)
// keyTo = rowKeyActionCounter(mid, tsto)
// ctx, cancel = context.WithTimeout(c, time.Duration(d.c.Hbase.ReadTimeout))
// )
// defer cancel()
// log.Info("Scan mid [%d] keyFrom [%s] keyTo [%s]", mid, keyFrom, keyTo)
// if scan, err = hrpc.NewScanRangeStr(ctx, _hbaseActionTable, keyFrom, keyTo); err != nil {
// err = errors.Wrapf(err, "hrcp.NewScanRangeStr(%s,%s,%s)", _hbaseActionTable, keyFrom, keyTo)
// return
// }
// if results, err = d.hbase.Scan(ctx, scan); err != nil {
// err = errors.Wrapf(err, "hbase.Scan(%s,%s,%s)", _hbaseActionTable, keyFrom, keyTo)
// return
// }
// if len(results) == 0 {
// return
// }
// for _, res := range results {
// if res == nil {
// continue
// }
// actionCounter := &model.ActionCounter{Mid: mid}
// for i, c := range res.Cells {
// if c == nil {
// continue
// }
// if bytes.Equal([]byte(_hbaseActionFC), c.Family) {
// switch string(c.Qualifier) {
// case _hbaseActionQCoinCount:
// actionCounter.CoinCount = binary.BigEndian.Uint64(c.Value)
// case _hbaseActionQReplyCount:
// actionCounter.ReplyCount = int64(binary.BigEndian.Uint64(c.Value))
// case _hbaseActionQCoinLowRisk:
// actionCounter.CoinLowRisk = binary.BigEndian.Uint64(c.Value)
// case _hbaseActionQCoinHighRisk:
// actionCounter.CoinHighRisk = binary.BigEndian.Uint64(c.Value)
// case _hbaseActionQReplyLowRisk:
// actionCounter.ReplyLowRisk = binary.BigEndian.Uint64(c.Value)
// case _hbaseActionQReplyHighRisk:
// actionCounter.ReplyHighRisk = binary.BigEndian.Uint64(c.Value)
// case _hbaseActionQReplyLiked:
// actionCounter.ReplyLiked = int64(binary.BigEndian.Uint64(c.Value))
// case _hbaseActionQReplyUnLiked:
// actionCounter.ReplyUnliked = int64(binary.BigEndian.Uint64(c.Value))
// }
// }
// if i == 0 {
// actionCounter.Version = time.Unix(parseVersion(string(c.Row), c.Timestamp), 0)
// }
// }
// log.Info("User action counter [%+v]", actionCounter)
// actionCounters = append(actionCounters, actionCounter)
// }
// return
// }
func parseVersion(rowKey string, timestamp *uint64) (ts int64) {
strs := strings.Split(rowKey, "_")
if len(strs) < 2 {
return int64(*timestamp)
}
var err error
if ts, err = strconv.ParseInt(strs[1], 10, 64); err != nil {
return int64(*timestamp)
}
return ts
}

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 = ["mock_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 = ["mock.go"],
importpath = "go-common/app/job/main/figure-timer/dao/mock_dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/figure-timer/model:go_default_library",
"//vendor/github.com/golang/mock/gomock: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 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: go-common/app/job/main/figure-timer/dao (interfaces: DaoInt)
// Package mock_dao is a generated GoMock package.
package mock_dao
import (
context "context"
model "go-common/app/job/main/figure-timer/model"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockDaoInt is a mock of DaoInt interface
type MockDaoInt struct {
ctrl *gomock.Controller
recorder *MockDaoIntMockRecorder
}
// MockDaoIntMockRecorder is the mock recorder for MockDaoInt
type MockDaoIntMockRecorder struct {
mock *MockDaoInt
}
// NewMockDaoInt creates a new mock instance
func NewMockDaoInt(ctrl *gomock.Controller) *MockDaoInt {
mock := &MockDaoInt{ctrl: ctrl}
mock.recorder = &MockDaoIntMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockDaoInt) EXPECT() *MockDaoIntMockRecorder {
return m.recorder
}
// ActionCounter mocks base method
func (m *MockDaoInt) ActionCounter(arg0 context.Context, arg1, arg2 int64) (*model.ActionCounter, error) {
ret := m.ctrl.Call(m, "ActionCounter", arg0, arg1, arg2)
ret0, _ := ret[0].(*model.ActionCounter)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ActionCounter indicates an expected call of ActionCounter
func (mr *MockDaoIntMockRecorder) ActionCounter(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionCounter", reflect.TypeOf((*MockDaoInt)(nil).ActionCounter), arg0, arg1, arg2)
}
// CalcRecords mocks base method
func (m *MockDaoInt) CalcRecords(arg0 context.Context, arg1, arg2, arg3 int64) ([]*model.FigureRecord, error) {
ret := m.ctrl.Call(m, "CalcRecords", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].([]*model.FigureRecord)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CalcRecords indicates an expected call of CalcRecords
func (mr *MockDaoIntMockRecorder) CalcRecords(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalcRecords", reflect.TypeOf((*MockDaoInt)(nil).CalcRecords), arg0, arg1, arg2, arg3)
}
// Close mocks base method
func (m *MockDaoInt) Close() {
m.ctrl.Call(m, "Close")
}
// Close indicates an expected call of Close
func (mr *MockDaoIntMockRecorder) Close() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDaoInt)(nil).Close))
}
// Figure mocks base method
func (m *MockDaoInt) Figure(arg0 context.Context, arg1 int64) (*model.Figure, error) {
ret := m.ctrl.Call(m, "Figure", arg0, arg1)
ret0, _ := ret[0].(*model.Figure)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Figure indicates an expected call of Figure
func (mr *MockDaoIntMockRecorder) Figure(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Figure", reflect.TypeOf((*MockDaoInt)(nil).Figure), arg0, arg1)
}
// FigureCache mocks base method
func (m *MockDaoInt) FigureCache(arg0 context.Context, arg1 int64) (*model.Figure, error) {
ret := m.ctrl.Call(m, "FigureCache", arg0, arg1)
ret0, _ := ret[0].(*model.Figure)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FigureCache indicates an expected call of FigureCache
func (mr *MockDaoIntMockRecorder) FigureCache(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FigureCache", reflect.TypeOf((*MockDaoInt)(nil).FigureCache), arg0, arg1)
}
// Figures mocks base method
func (m *MockDaoInt) Figures(arg0 context.Context, arg1 int64, arg2 int) ([]*model.Figure, bool, error) {
ret := m.ctrl.Call(m, "Figures", arg0, arg1, arg2)
ret0, _ := ret[0].([]*model.Figure)
ret1, _ := ret[1].(bool)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// Figures indicates an expected call of Figures
func (mr *MockDaoIntMockRecorder) Figures(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Figures", reflect.TypeOf((*MockDaoInt)(nil).Figures), arg0, arg1, arg2)
}
// InsertRankHistory mocks base method
func (m *MockDaoInt) InsertRankHistory(arg0 context.Context, arg1 *model.Rank) (int64, error) {
ret := m.ctrl.Call(m, "InsertRankHistory", arg0, arg1)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertRankHistory indicates an expected call of InsertRankHistory
func (mr *MockDaoIntMockRecorder) InsertRankHistory(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertRankHistory", reflect.TypeOf((*MockDaoInt)(nil).InsertRankHistory), arg0, arg1)
}
// PendingMidsCache mocks base method
func (m *MockDaoInt) PendingMidsCache(arg0 context.Context, arg1, arg2 int64) ([]int64, error) {
ret := m.ctrl.Call(m, "PendingMidsCache", arg0, arg1, arg2)
ret0, _ := ret[0].([]int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// PendingMidsCache indicates an expected call of PendingMidsCache
func (mr *MockDaoIntMockRecorder) PendingMidsCache(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PendingMidsCache", reflect.TypeOf((*MockDaoInt)(nil).PendingMidsCache), arg0, arg1, arg2)
}
// Ping mocks base method
func (m *MockDaoInt) Ping(arg0 context.Context) error {
ret := m.ctrl.Call(m, "Ping", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Ping indicates an expected call of Ping
func (mr *MockDaoIntMockRecorder) Ping(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockDaoInt)(nil).Ping), arg0)
}
// PutCalcRecord mocks base method
func (m *MockDaoInt) PutCalcRecord(arg0 context.Context, arg1 *model.FigureRecord, arg2 int64) error {
ret := m.ctrl.Call(m, "PutCalcRecord", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// PutCalcRecord indicates an expected call of PutCalcRecord
func (mr *MockDaoIntMockRecorder) PutCalcRecord(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutCalcRecord", reflect.TypeOf((*MockDaoInt)(nil).PutCalcRecord), arg0, arg1, arg2)
}
// RemoveCache mocks base method
func (m *MockDaoInt) RemoveCache(arg0 context.Context, arg1 int64) error {
ret := m.ctrl.Call(m, "RemoveCache", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// RemoveCache indicates an expected call of RemoveCache
func (mr *MockDaoIntMockRecorder) RemoveCache(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveCache", reflect.TypeOf((*MockDaoInt)(nil).RemoveCache), arg0, arg1)
}
// SetFigureCache mocks base method
func (m *MockDaoInt) SetFigureCache(arg0 context.Context, arg1 *model.Figure) error {
ret := m.ctrl.Call(m, "SetFigureCache", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// SetFigureCache indicates an expected call of SetFigureCache
func (mr *MockDaoIntMockRecorder) SetFigureCache(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFigureCache", reflect.TypeOf((*MockDaoInt)(nil).SetFigureCache), arg0, arg1)
}
// UpsertFigure mocks base method
func (m *MockDaoInt) UpsertFigure(arg0 context.Context, arg1 *model.Figure) (int64, error) {
ret := m.ctrl.Call(m, "UpsertFigure", arg0, arg1)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpsertFigure indicates an expected call of UpsertFigure
func (mr *MockDaoIntMockRecorder) UpsertFigure(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertFigure", reflect.TypeOf((*MockDaoInt)(nil).UpsertFigure), arg0, arg1)
}
// UpsertRank mocks base method
func (m *MockDaoInt) UpsertRank(arg0 context.Context, arg1 *model.Rank) (int64, error) {
ret := m.ctrl.Call(m, "UpsertRank", arg0, arg1)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpsertRank indicates an expected call of UpsertRank
func (mr *MockDaoIntMockRecorder) UpsertRank(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertRank", reflect.TypeOf((*MockDaoInt)(nil).UpsertRank), arg0, arg1)
}
// UserInfo mocks base method
func (m *MockDaoInt) UserInfo(arg0 context.Context, arg1, arg2 int64) (*model.UserInfo, error) {
ret := m.ctrl.Call(m, "UserInfo", arg0, arg1, arg2)
ret0, _ := ret[0].(*model.UserInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UserInfo indicates an expected call of UserInfo
func (mr *MockDaoIntMockRecorder) UserInfo(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserInfo", reflect.TypeOf((*MockDaoInt)(nil).UserInfo), arg0, arg1, arg2)
}

View File

@ -0,0 +1,13 @@
package mock_dao
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestFake(t *testing.T) {
Convey("Test fake", t, func() {
t.Log("nothing here")
})
}

View File

@ -0,0 +1,119 @@
package dao
import (
"context"
"database/sql"
"fmt"
"time"
"go-common/app/job/main/figure-timer/model"
xsql "go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_shard = 100
_selFigure = `SELECT id,mid,score,lawful_score,wide_score,friendly_score,bounty_score,creativity_score,ver,ctime,mtime FROM figure_user_%02d WHERE mid=? LIMIT 1`
_selFigures = `SELECT id,mid,score,lawful_score,wide_score,friendly_score,bounty_score,creativity_score,ver,ctime,mtime FROM figure_user_%02d WHERE mid>? LIMIT ?`
_upsertFigure = `INSERT INTO figure_user_%02d (mid,score,lawful_score,wide_score,friendly_score,bounty_score,creativity_score,ver,ctime,mtime) VALUES (?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE score=?,lawful_score=?,wide_score=?,friendly_score=?,bounty_score=?,creativity_score=?,ver=?,mtime=?`
_insertRank = `INSERT INTO figure_rank (score_from,score_to,percentage) VALUES(?,?,?) ON DUPLICATE KEY UPDATE score_from=?,score_to=?,percentage=?`
_insertRankHistory = `INSERT INTO figure_rank_history (score_from,score_to,percentage,ver) VALUES(?,?,?,?)`
)
func hit(mid int64) int64 {
return mid % _shard
}
// Figure get Figure from db
func (d *Dao) Figure(c context.Context, mid int64) (figure *model.Figure, err error) {
row := d.mysql.QueryRow(c, fmt.Sprintf(_selFigure, hit(mid)), mid)
figure = &model.Figure{}
if err = row.Scan(&figure.ID, &figure.Mid, &figure.Score, &figure.LawfulScore, &figure.WideScore, &figure.FriendlyScore, &figure.BountyScore, &figure.CreativityScore, &figure.Ver, &figure.Ctime, &figure.Mtime); err != nil {
if err == xsql.ErrNoRows {
err = nil
figure = nil
return
}
err = errors.WithStack(err)
return
}
return
}
// Figures get all figure info from formMid
func (d *Dao) Figures(c context.Context, fromMid int64, limit int) (figures []*model.Figure, end bool, err error) {
if limit <= 0 {
return
}
var (
rows *xsql.Rows
)
if rows, err = d.mysql.Query(c, fmt.Sprintf(_selFigures, hit(fromMid)), fromMid, limit); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
figure := &model.Figure{}
if err = rows.Scan(&figure.ID, &figure.Mid, &figure.Score, &figure.LawfulScore, &figure.WideScore, &figure.FriendlyScore, &figure.BountyScore, &figure.CreativityScore, &figure.Ver, &figure.Ctime, &figure.Mtime); err != nil {
if err == xsql.ErrNoRows {
err = nil
end = true
return
}
return
}
figures = append(figures, figure)
}
if len(figures) < limit {
end = true
}
err = errors.WithStack(rows.Err())
return
}
// UpsertFigure insert or update(if mid duplicated) Figure
func (d *Dao) UpsertFigure(c context.Context, figure *model.Figure) (id int64, err error) {
var (
result sql.Result
now = time.Now()
)
if result, err = d.mysql.Exec(c, fmt.Sprintf(_upsertFigure, hit(figure.Mid)), figure.Mid, figure.Score, figure.LawfulScore, figure.WideScore, figure.FriendlyScore, figure.BountyScore, figure.CreativityScore, figure.Ver, now, now, figure.Score, figure.LawfulScore, figure.WideScore, figure.FriendlyScore, figure.BountyScore, figure.CreativityScore, figure.Ver, now); err != nil {
return
}
if id, err = result.LastInsertId(); err != nil {
err = errors.WithStack(err)
}
return
}
// InsertRankHistory insert figure rank history to db
func (d *Dao) InsertRankHistory(c context.Context, rank *model.Rank) (id int64, err error) {
var (
res sql.Result
)
if res, err = d.mysql.Exec(c, _insertRankHistory, rank.ScoreFrom, rank.ScoreTo, rank.Percentage, rank.Ver); err != nil {
return
}
if id, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
return
}
return
}
// UpsertRank insert or update figure rank to db
func (d *Dao) UpsertRank(c context.Context, rank *model.Rank) (id int64, err error) {
var (
res sql.Result
)
if res, err = d.mysql.Exec(c, _insertRank, rank.ScoreFrom, rank.ScoreTo, rank.Percentage, rank.ScoreFrom, rank.ScoreTo, rank.Percentage); err != nil {
return
}
if id, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
return
}
return
}

View File

@ -0,0 +1,99 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"go-common/app/job/main/figure-timer/model"
"go-common/library/cache/redis"
"go-common/library/log"
"github.com/pkg/errors"
)
func keyFigure(mid int64) string {
return fmt.Sprintf("f:%d", mid)
}
func keyPendingMids(ver int64, shard int64) string {
return fmt.Sprintf("w:u%d%d", ver, shard)
}
// PingRedis check redis connection
func (d *Dao) PingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("SET", "PING", "PONG")
return
}
// FigureCache get FigureUser from cache
func (d *Dao) FigureCache(c context.Context, mid int64) (figure *model.Figure, err error) {
key := keyFigure(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
}
figure = &model.Figure{}
if err = json.Unmarshal(item, &figure); err != nil {
log.Error("json.Unmarshal(%v) err(%v)", item, err)
}
return
}
// SetFigureCache set FigureUser to cache
func (d *Dao) SetFigureCache(c context.Context, figure *model.Figure) (err error) {
key := keyFigure(figure.Mid)
conn := d.redis.Get(c)
defer conn.Close()
values, err := json.Marshal(figure)
if err != nil {
return
}
if err = conn.Send("SET", key, values); err != nil {
log.Error("conn.Send(SET, %s, %v) error(%v)", key, values, err)
return
}
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
log.Error("conn.Send(Expire, %s, %d) error(%v)", key, d.redisExpire, err)
return
}
return
}
// PendingMidsCache get PendingUser set from cache
func (d *Dao) PendingMidsCache(c context.Context, version int64, shard int64) (mids []int64, err error) {
var (
conn = d.redis.Get(c)
key = keyPendingMids(version, shard)
)
defer conn.Close()
if mids, err = redis.Int64s(conn.Do("SMEMBERS", key)); err != nil {
if err == redis.ErrNil {
err = nil
return
}
err = errors.Wrapf(err, "redis.Int64s(conn.Do(SMEMEBERS,%s))", key)
return
}
return
}
// RemoveCache remove figure cache
func (d *Dao) RemoveCache(c context.Context, mid int64) (err error) {
key := keyFigure(mid)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("DEL", key); err != nil {
log.Error("conn.Send(DEL, %s) error(%v)", key, err)
return
}
return
}

View File

@ -0,0 +1,40 @@
CREATE TABLE `figure_user_[00-99]` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户信用分模型',
`mid` int(11) NOT NULL COMMENT '用户mid',
`score` smallint(6) NOT NULL DEFAULT '0' COMMENT '信用分值',
`lawful_score` smallint(6) NOT NULL DEFAULT '0' COMMENT '守序',
`wide_score` smallint(6) NOT NULL DEFAULT '0' COMMENT '博览',
`friendly_score` smallint(6) NOT NULL DEFAULT '0' COMMENT '友爱',
`bounty_score` smallint(6) NOT NULL DEFAULT '0' COMMENT '慷慨',
`creativity_score` smallint(6) NOT NULL DEFAULT '0' COMMENT '创造',
`ver` int(11) NOT NULL DEFAULT '0' COMMENT '版本',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创造世界',
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mid` (`mid`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB AUTO_INCREMENT=697525 DEFAULT CHARSET=utf8 COMMENT='信用用户表';
CREATE TABLE `figure_rank` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`score_from` smallint(6) NOT NULL DEFAULT '0' COMMENT '起始分段分值,包含',
`score_to` smallint(6) NOT NULL DEFAULT '0' COMMENT '结束分段分值,包含',
`percentage` smallint(6) NOT NULL DEFAULT '0' COMMENT '排名百分比',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_percentage` (`percentage`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='当前信用分排名表'
CREATE TABLE `figure_rank_history` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`score_from` smallint(6) NOT NULL DEFAULT '0' COMMENT '起始分段分值,包含',
`score_to` smallint(6) NOT NULL DEFAULT '0' COMMENT '结束分段分值,包含',
`percentage` smallint(6) NOT NULL DEFAULT '0' COMMENT '排名百分比',
`ver` int(11) NOT NULL DEFAULT '0' COMMENT '版本',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='信用分历史排名表';

View File

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/job/main/figure-timer/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/service: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,37 @@
package http
import (
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
svc *service.Service
)
// Init init a http server
func Init(s *service.Service) {
svc = s
e := bm.DefaultServer(conf.Conf.BM)
innerRouter(e)
if err := e.Start(); err != nil {
log.Error("%+v", err)
panic(err)
}
}
// innerRouter init inner router.
func innerRouter(e *bm.Engine) {
e.Ping(ping)
}
// ping check server ok.
func ping(c *bm.Context) {
// if err := svc.Ping(c); err != nil {
// log.Error("figure-timer-job ping err (%+v)", err)
// c.AbortWithStatus(http.StatusServiceUnavailable)
// }
}

View File

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"db.go",
"hbase.go",
],
importpath = "go-common/app/job/main/figure-timer/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,29 @@
package model
import "time"
// Figure user figure model
type Figure struct {
ID int64 `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 int64 `json:"-"`
Ctime time.Time `json:"-"`
Mtime time.Time `json:"-"`
}
// Rank rank fiigure
type Rank struct {
ID int64 `json:"-"`
ScoreFrom int32 `json:"score_from"`
ScoreTo int32 `json:"score_to"`
Percentage int8 `json:"percentage"`
Ver int64 `json:"ver"`
Ctime time.Time `json:"-"`
Mtime time.Time `json:"-"`
}

View File

@ -0,0 +1,49 @@
package model
import (
"time"
)
type FigureRecord struct {
Mid int64
XPosLawful int64
XNegLawful int64
XPosWide int64
XNegWide int64
XPosFriendly int64
XNegFriendly int64
XPosCreativity int64
XNegCreativity int64
XPosBounty int64
XNegBounty int64
Version time.Time
}
type UserInfo struct {
Mid int64
Exp uint64 // 当周期最终经验值
SpyScore uint64 // spy得分
ArchiveViews uint64 // 观看视频累计天使
VIPStatus uint64 // VIP状态
DisciplineCommittee uint16 // 风纪委得分
}
type ActionCounter struct {
Mid int64
CoinCount uint64 // 投币行为数
ReplyCount int64 // 评论次数
DanmakuCount int64 // 弹幕计次
CoinLowRisk uint64 // 投币疑似异常
CoinHighRisk uint64 // 投币高危异常
ReplyLowRisk uint64 // 评论疑似异常
ReplyHighRisk uint64 // 评论高危异常
ReplyLiked int64 // 评论被赞数
ReplyUnliked int64 // 评论被踩数
ReportReplyPassed int64 // 举报评论通过
ReportDanmakuPassed int64 // 举报弹幕通过
PublishReplyDeleted int64 // 评论被删除
PublishDanmakuDeleted int64 // 弹幕被删除
PayMoney int64 // 支付消费金额(单位分)
PayLiveMoney int64 // 支付消费金额(单位分)
Version time.Time
}

View File

@ -0,0 +1,57 @@
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/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/dao/mock_dao:go_default_library",
"//app/job/main/figure-timer/model:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/golang/mock/gomock:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"calc.go",
"fix.go",
"rank.go",
"service.go",
],
importpath = "go-common/app/job/main/figure-timer/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/figure-timer/conf:go_default_library",
"//app/job/main/figure-timer/dao:go_default_library",
"//app/job/main/figure-timer/model:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/robfig/cron: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,351 @@
package service
import (
"context"
"math"
"time"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
)
const (
bizTypePosLawful = iota
bizTypeNegLawful
// bizTypePosWide
// bizTypeNegWide
bizTypePosFriendly
bizTypeNegFriendly
bizTypePosCreativity
// bizTypeNegCreativity
bizTypePosBounty
// bizTypeNegBounty
)
// HandleFigure handle all figure score for a mid
func (s *Service) HandleFigure(c context.Context, mid int64, weekVer int64) (err error) {
var (
figure = &model.Figure{}
userInfo *model.UserInfo
actionCounter *model.ActionCounter
records []*model.FigureRecord
newRecord *model.FigureRecord
weekVerRecordsFrom = time.Unix(weekVer, 0).AddDate(0, 0, -7*52).Unix()
weekVerRecordsTo = time.Unix(weekVer, 0).AddDate(0, 0, -7).Unix() + 1
)
//1. get user_info from hbase
if userInfo, err = s.dao.UserInfo(c, mid, weekVer); err != nil {
return
}
//2. get action_counter from hbase
if actionCounter, err = s.dao.ActionCounter(c, mid, weekVer); err != nil {
return
}
//3. get figure_records from hbase
if records, err = s.dao.CalcRecords(c, mid, weekVerRecordsFrom, weekVerRecordsTo); err != nil {
return
}
//4. calc figure
figure, newRecord = s.CalcFigure(c, userInfo, []*model.ActionCounter{actionCounter}, records, weekVer)
log.Info("User figure [%+v]", figure)
log.Info("User newRecord [%+v]", newRecord)
//5. save to db
time.Sleep(time.Millisecond)
if figure.ID, err = s.dao.UpsertFigure(c, figure); err != nil {
log.Error("%+v", err)
}
//6. save new record
if err = s.dao.PutCalcRecord(c, newRecord, weekVer); err != nil {
log.Error("%+v", err)
}
//7. remove existed redis
if err = s.dao.RemoveCache(c, mid); err != nil {
log.Error("%+v", err)
}
rank.AddScore(figure.Score)
return
}
// CalcFigure calc figure.
func (s *Service) CalcFigure(c context.Context, userInfo *model.UserInfo, actionCounters []*model.ActionCounter, records []*model.FigureRecord, weekVer int64) (figure *model.Figure, newRecord *model.FigureRecord) {
figure = &model.Figure{Mid: userInfo.Mid, Ver: weekVer}
newRecord = &model.FigureRecord{Mid: userInfo.Mid, Version: time.Unix(weekVer, 0)}
var (
posx float64
negx float64
newPosx, newNegx float64
k1, k2, k3, k4, k5 float64 = s.c.Property.Calc.K1, s.c.Property.Calc.K2, s.c.Property.Calc.K3, s.c.Property.Calc.K4, s.c.Property.Calc.K5
posOffset, negOffset int64
lawfulBase = s.c.Property.Calc.InitLawfulScore
lawfulPosMax = s.c.Property.Calc.LawfulPosMax
lawfulNegMax = s.c.Property.Calc.LawfulNegMax
lawfulPosK = s.c.Property.Calc.LawfulPosK
lawfulNegK1 = s.c.Property.Calc.LawfulNegK1
lawfulNegK2 = s.c.Property.Calc.LawfulNegK2
lawfulPosL = s.c.Property.Calc.LawfulPosL
lawfulNegL = s.c.Property.Calc.LawfulNegL
lawfulPosC3 = s.c.Property.Calc.LawfulPosC3
lawfulNegC1 = s.c.Property.Calc.LawfulNegC1
lawfulPosQ3 = s.c.Property.Calc.LawfulPosQ3
lawfulNegQ1 = s.c.Property.Calc.LawfulNegQ1
wideBase = s.c.Property.Calc.InitWideScore
widePosMax = s.c.Property.Calc.WidePosMax
widePosK = s.c.Property.Calc.WidePosK
wideC1 = s.c.Property.Calc.WideC1 //有播放的活跃天数
wideQ1 = s.c.Property.Calc.WideQ1
wideC2 = s.c.Property.Calc.WideC2 // 账号累计经验值
wideQ2 = s.c.Property.Calc.WideQ2
friendlyBase = s.c.Property.Calc.InitFriendlyScore
friendlyPosMax = s.c.Property.Calc.FriendlyPosMax
friendlyNegMax = s.c.Property.Calc.FriendlyNegMax
friendlyPosK = s.c.Property.Calc.FriendlyPosK
friendlyNegK = s.c.Property.Calc.FriendlyNegK
friendlyPosL = s.c.Property.Calc.FriendlyPosL
friendlyNegL = s.c.Property.Calc.FriendlyNegL
bountyBase = s.c.Property.Calc.InitBountyScore
bountyMax = s.c.Property.Calc.BountyMax
bountyPosL = s.c.Property.Calc.BountyPosL
bountyK = s.c.Property.Calc.BountyK
bountyQ1 = s.c.Property.Calc.BountyQ1
bountyC1 = s.c.Property.Calc.BountyC1
creativityBase = s.c.Property.Calc.InitCreativityScore
creativityPosMax = s.c.Property.Calc.CreativityPosMax
creativityPosK = s.c.Property.Calc.CreativityPosK
creativityPosL1 = s.c.Property.Calc.CreativityPosL1
)
//1. lawful
newPosx, posx = s.calcActionX(lawfulPosL, bizTypePosLawful, actionCounters, records, weekVer)
posx += lawfulPosQ3 * lawfulPosC3 * float64(userInfo.DisciplineCommittee)
posOffset = calcOffset(lawfulPosMax, lawfulPosK, posx)
spyScore := userInfo.SpyScore
negx = lawfulNegQ1 * lawfulNegC1 * float64(80-float64(spyScore))
if negx < 0 {
negx = 0.0
}
negOffset = calcOffset(lawfulNegMax, lawfulNegK1, negx)
newNegx, negx = s.calcActionX(lawfulNegL, bizTypeNegLawful, actionCounters, records, weekVer)
negOffset += calcOffset(lawfulNegMax, lawfulNegK2, negx)
figure.LawfulScore = int32(lawfulBase + posOffset - negOffset)
if figure.LawfulScore < 0 {
figure.LawfulScore = 0
}
newRecord.XPosLawful, newRecord.XNegLawful = int64(newPosx), int64(newNegx)
//2. wide
newPosx, newNegx = 0, 0
posx = wideQ1*wideC1*float64(userInfo.ArchiveViews) + wideQ2*wideC2*float64(userInfo.Exp)
posOffset = calcOffset(widePosMax, widePosK, posx)
negx = 0
negOffset = 0
figure.WideScore = int32(wideBase + posOffset - negOffset)
if figure.WideScore < 0 {
figure.WideScore = 0
}
newRecord.XPosWide, newRecord.XNegWide = int64(newPosx), int64(newNegx)
//3. friendly
newPosx, newNegx = 0, 0
newPosx, posx = s.calcActionX(friendlyPosL, bizTypePosFriendly, actionCounters, records, weekVer)
posOffset = calcOffset(friendlyPosMax, friendlyPosK, posx)
newNegx, negx = s.calcActionX(friendlyNegL, bizTypeNegFriendly, actionCounters, records, weekVer)
negOffset = calcOffset(friendlyNegMax, friendlyNegK, negx)
figure.FriendlyScore = int32(friendlyBase + posOffset - negOffset)
if figure.FriendlyScore < 0 {
figure.FriendlyScore = 0
}
newRecord.XPosFriendly, newRecord.XNegFriendly = int64(newPosx), int64(newNegx)
//4. bounty
newPosx, newNegx = 0, 0
var bountyVIP float64
if userInfo.VIPStatus > 0 {
bountyVIP = 1
}
newPosx, posx = s.calcActionX(bountyPosL, bizTypePosBounty, actionCounters, records, weekVer)
posx += bountyQ1 * bountyC1 * bountyVIP
posOffset = calcOffset(bountyMax, bountyK, posx)
negx = 0
negOffset = 0
figure.BountyScore = int32(bountyBase + posOffset - negOffset)
if figure.BountyScore < 0 {
figure.BountyScore = 0
}
newRecord.XPosBounty, newRecord.XNegBounty = int64(newPosx), int64(newNegx)
//5. creativity
newPosx, newNegx = 0, 0
newPosx, posx = s.calcActionX(creativityPosL1, bizTypePosCreativity, actionCounters, records, weekVer)
posOffset = calcOffset(creativityPosMax, creativityPosK, posx)
negx = 0
negOffset = 0
figure.CreativityScore = int32(creativityBase + posOffset - negOffset)
if figure.CreativityScore < 0 {
figure.CreativityScore = 0
}
newRecord.XPosCreativity, newRecord.XNegCreativity = int64(newPosx), int64(newNegx)
//6. calc score
figure.Score = int32(math.Floor(k1*float64(figure.LawfulScore) + k2*float64(figure.WideScore) + k3*float64(figure.FriendlyScore) + k4*float64(figure.CreativityScore) + k5*float64(figure.BountyScore)))
return
}
// x must >= 0
func calcOffset(max, k, x float64) (score int64) {
return int64(math.Floor(max * (1 - math.Pow(math.E, -(k*x)))))
}
func (s *Service) calcActionX(L float64, bizType int8, actionCounters []*model.ActionCounter, records []*model.FigureRecord, weekVer int64) (newx, totalx float64) {
var (
day int64 = 24 * 3600
t float64
lawfulPosC1 = s.c.Property.Calc.LawfulPosC1
lawfulPosC2 = s.c.Property.Calc.LawfulPosC2
lawfulPosQ1 = s.c.Property.Calc.LawfulPosQ1
lawfulPosQ2 = s.c.Property.Calc.LawfulPosQ2
lawfulNegC2 = s.c.Property.Calc.LawfulNegC2
lawfulNegC3 = s.c.Property.Calc.LawfulNegC3
lawfulNegQ2 = s.c.Property.Calc.LawfulNegQ2
lawfulNegQ3 = s.c.Property.Calc.LawfulNegQ3
friendlyPosQ1 = s.c.Property.Calc.FriendlyPosQ1
friendlyPosC1 = s.c.Property.Calc.FriendlyPosC1
friendlyPosQ2 = s.c.Property.Calc.FriendlyPosQ2
friendlyPosC2 = s.c.Property.Calc.FriendlyPosC2
friendlyPosQ3 = s.c.Property.Calc.FriendlyPosQ3
friendlyPosC3 = s.c.Property.Calc.FriendlyPosC3
friendlyNegQ1 = s.c.Property.Calc.FriendlyNegQ1
friendlyNegC1 = s.c.Property.Calc.FriendlyNegC1
friendlyNegQ2 = s.c.Property.Calc.FriendlyNegQ2
friendlyNegC2 = s.c.Property.Calc.FriendlyNegC2
friendlyNegQ3 = s.c.Property.Calc.FriendlyNegQ3
friendlyNegC3 = s.c.Property.Calc.FriendlyNegC3
friendlyNegQ4 = s.c.Property.Calc.FriendlyNegQ4
friendlyNegC4 = s.c.Property.Calc.FriendlyNegC4
creativityQ1 = s.c.Property.Calc.CreativityQ1
creativityC1 = s.c.Property.Calc.CreativityC1
bountyQ2 = s.c.Property.Calc.BountyQ2
bountyC2 = s.c.Property.Calc.BountyC2
bountyQ3 = s.c.Property.Calc.BountyQ3
bountyC3 = s.c.Property.Calc.BountyC3
)
if L == 0.0 {
return 0, 0
}
for _, ac := range actionCounters {
t = float64(7 - (ac.Version.Unix()-weekVer)/day)
if t <= 0 {
continue
}
switch bizType {
case bizTypePosLawful:
if ac.ReportReplyPassed < 0 {
ac.ReportReplyPassed = 0
}
newx += actionX(lawfulPosQ1, lawfulPosC1, float64(ac.ReportReplyPassed), t, L)
if ac.ReportDanmakuPassed < 0 {
ac.ReportDanmakuPassed = 0
}
newx += actionX(lawfulPosQ2, lawfulPosC2, float64(ac.ReportDanmakuPassed), t, L)
case bizTypeNegLawful:
if ac.PublishReplyDeleted < 0 {
ac.PublishReplyDeleted = 0
}
newx += actionX(lawfulNegQ2, lawfulNegC2, float64(ac.PublishReplyDeleted), t, L)
if ac.PublishDanmakuDeleted < 0 {
ac.PublishDanmakuDeleted = 0
}
newx += actionX(lawfulNegQ3, lawfulNegC3, float64(ac.PublishDanmakuDeleted), t, L)
case bizTypePosFriendly:
newx += actionX(friendlyPosQ1, friendlyPosC1, float64(ac.CoinCount), t, L)
newx += actionX(friendlyPosQ2, friendlyPosC2, float64(ac.ReplyCount), t, L)
newx += actionX(friendlyPosQ3, friendlyPosC3, float64(ac.DanmakuCount), t, L)
case bizTypeNegFriendly:
newx += actionX(friendlyNegQ1, friendlyNegC1, float64(ac.CoinHighRisk), t, L)
newx += actionX(friendlyNegQ2, friendlyNegC2, float64(ac.CoinLowRisk), t, L)
if ac.PublishReplyDeleted < 0 {
ac.PublishReplyDeleted = 0
}
newx += actionX(friendlyNegQ3, friendlyNegC3, float64(ac.PublishReplyDeleted), t, L)
if ac.PublishDanmakuDeleted < 0 {
ac.PublishDanmakuDeleted = 0
}
newx += actionX(friendlyNegQ4, friendlyNegC4, float64(ac.PublishDanmakuDeleted), t, L)
case bizTypePosCreativity:
replyLikeCount := float64(ac.ReplyLiked) - float64(ac.ReplyUnliked)
if replyLikeCount < 0 {
replyLikeCount = 0.0
}
newx += actionX(creativityQ1, creativityC1, replyLikeCount, t, L)
case bizTypePosBounty:
newx += actionX(bountyQ2, bountyC2, float64(ac.PayMoney), t, L)
newx += actionX(bountyQ3, bountyC3, float64(ac.PayLiveMoney), t, L)
}
}
totalx = newx
for _, r := range records {
t = float64((weekVer - r.Version.Unix()) / day)
if t <= 0 {
continue
}
switch bizType {
case bizTypePosLawful:
totalx += actionX(1, 1, float64(r.XPosLawful), t, L)
case bizTypeNegLawful:
totalx += actionX(1, 1, float64(r.XNegLawful), t, L)
case bizTypePosFriendly:
totalx += actionX(1, 1, float64(r.XPosFriendly), t, L)
case bizTypeNegFriendly:
totalx += actionX(1, 1, float64(r.XNegFriendly), t, L)
case bizTypePosCreativity:
totalx += actionX(1, 1, float64(r.XPosCreativity), t, L)
case bizTypePosBounty:
totalx += actionX(1, 1, float64(r.XPosBounty), t, L)
}
}
return
}
func actionX(q, c, x, t, L float64) float64 {
return q * c * x * math.Pow(math.E, -math.Pow((t/L), 2))
}
// InitFigure initialize user figure
func (s *Service) InitFigure(c context.Context, mid int64, ver string) (figure *model.Figure, err error) {
figure = &model.Figure{}
figure.Mid = mid
figure.LawfulScore = int32(conf.Conf.Property.Calc.InitLawfulScore)
figure.WideScore = int32(conf.Conf.Property.Calc.InitWideScore)
figure.FriendlyScore = int32(conf.Conf.Property.Calc.InitFriendlyScore)
figure.BountyScore = int32(conf.Conf.Property.Calc.InitBountyScore)
figure.CreativityScore = int32(conf.Conf.Property.Calc.InitCreativityScore)
figure.Ver = s.curVer
if figure.ID, err = s.dao.UpsertFigure(c, figure); err != nil {
return
}
if err = s.dao.SetFigureCache(c, figure); err != nil {
log.Error("%+v", err)
}
return
}
// get ever monday start time ts.
func weekVersion(now time.Time) (ts int64) {
var (
wd int
w time.Weekday
)
w = now.Weekday()
switch w {
case time.Sunday:
wd = 6
default:
wd = int(w) - 1
}
return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -wd).Unix()
}

View File

@ -0,0 +1,99 @@
package service
import (
"context"
"runtime/debug"
"time"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
)
func (s *Service) fixproc() {
defer func() {
if x := recover(); x != nil {
log.Error("fixproc panic(%+v)", x)
log.Error("%+v", string(debug.Stack()))
}
}()
var (
ctx = context.TODO()
err error
weekVerRecordsFrom = time.Now().AddDate(0, 0, -7*52).Unix()
weekVerRecordsTo = time.Now().Unix() + 1
)
for shard := s.c.Property.PendingMidStart; shard < 100; shard++ {
log.Info("start fix: %d", shard)
var (
figures []*model.Figure
fromMid = int64(shard)
end bool
)
for !end {
if figures, end, err = s.dao.Figures(ctx, fromMid, 100); err != nil {
log.Error("%+v", err)
break
}
for _, figure := range figures {
var (
records []*model.FigureRecord
)
if fromMid < figure.Mid {
fromMid = figure.Mid
}
if records, err = s.dao.CalcRecords(ctx, figure.Mid, weekVerRecordsFrom, weekVerRecordsTo); err != nil {
log.Error("%+v", err)
continue
}
s.fixRecord(ctx, figure.Mid, records)
}
}
log.Info("end fix: %d", shard)
}
}
// 全量清洗用户mid所有的records
func (s *Service) fixRecord(c context.Context, mid int64, records []*model.FigureRecord) {
var (
err error
action *model.ActionCounter
x float64
)
for _, record := range records {
time.Sleep(time.Millisecond)
beforeRecord := *record
// 获得本次record 记录对应的 action 记录
if action, err = s.dao.ActionCounter(c, mid, record.Version.Unix()); err != nil {
log.Error("%+v", err)
continue
}
actions := []*model.ActionCounter{action}
// lawful
x, _ = s.calcActionX(s.c.Property.Calc.LawfulPosL, bizTypePosLawful, actions, nil, record.Version.Unix())
record.XPosLawful = int64(x)
x, _ = s.calcActionX(s.c.Property.Calc.LawfulNegL, bizTypeNegLawful, actions, nil, record.Version.Unix())
record.XNegLawful = int64(x)
// wide
record.XPosWide = 0
record.XNegWide = 0
// friendly
x, _ = s.calcActionX(s.c.Property.Calc.FriendlyPosL, bizTypePosFriendly, actions, nil, record.Version.Unix())
record.XPosFriendly = int64(x)
x, _ = s.calcActionX(s.c.Property.Calc.FriendlyNegL, bizTypeNegFriendly, actions, nil, record.Version.Unix())
record.XNegFriendly = int64(x)
// bounty
x, _ = s.calcActionX(s.c.Property.Calc.BountyPosL, bizTypePosBounty, actions, nil, record.Version.Unix())
record.XPosBounty = int64(x)
record.XNegBounty = 0
// creativity
x, _ = s.calcActionX(s.c.Property.Calc.CreativityPosL1, bizTypePosCreativity, actions, nil, record.Version.Unix())
record.XPosCreativity = int64(x)
record.XNegCreativity = 0
// 更新本次的fix
if err = s.dao.PutCalcRecord(c, record, record.Version.Unix()); err != nil {
log.Error("%+v", err)
} else {
log.Info("fix figure record before [%+v] --> now [%+v]", beforeRecord, record)
}
}
}

View File

@ -0,0 +1,83 @@
package service
import (
"context"
"sort"
"sync"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
)
var (
rank = &scores{}
)
type scores struct {
ss []int32
mu sync.Mutex
}
func (s *scores) AddScore(score int32) {
s.mu.Lock()
defer s.mu.Unlock()
s.ss = append(s.ss, score)
}
func (s *scores) Init() {
s.ss = make([]int32, 0)
}
func (s *scores) Sort() {
sort.Sort(s)
}
func (s *scores) Get(i int) int32 {
return s.ss[i]
}
func (s *scores) Len() int {
return len(s.ss)
}
func (s *scores) Less(i1, i2 int) bool {
return s.ss[i1] > s.ss[i2]
}
func (s *scores) Swap(i1, i2 int) {
s.ss[i1], s.ss[i2] = s.ss[i2], s.ss[i1]
}
func (s *Service) calcRank(c context.Context, ver int64) {
if rank.Len() <= 0 {
return
}
rank.Sort()
var (
scoreFrom, scoreTo int32
err error
)
scoreTo = rank.Get(0)
for i := int8(1); i <= 100; i++ {
var index int
if i == 100 {
scoreFrom = 0
} else {
index = int(float64(rank.Len()) * (float64(i) / 100.0))
scoreFrom = rank.Get(index)
}
if scoreFrom > scoreTo {
scoreTo = scoreFrom
}
r := &model.Rank{ScoreFrom: scoreFrom, ScoreTo: scoreTo, Percentage: i, Ver: ver}
if _, err = s.dao.InsertRankHistory(c, r); err != nil {
log.Error("%+v", err)
return
}
if _, err = s.dao.UpsertRank(c, r); err != nil {
log.Error("%+v", err)
return
}
scoreTo = scoreFrom - 1
}
}

View File

@ -0,0 +1,216 @@
package service
import (
"context"
"sync"
"sync/atomic"
"time"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/dao"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
"github.com/robfig/cron"
)
// Service struct of service.
type Service struct {
c *conf.Config
dao dao.Int
missch chan func()
curVer int64
cron *cron.Cron
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
missch: make(chan func(), 1024),
}
s.cron = cron.New()
s.cron.AddFunc(s.c.Property.CycleCron, s.cycleproc)
if c.Property.CycleAll {
s.cron.AddFunc(s.c.Property.CycleAllCron, s.cycleallproc)
}
go s.missproc()
if c.Property.FixRecord {
go s.fixproc()
}
s.cron.Start()
return
}
func (s *Service) missproc() {
defer func() {
if x := recover(); x != nil {
log.Error("s.missproc panic(%v)", x)
go s.missproc()
log.Info("s.missproc recover")
}
}()
for {
for fn := range s.missch {
fn()
}
}
}
func (s *Service) cycleproc() {
defer func() {
if x := recover(); x != nil {
log.Error("s.cycleproc panic(%v)", x)
go s.cycleproc()
log.Info("s.cycleproc recover")
}
}()
var (
err error
mids []int64
c = context.TODO()
wg sync.WaitGroup
newVer = weekVersion(time.Now().AddDate(0, 0, int(-s.c.Property.CalcWeekOffset*7)))
)
// Refresh Version
atomic.StoreInt64(&s.curVer, newVer)
log.Info("Calc active users start ver [%d]", s.curVer)
rank.Init()
// Calc figure concurrently
for i := s.c.Property.PendingMidStart; i < s.c.Property.PendingMidShard; i++ {
if mids, err = s.PendingMids(c, s.curVer, i, s.c.Property.PendingMidRetry); err != nil {
log.Error("%+v", err)
}
if len(mids) == 0 {
continue
}
smids := splitMids(mids, s.c.Property.ConcurrencySize)
for c := range smids {
csmids := smids[c]
wg.Add(1)
go func() {
defer func() {
wg.Done()
}()
for _, mid := range csmids {
log.Info("Start handle mid [%d] figure ver [%d]", mid, s.curVer)
if err = s.HandleFigure(context.TODO(), mid, s.curVer); err != nil {
log.Error("%+v", err)
}
}
}()
}
wg.Wait()
}
log.Info("Calc rank info start [%d]", s.curVer)
s.calcRank(c, s.curVer)
log.Info("Calc rank info finished [%d]", s.curVer)
log.Info("Calc active users finished ver [%d]", s.curVer)
}
func splitMids(mids []int64, concurrencySize int64) (smids [][]int64) {
if len(mids) == 0 {
return
}
if concurrencySize == 0 {
concurrencySize = 1
}
step := int64(len(mids))/concurrencySize + 1
for c := int64(0); c < concurrencySize; c++ {
var cMids []int64
indexFrom := c * step
indexTo := (c + 1) * step
if indexFrom >= int64(len(mids)) {
break
}
if indexTo >= int64(len(mids)) {
cMids = mids[indexFrom:]
} else {
cMids = mids[indexFrom:indexTo]
}
smids = append(smids, cMids)
}
return
}
// PendingMids get pending mid list with retry
func (s *Service) PendingMids(c context.Context, version int64, shard int64, retry int64) (mids []int64, err error) {
var (
maxDo = retry + 1
doTimes int64
)
for doTimes < maxDo {
if mids, err = s.dao.PendingMidsCache(c, s.curVer, shard); err != nil {
doTimes++
log.Info("s.dao.PendingMidsCache(%d,%d) retry (%d) error (%+v)", version, shard, doTimes, err)
} else {
doTimes = maxDo
}
}
return
}
func (s *Service) cycleallproc() {
defer func() {
if x := recover(); x != nil {
log.Error("cycleallproc panic(%+v)", x)
}
}()
var (
ctx = context.TODO()
err error
newVer = weekVersion(time.Now().AddDate(0, 0, int(-s.c.Property.CalcWeekOffset*7)))
)
// Refresh Version
atomic.StoreInt64(&s.curVer, newVer)
log.Info("cycleallproc active users start ver [%d]", s.curVer)
rank.Init()
for shard := s.c.Property.PendingMidStart; shard < 100; shard++ {
log.Info("cycleallproc start run: %d", shard)
var (
figures []*model.Figure
fromMid = int64(shard)
end bool
)
for !end {
if figures, end, err = s.dao.Figures(ctx, fromMid, 100); err != nil {
log.Error("%+v", err)
break
}
if len(figures) == 0 {
continue
}
for _, figure := range figures {
if fromMid < figure.Mid {
fromMid = figure.Mid
}
log.Info("Start handle mid [%d] figure ver [%d]", figure.Mid, s.curVer)
if err = s.HandleFigure(ctx, figure.Mid, s.curVer); err != nil {
log.Error("%+v", err)
continue
}
}
}
log.Info("cycleallproc rank info start [%d]", s.curVer)
s.calcRank(ctx, s.curVer)
log.Info("cycleallproc rank info finished [%d]", s.curVer)
log.Info("cycleallproc active users finished ver [%d]", s.curVer)
}
}
// Close kafka consumer close.
func (s *Service) Close() (err error) {
s.dao.Close()
return
}
// Wait wait service end.
func (s *Service) Wait() {
}
// Ping check service health.
func (s *Service) Ping(c context.Context) error {
return s.dao.Ping(c)
}

View File

@ -0,0 +1,177 @@
package service
import (
"context"
"flag"
"math/rand"
"testing"
"time"
"go-common/app/job/main/figure-timer/conf"
"go-common/app/job/main/figure-timer/dao/mock_dao"
"go-common/app/job/main/figure-timer/model"
"go-common/library/log"
"github.com/golang/mock/gomock"
. "github.com/smartystreets/goconvey/convey"
)
var (
s *Service
c = context.TODO()
)
func init() {
var err error
flag.Set("conf", "../cmd/figure-timer-job-test.toml")
if err = conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
s = New(conf.Conf)
}
func TestSplitMids(t *testing.T) {
Convey("TEST split mids", t, func() {
var (
mids []int64
concurrency int64 = 100
midsSize int64 = 233
)
for i := int64(0); i < midsSize; i++ {
mids = append(mids, i)
}
smids := splitMids(mids, concurrency)
actualC := concurrency
if actualC == 0 {
actualC = 1
}
if actualC > midsSize {
actualC = midsSize
}
So(len(smids), ShouldEqual, 233/3+1)
total := 0
for s := range smids {
total += len(smids[s])
}
So(total, ShouldEqual, len(mids))
})
}
func TestVersion(t *testing.T) {
Convey("TEST week version", t, func() {
var (
ts = []time.Time{
time.Date(2017, 9, 25, 0, 0, 0, 0, time.Local),
time.Date(2017, 10, 1, 23, 59, 59, 99, time.Local),
time.Date(2017, 9, 29, 12, 12, 12, 12, time.Local),
}
actual = time.Date(2017, 9, 25, 0, 0, 0, 0, time.Local)
)
for _, t := range ts {
ver := weekVersion(t)
So(ver, ShouldResemble, actual.Unix())
}
})
}
func TestCalcFigure(t *testing.T) {
Convey("TEST calc offset", t, func() {
var (
max = 100.0
k = 0.1
x1 = 10.0
x2 = 0.0
)
r := calcOffset(max, k, x1)
So(r, ShouldEqual, 63)
r = calcOffset(max, k, x2)
So(r, ShouldEqual, 0)
})
Convey("TEST calc figure", t, func() {
var (
userInfo = &model.UserInfo{
Mid: 233,
Exp: 673,
SpyScore: 100,
ArchiveViews: 0,
VIPStatus: 0,
}
actionCounters = []*model.ActionCounter{
{
Mid: 233,
CoinCount: 0,
ReplyCount: 0,
CoinLowRisk: 0,
CoinHighRisk: 0,
ReplyLowRisk: 0,
ReplyHighRisk: 0,
ReplyLiked: 0,
ReplyUnliked: 0,
Version: time.Date(2017, 10, 2, 0, 0, 0, 0, time.Local),
},
}
records = []*model.FigureRecord{
{
XPosCreativity: 0,
XNegFriendly: 0,
XPosFriendly: 0,
XNegLawful: 80,
Version: time.Date(2017, 9, 25, 0, 0, 0, 0, time.Local),
},
}
weekVer = time.Date(2018, 1, 2, 0, 0, 0, 0, time.Local).Unix()
)
figure, newRecord := s.CalcFigure(c, userInfo, actionCounters, records, weekVer)
So(figure, ShouldNotBeNil)
So(newRecord, ShouldNotBeNil)
So(figure.LawfulScore, ShouldEqual, 2859)
So(newRecord.XNegLawful, ShouldEqual, 0)
So(newRecord.XPosLawful, ShouldEqual, 0)
})
}
func TestRank(t *testing.T) {
rand.Seed(time.Now().Unix())
rank.Init()
for i := 0; i < 1; i++ {
rank.AddScore(rand.Int31n(5000))
}
s.calcRank(c, time.Now().Unix())
}
func TestFix(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDao := mock_dao.NewMockDaoInt(ctrl)
mockDao.EXPECT().Figures(gomock.Any(), gomock.Any(), gomock.Any()).Return(
[]*model.Figure{
{Mid: 1000},
{Mid: 2000},
},
true,
nil,
).AnyTimes()
mockDao.EXPECT().CalcRecords(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
[]*model.FigureRecord{
{Version: time.Date(2018, 1, 2, 0, 0, 0, 0, time.Local)},
},
nil,
).AnyTimes()
mockDao.EXPECT().ActionCounter(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&model.ActionCounter{
PayLiveMoney: 2000,
Version: time.Date(2018, 1, 2, 0, 0, 0, 0, time.Local),
},
nil,
).AnyTimes()
mockDao.EXPECT().PutCalcRecord(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
s.dao = mockDao
Convey("TEST fix record", t, func() {
s.fixproc()
})
}