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

View File

@@ -0,0 +1,34 @@
### web-goblin-job
#### Version 1.3.1
##### Features
1. waitgroup
#### Version 1.3.0
##### Features
> 1.esports 综合排序更新score
#### Version 1.2.3
##### Features
> 1.添加全网搜素对外接口
#### Version 1.2.2
##### Features
> 1.esports 订阅接Push
#### Version 1.2.1
##### Features
> 1.系统消息跳转连接加https
#### Version 1.2.0
##### Features
> 1.esports 订阅发送系统消息
#### Version 1.0.1
##### Features
> 1.use bm
#### Version 1.0.0
##### Features
> 1. 项目初始化
> 2. 通知broadcast更新首页分区动态数

View File

@@ -0,0 +1,14 @@
# Owner
liweijia
renwei
# Author
guanyanliang
wuhao02
liuxuan
liweijia
# Reviewer
guanyanliang
liweijia
zhapuyu

View File

@@ -0,0 +1,20 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- guanyanliang
- liuxuan
- liweijia
- renwei
- wuhao02
labels:
- job
- job/main/web-goblin
- main
options:
no_parent_owners: true
reviewers:
- guanyanliang
- liuxuan
- liweijia
- wuhao02
- zhapuyu

View File

@@ -0,0 +1,15 @@
# web-goblin-job
# 项目简介
> 1.提供web端job
# 编译环境
> 请使用golang v1.7.x以上版本编译执行。
# 依赖包
> 1.公共包go-common
# 编译执行
> 在主目录执行go build。
> 编译后可执行 ./cmd/cmd -conf web-goblin-job-test.toml 使用项目本地配置文件启动服务。
> 也可执行 ./web-goblin-job -conf_appid=web-goblin-job -conf_version=v2.1.0 -conf_host=172.16.33.134:9011 -conf_path=/data/conf/web-goblin-job -conf_env=10 -conf_token=*** 使用配置中心测试环境配置启动服务如无法启动可检查token是否正确。

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 = ["web-goblin-job-test.toml"],
importpath = "go-common/app/job/main/web-goblin/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,6 @@
#!/bin/bash
command -v goconvey >/dev/null 2>&1 || { echo >&2 "required goconvey but it's not installed."; echo "Aborting."; echo "Please run commond: go get github.com/smartystreets/goconvey"; exit 1; }
cd ../
goconvey -excludedDirs "vendor" -packages 1

View File

@@ -0,0 +1,42 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/job/main/web-goblin/conf"
"go-common/app/job/main/web-goblin/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("web-goblin start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
http.Init(conf.Conf)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("web-goblin get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("web-goblin exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,94 @@
[log]
#stdout = true
dir = "/data/log/web-goblin-job/"
[rule]
broadFeed = 5000
before = "2h"
sleepInterval = "5m"
alertTitle = "你订阅的比赛即将开播了"
alertBodyDefault = "%s中,你订阅的赛程【%s %s VS %s】即将开播快前去观看比赛吧! %s"
alertBodySpecial = "%s中,你订阅的赛程【%s %s】即将开播快前去观看比赛吧! %s"
scoreSleep = "1h"
coinPercent = 0.4
favPercent = 0.3
dmPercent = 0.4
replyPercent = 0.4
viewPercent = 0.25
likePercent = 0.4
sharePercent = 0.6
newDay = 14.0
newPercent = 1.5
[httpClient]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[messageHTTPClient]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "50ms"
timeout = "1s"
keepAlive = "60s"
[host]
api = "http://api.bilibili.co"
[favoriteRPC]
timeout = "1s"
[app]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
[db]
[db.esports]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_esports?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
idleTimeout = "4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[db.esports.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
[push]
businessID = 1
businessToken = "fi6xf5xmldudexsh5tgukerohn3mgrl3"
retryTimes = 3
partSize = 100000
title = "你订阅的比赛开播了~"
bodyDefault = "%s %s VS %s 已开播,快来观看比赛吧! %s"
bodySpecial = "%s %s 已开播,快来观看比赛吧!%s"
onlyMids = "27905253,94012604"
[message]
URL = "http://message.bilibili.co/api/notify/send.user.notify.do"
MC = "1_4_2"
[archiveNotifySub]
key = "9765cdac5894f2ba"
secret = "68bc8df51f0935d5f4d24721fbc15a80"
group = "ArchiveNotify-MainWebSvr-Search-S"
topic = "ArchiveNotify-T"
action = "sub"
name = "web-goblin-job/archiveNotifysub"
proto = "tcp"
addr = "172.16.33.158:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/job/main/web-goblin/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus: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,159 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/queue/databus"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Tracer *trace.Config
Redis *redis.Config
Memcache *memcache.Config
DB *DB
Ecode *ecode.Config
// rpc
DynamicRPC *rpc.ClientConfig
FavoriteRPC *rpc.ClientConfig
ArchiveRPC *rpc.ClientConfig
// Host
Host Host
// HTTP client
HTTPClient *bm.ClientConfig
MessageHTTPClient *bm.ClientConfig
// Rule
Rule *Rule
//Push push urls
Push *Push
//Message
Message Message
// App
App *bm.App
// databus
ArchiveNotifySub *databus.Config
// Warden Client
ArcClient *warden.ClientConfig
}
// Push push.
type Push struct {
BusinessID int
BusinessToken string
PartSize int
RetryTimes int
Title string
BodyDefault string
BodySpecial string
OnlyMids string
}
// Message .
type Message struct {
URL string
MC string
}
// Rule .
type Rule struct {
BroadFeed int
SleepInterval time.Duration
Before time.Duration
ScoreSleep time.Duration
AlertTitle string
AlertBodyDefault string
AlertBodySpecial string
CoinPercent float64
FavPercent float64
DmPercent float64
ReplyPercent float64
ViewPercent float64
LikePercent float64
SharePercent float64
NewDay float64
NewPercent float64
}
// Host remote host
type Host struct {
API string
}
// DB define MySQL config
type DB struct {
Esports *sql.Config
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() 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,64 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"message.go",
"mysql.go",
"push.go",
],
importpath = "go-common/app/job/main/web-goblin/dao/esports",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/model/esports:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"message_test.go",
"mysql_test.go",
"push_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/model/esports:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,47 @@
package esports
import (
"context"
"go-common/app/job/main/web-goblin/conf"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
)
const _pushURL = "/x/internal/push-strategy/task/add"
// Dao dao
type Dao struct {
c *conf.Config
// http client
http *bm.Client
messageHTTPClient *bm.Client
// push service URL
pushURL string
// db
db *sql.DB
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
http: bm.NewClient(c.HTTPClient),
messageHTTPClient: bm.NewClient(c.MessageHTTPClient),
db: sql.NewMySQL(c.DB.Esports),
pushURL: c.Host.API + _pushURL,
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping ping dao
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
return
}
return
}

