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

View File

@@ -0,0 +1,80 @@
#### 创作中心-异步任务
#### Version 1.2.12
>1.过滤荣誉周报退订用户
>2.荣誉周报新年系统通知改版
#### Version 1.2.11
>1.使用hbase util的parser
>2.使用grpc替换旧UpMids方法
##### Version 1.2.10
>1.infoc记录周报信息发送状态
##### Version 1.2.9
>1.恢复周报任务防止重跑逻辑
>2.添加黑名单人员
##### Version 1.2.8
>1.删除周报任务防止重跑逻辑
##### Version 1.2.7
>1.修复荣誉周报负数无符号
##### Version 1.2.6
>1.修改荣誉周报发送策略
##### Version 1.2.5
> 1.修复T+1任务状态更新问题
##### Version 1.2.4
> 1.修复荣誉周报白名单退出问题
##### Version 1.2.3
> 1.荣誉周报只发送给180天活跃的up主
##### Version 1.2.2
> 1.只发送未完成新手任务的消息通知
##### Version 1.2.1
> 1.任务支持手机投稿实时监测
> 2.播放和点赞消费拆分
> 3.优化消费队列
> 4.定时消息通知
##### Version 1.2.0
> 1.web任务系统上线
##### Version 1.1.9
> 1.UP主荣誉周报 fix mids slice bounds out of range
##### Version 1.1.8
> 1.UP主荣誉周报 定时任务fix
##### Version 1.1.6
> 1.UP主荣誉周报计算
##### Version 1.1.5
> 1.添加创作学院热值计算
##### Version 1.1.3
> 1.bm
##### Version 1.1.2
> 1.迁移到main
##### Version 1.1.1
> 1.去除statsd
##### Version 1.1.0
> 1.支持移动端投稿身份校验
##### Version 1.0.2
> 1.del import code
##### Version 1.0.1
> 1.fix concurrent map iteration and map write.
##### Version 1.0.0
> 1.查询稿件表判断up主身份
> 1.消费稿件表binlog

View File

@@ -0,0 +1,10 @@
# Owner
shencen
wangzhe01
# Author
shencen fengpengfei shaojiatong
# Reviewer
haoguanwei
shencen

View File

@@ -0,0 +1,16 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- shencen
- shencen fengpengfei shaojiatong
- wangzhe01
labels:
- job
- job/main/creative
- main
options:
no_parent_owners: true
reviewers:
- haoguanwei
- shencen
- shencen fengpengfei shaojiatong

View File

@@ -0,0 +1,10 @@
#### creative-job
##### 项目简介
> 1.创作中心databus消费等异步任务
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common

View File

