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