View File

@@ -0,0 +1,36 @@
package esports
import (
"flag"
"os"
"testing"
"go-common/app/job/main/web-goblin/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.web-goblin-job")
flag.Set("conf_token", "12e333e44103d9cba0d6c967f37dd311")
flag.Set("tree_id", "42274")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/web-goblin-job-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}

View File

@@ -0,0 +1,70 @@
package esports
import (
"context"
"net/url"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
var _notify = "4"
// SendMessage send system notify.
func (d *Dao) SendMessage(mids []int64, msg string, contest *mdlesp.Contest) (err error) {
params := url.Values{}
params.Set("mid_list", xstr.JoinInts(mids))
params.Set("title", d.c.Rule.AlertTitle)
params.Set("mc", d.c.Message.MC)
params.Set("data_type", _notify)
params.Set("context", msg)
var res struct {
Code int `json:"code"`
}
err = d.messageHTTPClient.Post(context.Background(), d.c.Message.URL, "", params, &res)
if err != nil {
log.Error("SendMessage d.messageHTTPClient.Post(%s) error(%+v)", d.c.Message.URL+"?"+params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("SendMessage url(%s) res code(%d)", d.c.Message.URL+"?"+params.Encode(), res.Code)
err = ecode.Int(res.Code)
}
return
}
//Batch 批量处理
func (d *Dao) Batch(list []int64, msg string, contest *mdlesp.Contest, batchSize int, f func(users []int64, msg string, contest *mdlesp.Contest) error) {
if msg == "" {
log.Warn("Batch msg is empty")
return
}
retry := d.c.Push.RetryTimes
for {
var (
mids []int64
err error
)
l := len(list)
if l == 0 {
break
} else if l <= batchSize {
mids = list[:l]
} else {
mids = list[:batchSize]
l = batchSize
}
list = list[l:]
for i := 0; i < retry; i++ {
if err = f(mids, msg, contest); err == nil {
break
}
}
if err != nil {
log.Error("Batch error(%v), params(%s)", err, msg)
}
}
}

View File

@@ -0,0 +1,40 @@
package esports
import (
"testing"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
"github.com/smartystreets/goconvey/convey"
)
func TestEsportsSendMessage(t *testing.T) {
var (
mids = []int64{}
msg = "2018 LPL 春季赛 中你订阅的赛程“2018-01-25 19:00 RNG VS SNG”即将开播快前去观看比赛吧 点击前往直播间"
contest = &mdlesp.Contest{}
)
mids = append(mids, 111)
mids = append(mids, 222)
convey.Convey("SendMessage", t, func(ctx convey.C) {
err := d.SendMessage(mids, msg, contest)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestEsportsBatch(t *testing.T) {
var (
list = []int64{}
msg = ""
contest = &mdlesp.Contest{}
batchSize = int(0)
f func(users []int64, msg string, contest *mdlesp.Contest) error
)
convey.Convey("Batch", t, func(ctx convey.C) {
d.Batch(list, msg, contest, batchSize, f)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}

View File

@@ -0,0 +1,135 @@
package esports
import (
"context"
"fmt"
"strconv"
"time"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
arcmdl "go-common/app/service/main/archive/api"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
"github.com/pkg/errors"
)
const (
_contestsSQL = "SELECT c.id,c.stime,c.live_room,c.home_id,c.away_id,c.success_team,c.special,c.special_name,c.special_tips,s.title,s.sub_title FROM `es_contests` as c INNER JOIN `es_seasons` as s ON c.sid=s.id WHERE c.status = 0 AND c.stime >= ? and c.stime < ? "
_teamSQL = "SELECT id,title,sub_title FROM `es_teams` WHERE is_deleted = 0 AND (id = ? or id = ?)"
_arcSQL = "SELECT id,aid,score,is_deleted FROM `es_archives` WHERE is_deleted = 0 AND id > ? ORDER BY id ASC LIMIT ? "
_arcEditSQL = "UPDATE es_archives SET score = CASE %s END WHERE aid IN (%s)"
)
// Contests contests by time.
func (d *Dao) Contests(c context.Context, stime, etime int64) (res []*mdlesp.Contest, err error) {
var (
rows *xsql.Rows
)
if rows, err = d.db.Query(c, _contestsSQL, stime, etime); err != nil {
log.Error("Contests:d.db.Query(%d) error(%v)", stime, err)
return
}
defer rows.Close()
for rows.Next() {
r := new(mdlesp.Contest)
if err = rows.Scan(&r.ID, &r.Stime, &r.LiveRoom, &r.HomeID, &r.AwayID, &r.SuccessTeam, &r.Special, &r.SpecialName, &r.SpecialTips, &r.SeasonTitle, &r.SeasonSubTitle); err != nil {
log.Error("Contests:row.Scan() error(%v)", err)
return
}
res = append(res, r)
}
if err = rows.Err(); err != nil {
log.Error("rows.Err() error(%v)", err)
}
return
}
// Teams teams by id.
func (d *Dao) Teams(c context.Context, homeID, awayID int64) (res []*mdlesp.Team, err error) {
var (
rows *xsql.Rows
)
if rows, err = d.db.Query(c, _teamSQL, homeID, awayID); err != nil {
log.Error("Teams:d.db.Query homeID(%d) awayID(%d) error(%v)", homeID, awayID, err)
return
}
defer rows.Close()
for rows.Next() {
r := new(mdlesp.Team)
if err = rows.Scan(&r.ID, &r.Title, &r.SubTitle); err != nil {
log.Error("Teams:row.Scan() error(%v)", err)
return
}
res = append(res, r)
}
if err = rows.Err(); err != nil {
log.Error("rows.Err() error(%v)", err)
}
return
}
// Arcs archives by ids.
func (d *Dao) Arcs(c context.Context, id int64, limit int) (res []*mdlesp.Arc, err error) {
var (
rows *xsql.Rows
)
if rows, err = d.db.Query(c, _arcSQL, id, limit); err != nil {
log.Error("Arcs:d.db.Query id(%d) limit(%d) error(%v)", id, limit, err)
return
}
defer rows.Close()
for rows.Next() {
r := new(mdlesp.Arc)
if err = rows.Scan(&r.ID, &r.Aid, &r.Score, &r.IsDeleted); err != nil {
log.Error("Arcs:row.Scan() error(%v)", err)
return
}
res = append(res, r)
}
if err = rows.Err(); err != nil {
log.Error("rows.Err() error(%v)", err)
}
return
}
// UpArcScore update archive score.
func (d *Dao) UpArcScore(c context.Context, partArcs []*mdlesp.Arc, arcs map[int64]*arcmdl.Arc) (err error) {
var (
caseStr string
aids []int64
score int64
)
for _, v := range partArcs {
if arc, ok := arcs[v.Aid]; ok {
score = d.score(arc)
} else {
continue
}
caseStr = fmt.Sprintf("%s WHEN aid = %d THEN %d", caseStr, v.Aid, score)
aids = append(aids, v.Aid)
}
if len(aids) == 0 {
return
}
if _, err = d.db.Exec(c, fmt.Sprintf(_arcEditSQL, caseStr, xstr.JoinInts(aids))); err != nil {
err = errors.Wrapf(err, "UpArcScore d.db.Exec")
}
return
}
func (d *Dao) score(arc *arcmdl.Arc) (res int64) {
tmpRs := float64(arc.Stat.Coin)*d.c.Rule.CoinPercent +
float64(arc.Stat.Fav)*d.c.Rule.FavPercent + float64(arc.Stat.Danmaku)*d.c.Rule.DmPercent +
float64(arc.Stat.Reply)*d.c.Rule.ReplyPercent + float64(arc.Stat.View)*d.c.Rule.ViewPercent +
float64(arc.Stat.Like)*d.c.Rule.LikePercent + float64(arc.Stat.Share)*d.c.Rule.SharePercent
now := time.Now()
hours := now.Sub(arc.PubDate.Time()).Hours()
if hours/24 <= d.c.Rule.NewDay {
tmpRs = tmpRs * 1.5
}
decimal, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", tmpRs), 64)
res = int64(decimal * 100)
return
}

View File

@@ -0,0 +1,82 @@
package esports
import (
"context"
"testing"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
arcmdl "go-common/app/service/main/archive/api"
"github.com/smartystreets/goconvey/convey"
)
func TestEsportsContests(t *testing.T) {
var (
c = context.Background()
stime = int64(1539590040)
etime = int64(1539590040)
)
convey.Convey("Contests", t, func(ctx convey.C) {
res, err := d.Contests(c, stime, etime)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(len(res), convey.ShouldBeGreaterThanOrEqualTo, 0)
})
})
}
func TestEsportsTeams(t *testing.T) {
var (
c = context.Background()
homeID = int64(1)
awayID = int64(2)
)
convey.Convey("Teams", t, func(ctx convey.C) {
res, err := d.Teams(c, homeID, awayID)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(len(res), convey.ShouldBeGreaterThanOrEqualTo, 0)
})
})
}
func TestEsportsArcs(t *testing.T) {
var (
c = context.Background()
id = int64(1)
limit = int(50)
)
convey.Convey("Arcs", t, func(ctx convey.C) {
res, err := d.Arcs(c, id, limit)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
}
func TestEsportsUpArcScore(t *testing.T) {
var (
c = context.Background()
partArcs = []*mdlesp.Arc{}
arcs map[int64]*arcmdl.Arc
)
convey.Convey("UpArcScore", t, func(ctx convey.C) {
err := d.UpArcScore(c, partArcs, arcs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestEsportsscore(t *testing.T) {
var (
arc = &arcmdl.Arc{}
)
convey.Convey("score", t, func(ctx convey.C) {
res := d.score(arc)
ctx.Convey("Then res should not be nil.", func(ctx convey.C) {
ctx.So(res, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,112 @@
package esports
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"sort"
"strconv"
"time"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_pinkVersion = 1
_linkLive = 3
)
type _response struct {
Code int `json:"code"`
Data int `json:"data"`
}
// NoticeUser pushs the notification to users.
func (d *Dao) NoticeUser(mids []int64, body string, contest *mdlesp.Contest) (err error) {
var strMids string
if d.c.Push.OnlyMids == "" {
strMids = xstr.JoinInts(mids)
} else {
strMids = d.c.Push.OnlyMids
}
uuid := d.getUUID(strMids, contest)
buf := new(bytes.Buffer)
w := multipart.NewWriter(buf)
w.WriteField("app_id", strconv.Itoa(_pinkVersion))
w.WriteField("business_id", strconv.Itoa(d.c.Push.BusinessID))
w.WriteField("alert_title", d.c.Push.Title)
w.WriteField("alert_body", body)
w.WriteField("mids", strMids)
w.WriteField("link_type", strconv.Itoa(_linkLive))
w.WriteField("link_value", strconv.FormatInt(contest.LiveRoom, 10))
w.WriteField("uuid", uuid)
w.Close()
//签名
query := map[string]string{
"ts": strconv.FormatInt(time.Now().Unix(), 10),
"appkey": d.c.App.Key,
}
query["sign"] = d.signature(query, d.c.App.Secret)
url := fmt.Sprintf("%s?ts=%s&appkey=%s&sign=%s", d.pushURL, query["ts"], query["appkey"], query["sign"])
req, err := http.NewRequest(http.MethodPost, url, buf)
if err != nil {
log.Error("http.NewRequest(%s) error(%v)", url, err)
return
}
req.Header.Set("Content-Type", w.FormDataContentType())
req.Header.Set("Authorization", fmt.Sprintf("token=%s", d.c.Push.BusinessToken))
res := &_response{}
if err = d.http.Do(context.TODO(), req, &res); err != nil {
log.Error("httpClient.Do() error(%v)", err)
return
}
if res.Code != 0 || res.Data == 0 {
log.Error("push failed mids_total(%d) body(%s) response(%+v)", len(mids), body, res)
} else {
log.Info("push success mids_total(%d) body(%s) response(%+v)", len(mids), body, res)
}
return
}
//signature 加密算法
func (d *Dao) signature(params map[string]string, secret string) string {
keys := []string{}
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
buf := bytes.Buffer{}
for _, k := range keys {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(url.QueryEscape(k) + "=")
buf.WriteString(url.QueryEscape(params[k]))
}
//加密
h := md5.New()
io.WriteString(h, buf.String()+secret)
return fmt.Sprintf("%x", h.Sum(nil))
}
func (d *Dao) getUUID(mids string, contest *mdlesp.Contest) string {
var b bytes.Buffer
b.WriteString(strconv.Itoa(d.c.Push.BusinessID))
b.WriteString(strconv.FormatInt(contest.ID, 10))
b.WriteString(strconv.FormatInt(contest.Stime, 10))
b.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
b.WriteString(mids)
mh := md5.Sum(b.Bytes())
uuid := hex.EncodeToString(mh[:])
return uuid
}

View File

@@ -0,0 +1,49 @@
package esports
import (
"testing"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
"github.com/smartystreets/goconvey/convey"
)
func TestEsportsNoticeUser(t *testing.T) {
var (
mids = []int64{11, 22, 33}
body = ""
contest = &mdlesp.Contest{}
)
convey.Convey("NoticeUser", t, func(ctx convey.C) {
err := d.NoticeUser(mids, body, contest)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestEsportssignature(t *testing.T) {
var (
params map[string]string
secret = ""
)
convey.Convey("signature", t, func(ctx convey.C) {
p1 := d.signature(params, secret)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestEsportsgetUUID(t *testing.T) {
var (
mids = "11,22,33"
contest = &mdlesp.Contest{}
)
convey.Convey("getUUID", t, func(ctx convey.C) {
p1 := d.getUUID(mids, contest)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,57 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"dynamic.go",
"ugc.go",
],
importpath = "go-common/app/job/main/web-goblin/dao/web",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//library/database/elastic:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom: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"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"dynamic_test.go",
"ugc_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,49 @@
package web
import (
"context"
"go-common/app/job/main/web-goblin/conf"
"go-common/library/database/elastic"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/stat/prom"
)
const _broadURL = "/x/internal/broadcast/push/all"
// Dao dao
type Dao struct {
c *conf.Config
// http client
http *bm.Client
// broadcast URL
broadcastURL string
ela *elastic.Elastic
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
http: bm.NewClient(c.HTTPClient),
broadcastURL: c.Host.API + _broadURL,
ela: elastic.NewElastic(nil),
}
return
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
return nil
}
// PromError stat and log.
func PromError(name string, format string, args ...interface{}) {
prom.BusinessErrCount.Incr(name)
log.Error(format, args...)
}

View File

@@ -0,0 +1,36 @@
package web
import (
"flag"
"os"
"testing"
"go-common/app/job/main/web-goblin/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.web-goblin-job")
flag.Set("conf_token", "12e333e44103d9cba0d6c967f37dd311")
flag.Set("tree_id", "42274")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/web-goblin-job-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}

View File

@@ -0,0 +1,35 @@
package web
import (
"context"
"net/url"
"strconv"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_opt = "1002"
_platform = "web"
)
// PushAll broadcast push all
func (d *Dao) PushAll(c context.Context, msg string, ip string) (err error) {
var res struct {
Code int `json:"code"`
}
params := url.Values{}
params.Set("operation", _opt)
params.Set("speed", strconv.Itoa(d.c.Rule.BroadFeed))
params.Set("platform", _platform)
params.Set("message", msg)
if err = d.http.Post(c, d.broadcastURL, ip, params, &res); err != nil {
log.Error("PushAll url(%s) error(%v)", d.broadcastURL+"?"+params.Encode(), err)
return
}
if res.Code != ecode.OK.Code() {
err = ecode.Int(res.Code)
}
return
}

View File

@@ -0,0 +1,22 @@
package web
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestWebPushAll(t *testing.T) {
var (
c = context.Background()
msg = `{"1":64060766,"180":23753334,"31":49310729,"live":516587}`
ip = "127.0.0.1"
)
convey.Convey("PushAll", t, func(ctx convey.C) {
err := d.PushAll(c, msg, ip)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,19 @@
package web
import (
"context"
"go-common/library/log"
)
const _ugcIncre = "web_goblin"
// UgcSearch ugc insert .
func (d *Dao) UgcSearch(ctx context.Context, data map[string]interface{}) (err error) {
insert := d.ela.NewUpdate(_ugcIncre).Insert()
insert.AddData(_ugcIncre, data)
if err = insert.Do(ctx); err != nil {
log.Error("insert.Do error(%v)", err)
}
return
}

View File

@@ -0,0 +1,23 @@
package web
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestWebUgcSearch(t *testing.T) {
convey.Convey("UgcSearch", t, func(ctx convey.C) {
var (
c = context.Background()
data map[string]interface{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UgcSearch(c, data)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/job/main/web-goblin/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/service/esports:go_default_library",
"//app/job/main/web-goblin/service/web: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,44 @@
package http
import (
"net/http"
"go-common/app/job/main/web-goblin/conf"
"go-common/app/job/main/web-goblin/service/esports"
"go-common/app/job/main/web-goblin/service/web"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
srvweb *web.Service
srvesp *esports.Service
)
// Init init
func Init(c *conf.Config) {
srvweb = web.New(c)
srvesp = esports.New(c)
engine := bm.DefaultServer(c.BM)
router(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
func router(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
}
func ping(c *bm.Context) {
if err := srvweb.Ping(c); err != nil {
log.Error("web-goblin-job ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/job/main/web-goblin/model/esports",
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,31 @@
package esports
// Contest contest.
type Contest struct {
ID int64 `json:"id"`
Stime int64 `json:"stime"`
LiveRoom int64 `json:"live_room"`
HomeID int64 `json:"home_id"`
AwayID int64 `json:"away_id"`
SuccessTeam int64 `json:"success_team"`
SeasonTitle string `json:"season_title"`
SeasonSubTitle string `json:"season_sub_title"`
Special int `json:"special"`
SpecialName string `json:"special_name"`
SpecialTips string `json:"special_tips"`
}
// Team team.
type Team struct {
ID int64 `json:"id"`
Title string `json:"title"`
SubTitle string `json:"sub_title"`
}
// Arc arc.
type Arc struct {
ID int64 `json:"id"`
Aid int64 `json:"aid"`
Score int64 `json:"score"`
IsDeleted int `json:"is_deleted"`
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["model.go"],
importpath = "go-common/app/job/main/web-goblin/model/web",
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,19 @@
package web
// ArcMsg archive .
type ArcMsg struct {
Action string `json:"action"`
Table string `json:"table"`
New *ArchiveSub `json:"new"`
Old *ArchiveSub `json:"old"`
}
// ArchiveSub archive .
type ArchiveSub struct {
Aid int64 `json:"aid"`
Mid int64 `json:"mid"`
PubTime string `json:"pubtime"`
CTime string `json:"ctime"`
MTime string `json:"mtime"`
State int `json:"state"`
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["service.go"],
importpath = "go-common/app/job/main/web-goblin/service/esports",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/dao/esports:go_default_library",
"//app/job/main/web-goblin/model/esports:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/favorite/api/gorpc:go_default_library",
"//app/service/main/favorite/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,271 @@
package esports
import (
"context"
"fmt"
"math"
"time"
"go-common/app/job/main/web-goblin/conf"
"go-common/app/job/main/web-goblin/dao/esports"
mdlesp "go-common/app/job/main/web-goblin/model/esports"
arcclient "go-common/app/service/main/archive/api"
arcmdl "go-common/app/service/main/archive/api"
favrpc "go-common/app/service/main/favorite/api/gorpc"
"go-common/app/service/main/favorite/model"
mdlfav "go-common/app/service/main/favorite/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_favUsers = 1000
_favTryTimes = 3
_defContest = 0
_linkinfo = "点击前往直播间"
_pushinfo = "进入直播>>"
_msgSize = 1000
_tpMessage = 0
_tpPush = 1
_arcMaxLimit = 50
)
// Service struct
type Service struct {
c *conf.Config
dao *esports.Dao
// rpc
fav *favrpc.Service
arcClient arcclient.ArchiveClient
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: esports.New(c),
fav: favrpc.New2(c.FavoriteRPC),
}
var err error
if s.arcClient, err = arcclient.NewClient(c.ArcClient); err != nil {
panic(err)
}
go s.contests()
go s.contestsPush()
go s.arcScore()
return s
}
func (s *Service) contests() {
var (
err error
contests []*mdlesp.Contest
)
for {
stime := time.Now().Add(time.Duration(s.c.Rule.Before))
etime := stime.Add(time.Duration(s.c.Rule.SleepInterval))
if contests, err = s.dao.Contests(context.Background(), stime.Unix(), etime.Unix()); err != nil {
time.Sleep(time.Second)
log.Error("contests s.dao.Contests stime(%d) error(%v)", stime.Unix(), err)
continue
}
for _, contest := range contests {
tmpContest := contest
go s.sendContests(tmpContest)
}
time.Sleep(time.Duration(s.c.Rule.SleepInterval))
}
}
func (s *Service) contestsPush() {
var (
err error
contests []*mdlesp.Contest
)
for {
stime := time.Now()
etime := stime.Add(time.Second)
if contests, err = s.dao.Contests(context.Background(), stime.Unix(), etime.Unix()); err != nil {
time.Sleep(time.Millisecond * 100)
log.Error("contestsPush s.dao.Contests stime(%d) error(%v)", stime.Unix(), err)
continue
}
for _, contest := range contests {
tmpContest := contest
go s.pubContests(tmpContest)
}
time.Sleep(time.Second)
}
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}
func (s *Service) sendContests(contest *mdlesp.Contest) (err error) {
var (
mids []int64
msg string
)
link := fmt.Sprintf("#{%s}{\"https://live.bilibili.com/%d\"}", _linkinfo, contest.LiveRoom)
if mids, msg, err = s.midsParams(contest, link, _tpMessage); err != nil {
log.Error("sendContests s.midsParams(%+v) mids_total(%d) error(%v)", contest, len(mids), err)
return
}
s.dao.Batch(mids, msg, contest, _msgSize, s.dao.SendMessage)
return
}
func (s *Service) pubContests(contest *mdlesp.Contest) (err error) {
var (
mids []int64
msg string
)
if mids, msg, err = s.midsParams(contest, _pushinfo, _tpPush); err != nil {
log.Error("pubContests s.midsParams(%+v) mids_total(%d) error(%v)", contest, len(mids), err)
return
}
s.dao.Batch(mids, msg, contest, s.c.Push.PartSize, s.dao.NoticeUser)
return
}
func (s *Service) midsParams(contest *mdlesp.Contest, link string, tp int) (mids []int64, msg string, err error) {
var (
userList *mdlfav.UserList
teams []*mdlesp.Team
homeID, awayID int64
tMap map[int64]string
pageCount int
)
if userList, err = s.favUsers(contest.ID, 1); err != nil || userList == nil || len(userList.List) == 0 {
log.Error("s.favUsers contestID(%v) first error(%+v)", contest.ID, err)
return
}
ms := make(map[int64]struct{}, userList.Page.Total)
for _, user := range userList.List {
if _, ok := ms[user.Mid]; ok {
continue
}
ms[user.Mid] = struct{}{}
mids = append(mids, user.Mid)
}
if userList.Page.Size == 0 {
pageCount = 0
} else {
pageCount = int(math.Ceil(float64(userList.Page.Total) / float64(userList.Page.Size)))
}
for i := 2; i <= pageCount; i++ {
if userList, err = s.favUsers(contest.ID, i); err != nil || userList == nil || len(userList.List) == 0 {
log.Error("s.favUsers contestID(%v) pn(%d) error(%+v)", contest.ID, i, err)
err = nil
continue
}
for _, user := range userList.List {
if _, ok := ms[user.Mid]; ok {
continue
}
ms[user.Mid] = struct{}{}
mids = append(mids, user.Mid)
}
}
if len(mids) == 0 {
err = ecode.RequestErr
return
}
tm := time.Unix(contest.Stime, 0)
stime := tm.Format("2006-01-02 15:04:05")
if contest.Special == _defContest {
homeID = contest.HomeID
awayID = contest.AwayID
if teams, err = s.dao.Teams(context.Background(), homeID, awayID); err != nil || len(teams) == 0 {
log.Error("midsParams s.dao.Teams homeID(%d) awayID(%d) error(%v)", homeID, awayID, err)
return
}
tMap = make(map[int64]string, 2)
for _, temp := range teams {
tMap[temp.ID] = temp.Title
}
if tp == _tpMessage {
msg = fmt.Sprintf(s.c.Rule.AlertBodyDefault, contest.SeasonTitle, stime, tMap[contest.HomeID], tMap[contest.AwayID], link)
} else if tp == _tpPush {
msg = fmt.Sprintf(s.c.Push.BodyDefault, contest.SeasonTitle, tMap[contest.HomeID], tMap[contest.AwayID], link)
}
} else {
if tp == _tpMessage {
msg = fmt.Sprintf(s.c.Rule.AlertBodySpecial, contest.SeasonTitle, stime, contest.SpecialName, link)
} else if tp == _tpPush {
msg = fmt.Sprintf(s.c.Push.BodySpecial, contest.SeasonTitle, contest.SpecialName, link)
}
}
count := len(mids)
log.Info("midsParams get contest cid(%d) users number(%d)", contest.ID, count)
return
}
func (s *Service) favUsers(cid int64, pn int) (res *mdlfav.UserList, err error) {
for i := 0; i < _favTryTimes; i++ {
if res, err = s.fav.Users(context.Background(), &model.ArgUsers{Type: model.TypeEsports, Oid: cid, Pn: pn, Ps: _favUsers}); err == nil {
break
}
time.Sleep(time.Millisecond * 100)
}
if err != nil {
err = errors.Wrapf(err, "favUsers s.fav.Users cid(%d) pn(%d)", cid, pn)
}
return
}
func (s *Service) arcScore() {
var (
id int64
c = context.Background()
)
for {
av, err := s.dao.Arcs(c, id, _arcMaxLimit)
if err != nil {
log.Error("ArcScore s.dao.Arcs ID(%d) Limit(%d) error(%v)", id, _arcMaxLimit, err)
id = id + int64(_arcMaxLimit)
time.Sleep(time.Second)
continue
}
if len(av) == 0 {
id = 0
time.Sleep(time.Duration(s.c.Rule.ScoreSleep))
continue
}
go s.upArcScore(c, av)
id = av[len(av)-1].ID
time.Sleep(time.Second)
}
}
func (s *Service) upArcScore(c context.Context, partArcs []*mdlesp.Arc) (err error) {
var (
partAids []int64
arcsReply *arcmdl.ArcsReply
)
for _, arc := range partArcs {
partAids = append(partAids, arc.Aid)
}
if len(partAids) == 0 {
return
}
if arcsReply, err = s.arcClient.Arcs(c, &arcmdl.ArcsRequest{Aids: partAids}); err != nil || arcsReply == nil {
log.Error("upArcScore s.arcClient.Arcs(%v) error(%v)", partAids, err)
return
}
if len(arcsReply.Arcs) > 0 {
if err = s.dao.UpArcScore(c, partArcs, arcsReply.Arcs); err != nil {
log.Error("upArcScore s.dao.UpArcScore arcs(%+v) error(%v)", arcsReply, err)
}
}
return
}

View File

@@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"service.go",
"ugc.go",
],
importpath = "go-common/app/job/main/web-goblin/service/web",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/dao/web:go_default_library",
"//app/job/main/web-goblin/model/web:go_default_library",
"//app/service/main/dynamic/model:go_default_library",
"//app/service/main/dynamic/rpc/client:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus: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"],
)
go_test(
name = "go_default_test",
srcs = [
"service_test.go",
"ugc_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/job/main/web-goblin/conf:go_default_library",
"//app/job/main/web-goblin/model/web:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,104 @@
package web
import (
"context"
"encoding/json"
"sync"
"time"
"go-common/app/job/main/web-goblin/conf"
mdlweb "go-common/app/job/main/web-goblin/dao/web"
"go-common/app/job/main/web-goblin/model/web"
dymdl "go-common/app/service/main/dynamic/model"
dyrpc "go-common/app/service/main/dynamic/rpc/client"
"go-common/library/log"
"go-common/library/queue/databus"
)
// Service struct .
type Service struct {
c *conf.Config
dao *mdlweb.Dao
dy *dyrpc.Service
waiter *sync.WaitGroup
archiveNotifySub *databus.Databus
}
// New init .
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: mdlweb.New(c),
dy: dyrpc.New(c.DynamicRPC),
waiter: new(sync.WaitGroup),
archiveNotifySub: databus.New(c.ArchiveNotifySub),
}
go s.broadcastDy()
s.waiter.Add(1)
go s.allSearch()
return s
}
// Ping Service .
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service .
func (s *Service) Close() {
s.dao.Close()
}
func (s *Service) broadcastDy() {
var (
dynamics map[string]int
err error
b []byte
)
for {
if dynamics, err = s.dy.RegionTotal(context.Background(), &dymdl.ArgRegionTotal{RealIP: ""}); err != nil {
mdlweb.PromError("RegionTotal接口错误", "s.dy.RegionTotal error(%v)", err)
time.Sleep(time.Second)
continue
}
if b, err = json.Marshal(dynamics); err != nil {
log.Error("broadcastDy json.Marshal error(%v)", err)
return
}
if err = s.dao.PushAll(context.Background(), string(b), ""); err != nil {
log.Error("s.dao.PushAll(%+v) error(%v)", dynamics, err)
time.Sleep(time.Second)
continue
}
time.Sleep(time.Second * 5)
}
}
func (s *Service) allSearch() {
var (
err error
ctx = context.Background()
)
defer s.waiter.Done()
for {
msg, ok := <-s.archiveNotifySub.Messages()
if !ok {
log.Error("web-goblin-job databus Consumer exit")
return
}
res := &web.ArcMsg{}
if err = json.Unmarshal(msg.Value, res); err != nil {
msg.Commit()
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
continue
}
if res.Table != _archive {
continue
}
s.UgcIncrement(ctx, res)
msg.Commit()
log.Info("consume allSearch ugc key:%s partition:%d offset:%d)", msg.Key, msg.Partition, msg.Offset)
time.Sleep(10 * time.Millisecond)
}
}

View File

@@ -0,0 +1,27 @@
package web
import (
"flag"
"path/filepath"
"time"
"go-common/app/job/main/web-goblin/conf"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/web-goblin-job-test.toml")
flag.Set("conf", dir)
conf.Init()
srv = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
f(srv)
}
}

View File

@@ -0,0 +1,45 @@
package web
import (
"context"
"time"
"go-common/app/job/main/web-goblin/model/web"
"go-common/library/log"
)
const (
_opadd = "add"
_opdel = "del"
_insert = "insert"
_update = "update"
_archive = "archive"
)
// UgcIncrement ugc increment .
func (s *Service) UgcIncrement(ctx context.Context, arg *web.ArcMsg) (err error) {
m := make(map[string]interface{})
if arg.New.CTime != "" {
m["ctime"] = arg.New.CTime
}
m["mtime"] = time.Now().Format("2006-01-02 15:04:05")
if arg.New.PubTime != "" {
m["ptime"] = arg.New.PubTime
}
m["mid"] = arg.New.Mid
m["aid"] = arg.New.Aid
if arg.Action == _insert {
m["action"] = _opadd
}
if arg.Action == _update {
if arg.New.State != arg.Old.State && arg.New.State < 0 {
m["action"] = _opdel
} else {
m["action"] = _update
}
}
if err = s.dao.UgcSearch(ctx, m); err != nil {
log.Error("s.dao.UgcIncre error", err)
}
return
}

View File

@@ -0,0 +1,26 @@
package web
import (
"context"
"testing"
"go-common/app/job/main/web-goblin/model/web"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_UgcIncrement(t *testing.T) {
Convey("test UgcIncrement", t, WithService(func(s *Service) {
var (
err error
ctx = context.Background()
v = &web.ArcMsg{
Action: "add",
Table: "archive",
New: nil,
}
)
err = s.UgcIncrement(ctx, v)
So(err, ShouldBeNil)
}))
}