@@ -0,0 +1,42 @@
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 = ["creative-job.toml"],
importpath = "go-common/app/job/main/creative/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/http: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,447 @@
# This is a TOML document. Boom.
consume = false
hotswitch = false
honorSwitch = false
honorStep = 20
honorFlushSpec = "0 0 12 * * 0"
honorMSGSpec = "0 0 19 * * 0"
sendEveryWeek = true
[task]
#扫表任务配置
rowLimit = 12 # 每次从表中取的最大数据量
tableJobNum = 100 # 开启n张表的扫描任务的协程数量
tableConsumeNum = 10 # 开启消费表数据的协程数量
chanSize = 1024 # chan 缓冲长度
switchDatabus = false # 消息qps较小的消费开关
switchHighQPS = false # 播放/点赞等高qps消费开关
databusQueueLen = 500 # 消息qps较小的消费队列长度
statLikeQueueLen = 1000 # 点赞消费队列长度
statViewQueueLen = 2000 # 播放消费队列长度
#过期任务通知
switchMsgNotify = true #开启消息通知
testNotifyMids = "1,2,3,4" #test mids 逗号隔开
taskMsgCode = "1_17_2"
taskTitle = "你的新手任务尚未完成"
taskContent = "亲爱的UP主你的新手任务尚未完成快来完成新手任务领取丰厚礼包吧请前往web端点击 #{任务成就入口}{\"https://member.bilibili.com/v2#/mission-achieve/taskManage\"} 参与完成哦~"
taskExpireTime = 600 #设置多久过期 单位/s3600*24*30=2592000 s 对第30天未完成新手任务的UP主发送消息通知
taskSendHour = 16 #几点发送
taskSendMiniute = 51 #几分发送
taskSendSecond = 5 #几秒发送
taskBatchMidNum = 100 #过期任务通知每次批量发送多个mid
taskTableJobNum = 100 #开启n张表的扫描任务的协程数量
taskRowLimitNum = 100 #每次从表中取的最大数据量
taskTableConsumeNum = 10 # 开启消费表数据的协程数量
#奖品通知
rewardMsgCode = "1_17_3"
rewardTitle = "你当前有待领取的奖励"
rewardContent = "亲爱的UP主您有新的任务成就礼包待领取快来领取奖品吧请前往web端点击 #{任务成就入口}{\"https://member.bilibili.com/v2#/mission-achieve/taskManage\"} 领取哦~"
rewardTableJobNum = 1 #开启n张表的扫描任务的协程数量
rewardRowLimitNum = 100 #每次从表中取的最大数据量
rewardTableConsumeNum = 10 # 开启消费表数据的协程数量
rewardWeek = 1 #设置周几跑奖品通知 0-6 发送时间为每周六的20:00用户为上周周六18:00 - 本周周六17:59所有达到领取奖励且未领取的用户
rewardLastDay = -30 #从过去多少天开始
rewardLastHour = 0 #从过去开始几点 0-23
rewardLastMiniute = 1 #从过去开始几分 0-59
rewardLastSecond = 0 #从过去开始几秒 0-59
rewardNowHour = 15 #从当前时间几点开始
rewardNowMiniute = 58 #从当前时间几分开始
rewardNowSecond = 5 #从当前时间几秒开始
rewardBatchMidNum = 100 #奖励通知每次批量发送多个mid
rewardSendHour = 0 #基于当前时间往后多少小时发送
rewardSendMiniute = 0 #基于当前时间往后多少分钟发送
rewardSendSecond = 5 #基于当前时间往后多少秒发送
biliMid = 37090048 #哔哩哔哩创作中心公众号
#新手和进阶粉丝数
newFollower = 1
advancedFollower=2
#计数下限
statView = 1
statLike = 1
statReply = 1
statShare = 1
statFav = 1
statCoin = 1
statDM = 1
#计数上限
statViewUp = 3
statLikeUp = 3
statReplyUp = 3
statShareUp = 3
statFavUp = 3
statCoinUp = 3
statDMUp = 3
[host]
message = "http://message.bilibili.co"
api = "http://api.bilibili.co"
videoup = "http://uat-archive.api.bilibili.co"
[log]
dir = "/data/log/creative-job/"
stdout = false
[log.syslog]
project = "creative-job"
chanSize = 10240
[bm]
[bm.outer]
addr = "0.0.0.0:7461"
maxListen = 1000
timeout="1s"
readTimeout="1s"
writeTimeout="1s"
[httpClient]
[httpClient.normal]
key = "b8f239ca38a53308"
secret = "5460ef72fe13c10dfb53442b9111427e"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
[httpClient.normal.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.slow]
key = "b8f239ca38a53308"
secret = "5460ef72fe13c10dfb53442b9111427e"
dial = "1s"
timeout = "10s"
keepAlive = "60s"
timer = 10
[httpClient.slow.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[db]
[db.creative]
name = "[creative]tcp@172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_creative?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
idleTimeout ="4h"
queryTimeout = "5s"
execTimeout = "5s"
tranTimeout = "5s"
[db.creative.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[hbaseOld]
master = ""
meta = ""
dialTimeout = "1s"
readTimeout = "150ms"
readsTimeout = "600ms"
writeTimeout = "200ms"
writesTimeout = "600ms"
[hbaseOld.zookeeper]
root = ""
addrs = ["10.23.58.154:2181","10.23.58.141:2181","10.23.58.20:2181"]
timeout = "30s"
[arcSub]
key = "4c76cbb7a985ac90"
secret = "f36fbb15a85c6e21b0ee22a560ef3a67"
group = "Archive-MainArchive-S"
topic = "Archive-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub"
proto = "tcp"
addr = "172.16.33.158:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[arcNotifySub]
key = "4c76cbb7a985ac90"
secret = "f36fbb15a85c6e21b0ee22a560ef3a67"
group = "ArchiveNotify-MainArchive-S"
topic = "ArchiveNotify-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/arcNotify"
proto = "tcp"
addr = "172.16.33.158:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[upPub]
key = "4c76cbb7a985ac90"
secret = "f36fbb15a85c6e21b0ee22a560ef3a67"
group = "Creative-MainArchive-P"
topic = "Creative-T"
action = "pub"
offset = "old"
buffer = 128
name = "creative-job/pub"
proto = "tcp"
addr = "172.16.33.158:6205"
active = 10
idle = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[monitor]
moni="http://moni.bilibili.co/dashboard/db/databus?orgId=1"
host = "http://bap.bilibili.co"
username="fengpengfei"
appToken = "jLgSvndTeranxGMN"
appSecret = "CcCdlEBYqxqrgAieJuFVZUsgPmweLRms"
[taskSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "CreativeTask-MainArchive-S"
topic = "CreativeTask-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/task"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[shareSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "ShareMid-MainArchive-S"
topic = "ShareMid-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/share"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[relationSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "Relation-MainArchive-CreativeNewcomer-S"
topic = "Relation-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/relation"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statLikeSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatLike-MainArchive-CreativeNewcomer-S"
topic = "StatLike-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statLike"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statShareSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatShare-MainArchive-CreativeNewcomer-S"
topic = "StatShare-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statShare"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statCoinSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatCoin-MainArchive-CreativeNewcomer-S"
topic = "StatCoin-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statCoin"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statFavSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatFav-MainArchive-CreativeNewcomer-S"
topic = "StatFav-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statFav"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statReplySub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatReply-MainArchive-CreativeNewcomer-S"
topic = "StatReply-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statReply"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statDMSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatDM-MainArchive-CreativeNewcomer-S"
topic = "StatDM-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statDM"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[statViewSub]
key = "4c76cbb7a985ac90"
secret = "43bb22ce34a6b13e7814f09cb8116522"
group = "StatView-MainArchive-CreativeNewcomer-S"
topic = "StatView-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/statView"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[newUpSub]
key = "4c76cbb7a985ac90"
secret = "f36fbb15a85c6e21b0ee22a560ef3a67"
group = "ArchiveNotify-MainArchive-CreativeNewcomer-S"
topic = "ArchiveNotify-T"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/newup"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[mobileUpSub]
key = "4c76cbb7a985ac90"
secret = "f36fbb15a85c6e21b0ee22a560ef3a67"
group = "Videoup2Bvc-MainArchive-CreativeNewcomer-S"
topic = "Videoup2Bvc"
action = "sub"
offset = "old"
buffer = 128
name = "creative-job/sub/mobileup"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[weeklyHonorInfoc]
taskID = "001810"
proto = "tcp"
addr = "172.18.33.125:15140"
chanSize = 10240

View File

@@ -0,0 +1,48 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/job/main/creative/conf"
"go-common/app/job/main/creative/http"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
trace.Init(conf.Conf.Tracer)
defer trace.Close()
defer log.Close()
log.Info("creative-jobstart")
// service init
http.Init(conf.Conf)
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("creative-job get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGINT:
http.Svc.Close()
log.Info("creative-jobexit")
time.Sleep(1 * time.Second)
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

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/creative/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc: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,252 @@
package conf
import (
"errors"
"flag"
"github.com/BurntSushi/toml"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/log/infoc"
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"
"go-common/library/database/hbase.v2"
)
// Conf info.
var (
ConfPath string
Conf = &Config{}
client *conf.Client
)
// Config struct.
type Config struct {
// db
DB *DB
// base
// ecode
Ecode *ecode.Config
// log
Log *log.Config
// httpClinet
HTTPClient *HTTPClient
// BM
BM *HTTPServers
// tracer
Tracer *trace.Config
// host
Host *Host
// Databus
Env string
Consume bool
Pub bool
ArcSub *databus.Config
ArcNotifySub *databus.Config
UpPub *databus.Config
//task
TaskSub, ShareSub, RelationSub, StatLikeSub *databus.Config
StatShareSub, StatCoinSub, StatFavSub *databus.Config
StatReplySub, StatDMSub, StatViewSub *databus.Config
NewUpSub *databus.Config
Task *Task
// channal len
ChanSize int64
//moni
Monitor *Monitor
// rpc client2
ArchiveRPC *rpc.ClientConfig
ArticleRPC *rpc.ClientConfig
// grpc Client
CreativeGRPClient *warden.ClientConfig
UpGRPCClient *warden.ClientConfig
//hot compute switch
HotSwitch bool
//hot compute switch
HonorSwitch bool
HonorStep int
HonorMSGSpec string
HonorFlushSpec string
SendEveryWeek bool
// hbase
HBaseOld *HBaseConfig
// infoc
WeeklyHonorInfoc *infoc.Config
}
// DB conf.
type DB struct {
// Creative db
Creative *sql.Config
// Archive db
Archive *sql.Config
}
// HBaseConfig for new hbase client.
type HBaseConfig struct {
*hbase.Config
WriteTimeout time.Duration
ReadTimeout time.Duration
}
// HTTPServers Http Servers
type HTTPServers struct {
Outer *bm.ServerConfig
}
// HTTPClient conf.
type HTTPClient struct {
Normal *bm.ClientConfig
Slow *bm.ClientConfig
}
// Host conf.
type Host struct {
Monitor string
Passport string
Account string
Message string
API string
Videoup string
}
// Monitor conf.
type Monitor struct {
Host string
Moni string
UserName string
AppSecret string
AppToken string
}
// Task conf.
type Task struct {
//扫表
RowLimit int //每次从表中取的最大数据量
TableJobNum int //开启n张表的扫描任务的协程数量
TableConsumeNum int //开启消费表数据的协程数量
//databus 消费
SwitchHighQPS bool
SwitchDatabus bool
DatabusQueueLen int
StatViewQueueLen int
StatLikeQueueLen int
ChanSize int
//task notify
SwitchMsgNotify bool
TaskRowLimitNum int
TaskTableJobNum int
TaskTableConsumeNum int
TaskExpireTime int64
TaskSendHour int
TaskSendMiniute int
TaskSendSecond int
TaskBatchMidNum int
TaskMsgCode string
TaskTitle string
TaskContent string
TestNotifyMids string
//reward notify
RewardRowLimitNum int
RewardTableJobNum int
RewardTableConsumeNum int
RewardWeek int
RewardLastDay int
RewardLastHour int
RewardLastMiniute int
RewardLastSecond int
RewardNowHour int
RewardNowMiniute int
RewardNowSecond int
RewardSendHour int
RewardSendMiniute int
RewardSendSecond int
RewardBatchMidNum int
RewardMsgCode string
RewardTitle string
RewardContent string
BiliMID int64
//新手和进阶粉丝数
NewFollower int64
AdvancedFollower int64
//单个稿件计数
StatView int64
StatLike int64
StatReply int64
StatShare int64
StatFav int64
StatCoin int64
StatDM int64
//单个稿件计数设定上限
StatViewUp int64
StatLikeUp int64
StatReplyUp int64
StatShareUp int64
StatFavUp int64
StatCoinUp int64
StatDMUp int64
}
func init() {
flag.StringVar(&ConfPath, "conf", "", "default config path")
}
// Init conf.
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 (
tomlStr string
ok bool
tmpConf *Config
)
if tomlStr, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(tomlStr, &tmpConf); err != nil {
return errors.New("could not decode toml config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"academy.go",
"dao.go",
],
importpath = "go-common/app/job/main/creative/dao/academy",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/xstr: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 = [
"academy_test.go",
"dao_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,49 @@
package academy
import (
"context"
"fmt"
"time"
"go-common/app/job/main/creative/model"
"go-common/library/log"
"go-common/library/xstr"
)
const (
// select
_getArcsSQL = "SELECT id,oid,business FROM academy_archive WHERE state=? AND business=? AND id > ? order by id ASC limit ?"
)
//Archives get limit achives.
func (d *Dao) Archives(c context.Context, id int64, bs, limit int) (res []*model.OArchive, err error) {
rows, err := d.db.Query(c, _getArcsSQL, 0, bs, id, limit)
res = make([]*model.OArchive, 0)
for rows.Next() {
a := &model.OArchive{}
if err = rows.Scan(&a.ID, &a.OID, &a.Business); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
res = append(res, a)
}
return
}
//UPHotByAIDs update hot by aids.
func (d *Dao) UPHotByAIDs(c context.Context, hots map[int64]int64) error {
var oids []int64
sql := "UPDATE academy_archive SET hot = CASE oid "
for oid, hot := range hots {
sql += fmt.Sprintf("WHEN %d THEN %d ", oid, hot)
oids = append(oids, oid)
}
sql += fmt.Sprintf("END, mtime=? WHERE oid IN (%s)", xstr.JoinInts(oids))
_, err := d.db.Exec(c, sql, time.Now())
if err != nil {
log.Error("d.db.Exec sql(%s) error(%v)", sql, err)
}
log.Info("d.db.Exec sql(%s) hots(%+v) error(%v)", sql, hots, err)
return err
}

View File

@@ -0,0 +1,42 @@
package academy
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestAcademyArchives(t *testing.T) {
convey.Convey("Archives", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(10110127)
bs = int(1)
limit = int(10)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.Archives(c, id, bs, limit)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestAcademyUPHotByAIDs(t *testing.T) {
convey.Convey("UPHotByAIDs", t, func(ctx convey.C) {
var (
c = context.Background()
hots = map[int64]int64{
10110127: 11,
}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.UPHotByAIDs(c, hots)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

View File

@@ -0,0 +1,34 @@
package academy
import (
"context"
"go-common/app/job/main/creative/conf"
"go-common/library/database/sql"
)
// Dao is creative dao.
type Dao struct {
// config
c *conf.Config
// db
db *sql.DB
}
// New init api url
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.Creative),
}
return
}
// Ping creativeDb
func (d *Dao) Ping(c context.Context) (err error) {
return d.db.Ping(c)
}
// Close creativeDb
func (d *Dao) Close() (err error) {
return d.db.Close()
}

View File

@@ -0,0 +1,34 @@
package academy
import (
"flag"
"go-common/app/job/main/creative/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.creative-job")
flag.Set("conf_token", "43943fda0bb311e8865c66d44b23cda7")
flag.Set("tree_id", "16037")
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/creative-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,60 @@
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",
"rpc_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//vendor/github.com/go-sql-driver/mysql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"rpc.go",
],
importpath = "go-common/app/job/main/creative/dao/archive",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/openplatform/article/model:go_default_library",
"//app/interface/openplatform/article/rpc/client:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/model:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/api/gorpc:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//library/ecode: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,77 @@
package archive
import (
"context"
"net/url"
"strconv"
article "go-common/app/interface/openplatform/article/rpc/client"
"go-common/app/job/main/creative/conf"
"go-common/app/job/main/creative/model"
archive "go-common/app/service/main/archive/api/gorpc"
"go-common/library/ecode"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
// Dao is archive dao.
type Dao struct {
// config
c *conf.Config
// rpc
arc *archive.Service2
art *article.Service
// http client
client *httpx.Client
viewURL string
}
// New init api url
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
arc: archive.New2(c.ArchiveRPC),
art: article.New(c.ArticleRPC),
// http client
client: httpx.NewClient(c.HTTPClient.Normal),
viewURL: c.Host.Videoup + "/videoup/view",
}
return
}
// Ping fn
func (d *Dao) Ping(c context.Context) (err error) {
return
}
// Close fn
func (d *Dao) Close() (err error) {
return
}
// View get archive
func (d *Dao) View(c context.Context, mid, aid int64) (av *model.ArcVideo, err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("aid", strconv.FormatInt(aid, 10))
var res struct {
Code int `json:"code"`
Data *model.ArcVideo `json:"data"`
}
if err = d.client.Get(c, d.viewURL, "", params, &res); err != nil {
log.Error("archive.view url(%s) mid(%d) error(%v)", d.viewURL+"?"+params.Encode(), mid, err)
err = ecode.CreativeArchiveAPIErr
return
}
if res.Code != 0 {
log.Error("archive.view url(%s) mid(%d) res(%v)", d.viewURL+"?"+params.Encode(), mid, res)
err = ecode.Int(res.Code)
return
}
if res.Data.Archive == nil {
log.Error("archive.view url(%s) mid(%d) res(%v)", d.viewURL+"?"+params.Encode(), mid, res)
return
}
av = res.Data
return
}

View File

@@ -0,0 +1,55 @@
package archive
import (
"context"
"flag"
"os"
"testing"
"go-common/app/job/main/creative/conf"
_ "github.com/go-sql-driver/mysql"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.creative-job")
flag.Set("conf_token", "43943fda0bb311e8865c66d44b23cda7")
flag.Set("tree_id", "16037")
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/creative-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func Test_UpCount(t *testing.T) {
var (
err error
c = context.TODO()
mid = int64(123)
)
convey.Convey("UpCount", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
_, err = d.UpCount(c, mid)
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

View File

@@ -0,0 +1,65 @@
package archive
import (
"context"
"strconv"
"go-common/app/interface/openplatform/article/model"
"go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
)
// UpCount get archives count.
func (d *Dao) UpCount(c context.Context, mid int64) (count int, err error) {
var arg = &archive.ArgUpCount2{Mid: mid}
if count, err = d.arc.UpCount2(c, arg); err != nil {
log.Error("rpc UpCount2 (%v) error(%v)", mid, err)
err = ecode.CreativeArcServiceErr
}
return
}
// Archives get archive list.
func (d *Dao) Archives(c context.Context, aids []int64) (a map[int64]*api.Arc, err error) {
var arg = &archive.ArgAids2{Aids: aids, RealIP: ""}
if a, err = d.arc.Archives3(c, arg); err != nil {
log.Error("d.arc.Archive3 aids(%v+)|error(%v)", aids, err)
err = ecode.CreativeArcServiceErr
}
return
}
// Archive get archive info.
func (d *Dao) Archive(c context.Context, aid int64) (a *api.Arc, err error) {
var arg = &archive.ArgAid2{Aid: aid, RealIP: ""}
if a, err = d.arc.Archive3(c, arg); err != nil {
log.Error("d.arc.Archive3 aid(%d)|error(%v)", aid, err)
err = ecode.CreativeArcServiceErr
}
return
}
// Stats get archives stat.
func (d *Dao) Stats(c context.Context, aids []int64) (a map[int64]*api.Stat, err error) {
var arg = &archive.ArgAids2{Aids: aids, RealIP: ""}
if a, err = d.arc.Stats3(c, arg); err != nil {
log.Error("rpc Stats (%v) error(%v)", aids, err)
err = ecode.CreativeArcServiceErr
}
return
}
// ArticleMetas batch get articles by aids.
func (d *Dao) ArticleMetas(c context.Context, aids []int64) (res map[int64]*model.Meta, err error) {
arg := &model.ArgAids{Aids: aids, RealIP: ""}
if res, err = d.art.ArticleMetas(c, arg); err != nil {
log.Error("d.art.ArticleMetas aids(%+v)|error(%v)", arg, err)
if _, er := strconv.ParseInt(err.Error(), 10, 64); er != nil {
err = ecode.CreativeArticleRPCErr
}
}
return
}

View File

@@ -0,0 +1,83 @@
package archive
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestArchiveUpCount(t *testing.T) {
convey.Convey("UpCount", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(1)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.UpCount(c, mid)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestArchiveArchives(t *testing.T) {
convey.Convey("Archives", t, func(ctx convey.C) {
var (
c = context.Background()
aids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.Archives(c, aids)
ctx.Convey("Then err should be nil.a should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestArchiveArchive(t *testing.T) {
convey.Convey("Archive", t, func(ctx convey.C) {
var (
c = context.Background()
aid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.Archive(c, aid)
ctx.Convey("Then err should be nil.a should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestArchiveStats(t *testing.T) {
convey.Convey("Stats", t, func(ctx convey.C) {
var (
c = context.Background()
aids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.Stats(c, aids)
ctx.Convey("Then err should be nil.a should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestArchiveArticleMetas(t *testing.T) {
convey.Convey("ArticleMetas", t, func(ctx convey.C) {
var (
c = context.Background()
aids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
_, err := d.ArticleMetas(c, aids)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

View File

@@ -0,0 +1,47 @@
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/creative/conf:go_default_library",
"//vendor/github.com/go-sql-driver/mysql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["dao.go"],
importpath = "go-common/app/job/main/creative/dao/monitor",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/model:go_default_library",
"//library/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,120 @@
package monitor
import (
"bytes"
"context"
"crypto/md5"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
"go-common/app/job/main/creative/conf"
"go-common/app/job/main/creative/model"
"go-common/library/log"
)
const (
_uri = "/api/v1/message/add"
_method = "POST"
_fileType = "application/json"
)
// Dao is message dao.
type Dao struct {
c *conf.Config
client *http.Client
url string
}
// New new a message dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
client: &http.Client{
Timeout: time.Duration(time.Second * 1),
},
url: c.Monitor.Host + _uri,
}
return
}
// Send send exception message to owner.
func (d *Dao) Send(c context.Context, username, msg string) (err error) {
params := url.Values{}
now := time.Now().Unix()
params.Set("username", username)
params.Set("content", msg)
params.Set("title", "test")
params.Set("url", "")
params.Set("type", "wechat")
params.Set("token", d.c.Monitor.AppToken)
params.Set("timestamp", strconv.FormatInt(now, 10))
bap := &model.BAP{
UserName: params.Get("username"),
Content: params.Get("content"),
Title: params.Get("title"),
URL: params.Get("url"),
Ty: params.Get("type"),
Token: params.Get("token"),
TimeStamp: now,
Signature: d.getSign(params),
}
jsonStr, err := json.Marshal(bap)
if err != nil {
log.Error("monitor json.Marshal error (%v)", err)
return
}
req, err := http.NewRequest(_method, d.url, bytes.NewBuffer(jsonStr))
if err != nil {
log.Error("monitor http.NewRequest error (%v)", err)
return
}
req.Header.Add("Content-Type", _fileType)
// timeout
ctx, cancel := context.WithTimeout(c, 800*time.Millisecond)
req = req.WithContext(ctx)
defer cancel()
response, err := d.client.Do(req)
if err != nil {
log.Error("monitor d.client.Post error(%v)", err)
return
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
log.Error("monitor http.StatusCode nq http.StatusOK (%d) | url(%s)", response.StatusCode, d.url)
return
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Error("monitor ioutil.ReadAll error(%v)", err)
return
}
var result struct {
Status int `json:"status"`
Msg string `json:"msg"`
}
if err = json.Unmarshal(body, &result); err != nil {
log.Error("monitor json.Unmarshal status(%d) error(%v)", result.Status, err)
}
if result.Status != 0 {
log.Error("monitor get status(%d) msg(%s)", result.Status, result.Msg)
}
return
}
func (d *Dao) getSign(params url.Values) (sign string) {
for k, v := range params {
if len(v) == 0 {
params.Del(k)
}
}
h := md5.New()
io.WriteString(h, params.Encode()+d.c.Monitor.AppSecret)
sign = fmt.Sprintf("%x", h.Sum(nil))
return
}

View File

@@ -0,0 +1,55 @@
package monitor
import (
"context"
"flag"
"os"
"testing"
"go-common/app/job/main/creative/conf"
_ "github.com/go-sql-driver/mysql"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.creative-job")
flag.Set("conf_token", "43943fda0bb311e8865c66d44b23cda7")
flag.Set("tree_id", "16037")
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/creative-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func Test_Monitor(t *testing.T) {
var (
err error
c = context.TODO()
username = "test"
)
convey.Convey("monitor", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
err = d.Send(c, username, "creative-job test")
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

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 = [
"api.go",
"dao.go",
"db.go",
],
importpath = "go-common/app/job/main/creative/dao/newcomer",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/creative/api:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr: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 = [
"api_test.go",
"dao_test.go",
"db_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,45 @@
package newcomer
import (
"context"
"errors"
"net/url"
"go-common/library/log"
"go-common/library/xstr"
)
// SendNotify send msg notify user
func (d *Dao) SendNotify(c context.Context, mids []int64, mc, title, context string) (err error) {
var (
params = url.Values{}
res struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data *struct {
TotalCount int `json:"total_count"`
ErrorCount int `json:"error_count"`
ErrorMidList []int64 `json:"error_mid_list"`
} `json:"data"`
}
)
params.Set("mc", mc) //消息码,用于识别消息类别
params.Set("data_type", "4") //消息类型1、回复我的 2、@我 3、收到的爱 4、业务通知 5、系统公告
params.Set("title", title) //消息标题
params.Set("context", context) //消息实体内容
params.Set("mid_list", xstr.JoinInts(mids)) //用于接收该消息的用户mid列表不超过1000个(半角逗号分割)
log.Info("SendNotify params(%+v)|msgURI(%s)", params.Encode(), d.msgURI)
if err = d.httpClient.Post(c, d.msgURI, "", params, &res); err != nil {
log.Error("d.httpClient.Post(%s,%v,%d)", d.msgURI, params, err)
return
}
if res.Code != 0 {
err = errors.New("code != 0")
log.Error("d.httpClient.Post(%s,%v,%v,%d)", d.msgURI, params, err, res.Code)
}
if res.Data != nil {
log.Info("SendNotify log total_count(%d) error_count(%d) error_mid_list(%v)", res.Data.TotalCount, res.Data.ErrorCount, res.Data.ErrorMidList)
}
return
}

View File

@@ -0,0 +1,26 @@
package newcomer
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestNewcomerSendNotify(t *testing.T) {
convey.Convey("Msg", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{27515405}
mc = "1_17_2"
title = "creative"
context = "sssss"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SendNotify(c, mids, mc, title, context)
ctx.Convey("Then err should be nil.msg should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

View File

@@ -0,0 +1,55 @@
package newcomer
import (
"context"
grpc "go-common/app/interface/main/creative/api"
"go-common/app/job/main/creative/conf"
"go-common/library/database/sql"
httpx "go-common/library/net/http/blademaster"
)
// Dao is search dao.
type Dao struct {
c *conf.Config
//db
db *sql.DB
//grpc
grpcClient grpc.CreativeClient
// http
httpClient *httpx.Client
msgURI string
}
// New new a search dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.Creative),
httpClient: httpx.NewClient(c.HTTPClient.Slow),
msgURI: c.Host.Message + "/api/notify/send.user.notify.do", //发送用户通知消息接口
}
var err error
if d.grpcClient, err = grpc.NewClient(c.CreativeGRPClient); err != nil {
panic(err)
}
return d
}
// CheckTaskState call grpc client.
func (d *Dao) CheckTaskState(c context.Context, MID, TaskID int64) (*grpc.TaskReply, error) {
return d.grpcClient.CheckTaskState(c, &grpc.TaskRequest{Mid: MID, TaskId: TaskID})
}
// Ping ping grpc.
func (d *Dao) Ping(c context.Context) (err error) {
d.grpcClient.Ping(c, nil)
return
}
// Close close grpc.
func (d *Dao) Close(c context.Context) (err error) {
d.grpcClient.Close(c, nil)
return
}

View File

@@ -0,0 +1,34 @@
package newcomer
import (
"flag"
"go-common/app/job/main/creative/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.creative-job")
flag.Set("conf_token", "43943fda0bb311e8865c66d44b23cda7")
flag.Set("tree_id", "16037")
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/creative-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,230 @@
package newcomer
import (
"context"
"fmt"
"time"
"go-common/app/job/main/creative/model"
"go-common/library/log"
)
//UserTasks get user unfinish task.
func (d *Dao) UserTasks(c context.Context, index string, id int64, limit int) (res []*model.UserTask, err error) {
_getUserTaskSQL := "SELECT id, mid, task_id, task_group_id, task_type, state, task_bind_time, ctime, mtime FROM newcomers_task_user_%s WHERE state=-1 AND id > ? order by id asc limit ?"
rows, err := d.db.Query(c, fmt.Sprintf(_getUserTaskSQL, index), id, limit)
if err != nil {
log.Error("UserTasks d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.UserTask, 0)
for rows.Next() {
r := &model.UserTask{}
if err = rows.Scan(&r.ID, &r.MID, &r.TaskID, &r.TaskGroupID, &r.TaskType, &r.State, &r.TaskBindTime, &r.CTime, &r.MTime); err != nil {
log.Error("UserTasks rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}
// getTableName by mid%100
func getTableName(mid int64) string {
return fmt.Sprintf("%02d", mid%100)
}
// UpUserTask update user task finish state
func (d *Dao) UpUserTask(c context.Context, mid, tid int64) (int64, error) {
_upUserTaskSQL := "UPDATE newcomers_task_user_%s SET state=? WHERE mid=? AND task_id=?"
res, err := d.db.Exec(c, fmt.Sprintf(_upUserTaskSQL, getTableName(mid)), 0, mid, tid)
if err != nil {
log.Error("RewardActivate d.db.Exec mid(%d) id(%d) error(%v)", mid, tid, err)
return 0, err
}
return res.RowsAffected()
}
//UserTasksByMIDAndState get user unfinish task by mid & state.
func (d *Dao) UserTasksByMIDAndState(c context.Context, mid int64, state int) (res []*model.UserTask, err error) {
_getUserTaskByMIDSQL := "SELECT id, mid, task_id, task_group_id, task_type, state, ctime, mtime FROM newcomers_task_user_%s WHERE mid=? AND state=?"
rows, err := d.db.Query(c, fmt.Sprintf(_getUserTaskByMIDSQL, getTableName(mid)), mid, state)
if err != nil {
log.Error("UserTasksByMIDAndState d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.UserTask, 0)
for rows.Next() {
r := &model.UserTask{}
if err = rows.Scan(&r.ID, &r.MID, &r.TaskID, &r.TaskGroupID, &r.TaskType, &r.State, &r.CTime, &r.MTime); err != nil {
log.Error("UserTasksByMIDAndState rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}
//Tasks get all task.
func (d *Dao) Tasks(c context.Context) (res []*model.Task, err error) {
_getTaskSQL := "SELECT id, group_id, type, state, target_type, target_value, title, `desc`, comment, ctime, mtime FROM newcomers_task WHERE state=0"
rows, err := d.db.Query(c, _getTaskSQL)
if err != nil {
log.Error("Tasks d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.Task, 0)
for rows.Next() {
r := &model.Task{}
if err = rows.Scan(&r.ID, &r.GroupID, &r.Type, &r.State, &r.TargetType, &r.TargetValue, &r.Title, &r.Desc, &r.Comment, &r.CTime, &r.MTime); err != nil {
log.Error("Tasks rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}
// TaskByTID get task by task_id
func (d *Dao) TaskByTID(c context.Context, mid int64, tid int64) (res *model.Task, err error) {
_getOneTaskSQL := fmt.Sprintf("SELECT task_id,task_group_id,task_type,state FROM newcomers_task_user_%s WHERE mid=? AND task_id=?", getTableName(mid))
row := d.db.QueryRow(c, _getOneTaskSQL, mid, tid)
res = &model.Task{}
if err = row.Scan(&res.ID, &res.GroupID, &res.Type, &res.State); err != nil {
log.Error("TaskByTID ow.Scan error(%v)", err)
return
}
return
}
// CheckTaskComplete check task complete state
func (d *Dao) CheckTaskComplete(c context.Context, mid int64, tid int64) bool {
task, err := d.TaskByTID(c, mid, tid)
if err != nil || task == nil {
return false
}
if task.State == 0 {
return true
}
return false
}
//UserTasksNotify get unfinish task send notify to user.
func (d *Dao) UserTasksNotify(c context.Context, index string, id int64, start, end string, limit int) (res []*model.UserTask, err error) {
_getUserTaskSQL := "SELECT id, mid, task_id, task_group_id, task_type, state, ctime, mtime FROM newcomers_task_user_%s WHERE state=-1 AND task_type=1 AND id>? AND ctime >= ? AND ctime <= ? order by id asc limit ?"
rows, err := d.db.Query(c, fmt.Sprintf(_getUserTaskSQL, index), id, start, end, limit)
if err != nil {
log.Error("UserTasks d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.UserTask, 0)
for rows.Next() {
r := &model.UserTask{}
if err = rows.Scan(&r.ID, &r.MID, &r.TaskID, &r.TaskGroupID, &r.TaskType, &r.State, &r.CTime, &r.MTime); err != nil {
log.Error("UserTasks rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}
//UserTasksByMID get user unfinish task by mid.
func (d *Dao) UserTasksByMID(c context.Context, mid int64) (res []*model.UserTask, err error) {
_getUserTaskByMIDSQL := "SELECT id, mid, task_id, task_group_id, task_type, state, ctime, mtime FROM newcomers_task_user_%s WHERE mid=?"
rows, err := d.db.Query(c, fmt.Sprintf(_getUserTaskByMIDSQL, getTableName(mid)), mid)
if err != nil {
log.Error("UserTasksByMID d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.UserTask, 0)
for rows.Next() {
r := &model.UserTask{}
if err = rows.Scan(&r.ID, &r.MID, &r.TaskID, &r.TaskGroupID, &r.TaskType, &r.State, &r.CTime, &r.MTime); err != nil {
log.Error("UserTasksByMID rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}
// AllGiftRewards get all GiftRewards for cache
func (d *Dao) AllGiftRewards(c context.Context) (res map[int8][]*model.GiftReward, err error) {
_getAllGiftRewardSQL := "SELECT task_type,reward_id,state,comment,ctime,mtime FROM newcomers_gift_reward WHERE state=0"
rows, err := d.db.Query(c, _getAllGiftRewardSQL)
if err != nil {
log.Error("AllGiftRewards d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make(map[int8][]*model.GiftReward)
for rows.Next() {
t := &model.GiftReward{}
if err = rows.Scan(&t.TaskType, &t.RewardID, &t.State, &t.Comment, &t.CTime, &t.MTime); err != nil {
log.Error("AllGiftRewards rows.Scan error(%v)", err)
return
}
res[t.TaskType] = append(res[t.TaskType], t)
}
return
}
// GiftRewardCount get received gift reward count
func (d *Dao) GiftRewardCount(c context.Context, mid int64) (res int, err error) {
sqlStr := "SELECT count(DISTINCT task_gift_id) FROM newcomers_reward_receive WHERE NOT task_gift_id=0 AND mid=?"
row := d.db.QueryRow(c, sqlStr, mid)
err = row.Scan(&res)
if err != nil {
log.Error("GiftRewardCount d.db.QueryRow error(%v)", err)
}
return
}
// BaseRewardCount get received base reward count
func (d *Dao) BaseRewardCount(c context.Context, mid int64) (res int, err error) {
sqlStr := "SELECT count(DISTINCT task_group_id) FROM newcomers_reward_receive WHERE NOT task_group_id=0 AND mid=?"
row := d.db.QueryRow(c, sqlStr, mid)
err = row.Scan(&res)
if err != nil {
log.Error("BaseRewardCount d.db.QueryRow error(%v)", err)
}
return
}
//CheckTasksForRewardNotify get finish task send notify to user.
func (d *Dao) CheckTasksForRewardNotify(c context.Context, index string, id int64, startMtime, endMtime time.Time, limit int) (res []*model.UserTask, err error) {
_getUserTaskSQL := "SELECT id, mid, task_id, task_group_id, task_type, state, ctime, mtime FROM newcomers_task_user_%s WHERE state=0 AND id>? AND mtime>=? AND mtime<=? order by id asc limit ?"
rows, err := d.db.Query(c, fmt.Sprintf(_getUserTaskSQL, index), id, startMtime.Format("2006-01-02 15:04:05"), endMtime.Format("2006-01-02 15:04:05"), limit)
if err != nil {
log.Error("UserTasks d.db.Query error(%v)", err)
return
}
defer rows.Close()
res = make([]*model.UserTask, 0)
for rows.Next() {
r := &model.UserTask{}
if err = rows.Scan(&r.ID, &r.MID, &r.TaskID, &r.TaskGroupID, &r.TaskType, &r.State, &r.CTime, &r.MTime); err != nil {
log.Error("UserTasks rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
return
}

View File

@@ -0,0 +1,200 @@
package newcomer
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestNewcomerUserTasks(t *testing.T) {
convey.Convey("UserTasks", t, func(ctx convey.C) {
var (
c = context.Background()
index = ""
id = int64(0)
limit = int(100)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.UserTasks(c, index, id, limit)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomergetTableName(t *testing.T) {
convey.Convey("getTableName", t, func(ctx convey.C) {
var (
mid = int64(27515405)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := getTableName(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldEqual, p1)
})
})
})
}
func TestNewcomerUpUserTask(t *testing.T) {
convey.Convey("UpUserTask", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
tid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.UpUserTask(c, mid, tid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerUserTasksByMID(t *testing.T) {
convey.Convey("UserTasksByMID", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.UserTasksByMID(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerTaskByTID(t *testing.T) {
convey.Convey("TaskByTID", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
tid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.TaskByTID(c, mid, tid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerTasks(t *testing.T) {
convey.Convey("Tasks", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.Tasks(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerCheckTaskComplete(t *testing.T) {
convey.Convey("CheckTaskComplete", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
tid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res := d.CheckTaskComplete(c, mid, tid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(res, convey.ShouldEqual, res)
})
})
})
}
func TestNewcomerUserTasksNotify(t *testing.T) {
convey.Convey("UserTasksNotify", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
index = "1"
start = time.Now().Format("2006-01-02 15:04:05")
end = time.Now().Format("2006-01-02 15:04:05")
limit = 100
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.UserTasksNotify(c, index, id, start, end, limit)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerGiftRewardCount(t *testing.T) {
convey.Convey("GiftRewardCount", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.GiftRewardCount(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerBaseRewardCount(t *testing.T) {
convey.Convey("BaseRewardCount", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.BaseRewardCount(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerCheckTasksForRewardNotify(t *testing.T) {
convey.Convey("CheckTasksForRewardNotify", t, func(ctx convey.C) {
var (
c = context.Background()
id = int64(0)
index = "1"
start = time.Now()
end = time.Now()
limit = 100
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.CheckTasksForRewardNotify(c, index, id, start, end, limit)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestNewcomerUserTasksByMIDAndState(t *testing.T) {
convey.Convey("UserTasksByMIDAndState", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(27515405)
state = 0
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.UserTasksByMIDAndState(c, mid, state)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}

View File

@@ -0,0 +1,79 @@
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",
"hbase.go",
"httpclient.go",
"infoc.go",
"monkey.go",
"mysql.go",
"rpc.go",
],
importpath = "go-common/app/job/main/creative/dao/weeklyhonor",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/up/util/hbaseutil:go_default_library",
"//app/interface/main/creative/model/weeklyhonor:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/service/main/archive/api/gorpc:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/bouk/monkey: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"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"hbase_test.go",
"httpclient_test.go",
"infoc_test.go",
"mysql_test.go",
"rpc_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/interface/main/creative/model/weeklyhonor:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//vendor/github.com/bouk/monkey:go_default_library",
"//vendor/github.com/golang/mock/gomock:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,63 @@
package weeklyhonor
import (
"context"
"time"
"go-common/app/job/main/creative/conf"
arcrpc "go-common/app/service/main/archive/api/gorpc"
upgrpc "go-common/app/service/main/up/api/v1"
"go-common/library/database/hbase.v2"
"go-common/library/database/sql"
binfoc "go-common/library/log/infoc"
bm "go-common/library/net/http/blademaster"
)
// Dao is creative dao.
type Dao struct {
// config
c *conf.Config
// db
db *sql.DB
// httpClient
httpClient *bm.Client
// hbase
hbase *hbase.Client
hbaseTimeOut time.Duration
// rpc
arc *arcrpc.Service2
infoc *binfoc.Infoc
// grpc
upClient upgrpc.UpClient
}
// New init api url
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
db: sql.NewMySQL(c.DB.Creative),
httpClient: bm.NewClient(c.HTTPClient.Normal),
// hbase
hbase: hbase.NewClient(c.HBaseOld.Config),
hbaseTimeOut: time.Duration(time.Millisecond * 200),
arc: arcrpc.New2(c.ArchiveRPC),
infoc: binfoc.New(c.WeeklyHonorInfoc),
}
var err error
d.upClient, err = upgrpc.NewClient(c.UpGRPCClient)
if err != nil {
panic(err)
}
return
}
// Ping creativeDb
func (d *Dao) Ping(c context.Context) (err error) {
return d.pingMySQL(c)
}
// Close creativeDb
func (d *Dao) Close() (err error) {
_ = d.infoc.Close()
return d.db.Close()
}

View File

@@ -0,0 +1,54 @@
package weeklyhonor
import (
"flag"
"os"
"strings"
"testing"
"go-common/app/job/main/creative/conf"
"github.com/golang/mock/gomock"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.archive.creative-job")
flag.Set("conf_token", "43943fda0bb311e8865c66d44b23cda7")
flag.Set("tree_id", "16037")
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/creative-job.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
d.httpClient.SetTransport(gock.DefaultTransport)
return r
}
func WithMock(t *testing.T, f func(controller *gomock.Controller)) func() {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
return func() {
f(ctrl)
}
}

View File

@@ -0,0 +1,63 @@
package weeklyhonor
import (
"context"
"strconv"
"go-common/app/admin/main/up/util/hbaseutil"
model "go-common/app/interface/main/creative/model/weeklyhonor"
"go-common/library/ecode"
"go-common/library/log"
"github.com/tsuna/gohbase/hrpc"
)
// reverse for string.
func reverseString(s string) string {
rs := []rune(s)
l := len(rs)
for f, t := 0, l-1; f < t; f, t = f+1, t-1 {
rs[f], rs[t] = rs[t], rs[f]
}
ns := string(rs)
if l < 10 {
for i := 0; i < 10-l; i++ {
ns = ns + "0"
}
}
return ns
}
func honorRowKey(id int64, date string) string {
idStr := strconv.FormatInt(id, 10)
s := reverseString(idStr) + date
return s
}
// HonorStat get up honor.
func (d *Dao) HonorStat(c context.Context, mid int64, date string) (stat *model.HonorStat, err error) {
var (
result *hrpc.Result
ctx, cancel = context.WithTimeout(c, d.hbaseTimeOut)
tableName = "up_honorary_weekly"
key = honorRowKey(mid, date)
)
defer cancel()
if result, err = d.hbase.GetStr(ctx, tableName, key); err != nil {
log.Error("HonorStat d.hbase.GetStr tableName(%s) mid(%d) key(%v) error(%v)", tableName, mid, key, err)
err = ecode.CreativeDataErr
return
}
if result == nil {
return
}
stat = new(model.HonorStat)
parser := hbaseutil.Parser{}
err = parser.Parse(result.Cells, &stat)
if err != nil {
log.Error("failed to parse hbase cells,tableName(%s) mid(%d) key(%v) stat(%+v) err(%v)", tableName, mid, key, stat, err)
return
}
log.Info("HonorStat d.hbase.GetStr tableName(%s) mid(%d) key(%v) stat(%+v)", tableName, mid, key, stat)
return
}

View File

@@ -0,0 +1,130 @@
package weeklyhonor
import (
"bytes"
"context"
"encoding/binary"
"reflect"
"testing"
"go-common/library/database/hbase.v2"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
"github.com/tsuna/gohbase/hrpc"
)
func TestWeeklyhonorreverseString(t *testing.T) {
convey.Convey("reverseString", t, func(ctx convey.C) {
var (
s = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := reverseString(s)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldEqual, p1)
})
})
})
}
func TestWeeklyhonorhonorRowKey(t *testing.T) {
convey.Convey("honorRowKey", t, func(ctx convey.C) {
var (
id = int64(0)
date = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := honorRowKey(id, date)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldEqual, p1)
})
})
})
}
func TestWeeklyhonorHonorStat(t *testing.T) {
var (
c = context.Background()
mid = int64(0)
date = "20181112"
val1 int32 = 500
val2 int32 = -2
cs = getMockCells(val1, val2)
)
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.hbase), "GetStr", func(_ *hbase.Client, _ context.Context, _, _ string, _ ...func(hrpc.Call) error) (*hrpc.Result, error) {
res := &hrpc.Result{
Cells: cs,
}
return res, nil
})
defer guard.Unpatch()
convey.Convey("honorStat", t, func() {
stat, err := d.HonorStat(c, mid, date)
convey.So(err, convey.ShouldBeNil)
convey.So(stat, convey.ShouldNotBeNil)
convey.So(stat.Play, convey.ShouldEqual, val1)
convey.So(stat.PlayInc, convey.ShouldEqual, val1)
convey.So(stat.Rank0, convey.ShouldEqual, val1)
convey.So(stat.FansInc, convey.ShouldEqual, val2)
})
}
func getMockCells(v1, v2 int32) []*hrpc.Cell {
s1 := make([]byte, 0)
buf1 := bytes.NewBuffer(s1)
s2 := make([]byte, 0)
buf2 := bytes.NewBuffer(s2)
binary.Write(buf1, binary.BigEndian, v1)
binary.Write(buf2, binary.BigEndian, v2)
cells := []*hrpc.Cell{
{
Qualifier: []byte("play"),
Value: buf1.Bytes(),
Family: []byte("f"),
},
{
Qualifier: []byte("play_last_w"),
Value: buf1.Bytes(),
Family: []byte("f"),
},
{
Qualifier: []byte("play_inc"),
Value: buf1.Bytes(),
Family: []byte("f"),
},
{
Qualifier: []byte("fans_inc"),
Value: buf2.Bytes(),
Family: []byte("f"),
},
{
Qualifier: []byte("rank168"),
Value: buf1.Bytes(),
Family: []byte("r"),
},
{
Qualifier: []byte("rank0"),
Value: buf1.Bytes(),
Family: []byte("r"),
},
{
Qualifier: []byte("rank1"),
Value: buf1.Bytes(),
Family: []byte("r"),
},
{
Qualifier: []byte("rank3"),
Value: buf1.Bytes(),
Family: []byte("r"),
},
{
Qualifier: []byte("rank4"),
Value: buf1.Bytes(),
Family: []byte("r"),
},
}
return cells
}

View File

@@ -0,0 +1,110 @@
package weeklyhonor
import (
"context"
"errors"
"fmt"
"net/url"
"time"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_title = "叮!你有一份荣誉周报待查收!"
_newYearTitle = "嘭~~~啪!你有一份荣誉周报待查收!"
_content = "这周的成就已达成,快进来瞅瞅吧~ #{周报传送门}{\"https://member.bilibili.com/studio/annyroal/upper-honor-weekly/my\"}"
_newYearContent = "这周的成就已达成,元气满满过新年~ #{周报传送门}{\"https://member.bilibili.com/studio/annyroal/upper-honor-weekly/my\"}"
_notifyDataType = "4" // 消息类型1、回复我的2、@我3、收到的爱4、业务通知
_notifyURL = "/api/notify/send.user.notify.do"
_notifyMC = "1_17_1"
_upMidsURL = "/x/internal/uper/list_up"
)
type newYear struct {
start, end time.Time
}
var (
ny = newYear{
start: time.Date(2019, 2, 3, 0, 0, 0, 0, time.Local),
end: time.Date(2019, 2, 24, 0, 0, 0, 0, time.Local),
}
)
type msgReply struct {
Code int `json:"code"`
Data *data `json:"data"`
}
type data struct {
TotalCount int `json:"total_count"`
ErrorCount int `json:"error_count"`
ErrorMidList []int64 `json:"error_mid_list"`
}
// SendNotify 发送站内信
func (d *Dao) SendNotify(c context.Context, mids []int64) (errMids []int64, err error) {
title, content := getTitleAndContent()
res := msgReply{}
params := url.Values{}
params.Set("mc", _notifyMC)
params.Set("title", title)
params.Set("data_type", _notifyDataType)
params.Set("context", content)
params.Set("mid_list", xstr.JoinInts(mids))
notifyURI := d.c.Host.Message + _notifyURL
if err = d.httpClient.Post(c, notifyURI, "", params, &res); err != nil {
log.Error("d.httpClient.Post(%s,%v,%d)", notifyURI, params, err)
return
}
if res.Code != 0 {
err = errors.New("code != 0")
log.Error("d.httpClient.Post(%s,%v,%v,%d)", notifyURI, params, err, res.Code)
}
if res.Data != nil {
errMids = res.Data.ErrorMidList
log.Info("SendNotify log total_count(%d) error_count(%d) error_mid_list(%v)", res.Data.TotalCount, res.Data.ErrorCount, res.Data.ErrorMidList)
}
return
}
func getTitleAndContent() (string, string) {
now := time.Now()
if now.After(ny.end) || now.Before(ny.start) {
return _title, _content
}
return _newYearTitle, _newYearContent
}
// Deprecated: use UpActivesList instead.
func (d *Dao) UpMids(c context.Context, size int, lastid int64, activeOnly bool) (mids []int64, newid int64, err error) {
res := &struct {
Code int `json:"code"`
Data *struct {
Result []int64 `json:"result"`
LastID int64 `json:"last_id"`
} `json:"data"`
}{}
params := url.Values{}
params.Set("size", fmt.Sprintf("%d", size))
params.Set("last_id", fmt.Sprintf("%d", lastid))
if activeOnly {
// filter by having archive within 180 days
params.Set("activity", "1,2,3")
}
midsURI := d.c.Host.API + _upMidsURL
if err = d.httpClient.Get(c, midsURI, "", params, &res); err != nil {
log.Error("d.httpClient.Get(%s,%v,%d)", midsURI+"?"+params.Encode(), params, err)
return
}
if res.Code != 0 {
err = errors.New("code != 0")
log.Error("d.httpClient.Get(%s,%v,%v,%d)", midsURI, params, err, res.Code)
}
if res != nil && res.Data != nil {
return res.Data.Result, res.Data.LastID, nil
}
return
}

View File

@@ -0,0 +1,45 @@
package weeklyhonor
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
func TestWeeklyhonorSendNotify(t *testing.T) {
convey.Convey("SendNotify", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{11}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
defer gock.Off()
httpMock("POST", d.c.Host.Message+_notifyURL).Reply(200).JSON(`{"code":0,"data":{}}`)
_, err := d.SendNotify(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, err)
})
})
})
}
func TestWeeklyhonorUpMids(t *testing.T) {
convey.Convey("UpMids", t, func(ctx convey.C) {
var (
c = context.Background()
size = int(0)
lastid = int64(0)
activeOnly bool
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
mids, newid, err := d.UpMids(c, size, lastid, activeOnly)
ctx.Convey("Then err should be nil.mids,newid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(newid, convey.ShouldNotBeNil)
ctx.So(mids, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,21 @@
package weeklyhonor
import (
"context"
"go-common/library/log"
"time"
)
// HonorInfoc log honor msg send status
func (d *Dao) HonorInfoc(c context.Context, mid int64, success int32) (err error) {
ctime := time.Now().Format("20060102 15:04:05")
i := map[string]interface{}{
"mid": mid,
"ctime": ctime,
"exc": "",
"success": success,
}
log.Warn("infocproc create infoc(%v)", i)
err = d.infoc.Info(mid, ctime, "", success)
return
}

View File

@@ -0,0 +1,24 @@
package weeklyhonor
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestWeeklyhonorHonorInfoc(t *testing.T) {
convey.Convey("HonorInfoc", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
success = int32(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.HonorInfoc(c, mid, success)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,52 @@
package weeklyhonor
import (
"context"
model "go-common/app/interface/main/creative/model/weeklyhonor"
upgrpc "go-common/app/service/main/up/api/v1"
"reflect"
"github.com/bouk/monkey"
)
//MockHonorStat is
func (d *Dao) MockHonorStat(stat *model.HonorStat, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "HonorStat", func(_ *Dao, _ context.Context, _ int64, _ string) (*model.HonorStat, error) {
return stat, err
})
}
//MockSendNotify is
func (d *Dao) MockSendNotify(err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "SendNotify", func(_ *Dao, _ context.Context, _ []int64) error {
return err
})
}
//MockLatestHonorLogs is
func (d *Dao) MockLatestHonorLogs(hls []*model.HonorLog, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "LatestHonorLogs", func(_ *Dao, _ context.Context, _ []int64) ([]*model.HonorLog, error) {
return hls, err
})
}
//MockClickCounts is
func (d *Dao) MockClickCounts(res map[int64]int32, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "ClickCounts", func(_ *Dao, _ context.Context, _ []int64) (map[int64]int32, error) {
return res, err
})
}
//MockUpCount is
func (d *Dao) MockUpCount(count int, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpCount", func(_ *Dao, _ context.Context, _ int64) (int, error) {
return count, err
})
}
//MockUpActivesList is
func (d *Dao) MockUpActivesList(upActives []*upgrpc.UpActivity, newid int64, err error) (guard *monkey.PatchGuard) {
return monkey.PatchInstanceMethod(reflect.TypeOf(d), "UpActivesList", func(_ *Dao, _ context.Context, _ int64, _ int) ([]*upgrpc.UpActivity, int64, error) {
return upActives, newid, err
})
}

View File

@@ -0,0 +1,107 @@
package weeklyhonor
import (
"context"
"fmt"
model "go-common/app/interface/main/creative/model/weeklyhonor"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_honorLogsSQL = `SELECT id,mid,hid,count,ctime,mtime FROM weeklyhonor WHERE mid=?`
_honorLatestLogsSQL = `SELECT id,mid,hid,mtime FROM weeklyhonor WHERE mid in (%s) and mtime >= ?`
_honorClicksSQL = `SELECT mid,count FROM weeklyhonor_click WHERE mid in (%s) and mtime >= ?`
_upsertCountSQL = `INSERT INTO weeklyhonor (mid,hid,count) VALUES (?,?,?) ON DUPLICATE KEY UPDATE count=count+1`
)
// pingMySQL check mysql connection.
func (d *Dao) pingMySQL(c context.Context) error {
return d.db.Ping(c)
}
// HonorLogs .
func (d *Dao) HonorLogs(c context.Context, mid int64) (hls map[int]*model.HonorLog, err error) {
rows, err := d.db.Query(c, _honorLogsSQL, mid)
if err != nil {
log.Error("d.db.Query(%s,%d) error(%v)", _honorLogsSQL, mid, err)
return
}
defer rows.Close()
hls = make(map[int]*model.HonorLog)
for rows.Next() {
h := new(model.HonorLog)
if err = rows.Scan(&h.ID, &h.MID, &h.HID, &h.Count, &h.CTime, &h.MTime); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
hls[h.HID] = h
}
err = rows.Err()
return
}
// LatestHonorLogs list latest honor logs by mids
func (d *Dao) LatestHonorLogs(c context.Context, mids []int64) (hls []*model.HonorLog, err error) {
latestSun := model.LatestSunday()
midsStr := xstr.JoinInts(mids)
if midsStr == "" {
return
}
sql := fmt.Sprintf(_honorLatestLogsSQL, midsStr)
rows, err := d.db.Query(c, sql, latestSun)
if err != nil {
log.Error("d.db.Query(%s,%q,%v) error(%v)", _honorLatestLogsSQL, mids, latestSun, err)
return
}
defer rows.Close()
for rows.Next() {
h := new(model.HonorLog)
if err = rows.Scan(&h.ID, &h.MID, &h.HID, &h.MTime); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
hls = append(hls, h)
}
err = rows.Err()
return
}
// UpsertCount .
func (d *Dao) UpsertCount(c context.Context, mid int64, hid int) (affected int64, err error) {
res, err := d.db.Exec(c, _upsertCountSQL, mid, hid, 1)
if err != nil {
log.Error("d.db.Exec(%s,%d,%d,%d) error(%v)", _upsertCountSQL, mid, hid, 1, err)
return
}
return res.RowsAffected()
}
// ClickCounts honor click count map
func (d *Dao) ClickCounts(c context.Context, mids []int64) (res map[int64]int32, err error) {
res = make(map[int64]int32)
twoWeekAgo := model.LatestSunday().AddDate(0, 0, -14)
midsStr := xstr.JoinInts(mids)
if midsStr == "" {
return
}
sql := fmt.Sprintf(_honorClicksSQL, midsStr)
rows, err := d.db.Query(c, sql, twoWeekAgo)
if err != nil {
log.Error("d.db.Query(%s,%v) error(%v)", sql, twoWeekAgo, err)
return
}
defer rows.Close()
for rows.Next() {
var mid, count int64
if err = rows.Scan(&mid, &count); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
if count > 0 {
res[mid] = int32(count)
}
}
return
}

View File

@@ -0,0 +1,85 @@
package weeklyhonor
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestWeeklyhonorpingMySQL(t *testing.T) {
convey.Convey("pingMySQL", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingMySQL(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestWeeklyhonorHonorLogs(t *testing.T) {
convey.Convey("HonorLogs", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(2)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.HonorLogs(c, mid)
ctx.Convey("Then err should be nil.hls should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestWeeklyhonorLatestHonorLogs(t *testing.T) {
convey.Convey("LatestHonorLogs", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.LatestHonorLogs(c, mids)
ctx.Convey("Then err should be nil.hls should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestWeeklyhonorUpsertCount(t *testing.T) {
convey.Convey("UpsertCount", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
hid = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
affected, err := d.UpsertCount(c, mid, hid)
ctx.Convey("Then err should be nil.affected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(affected, convey.ShouldNotBeNil)
})
})
})
}
func TestWeeklyhonorClickCounts(t *testing.T) {
convey.Convey("ClickCounts", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.ClickCounts(c, mids)
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)
})
})
})
}

View File

@@ -0,0 +1,53 @@
package weeklyhonor
import (
"context"
"go-common/app/service/main/archive/model/archive"
upgrpc "go-common/app/service/main/up/api/v1"
"go-common/library/ecode"
"go-common/library/log"
)
const fromWeeklyHonor = 1
// UpCount get archives count.
func (d *Dao) UpCount(c context.Context, mid int64) (count int, err error) {
var arg = &archive.ArgUpCount2{Mid: mid}
if count, err = d.arc.UpCount2(c, arg); err != nil {
log.Error("rpc UpCount2 (%v) error(%v)", mid, err)
err = ecode.CreativeArcServiceErr
}
return
}
// UpActivesList list up-actives
func (d *Dao) UpActivesList(c context.Context, lastID int64, ps int) (upActives []*upgrpc.UpActivity, newid int64, err error) {
upListReq := upgrpc.UpListByLastIDReq{
LastID: lastID,
Ps: ps,
}
reply, err := d.upClient.UpInfoActivitys(c, &upListReq)
if err != nil {
log.Error("failed to list up&active info,err(%v)", err)
return
}
newid = reply.GetLastID()
upActives = reply.GetUpActivitys()
return
}
// GetUpSwitch get up switch state
func (d *Dao) GetUpSwitch(c context.Context, mid int64) (state uint8, err error) {
req := upgrpc.UpSwitchReq{
Mid: mid,
From: fromWeeklyHonor,
}
reply, err := d.upClient.UpSwitch(c, &req)
if err != nil {
log.Error("d.upClient.UpSwitch req(%+v),err(%v)", req, err)
return
}
state = reply.GetState()
return
}

View File

@@ -0,0 +1,96 @@
package weeklyhonor
import (
"context"
"fmt"
"testing"
whmdl "go-common/app/interface/main/creative/model/weeklyhonor"
upgrpc "go-common/app/service/main/up/api/v1"
"github.com/golang/mock/gomock"
"github.com/smartystreets/goconvey/convey"
)
var c = context.Background()
func TestWeeklyhonorUpCount(t *testing.T) {
convey.Convey("UpCount", t, func(ctx convey.C) {
var (
mid = int64(1627855)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
count, err := d.UpCount(c, mid)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestWeeklyhonorUpActiveLists(t *testing.T) {
convey.Convey("UpActivesList", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", WithMock(t, func(mockCtrl *gomock.Controller) {
// mock
mockUpClient := upgrpc.NewMockUpClient(mockCtrl)
d.upClient = mockUpClient
mockReq := upgrpc.UpListByLastIDReq{
LastID: 0,
Ps: 100,
}
mockReply := upgrpc.UpActivityListReply{
UpActivitys: []*upgrpc.UpActivity{
{Mid: 1},
{Activity: 2},
},
LastID: 1,
}
mockUpClient.EXPECT().UpInfoActivitys(gomock.Any(), &mockReq).Return(&mockReply, nil)
// test
upActives, newId, err := d.UpActivesList(c, 0, 100)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(upActives[0], convey.ShouldEqual, mockReply.UpActivitys[0])
ctx.So(newId, convey.ShouldEqual, mockReply.LastID)
})
}))
})
}
func TestWeeklyhonorGetUpSwitch(t *testing.T) {
convey.Convey("GetUpSwitch", t, WithMock(t, func(mockCtrl *gomock.Controller) {
var (
c = context.Background()
mid = int64(75379101)
)
convey.Convey("When everything gose positive", func(ctx convey.C) {
mockUpClient := upgrpc.NewMockUpClient(mockCtrl)
d.upClient = mockUpClient
mockReq := upgrpc.UpSwitchReq{
Mid: mid,
From: fromWeeklyHonor,
}
mockState := whmdl.HonorUnSub
mockUpClient.EXPECT().UpSwitch(gomock.Any(), &mockReq).Return(&upgrpc.UpSwitchReply{State: mockState}, nil)
state, err := d.GetUpSwitch(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(state, convey.ShouldEqual, mockState)
})
})
convey.Convey("When return err", func(ctx convey.C) {
mockUpClient := upgrpc.NewMockUpClient(mockCtrl)
d.upClient = mockUpClient
mockReq := upgrpc.UpSwitchReq{
Mid: mid,
From: fromWeeklyHonor,
}
mockUpClient.EXPECT().UpSwitch(gomock.Any(), &mockReq).Return(nil, fmt.Errorf("mock err"))
_, err := d.GetUpSwitch(c, mid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
}))
}

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/creative/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/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,65 @@
package http
import (
"net/http"
"go-common/app/job/main/creative/conf"
"go-common/app/job/main/creative/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
//Svc service.
Svc *service.Service
)
// Init init account service.
func Init(c *conf.Config) {
// service
initService(c)
Svc.InitCron()
// init inner router
eng := bm.DefaultServer(c.BM.Outer)
innerRouter(eng)
if err := eng.Start(); err != nil {
log.Error("bm.DefaultServer error(%v)", err)
panic(err)
}
}
func initService(c *conf.Config) {
Svc = service.New(c)
}
// innerRouter init inner router.
func innerRouter(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/job/creative")
{
g.GET("/test", sendMsg)
}
}
// ping check server ok.
func ping(c *bm.Context) {
if err := Svc.Ping(c); err != nil {
log.Error("svr.Ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
func sendMsg(c *bm.Context) {
arg := new(struct {
Mids []int64 `form:"mids,split" validate:"required"`
})
if err := c.Bind(arg); err != nil {
return
}
c.JSON(Svc.TestSendMsg(c, arg.Mids), nil)
}

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 = [
"academy.go",
"archive.go",
"monitor.go",
"msg.go",
"newcomer.go",
],
importpath = "go-common/app/job/main/creative/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,15 @@
package model
const (
//BusinessForArchvie 稿件
BusinessForArchvie = 1
//BusinessForArticle 专栏
BusinessForArticle = 2
)
//OArchive for academy.
type OArchive struct {
ID int64
OID int64
Business int
}

View File

@@ -0,0 +1,14 @@
package model
//Archive for db.
type Archive struct {
AID int64 `json:"aid"`
MID int64 `json:"mid"`
State int `json:"state"`
UpFrom int8 `json:"up_from"`
}
// ArcVideo str
type ArcVideo struct {
Archive *Archive
}

View File

@@ -0,0 +1,13 @@
package model
// BAP for wechat msg.
type BAP struct {
UserName string `json:"username"`
Title string `json:"title"`
Content string `json:"content"`
URL string `json:"url"`
Ty string `json:"type"`
Token string `json:"token"`
Signature string `json:"signature"`
TimeStamp int64 `json:"timestamp"`
}

View File

@@ -0,0 +1,128 @@
package model
import "encoding/json"
//Msg for databus.
type Msg struct {
MID int64 `json:"mid"`
From int `json:"from"`
IsAuthor int `json:"is_author"`
TimeStamp int64 `json:"timestamp"`
}
// CanalMsg canal databus msg.
type CanalMsg struct {
Action string `json:"action"`
Table string `json:"table"`
New json.RawMessage `json:"new"`
Old json.RawMessage `json:"old"`
}
//TaskMsg for task notify.
type TaskMsg struct {
MID int64 `json:"mid"`
Count int64 `json:"count"`
From int `json:"from"`
TimeStamp int64 `json:"timestamp"`
}
// ShareMsg share databus msg.
type ShareMsg struct {
OID int64 `json:"oid"`
MID int64 `json:"mid"`
TP int `json:"tp"`
Time int64 `json:"time"`
}
// StatLike archive like count
type StatLike struct {
MID int64 `json:"mid"`
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
DislikeCount int64 `json:"dislike_count"`
TimeStamp int64 `json:"timestamp"`
}
// StatView ViewMsg archive view count
type StatView struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatDM archive DM count
type StatDM struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatReply archive reply count
type StatReply struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatFav archive collection count
type StatFav struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatCoin archive coin count
type StatCoin struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatShare archive share count
type StatShare struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// StatRank archive rank
type StatRank struct {
Type string `json:"type"`
ID int64 `json:"id"`
Count int64 `json:"count"`
TimeStamp int64 `json:"timestamp"`
}
// RelaMessage Message define relation binlog databus message.
type RelaMessage struct {
Action string `json:"action"`
Table string `json:"table"`
New json.RawMessage `json:"new"`
Old json.RawMessage `json:"old"`
}
// Relation user_relation_mid_0~user_relation_mid_49
type Relation struct {
MID int64 `json:"mid,omitempty"`
FID int64 `json:"fid,omitempty"`
Attribute uint32 `json:"attribute"`
Status int `json:"status"`
MTime string `json:"mtime"`
CTime string `json:"ctime"`
}
// Stat user_relation_stat
type Stat struct {
MID int64 `json:"mid,omitempty"`
Following int64 `json:"following"`
Whisper int64 `json:"whisper"`
Black int64 `json:"black"`
Follower int64 `json:"follower"`
}

View File

@@ -0,0 +1,117 @@
package model
import "time"
const (
_ int8 = iota
// TargetType001 该UID下开放浏览的稿件≥1
TargetType001
// TargetType002 该UID分享自己视频的次数≥1
TargetType002
// TargetType003 该UID在创作学院的观看记录≥1
TargetType003
// TargetType004 该UID下所有avid的获得评论数≥3
TargetType004
// TargetType005 该UID下所有avid获得分享数≥3
TargetType005
// TargetType006 该UID的所有avid的获得收藏数≥5
TargetType006
// TargetType007 该UID下所有avid的获得硬币数≥5
TargetType007
// TargetType008 该UID下所有avid获得点赞数≥5
TargetType008
// TargetType009 该UID下所有avid的获得弹幕数≥5
TargetType009
// TargetType010 该UID的粉丝数≥10
TargetType010
// TargetType011 任务完成期间该UID的水印开关为打开状态
TargetType011
// TargetType012 该UID的关注列表含有“哔哩哔哩创作中心”
TargetType012
// TargetType013 用手机投稿上传视频
TargetType013
// TargetType014 该UID下开放浏览的稿件≥5
TargetType014
// TargetType015 该UID下任意avid的获得点击量≥1000
TargetType015
// TargetType016 该UID下任意avid的评论≥30
TargetType016
// TargetType017 该UID下任意avid的获得分享数≥10
TargetType017
// TargetType018 该UID下任意avid的获得收藏数≥30
TargetType018
// TargetType019 该UID下任意avid的获得硬币数≥50
TargetType019
// TargetType020 该UID下任意avid的获得点赞数≥50
TargetType020
// TargetType021 该UID下任意avid的获得弹幕数≥50
TargetType021
// TargetType022 该UID的粉丝数≥1000
TargetType022
// TargetType023 该UID的激励计划状态为已开通
TargetType023
// TargetType024 该UID粉丝勋章为开启状态
TargetType024
)
const (
//TaskIncomplete 任务未完成
TaskIncomplete = -1
//TaskCompleted 任务已完成
TaskCompleted = 0
//MsgForWaterMark 发送用户设置水印消息
MsgForWaterMark = 1
//MsgForAcademyFavVideo 发送用户已在创作学院观看过自己喜欢的视频的消息
MsgForAcademyFavVideo = 2
//MsgForGrowAccount 发送用户已在参加激励计划的消息
MsgForGrowAccount = 3
//MsgForOpenFansMedal 成功开通粉丝勋章
MsgForOpenFansMedal = 4
)
// UserTask for def user task struct.
type UserTask struct {
ID int64 `json:"id"`
MID int64 `json:"mid"`
TaskID int64 `json:"task_id"`
TaskGroupID int64 `json:"task_group_id"`
TaskType int8 `json:"task_type"`
State int8 `json:"state"`
TaskBindTime time.Time `json:"task_bind_time"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"mtime"`
}
// Task for def task struct.
type Task struct {
ID int64 `json:"id"`
GroupID int64 `json:"-"`
Type int8 `json:"type"`
State int8 `json:"-"`
Title string `json:"title"`
Desc string `json:"desc"`
Comment string `json:"-"`
TargetType int8 `json:"-"`
TargetValue int64 `json:"-"`
CompleteSate int8 `json:"complete_state"`
CTime time.Time `json:"-"`
MTime time.Time `json:"-"`
}
// GiftReward for gift reward
type GiftReward struct {
ID int64 `json:"id"`
TaskType int8 `json:"task_type"`
RewardID int64 `json:"reward_id"`
State int8 `json:"state"`
Comment string `json:"comment"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"mtime"`
}
// Up for up new arc.
type Up struct {
AID int64
MID int64
}

View File

@@ -0,0 +1,73 @@
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",
"weeklyhonor_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/creative/model/weeklyhonor:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"academy.go",
"databus.go",
"service.go",
"task.go",
"weeklyhonor.go",
],
importpath = "go-common/app/job/main/creative/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/creative/model/weeklyhonor:go_default_library",
"//app/interface/openplatform/article/model:go_default_library",
"//app/job/main/creative/conf:go_default_library",
"//app/job/main/creative/dao/academy:go_default_library",
"//app/job/main/creative/dao/archive:go_default_library",
"//app/job/main/creative/dao/monitor:go_default_library",
"//app/job/main/creative/dao/newcomer:go_default_library",
"//app/job/main/creative/dao/weeklyhonor:go_default_library",
"//app/job/main/creative/model:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/up/api/v1:go_default_library",
"//library/conf/env:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr: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,115 @@
package service
import (
"context"
"math"
"time"
"go-common/app/interface/openplatform/article/model"
"go-common/app/service/main/archive/api"
"go-common/library/log"
)
//FlushHot for compute archive hot value by time.
func (s *Service) FlushHot(bs int) {
var (
c = context.TODO()
id int64
limit = 30
)
for {
arcs, err := s.aca.Archives(c, id, bs, limit)
if err != nil {
log.Error("s.aca.Archives id(%d) error(%v)", id, err)
return
}
count := len(arcs)
if count == 0 {
id = 0 //重新按定时跑数据
time.Sleep(time.Hour * 1)
continue
}
oids := make([]int64, 0)
for _, a := range arcs {
oids = append(oids, a.OID)
id = a.ID //标记当前处理到的id
}
hots, err := s.computeHotByOIDs(c, oids, bs)
if err != nil {
log.Error("s.aca.Archives id(%d) error(%v)", id, err)
return
}
if s.aca.UPHotByAIDs(c, hots) != nil {
log.Error("s.aca.UPHotByAIDs hots(%+v) error(%v)", hots, err)
return
}
}
}
func (s *Service) computeHotByOIDs(c context.Context, oids []int64, bs int) (res map[int64]int64, err error) {
res = make(map[int64]int64)
if bs == 1 {
arcs, err := s.arc.Archives(c, oids)
if err != nil {
return nil, err
}
stat, err := s.arc.Stats(c, oids)
if err != nil {
log.Error("s.arc.Stats oids(%+v)|business(%d)|error(%v)", oids, bs, err)
return nil, err
}
for _, oid := range oids {
if v, ok := arcs[oid]; ok && v != nil {
if t, ok := stat[oid]; ok && t != nil {
res[oid] = countArcHot(t, int64(v.PubDate))
}
}
}
} else if bs == 2 {
arts, err := s.arc.ArticleMetas(c, oids)
if err != nil {
return nil, err
}
for _, oid := range oids {
if v, ok := arts[oid]; ok && v != nil {
res[oid] = countArtHot(v)
}
}
}
return
}
//countArcHot 视频=硬币*0.4+收藏*0.3+弹幕*0.4+评论*0.4+播放*0.25+点赞*0.4+分享*0.6 最新视频(一天内发布)提权[总值*1.5]
func countArcHot(t *api.Stat, ptime int64) int64 {
if t == nil {
return 0
}
hot := float64(t.Coin)*0.4 +
float64(t.Fav)*0.3 +
float64(t.Danmaku)*0.4 +
float64(t.Reply)*0.4 +
float64(t.View)*0.25 +
float64(t.Like)*0.4 +
float64(t.Share)*0.6
if ptime >= time.Now().AddDate(0, 0, -1).Unix() && ptime <= time.Now().Unix() {
hot *= 1.5
}
return int64(math.Floor(hot))
}
// countArtHot 专栏=硬币*0.4+收藏*0.3+评论*0.4+阅读*0.25+点赞*0.4+分享*0.6 最新专栏(一天内发布)提权[总值*1.5]
func countArtHot(t *model.Meta) int64 {
if t.Stats == nil {
return 0
}
hot := float64(t.Stats.Coin)*0.4 +
float64(t.Stats.Favorite)*0.3 +
float64(t.Stats.Reply)*0.4 +
float64(t.Stats.View)*0.25 +
float64(t.Stats.Like)*0.4 +
float64(t.Stats.Share)*0.6
if int64(t.PublishTime) >= time.Now().AddDate(0, 0, -1).Unix() && int64(t.PublishTime) <= time.Now().Unix() {
hot *= 1.5
}
return int64(math.Floor(hot))
}

View File

@@ -0,0 +1,479 @@
package service
import (
"context"
"encoding/json"
"strconv"
"strings"
"time"
"go-common/app/job/main/creative/model"
"go-common/library/log"
)
const (
_archive = "archive"
_insert = "insert"
_update = "update"
fromArchiveUp = 0
fromArchiveNewUp = 1
_relationMidTable = "user_relation_mid_"
_relationStatTable = "user_relation_stat_"
)
//pub up auth msg.
func (s *Service) pub(mid int64, from, isAuthor int) (err error) {
c := context.TODO()
msg := &model.Msg{
MID: mid,
From: from,
IsAuthor: isAuthor,
TimeStamp: time.Now().Unix(),
}
if err = s.upPub.Send(c, strconv.FormatInt(mid, 10), msg); err != nil {
log.Error("pub mid(%d) error(%v)", mid, err)
}
return
}
func (s *Service) arcNotifyCanalConsume() {
var err error
for msg := range s.arcNotifySub.Messages() {
msg.Commit()
s.arcNotifyMo++
m := &model.CanalMsg{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if m.Table == _archive {
s.arcNotifyMsg(m)
log.Info("arcNotifyCanalConsume key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
}
}
s.wg.Done()
}
func (s *Service) arcNotifyMsg(m *model.CanalMsg) {
var (
err error
newArc = &model.Archive{}
oldArc = &model.Archive{}
)
if err = json.Unmarshal(m.New, newArc); err != nil {
log.Error("arcNotifyMsg newMsg json.Unmarshal(%s) error(%v)", string(m.New), err)
return
}
mid := newArc.MID
if mid <= 0 {
log.Error("arcNotifyMsg mid (%d) error", mid)
return
}
if m.Action == _insert && newArc.State >= 0 { //0->1
s.pub(mid, fromArchiveUp, 1)
} else if m.Action == _update {
if err = json.Unmarshal(m.Old, oldArc); err != nil {
log.Error("arcNotifyMsg oldMsg json.Unmarshal(%s) error(%v)", string(m.Old), err)
return
}
if oldArc.State < 0 && (newArc.State >= 0 || newArc.State == -6) { //0->1
s.pub(mid, fromArchiveUp, 1)
}
if (oldArc.State >= 0 || oldArc.State == -6) && newArc.State < 0 { //1->0
cnt, err := s.arc.UpCount(context.Background(), mid)
if err != nil {
log.Error("arcNotifyMsg s.arc.UpCount(%d) error(%v)", mid, err)
return
}
if cnt <= 0 {
s.pub(mid, fromArchiveUp, 0)
}
}
}
}
func (s *Service) arcCanalConsume() {
var err error
for msg := range s.arcSub.Messages() {
msg.Commit()
s.arcMo++
m := &model.CanalMsg{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if m.Table == _archive && m.Action == _insert {
arc := &model.Archive{}
if err = json.Unmarshal(m.New, arc); err != nil {
log.Error("creative-job binglog newMsg json.Unmarshal(%s) error(%v)", m.New, err)
continue
}
if arc.MID > 0 {
s.pub(arc.MID, fromArchiveNewUp, 1)
}
log.Info("arcCanalConsume key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
}
}
s.wg.Done()
}
func (s *Service) task() {
for msg := range s.taskSub.Messages() {
msg.Commit()
log.Info("databus task key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.taskSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- msg
}
for _, c := range s.taskSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) share() {
for msg := range s.shareSub.Messages() {
msg.Commit()
m := &model.ShareMsg{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus share json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if m.TP != 3 {
continue
}
mid, authorMID := m.MID, s.getMIDByAID(m.OID)
if mid != authorMID { //不是该用户分享的稿件则不做任何处理
log.Warn("s.arc.Archive mid(%d)|author mid(%d)", mid, authorMID)
continue
}
log.Info("databus share key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.shareSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
for _, c := range s.shareSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) relation() {
var err error
newF, advancedF := s.c.Task.NewFollower, s.c.Task.AdvancedFollower
for msg := range s.relationSub.Messages() {
msg.Commit()
rl := &model.RelaMessage{}
if err = json.Unmarshal(msg.Value, rl); err != nil {
log.Error("databus relation json.Unmarshal (%v) error(%v)", msg.Value, err)
continue
}
if !strings.HasPrefix(rl.Table, _relationStatTable) && !strings.HasPrefix(rl.Table, _relationMidTable) {
continue
}
if strings.HasPrefix(rl.Table, _relationStatTable) {
ost := &model.Stat{}
st := &model.Stat{}
if rl.Action == "update" {
if err = json.Unmarshal(rl.Old, ost); err != nil {
log.Error("relation old msg json.Unmarshal(%s) error(%v)", string(rl.Old), err)
continue
}
}
if err = json.Unmarshal(rl.New, st); err != nil {
log.Error("relation new msg json.Unmarshal(%s) error(%v)", string(rl.New), err)
continue
}
isFollower := false
if ost.Follower < newF && st.Follower >= newF { //新手任务粉丝数限制
isFollower = true
}
if ost.Follower < advancedF && st.Follower >= advancedF { //进阶任务粉丝数限制
isFollower = true
}
if isFollower {
log.Info("databus relation key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.followerQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- st
}
}
if strings.HasPrefix(rl.Table, _relationMidTable) {
fl := &model.Relation{}
if err = json.Unmarshal(rl.New, fl); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", string(rl.New), err)
continue
}
if fl.FID != s.c.Task.BiliMID { //过滤关注哔哩哔哩创作中心
continue
}
log.Info("databus relation key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.relationQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- fl
}
}
for _, c := range s.relationQueue {
close(c)
}
for _, c := range s.followerQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statView() {
statView, statViewUp := s.c.Task.StatView, s.c.Task.StatViewUp
for msg := range s.statViewSub.Messages() {
msg.Commit()
m := &model.StatView{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statView json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") {
continue
}
if m.Count >= statView && m.Count <= statViewUp {
s.statViewSubQueue[s.shardingQueueIndex(msg.Key, s.statViewQueueLen)] <- m
}
}
for _, c := range s.statViewSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statLike() {
statLike, statLikeUp := s.c.Task.StatLike, s.c.Task.StatLikeUp
for msg := range s.statLikeSub.Messages() {
msg.Commit()
m := &model.StatLike{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statLike json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") {
continue
}
if m.Count >= statLike && m.Count <= statLikeUp {
s.statLikeSubQueue[s.shardingQueueIndex(msg.Key, s.statLikeQueueLen)] <- m
}
}
for _, c := range s.statLikeSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statShare() {
statShare, statShareUp := s.c.Task.StatShare, s.c.Task.StatShareUp
for msg := range s.statShareSub.Messages() {
msg.Commit()
m := &model.StatShare{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statShare json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") {
continue
}
if m.Count >= statShare && m.Count <= statShareUp {
log.Info("databus statShare key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.statShareSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
}
for _, c := range s.statShareSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statCoin() {
statCoin, statCoinUp := s.c.Task.StatCoin, s.c.Task.StatCoinUp
for msg := range s.statCoinSub.Messages() {
msg.Commit()
m := &model.StatCoin{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statCoin json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") {
continue
}
if m.Count >= statCoin && m.Count <= statCoinUp {
log.Info("databus statCoin key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.statCoinSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
}
for _, c := range s.statCoinSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statFav() {
statFav, statFavUp := s.c.Task.StatFav, s.c.Task.StatFavUp
for msg := range s.statFavSub.Messages() {
msg.Commit()
m := &model.StatFav{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statFav json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") {
continue
}
if m.Count >= statFav && m.Count <= statFavUp {
log.Info("databus statFav key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.statFavSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
}
for _, c := range s.statFavSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statReply() {
statReply, statReplyUp := s.c.Task.StatReply, s.c.Task.StatReplyUp
for msg := range s.statReplySub.Messages() {
msg.Commit()
m := &model.StatReply{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statReply json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") || m.Count < s.c.Task.StatReply {
continue
}
if m.Count >= statReply && m.Count <= statReplyUp {
log.Info("databus statReply key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.statReplySubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
}
for _, c := range s.statReplySubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) statDM() {
statDM, statDMUp := s.c.Task.StatDM, s.c.Task.StatDMUp
for msg := range s.statDMSub.Messages() {
msg.Commit()
m := &model.StatDM{}
if err := json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus statDM json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if !strings.EqualFold(m.Type, "archive") || m.Count < s.c.Task.StatDM {
continue
}
if m.Count >= statDM && m.Count <= statDMUp {
log.Info("databus statDM key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
s.statDMSubQueue[s.shardingQueueIndex(msg.Key, s.databusQueueLen)] <- m
}
}
for _, c := range s.statDMSubQueue {
close(c)
}
s.wg.Done()
}
func (s *Service) newUp() {
var err error
for msg := range s.newUpSub.Messages() {
msg.Commit()
m := &model.CanalMsg{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("databus newUp json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
if m.Table != _archive {
continue
}
newArc := &model.Archive{}
if err = json.Unmarshal(m.New, newArc); err != nil {
log.Error("databus newUp newMsg json.Unmarshal(%s) error(%v)", string(m.New), err)
continue
}
mid, aid := newArc.MID, newArc.AID
if mid == 0 || aid == 0 {
log.Error("databus newUp mid(%d) | aid(%d) error", mid, aid)
continue
}
isUp := false
if m.Action == "insert" && newArc.State >= 0 {
isUp = true
} else if m.Action == "update" {
oldArc := &model.Archive{}
if err = json.Unmarshal(m.Old, oldArc); err != nil {
log.Error("newUp oldMsg json.Unmarshal(%s) error(%v)", string(m.Old), err)
continue
}
if oldArc.State < 0 && (newArc.State >= 0 || newArc.State == -6) { //0->1
isUp = true
}
}
if isUp {
av, err := s.arc.View(context.Background(), mid, aid) //获取投稿来源
if err != nil {
log.Error("newUp s.arc.View mid(%d) mid(%d) av(%+v) error(%v)", mid, aid, av, err)
}
if av != nil && av.Archive != nil && (av.Archive.UpFrom == 3 || av.Archive.UpFrom == 8 || av.Archive.UpFrom == 9) { // 3-App , 8-Android , 9-IOS
log.Info("databus mobile mid(%d) aid(%d) av(%+v)", mid, aid, av)
s.mobileUpQueue[s.shardingQueueIndex(strconv.FormatInt(mid, 10), s.databusQueueLen)] <- &model.Up{AID: aid, MID: mid}
}
cnt, err := s.arc.UpCount(context.Background(), mid)
if err != nil {
log.Error("newUp s.arc.UpCount(%d) error(%v)", mid, err)
continue
}
if cnt == 1 { //新手投下自己的第一个稿件
log.Info("databus newUp mid(%d) aid(%d) count(%d)", mid, aid, cnt)
s.newUpQueue[s.shardingQueueIndex(strconv.FormatInt(mid, 10), s.databusQueueLen)] <- &model.Up{AID: aid, MID: mid}
}
if cnt >= 5 { //进阶任务视频投稿超过5个
log.Info("databus oldUp mid(%d) aid(%d) count(%d)", mid, aid, cnt)
s.oldUpQueue[s.shardingQueueIndex(strconv.FormatInt(mid, 10), s.databusQueueLen)] <- &model.Up{AID: aid, MID: mid}
}
}
}
for _, c := range s.newUpQueue {
close(c)
}
for _, c := range s.oldUpQueue {
close(c)
}
for _, c := range s.mobileUpQueue {
close(c)
}
s.wg.Done()
}

View File

@@ -0,0 +1,259 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/job/main/creative/conf"
"go-common/app/job/main/creative/dao/academy"
"go-common/app/job/main/creative/dao/archive"
"go-common/app/job/main/creative/dao/monitor"
"go-common/app/job/main/creative/dao/newcomer"
"go-common/app/job/main/creative/dao/weeklyhonor"
"go-common/app/job/main/creative/model"
"go-common/library/conf/env"
"go-common/library/queue/databus"
"go-common/library/xstr"
"github.com/robfig/cron"
)
// Service is service.
type Service struct {
c *conf.Config
//arc databus
arcSub *databus.Databus
arcNotifySub *databus.Databus
upPub *databus.Databus
// wait group
wg sync.WaitGroup
// monitor
monitor *monitor.Dao
arc *archive.Dao
arcNotifyMo int64
arcMo int64
// chan for mids
midsChan chan map[int64]int
//aca
aca *academy.Dao
// honDao
honDao *weeklyhonor.Dao
//task databus
newc *newcomer.Dao
taskSub, shareSub, relationSub, statLikeSub *databus.Databus
statShareSub, statCoinSub, statFavSub *databus.Databus
statReplySub, statDMSub, statViewSub, newUpSub *databus.Databus
taskSubQueue []chan *databus.Message
shareSubQueue []chan *model.ShareMsg
relationQueue []chan *model.Relation //用户关注队列
followerQueue []chan *model.Stat //粉丝数队列
newUpQueue []chan *model.Up //新投稿
oldUpQueue []chan *model.Up //进阶任务视频投稿超过5个
mobileUpQueue []chan *model.Up //手机投稿
databusQueueLen int //消费databus 队列长度
statViewQueueLen int //播放消费databus 队列长度
statLikeQueueLen int //点赞消费databus 队列长度
chanSize int //chan 缓冲长度
//单个稿件计数
statViewSubQueue []chan *model.StatView
statLikeSubQueue []chan *model.StatLike
statShareSubQueue []chan *model.StatShare
statCoinSubQueue []chan *model.StatCoin
statFavSubQueue []chan *model.StatFav
statReplySubQueue []chan *model.StatReply
statDMSubQueue []chan *model.StatDM
//db
taskQueue []chan []*model.UserTask
TaskCache []*model.Task
TaskMapCache map[int64]*model.Task
GiftRewardCache map[int8][]*model.GiftReward //gift-reward
//notify
taskNotifyQueue []chan []int64
rewardNotifyQueue []chan []int64
testNotifyMids map[int64]struct{}
}
// New is go-common/app/service/videoup service implementation.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
arcSub: databus.New(c.ArcSub),
arcNotifySub: databus.New(c.ArcNotifySub),
upPub: databus.New(c.UpPub),
monitor: monitor.New(c),
midsChan: make(chan map[int64]int, c.ChanSize),
arc: archive.New(c),
aca: academy.New(c),
honDao: weeklyhonor.New(c),
//task
newc: newcomer.New(c),
taskQueue: make([]chan []*model.UserTask, c.Task.TableConsumeNum),
TaskMapCache: make(map[int64]*model.Task),
GiftRewardCache: make(map[int8][]*model.GiftReward),
//notify
taskNotifyQueue: make([]chan []int64, c.Task.TaskTableConsumeNum),
rewardNotifyQueue: make([]chan []int64, c.Task.RewardTableConsumeNum),
testNotifyMids: make(map[int64]struct{}),
chanSize: c.Task.ChanSize,
}
s.newTaskDatabus()
if c.Consume {
s.wg.Add(1)
go s.arcCanalConsume()
s.wg.Add(1)
go s.arcNotifyCanalConsume()
go s.monitorConsume()
}
if c.HotSwitch {
go s.FlushHot(model.BusinessForArchvie) //计算视频稿件的hot
go s.FlushHot(model.BusinessForArticle) //计算专栏稿件的hot
}
s.taskConsume()
return
}
// InitCron init cron
func (s *Service) InitCron() {
c := cron.New()
if s.c.HonorSwitch {
c.AddFunc(s.c.HonorFlushSpec, s.FlushHonor)
c.AddFunc(s.c.HonorMSGSpec, s.SendMsg)
}
c.Start()
}
func (s *Service) newTaskDatabus() {
s.databusQueueLen = s.c.Task.DatabusQueueLen
s.statViewQueueLen = s.c.Task.StatViewQueueLen //播放
s.statLikeQueueLen = s.c.Task.StatLikeQueueLen //点赞
s.taskSub = databus.New(s.c.TaskSub)
s.shareSub = databus.New(s.c.ShareSub) //分享自己的稿件
s.relationSub = databus.New(s.c.RelationSub)
s.newUpSub = databus.New(s.c.NewUpSub) //新投稿up主
//单个稿件计数
s.statViewSub = databus.New(s.c.StatViewSub)
s.statLikeSub = databus.New(s.c.StatLikeSub)
s.statShareSub = databus.New(s.c.StatShareSub) //计数分享
s.statCoinSub = databus.New(s.c.StatCoinSub)
s.statFavSub = databus.New(s.c.StatFavSub)
s.statReplySub = databus.New(s.c.StatReplySub)
s.statDMSub = databus.New(s.c.StatDMSub)
s.taskSubQueue = make([]chan *databus.Message, s.databusQueueLen) //设置水印、观看创作学院视频、参加激励计划、开通粉丝勋章
s.shareSubQueue = make([]chan *model.ShareMsg, s.databusQueueLen) //分享自己的稿件
s.relationQueue = make([]chan *model.Relation, s.databusQueueLen) //用户关注队列
s.followerQueue = make([]chan *model.Stat, s.databusQueueLen) //粉丝数队列
s.newUpQueue = make([]chan *model.Up, s.databusQueueLen) //新投稿up主
s.oldUpQueue = make([]chan *model.Up, s.databusQueueLen) //进阶任务视频投稿超过5个
s.mobileUpQueue = make([]chan *model.Up, s.databusQueueLen) //进阶任务手机投稿
//单个稿件计数
s.statViewSubQueue = make([]chan *model.StatView, s.statViewQueueLen)
s.statLikeSubQueue = make([]chan *model.StatLike, s.statLikeQueueLen)
s.statShareSubQueue = make([]chan *model.StatShare, s.databusQueueLen)
s.statCoinSubQueue = make([]chan *model.StatCoin, s.databusQueueLen)
s.statFavSubQueue = make([]chan *model.StatFav, s.databusQueueLen)
s.statReplySubQueue = make([]chan *model.StatReply, s.databusQueueLen)
s.statDMSubQueue = make([]chan *model.StatDM, s.databusQueueLen)
}
// TaskClose close task sub.
func (s *Service) TaskClose() {
s.taskSub.Close() //水印、激励、观看创作学院、开通粉丝勋章
s.shareSub.Close() //个人稿件分享
s.relationSub.Close() //关注哔哩哔哩创组中心,新手和进阶粉丝数
s.newUpSub.Close() //投第一个稿件
//计数
s.statViewSub.Close()
s.statLikeSub.Close()
s.statShareSub.Close()
s.statCoinSub.Close()
s.statFavSub.Close()
s.statReplySub.Close()
s.statDMSub.Close()
}
func (s *Service) taskConsume() {
s.loadTasks() //定时缓存所有任务
s.loadGiftRewards() //定时缓存所有奖励
go s.loadProc()
//非实时任务状态变更
s.initTaskQueue()
go s.commitTask()
//过期任务通知
if s.c.Task.SwitchMsgNotify {
mids, _ := xstr.SplitInts(s.c.Task.TestNotifyMids)
for _, mid := range mids {
s.testNotifyMids[mid] = struct{}{} //test mids
}
s.initTaskNotifyQueue()
go s.expireTaskNotify()
//奖励领取通知
s.initRewardNotifyQueue()
go s.rewardReceiveNotify()
}
//实时任务状态变更
if s.c.Task.SwitchHighQPS { //消息qps 较高的消费
s.initStatViewQueue()
s.initStatLikeQueue()
s.wg.Add(2)
go s.statView() //1
go s.statLike() //2
}
if s.c.Task.SwitchDatabus { //消息qps 较少的消费
s.wg.Add(9)
s.initDatabusQueue()
go s.task() //1
go s.share() //2
go s.relation() //3
go s.statShare() //4
go s.statCoin() //5
go s.statFav() //6
go s.statReply() //7
go s.statDM() //8
go s.newUp() //9
}
}
// Ping service
func (s *Service) Ping(c context.Context) (err error) {
return
}
func (s *Service) monitorConsume() {
if s.c.Env != env.DeployEnvProd {
return
}
var arcNotifyMo, arcmo int64
for {
time.Sleep(time.Minute * 1)
if s.arcNotifyMo-arcNotifyMo == 0 {
s.monitor.Send(context.TODO(), s.c.Monitor.UserName, "creative-job did not consume within a minute, moni url"+s.c.Monitor.Moni)
}
arcNotifyMo = s.arcNotifyMo
if s.arcMo-arcmo == 0 {
s.monitor.Send(context.TODO(), s.c.Monitor.UserName, "creative-job did not consume within a minute, moni url"+s.c.Monitor.Moni)
}
arcmo = s.arcMo
}
}
// Close sub.
func (s *Service) Close() {
s.arcSub.Close()
s.arcNotifySub.Close()
s.upPub.Close()
close(s.midsChan)
s.TaskClose() //task databus close
s.wg.Wait()
}

View File

@@ -0,0 +1,40 @@
package service
import (
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/job/main/creative/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
s *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/creative-job.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
Reset(func() {})
f(s)
}
}
func Test_Pub(t *testing.T) {
Convey("pub", t, WithService(func(s *Service) {
Convey("pub", func() {
err := s.pub(int64(2089809), 0, 1)
So(err, ShouldBeNil)
})
}))
}

View File

@@ -0,0 +1,655 @@
package service
import (
"context"
"encoding/json"
"fmt"
"hash/crc32"
"time"
"go-common/app/job/main/creative/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/queue/databus"
)
//commitTask for commit task
func (s *Service) commitTask() {
for i := 0; i < s.c.Task.TableJobNum; i++ {
go func(i int) {
s.dispatchData(fmt.Sprintf("%02d", i))
}(i)
}
}
//shardingQueueIndex sharding queue index
func (s *Service) shardingQueueIndex(name string, ql int) (i int) { ////注使用校验和取模的原因是使得获取的消息均匀散入不同的worker队列中
ch := crc32.ChecksumIEEE([]byte(name))
i = int(ch) % ql
return
}
func (s *Service) dispatchData(index string) { //index 分表后缀名
var id int64
limit := s.c.Task.RowLimit
for {
res, err := s.newc.UserTasks(context.Background(), index, id, limit)
if err != nil {
log.Error("s.newc.UserTasks table index(%s)|id(%d)|limit(%d)|err(%v)", index, id, limit, err)
return
}
if len(res) == 0 {
time.Sleep(600 * time.Second)
id = 0 //reset id
continue
}
id = res[len(res)-1].ID
tks := make([]*model.UserTask, 0, len(res))
for _, v := range res {
target, ok := s.TaskMapCache[v.TaskID]
if !ok || target == nil {
continue
}
//过滤 非T+1的任务
if target.TargetType != model.TargetType004 &&
target.TargetType != model.TargetType005 &&
target.TargetType != model.TargetType006 &&
target.TargetType != model.TargetType007 &&
target.TargetType != model.TargetType008 &&
target.TargetType != model.TargetType009 {
continue
}
tks = append(tks, v)
}
if len(tks) > 0 {
s.taskQueue[s.shardingQueueIndex(index, s.c.Task.TableConsumeNum)] <- tks
}
time.Sleep(5 * time.Second)
}
}
func (s *Service) initTaskQueue() {
for i := 0; i < s.c.Task.TableConsumeNum; i++ {
ut := make(chan []*model.UserTask, s.chanSize)
s.taskQueue[i] = ut
go func(ch chan []*model.UserTask) {
s.updateTaskStateByGRPC(ch)
}(ut)
}
}
//updateTaskStateByGRPC for check task by call grpc.
func (s *Service) updateTaskStateByGRPC(c chan []*model.UserTask) {
for msg := range c {
for _, v := range msg {
mid, tid := v.MID, v.TaskID
reply, err := s.newc.CheckTaskState(context.Background(), mid, tid)
if err != nil {
if ec := ecode.Cause(err); ec.Code() == ecode.ServiceUnavailable.Code() {
log.Error("s.newc.CheckTaskState mid(%d)|task id(%d)|err(%v)", mid, tid, err)
return
}
log.Warn("s.newc.CheckTaskState mid(%d)|task id(%d)|err(%v)", mid, tid, err)
continue
}
log.Info("updateTaskStateByGRPC mid(%d)|task id(%d)", mid, tid)
if reply != nil && reply.FinishState {
_, err := s.newc.UpUserTask(context.Background(), mid, tid)
if err != nil {
log.Error("DriveStateByUser s.newc.UpUserTask mid(%d)|task id(%d)|err(%v)", mid, tid, err)
return
}
}
time.Sleep(50 * time.Millisecond)
}
}
}
func (s *Service) initStatViewQueue() {
for i := 0; i < s.statViewQueueLen; i++ {
view := make(chan *model.StatView, s.chanSize)
s.statViewSubQueue[i] = view
go func(m chan *model.StatView) { //播放
for v := range m {
log.Info("StatView v(%+v)|指标该UID下任意avid的获得-点击量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType015, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(view)
}
}
func (s *Service) initStatLikeQueue() {
for i := 0; i < s.statLikeQueueLen; i++ {
li := make(chan *model.StatLike, s.chanSize)
s.statLikeSubQueue[i] = li
go func(m chan *model.StatLike) { //点赞
for v := range m {
log.Info("StatLike v(%+v)|指标该UID下任意avid的获得-点赞量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType020, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(li)
}
}
func (s *Service) initDatabusQueue() {
for i := 0; i < s.databusQueueLen; i++ {
tk := make(chan *databus.Message, s.chanSize)
sh := make(chan *model.ShareMsg, s.chanSize)
fl := make(chan *model.Stat, s.chanSize) //粉丝数
rela := make(chan *model.Relation, s.chanSize) //关注
np := make(chan *model.Up, s.chanSize) //最新投稿
op := make(chan *model.Up, s.chanSize) //最新投稿
mp := make(chan *model.Up, s.chanSize) //手机投稿
//单个稿件计数
stsh := make(chan *model.StatShare, s.chanSize)
coin := make(chan *model.StatCoin, s.chanSize)
fav := make(chan *model.StatFav, s.chanSize)
rep := make(chan *model.StatReply, s.chanSize)
dm := make(chan *model.StatDM, s.chanSize)
s.taskSubQueue[i] = tk
s.shareSubQueue[i] = sh
s.followerQueue[i] = fl //粉丝
s.relationQueue[i] = rela //关注
s.newUpQueue[i] = np //新投稿
s.oldUpQueue[i] = op //投下5个稿
s.mobileUpQueue[i] = mp //手机投稿
//单个稿件计数
s.statShareSubQueue[i] = stsh
s.statCoinSubQueue[i] = coin
s.statFavSubQueue[i] = fav
s.statReplySubQueue[i] = rep
s.statDMSubQueue[i] = dm
//水印设置、观看创作学院视频、参加激励计划
go func(m chan *databus.Message) {
s.startByTask(m)
}(tk)
//分享自己的稿件
go func(m chan *model.ShareMsg) {
for v := range m {
log.Info("startByShare mid(%d)|v(%+v)|指标该UID分享自己视频的次数≥1", v.MID, v)
s.completeUserTask(v.MID, v.OID, model.TargetType002, 1)
time.Sleep(time.Millisecond * 10)
}
}(sh)
//关注哔哩哔哩创作中心和粉丝数判断
go func(m chan *model.Stat) {
for v := range m {
log.Info("followerStat mid(%d)|v(%+v)|指标该UID的粉丝数≥10 或者 1000", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType010, v.Follower) //新手任务粉丝数
s.completeUserTask(v.MID, 0, model.TargetType022, v.Follower) //进阶任务粉丝数
}
}(fl)
go func(m chan *model.Relation) {
for v := range m {
log.Info("relationMID mid(%d)|v(%+v)|指标该UID的关注列表含有“哔哩哔哩创作中心", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType012, 1)
}
}(rela)
// 该UID下开放浏览的稿件≥1
go func(m chan *model.Up) {
for v := range m {
log.Info("newUP mid(%d)|v(%+v)|指标该UID下开放浏览的稿件≥1", v.MID, v)
s.completeUserTask(v.MID, v.AID, model.TargetType001, 1)
}
}(np)
// 该UID下开放浏览的稿件≥5
go func(m chan *model.Up) {
for v := range m {
log.Info("oldUP mid(%d)|v(%+v)|指标该UID下开放浏览的稿件≥5", v.MID, v)
s.completeUserTask(v.MID, v.AID, model.TargetType014, 1)
}
}(op)
//单个稿件计数
go func(m chan *model.StatReply) {
for v := range m {
log.Info("StatReply v(%+v)|指标该UID下任意avid的获得-评论量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType016, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(rep)
go func(m chan *model.StatShare) {
for v := range m {
log.Info("StatShare v(%+v)|指标该UID下任意avid的获得-分享量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType017, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(stsh)
go func(m chan *model.StatFav) {
for v := range m {
log.Info("StatFav v(%+v)|指标该UID下任意avid的获得-收藏量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType018, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(fav)
go func(m chan *model.StatCoin) {
for v := range m {
log.Info("StatCoin v(%+v)|指标该UID下任意avid的获得-硬币量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType019, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(coin)
go func(m chan *model.StatDM) {
for v := range m {
log.Info("StatDM v(%+v)|指标该UID下任意avid的获得-弹幕量(%d)", v, v.Count)
s.completeUserTask(s.getMIDByAID(v.ID), v.ID, model.TargetType021, v.Count)
time.Sleep(time.Millisecond * 10)
}
}(dm)
go func(m chan *model.Up) {
for v := range m {
log.Info("Mobile mid(%d)|v(%+v)|指标该UID通过手机投稿的稿件≥1", v.MID, v)
s.completeUserTask(v.MID, v.AID, model.TargetType013, 1)
time.Sleep(time.Millisecond * 10)
}
}(mp)
}
}
func (s *Service) startByTask(c chan *databus.Message) {
for msg := range c {
v := &model.TaskMsg{}
if err := json.Unmarshal(msg.Value, v); err != nil {
log.Error("startByTask json.Unmarshal(%v) error(%v)", string(msg.Value), err)
continue
}
switch v.From {
case model.MsgForWaterMark:
log.Info("startByTask WaterMark mid(%d)|v(%+v)|指标任务完成期间该UID的水印开关为打开状态", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType011, v.Count)
case model.MsgForAcademyFavVideo:
log.Info("startByTask AcademyFavVideo mid(%d)|v(%+v)|指标该UID在创作学院的观看记录≥1", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType003, v.Count)
case model.MsgForGrowAccount:
log.Info("startByTask GrowAccount mid(%d)|v(%+v)|指标该UID的激励计划状态为已开通", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType023, v.Count)
case model.MsgForOpenFansMedal:
log.Info("startByTask OpenFansMedal mid(%d)|v(%+v)|指标该UID粉丝勋章为开启状态", v.MID, v)
s.completeUserTask(v.MID, 0, model.TargetType024, v.Count)
}
time.Sleep(time.Millisecond * 10)
}
}
func (s *Service) getMIDByAID(aid int64) (mid int64) {
arc, err := s.arc.Archive(context.Background(), aid)
if err != nil || arc == nil {
log.Error("getMIDByAID s.arc.Archive aid(%d)|err(%v)", aid, err)
return
}
mid = arc.Author.Mid
return
}
func (s *Service) getTaskIDByTargetType(mid int64, ty int8) (tid int64) {
userTasks, err := s.newc.UserTasksByMIDAndState(context.Background(), mid, model.TaskIncomplete)
if err != nil {
log.Error("s.newc.UserTasksByMIDAndState mid(%d)|err(%v)", mid, err)
return
}
if len(userTasks) == 0 {
return
}
for _, v := range userTasks {
t, ok := s.TaskMapCache[v.TaskID]
if ok && t.TargetType == ty {
tid = v.TaskID
break
}
}
return
}
// completeUserTask update user task to complete
func (s *Service) completeUserTask(mid, aid int64, ty int8, count int64) {
tid := s.getTaskIDByTargetType(mid, ty)
if tid == 0 {
return
}
target, ok := s.TaskMapCache[tid]
if !ok || target == nil {
return
}
if count >= target.TargetValue {
if _, err := s.newc.UpUserTask(context.Background(), mid, tid); err != nil {
log.Error("s.newc.UpUserTask mid(%d)|tid(%d)|err(%v)", mid, tid, err)
return
}
log.Info("completeUserTask mid(%d)|aid(%d)|count(%d)|taskID(%d)|targetType(%d)|targetValue(%d)", mid, aid, count, tid, ty, target.TargetValue)
}
}
// 对第30天未完成新手任务的UP主发送消息通知记录时间点为用户加入任务成就的时间;该消息有且仅发送一次。
func (s *Service) expireTaskNotify() {
for i := 0; i < s.c.Task.TaskTableJobNum; i++ {
go func(i int) {
s.dispatchTasksNotify(fmt.Sprintf("%02d", i))
}(i)
}
}
func (s *Service) dispatchTasksNotify(index string) {
var id int64
ext := s.c.Task.TaskExpireTime
th := s.c.Task.TaskSendHour
tm := s.c.Task.TaskSendMiniute
ts := s.c.Task.TaskSendSecond
limit := s.c.Task.TaskRowLimitNum
batchSize := s.c.Task.TaskBatchMidNum //每次发送mid数量
for {
now := time.Now()
if now.Hour() != th || now.Minute() != tm || now.Second() != ts {
// log.Info("dispatchTasksNotify minuts(%d) second(%d)", now.Minute(), now.Second())
time.Sleep(1 * time.Second)
continue
}
ctime := now.Unix() - ext //检查任务是否超过30天未完成
year, month, day := time.Unix(ctime, 0).Date()
start := time.Date(year, month, day, 0, 0, 0, 0, time.Local).Format("2006-01-02 15:04:05")
end := time.Date(year, month, day, 23, 59, 59, 999, time.Local).Format("2006-01-02 15:04:05")
log.Info("dispatchTasksNotify now(%s)|start(%s)|end(%s)", now.Format("2006-01-02 15:04:05"), start, end)
midMap := make(map[int64]*model.UserTask)
for {
res, err := s.newc.UserTasksNotify(context.Background(), index, id, start, end, limit)
if err != nil {
log.Error("s.newc.UserTasksNotify table index(%s)|start(%s)|end(%s)|limit(%d)|err(%v)", index, start, end, limit, err)
return
}
if len(res) == 0 {
id = 0
break
}
for _, v := range res {
midMap[v.MID] = v
}
id = res[len(res)-1].ID //next limit
time.Sleep(1 * time.Second)
}
if len(midMap) == 0 {
continue
}
mids := make([]int64, 0, len(midMap))
for mid := range midMap {
mids = append(mids, mid)
}
var tmids []int64
count := len(mids)/batchSize + 1
for i := 0; i < count; i++ {
if i == count-1 {
tmids = mids[i*batchSize:]
} else {
tmids = mids[i*batchSize : (i+1)*batchSize]
}
if len(tmids) > 0 {
s.taskNotifyQueue[s.shardingQueueIndex(index, s.c.Task.TaskTableConsumeNum)] <- tmids
}
}
}
}
func (s *Service) initTaskNotifyQueue() {
for i := 0; i < s.c.Task.TaskTableConsumeNum; i++ {
ut := make(chan []int64, s.chanSize)
s.taskNotifyQueue[i] = ut
go func(ch chan []int64) {
s.sendTaskNotify(ch)
}(ut)
}
}
func (s *Service) sendTaskNotify(c chan []int64) {
for mids := range c {
if len(mids) == 0 {
time.Sleep(time.Second * 60)
continue
}
for i := 1; i < 3; i++ {
if err := s.newc.SendNotify(context.Background(), mids, s.c.Task.TaskMsgCode, s.c.Task.TaskTitle, s.c.Task.TaskContent); err != nil {
log.Error("sendTaskNotify s.newc.SendNotify(%v) error(%v)", mids, err)
time.Sleep(time.Millisecond * 10)
continue
} else {
log.Info("sendTaskNotify s.newc.SendNotify mids(%+v)", mids)
break
}
}
time.Sleep(time.Millisecond * 10)
}
}
// loadproc
func (s *Service) loadProc() {
for {
time.Sleep(3 * time.Minute)
s.loadTasks()
s.loadGiftRewards()
}
}
//load tags
func (s *Service) loadTasks() {
res, err := s.newc.Tasks(context.Background())
if err != nil {
log.Error("s.newc.Tasks error(%v)", err)
return
}
if len(res) == 0 {
return
}
s.TaskCache = res
temp := make(map[int64]*model.Task)
for _, v := range s.TaskCache {
temp[v.ID] = v
}
s.TaskMapCache = temp
}
//load gift-reward
func (s *Service) loadGiftRewards() {
res, err := s.newc.AllGiftRewards(context.Background())
if err != nil {
log.Error("s.newc.AllGiftRewards error(%v)", err)
return
}
if len(res) == 0 {
return
}
s.GiftRewardCache = res
}
// 检查用户是否有奖励可领取
func (s *Service) checkRewardReceive(c context.Context, mid int64) (is bool) {
tasks, err := s.newc.UserTasksByMID(c, mid)
if err != nil {
log.Error("s.newc.UserTasksByMID mid(%v)|error(%v)", mid, err)
return
}
groupMap := make(map[int64][]*model.UserTask)
giftMap := make(map[int8][]*model.UserTask)
for _, v := range tasks {
groupMap[v.TaskGroupID] = append(groupMap[v.TaskGroupID], v)
if _, ok := s.GiftRewardCache[v.TaskType]; ok {
giftMap[v.TaskType] = append(giftMap[v.TaskType], v)
}
}
groupNum := 0 // 组奖励 已完成个数
giftNum := make(map[int8]bool) // 礼包奖励 已完成个数
for _, ts := range groupMap {
for _, t := range ts {
if t.State == model.TaskIncomplete {
groupNum++
if _, ok := s.GiftRewardCache[t.TaskType]; ok {
giftNum[t.TaskType] = true
}
break
}
}
}
r1, err := s.newc.BaseRewardCount(c, mid) // 组奖励 已领取个数
if err != nil {
log.Error("s.newc.BaseRewardCount mid(%v)|error(%v)", mid, err)
return
}
r2, err := s.newc.GiftRewardCount(c, mid) // 礼包奖励 已领取个数
if err != nil {
log.Error("s.newc.GiftRewardCount mid(%v)|error(%v)", mid, err)
return
}
total := len(groupMap) + len(giftMap) //奖励总数
untotal := groupNum + len(giftNum) //未完成的奖励
receive := r1 + r2 //已领取奖励
// 可领取的奖励 = 奖励总数 -未完成的奖励 - 已领取奖励
count := total - untotal - receive
log.Info("checkRewardReceive mid(%d)|奖励总数(%d)|未完成奖励总数(%d)|已领取奖励总数(%d)|可领取奖励总数(%d)", mid, total, untotal, receive, count)
if count > 0 {
is = true
}
return
}
// 该消息每周最多发送 1 条发送时间为每周六的20:00用户为上周周六18:00 - 本周周六17:59所有达到领取奖励且 未领取 的用户。
// 通知仅限用户有未领取的奖励时发送:若在该时间段,用户已领取全部可领取的奖励,
// 则不发送通知,如果用户已领取部分可领取的奖励,仍有部分奖励未领取,则仍然发送通知
func (s *Service) rewardReceiveNotify() {
for i := 0; i < s.c.Task.RewardTableJobNum; i++ {
go func(i int) {
s.dispatchRewardNotify(fmt.Sprintf("%02d", i))
}(i)
}
}
func (s *Service) dispatchRewardNotify(index string) {
var id int64
week := s.c.Task.RewardWeek //星期几
ld := s.c.Task.RewardLastDay //从过去多少天开始查询
lh := s.c.Task.RewardLastHour //几点开始查询
lm := s.c.Task.RewardLastMiniute //几分开始查询
ls := s.c.Task.RewardLastSecond //几秒开始查询
nh := s.c.Task.RewardNowHour //从当前时间几点开始
nm := s.c.Task.RewardNowMiniute //从当前时间几分开始
ns := s.c.Task.RewardNowSecond //从当前时间几秒开始
limit := s.c.Task.RewardRowLimitNum
batchSize := s.c.Task.RewardBatchMidNum //每次发送mid数量
for {
now := time.Now()
if int(now.Weekday()) != week || now.Hour() != nh || now.Minute() != nm || now.Second() != ns {
// log.Info("dispatchRewardNotify Weekday(%d) Hour(%d) Minute(%d) Second(%d)", now.Weekday(), now.Hour(), now.Minute(), now.Second())
time.Sleep(1 * time.Second)
continue
}
last := now.AddDate(0, 0, ld).Add(time.Hour * time.Duration(lh)).Add(time.Minute * time.Duration(lm)).Add(time.Second * time.Duration(ls))
log.Info("dispatchRewardNotify last(%s) now(%s)\n", last.Format("2006-01-02 15:04:05"), now.Format("2006-01-02 15:04:05"))
midMap := make(map[int64]*model.UserTask)
for {
res, err := s.newc.CheckTasksForRewardNotify(context.Background(), index, id, last, now, limit)
if err != nil {
log.Error("s.newc.CheckTasksForRewardNotify table index(%s)|id(%d)|limit(%d)|err(%v)", index, id, limit, err)
return
}
if len(res) == 0 {
id = 0
break
}
for _, v := range res {
midMap[v.MID] = v
}
id = res[len(res)-1].ID //next limit
time.Sleep(1 * time.Second)
}
if len(midMap) == 0 {
continue
}
mids := make([]int64, 0, len(midMap))
for mid := range midMap {
if s.checkRewardReceive(context.Background(), mid) {
mids = append(mids, mid)
}
}
var tmids []int64
count := len(mids)/batchSize + 1
for i := 0; i < count; i++ {
if i == count-1 {
tmids = mids[i*batchSize:]
} else {
tmids = mids[i*batchSize : (i+1)*batchSize]
}
if len(tmids) > 0 {
s.rewardNotifyQueue[s.shardingQueueIndex(index, s.c.Task.RewardTableConsumeNum)] <- tmids
}
}
}
}
func (s *Service) initRewardNotifyQueue() {
for i := 0; i < s.c.Task.RewardTableConsumeNum; i++ {
ut := make(chan []int64, s.chanSize)
s.rewardNotifyQueue[i] = ut
go func(ch chan []int64) {
s.sendRewardNotify(ch)
}(ut)
}
}
func (s *Service) sendRewardNotify(c chan []int64) {
for mids := range c {
if len(mids) == 0 {
time.Sleep(time.Second * 1)
continue
}
for i := 1; i < 3; i++ {
if err := s.newc.SendNotify(context.Background(), mids, s.c.Task.RewardMsgCode, s.c.Task.RewardTitle, s.c.Task.RewardContent); err != nil {
log.Error("sendRewardNotify s.newc.SendNotify mids(%+v) error(%v)", mids, err)
time.Sleep(time.Millisecond * 100)
continue
} else {
log.Info("sendRewardNotify s.newc.SendNotify mids(%+v)", mids)
break
}
}
time.Sleep(time.Millisecond * 10)
}
}

View File

@@ -0,0 +1,285 @@
package service
import (
"context"
"strings"
"time"
wkhmdl "go-common/app/interface/main/creative/model/weeklyhonor"
upgrpc "go-common/app/service/main/up/api/v1"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
var (
forbidMidMap = map[int64]struct{}{
24: {},
2: {},
517999: {},
9099524: {},
208259: {},
202466803: {},
40016273: {},
245482023: {},
84089650: {},
31465698: {},
22160843: {},
3098848: {},
39592268: {},
223931175: {},
}
honorMap map[int][]*wkhmdl.HonorWord
)
// SendMsg .
func (s *Service) SendMsg() {
log.Info("Start SendMsg")
var (
c = context.TODO()
lastid int64
size = 1000
)
for {
var (
upActives []*upgrpc.UpActivity
newid int64
err error
)
for i := 0; i < 5; i++ {
upActives, newid, err = s.honDao.UpActivesList(c, lastid, size)
if err == nil {
break
}
}
if err != nil {
log.Error("s.honDao.UpActivesList(%d,%d) error(%v)", lastid, size, err)
break
}
// filter mid
mids, err := s.filterMids(c, upActives)
if err != nil {
continue
}
var errMids []int64
if len(mids) > 0 {
for i := 1; i < 3; i++ {
if errMids, err = s.honDao.SendNotify(c, mids); err != nil {
log.Error("s.honDao.SendNotify(%v) error(%v)", mids, err)
continue
}
break
}
}
go s.infocMsgStat(mids, errMids)
if len(upActives) < size {
break
}
lastid = newid
time.Sleep(time.Second)
}
log.Info("Finish SendMsg")
}
// FlushHonor .
func (s *Service) FlushHonor() {
log.Info("FlushHonor Start")
var (
c = context.TODO()
lastid int64
size = 1000
batchSize = s.c.HonorStep
)
for {
upActives, newid, err := s.honDao.UpActivesList(c, lastid, size)
if err != nil {
log.Error("s.honDao.UpActivesList(%d,%d) error(%v)", lastid, size, err)
time.Sleep(time.Millisecond * 100)
continue
}
var mids []int64
for _, v := range upActives {
mids = append(mids, v.Mid)
}
g := new(errgroup.Group)
var pmids []int64
routines := len(mids)/batchSize + 1
for i := 0; i < routines; i++ {
if i == routines-1 {
pmids = mids[i*batchSize:]
} else {
pmids = mids[i*batchSize : (i+1)*batchSize]
}
t := pmids
g.Go(func() (err error) {
err = s.upsertHonor(c, t)
if err != nil {
log.Error("s.upsertHonor(%v) error(%v)", t, err)
return err
}
return nil
})
}
g.Wait()
if len(mids) < size {
break
}
lastid = newid
}
log.Info("FlushHonor done")
}
func (s *Service) upsertHonor(c context.Context, mids []int64) error {
now := time.Now()
day := int(now.Weekday())
date := now.AddDate(0, 0, -day-1)
saturday := date.Format("20060102")
LOOP:
for _, mid := range mids {
var hls map[int]*wkhmdl.HonorLog
hls, err := s.honDao.HonorLogs(c, mid)
if err != nil {
log.Error("s.honDao.HonorLogs(%d) error(%v)", mid, err)
time.Sleep(time.Millisecond * 100)
continue
}
var lastHid int
for _, v := range hls {
if int64(v.MTime) > date.Unix() && int64(v.MTime) < date.AddDate(0, 0, 7).Unix() {
continue LOOP
}
if int64(v.MTime) < date.Unix() && int64(v.MTime) > date.AddDate(0, 0, -7).Unix() {
lastHid = v.HID
}
}
var hs *wkhmdl.HonorStat
for i := 0; i < 3; i++ {
hs, err = s.honDao.HonorStat(c, mid, saturday)
if err != nil {
log.Error("s.honDao.HonorStat(%d,%v) error(%v)", mid, saturday, err)
continue
}
if hs != nil {
break
}
}
if hs == nil {
log.Error("FlushHonor nil hs mid(%d)", mid)
time.Sleep(time.Millisecond * 100)
continue
}
newHid := hs.GenHonor(mid, lastHid)
affected, err := s.honDao.UpsertCount(c, mid, newHid)
if err != nil || affected == 0 {
log.Error("s.honDao.UpsertCount(%d,%d) affceted(%d) error(%v)", mid, newHid, affected, err)
}
log.Info("FlushHonor mid(%d)", mid)
}
return nil
}
// TestSendMsg .
func (s *Service) TestSendMsg(c context.Context, mids []int64) (err error) {
for i := 1; i < 3; i++ {
if _, err = s.honDao.SendNotify(c, mids); err != nil {
log.Error("s.honDao.SendNotify(%v) error(%v)", mids, err)
continue
} else {
break
}
}
return
}
func (s *Service) filterMids(c context.Context, upActives []*upgrpc.UpActivity) (mids []int64, err error) {
if len(upActives) == 0 {
return
}
var rawMids []int64
for _, v := range upActives {
rawMids = append(rawMids, v.Mid)
}
hls, err := s.honDao.LatestHonorLogs(c, rawMids)
if err != nil {
log.Error("failed to get latest honor logs err(%v)", err)
return
}
midClickMap, err := s.honDao.ClickCounts(c, rawMids)
if err != nil {
log.Error("failed to get honor click count err (%v)", err)
return
}
highLevMap := highLevMidMap(hls)
for _, v := range upActives {
var subState uint8
if subState, err = s.honDao.GetUpSwitch(c, v.Mid); subState == wkhmdl.HonorUnSub {
continue
}
if err != nil {
log.Error("s.honDao.GetUpSwitch mid(%d) err(%v)", v.Mid, err)
}
var cnt int
if cnt, err = s.honDao.UpCount(c, v.Mid); err == nil && cnt == 0 {
continue
}
if highLevMap[v.Mid] {
mids = append(mids, v.Mid)
continue
}
if _, ok := forbidMidMap[v.Mid]; ok {
continue
}
if v.Activity > 3 {
continue
}
if _, ok := midClickMap[v.Mid]; ok {
mids = append(mids, v.Mid)
continue
}
if sunday := wkhmdl.LatestSunday(); s.c.SendEveryWeek || isOddWeek(sunday) {
mids = append(mids, v.Mid)
}
}
return mids, err
}
func isOddWeek(date time.Time) bool {
_, w := date.ISOWeek()
return w%2 != 0
}
func highLevMidMap(hls []*wkhmdl.HonorLog) (res map[int64]bool) {
if honorMap == nil {
honorMap = wkhmdl.HMap()
}
res = make(map[int64]bool)
for _, h := range hls {
hws, ok := honorMap[h.HID]
if !ok || len(hws) == 0 {
continue
}
last := len(hws) - 1
res[h.MID] = isHighLev(hws[last].Priority)
}
return
}
func isHighLev(p string) bool {
return strings.HasPrefix(p, "A") || strings.HasPrefix(p, "R") || strings.HasPrefix(p, "S")
}
func (s *Service) infocMsgStat(mids, errMids []int64) {
errMidsMap := make(map[int64]bool)
for _, mid := range errMids {
errMidsMap[mid] = true
}
for _, mid := range mids {
var success int32
if !errMidsMap[mid] {
success = 1
}
err := s.honDao.HonorInfoc(context.Background(), mid, success)
if err != nil {
log.Error("failed to log honor infoc,mid(%d),success(%d),err(%v)", mid, success, err)
}
}
}

View File

@@ -0,0 +1,172 @@
package service
import (
"context"
"testing"
wkhmdl "go-common/app/interface/main/creative/model/weeklyhonor"
upgrpc "go-common/app/service/main/up/api/v1"
xtime "go-common/library/time"
"github.com/smartystreets/goconvey/convey"
)
var (
mtime = xtime.Time(wkhmdl.LatestSunday().Unix())
c = context.Background()
rawUpActives = []*upgrpc.UpActivity{
{Mid: 1, Activity: 4}, {Mid: 2, Activity: 4}, {Mid: 3, Activity: 4}, {Mid: 4, Activity: 3}, {Mid: 5, Activity: 4}, {Mid: 6, Activity: 1}, {Mid: 7, Activity: 1}, {Mid: 24, Activity: 2},
}
mockHls = []*wkhmdl.HonorLog{
// lev:SSR clicked
{
ID: 1,
MID: 1,
HID: 1,
MTime: mtime,
},
// lev:SR & BlackList clicked
{
ID: 2,
MID: 2,
HID: 9,
MTime: mtime,
},
// lev:R clicked
{
ID: 3,
MID: 3,
HID: 17,
MTime: mtime,
},
// lev:A
{
ID: 4,
MID: 4,
HID: 26,
MTime: mtime,
},
// lev:B clicked
{
ID: 5,
MID: 5,
HID: 35,
MTime: mtime,
},
// lev:C clicked
{
ID: 6,
MID: 6,
HID: 46,
MTime: mtime,
},
// lev:D
{
ID: 7,
MID: 7,
HID: 56,
MTime: mtime,
},
// BlackList clicked
{
ID: 8,
MID: 24,
HID: 50,
},
}
clickMap = map[int64]int32{
1: 1,
2: 1,
3: 1,
5: 1,
6: 1,
24: 1,
}
)
func TestServiceSendMsg(t *testing.T) {
convey.Convey("SendMsg", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
// mock
s.honDao.MockUpActivesList(rawUpActives, 0, nil)
s.honDao.MockUpCount(1, nil)
s.honDao.MockLatestHonorLogs(mockHls, nil)
s.honDao.MockClickCounts(clickMap, nil)
s.honDao.MockSendNotify(nil)
// test
s.SendMsg()
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestServiceFlushHonor(t *testing.T) {
convey.Convey("FlushHonor", t, func(ctx convey.C) {
ctx.Convey("When everything gose positive", func(ctx convey.C) {
// mock
s.honDao.MockUpActivesList(rawUpActives, 0, nil)
mockStat := wkhmdl.HonorStat{
Play: 100,
PlayLastW: 100,
}
s.honDao.MockHonorStat(&mockStat, nil)
// test
s.FlushHonor()
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestServiceupsertHonor(t *testing.T) {
convey.Convey("upsertHonor", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := s.upsertHonor(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestServiceTestSendMsg(t *testing.T) {
convey.Convey("TestSendMsg", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := s.TestSendMsg(c, mids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestServicefilterMids(t *testing.T) {
convey.Convey("filterUnActiveMids", t, func(ctx convey.C) {
var (
filteredMids = []int64{1, 2, 3, 4, 6}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
s.honDao.MockUpCount(1, nil)
s.honDao.MockLatestHonorLogs(mockHls, nil)
s.honDao.MockClickCounts(clickMap, nil)
mids, err := s.filterMids(c, rawUpActives)
ctx.Convey("Then err should be nil.mids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
sunday := wkhmdl.LatestSunday()
if isOddWeek(sunday) {
filteredMids = append(filteredMids, 7)
}
ctx.So(mids, convey.ShouldResemble, filteredMids)
})
})
})
}