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,32 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/tv/cmd:all-srcs",
"//app/interface/main/tv/conf:all-srcs",
"//app/interface/main/tv/dao/account:all-srcs",
"//app/interface/main/tv/dao/app:all-srcs",
"//app/interface/main/tv/dao/archive:all-srcs",
"//app/interface/main/tv/dao/audit:all-srcs",
"//app/interface/main/tv/dao/cms:all-srcs",
"//app/interface/main/tv/dao/favorite:all-srcs",
"//app/interface/main/tv/dao/goblin:all-srcs",
"//app/interface/main/tv/dao/history:all-srcs",
"//app/interface/main/tv/dao/pgc:all-srcs",
"//app/interface/main/tv/dao/search:all-srcs",
"//app/interface/main/tv/dao/thirdp:all-srcs",
"//app/interface/main/tv/dao/upper:all-srcs",
"//app/interface/main/tv/http:all-srcs",
"//app/interface/main/tv/model:all-srcs",
"//app/interface/main/tv/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,297 @@
### tv的Gateway服务.
#### V1.8.9
> 1. pgc/view新版详情页隐藏未过审单集
#### V1.8.8
> 1. 牌照回调接口优化:
- 增加error返回sign错误json错误db操作等
- 查询是否回调的ID是否存在由查DB改为查MC
- 赋值操作收敛到model中
- DB操作改为事务
> 2. 牌照回调接口审核原因改为不必填
#### V1.8.7
> 1. tv vip 订单增加应用渠道信息
#### V1.8.6
> 1. 模块页自动填充的部分加上贴标逻辑
#### V1.8.5
> 1. ugc playurl鉴权接tv-service
#### V1.8.4
> 1. guest_create 接口移除 csrf 验证
#### V1.8.3
> 1. loadep接口针对旧版本build id<101500点击播放非免费内容时报错
#### V1.8.2
> 1. seasonCMS中新增PayStatus字段回源逻辑添加该字段
> 2. 修改模块页、追番、索引页、历史记录、
#### V1.8.1
> 1. 支持 tv 会员
> 2. 修改访客扫码为 GET 请求
#### V1.8.0
> 1. pgc详情页v2参数名称修改原为season_type新改为type吐给客户端时兼容season_type也赋值
#### V1.7.9
> 1. PGC season的area字段由int改为string兼容
#### V1.7.8
> 1. 修复modpage的map并发读写的问题
#### V1.7.7
> 1. pgc的详情页以及风格标签跳转逻辑迁移到service/pgc包中优化冗余代码
> 2. 新增pgc的新版详情页接口用于新版本v1.15,继承旧版的审核中隐藏逻辑,去掉旧版的付费隐藏逻辑
> 3. 抽象详情页的season干预、ep干预、审核中ep隐藏等代码为service方法使得media和mediaV2代码减少冗余部分
#### V1.7.6
> 1. 修复动态分区发版时会503的问题调整load动态分区和load模块页的顺序
#### V1.7.5
> 1. 修复野版pgc搜索err之后没有return的bug
#### V1.7.4
> 1. ugc详情页增加单p的过审状态判断如果为审核中则不展示该单p如果当前稿件没有可展示单p则报错为稿件审核不过
> 2. ugc详情页判断是否为空稿件如果为空稿件则打入channel中异步进行稿件的cms下架操作
> 3. 补全cms/dao中的UT
#### V1.7.3
> 1. 索引标签新增排序字段,标签接口吐出标签时,按照排序字段排列
#### V1.7.2
> 1. 野版搜索综合中显示影视电视剧等内容
#### V1.7.1
> 1. 当PGC排行榜接口返回为空时增加检验逻辑进行报错不更新内存直到下一次不空时再更新
#### V1.7.0
> 1. 野版搜索优化
#### V1.6.9
> 1. 详情页添加风格展示
#### V1.6.8
> 1. modpage接口新增字段more_new_page支持moretype为2新pgc索引3新ugc索引下发索引的category
> 2. 做新老版本的兼容当用户为老版本时1.13及以前下发morepage字段为其模块所在页面的page_id
> 3. 精选页配置跳转新索引页时,对于老版本根据填充内容映射:
- 如果填充为pgc索引时直接映射
- 如果填充为ugc二级分区索引时寻找其ugc一级分区索引
- 如果ugc一级分区索引时通过ugc分区名称映射到旧ugc索引页如果映射不到则强制跳转为pgc番剧索引页
#### V1.6.7
> 1. playurl新增ip透传
> 2. playurl改为使用独立的http client配置
#### V1.6.6
> 1. 修改pgc索引页的版权筛选的字段从copyright改为copyright_info
> 2. 修复ugc索引页的排序问题
> 3. 修正动态标签中时间的问题将2004-2000倒序成2000-2004以便ES接受
> 4. 修复首页推荐位卡片type为0的问题由PGC排行榜数据补全时赋予pgc类型
#### V1.6.5
> 1. 新增动态分区接口
#### V1.6.4
> 1. 新增索引页接口pgcugc支持后台干预
> 2. 新老索引页均支持显示pgc第二行
> 3. 补齐dao/app下的UT
#### V1.6.3
> 1. 新增索引筛选标签接口
#### V1.6.2
> 1. 芒果推荐位增加字段
#### V1.6.1
> 1. 修复模块页首页推荐位放入ugc时无法兼容新老版本的问题
#### V1.6.0
> 1. 下线付费内容和审核内容
#### V1.5.9
> 1. tv-interface接grpc: arc, acc
> 2. 完善dao/archive, account, history中的UT
#### V1.5.8
> 1. 因为tv-interface暂时未接grpcarchive-view结构体与tv-jobgrpc不一致会导致缓存解析失败吐出aid=0等信息临时检验aid是否为0如为0则强制回源gorpc
> 2. 完善dao/archive中的UT
#### V1.5.7
> 1. 收藏接口:客户端字段使用错误,修改服务端字段含义进行修复
#### V1.5.6
> 1. 新增接口供视频云调取用于写入pgc的提交时间
> 2. 兼容pgc和ugc都会出现的一个cid出现多次的情况将视频云回调更新操作改为支持更新多行
#### V1.5.5
> 1. 配合芒果媒资同步pgc的seasonCMS中新增origin_name和alias两个字段epCMS中新增pay_status两个字段
> 2. 新增芒果媒资同步四个接口:- pgc的seasonep + ugc的稿件和video
> 3. 将season_id, epid, aid, cid 全部统一为int64方便操作
#### V1.5.4
> 1. 新增芒果推荐位接口
> 2. 在service、model、dao中新增thirdp第三方媒资、推荐文件夹用来放置芒果、当贝等相关业务
> 3. 稿件分区数据从service下放到dao层
#### V1.5.3
> 1. 将收藏的添加、删除接口由rpc改为对接http接口
#### V1.5.2
> 1. 首页、分区页、模块页获取干预数据时,检查数据源是否失效,如失效则置为删除
> 2. 补充interface的UT
#### V1.5.1
> 1. 新增tv版收藏展示、添加或删除收藏接口
> 2. 整理代码,将若干短的代码合并到同一文件中,避免过多代码文件
#### V1.5.0
> 1. 透传ugc play url
> 2. 新增大会员清晰度降路逻辑
#### V1.4.9
> 1. 修复加载页面时panic问题当底层逻辑报错时不更新page数据线上继续使用老数据
#### V1.4.8
> 1. ugc、pgc详情页接口新增观看进度
> 2. 历史记录取cms缓存改为批量获取
#### V1.4.7
> 1. 新增历史记录接口
> 2. load数据的逻辑cache+db全部下沉到dao层
#### V1.4.6
> 1. Service层进行拆分
> 2. 优化PGC数据的使用方式不再使用service中的全局变量
> 3. 新老版本干预映射修改老版本只取pgc干预数据
> 4. 模块页全面支持UGC干预和UGC数据填充附加版本过滤逻辑1.12版本支持ugc+pgc混排1.11以下版本只支持pgc视频
> 5. 首页推荐位改为14条数据并且支持UGC数据支持版本过滤
#### V1.4.5
> 1. 修改ugc详情页如果up主信息下架则不予以展示。如up主信息有干预则展示干预信息。
#### V1.4.4
> 1. 新增tv版搜索接口ugc一级分类接口
> 2. tv版sug增加版本过滤逻辑
> 3. pgc详情页接口数据tv端不存在时由500改为报404pgc相关推荐接口增加重试到3次
#### V1.4.3
> 1. 增加转码信息回调接口,供视频云调用,回传转码信息
> 2. identify迁移verify和auth
> 3. http server迁移engine.Start
#### V1.4.2
> 1. 干预走新老版本对应,老版本干预数据如果有配置,改为走新版本对应模块的干预
#### V1.4.1
> 1. 当贝接口新增最新一集正片字段
#### V1.4.0
> 1. 增加ugc详情页含原pgc详情页全部逻辑稿件鉴权错误语提示cms干预相关推荐根据牌照鉴权进行过滤
> 2. DAO层拆分为account,archive详情页新增,audit原牌照回传逻辑),cms牌照cms干预逻辑appapp页面逻辑
> 3. 新增ugc的鉴权接口优化pgc的鉴权接口逻辑
#### V1.3.13
> 1. 修复modpage追番共用的问题
#### V1.3.12
> 1. 增加默认闪屏逻辑,避免过多-400报错
#### V1.3.11
> 1. 修复map concurrent write的问题
#### V1.3.10
> 1. 优化service加载代码干预数据生成客户端卡片过程不走DB改走MC
> 2. 优化媒资信息(封面、标题)干预代码,由单个改成批量
> 3. 合并tv-interface/PgcNewIndex分支代码异步加载来自于PGC接口的index_show进入service内存中
> 4. 增加ecode的引用在当贝页面参数错误时进行错误提示
#### V1.3.9
> 1. service中所有阻塞改为goroutine
#### V1.3.8
> 1. 修复loadTypes阻塞的问题
#### V1.3.7
> 1. 新增当贝的全量过审pgc-season & ugc-archive接口
#### V1.3.6
> 1. 新增模块化页面接口
#### V1.3.5
> 1. 修改详情页接口在episode中增加返回水印信息回源也增加watermark的部分
#### V1.3.4
> 1. 审核回传支持ugc视频
> 2. 审核回传逻辑从http层移动到service层方便复用
#### V1.3.3
> 1. 修改zone配置的名称避免修改原配置
#### V1.3.2
> 1. 推荐位干预逻辑修改,首页、番剧、国创增加最新更新模块
> 2. 详情页+ep和season的鉴权接口报错细化改为可配置的
> 3. 新增大家都在搜tv版接口
#### V1.3.1
> 1. 修改默认配置
#### V1.3.0
> 1. BM 框架迁移
#### V1.2.4
> 1. 新增搜索结果封装接口
#### V1.2.3
> 1. 修改回传时的前缀从bilibili改为xds
> 2. 修复主页隐藏index_show影响分区页的问题
> 3. 详情页接口增加MC回源逻辑
#### V1.2.2
> 1. 新增分区索引页面全部过审season分区列表支持分页
> 2. 修改索引页面结构
#### V1.2.1
> 1. 新增pgc相关推荐接口
#### V1.2.0
> 1. 新增闪屏接口
#### V1.1.3
> 1. 修复首页追番数据的问题
#### V1.1.2
> 1. 增加tv端升级接口
> 2. 添加详情页接口对accessKey的支持
#### V1.1.1
> 1. 添加详情页接口聚合鉴权功能支持内容干预从MC取MC中没有时不回源
> 2. 排行榜接口支持cms层面的干预
#### V1.1.0
> 1. 添加首页(精选页)推荐位、列表排行榜接口
> 2, 添加分区推荐位、列表排行榜接口
> 3. 添加播控鉴权接口
#### V1.0.2
> 新增注入时间和审核原由逻辑
#### V1.0.1
> 审核通过season和ep自动上线
#### V1.0.0
> 视频审核回调接口

View File

@@ -0,0 +1,11 @@
# Owner
renwei
liweijia
zhaogangtao
# Author
zhaoshichen
# Reviewer
wuhao
guanyanliang

View File

@@ -0,0 +1,17 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- liweijia
- renwei
- zhaogangtao
- zhaoshichen
labels:
- interface
- interface/main/tv
- main
options:
no_parent_owners: true
reviewers:
- guanyanliang
- wuhao
- zhaoshichen

View File

@@ -0,0 +1,7 @@
### tv的Gateway服务.
##### 项目简介
> 1.提供牌照方视频审核接口
> 2.提供TV端APP调用的排行榜接口
##### 编译环境
> 请使用golang v1.7.x以上版本编译执行。

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["tv-interface.toml"],
importpath = "go-common/app/interface/main/tv/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

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

View File

@@ -0,0 +1,307 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
dir = "./"
family = "tv-interface"
env = "qa"
tick = "10m"
# Add Zone's middle. Migrate Home's cfg to Zone.0
[Newzone]
[Newzone.0]
Name = "home"
Top = 14
Middle = 10
Bottom = 10
MiddleM = 21
[Newzone.1]
Name = "jp"
Top = 5
Middle = 12
Bottom = 59
TopM = 8
MiddleM = 64
[Newzone.4]
Name = "cn"
Top = 5
Middle = 12
Bottom = 59
[Newzone.2]
Name = "movie"
Top = 5
Bottom = 59
[Newzone.3]
Name = "documentary"
Top = 6
Bottom = 39
[Newzone.5]
Name = "tv"
Top = 5
Bottom = 59
[Search]
URL = "http://s.search.bilibili.co/main/suggest"
MainVer = "v4"
SugNum = 15
SugType = "tv"
SugPGCBuild = 1011
Highlight = "true"
HotwordFre = "3m"
ResultURL = "http://s.search.bilibili.co/tv/search"
UserSearch = "http://s.search.bilibili.co"
[searchClient]
key = "d1941c223cebf297"
secret = "33a369a06f3fcec200dfb9d9f5e8a868"
dial = "500ms"
timeout = "2s"
keepAlive = "60s"
timer = 10
[TVApp]
MobiApp = "android"
Build = "1007"
Platform = "android"
[Homepage]
FollowSize = 10
MaxRecomIntervs = 5
LoadDuration = "10m"
HideIndexShow = ["cn","jp"]
[httpClient]
key = "cadf599ba8b3796a"
secret = "42fbb979aa742013d713a088f912673b"
dial = "500ms"
timeout = "2s"
keepAlive = "60s"
timer = 10
[httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[PlayurlClient]
key = "cadf599ba8b3796a"
secret = "42fbb979aa742013d713a088f912673b"
dial = "500ms"
timeout = "500ms"
keepAlive = "60s"
timer = 10
[PlayurlClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[mysql]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_tv?timeout=2s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "200ms"
tranTimeout = "200ms"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 100
ratio = 0.5
request = 100
[memcache]
name = "tv/memcache"
proto = "tcp"
addr = "172.18.33.60:11231"
active = 50
idle = 10
dialTimeout = "50ms"
readTimeout = "100ms"
writeTimeout = "100ms"
idleTimeout = "80s"
relateExpire = "20m"
arcExpire = "1h"
viewExpire = "1h"
cmsExpire = "36h"
hisExpire = "20m"
mangoExpire = "10m"
[log]
dir = "/data/log/tv/"
stdout = true
[archiveRPC]
timeout = "1s"
[accountRPC]
timeout = "1s"
[favoriteRPC]
timeout = "1s"
[hisRPC]
timeout = "3s"
[cfg]
zoneps = 40
ModsReload = "70s"
TypesReload = "1h"
PageReload = "30m"
IndexShowReload = "20m"
EsIntervReload = "2m"
DefaultSplash = "http://i0.hdslb.com/bfs/tvcover/39a900048b7f2e969661b03e72cf836fde2226e3.png"
FavPs = 50
PGCFilterBuild = 1011
vipQns = ["74","112","116","64"]
[cfg.AuditSign]
key = "bili"
secret = "bili.tv"
[cfg.EmptyArc]
ChanSize = 1024
UnshelvePS = 2
[cfg.ZonesInfo]
pgcZonesID = [1,2,3,4,5]
ugcZonesID = [6,7,8,9,10]
zonesName = ["jp", "cn", "tv", "movie", "documentary"]
TargetTypes = [3,4,36,155,160]
ugcTypes = [160, 36, 4, 155, 3, 5, 167, 1, 165, 181, 129, 119]
oldIdxMapping = {3=6,4=7,36=8,160=9,155=10}
oldIdxJump = 1
[cfg.AuthMsg]
pgcOffline = "该内容已经PGC下架了先看看别的吧"
cmsInvalid = "该内容已经CMS下架了先看看别的吧"
licenseReject = "云视听播控平台鉴权失败,该内容暂时无法观看"
[cfg.Dangbei]
Pagesize = 50
Expire = "1m"
MangoPS = 30
[cfg.HisCfg]
Pagesize = 1000
Businesses = ["archive","pgc"]
[cfg.EsIdx]
[cfg.EsIdx.PgcIdx]
Business = "pgc_media"
Index = "pgc_media"
[cfg.EsIdx.UgcIdx]
Business = "tv_ugc_archive"
Index = "tv_ugc_archive"
[cfg.IndexLabel]
Fre = "30m"
PGCOrder = ["style_id","season_version","area","is_finish","copyright","season_status","season_month","producer_id","pubdate","year"]
UGCOrder = ["typeid","pubtime"]
YearParam = ["pub_date","year"]
[cfg.IndexLabel.YearV]
[cfg.IndexLabel.YearV."80年代"]
dur = "1980-1989"
[cfg.IndexLabel.YearV."90年代"]
dur = "1990-1999"
[cfg.IndexLabel.YearV."更早"]
dur = "1900-1979"
[cfg.vipMark]
V1HideChargeable = false
EpFree = 2
[cfg.vipMark.EP]
Title = "vip"
Cover = "http://i0.hdslb.com/bfs/tvcover/39a900048b7f2e969661b03e72cf836fde2226e3.png"
[cfg.vipMark.LoadepMsg]
Build = 101500
Msg = "当前版本不支持会员内容播放,请升级至最新版本哦"
[cfg.SnVipCorner]
Title = "sn_vip_title"
Cover = "sn_vip_cover"
[HTTPServer]
addr = "0.0.0.0:6683"
timeout = "1s"
[identify]
whiteAccessKey = "a2a1eb0ac97d6ba08b85aa0151528f34"
whiteMid = 23675773
csrf = false
[identify.app]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 10
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.co"
secret = "http://open.bilibili.co"
[identify.httpClient]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[redis]
name = "tv-interface"
proto = "tcp"
#addr = "172.18.33.61:6886"
addr = "localhost:6379"
active = 10
idle = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "24h"
cron = "*/5 * * * * *"
[Host]
data = "http://data.bilibili.co"
ApiIndex = "http://api.bilibili.co/pgc/internal/tv/rank/index"
ApiZone = "http://api.bilibili.co/pgc/internal/tv/rank/list"
APIFollow = "http://bangumi.bilibili.com/api/mydynamic_follows"
APIMedia = "https://bangumi.bilibili.com/view/api/season"
APIMediaV2 = "http://api.bilibili.com/pgc/view/app/season"
APIRecom = "https://bangumi.bilibili.com/api/season/v2/recommend"
ApiNewindex = "http://api.bilibili.co/pgc/internal/season/cards"
UgcPlayURL = "http://uat-tv-ugc.bilibili.co/v2/playurl"
AIUgcType = "http://data-test.bilibili.co/data/rank/all_region-%d-tv.json"
FavAdd = "http://api.bilibili.co/x/internal/v2/fav/video/add"
FavDel = "http://api.bilibili.co/x/internal/v2/fav/video/del"
ESHost = "http://uat-manager.bilibili.co"
ApiCo = "http://uat-api.bilibili.co"
[Wild]
[Wild.wildSearch]
userNum = 1
userVideoLimit = 1
biliUserNum = 1
biliUserVl = 1
seasonNum = 3
movieNum = 3
seasonMore = 1
movieMore = 1
[region]
stopSpan = "5m"
[style]
labelSpan = "5m"
[IP]
[IP.White]
TvVip = ["183.3.131.82", "10.23.162.22"]

View File

@@ -0,0 +1,49 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"cache.go",
"conf.go",
"homepage.go",
"search_sug.go",
"zone.go",
],
importpath = "go-common/app/interface/main/tv/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,24 @@
package conf
import (
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
xtime "go-common/library/time"
)
// Redis redis
type Redis struct {
*redis.Config
Expire xtime.Duration
}
// Memcache config
type Memcache struct {
*memcache.Config
RelateExpire xtime.Duration
ViewExpire xtime.Duration
ArcExpire xtime.Duration
CmsExpire xtime.Duration
HisExpire xtime.Duration
MangoExpire xtime.Duration
}

View File

@@ -0,0 +1,288 @@
package conf
import (
"errors"
"flag"
"go-common/app/interface/main/tv/model"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
// Conf global variable.
var (
Conf = &Config{}
confPath string
client *conf.Client
)
// Config struct of conf.
type Config struct {
// zone configure
Newzone map[string]*PageCfg
// log
Log *log.Config
// tracer
Tracer *trace.Config
// http server config
HTTPServer *bm.ServerConfig
// auth
Auth *auth.Config
// verify
Verify *verify.Config
// mysql
Mysql *sql.Config
// memcache
Memcache *Memcache
// app
TVApp *TVApp
// homepage settings
Homepage *PageConf
// HTTPClient .
HTTPClient *bm.ClientConfig
PlayurlClient *bm.ClientConfig
SearchClient *bm.ClientConfig
// Redis
Redis *Redis
// Cfg common configuration
Cfg *Cfg
// Search Config
Search *Search
// RPC config
ArcClient *warden.ClientConfig
AccClient *warden.ClientConfig
HisRPC *rpc.ClientConfig
FavoriteRPC *rpc.ClientConfig
TvVipClient *warden.ClientConfig
// Ip Whitelist
IP *IP
// ecode
Ecode *ecode.Config
// api url
Host *Host
Region *Region
Style *Style
Wild *Wild
}
// IPWhite .
type IPWhite struct {
TvVip []string
}
// IP .
type IP struct {
White *IPWhite
}
// Style label .
type Style struct {
LabelSpan xtime.Duration
}
// Region .
type Region struct {
StopSpan xtime.Duration // get region time span
}
// IndexLabel def.
type IndexLabel struct {
Fre xtime.Duration
PGCOrder []string // pgc order
UGCOrder []string // ugc order
YearV map[string]*YearVDur // year value pair
YearParam []string // year params = pub_date, year
}
// YearVDur def
type YearVDur struct {
Dur string `json:"dur"`
}
// IsYear distinguishes whether the param is year type param
func (u *IndexLabel) IsYear(param string) bool {
for _, v := range u.YearParam {
if v == param {
return true
}
}
return false
}
// Host api urls
type Host struct {
Data string // data.bilibili.co
APIIndex string // homepage pgc data source
APIZone string // zonepage pgc data source
APIFollow string // pgc follow
APIMedia string // pgc media detail
APIMediaV2 string // pgc media detail v2
APIRecom string // pgc recom
APINewindex string // pgc index_show
UgcPlayURL string // ugc play url
AIUgcType string // ai ugc type data
APICo string
FavAdd string // favorite add url
FavDel string // favorite del url
ReqURL string // version update request url
ESHost string // manager url
}
// Wild .
type Wild struct {
WildSearch *WildSearch
}
// WildSearch wild search .
type WildSearch struct {
UserNum int
UserVideoLimit int
BiliUserNum int
BiliUserVl int
SeasonNum int
MovieNum int
SeasonMore int
MovieMore int
}
// Cfg def.
type Cfg struct {
ZonePs int // Zone index page size
AuthMsg *AuthMsg // auth error message config
ZonesInfo *ZonesInfo // all the zones info
Dangbei *Dangbei // dangbei configuration
PageReload xtime.Duration // all page reload duration
IndexShowReload xtime.Duration // index show reload duration
EsIntervReload xtime.Duration // es intervention reload duration
DefaultSplash string // default splash url
FavPs int // favorite cfg
PGCFilterBuild int // the build number, under which we export only pgc modules and data
VipQns []string // the qualities dedicated for vips
HisCfg *HisCfg // history related cfg
EsIdx *EsIdx // elastic search index page cfg
IndexLabel *IndexLabel // index label cfg
EmptyArc *EmptyArc // chan size
VipMark *VipMark // vip mark
SnVipCorner *model.SnVipCorner // season vip corner mark cfg
AuditSign *AuditSign
}
// AuditSign cfg is used to check license owner requests
type AuditSign struct {
Key string
Secret string
}
// TvVip def.
type TvVip struct {
Build int64
Msg string
}
// VipMark def.
type VipMark struct {
V1HideChargeable bool // whether we hide chargeable episode in pgc view V1
EpFree int // ep's pay status which means free
EP *model.CornerMark
LoadepMsg *TvVip // tv vip cfg
}
// EmptyArc def.
type EmptyArc struct {
ChanSize int64
UnshelvePS int
}
// EsIdx def.
type EsIdx struct {
UgcIdx, PgcIdx *EsCfg
}
// EsCfg def.
type EsCfg struct {
Business string
Index string
}
// HisCfg def.
type HisCfg struct {
Businesses []string
Pagesize int
}
// Dangbei cfg def.
type Dangbei struct {
Pagesize int64 // dangbei api page size
MangoPS int // mango page size
Expire xtime.Duration // dangbei page ID expiration
}
// AuthMsg configures the auth error messages
type AuthMsg struct {
PGCOffline string // offline pgc
CMSInvalid string // cms not valid
LicenseReject string // license owner rejected
}
// App config
type App struct {
*bm.App
}
func configCenter() (err error) {
if client, err = conf.New(); err != nil {
panic(err)
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}
func init() {
flag.StringVar(&confPath, "conf", "", "config path")
}
// Init init conf.
func Init() (err error) {
if confPath == "" {
return configCenter()
}
_, err = toml.DecodeFile(confPath, &Conf)
return
}

View File

@@ -0,0 +1,14 @@
package conf
// TVApp defines the configuration on behalf of the tv app
type TVApp struct {
MobiApp string
Build string
Platform string
}
// PageConf defines the configuration for the home page info
type PageConf struct {
FollowSize int
HideIndexShow []string // the zones that we need to hide the index show part
}

View File

@@ -0,0 +1,16 @@
package conf
import xtime "go-common/library/time"
// Search related config def.
type Search struct {
URL string
MainVer string
SugNum int
SugPGCBuild int
SugType string
Highlight string // use highlight or not
HotwordFre xtime.Duration // hotword reload frequency
ResultURL string
UserSearch string
}

View File

@@ -0,0 +1,23 @@
package conf
// PageCfg cfg
type PageCfg struct {
Name string // page name
Top int // recom positions
Middle int // last updated
Bottom int // bottom list
TopM int // top interv module id
MiddleM int // middle interv module id
}
// ZonesInfo loads all the zones' ID and name
type ZonesInfo struct {
PGCZonesID []int // all the zones' ID that need to be loaded
UGCZonesID []int16 // all the ugc zones' ID
PageIDs []int // all the page ID's
ZonesName []string // all the zones' name that need to be loaded
TargetTypes []int32 // ugc types that we could pick source data
UgcTypes []int32 // ugc archive type order, for search types listing
OldIdxJump int // when the module is UGC and we can't find the old index page corresponding, we tell the client to jump to this category's index page
OldIdxMapping map[string]int // ugc idx ID old and new mapping
}

View File

@@ -0,0 +1,55 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"card_test.go",
"dao_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/service/main/account/model:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"card.go",
"dao.go",
],
importpath = "go-common/app/interface/main/tv/dao/account",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,35 @@
package account
import (
"context"
accwar "go-common/app/service/main/account/api"
accmdl "go-common/app/service/main/account/model"
"go-common/library/ecode"
"go-common/library/log"
)
// Card3 get card info by mid
func (d *Dao) Card3(c context.Context, mid int64) (res *accmdl.Card, err error) {
var (
arg = &accwar.MidReq{Mid: mid}
reply *accwar.CardReply
)
if reply, err = d.accClient.Card3(c, arg); err != nil || reply == nil || reply.Card == nil {
if err != nil {
log.Error("s.accDao.Info(%d) error(%v)", mid, err)
}
err = ecode.AccessDenied
return
}
res = reply.Card
return
}
// IsVip checks whether the member is vip
func IsVip(card *accmdl.Card) bool {
if card.Vip.Type == 0 || card.Vip.Status == 0 || card.Vip.Status == 2 || card.Vip.Status == 3 {
return false
}
return true
}

View File

@@ -0,0 +1,51 @@
package account
import (
"context"
"testing"
accmdl "go-common/app/service/main/account/model"
"go-common/library/ecode"
"github.com/smartystreets/goconvey/convey"
)
func TestAccountCard3(t *testing.T) {
var (
c = context.Background()
mid = int64(27515256)
)
convey.Convey("Card3", t, func(ctx convey.C) {
res, err := d.Card3(c, mid)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
res, err = d.Card3(c, 777777777777)
ctx.So(err, convey.ShouldEqual, ecode.AccessDenied)
ctx.So(res, convey.ShouldBeNil)
})
}
func TestAccountIsVip(t *testing.T) {
var (
cardFalse = &accmdl.Card{}
// card.Vip.Type == 0 || card.Vip.Status == 0 || card.Vip.Status == 2 || card.Vip.Status == 3
cardTrue = &accmdl.Card{
Vip: accmdl.VipInfo{
Type: 1,
Status: 1,
},
}
)
convey.Convey("IsVip", t, func(ctx convey.C) {
p1 := IsVip(cardFalse)
ctx.Convey("Then p1 should be false.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldBeFalse)
})
p2 := IsVip(cardTrue)
ctx.Convey("Then p2 should be true.", func(ctx convey.C) {
ctx.So(p2, convey.ShouldBeTrue)
})
})
}

View File

@@ -0,0 +1,22 @@
package account
import (
"go-common/app/interface/main/tv/conf"
accwar "go-common/app/service/main/account/api"
)
// Dao is account dao.
type Dao struct {
// rpc
accClient accwar.AccountClient
}
// New account dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{}
var err error
if d.accClient, err = accwar.NewClient(c.AccClient); err != nil {
panic(err)
}
return
}

View File

@@ -0,0 +1,34 @@
package account
import (
"flag"
"os"
"testing"
"go-common/app/interface/main/tv/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}

View File

@@ -0,0 +1,76 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"homepage_test.go",
"intervention_test.go",
"module_test.go",
"pgc_cards_test.go",
"recommend_test.go",
"redis_test.go",
"region_test.go",
"zone_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"homepage.go",
"intervention.go",
"module.go",
"pgc_cards.go",
"recommend.go",
"redis.go",
"region.go",
"zone.go",
],
importpath = "go-common/app/interface/main/tv/dao/app",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/search:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/stat/prom:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,50 @@
package dao
import (
"time"
"go-common/app/interface/main/tv/conf"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"go-common/library/stat/prom"
)
// Dao .
type Dao struct {
db *sql.DB
conf *conf.Config
client *bm.Client
redis *redis.Pool
mc *memcache.Pool
dbeiExpire int64
}
// New .
func New(c *conf.Config) *Dao {
return &Dao{
db: sql.NewMySQL(c.Mysql),
conf: c,
client: bm.NewClient(c.HTTPClient),
redis: redis.NewPool(c.Redis.Config),
dbeiExpire: int64(time.Duration(c.Cfg.Dangbei.Expire) / time.Second),
mc: memcache.NewPool(c.Memcache.Config),
}
}
// Prom
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
)
// PromError prom error
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo add prom info
func PromInfo(name string) {
infosCount.Incr(name)
}

View File

@@ -0,0 +1,53 @@
package dao
import (
"context"
"flag"
"os"
"strings"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
ctx = context.TODO()
)
func init() {
// dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
// flag.Set("conf", dir)
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
d.client.SetTransport(gock.DefaultTransport)
return r
}

View File

@@ -0,0 +1,74 @@
package dao
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// Zones represents the different zones to display on the homepage recommendation area
var Zones = []string{"jp", "cn", "tv", "movie", "documentary"}
// HeaderData gets the header data from PGC API
func (d *Dao) HeaderData(ctx context.Context, appInfo *conf.TVApp) (result map[string][]*model.Card, err error) {
var (
res = new(struct {
Code int `json:"code"`
Result map[string][]*model.Card `json:"result"`
})
bangumiURL = d.conf.Host.APIIndex
params = url.Values{}
)
params.Set("build", appInfo.Build)
params.Set("mobi_app", appInfo.MobiApp)
params.Set("platform", appInfo.Platform)
if err = d.client.Get(ctx, bangumiURL, "", params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), bangumiURL+"?"+params.Encode())
return
}
result = res.Result
// check result contain the 5 type data
for _, v := range Zones {
_, ok := result[v]
if !ok {
err = fmt.Errorf("Result Miss Data: %s", v)
return
}
}
return
}
// FollowData gets the follow data from PGC API
func (d *Dao) FollowData(ctx context.Context, appInfo *conf.TVApp, accessKey string) (result []*model.Follow, err error) {
var (
bangumiURL = d.conf.Host.APIFollow
params = url.Values{}
res = model.ResFollow{}
)
params.Set("access_key", accessKey)
params.Set("build", appInfo.Build)
params.Set("mobi_app", appInfo.MobiApp)
params.Set("platform", appInfo.Platform)
params.Set("pagesize", fmt.Sprintf("%d", d.conf.Homepage.FollowSize))
if err = d.client.Get(ctx, bangumiURL, "", params, &res); err != nil {
log.Error("FollowData ERROR:%v", err)
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), bangumiURL+"?"+params.Encode())
log.Error("FollowData ERROR:%v", err)
return
}
result = res.Result
return
}

View File

@@ -0,0 +1,63 @@
package dao
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoHeaderData(t *testing.T) {
var (
ctx = context.Background()
appInfo = d.conf.TVApp
normalStr = `{"code":0,"message":"success","result":{"cn":[{"cover":"http://i0.hdslb.com/bfs/bangumi/1fb653e1e9a825ef1487216c834d3f72c647a8aa.jpg","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/4bbd70afe81fd1c483ca61a038a8a983358ef3ac.jpg","id":254310,"index":"34","index_show":"连载中"},"season_id":24438,"title":"小绿和小蓝"}],"documentary":[{"cover":"http://i0.hdslb.com/bfs/bangumi/30daefc74e09e9cb1a3e8771226002efe186aaf6.jpg","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/efe3a69efe4779662d4eff11f12e4b94cc3e52b6.jpg","id":253992,"index":"花絮12","index_show":"连载中"},"season_id":25810,"title":"历史那些事"}],"jp":[{"cover":"http://i0.hdslb.com/bfs/bangumi/a4c0e0ccc44fe3949a734f546cf5bb07da925bad.png","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/523e3b8faee592c683ea55cfdcdb26106651fdea.jpg","id":250465,"index":"6","index_show":"连载中"},"season_id":25739,"title":"关于我转生变成史莱姆这档事"}],"movie":[{"cover":"http://i0.hdslb.com/bfs/bangumi/75c7528cbf3254dd20a4512376ced74733ab98ef.jpg","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/8beff40b27076475353d25ca590d0932791f71d6.jpg","id":253907,"index":"中文","index_show":"全1话"},"season_id":25944,"title":"黑子的篮球 LAST GAME"}],"tv":[{"cover":"http://i0.hdslb.com/bfs/bangumi/f79e3d4bb24d73db6407d771a4293d737376a11a.jpg","new_ep":{"cover":"http://i1.hdslb.com/bfs/archive/e24e3c851a81d46d3706d7d5401d63e0f6d9453e.jpg","id":143246,"index ":"30 ","index_show ":"全30话 "},"season_id ":21448,"title ":"亮剑"}]}}`
httpErrStr = `{"code": -400}`
missingZoneStr = `{"code":0,"message":"success","result":{}}`
)
convey.Convey("HeaderData Normal Situation", t, func(c convey.C) {
c.Convey("Then err should be nil.result should not be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.APIIndex).Reply(200).JSON(normalStr)
result, err := d.HeaderData(ctx, appInfo)
cx.So(err, convey.ShouldBeNil)
cx.So(result, convey.ShouldNotBeNil)
})
c.Convey("Http err, Then err Should not be nil", func(cx convey.C) {
httpMock("GET", d.conf.Host.APIIndex).Reply(200).JSON(httpErrStr)
_, err := d.HeaderData(ctx, appInfo)
cx.So(err, convey.ShouldNotBeNil)
fmt.Println(err)
})
c.Convey("Then err should be about the zone", func(cx convey.C) {
httpMock("GET", d.conf.Host.APIIndex).Reply(200).JSON(missingZoneStr)
_, err := d.HeaderData(ctx, appInfo)
cx.So(err, convey.ShouldNotBeNil)
cx.So(err.Error(), convey.ShouldEqual, "Result Miss Data: jp")
fmt.Println(err)
})
})
}
func TestDaoFollowData(t *testing.T) {
var (
ctx = context.Background()
appInfo = d.conf.TVApp
accessKey = "9dfa21e5d98f0be3410974f6894b72af"
normalStr = `{"code":0,"count":"59","message":"success","pages":"6","result":[{"actor":[],"alias":"ラーメン大好き小泉さん","allow_bp":"1","allow_download":"0","area":"日本","bangumi_id":"3905","bangumi_title":"爱吃拉面的小泉同学","brief":"今天,她也在某处享用着拉面——\n冷淡而沉默不和他人亲近的神秘转学生小泉同学。她其实是每天追求着美...","copyright":"bilibili","cover":"http://i0.hdslb.com/bfs/bangumi/e4dcc80598a4133af6b1a880bb8006fa5346f31b.jpg","danmaku_count":"436189","ed_jump":5,"episodes":[],"evaluate":"","favorites":"975275","is_finish":"1","is_started":1,"last_time":"2018-03-22 19:00:00.0","limitGroupId":1193,"new_cover":"http://i0.hdslb.com/bfs/archive/a06bbc48bc0c5c95afe0d2875d12376fc764807a.jpg","new_ep":{"av_id":"21083439","cover":"http://i0.hdslb.com/bfs/archive/a06bbc48bc0c5c95afe0d2875d12376fc764807a.jpg","danmaku":"34591825","episode_id":"164993","episode_status":2,"from":"bangumi","index":"12","index_title":"名古屋 / 再会","page":"1","up":{},"update_time":"2018-03-22 19:00:00.0"},"newest_ep_id":"164993","newest_ep_index":"12","play_count":"14835307","progress":"全12话","pub_string":"","pub_time":"2018-01-04 19:00:00","related_seasons":[],"season_id":"21728","season_status":2,"season_title":"TV","seasons":[],"share_url":"http://bangumi.bilibili.com/anime/21728/","spid":"0","squareCover":"http://i0.hdslb.com/bfs/bangumi/a160be35d676316efeb53157001b8df257a48a77.jpg","staff":"","tag2s":[],"tags":[{"cover":"http://i0.hdslb.com/bfs/bangumi/29baa04d18505c775c131e0d0db0bf8704cc61bb.jpg","tag_id":"21","tag_name":"治愈"},{"cover":"http://i0.hdslb.com/bfs/bangumi/0a89e6fc2da1a8f714ef3872d3b58df7137eb195.jpg","tag_id":"106","tag_name":"美食"},{"cover":"http://i0.hdslb.com/bfs/bangumi/3121473d5dd03a9bcccb8490034207e724e731b3.jpg","tag_id":"135","tag_name":"漫画改"}],"title":"爱吃拉面的小泉同学","total_count":"12","trailerAid":"-1","update_pattern":"","user_season":{"attention":"0","bp":0,"last_ep_index":"","last_time":"0","report_ts":0},"watchingCount":"0","weekday":"4"}]}`
httpErrStr = `{code": -400}`
)
convey.Convey("FollowData", t, func(c convey.C) {
c.Convey("Then err should be nil.result should not be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.APIFollow).Reply(200).JSON(normalStr)
result, err := d.FollowData(ctx, appInfo, accessKey)
cx.So(err, convey.ShouldBeNil)
cx.So(result, convey.ShouldNotBeNil)
})
c.Convey("Then err should not be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.APIFollow).Reply(200).JSON(httpErrStr)
_, err := d.FollowData(ctx, appInfo, accessKey)
cx.So(err, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,160 @@
package dao
import (
"context"
"database/sql"
"fmt"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/search"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_intervs = "SELECT cont_id, cont_type FROM tv_rank WHERE is_deleted = 0"
_parseInterv = "SELECT cont_id, cont_type FROM tv_rank "
_modIntervs = "%s WHERE module_id = %d AND is_deleted = 0 ORDER BY position ASC LIMIT %d"
_rankList = "%s WHERE rank = %d AND category = %d AND is_deleted = 0 ORDER BY position ASC LIMIT %d"
_rmPGCInterv = "UPDATE tv_rank SET is_deleted = 1 WHERE cont_type != 2 AND is_deleted = 0 AND cont_id IN (%s)"
_rmUGCInterv = "UPDATE tv_rank SET is_deleted = 1 WHERE cont_type = 2 AND is_deleted = 0 AND cont_id IN (%s)"
_idxIntervs = "SELECT rank, category, cont_id FROM tv_rank WHERE category IN (?,?) AND rank > 0 AND is_deleted = 0 ORDER BY position ASC"
_pgcIdxInterv = 6
_ugcIdxInterv = 7
)
// ModIntervs get intervention data with a given mod ID
func (d *Dao) ModIntervs(c context.Context, modID int, capacity int) (resp *model.RespModInterv, err error) {
var sql = fmt.Sprintf(_modIntervs, _parseInterv, modID, capacity)
return d.rowsTreat(c, sql)
}
// ZoneIntervs get db data
func (d *Dao) ZoneIntervs(c context.Context, req *model.ReqZoneInterv) (resp *model.RespModInterv, err error) {
var sql = fmt.Sprintf(_rankList, _parseInterv, req.RankType, req.Category, req.Limit)
return d.rowsTreat(c, sql)
}
// IdxIntervs def.
func (d *Dao) IdxIntervs(c context.Context) (idxSave *search.IdxIntervSave, err error) {
var (
rows *xsql.Rows
)
idxSave = &search.IdxIntervSave{
Pgc: make(map[int][]int64),
Ugc: make(map[int][]int64),
}
if rows, err = d.db.Query(c, _idxIntervs, _pgcIdxInterv, _ugcIdxInterv); err != nil {
log.Error("IdxIntervs d.db.Query (%s) error(%v)", _idxIntervs, err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.Rank{}
if err = rows.Scan(&li.Rank, &li.Category, &li.ContID); err != nil {
return
}
if li.Category == _pgcIdxInterv {
idxSave.Pgc[li.Rank] = append(idxSave.Pgc[li.Rank], li.ContID)
} else {
idxSave.Ugc[li.Rank] = append(idxSave.Ugc[li.Rank], li.ContID)
}
}
err = rows.Err()
return
}
// AllIntervs picks all the active intervention data
func (d *Dao) AllIntervs(c context.Context) (sids []int64, aids []int64, err error) {
var (
pgcMap = make(map[int64]int)
ugcMap = make(map[int64]int)
)
rows, err := d.db.Query(c, _intervs)
if err != nil {
log.Error("AllIntervs d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.SimpleRank{}
if err = rows.Scan(&li.ContID, &li.ContType); err != nil {
log.Error("AllIntervs row.Scan error(%v)", err)
return
}
if li.IsUGC() {
ugcMap[li.ContID] = 1
} else {
pgcMap[li.ContID] = 1
}
}
for avid := range ugcMap {
aids = append(aids, avid)
}
for sid := range pgcMap {
sids = append(sids, sid)
}
log.Info("AllIntervs Distinct UGC Intervs %d, PGC Intervs %d", len(aids), len(sids))
return
}
func (d *Dao) rowsTreat(c context.Context, sql string) (resp *model.RespModInterv, err error) {
var ranks []*model.SimpleRank
resp = new(model.RespModInterv)
rows, err := d.db.Query(c, sql)
if err != nil {
log.Error("ZoneIntervs d.db.Query (%s) error(%v)", sql, err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.SimpleRank{}
if err = rows.Scan(&li.ContID, &li.ContType); err != nil {
log.Error("ZoneIntervs row.Scan error(%v)", err)
return
}
ranks = append(ranks, li)
}
resp = &model.RespModInterv{
Ranks: ranks,
}
for _, v := range ranks {
if v.IsUGC() {
resp.AIDs = append(resp.AIDs, v.ContID)
} else {
resp.SIDs = append(resp.SIDs, v.ContID)
}
}
return
}
// RmInterv removes invalids interventions
func (d *Dao) RmInterv(c context.Context, aids []int64, sids []int64) (err error) {
var (
resPGC, resUGC sql.Result
rowsPGC, rowsUGC int64
)
if len(sids) > 0 {
if resPGC, err = d.db.Exec(c, fmt.Sprintf(_rmPGCInterv, xstr.JoinInts(sids))); err != nil {
log.Error("RmInterv Sids %v, Err %v", sids, err)
return
}
if rowsPGC, err = resPGC.RowsAffected(); err != nil {
log.Error("RmInterv Sids %v, Err %v", sids, err)
return
}
}
if len(aids) > 0 {
if resUGC, err = d.db.Exec(c, fmt.Sprintf(_rmUGCInterv, xstr.JoinInts(aids))); err != nil {
log.Error("RmInterv Aids %v, Err %v", aids, err)
return
}
if rowsUGC, err = resUGC.RowsAffected(); err != nil {
log.Error("RmInterv Aids %v, Err %v", aids, err)
return
}
}
log.Warn("RmInterv Aids %v, Sids %v, UGCDel Rows %d, PGCDel Rows %d", aids, sids, rowsUGC, rowsPGC)
return
}

View File

@@ -0,0 +1,98 @@
package dao
import (
"encoding/json"
"fmt"
"testing"
"go-common/app/interface/main/tv/model"
"context"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_ModIntervs(t *testing.T) {
Convey("TestDao_ModIntervs", t, WithDao(func(d *Dao) {
var modID int
if err := d.db.QueryRow(ctx,
"SELECT module_id FROM tv_rank "+
"WHERE is_deleted = 0 AND module_id != 0 AND category = 5 AND type = 1"+
"LIMIT 1").Scan(&modID); err != nil || modID == 0 {
return
}
sids, err := d.ModIntervs(ctx, modID, 10)
fmt.Println(sids)
So(err, ShouldBeNil)
}))
}
func TestDao_ZoneIntervs(t *testing.T) {
Convey("TestDao_ZoneIntervs", t, WithDao(func(d *Dao) {
// home page
resp, err := d.ZoneIntervs(ctx, &model.ReqZoneInterv{
RankType: 0,
Category: 1,
Limit: 50,
})
for _, v := range resp.Ranks {
fmt.Println(v)
}
So(err, ShouldBeNil)
So(len(resp.Ranks), ShouldBeGreaterThan, 0)
}))
}
func TestDao_AllIntervs(t *testing.T) {
Convey("TestDao_AllIntervs", t, WithDao(func(d *Dao) {
var countUGC, countPGC int64
d.db.QueryRow(ctx, "SELECT COUNT(1) FROM tv_rank WHERE is_deleted =0 AND cont_type = 2 ").Scan(&countUGC)
d.db.QueryRow(ctx, "SELECT COUNT(1) FROM tv_rank WHERE is_deleted =0 AND cont_type != 2 ").Scan(&countPGC)
sids, aids, err := d.AllIntervs(ctx)
So(err, ShouldBeNil)
if countUGC > 0 {
So(len(aids), ShouldBeGreaterThan, 0)
fmt.Println(aids)
} else {
fmt.Println("empty ugc rank")
}
if countPGC > 0 {
So(len(sids), ShouldBeGreaterThan, 0)
fmt.Println(sids)
} else {
fmt.Println("empty pgc ranks")
}
}))
}
func TestDao_RmInterv(t *testing.T) {
Convey("TestDao_RmInterv", t, WithDao(func(d *Dao) {
var aid, sid int64
d.db.QueryRow(ctx, "SELECT cont_id FROM tv_rank WHERE is_deleted =0 AND cont_type = 2 LIMIT 1").Scan(&aid)
d.db.QueryRow(ctx, "SELECT cont_id FROM tv_rank WHERE is_deleted =0 AND cont_type != 2 LIMIT 1 ").Scan(&sid)
if aid > 0 {
err := d.RmInterv(context.Background(), []int64{aid}, []int64{})
So(err, ShouldBeNil)
d.db.Exec(ctx, "UPDATE tv_rank SET is_deleted = 0 WHERE cont_id = ?", aid)
} else {
fmt.Println("empty ugc rank")
}
if sid > 0 {
err := d.RmInterv(context.Background(), []int64{}, []int64{sid})
So(err, ShouldBeNil)
d.db.Exec(ctx, "UPDATE tv_rank SET is_deleted = 0 WHERE cont_id = ?", sid)
} else {
fmt.Println("empty pgc ranks")
}
}))
}
func TestDao_IdxIntervs(t *testing.T) {
Convey("TestDao_IdxIntervs", t, WithDao(func(d *Dao) {
res, err := d.IdxIntervs(ctx)
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
qq, _ := json.Marshal(res)
fmt.Println(string(qq))
}))
}

View File

@@ -0,0 +1,55 @@
package dao
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_modPage = "SELECT id,page_id,type,title,icon,source,capacity,flexible, more, `order`,moretype,morepage,src_type FROM tv_modules WHERE page_id = ? AND deleted = 0 AND valid = 1 ORDER BY `order`"
_passedSn = "SELECT id FROM tv_ep_season WHERE is_deleted = 0 AND `check` = 1 AND valid = 1"
)
// ModPage gets all the modules in one page
func (d *Dao) ModPage(ctx context.Context, pid int) (modules []*model.Module, err error) {
var rows *sql.Rows
rows, err = d.db.Query(ctx, _modPage, pid)
if err != nil {
log.Error("ModPage.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
a := &model.Module{}
if err = rows.Scan(&a.ID, &a.PageID, &a.Type, &a.Title, &a.Icon, &a.Source, &a.Capacity, &a.Flexible, &a.More, &a.Order, &a.MoreType, &a.MorePage, &a.SrcType); err != nil {
log.Error("ModPage row.Scan error(%v)", err)
return
}
modules = append(modules, a)
}
return
}
// PassedSns gets all passed seasons, to prepare their index_show data
func (d *Dao) PassedSns(ctx context.Context) (ids []int64, err error) {
rows, err := d.db.Query(ctx, _passedSn)
if err != nil {
log.Error("PassedSns.Query error(%v)", err)
return nil, err
}
defer rows.Close()
for rows.Next() {
var sid int64
if err = rows.Scan(&sid); err != nil {
log.Error("PassedSns row.Scan error(%v)", err)
return nil, err
}
ids = append(ids, sid)
}
if err = rows.Err(); err != nil {
log.Error("PassedSns Rows Err %v,")
}
return
}

View File

@@ -0,0 +1,30 @@
package dao
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_ModPage(t *testing.T) {
Convey("TestDao_ModPage", t, WithDao(func(d *Dao) {
// home page
homeMods, err := d.ModPage(ctx, 0)
So(err, ShouldBeNil)
So(len(homeMods), ShouldBeGreaterThan, 0)
// jp page
jpMods, err2 := d.ModPage(ctx, 1)
So(err2, ShouldBeNil)
So(len(jpMods), ShouldBeGreaterThan, 0)
}))
}
func TestDao_PassedSns(t *testing.T) {
Convey("TestDao_PassedSns", t, WithDao(func(d *Dao) {
ids, err := d.PassedSns(ctx)
So(err, ShouldBeNil)
So(len(ids), ShouldBeGreaterThan, 0)
fmt.Println(ids)
}))
}

View File

@@ -0,0 +1,39 @@
package dao
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// PgcCards get season new index from pgc
func (d *Dao) PgcCards(ctx context.Context, ids string) (result map[string]*model.SeasonCard, err error) {
var (
response = &model.PgcResponse{}
params = url.Values{}
pgcSeasonURL = d.conf.Host.APINewindex
)
params.Set("season_ids", ids)
if err = d.client.Get(ctx, pgcSeasonURL, "", params, response); err != nil {
log.Error("PgcCards New Ep ERROR:%v", err)
return
}
if response.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(response.Code), fmt.Sprintf("PgcCards API Error %v", response.Message))
log.Error("PgcCards ERROR:%v, URL: %s", err, pgcSeasonURL+"?"+params.Encode())
return
}
if response.Result == nil {
err = errors.Wrap(ecode.ServerErr, "PgcCards api returns empty")
log.Error("PgcCards ERROR:%v", err)
return
}
result = response.Result
return
}

View File

@@ -0,0 +1,39 @@
package dao
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoPgcCards(t *testing.T) {
var (
ctx = context.Background()
ids = "113,296"
mockStr = `{"code":0,"message":"success","result":{"113":{"badge":"","badge_type":0,"cover":"http://i0.hdslb.com/bfs/bangumi/1c3da58352df66fd409831f7d5e13594c804144e.jpg","is_finish":1,"is_play":1,"is_started":1,"media_id":113,"new_ep":{"cover":"http://i0.hdslb.com/bfs/bangumi/88c8d5f6f149880fa31f6d039ed96f58e7a150f1.jpg","id":113506,"index_show":"全13话"},"rating":{"count":1141,"score":9.5},"rights":{"allow_review":1},"season_id":113,"season_title":"TV","season_type":1,"season_type_name":"番剧","stat":{"danmaku":298320,"follow":277453,"view":6978766},"title":"我们大家的河合庄","total_count":13},"296":{"badge":"","badge_type":0,"cover":"http://i0.hdslb.com/bfs/bangumi/37e22d2feafdf9ea5ad2b39860bd0205fb5a2d1d.png","is_finish":1,"is_play":1,"is_started":1,"media_id":296,"new_ep":{"cover":"http://i0.hdslb.com/bfs/bangumi/ae475384c527366a6ef07b414e1e0364695c2aa8.jpg","id":27915,"index_show":"全13话"},"rating":{"count":643,"score":9.2},"rights":{"allow_review":1},"season_id":296,"season_title":"TV","season_type":1,"season_type_name":"番剧","stat":{"danmaku":342835,"follow":253085,"view":5879216},"title":"天体的秩序","total_count":13}}}`
httpErrStr = `{"code":-500}`
EmptyResStr = `{"code":0}`
)
convey.Convey("PgcCards", t, func(c convey.C) {
c.Convey("Normal Situation, Then err should be nil.result should not be nil.", func(c convey.C) {
httpMock("GET", d.conf.Host.APINewindex).Reply(200).JSON(mockStr)
result, err := d.PgcCards(ctx, ids)
c.So(err, convey.ShouldBeNil)
c.So(result, convey.ShouldNotBeNil)
})
c.Convey("Http code Err Situation, Err can't be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.APINewindex).Reply(200).JSON(httpErrStr)
_, err := d.PgcCards(ctx, ids)
cx.So(err, convey.ShouldNotBeNil)
fmt.Println(err)
})
c.Convey("Empty Res Situation, Then err Should not be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.APINewindex).Reply(200).JSON(EmptyResStr)
_, err := d.PgcCards(ctx, ids)
cx.So(err, convey.ShouldNotBeNil)
fmt.Println(err)
})
})
}

View File

@@ -0,0 +1,47 @@
package dao
import (
"context"
"net/url"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"fmt"
"github.com/pkg/errors"
)
// RecomData gets the recom data from PGC API
func (d *Dao) RecomData(ctx context.Context, appInfo *conf.TVApp, sid string, stype string) (result []*model.Recom, err error) {
var (
bangumiURL = d.conf.Host.APIRecom
params = url.Values{}
response = &model.ResponseRecom{}
)
params.Set("season_id", sid)
params.Set("season_type", stype)
params.Set("build", appInfo.Build)
params.Set("mobi_app", appInfo.MobiApp)
params.Set("platform", appInfo.Platform)
log.Info("[RecomData Request] URL: %s, Params: %s", bangumiURL, params.Encode())
if err = d.client.Get(ctx, bangumiURL, "", params, response); err != nil {
log.Error("RecomData ERROR:%v", err)
return
}
if response.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(response.Code), fmt.Sprintf("Bangumi API Error %v", response.Message))
log.Error("RecomData ERROR:%v, URL: %s", err, bangumiURL+"?"+params.Encode())
return
}
if response.Result == nil {
err = errors.Wrap(ecode.ServerErr, "bangumi api returns empty")
log.Error("RecomData ERROR:%v", err)
return
}
result = response.Result.List
log.Info("[RecomData] For Sid: %s, Stype: %s, Get PGC data NB: %d", sid, stype, len(result))
return
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRecomData(t *testing.T) {
var (
ctx = context.Background()
appInfo = d.conf.TVApp
sid = "20001"
stype = "4"
normalStr = `{"code":0,"message":"success","result":{"from":1,"list":[{"badge":"","badge_type":0,"cover":"http://i0.hdslb.com/bfs/bangumi/3a315d3dd6223adbd69d3361db9474f2d865aafd.jpg","follow_count":1864,"index_show":"全52集","is_finish":1,"is_started":1,"newest_ep_cover":"http://i0.hdslb.com/bfs/archive/aa02da8dc49f1cdfc8562efc50d455a14194287a.jpg","newest_ep_index":"52","play_count":494484,"season_id":22112,"season_status":2,"season_type":5,"season_type_name":"电视剧","title":"猎场DVD版","total_count":52,"url":"http://www.bilibili.com/bangumi/play/ss22112"}],"season_id":20001,"title":""}}`
httpErrStr = `{"code":-400}`
emptyStr = `{"code":0}`
)
convey.Convey("RecomData", t, func(c convey.C) {
c.Convey("Then err should be nil.result should not be nil.", func(c convey.C) {
httpMock("GET", d.conf.Host.APIRecom).Reply(200).JSON(normalStr)
result, err := d.RecomData(ctx, appInfo, sid, stype)
c.So(err, convey.ShouldBeNil)
c.So(result, convey.ShouldNotBeNil)
})
c.Convey("Http Err", func(c convey.C) {
httpMock("GET", d.conf.Host.APIRecom).Reply(200).JSON(httpErrStr)
_, err := d.RecomData(ctx, appInfo, sid, stype)
c.So(err, convey.ShouldNotBeNil)
fmt.Println(err)
})
c.Convey("Empty Result Err", func(c convey.C) {
httpMock("GET", d.conf.Host.APIRecom).Reply(200).JSON(emptyStr)
_, err := d.RecomData(ctx, appInfo, sid, stype)
c.So(err, convey.ShouldNotBeNil)
fmt.Println(err)
})
})
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"fmt"
"go-common/library/cache/redis"
"go-common/library/log"
)
// keyZone gets the key of the zone in Redis
func keyZone(category int) string {
return fmt.Sprintf("zone_idx_%d", category)
}
// ZrevrangeList picks up the page of ids .
func (d *Dao) ZrevrangeList(c context.Context, category int, start, end int) (sids []int64, count int, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := keyZone(category)
if err = conn.Send("ZREVRANGE", key, start, end); err != nil {
log.Error("conn.Do(ZREVRANGE, %s) error(%v)", key, err)
return
}
if err = conn.Send("ZCARD", key); err != nil {
log.Error("conn.Do(ZCARD) err(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush() err(%v)", err)
return
}
if sids, err = redis.Int64s(conn.Receive()); err != nil {
log.Error("redis.Int64s()err(%v)", err)
return
}
if count, err = redis.Int(conn.Receive()); err != nil {
log.Error("redis.INT64 err(%v)", err)
}
return
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyZone(t *testing.T) {
var category = int(0)
convey.Convey("keyZone", t, func(c convey.C) {
p1 := keyZone(category)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoZrevrangeList(t *testing.T) {
var (
c = context.Background()
category = int(1)
start = int(0)
end = int(10)
)
convey.Convey("ZrevrangeList", t, func(ctx convey.C) {
sids, count, err := d.ZrevrangeList(c, category, start, end)
ctx.Convey("Then err should be nil.sids,count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
ctx.So(sids, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,48 @@
package dao
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_selectSQL = "SELECT page_id,title,index_type,index_tid FROM tv_pages WHERE deleted=0 AND valid=1 ORDER BY rank ASC"
_findMaxmTime = "SELECT max(mtime) FROM tv_pages WHERE deleted=0 AND valid=1"
)
// Regions .
func (d *Dao) Regions(c context.Context) (res []*model.Region, err error) {
var rows *sql.Rows
if rows, err = d.db.Query(c, _selectSQL); err != nil {
log.Error("d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.Region)
if err = rows.Scan(&r.PageID, &r.Title, &r.IndexType, &r.IndexTid); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
res = append(res, r)
}
if err = rows.Err(); err != nil {
log.Error("rows.Err() error(%v)", err)
}
return
}
// FindLastMtime .
func (d *Dao) FindLastMtime(c context.Context) (res int64, err error) {
var m time.Time
if err = d.db.QueryRow(c, _findMaxmTime).Scan(&m); err != nil {
log.Error("d.db.QueryRow error(%v)", err)
return
}
res = m.Unix()
return
}

View File

@@ -0,0 +1,34 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoRegions(t *testing.T) {
convey.Convey("Regions", t, func(ctx convey.C) {
var c = context.Background()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Regions(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
println(len(res))
})
})
})
}
func TestDaoFindLastMtime(t *testing.T) {
convey.Convey("Regions", t, func(ctx convey.C) {
var c = context.Background()
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Regions(c)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
println(len(res))
})
})
})
}

View File

@@ -0,0 +1,70 @@
package dao
import (
"context"
"fmt"
"net/url"
"strconv"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// ChannelData gets the header data from PGC API
func (d *Dao) ChannelData(c context.Context, seasonType int, appInfo *conf.TVApp) (result []*model.Card, err error) {
var res struct {
Code int `json:"code"`
Result []*model.Card `json:"result"`
}
bangumiURL := d.conf.Host.APIZone
params := url.Values{}
params.Set("build", appInfo.Build)
params.Set("mobi_app", appInfo.MobiApp)
params.Set("platform", appInfo.Platform)
params.Set("season_type", strconv.Itoa(int(seasonType)))
if err = d.client.Get(c, bangumiURL, "", params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), bangumiURL+"?"+params.Encode())
return
}
if len(res.Result) == 0 {
err = ecode.TvPGCRankEmpty
log.Error("[LoadPGCList] Zone %d, Err %v", seasonType, err)
return
}
for _, v := range res.Result {
if v.NewEP != nil {
v.BePGC()
result = append(result, v)
}
}
if len(result) == 0 {
err = ecode.TvPGCRankNewEPNil
log.Error("[LoadPGCList] Zone %d, Err %v", seasonType, err)
}
return
}
// UgcAIData gets the ugc types rank data from AI
func (d *Dao) UgcAIData(c context.Context, tid int16) (result []*model.AIData, err error) {
var (
res model.RespAI
AIURL = fmt.Sprintf(d.conf.Host.AIUgcType, tid)
params = url.Values{}
)
if err = d.client.Get(c, AIURL, "", params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), AIURL+"?"+params.Encode())
return
}
result = res.List
return
}

View File

@@ -0,0 +1,72 @@
package dao
import (
"context"
"fmt"
"testing"
"go-common/library/ecode"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoChannelData(t *testing.T) {
var (
ctx = context.Background()
seasonType = int(0)
appInfo = d.conf.TVApp
)
convey.Convey("ChannelData", t, func(c convey.C) {
c.Convey("Then err should be nil.result should not be nil.", func(c convey.C) {
httpMock("GET", d.conf.Host.APIZone).Reply(200).
JSON(`{"code":0,"message":"success","result":[{"cover":"http://i0.hdslb.com/bfs/bangumi/75c7528cbf3254dd20a4512376ced74733ab98ef.jpg","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/8beff40b27076475353d25ca590d0932791f71d6.jpg","id":253907,"index":"中文","index_show":"全1话"},"season_id":25944,"title":"黑子的篮球 LAST GAME"},{"cover":"http://i0.hdslb.com/bfs/bangumi/3622ff139d6875c7b196edd3f9db7d0a3883a158.jpg","new_ep":{"cover":"http://i0.hdslb.com/bfs/archive/86ec94eb1e00785d15612b700e95eb309710597a.jpg","id":151657,"index":"序列之争","index_show":"全1话"},"season_id":12364,"title":"刀剑神域:序列之争"}]}`)
result, err := d.ChannelData(ctx, seasonType, appInfo)
c.So(err, convey.ShouldBeNil)
c.So(result, convey.ShouldNotBeNil)
})
c.Convey("We mock http error, The error should not be nil", func(c convey.C) {
httpMock("GET", d.conf.Host.APIZone).Reply(200).
JSON(`{"code":-100,"message":"fail","result":[]}`)
result, err := d.ChannelData(ctx, seasonType, appInfo)
c.So(err, convey.ShouldNotBeNil)
c.So(len(result), convey.ShouldBeZeroValue)
})
c.Convey("result empty error", func(c convey.C) {
httpMock("GET", d.conf.Host.APIZone).Reply(200).
JSON(`{"code":0,"message":"succ","result":[]}`)
result, err := d.ChannelData(ctx, seasonType, appInfo)
c.So(err, convey.ShouldEqual, ecode.TvPGCRankEmpty)
c.So(len(result), convey.ShouldBeZeroValue)
})
c.Convey("result not empty but NewEP empty error", func(c convey.C) {
httpMock("GET", d.conf.Host.APIZone).Reply(200).
JSON(`{"code":0,"message":"succ","result":[{"cover":"ttt"}]}`)
result, err := d.ChannelData(ctx, seasonType, appInfo)
c.So(err, convey.ShouldEqual, ecode.TvPGCRankNewEPNil)
c.So(len(result), convey.ShouldBeZeroValue)
})
})
}
func TestDaoUgcAIData(t *testing.T) {
var (
ctx = context.Background()
tid = int16(3)
normalStr = `{"note":"统计所有投稿在 2018年11月02日 - 2018年11月09日 的数据综合得分,每日更新一次","source_date":"2018-11-09","code":0,"num":250,"list":[{"aid":35129595,"mid":423442,"pts":571043,"play":277133,"coins":22257,"video_review":2949},{"aid":34994701,"mid":284120,"pts":272817,"play":103881,"coins":7543,"video_review":446},{"aid":35395331,"mid":314791153,"pts":270235,"play":160364,"coins":13475,"video_review":2586}]}`
httpCodeErr = `{"code":-100}`
url = fmt.Sprintf(d.conf.Host.AIUgcType, tid)
)
convey.Convey("UgcAIData", t, func(c convey.C) {
c.Convey("Then err should be nil.result should not be nil.", func(c convey.C) {
httpMock("GET", url).Reply(200).JSON(normalStr)
result, err := d.UgcAIData(ctx, tid)
c.So(err, convey.ShouldBeNil)
c.So(result, convey.ShouldNotBeNil)
})
c.Convey("Http code error", func(c convey.C) {
httpMock("GET", url).Reply(200).JSON(httpCodeErr)
_, err := d.UgcAIData(ctx, tid)
c.So(err, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,74 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"api_test.go",
"arctypes_test.go",
"dao_test.go",
"memcache_test.go",
"valid_test.go",
"view_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model/view:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"api.go",
"arctypes.go",
"dao.go",
"memcache.go",
"valid.go",
"view.go",
],
importpath = "go-common/app/interface/main/tv/dao/archive",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/interface/main/history/rpc/client:go_default_library",
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/view:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,41 @@
package archive
import (
"context"
"net/url"
"strconv"
"go-common/library/ecode"
"go-common/library/xstr"
"github.com/pkg/errors"
)
const (
_realteURL = "/recsys/related"
)
// RelateAids get relate by aid
func (d *Dao) RelateAids(c context.Context, aid int64, ip string) (aids []int64, err error) {
params := url.Values{}
params.Set("key", strconv.FormatInt(aid, 10))
var res struct {
Code int `json:"code"`
Data []*struct {
Value string `json:"value"`
} `json:"data"`
}
if err = d.client.Get(c, d.relateURL, ip, params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), d.relateURL+"?"+params.Encode())
return
}
if len(res.Data) != 0 {
if aids, err = xstr.SplitInts(res.Data[0].Value); err != nil {
err = errors.Wrap(err, res.Data[0].Value)
}
}
return
}

View File

@@ -0,0 +1,28 @@
package archive
import (
"context"
"fmt"
"testing"
"go-common/library/database/sql"
. "github.com/smartystreets/goconvey/convey"
)
func getPassAid(db *sql.DB) (aid int64, err error) {
if err = db.QueryRow(context.Background(), "select aid from ugc_archive where deleted = 0 and valid = 1 and result = 1 limit 1").Scan(&aid); err != nil {
fmt.Println(err)
}
return
}
func TestDao_RelateAids(t *testing.T) {
Convey("TestDao_RelateAids", t, WithDao(func(d *Dao) {
httpMock("GET", d.relateURL).Reply(200).JSON(`{"data":[{"key": "1234", "value": "23999,515522,55,10099960,10099978,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959,10099959"}]}`)
aids, err := d.RelateAids(context.Background(), 333444, "")
So(err, ShouldBeNil)
So(len(aids), ShouldBeGreaterThan, 0)
fmt.Println(aids)
}))
}

View File

@@ -0,0 +1,120 @@
package archive
import (
"context"
"go-common/app/interface/main/tv/model"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/ecode"
"go-common/library/log"
)
// call ArcRPC for types data
func (d *Dao) loadTypes(ctx context.Context) {
var (
res = make(map[int32]*arcwar.Tp)
resRel = make(map[int32][]*arcwar.Tp)
reply *arcwar.TypesReply
err error
)
if reply, err = d.arcClient.Types(ctx, &arcwar.NoArgRequest{}); err != nil {
log.Error("arcRPC loadType Error %v", err)
return
}
res = reply.Types
log.Info("Reload Types Data! Len: %d", len(res))
for _, v := range res {
if v.Pid != 0 {
if _, ok := resRel[v.Pid]; !ok {
resRel[v.Pid] = []*arcwar.Tp{}
}
resRel[v.Pid] = append(resRel[v.Pid], v)
}
}
if len(res) > 0 {
d.arcTypes = res
}
if len(resRel) > 0 {
d.arcTypesRel = resRel
}
}
// GetPTypeName get first level of types name
func (d *Dao) GetPTypeName(typeID int32) (firstName string, secondName string) {
var (
second, first *arcwar.Tp
ok bool
)
if second, ok = d.arcTypes[typeID]; !ok {
log.Error("can't find type for ID: %d ", typeID)
return
}
secondName = second.Name
if first, ok = d.arcTypes[second.Pid]; !ok {
log.Error("can't find type for ID: %d, second Info: %v", second, second.Pid)
return
}
firstName = first.Name
return
}
// TargetTypes get all the ugc ranks that AI prepared for us
func (d *Dao) TargetTypes() (tids []int32, err error) {
if len(d.arcTypesRel) == 0 {
err = ecode.ServiceUnavailable
return
}
for _, v := range d.conf.Cfg.ZonesInfo.TargetTypes {
if children, ok := d.arcTypesRel[v]; ok { // second level types
for _, child := range children {
tids = append(tids, child.ID)
}
}
tids = append(tids, v)
}
return
}
// FirstTypes returns only first level of types
func (d *Dao) FirstTypes() (typeMap map[int32]*model.ArcType, err error) {
if len(d.arcTypes) == 0 {
err = ecode.ServiceUnavailable
return
}
typeMap = make(map[int32]*model.ArcType)
for _, v := range d.arcTypes { // only pick first level of types
if v.Pid == 0 {
typeMap[v.ID] = &model.ArcType{
ID: v.ID,
Name: v.Name,
}
}
}
return
}
// TypeInfo returns the type info
func (d *Dao) TypeInfo(typeid int32) (*arcwar.Tp, error) {
if len(d.arcTypes) == 0 {
return nil, ecode.ServiceUnavailable
}
info, ok := d.arcTypes[typeid]
if !ok {
return nil, ecode.NothingFound
}
return info, nil
}
// TypeChildren returns a first level's type children
func (d *Dao) TypeChildren(typeid int32) (children []*arcwar.Tp, err error) {
if len(d.arcTypesRel) == 0 {
err = ecode.ServiceUnavailable
return
}
children, found := d.arcTypesRel[typeid]
if !found {
err = ecode.NothingFound
return
}
return
}

View File

@@ -0,0 +1,78 @@
package archive
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestArchiveloadTypes(t *testing.T) {
var (
c = context.Background()
)
convey.Convey("loadTypes", t, func(ctx convey.C) {
d.loadTypes(c)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}
func TestArchiveGetPTypeName(t *testing.T) {
var (
typeID = int32(3)
)
convey.Convey("GetPTypeName", t, func(ctx convey.C) {
firstName, secondName := d.GetPTypeName(typeID)
ctx.Convey("Then firstName,secondName should not be nil.", func(ctx convey.C) {
ctx.So(secondName, convey.ShouldNotBeNil)
ctx.So(firstName, convey.ShouldNotBeNil)
})
})
}
func TestArchiveTargetTypes(t *testing.T) {
convey.Convey("TargetTypes", t, func(ctx convey.C) {
tids, err := d.TargetTypes()
ctx.Convey("Then err should be nil.tids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tids, convey.ShouldNotBeNil)
})
})
}
func TestArchiveFirstTypes(t *testing.T) {
convey.Convey("FirstTypes", t, func(ctx convey.C) {
typeMap, err := d.FirstTypes()
ctx.Convey("Then err should be nil.typeMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(typeMap, convey.ShouldNotBeNil)
})
})
}
func TestArchiveTypeInfo(t *testing.T) {
var (
typeid = int32(3)
)
convey.Convey("TypeInfo", t, func(ctx convey.C) {
p1, err := d.TypeInfo(typeid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestArchiveTypeChildren(t *testing.T) {
var (
typeid = int32(3)
)
convey.Convey("TypeChildren", t, func(ctx convey.C) {
children, err := d.TypeChildren(typeid)
ctx.Convey("Then err should be nil.children should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(children, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,89 @@
package archive
import (
"context"
"runtime"
"time"
hisrpc "go-common/app/interface/main/history/rpc/client"
"go-common/app/interface/main/tv/conf"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/cache/memcache"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// Dao is archive dao.
type Dao struct {
// cfg
db *sql.DB
conf *conf.Config
relateURL string
// memory
arcTypes map[int32]*arcwar.Tp // map for arc types
arcTypesRel map[int32][]*arcwar.Tp // map for relation between arc type
// http client
client *bm.Client
// rpc
arcClient arcwar.ArchiveClient
hisRPC *hisrpc.Service
// memcache
arcMc *memcache.Pool
expireRlt int32
expireArc int32
expireView int32
// chan
mCh chan func()
}
// New new a archive dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
// http client
client: bm.NewClient(c.HTTPClient),
// rpc
hisRPC: hisrpc.New(c.HisRPC),
// cfg
relateURL: c.Host.Data + _realteURL,
conf: c,
db: sql.NewMySQL(c.Mysql),
// memorry
arcTypes: make(map[int32]*arcwar.Tp),
arcTypesRel: make(map[int32][]*arcwar.Tp),
// memcache
arcMc: memcache.NewPool(c.Memcache.Config),
expireRlt: int32(time.Duration(c.Memcache.RelateExpire) / time.Second),
expireView: int32(time.Duration(c.Memcache.ViewExpire) / time.Second),
expireArc: int32(time.Duration(c.Memcache.ArcExpire) / time.Second),
// mc proc
mCh: make(chan func(), 10240),
}
var err error
if d.arcClient, err = arcwar.NewClient(c.ArcClient); err != nil {
panic(err)
}
// video db
for i := 0; i < runtime.NumCPU()*2; i++ {
go d.cacheproc()
}
d.loadTypes(context.Background())
return
}
// addCache add archive to mc or redis
func (d *Dao) addCache(f func()) {
select {
case d.mCh <- f:
default:
log.Warn("cacheproc chan full")
}
}
// cacheproc write memcache and stat redis use goroutine
func (d *Dao) cacheproc() {
for {
f := <-d.mCh
f()
}
}

View File

@@ -0,0 +1,51 @@
package archive
import (
"flag"
"os"
"strings"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
)
func init() {
//dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
//flag.Set("conf", dir)
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
d.client.SetTransport(gock.DefaultTransport)
return r
}

View File

@@ -0,0 +1,146 @@
package archive
import (
"context"
"strconv"
"go-common/app/interface/main/tv/model/view"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/cache/memcache"
"go-common/library/log"
"github.com/pkg/errors"
)
const (
_prefixRelate = "al_"
_prefixViewStatic = "avp_"
_prefixArchive = "a3p_"
)
func keyRl(aid int64) string {
return _prefixRelate + strconv.FormatInt(aid, 10)
}
func keyView(aid int64) string {
return _prefixViewStatic + strconv.FormatInt(aid, 10)
}
func keyArc(aid int64) string {
return _prefixArchive + strconv.FormatInt(aid, 10)
}
// AddArcCache add arc cache
func (d *Dao) AddArcCache(aid int64, arc *arcwar.Arc) {
d.addCache(func() {
d.addArcCache(context.TODO(), aid, arc)
})
}
// AddRelatesCache add relates
func (d *Dao) AddRelatesCache(aid int64, rls []*view.Relate) {
d.addCache(func() {
d.addRelatesCache(context.TODO(), aid, rls)
})
}
// AddViewCache add view relates
func (d *Dao) AddViewCache(aid int64, vp *arcwar.ViewReply) {
d.addCache(func() {
d.addViewCache(context.TODO(), aid, vp)
})
}
// addViewCache add relates cache.
func (d *Dao) addViewCache(c context.Context, aid int64, vp *arcwar.ViewReply) (err error) {
conn := d.arcMc.Get(c)
key := keyView(aid)
item := &memcache.Item{Key: key, Object: vp, Flags: memcache.FlagJSON, Expiration: d.expireView}
if err = conn.Set(item); err != nil {
err = errors.Wrapf(err, "conn.Set(%s,%v,%d)", key, vp, d.expireView)
}
conn.Close()
return
}
// RelatesCache get relates.
func (d *Dao) RelatesCache(c context.Context, aid int64) (rls []*view.Relate, err error) {
conn := d.arcMc.Get(c)
key := keyRl(aid)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
err = errors.Wrapf(err, "conn.Get(%s)", key)
return
}
if err = conn.Scan(r, &rls); err != nil {
err = errors.Wrapf(err, "conn.Scan(%s)", r.Value)
}
return
}
// addRelatesCache add relates cache.
func (d *Dao) addRelatesCache(c context.Context, aid int64, rls []*view.Relate) (err error) {
conn := d.arcMc.Get(c)
key := keyRl(aid)
item := &memcache.Item{Key: key, Object: rls, Flags: memcache.FlagJSON, Expiration: d.expireRlt}
if err = conn.Set(item); err != nil {
err = errors.Wrapf(err, "conn.Set(%s,%v,%d)", key, rls, d.expireRlt)
}
conn.Close()
return
}
// addRelatesCache add relates cache.
func (d *Dao) addArcCache(c context.Context, aid int64, cached *arcwar.Arc) (err error) {
conn := d.arcMc.Get(c)
key := keyArc(aid)
item := &memcache.Item{Key: key, Object: cached, Flags: memcache.FlagJSON, Expiration: d.expireArc}
if err = conn.Set(item); err != nil {
err = errors.Wrapf(err, "conn.Set(%s,%v,%d)", key, cached, d.expireArc)
}
conn.Close()
return
}
// arcsCache get archives cache.
func (d *Dao) arcsCache(c context.Context, aids []int64) (cached map[int64]*arcwar.Arc, missed []int64, err error) {
cached = make(map[int64]*arcwar.Arc, len(aids))
conn := d.arcMc.Get(c)
defer conn.Close()
keys := make([]string, 0, len(aids))
aidmap := make(map[string]int64, len(aids))
for _, aid := range aids {
k := keyArc(aid)
if _, ok := aidmap[k]; !ok {
keys = append(keys, k)
aidmap[k] = aid
}
}
rs, err := conn.GetMulti(keys)
if err != nil {
err = errors.Wrapf(err, "conn.GetMulti(%v)", keys)
return
}
for k, r := range rs {
a := &arcwar.Arc{}
if err = conn.Scan(r, a); err != nil {
log.Error("conn.Scan(%s) error(%v)", r.Value, err)
err = nil
continue
}
cached[aidmap[k]] = a
// delete hit key
delete(aidmap, k)
}
// missed key
missed = make([]int64, 0, len(aidmap))
for _, aid := range aidmap {
missed = append(missed, aid)
}
return
}

View File

@@ -0,0 +1,171 @@
package archive
import (
"context"
"fmt"
"testing"
"go-common/app/interface/main/tv/model/view"
arcwar "go-common/app/service/main/archive/api"
"github.com/smartystreets/goconvey/convey"
)
func TestArchivekeyRl(t *testing.T) {
var (
aid = int64(123)
)
convey.Convey("keyRl", t, func(ctx convey.C) {
p1 := keyRl(aid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestArchivekeyView(t *testing.T) {
var (
aid = int64(123)
)
convey.Convey("keyView", t, func(ctx convey.C) {
p1 := keyView(aid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestArchivekeyArc(t *testing.T) {
var (
aid = int64(123)
)
convey.Convey("keyArc", t, func(ctx convey.C) {
p1 := keyArc(aid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestArchiveAddArcCache(t *testing.T) {
var (
aid = int64(123)
arc = &arcwar.Arc{
Aid: aid,
}
)
convey.Convey("AddArcCache", t, func(ctx convey.C) {
d.AddArcCache(aid, arc)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}
func TestArchiveAddRelatesCache(t *testing.T) {
var (
aid = int64(123)
rls = []*view.Relate{
{
Aid: aid,
},
}
)
convey.Convey("AddRelatesCache", t, func(ctx convey.C) {
d.AddRelatesCache(aid, rls)
d.addRelatesCache(context.TODO(), aid, rls)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}
func TestArchiveAddViewCache(t *testing.T) {
convey.Convey("AddViewCache", t, func(c convey.C) {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
vp, err := d.view3(context.Background(), aid)
fmt.Println(vp, " ", aid)
convey.So(err, convey.ShouldBeNil)
convey.So(vp, convey.ShouldNotBeNil)
d.AddViewCache(aid, vp)
c.Convey("No return values", func(ctx convey.C) {
})
})
}
func TestArchiveaddViewCache(t *testing.T) {
var (
c = context.Background()
aid = int64(123)
vp = &arcwar.ViewReply{}
)
convey.Convey("addViewCache", t, func(ctx convey.C) {
err := d.addViewCache(c, aid, vp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestArchiveRelatesCache(t *testing.T) {
var (
c = context.Background()
aid = int64(123)
)
convey.Convey("RelatesCache", t, func(ctx convey.C) {
rls, err := d.RelatesCache(c, aid)
ctx.Convey("Then err should be nil.rls should not be nil.", func(ctx convey.C) {
fmt.Println(rls)
ctx.So(err, convey.ShouldBeNil)
ctx.So(rls, convey.ShouldNotBeNil)
})
})
}
func TestArchiveaddRelatesCache(t *testing.T) {
var (
c = context.Background()
aid = int64(123)
rls = []*view.Relate{
{Aid: aid},
}
)
convey.Convey("addRelatesCache", t, func(ctx convey.C) {
err := d.addRelatesCache(c, aid, rls)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestArchiveaddArcCache(t *testing.T) {
var (
c = context.Background()
aid = int64(123)
cached = &arcwar.Arc{Aid: 123}
)
convey.Convey("addArcCache", t, func(ctx convey.C) {
err := d.addArcCache(c, aid, cached)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestArchivearcsCache(t *testing.T) {
var (
c = context.Background()
aids = []int64{123}
)
convey.Convey("arcsCache", t, func(ctx convey.C) {
cached, missed, err := d.arcsCache(c, aids)
ctx.Convey("Then err should be nil.cached,missed should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(len(missed)+len(cached), convey.ShouldEqual, len(aids))
fmt.Println(cached)
fmt.Println(missed)
})
})
}

View File

@@ -0,0 +1,28 @@
package archive
import (
arcwar "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
)
// validView distinguishes whether an view is valid
func validView(vp *arcwar.ViewReply, checkAttr bool) (valid bool) {
if vp == nil {
return
}
if vp.Arc == nil {
return
}
if vp.Arc.Aid == 0 {
return
}
if len(vp.Pages) == 0 {
return
}
if checkAttr {
if vp.Arc.AttrVal(archive.AttrBitIsMovie) == archive.AttrYes {
return // regard it as none
}
}
return true
}

View File

@@ -0,0 +1,21 @@
package archive
import (
"testing"
arcwar "go-common/app/service/main/archive/api"
"github.com/smartystreets/goconvey/convey"
)
func TestArchivevalidView(t *testing.T) {
var (
vp = &arcwar.ViewReply{}
)
convey.Convey("validView", t, func(ctx convey.C) {
valid := validView(vp, true)
ctx.Convey("Then valid should not be nil.", func(ctx convey.C) {
ctx.So(valid, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,221 @@
package archive
import (
"context"
history "go-common/app/interface/main/history/model"
"go-common/app/interface/main/tv/model/view"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/cache/memcache"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// Archive3 picks one archive data
func (d *Dao) Archive3(c context.Context, aid int64) (arc *arcwar.Arc, err error) {
var (
arcReply *arcwar.ArcReply
arg = &arcwar.ArcRequest{Aid: aid}
)
arcReply, err = d.arcClient.Arc(c, arg)
if err != nil {
log.Error("d.arcRPC.Archive(%v) error(%+v)", arg, err)
if ecode.Cause(err) == ecode.NothingFound {
err = nil
return
}
}
arc = arcReply.Arc
return
}
// LoadViews picks view meta information from Cache & RPC
func (d *Dao) LoadViews(ctx context.Context, aids []int64) (resMetas map[int64]*arcwar.ViewReply) {
var (
missedMetas = make(map[int64]*arcwar.ViewReply)
addCache = true
)
resMetas = make(map[int64]*arcwar.ViewReply, len(aids))
cachedMetas, missed, err := d.ViewsCache(ctx, aids)
if err != nil {
log.Error("LoadViews ViewsCache Sids:%v, Error:%v", aids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
for _, vv := range missed {
if vp, _ := d.view3(ctx, vv); vp != nil {
missedMetas[vv] = vp
resMetas[vv] = vp
}
}
}
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
log.Info("LoadViews Info Hit %d, Missed %d", len(cachedMetas), len(missed))
if addCache && len(missedMetas) > 0 { // async Reset the DB data in MC for next time
log.Info("Set MissedMetas %d Data in MC", missedMetas)
for aid, vp := range missedMetas {
d.AddViewCache(aid, vp)
}
}
return
}
// GetView gets the aid's View info from Cache or RPC
func (d *Dao) GetView(c context.Context, aid int64) (vp *arcwar.ViewReply, err error) {
var (
addCache = false
)
if vp, err = d.viewCache(c, aid); err != nil {
log.Error("ViewPage viewCache AID %d, Err %v", aid, err)
}
if !validView(vp, true) { // back source
if vp, err = d.view3(c, aid); err != nil {
log.Error("%+v", err)
err = ecode.NothingFound
return
}
addCache = true
}
if !validView(vp, false) {
err = ecode.NothingFound
return
}
if addCache {
d.AddViewCache(aid, vp)
return
}
return
}
// view3 view archive with pages pb.
func (d *Dao) view3(c context.Context, aid int64) (reply *arcwar.ViewReply, err error) {
var arg = &arcwar.ViewRequest{Aid: aid}
if reply, err = d.arcClient.View(c, arg); err != nil {
log.Error("d.arcRPC.view3(%v) error(%+v)", arg, err)
if ecode.Cause(err) == ecode.NothingFound {
err = nil
return
}
}
return
}
// viewCache get view cache from remote memecache .
func (d *Dao) viewCache(c context.Context, aid int64) (vs *arcwar.ViewReply, err error) {
conn := d.arcMc.Get(c)
key := keyView(aid)
defer conn.Close()
r, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
err = errors.Wrapf(err, "conn.Get(%s)", key)
return
}
vs = &arcwar.ViewReply{Arc: &arcwar.Arc{}}
if err = conn.Scan(r, vs); err != nil {
vs = nil
err = errors.Wrapf(err, "conn.Scan(%s)", r.Value)
}
return
}
// Progress is archive plays progress .
func (d *Dao) Progress(c context.Context, aid, mid int64) (h *view.History, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &history.ArgPro{Mid: mid, Aids: []int64{aid}, RealIP: ip}
his, err := d.hisRPC.Progress(c, arg)
if err != nil {
log.Error("d.hisRPC.Progress(%v) error(%v)", arg, err)
return
}
if his[aid] != nil {
h = &view.History{Cid: his[aid].Cid, Progress: his[aid].Pro}
}
return
}
// ViewsCache pick view from cache
func (d *Dao) ViewsCache(c context.Context, aids []int64) (cached map[int64]*arcwar.ViewReply, missed []int64, err error) {
if len(aids) == 0 {
return
}
var allKeys []string
cached = make(map[int64]*arcwar.ViewReply, len(aids))
idmap := make(map[string]int64, len(aids))
for _, id := range aids {
k := keyView(id)
allKeys = append(allKeys, k)
idmap[k] = id
}
conn := d.arcMc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
err = nil
return
}
for key, item := range replys {
vp := &arcwar.ViewReply{}
if err = conn.Scan(item, vp); err != nil {
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
if !validView(vp, true) {
continue
}
cached[idmap[key]] = vp
delete(idmap, key)
}
missed = make([]int64, 0, len(idmap))
for _, id := range idmap {
missed = append(missed, id)
}
return
}
// Archives multi get archives.
func (d *Dao) Archives(c context.Context, aids []int64) (as map[int64]*arcwar.Arc, err error) {
if len(aids) == 0 {
return
}
var (
missed []int64
tmp map[int64]*arcwar.Arc
reply *arcwar.ArcsReply
)
if as, missed, err = d.arcsCache(c, aids); err != nil {
as = make(map[int64]*arcwar.Arc, len(aids))
missed = aids
log.Error("%+v", err)
err = nil
}
if len(missed) == 0 {
return
}
arg := &arcwar.ArcsRequest{Aids: missed}
if reply, err = d.arcClient.Arcs(c, arg); err != nil {
log.Error("d.arcRPC.Archives3(%v) error(%v)", arg, err)
if reply, err = d.arcClient.Arcs(c, arg); err != nil {
err = errors.Wrapf(err, "%v", arg)
return
}
}
tmp = reply.Arcs
for aid, a := range tmp {
as[aid] = a
d.AddArcCache(aid, a) // re-fill the cache
}
return
}

View File

@@ -0,0 +1,95 @@
package archive
import (
"context"
"encoding/json"
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_View3(t *testing.T) {
Convey("view3", t, func() {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
vp, err := d.view3(context.Background(), aid)
fmt.Println(vp, " ", aid)
So(err, ShouldBeNil)
So(vp, ShouldNotBeNil)
})
}
func Test_ViewCache(t *testing.T) {
Convey("viewCache", t, func() {
reply, err := d.viewCache(context.TODO(), 123)
So(reply, ShouldNotBeNil)
So(err, ShouldBeNil)
})
}
func TestDao_Progress(t *testing.T) {
Convey("TestDao_Progress", t, func() {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
h, err := d.Progress(context.TODO(), aid, 88895137)
So(err, ShouldBeNil)
fmt.Println(h)
})
}
func TestDao_LoadViews(t *testing.T) {
Convey("TestDao_LoadViews", t, func() {
resMetas := d.LoadViews(context.Background(), []int64{10110328, 10099960})
data, _ := json.Marshal(resMetas)
fmt.Println(string(data))
})
}
func TestDao_Archives(t *testing.T) {
Convey("TestDao_Archives", t, WithDao(func(d *Dao) {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
as, err := d.Archives(context.Background(), []int64{aid})
So(err, ShouldBeNil)
So(len(as), ShouldBeGreaterThan, 0)
fmt.Println(as)
}))
}
func TestDao_GetView(t *testing.T) {
Convey("TestDao_Archives", t, WithDao(func(d *Dao) {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
vp, err := d.GetView(context.Background(), aid)
So(err, ShouldBeNil)
So(vp, ShouldNotBeNil)
fmt.Println(vp)
}))
}
func TestDao_Archive3(t *testing.T) {
Convey("TestDao_Archives", t, WithDao(func(d *Dao) {
aid, errGet := getPassAid(d.db)
if errGet != nil {
fmt.Println(errGet)
return
}
vp, err := d.Archive3(context.Background(), aid)
So(err, ShouldBeNil)
So(vp, ShouldNotBeNil)
fmt.Println(vp)
}))
}

View File

@@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"dao.go",
"transcode.go",
],
importpath = "go-common/app/interface/main/tv/dao/audit",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/time: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 = [
"audit_test.go",
"dao_test.go",
"transcode_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,50 @@
package audit
import (
"context"
xtime "time"
"go-common/app/interface/main/tv/model"
xsql "go-common/library/database/sql"
"go-common/library/time"
)
const (
_updateCont = "UPDATE `tv_content` SET `state` = ?, `valid` = ?, `reason` = ?, `inject_time` = ? WHERE `epid` = ? AND `is_deleted` = 0"
_updateSea = "UPDATE `tv_ep_season` SET `check` = ?, `valid` = ?, `reason` = ?, `inject_time` = ? WHERE `id` = ? AND `is_deleted` = 0"
_updateVideo = "UPDATE `ugc_video` SET `result` = ?, `valid` = ?, `reason` = ? , `inject_time` = ? WHERE `cid` = ? AND `deleted` = 0"
_updateArc = "UPDATE `ugc_archive` SET `result` = ?, `valid` = ?, `reason` = ?, `inject_time` = ? WHERE `aid` = ? AND `deleted` = 0"
)
// BeginTran def.
func (d *Dao) BeginTran(c context.Context) (tx *xsql.Tx, err error) {
return d.db.Begin(c)
}
// UpdateVideo .
func (d *Dao) UpdateVideo(c context.Context, v *model.AuditOp, tx *xsql.Tx) (err error) {
now := time.Time(xtime.Now().Unix())
_, err = tx.Exec(_updateVideo, v.Result, v.Valid, v.AuditMsg, now, v.KID)
return
}
// UpdateArc .
func (d *Dao) UpdateArc(c context.Context, v *model.AuditOp, tx *xsql.Tx) (err error) {
now := time.Time(xtime.Now().Unix())
_, err = tx.Exec(_updateArc, v.Result, v.Valid, v.AuditMsg, now, v.KID)
return
}
// UpdateCont .
func (d *Dao) UpdateCont(c context.Context, val *model.AuditOp, tx *xsql.Tx) (err error) {
now := time.Time(xtime.Now().Unix())
_, err = tx.Exec(_updateCont, val.Result, val.Valid, val.AuditMsg, now, val.KID)
return
}
// UpdateSea .
func (d *Dao) UpdateSea(c context.Context, val *model.AuditOp, tx *xsql.Tx) (err error) {
now := time.Time(xtime.Now().Unix())
_, err = tx.Exec(_updateSea, val.Result, val.Valid, val.AuditMsg, now, val.KID)
return
}

View File

@@ -0,0 +1,83 @@
package audit
import (
"context"
"go-common/app/interface/main/tv/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestAuditBeginTrans(t *testing.T) {
var (
c = context.Background()
)
convey.Convey("BeginTran", t, func(ctx convey.C) {
tx, err := d.BeginTran(c)
ctx.Convey("Then err should be nil.tx should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tx, convey.ShouldNotBeNil)
})
tx.Commit()
})
}
func TestAuditUpdateVideo(t *testing.T) {
var (
c = context.Background()
v = &model.AuditOp{}
tx, _ = d.BeginTran(c)
)
convey.Convey("UpdateVideo", t, func(ctx convey.C) {
err := d.UpdateVideo(c, v, tx)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
tx.Commit()
})
}
func TestAuditUpdateArc(t *testing.T) {
var (
c = context.Background()
v = &model.AuditOp{}
tx, _ = d.BeginTran(c)
)
convey.Convey("UpdateArc", t, func(ctx convey.C) {
err := d.UpdateArc(c, v, tx)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
tx.Commit()
})
}
func TestAuditUpdateCont(t *testing.T) {
var (
c = context.Background()
val = &model.AuditOp{}
tx, _ = d.BeginTran(c)
)
convey.Convey("UpdateCont", t, func(ctx convey.C) {
err := d.UpdateCont(c, val, tx)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
tx.Commit()
})
}
func TestAuditUpdateSea(t *testing.T) {
var (
c = context.Background()
val = &model.AuditOp{}
tx, _ = d.BeginTran(c)
)
convey.Convey("UpdateSea", t, func(ctx convey.C) {
err := d.UpdateSea(c, val, tx)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
tx.Commit()
})
}

View File

@@ -0,0 +1,24 @@
package audit
import (
"go-common/app/interface/main/tv/conf"
"go-common/library/cache/memcache"
"go-common/library/database/sql"
)
// Dao is account dao.
type Dao struct {
mc *memcache.Pool
conf *conf.Config
db *sql.DB
}
// New account dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
conf: c,
mc: memcache.NewPool(c.Memcache.Config),
db: sql.NewMySQL(c.Mysql),
}
return
}

View File

@@ -0,0 +1,44 @@
package audit
import (
"context"
"flag"
"os"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
ctx = context.Background()
)
func init() {
//dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
//flag.Set("conf", dir)
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}

View File

@@ -0,0 +1,76 @@
package audit
import (
"context"
"fmt"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/time"
"go-common/library/xstr"
)
const (
_PgcCID = "SELECT `id` FROM `tv_content` WHERE `cid` = ? AND `is_deleted` = 0"
_UgcCID = "SELECT `id` FROM `ugc_video` WHERE `cid` = ? AND `deleted` = 0"
_transcodePGC = "UPDATE `tv_content` SET `transcoded` = ?, mark_time = NOW() WHERE `id` IN (%s) "
_transcodeUGC = "UPDATE `ugc_video` SET `transcoded` = ? WHERE `id` IN (%s)"
_applyPGC = "UPDATE `tv_content` SET `apply_time` = ? WHERE `id` IN (%s)"
)
// checkCID picks one ugc or pgc video data with its cid
func (d *Dao) checkCID(c context.Context, cid int64, query string) (ids []int64, err error) {
rows, err := d.db.Query(c, query, cid)
if err != nil {
log.Error("checkCID d.db.Query (%s) cid %d, error(%v)", query, cid, err)
return
}
defer rows.Close()
for rows.Next() {
var li int64
if err = rows.Scan(&li); err != nil {
log.Error("checkCID Query (%s) row.Scan error(%v)", query, err)
return
}
ids = append(ids, li)
}
if err = rows.Err(); err != nil {
log.Error("checkCID Query (%s) rows Err cid %d, err %v", query, cid, err)
return
}
if len(ids) == 0 {
err = ecode.NothingFound
}
return
}
// UgcCID picks one ugc video data with its cid
func (d *Dao) UgcCID(c context.Context, cid int64) (ids []int64, err error) {
return d.checkCID(c, cid, _UgcCID)
}
// PgcCID picks one ugc video data with its cid
func (d *Dao) PgcCID(c context.Context, cid int64) (ids []int64, err error) {
return d.checkCID(c, cid, _PgcCID)
}
func (d *Dao) updateCIDs(c context.Context, query string, value interface{}) (err error) {
if _, err = d.db.Exec(c, query, value); err != nil {
log.Error("updateCIDs, Query %s, d.db.Exec.error(%v)", query, err)
}
return
}
// PgcTranscode updates the transcoded status of an ep data
func (d *Dao) PgcTranscode(c context.Context, ids []int64, action int64) (err error) {
return d.updateCIDs(c, fmt.Sprintf(_transcodePGC, xstr.JoinInts(ids)), action)
}
// UgcTranscode updates the transcoded status of an ep data
func (d *Dao) UgcTranscode(c context.Context, ids []int64, action int64) (err error) {
return d.updateCIDs(c, fmt.Sprintf(_transcodeUGC, xstr.JoinInts(ids)), action)
}
// ApplyPGC saves pgc apply_time; only PGC needs this
func (d *Dao) ApplyPGC(c context.Context, ids []int64, aTime int64) (err error) {
return d.updateCIDs(c, fmt.Sprintf(_applyPGC, xstr.JoinInts(ids)), time.Time(aTime))
}

View File

@@ -0,0 +1,128 @@
package audit
import (
"fmt"
"testing"
"time"
"go-common/library/database/sql"
. "github.com/smartystreets/goconvey/convey"
)
func getCID(db *sql.DB, isPGC bool, double bool) (cid int64, err error) {
var query string
if isPGC {
if double {
query = "SELECT cid FROM tv_content WHERE is_deleted = 0 GROUP BY cid HAVING COUNT(1) >1 LIMIT 1"
} else {
query = "SELECT cid FROM tv_content WHERE is_deleted = 0 LIMIT 1"
}
} else {
if double {
query = "SELECT cid FROM ugc_video WHERE deleted = 0 GROUP BY cid HAVING COUNT(1) > 1 LIMIT 1"
} else {
query = "SELECT cid FROM ugc_video WHERE deleted = 0 LIMIT 1"
}
}
if err = db.QueryRow(ctx, query).Scan(&cid); err != nil {
fmt.Println("No Ready Video : ", err)
return
}
fmt.Println("pick CID ", cid)
return
}
func tryDouble(isPGC bool) (cid int64, err error) {
var errDouble error
if cid, errDouble = getCID(d.db, isPGC, true); errDouble != nil {
fmt.Println("Double Err ", errDouble, " Will Use Single")
if cid, err = getCID(d.db, isPGC, false); err != nil {
fmt.Println("Single Err ", err, " Will Fail")
return
}
}
return
}
func TestDao_UgcCID(t *testing.T) {
Convey("TestDao_UgcCID", t, WithDao(func(d *Dao) {
cid, errTry := tryDouble(false)
if errTry != nil {
fmt.Println("No Cid Ready")
return
}
cont, err := d.UgcCID(ctx, cid)
So(err, ShouldBeNil)
So(len(cont), ShouldBeGreaterThan, 0)
fmt.Println(cont)
}))
}
func TestDao_PgcCID(t *testing.T) {
Convey("TestDao_PgcCID", t, WithDao(func(d *Dao) {
cid, errTry := tryDouble(true)
if errTry != nil {
fmt.Println("No Cid Ready")
return
}
cont, err := d.PgcCID(ctx, cid)
So(err, ShouldBeNil)
So(len(cont), ShouldBeGreaterThan, 0)
fmt.Println(cont)
}))
}
func TestDao_UgcTranscode(t *testing.T) {
Convey("TestDao_UgcTranscode", t, WithDao(func(d *Dao) {
cid, errTry := tryDouble(false)
if errTry != nil {
fmt.Println("No Cid Ready")
return
}
cont, _ := d.UgcCID(ctx, cid)
if len(cont) == 0 {
fmt.Println("UgcCid Error!")
return
}
fmt.Println("To Update ", cont)
err := d.UgcTranscode(ctx, cont, 1)
So(err, ShouldBeNil)
}))
}
func TestDao_PgcTranscode(t *testing.T) {
Convey("TestDao_PgcTranscode", t, WithDao(func(d *Dao) {
cid, errTry := tryDouble(true)
if errTry != nil {
fmt.Println("No Cid Ready")
return
}
cont, _ := d.PgcCID(ctx, cid)
if len(cont) == 0 {
fmt.Println("PgcCid Error!")
return
}
fmt.Println("To Update ", cont)
err := d.PgcTranscode(ctx, cont, 1)
So(err, ShouldBeNil)
}))
}
func TestDao_ApplyPGC(t *testing.T) {
Convey("TestDao_ApplyPGC", t, WithDao(func(d *Dao) {
cid, errTry := tryDouble(true)
if errTry != nil {
fmt.Println("No Cid Ready")
return
}
cont, _ := d.PgcCID(ctx, cid)
if len(cont) == 0 {
fmt.Println("PgcCid Error!")
return
}
fmt.Println("To Update ", cont)
err := d.ApplyPGC(ctx, cont, time.Now().Unix())
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,80 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"backsource_test.go",
"dao_test.go",
"db_operate_test.go",
"err_msg_test.go",
"mixed_test.go",
"pgc_auth_test.go",
"pgc_batch_test.go",
"pgc_load_test.go",
"pgc_single_test.go",
"ugc_batch_test.go",
"ugc_load_test.go",
"ugc_single_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"backsource.go",
"dao.go",
"db_operate.go",
"err_msg.go",
"mixed.go",
"pgc_auth.go",
"pgc_batch.go",
"pgc_load.go",
"pgc_single.go",
"ugc_batch.go",
"ugc_load.go",
"ugc_single.go",
],
importpath = "go-common/app/interface/main/tv/dao/cms",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup: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"],
)

View File

@@ -0,0 +1,287 @@
package cms
import (
"context"
"database/sql"
"fmt"
"go-common/app/interface/main/tv/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_seasonCols = "SELECT id, cover, title , upinfo, `desc`, category, area, play_time, role, staff, total_num, style,origin_name,alias,status FROM tv_ep_season "
_epCols = "SELECT epid, cover, title, subtitle, pay_status FROM tv_content "
_seasonMetas = _seasonCols + " WHERE id IN (%s)"
_arcMetas = "SELECT title, aid, content, cover, typeid, pubtime, videos, valid, deleted, result FROM ugc_archive WHERE aid IN (%s)"
_videoMeta = "SELECT cid, eptitle, aid, index_order, valid, deleted, result FROM ugc_video WHERE cid = ?"
_videoMetas = "SELECT cid, eptitle, aid, index_order, valid, deleted, result FROM ugc_video WHERE cid IN (%s)"
_epMetas = _epCols + " WHERE epid IN (%s) AND is_deleted = 0 "
_simpleEPC = "SELECT `id`, `epid`, `state`, `is_deleted`, `valid`, `season_id` , `mark` FROM `tv_content` WHERE `epid` = ? LIMIT 1"
_simpleEPCs = "SELECT `id`, `epid`, `state`, `is_deleted`, `valid`, `season_id` , `mark` FROM `tv_content` WHERE `epid` IN (%s)"
_simpleSea = "SELECT `id`, `is_deleted`, `check`, `valid` FROM `tv_ep_season` WHERE `id` = ? LIMIT 1"
_simpleSeas = "SELECT `id`, `is_deleted`, `check`, `valid` FROM `tv_ep_season` WHERE `id` IN (%s)"
_seasonCMS = _seasonCols + "WHERE id = ? AND is_deleted = 0"
_epCMS = _epCols + " WHERE epid = ? AND is_deleted = 0 "
_newestOrder = "SELECT a.epid,b.`order` FROM tv_content a LEFT JOIN tv_ep_content b ON a.epid=b.id " +
"WHERE a.season_id=? AND a.state= ? AND a.valid= ? AND a.is_deleted=0 ORDER by `order` DESC LIMIT 1"
epStatePass = 3
_CMSValid = 1
)
// VideoMetaDB picks video from DB
func (d *Dao) VideoMetaDB(c context.Context, cid int64) (meta *model.VideoCMS, err error) {
rows, err := d.db.Query(c, _videoMeta, cid)
if err != nil {
log.Error("VideoMetaDB d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.VideoCMS{}
if err = rows.Scan(&li.CID, &li.Title, &li.AID, &li.IndexOrder, &li.Valid, &li.Deleted, &li.Result); err != nil {
log.Error("VideoMetaDB row.Scan error(%v)", err)
return
}
meta = li
}
return
}
// ArcMetaDB picks arc from DB
func (d *Dao) ArcMetaDB(c context.Context, aid int64) (meta *model.ArcCMS, err error) {
rows, err := d.db.Query(c, fmt.Sprintf(_arcMetas, fmt.Sprintf("%d", aid)))
if err != nil {
log.Error("ArcMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.ArcCMS{}
if err = rows.Scan(&li.Title, &li.AID, &li.Content, &li.Cover, &li.TypeID, &li.Pubtime, &li.Videos, &li.Valid, &li.Deleted, &li.Result); err != nil {
log.Error("ArcMetas row.Scan error(%v)", err)
return
}
meta = li
}
return
}
// VideoMetas picks video from DB
func (d *Dao) VideoMetas(c context.Context, cids []int64) (meta map[int64]*model.VideoCMS, err error) {
meta = make(map[int64]*model.VideoCMS)
rows, err := d.db.Query(c, fmt.Sprintf(_videoMetas, xstr.JoinInts(cids)))
if err != nil {
log.Error("VideoMetaDB d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.VideoCMS{}
if err = rows.Scan(&li.CID, &li.Title, &li.AID, &li.IndexOrder, &li.Valid, &li.Deleted, &li.Result); err != nil {
log.Error("VideoMetaDB row.Scan error(%v)", err)
return
}
meta[int64(li.CID)] = li
}
return
}
// ArcMetas picks seasons from DB
func (d *Dao) ArcMetas(c context.Context, aids []int64) (metas map[int64]*model.ArcCMS, err error) {
metas = make(map[int64]*model.ArcCMS)
rows, err := d.db.Query(c, fmt.Sprintf(_arcMetas, xstr.JoinInts(aids)))
if err != nil {
log.Error("ArcMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.ArcCMS{}
if err = rows.Scan(&li.Title, &li.AID, &li.Content, &li.Cover, &li.TypeID, &li.Pubtime, &li.Videos, &li.Valid, &li.Deleted, &li.Result); err != nil {
log.Error("ArcMetas row.Scan error(%v)", err)
return
}
metas[li.AID] = li
}
return
}
// SeasonMetas picks seasons from DB
func (d *Dao) SeasonMetas(c context.Context, sids []int64) (metas map[int64]*model.SeasonCMS, err error) {
metas = make(map[int64]*model.SeasonCMS)
rows, err := d.db.Query(c, fmt.Sprintf(_seasonMetas, xstr.JoinInts(sids)))
if err != nil {
log.Error("SeasonMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.SeasonCMS{}
// SELECT id, cover, title , upinfo, `desc`, category, area, play_time, role, staff, total_num, style, origin_name, status
if err = rows.Scan(&li.SeasonID, &li.Cover, &li.Title, &li.UpInfo, &li.Desc, &li.Category, &li.Area,
&li.Playtime, &li.Role, &li.Staff, &li.TotalNum, &li.Style, &li.OriginName, &li.Alias, &li.PayStatus); err != nil {
log.Error("SeasonMetas row.Scan error(%v)", err)
return
}
metas[int64(li.SeasonID)] = li
}
for _, v := range metas {
v.NewestEPID, v.NewestOrder, _ = d.NewestOrder(c, v.SeasonID)
}
return
}
// NewestOrder picks one season's newest passed ep's order column value
func (d *Dao) NewestOrder(c context.Context, sid int64) (epid int64, newestOrder int, err error) {
if err = d.db.QueryRow(c, _newestOrder, sid, epStatePass, _CMSValid).Scan(&epid, &newestOrder); err != nil { // get the qualified aid to sync
log.Warn("d.NewestOrder(sid %d).Query error(%v)", sid, err)
}
return
}
// EpMetas picks ep info from DB
func (d *Dao) EpMetas(c context.Context, epids []int64) (metas map[int64]*model.EpCMS, err error) {
metas = make(map[int64]*model.EpCMS)
rows, err := d.db.Query(c, fmt.Sprintf(_epMetas, xstr.JoinInts(epids)))
if err != nil {
log.Error("EpMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
li := &model.EpCMS{}
if err = rows.Scan(&li.EPID, &li.Cover, &li.Title, &li.Subtitle, &li.PayStatus); err != nil {
log.Error("EpMetas row.Scan error(%v)", err)
return
}
metas[li.EPID] = li
}
return
}
// EpAuthDB pick ep data from DB for Cache missing case
func (d *Dao) EpAuthDB(c context.Context, cid int64) (ep *model.EpAuth, err error) {
var row *xsql.Row
if row = d.db.QueryRow(c, _simpleEPC, cid); err != nil {
log.Error("d.db.QueryRow(%d) error(%v)", cid, err)
return
}
ep = &model.EpAuth{}
if err = row.Scan(&ep.ID, &ep.EPID, &ep.State, &ep.IsDeleted, &ep.Valid, &ep.SeasonID, &ep.NoMark); err != nil {
if err == sql.ErrNoRows {
err = nil
ep = nil
} else {
log.Error("row.Scan() error(%v)", err)
}
}
return
}
// SnAuthDB .
func (d *Dao) SnAuthDB(c context.Context, cid int64) (s *model.SnAuth, err error) {
var row *xsql.Row
if row = d.db.QueryRow(c, _simpleSea, cid); err != nil {
log.Error("d.db.QueryRow(%d) error(%v)", cid, err)
return
}
s = &model.SnAuth{}
if err = row.Scan(&s.ID, &s.IsDeleted, &s.Check, &s.Valid); err != nil {
if err == sql.ErrNoRows {
err = nil
s = nil
} else {
log.Error("row.Scan() error(%v)", err)
}
}
return
}
// SnsAuthDB .
func (d *Dao) SnsAuthDB(c context.Context, sids []int64) (snsAuth map[int64]*model.SnAuth, err error) {
snsAuth = make(map[int64]*model.SnAuth)
rows, err := d.db.Query(c, fmt.Sprintf(_simpleSeas, xstr.JoinInts(sids)))
if err != nil {
log.Error("ArcMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
s := &model.SnAuth{}
if err = rows.Scan(&s.ID, &s.IsDeleted, &s.Check, &s.Valid); err != nil {
log.Error("SnsAuthDB row.Scan error(%v)", err)
return
}
snsAuth[s.ID] = s
}
return
}
// EpsAuthDB def.
func (d *Dao) EpsAuthDB(c context.Context, epids []int64) (epsAuth map[int64]*model.EpAuth, err error) {
epsAuth = make(map[int64]*model.EpAuth)
rows, err := d.db.Query(c, fmt.Sprintf(_simpleEPCs, xstr.JoinInts(epids)))
if err != nil {
log.Error("ArcMetas d.db.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
ep := &model.EpAuth{}
if err = rows.Scan(&ep.ID, &ep.EPID, &ep.State, &ep.IsDeleted, &ep.Valid, &ep.SeasonID, &ep.NoMark); err != nil {
log.Error("SnsAuthDB row.Scan error(%v)", err)
return
}
epsAuth[ep.EPID] = ep
}
return
}
// SeasonCMS gets the fields that can be changed from tv-cms side to offer the TV APP
func (d *Dao) SeasonCMS(c context.Context, sid int64) (season *model.SeasonCMS, err error) {
var row *xsql.Row
if row = d.db.QueryRow(c, _seasonCMS, sid); err != nil {
log.Error("d.db.QueryRow(%d) error(%v)", sid, err)
return
}
season = &model.SeasonCMS{}
// select id, cover, `desc`, title , upinfo, category, area, play_time, role, staff, total_num, style, status
if err = row.Scan(&season.SeasonID, &season.Cover, &season.Desc, &season.Title, &season.UpInfo,
&season.Category, &season.Area, &season.Playtime, &season.Role, &season.Staff, &season.TotalNum,
&season.Style, &season.OriginName, &season.Alias, &season.PayStatus); err != nil {
if err == sql.ErrNoRows {
err = nil
season = nil
} else {
log.Error("row.Scan(sid %d) error(%v)", sid, err)
}
return
}
// add newest info
season.NewestEPID, season.NewestOrder, _ = d.NewestOrder(c, sid)
return
}
// EpCMS gets the fields that can be changed from tv-cms side to offer the TV APP
func (d *Dao) EpCMS(c context.Context, epid int64) (ep *model.EpCMS, err error) {
var row *xsql.Row
if row = d.db.QueryRow(c, _epCMS, epid); err != nil {
log.Error("d.db.QueryRow(%d) error(%v)", epid, err)
return
}
ep = &model.EpCMS{}
// select id, cover, `desc`, title , upinfo, category, area, play_time, role, staff, total_num, style
if err = row.Scan(&ep.EPID, &ep.Cover, &ep.Title, &ep.Subtitle, &ep.PayStatus); err != nil {
if err == sql.ErrNoRows {
err = nil
ep = nil
} else {
log.Error("row.Scan(sid %d) error(%v)", epid, err)
}
return
}
return
}

View File

@@ -0,0 +1,344 @@
package cms
import (
"context"
"fmt"
"testing"
"go-common/app/interface/main/tv/model"
"go-common/library/database/sql"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsVideoMetaDB(t *testing.T) {
var (
c = context.Background()
cid = int64(0)
)
convey.Convey("VideoMetaDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.meta should not be nil.", func(ctx convey.C) {
sids, err := pickIDs(d.db, _pickCids)
if err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
cid = sids[0]
meta, err := d.VideoMetaDB(c, cid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(meta, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.VideoMetaDB(c, cid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsArcMetaDB(t *testing.T) {
var (
c = context.Background()
aid = int64(0)
)
convey.Convey("ArcMetaDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.meta should not be nil.", func(ctx convey.C) {
sids, err := pickIDs(d.db, _pickAids)
if err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
aid = sids[0]
meta, err := d.ArcMetaDB(c, aid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(meta, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.ArcMetaDB(c, aid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsVideoMetas(t *testing.T) {
var (
c = context.Background()
cids = []int64{}
err error
)
convey.Convey("VideoMetas", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.meta should not be nil.", func(ctx convey.C) {
if cids, err = pickIDs(d.db, _pickCids); err != nil || len(cids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
meta, err := d.VideoMetas(c, cids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(meta, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.VideoMetas(c, cids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsArcMetas(t *testing.T) {
var (
c = context.Background()
aids = []int64{}
err error
)
convey.Convey("ArcMetas", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.metas should not be nil.", func(ctx convey.C) {
if aids, err = pickIDs(d.db, _pickAids); err != nil || len(aids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
metas, err := d.ArcMetas(c, aids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(metas, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.ArcMetas(c, aids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsSeasonMetas(t *testing.T) {
var (
c = context.Background()
sids = []int64{}
err error
)
convey.Convey("SeasonMetas", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.metas should not be nil.", func(ctx convey.C) {
if sids, err = pickIDs(d.db, _pickSids); err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
metas, err := d.SeasonMetas(c, sids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(metas, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.SeasonMetas(c, sids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsNewestOrder(t *testing.T) {
var (
c = context.Background()
sid = int64(0)
)
convey.Convey("NewestOrder", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.epid,newestOrder should not be nil.", func(ctx convey.C) {
epid, newestOrder, err := d.NewestOrder(c, sid)
if err != nil {
ctx.So(err, convey.ShouldEqual, sql.ErrNoRows)
} else {
ctx.So(err, convey.ShouldBeNil)
ctx.So(newestOrder, convey.ShouldNotBeNil)
ctx.So(epid, convey.ShouldNotBeNil)
}
})
})
}
func TestCmsEpMetas(t *testing.T) {
var (
c = context.Background()
epids = []int64{}
err error
)
convey.Convey("EpMetas", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.metas should not be nil.", func(ctx convey.C) {
if epids, err = pickIDs(d.db, _pickEpids); err != nil || len(epids) == 0 {
fmt.Println("Empty epids ", err)
return
}
metas, err := d.EpMetas(c, epids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(metas, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.EpMetas(c, epids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsEpAuthDB(t *testing.T) {
var (
c = context.Background()
epid = int64(0)
)
convey.Convey("EpAuthDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.ep should not be nil.", func(ctx convey.C) {
epids, err := pickIDs(d.db, _pickEpids)
if err != nil || len(epids) == 0 {
fmt.Println("Empty epids ", err)
return
}
epid = epids[0]
ep, err := d.EpAuthDB(c, epid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(ep, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.EpAuthDB(c, epid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsSnAuthDB(t *testing.T) {
var (
c = context.Background()
sids []int64
sid int64
err error
)
convey.Convey("SnAuthDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.s should not be nil.", func(ctx convey.C) {
if sids, err = pickIDs(d.db, _pickSids); err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
sid = sids[0]
s, err := d.SnAuthDB(c, sid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(s, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.SnAuthDB(c, sid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsSnsAuthDB(t *testing.T) {
var (
c = context.Background()
sids []int64
err error
snsAuth map[int64]*model.SnAuth
)
convey.Convey("SnsAuthDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.snsAuth should not be nil.", func(ctx convey.C) {
if sids, err = pickIDs(d.db, _pickSids); err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
snsAuth, err = d.SnsAuthDB(c, sids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(snsAuth, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err = d.SnsAuthDB(c, sids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsEpsAuthDB(t *testing.T) {
var (
c = context.Background()
epids []int64
err error
epsAuth map[int64]*model.EpAuth
)
convey.Convey("EpsAuthDB", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.epsAuth should not be nil.", func(ctx convey.C) {
epids, err = pickIDs(d.db, _pickEpids)
if err != nil || len(epids) == 0 {
fmt.Println("Empty epids ", err)
return
}
epsAuth, err = d.EpsAuthDB(c, epids)
ctx.So(err, convey.ShouldBeNil)
ctx.So(epsAuth, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err = d.EpsAuthDB(c, epids)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsSeasonCMS(t *testing.T) {
var (
c = context.Background()
sids []int64
sid = int64(0)
err error
season *model.SeasonCMS
)
convey.Convey("SeasonCMS", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.season should not be nil.", func(ctx convey.C) {
sids, err = pickIDs(d.db, _pickSids)
if err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
sid = sids[0]
season, err = d.SeasonCMS(c, sid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(season, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err = d.SeasonCMS(c, sid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}
func TestCmsEpCMS(t *testing.T) {
var (
c = context.Background()
epid = int64(0)
)
convey.Convey("EpCMS", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.ep should not be nil.", func(ctx convey.C) {
epids, err := pickIDs(d.db, _pickEpids)
if err != nil || len(epids) == 0 {
fmt.Println("Empty epids ", err)
return
}
epid = epids[0]
ep, err := d.EpCMS(c, epid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(ep, convey.ShouldNotBeNil)
})
ctx.Convey("Db Error", func(ctx convey.C) {
d.db.Close()
_, err := d.EpCMS(c, epid)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}

View File

@@ -0,0 +1,72 @@
package cms
import (
"runtime"
"time"
"go-common/app/interface/main/tv/conf"
"go-common/library/cache/memcache"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/stat/prom"
)
// Dao is account dao.
type Dao struct {
mc *memcache.Pool
conf *conf.Config
db *sql.DB
mCh chan func()
expireCMS int32
}
// New account dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
conf: c,
mc: memcache.NewPool(c.Memcache.Config),
db: sql.NewMySQL(c.Mysql),
mCh: make(chan func(), 10240),
expireCMS: int32(time.Duration(c.Memcache.CmsExpire) / time.Second),
}
// video db
for i := 0; i < runtime.NumCPU()*2; i++ {
go d.cacheproc()
}
return
}
// Prom
var (
errorsCount = prom.BusinessErrCount
infosCount = prom.BusinessInfoCount
cachedCount = prom.CacheHit
missedCount = prom.CacheMiss
)
// PromError prom error
func PromError(name string) {
errorsCount.Incr(name)
}
// PromInfo add prom info
func PromInfo(name string) {
infosCount.Incr(name)
}
// addCache add archive to mc or redis
func (d *Dao) addCache(f func()) {
select {
case d.mCh <- f:
default:
log.Warn("cacheproc chan full")
}
}
// cacheproc write memcache and stat redis use goroutine
func (d *Dao) cacheproc() {
for {
f := <-d.mCh
f()
}
}

View File

@@ -0,0 +1,121 @@
package cms
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"testing"
"go-common/app/interface/main/tv/conf"
"go-common/library/database/sql"
. "github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
ctx = context.TODO()
)
const (
_pickSids = 1
_pickEpids = 2
_pickAids = 3
_pickCids = 4
)
func init() {
// dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
// flag.Set("conf", dir)
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}
func pickIDs(db *sql.DB, ttype int) (testSids []int64, err error) {
var (
querySQL string
rows *sql.Rows
)
switch ttype {
case _pickSids:
querySQL = "SELECT DISTINCT(id) FROM tv_ep_season WHERE is_deleted = 0 ORDER BY id DESC LIMIT 20"
case _pickEpids:
querySQL = "SELECT DISTINCT(epid) FROM tv_content WHERE is_deleted = 0 ORDER BY id DESC LIMIT 20"
case _pickAids:
querySQL = "SELECT DISTINCT(aid) FROM ugc_archive WHERE deleted = 0 ORDER BY id DESC LIMIT 20"
case _pickCids:
querySQL = "SELECT DISTINCT(cid) FROM ugc_video WHERE deleted = 0 ORDER BY id DESC LIMIT 20"
}
rows, err = db.Query(ctx, querySQL)
if err != nil {
fmt.Println("Query Err ", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
fmt.Printf("Scan Id %d, Err %v", id, err)
return
}
testSids = append(testSids, id)
}
return
}
func TestDao_MixedFilter(t *testing.T) {
Convey("TestDao_MixedFilter", t, WithDao(func(d *Dao) {
sids, errDB := pickIDs(d.db, _pickSids)
if errDB != nil {
fmt.Println("PickSids Err ", errDB)
return
}
aids, errDB2 := pickIDs(d.db, _pickAids)
if errDB != nil {
fmt.Println("PickAids Err ", errDB2)
return
}
okSids, okAids := d.MixedFilter(ctx, sids, aids)
fmt.Println(okSids)
fmt.Println(okAids)
So(len(okSids), ShouldBeLessThanOrEqualTo, len(sids))
So(len(okAids), ShouldBeLessThanOrEqualTo, len(aids))
}))
}
func TestDao_LoadVideosMeta(t *testing.T) {
Convey("TestDao_MixedFilter", t, WithDao(func(d *Dao) {
sids, errDB := pickIDs(d.db, _pickCids)
if errDB != nil {
fmt.Println("PickSids Err ", errDB)
return
}
res, err := d.LoadVideosMeta(ctx, sids)
So(err, ShouldBeNil)
data, _ := json.Marshal(res)
fmt.Println(string(data))
}))
}

View File

@@ -0,0 +1,18 @@
package cms
import (
"context"
"fmt"
"go-common/library/xstr"
)
const (
_unshelveArcs = "UPDATE ugc_archive SET valid = 0 WHERE aid IN (%s) AND valid = 1 AND deleted = 0"
)
// UnshelveArcs unshelves the arcs
func (d *Dao) UnshelveArcs(c context.Context, ids []int64) (err error) {
_, err = d.db.Exec(c, fmt.Sprintf(_unshelveArcs, xstr.JoinInts(ids)))
return
}

View File

@@ -0,0 +1,21 @@
package cms
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsUnshelveArcs(t *testing.T) {
var (
c = context.Background()
ids = []int64{123}
)
convey.Convey("UnshelveArcs", t, func(ctx convey.C) {
err := d.UnshelveArcs(c, ids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,77 @@
package cms
import "go-common/app/interface/main/tv/model"
const (
// auth failure cases
_pgcOffline = "pgc_offline"
_cmsInvalid = "cms_invalid"
_licenseReject = "license_reject"
// auth correct values in DB
_contentOnline = 0
_snPass = 1
_epPass = 3
_cmsValid = 1
)
// AuthMsg returns the error message in the config according to the error condition
func (d *Dao) authMsg(cond string) (msg string) {
msgCfg := d.conf.Cfg.AuthMsg
if cond == _licenseReject {
return msgCfg.LicenseReject
}
if cond == _cmsInvalid {
return msgCfg.CMSInvalid
}
if cond == _pgcOffline {
return msgCfg.PGCOffline
}
return "err msg config load error"
}
// SnErrMsg returns the season auth result and the error message in case of auth failure
func (d *Dao) SnErrMsg(season *model.SnAuth) (bool, string) {
if season.IsDeleted == _contentOnline && season.Check == _snPass && season.Valid == _cmsValid {
return true, ""
}
if season.Check != _snPass {
return false, d.authMsg(_licenseReject)
}
if season.IsDeleted != _contentOnline {
return false, d.authMsg(_pgcOffline)
}
return false, d.authMsg(_cmsInvalid) // must be cms valid logically
}
// UgcErrMsg returns the arc auth result and the error message in case of auth failure
func (d *Dao) UgcErrMsg(deleted int, result int, valid int) (bool, string) {
if deleted == _contentOnline && result == _snPass && valid == _cmsValid {
return true, ""
}
if result != _snPass {
return false, d.authMsg(_licenseReject)
}
if deleted != _contentOnline {
return false, d.authMsg(_pgcOffline)
}
return false, d.authMsg(_cmsInvalid) // must be cms valid logically
}
// AuditingMsg returns the msg for the archive whose all videos are being audited
func (d *Dao) AuditingMsg() (bool, string) {
return false, d.authMsg(_licenseReject) // must be cms valid logically
}
// EpErrMsg returns the ep auth result and the error message in case of auth failure
func (d *Dao) EpErrMsg(ep *model.EpAuth) (bool, string) {
if ep.IsDeleted == _contentOnline && ep.State == _epPass && ep.Valid == _cmsValid {
return true, ""
}
if ep.State != _epPass {
return false, d.authMsg(_licenseReject)
}
if ep.IsDeleted != _contentOnline {
return false, d.authMsg(_pgcOffline)
}
return false, d.authMsg(_cmsInvalid) // must be cms valid logically
}

View File

@@ -0,0 +1,71 @@
package cms
import (
"go-common/app/interface/main/tv/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsauthMsg(t *testing.T) {
var (
cond = ""
)
convey.Convey("authMsg", t, func(ctx convey.C) {
msg := d.authMsg(cond)
ctx.Convey("Then msg should not be nil.", func(ctx convey.C) {
ctx.So(msg, convey.ShouldNotBeNil)
})
})
}
func TestCmsSnErrMsg(t *testing.T) {
var (
season = &model.SnAuth{}
)
convey.Convey("SnErrMsg", t, func(ctx convey.C) {
p1, p2 := d.SnErrMsg(season)
ctx.Convey("Then p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsUgcErrMsg(t *testing.T) {
var (
deleted = int(0)
result = int(0)
valid = int(0)
)
convey.Convey("UgcErrMsg", t, func(ctx convey.C) {
p1, p2 := d.UgcErrMsg(deleted, result, valid)
ctx.Convey("Then p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsAuditingMsg(t *testing.T) {
convey.Convey("AuditingMsg", t, func(ctx convey.C) {
p1, p2 := d.AuditingMsg()
ctx.Convey("Then p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsEpErrMsg(t *testing.T) {
var (
ep = &model.EpAuth{}
)
convey.Convey("EpErrMsg", t, func(ctx convey.C) {
p1, p2 := d.EpErrMsg(ep)
ctx.Convey("Then p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,67 @@
package cms
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// MixedFilter filters ugc and pgc data to get the allowed data
func (d *Dao) MixedFilter(ctx context.Context, sids []int64, aids []int64) (okSids map[int64]int, okAids map[int64]int) {
g, _ := errgroup.WithContext(ctx)
g.Go(func() (err error) {
okAids = d.aidsFilter(context.Background(), aids)
return
})
g.Go(func() (err error) {
okSids = d.sidsFilter(context.Background(), sids)
return
})
g.Wait()
return
}
// filter canPlay Aids
func (d *Dao) aidsFilter(ctx context.Context, aids []int64) (okAids map[int64]int) {
var (
arcMetas map[int64]*model.ArcCMS
err error
)
okAids = make(map[int64]int)
if arcMetas, err = d.LoadArcsMediaMap(ctx, aids); err != nil {
log.Error("MixedFilter Aids %v, Err %v", aids, err)
return
}
if len(arcMetas) == 0 {
return
}
for aid, arcMeta := range arcMetas {
if arcMeta.CanPlay() {
okAids[aid] = 1
}
}
return
}
// filter canPlay Sids
func (d *Dao) sidsFilter(ctx context.Context, sids []int64) (okSids map[int64]int) {
var (
snsAuth map[int64]*model.SnAuth
err error
)
okSids = make(map[int64]int)
if snsAuth, err = d.LoadSnsAuthMap(ctx, sids); err != nil {
log.Error("MixedFilter Sids %v, Err %v", sids, err)
}
if len(snsAuth) == 0 {
return
}
for sid, snAuth := range snsAuth {
if snAuth.CanPlay() {
okSids[sid] = 1
}
}
return
}

View File

@@ -0,0 +1,49 @@
package cms
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsMixedFilter(t *testing.T) {
var (
ctx = context.Background()
sids = []int64{}
aids = []int64{}
)
convey.Convey("MixedFilter", t, func(c convey.C) {
okSids, okAids := d.MixedFilter(ctx, sids, aids)
c.Convey("Then okSids,okAids should not be nil.", func(c convey.C) {
c.So(okAids, convey.ShouldNotBeNil)
c.So(okSids, convey.ShouldNotBeNil)
})
})
}
func TestCmsaidsFilter(t *testing.T) {
var (
ctx = context.Background()
aids = []int64{}
)
convey.Convey("aidsFilter", t, func(c convey.C) {
okAids := d.aidsFilter(ctx, aids)
c.Convey("Then okAids should not be nil.", func(c convey.C) {
c.So(okAids, convey.ShouldNotBeNil)
})
})
}
func TestCmssidsFilter(t *testing.T) {
var (
ctx = context.Background()
sids = []int64{}
)
convey.Convey("sidsFilter", t, func(c convey.C) {
okSids := d.sidsFilter(ctx, sids)
c.Convey("Then okSids should not be nil.", func(c convey.C) {
c.So(okSids, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,198 @@
package cms
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_mcEPKey = "ep_%d"
_mcSeasonKey = "sn_%d"
)
// SeaCacheKey .
func (d *Dao) SeaCacheKey(sid int64) string {
return fmt.Sprintf(_mcSeasonKey, sid)
}
// EPCacheKey .
func (d *Dao) EPCacheKey(epid int64) string {
return fmt.Sprintf(_mcEPKey, epid)
}
// GetSeasonCache get SnAuth cache.
func (d *Dao) GetSeasonCache(c context.Context, sid int64) (s *model.SnAuth, err error) {
var (
key = d.SeaCacheKey(sid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &s); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
// GetEPCache get EpAuth cache.
func (d *Dao) GetEPCache(c context.Context, epid int64) (ep *model.EpAuth, err error) {
var (
key = d.EPCacheKey(epid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &ep); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
// AddSnAuthCache save model.SnAuth to memcache
func (d *Dao) AddSnAuthCache(c context.Context, s *model.SnAuth) (err error) {
var (
key = d.SeaCacheKey(s.ID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: s, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}
// AddEpAuthCache save model.EpAuth to memcache
func (d *Dao) AddEpAuthCache(c context.Context, ep *model.EpAuth) (err error) {
var (
key = d.EPCacheKey(ep.EPID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: ep, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}
// snAuthCache season auth cache
func (d *Dao) snAuthCache(c context.Context, ids []int64) (cached map[int64]*model.SnAuth, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.SnAuth, len(ids))
idmap, allKeys := keysTreat(ids, d.SeaCacheKey)
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取Season信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
return
}
for key, item := range replys {
art := &model.SnAuth{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取Season信息缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = missedTreat(idmap, len(cached))
return
}
// epAuthCache ep auth cache
func (d *Dao) epAuthCache(c context.Context, ids []int64) (cached map[int64]*model.EpAuth, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.EpAuth, len(ids))
idmap, allKeys := keysTreat(ids, d.EPCacheKey)
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取EP信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
return
}
for key, item := range replys {
art := &model.EpAuth{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取EP信息缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = missedTreat(idmap, len(cached))
return
}
// SnAuth get's season auth info from Cache & DB
func (d *Dao) SnAuth(c context.Context, sid int64) (sn *model.SnAuth, err error) {
if sn, err = d.GetSeasonCache(c, sid); err != nil { // mc Error
return
} else if sn == nil { // mc not found, go DB
if sn, err = d.SnAuthDB(c, int64(sid)); err != nil { // DB error
log.Error("SnAuthDB (%d) ERROR (%v)", sid, err)
return
}
if sn == nil { // DB not found, build a fake item in MC to avoid checking DB next time
log.Error("SnAuthDB (%d) not found(%v) in DB", sid)
sn = &model.SnAuth{ID: int64(sid), Check: 0, IsDeleted: 1}
}
if err = d.AddSnAuthCache(c, sn); err != nil { // set item in MC ( not found - fake, or true )
log.Error("AddSnAuthCache fail(%v)", err)
}
}
return
}
// EpAuth get's ep auth info from Cache & DB
func (d *Dao) EpAuth(c context.Context, epid int64) (ep *model.EpAuth, err error) {
if ep, err = d.GetEPCache(c, epid); err != nil { // MC error
log.Error("GetEPCache(%d) Error(%v)", epid, err)
return
} else if ep == nil { // MC not found, go DB
if ep, err = d.EpAuthDB(c, epid); err != nil { // DB error
log.Error("DBSimpleEP(%d) ERROR (%v)", epid, err)
return
}
if ep == nil { // DB not found, build a fake item in MC to avoid checking DB next time
log.Error("EPID(%d) not found", epid)
ep = &model.EpAuth{EPID: epid, State: 4, IsDeleted: 1}
}
if err = d.AddEpAuthCache(c, ep); err != nil {
log.Error("AddEpAuthCache fail(%v)", err)
}
}
return
}

View File

@@ -0,0 +1,134 @@
package cms
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsSeaCacheKey(t *testing.T) {
var (
sid = int64(0)
)
convey.Convey("SeaCacheKey", t, func(c convey.C) {
p1 := d.SeaCacheKey(sid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsEPCacheKey(t *testing.T) {
var (
epid = int64(0)
)
convey.Convey("EPCacheKey", t, func(c convey.C) {
p1 := d.EPCacheKey(epid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsGetSeasonCache(t *testing.T) {
var (
ctx = context.Background()
sid = int64(0)
)
convey.Convey("GetSeasonCache", t, func(c convey.C) {
s, err := d.GetSeasonCache(ctx, sid)
c.Convey("Then err should be nil.s should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(s, convey.ShouldNotBeNil)
})
})
}
func TestCmsGetEPCache(t *testing.T) {
var (
ctx = context.Background()
epid = int64(0)
)
convey.Convey("GetEPCache", t, func(c convey.C) {
ep, err := d.GetEPCache(ctx, epid)
c.Convey("Then err should be nil.ep should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(ep, convey.ShouldNotBeNil)
})
})
}
func TestCmsAddSnAuthCache(t *testing.T) {
var (
ctx = context.Background()
s = &model.SnAuth{}
)
convey.Convey("AddSnAuthCache", t, func(c convey.C) {
err := d.AddSnAuthCache(ctx, s)
c.Convey("Then err should be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
})
})
}
func TestCmsAddEpAuthCache(t *testing.T) {
var (
ctx = context.Background()
ep = &model.EpAuth{}
)
convey.Convey("AddEpAuthCache", t, func(c convey.C) {
err := d.AddEpAuthCache(ctx, ep)
c.Convey("Then err should be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
})
})
}
func TestCmssnAuthCache(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("snAuthCache", t, func(c convey.C) {
sids, errPick := pickIDs(d.db, _pickSids)
if errPick != nil || len(sids) == 0 {
fmt.Println("Empty sids ", errPick)
return
}
cached, missed, err := d.snAuthCache(ctx, sids)
c.Convey("Then err should be nil.cached,missed should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(len(missed)+len(cached), convey.ShouldBeGreaterThan, 0)
})
})
}
func TestCmsSnAuth(t *testing.T) {
var (
ctx = context.Background()
sid = int64(0)
)
convey.Convey("SnAuth", t, func(c convey.C) {
sn, err := d.SnAuth(ctx, sid)
c.Convey("Then err should be nil.sn should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(sn, convey.ShouldNotBeNil)
})
})
}
func TestCmsEpAuth(t *testing.T) {
var (
ctx = context.Background()
epid = int64(0)
)
convey.Convey("EpAuth", t, func(c convey.C) {
ep, err := d.EpAuth(ctx, epid)
c.Convey("Then err should be nil.ep should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(ep, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,152 @@
package cms
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_mcSnCMSKey = "sn_cms_%d"
_mcEPCMSKey = "ep_cms_%d"
)
func snCMSCacheKey(sid int64) string {
return fmt.Sprintf(_mcSnCMSKey, sid)
}
func epCMSCacheKey(epid int64) string {
return fmt.Sprintf(_mcEPCMSKey, epid)
}
func keysTreat(ids []int64, keyFunc func(int64) string) (idmap map[string]int64, allKeys []string) {
idmap = make(map[string]int64, len(ids))
for _, id := range ids {
k := keyFunc(id)
allKeys = append(allKeys, k)
idmap[k] = id
}
return
}
func missedTreat(idmap map[string]int64, lenCached int) (missed []int64) {
missed = make([]int64, 0, len(idmap))
for _, id := range idmap {
missed = append(missed, id)
}
missedCount.Add("tv-meta", int64(len(missed)))
cachedCount.Add("tv-meta", int64(lenCached))
return
}
// SeasonsMetaCache season cms meta cache
func (d *Dao) SeasonsMetaCache(c context.Context, ids []int64) (cached map[int64]*model.SeasonCMS, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.SeasonCMS, len(ids))
idmap, allKeys := keysTreat(ids, snCMSCacheKey)
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取Season信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
err = nil
return
}
for key, item := range replys {
art := &model.SeasonCMS{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取Season信息缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = missedTreat(idmap, len(cached))
return
}
// EpMetaCache season cms meta cache
func (d *Dao) EpMetaCache(c context.Context, ids []int64) (cached map[int64]*model.EpCMS, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.EpCMS, len(ids))
allKeys := make([]string, 0, len(ids))
idmap := make(map[string]int64, len(ids))
for _, id := range ids {
k := epCMSCacheKey(id)
allKeys = append(allKeys, k)
idmap[k] = id
}
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取EP信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
err = nil
return
}
for key, item := range replys {
art := &model.EpCMS{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取EP信息缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = make([]int64, 0, len(idmap))
for _, id := range idmap {
missed = append(missed, int64(id))
}
missedCount.Add("tv-meta", int64(len(missed)))
cachedCount.Add("tv-meta", int64(len(cached)))
return
}
//AddSeasonMetaCache add season meta cache
func (d *Dao) AddSeasonMetaCache(c context.Context, vs ...*model.SeasonCMS) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for _, v := range vs {
if v == nil {
continue
}
item := &memcache.Item{Key: snCMSCacheKey(v.SeasonID), Object: v, Flags: memcache.FlagJSON, Expiration: d.expireCMS}
if err = conn.Set(item); err != nil {
PromError("mc:增加Season信息缓存")
log.Error("conn.Store(%s) error(%v)", snCMSCacheKey(v.SeasonID), err)
return
}
}
return
}
//AddEpMetaCache add ep meta cache
func (d *Dao) AddEpMetaCache(c context.Context, vs ...*model.EpCMS) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for _, v := range vs {
if v == nil {
continue
}
item := &memcache.Item{Key: epCMSCacheKey(v.EPID), Object: v, Flags: memcache.FlagJSON, Expiration: d.expireCMS}
if err = conn.Set(item); err != nil {
PromError("mc:增加EP信息缓存")
log.Error("conn.Store(%s) error(%v)", epCMSCacheKey(v.EPID), err)
return
}
}
return
}

View File

@@ -0,0 +1,111 @@
package cms
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmssnCMSCacheKey(t *testing.T) {
var (
sid = int64(0)
)
convey.Convey("snCMSCacheKey", t, func(c convey.C) {
p1 := snCMSCacheKey(sid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsepCMSCacheKey(t *testing.T) {
var (
epid = int64(0)
)
convey.Convey("epCMSCacheKey", t, func(c convey.C) {
p1 := epCMSCacheKey(epid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmskeysTreat(t *testing.T) {
var (
ids = []int64{1, 2, 3}
keyFunc = snCMSCacheKey
)
convey.Convey("keysTreat", t, func(c convey.C) {
idmap, allKeys := keysTreat(ids, keyFunc)
c.Convey("Then idmap,allKeys should not be nil.", func(c convey.C) {
c.So(allKeys, convey.ShouldNotBeNil)
c.So(idmap, convey.ShouldNotBeNil)
})
})
}
func TestCmsmissedTreat(t *testing.T) {
var (
idmap map[string]int64
lenCached = int(0)
)
convey.Convey("missedTreat", t, func(c convey.C) {
missed := missedTreat(idmap, lenCached)
c.Convey("Then missed should not be nil.", func(c convey.C) {
c.So(missed, convey.ShouldNotBeNil)
})
})
}
func TestCmsEpMetaCache(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("EpMetaCache", t, func(c convey.C) {
c.Convey("Empty Input", func(c convey.C) {
cached, missed, err := d.EpMetaCache(ctx, []int64{})
c.So(err, convey.ShouldBeNil)
c.So(len(missed), convey.ShouldBeZeroValue)
c.So(len(cached), convey.ShouldBeZeroValue)
})
c.Convey("Normal Situation", func(c convey.C) {
epids, err := pickIDs(d.db, _pickEpids)
if err != nil || len(epids) == 0 {
fmt.Println("empty epids")
return
}
cached, missed, err := d.EpMetaCache(ctx, epids)
c.So(err, convey.ShouldBeNil)
c.So(len(missed)+len(cached), convey.ShouldNotEqual, 0)
})
})
}
func TestCmsAddSeasonMetaCache(t *testing.T) {
var (
ctx = context.Background()
vs = &model.SeasonCMS{}
)
convey.Convey("AddSeasonMetaCache", t, func(c convey.C) {
err := d.AddSeasonMetaCache(ctx, vs)
c.Convey("Then err should be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
})
})
}
func TestCmsAddEpMetaCache(t *testing.T) {
var (
ctx = context.Background()
vs = &model.EpCMS{}
)
convey.Convey("AddEpMetaCache", t, func(c convey.C) {
err := d.AddEpMetaCache(ctx, vs)
c.Convey("Then err should be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,226 @@
package cms
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
)
// LoadSnsAuthMap loads a batch of arc meta
func (d *Dao) LoadSnsAuthMap(ctx context.Context, sids []int64) (resMetas map[int64]*model.SnAuth, err error) {
var (
cachedMetas map[int64]*model.SnAuth // cache hit seasons
missedMetas map[int64]*model.SnAuth // cache miss seasons, pick from DB
missed []int64 // cache miss seasons
addCache = true // whether we need to fill DB data in MC
)
resMetas = make(map[int64]*model.SnAuth) // merge info from MC and from DB
if cachedMetas, missed, err = d.snAuthCache(ctx, sids); err != nil {
log.Error("LoadSnsAuthMap snAuthCache Sids:%v, Error:%v", sids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.SnsAuthDB(ctx, missed); err != nil {
log.Error("LoadSnsAuthMap SnsAuthDB Sids:%v, Error:%v", missed, err)
return
}
}
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
if addCache && len(missedMetas) > 0 {
for _, snAuth := range missedMetas {
d.addCache(func() {
d.AddSnAuthCache(ctx, snAuth)
})
}
}
return
}
// LoadEpsAuthMap loads a batch of arc meta
func (d *Dao) LoadEpsAuthMap(ctx context.Context, epids []int64) (resMetas map[int64]*model.EpAuth, err error) {
var (
cachedMetas map[int64]*model.EpAuth // cache hit seasons
missedMetas map[int64]*model.EpAuth // cache miss seasons, pick from DB
missed []int64 // cache miss seasons
addCache = true // whether we need to fill DB data in MC
)
resMetas = make(map[int64]*model.EpAuth) // merge info from MC and from DB
if cachedMetas, missed, err = d.epAuthCache(ctx, epids); err != nil {
log.Error("LoadEpsAuthMap epAuthCache epids:%v, Error:%v", epids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.EpsAuthDB(ctx, missed); err != nil {
log.Error("LoadEpsAuthMap EpsAuthDB epids:%v, Error:%v", missed, err)
return
}
}
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
if addCache && len(missedMetas) > 0 {
for _, epAuth := range missedMetas {
d.addCache(func() {
d.AddEpAuthCache(ctx, epAuth)
})
}
}
return
}
// LoadSnsCMSMap loads season cms meta data from cache and db
func (d *Dao) LoadSnsCMSMap(ctx context.Context, sids []int64) (resMetas map[int64]*model.SeasonCMS, err error) {
var (
cachedMetas, missedMetas map[int64]*model.SeasonCMS // cache hit seasons
missed []int64 // cache miss seasons
addCache = true // whether we need to fill DB data in MC
)
resMetas = make(map[int64]*model.SeasonCMS)
// pick up the information for these season ids
if cachedMetas, missed, err = d.SeasonsMetaCache(ctx, sids); err != nil {
log.Error("LoadSnsCMS SeasonMetaCache Sids:%v, Error:%v", sids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.SeasonMetas(ctx, missed); err != nil {
log.Error("LoadSnsCMS SeasonMetas Sids:%v, Error:%v", sids, err)
return
}
}
log.Info("Set Sids [%d], HitMetas [%d], MissedMetas [%d][%d] Data in MC", len(sids), len(cachedMetas), len(missed), len(missedMetas))
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
// async Reset the DB data in MC for next time
if addCache && len(missedMetas) > 0 {
for _, art := range missedMetas {
d.addCache(func() {
d.AddSeasonMetaCache(ctx, art)
})
}
}
return
}
// LoadSnsCMS loads the seasons meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadSnsCMS(ctx context.Context, sids []int64) (seasons []*model.SeasonCMS, newestEpids []int64, err error) {
var (
resMetas map[int64]*model.SeasonCMS // merge info from MC and from DB
)
if resMetas, err = d.LoadSnsCMSMap(ctx, sids); err != nil {
log.Error("LoadSnsCMS Sids %v, Err %v", sids, err)
return
}
// re-arrange the info, according to the order got from Redis
for _, v := range sids {
if SnCMS, ok := resMetas[v]; !ok {
log.Error("LoadSnsCMS Miss Info for Sid: %d", v)
continue
} else {
seasons = append(seasons, SnCMS)
newestEpids = append(newestEpids, SnCMS.NewestEPID)
}
}
return
}
// LoadSnCMS loads the sn meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadSnCMS(ctx context.Context, sid int64) (sn *model.SeasonCMS, err error) {
if sn, err = d.GetSnCMSCache(ctx, sid); err != nil {
log.Error("LoadSnsCMS Get Season[%d] from CMS Error (%v)", sid, err) // cache set/get error
return
}
if sn != nil { // if cache hit, return
return
}
if sn, err = d.SeasonCMS(ctx, sid); err != nil {
log.Error("[LoadSnCMS] SeasonCMS SeasonID ERROR (%d) (%v)", sid, err)
return
} else if sn == nil {
err = ecode.NothingFound
return
}
d.addCache(func() {
d.AddSeasonMetaCache(ctx, sn)
})
return
}
// LoadEpCMS loads the sn meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadEpCMS(ctx context.Context, epid int64) (ep *model.EpCMS, err error) {
if ep, err = d.GetEpCMSCache(ctx, epid); err != nil {
log.Error("LoadEpCMS Get EP[%d] from CMS Error (%v)", epid, err) // cache set/get error
return
} else if ep == nil {
if ep, err = d.EpCMS(ctx, epid); err != nil {
log.Error("[LoadEpCMS] EpCMS Epid ERROR (%d) (%v)", epid, err)
return
} else if ep == nil {
err = ecode.NothingFound
return
}
}
d.addCache(func() {
d.SetEpCMSCache(ctx, ep)
})
return
}
// LoadEpsCMS picks ep meta information from Cache & DB
func (d *Dao) LoadEpsCMS(ctx context.Context, epids []int64) (resMetas map[int64]*model.EpCMS, err error) {
var (
cachedMetas, missedMetas map[int64]*model.EpCMS
missed []int64
addCache = true
)
resMetas = make(map[int64]*model.EpCMS)
// pick up the information for these season ids
if cachedMetas, missed, err = d.EpMetaCache(ctx, epids); err != nil {
log.Error("loadEpCMS EpMetaCache Sids:%v, Error:%v", epids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.EpMetas(ctx, missed); err != nil {
log.Error("loadEpCMS EpMetas Sids:%v, Error:%v", epids, err)
return
}
}
// merge info from DB and the info from MC
resMetas = make(map[int64]*model.EpCMS, len(epids))
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
log.Info("Combine Info for %d Epids, Origin Length %d", len(epids), len(resMetas))
if addCache && len(missedMetas) > 0 { // async Reset the DB data in MC for next time
log.Info("Set MissedMetas %d Data in MC", missedMetas)
for _, art := range missedMetas {
d.addCache(func() {
d.AddEpMetaCache(ctx, art)
})
}
}
return
}

View File

@@ -0,0 +1,113 @@
package cms
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsLoadSnsAuthMap(t *testing.T) {
var (
ctx = context.Background()
sids = []int64{}
)
convey.Convey("LoadSnsAuthMap", t, func(c convey.C) {
resMetas, err := d.LoadSnsAuthMap(ctx, sids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resMetas, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadEpsAuthMap(t *testing.T) {
var (
ctx = context.Background()
epids = []int64{}
)
convey.Convey("LoadEpsAuthMap", t, func(c convey.C) {
resMetas, err := d.LoadEpsAuthMap(ctx, epids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resMetas, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadSnsCMSMap(t *testing.T) {
var (
ctx = context.Background()
sids = []int64{}
)
convey.Convey("LoadSnsCMSMap", t, func(c convey.C) {
resMetas, err := d.LoadSnsCMSMap(ctx, sids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resMetas, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadSnsCMS(t *testing.T) {
var (
ctx = context.Background()
sids = []int64{}
err error
)
convey.Convey("LoadSnsCMS", t, func(c convey.C) {
c.Convey("Then err should be nil.seasons,newestEpids should not be nil.", func(cx convey.C) {
if sids, err = pickIDs(d.db, _pickSids); err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
seasons, newestEpids, err := d.LoadSnsCMS(ctx, sids)
cx.So(err, convey.ShouldBeNil)
cx.So(newestEpids, convey.ShouldNotBeNil)
cx.So(seasons, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadSnCMS(t *testing.T) {
var (
ctx = context.Background()
sid = int64(0)
)
convey.Convey("LoadSnCMS", t, func(c convey.C) {
sn, err := d.LoadSnCMS(ctx, sid)
c.Convey("Then err should be nil.sn should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(sn, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadEpCMS(t *testing.T) {
var (
ctx = context.Background()
epid = int64(0)
)
convey.Convey("LoadEpCMS", t, func(c convey.C) {
ep, err := d.LoadEpCMS(ctx, epid)
c.Convey("Then err should be nil.ep should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(ep, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadEpsCMS(t *testing.T) {
var (
ctx = context.Background()
epids = []int64{}
)
convey.Convey("LoadEpsCMS", t, func(c convey.C) {
resMetas, err := d.LoadEpsCMS(ctx, epids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(resMetas, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,85 @@
package cms
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
// GetSnCMSCache get SeasonCMS cache.
func (d *Dao) GetSnCMSCache(c context.Context, sid int64) (s *model.SeasonCMS, err error) {
var (
key = snCMSCacheKey(sid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
missedCount.Add("tv-meta", 1)
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &s); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
cachedCount.Add("tv-meta", 1)
return
}
// SetSnCMSCache save model.SeasonCMS to memcache
func (d *Dao) SetSnCMSCache(c context.Context, s *model.SeasonCMS) (err error) {
var (
key = snCMSCacheKey(s.SeasonID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: s, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}
// GetEpCMSCache get EpCMS cache.
func (d *Dao) GetEpCMSCache(c context.Context, epid int64) (s *model.EpCMS, err error) {
var (
key = epCMSCacheKey(epid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
missedCount.Add("tv-meta", 1)
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &s); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
cachedCount.Add("tv-meta", 1)
return
}
// SetEpCMSCache save model.EpCMS to memcache
func (d *Dao) SetEpCMSCache(c context.Context, s *model.EpCMS) (err error) {
var (
key = epCMSCacheKey(s.EPID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: s, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,77 @@
package cms
import (
"fmt"
"testing"
"go-common/app/interface/main/tv/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_SetSnCMSCache(t *testing.T) {
Convey("TestDao_SetSnCMSCache Test", t, WithDao(func(d *Dao) {
err := d.SetSnCMSCache(ctx, &model.SeasonCMS{
SeasonID: 6462,
Cover: "Test_cover1",
Title: "Test_title1",
Desc: "Test_desc1",
})
So(err, ShouldBeNil)
}))
}
func TestDao_GetSnCMSCache(t *testing.T) {
Convey("TestDao_GetSnCMSCache Test", t, WithDao(func(d *Dao) {
sids, errPick := pickIDs(d.db, _pickSids)
if errPick != nil || len(sids) == 0 {
fmt.Println("Empty sids ", errPick)
return
}
sid := sids[0]
d.LoadSnCMS(ctx, sid)
season, err := d.GetSnCMSCache(ctx, sid)
So(err, ShouldBeNil)
So(season, ShouldNotBeNil)
fmt.Println(*season)
}))
}
func TestDao_GetEpCMSCache(t *testing.T) {
Convey("TestDao_GetEpCMSCache Test", t, WithDao(func(d *Dao) {
sids, errPick := pickIDs(d.db, _pickEpids)
if errPick != nil || len(sids) == 0 {
fmt.Println("Empty sids ", errPick)
return
}
epid := sids[1]
ep, err := d.GetEpCMSCache(ctx, epid)
So(err, ShouldBeNil)
So(ep, ShouldNotBeNil)
fmt.Println(*ep)
}))
}
func TestDao_SnCMSCacheKey(t *testing.T) {
Convey("TestDao_SnCMSCacheKey Test", t, WithDao(func(d *Dao) {
key := snCMSCacheKey(177)
So(key, ShouldNotBeBlank)
fmt.Println(key)
}))
}
func TestDao_EPCMSCacheKey(t *testing.T) {
Convey("TestDao_EPCMSCacheKey Test", t, WithDao(func(d *Dao) {
key := epCMSCacheKey(1)
So(key, ShouldNotBeBlank)
fmt.Println(key)
}))
}
func TestDao_ArcCMSCacheKey(t *testing.T) {
Convey("TestDao_ArcCMSCacheKey Test", t, WithDao(func(d *Dao) {
key := d.ArcCMSCacheKey(177)
So(key, ShouldNotBeBlank)
fmt.Println(key)
}))
}

View File

@@ -0,0 +1,91 @@
package cms
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
const (
_arcCMSDeleted = 1
_mcArcCMSKey = "arc_cms_%d"
_mcVideoCMSKey = "video_cms_%d"
)
// ArcCMSCacheKey .
func (d *Dao) ArcCMSCacheKey(aid int64) string {
return fmt.Sprintf(_mcArcCMSKey, aid)
}
// VideoCMSCacheKey .
func (d *Dao) VideoCMSCacheKey(cid int64) string {
return fmt.Sprintf(_mcVideoCMSKey, cid)
}
// ArcsMetaCache pick archive cms meta cache
func (d *Dao) ArcsMetaCache(c context.Context, ids []int64) (cached map[int64]*model.ArcCMS, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.ArcCMS, len(ids))
idmap, allKeys := keysTreat(ids, d.ArcCMSCacheKey)
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取Archive信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
err = nil
return
}
for key, item := range replys {
art := &model.ArcCMS{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取Archive信息缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = missedTreat(idmap, len(cached))
return
}
// VideosMetaCache pick video cms meta cache
func (d *Dao) VideosMetaCache(c context.Context, ids []int64) (cached map[int64]*model.VideoCMS, missed []int64, err error) {
if len(ids) == 0 {
return
}
cached = make(map[int64]*model.VideoCMS, len(ids))
idmap, allKeys := keysTreat(ids, d.VideoCMSCacheKey)
conn := d.mc.Get(c)
defer conn.Close()
replys, err := conn.GetMulti(allKeys)
if err != nil {
PromError("mc:获取Video信息缓存")
log.Error("conn.Gets(%v) error(%v)", allKeys, err)
err = nil
return
}
for key, item := range replys {
art := &model.VideoCMS{}
if err = conn.Scan(item, art); err != nil {
PromError("mc:获取Video信息缓存缓存json解析")
log.Error("item.Scan(%s) error(%v)", item.Value, err)
err = nil
continue
}
if art.Deleted == _arcCMSDeleted { // if it's deleted, we ignore it
log.Info("ArcCMS deleted, %v, %v", item, art)
continue
}
cached[idmap[key]] = art
delete(idmap, key)
}
missed = missedTreat(idmap, len(cached))
return
}

View File

@@ -0,0 +1,69 @@
package cms
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsArcCMSCacheKey(t *testing.T) {
var (
aid = int64(0)
)
convey.Convey("ArcCMSCacheKey", t, func(c convey.C) {
p1 := d.ArcCMSCacheKey(aid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsVideoCMSCacheKey(t *testing.T) {
var (
cid = int64(0)
)
convey.Convey("VideoCMSCacheKey", t, func(c convey.C) {
p1 := d.VideoCMSCacheKey(cid)
c.Convey("Then p1 should not be nil.", func(c convey.C) {
c.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestCmsArcsMetaCache(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("ArcsMetaCache", t, func(c convey.C) {
sids, errPick := pickIDs(d.db, _pickAids)
if errPick != nil || len(sids) == 0 {
fmt.Println("Empty sids ", errPick)
return
}
cached, missed, err := d.ArcsMetaCache(ctx, sids)
c.Convey("Then err should be nil.cached,missed should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(len(missed)+len(cached), convey.ShouldEqual, len(sids))
})
})
}
func TestCmsVideosMetaCache(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("VideosMetaCache", t, func(c convey.C) {
sids, errPick := pickIDs(d.db, _pickCids)
if errPick != nil || len(sids) == 0 {
fmt.Println("Empty sids ", errPick)
return
}
cached, missed, err := d.VideosMetaCache(ctx, sids)
c.Convey("Then err should be nil.cached,missed should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(len(missed)+len(cached), convey.ShouldEqual, len(sids))
})
})
}

View File

@@ -0,0 +1,148 @@
package cms
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
)
// LoadArcsMediaMap loads a batch of arc meta
func (d *Dao) LoadArcsMediaMap(ctx context.Context, aids []int64) (resMetas map[int64]*model.ArcCMS, err error) {
var (
cachedMetas map[int64]*model.ArcCMS // cache hit seasons
missedMetas map[int64]*model.ArcCMS // cache miss seasons, pick from DB
missed []int64 // cache miss seasons
addCache = true // whether we need to fill DB data in MC
)
resMetas = make(map[int64]*model.ArcCMS) // merge info from MC and from DB
// pick up the information for these season ids
if cachedMetas, missed, err = d.ArcsMetaCache(ctx, aids); err != nil {
log.Error("LoadArcsMedia ArcsMetaCache Aids:%v, Error:%v", aids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.ArcMetas(ctx, missed); err != nil {
log.Error("LoadArcsMedia ArcMetas Sids:%v, Error:%v", missed, err)
return
}
}
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
// async Reset the DB data in MC for next time
log.Info("Set Sids [%d], MissedMetas [%d] Data in MC", len(aids), len(missedMetas))
if addCache && len(missedMetas) > 0 {
for _, art := range missedMetas {
d.AddArcMetaCache(art)
}
}
return
}
// LoadVideosMeta picks the videos meta info
func (d *Dao) LoadVideosMeta(ctx context.Context, cids []int64) (resMetas map[int64]*model.VideoCMS, err error) {
var (
cachedMetas map[int64]*model.VideoCMS // cache hit seasons
missedMetas map[int64]*model.VideoCMS // cache miss seasons, pick from DB
missed []int64 // cache miss seasons
addCache = true // whether we need to fill DB data in MC
)
resMetas = make(map[int64]*model.VideoCMS) // merge info from MC and from DB
// pick up the information for these season ids
if cachedMetas, missed, err = d.VideosMetaCache(ctx, cids); err != nil {
log.Error("LoadVideosMeta VideosMetaCache Aids:%v, Error:%v", cids, err)
err = nil
addCache = false // mc error, we don't add
}
if len(missed) > 0 {
if missedMetas, err = d.VideoMetas(ctx, missed); err != nil {
log.Error("LoadVideosMeta VideoMetas Sids:%v, Error:%v", missed, err)
return
}
}
// merge info from DB and the info from MC
for sid, v := range cachedMetas {
resMetas[sid] = v
}
for sid, v := range missedMetas {
resMetas[sid] = v
}
// async Reset the DB data in MC for next time
log.Info("Set Sids [%d], MissedMetas [%d] Data in MC", len(cids), len(missedMetas))
if addCache && len(missedMetas) > 0 {
for _, art := range missedMetas {
d.AddVideoMetaCache(art)
}
}
return
}
// LoadArcsMedia loads the arc meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadArcsMedia(ctx context.Context, aids []int64) (arcs []*model.ArcCMS, err error) {
var (
resMetas map[int64]*model.ArcCMS // merge info from MC and from DB
)
if resMetas, err = d.LoadArcsMediaMap(ctx, aids); err != nil {
log.Error("LoadArcsMedia LoadArcsMediaMap Aids: %v, Err: %v", aids, err)
return
}
// re-arrange the info, according to the order got from Redis
for _, v := range aids {
if arcCMS, ok := resMetas[v]; !ok {
log.Error("PickDBeiPage LoadArcsMedia Miss Info for Sid: %d", v)
continue
} else {
arcs = append(arcs, arcCMS)
}
}
return
}
// LoadArcMeta loads the arc meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadArcMeta(ctx context.Context, aid int64) (arcMeta *model.ArcCMS, err error) {
if arcMeta, err = d.ArcMetaCache(ctx, aid); err != nil { // mc error
log.Error("LoadArcMedia Get Aid [%d] from CMS Error (%v)", aid, err)
return
}
if arcMeta != nil { // mc found
return
}
if arcMeta, err = d.ArcMetaDB(ctx, aid); err != nil { // db error
log.Error("LoadArcMedia ArcMetaDB Aid ERROR (%d) (%v)", aid, err)
return
}
if arcMeta == nil { // db not found
err = ecode.NothingFound
return
}
d.AddArcMetaCache(arcMeta) // db found, re-fill the cache
return
}
// LoadVideoMeta loads the video meta cms data from cache, for missed ones, pick them from the DB
func (d *Dao) LoadVideoMeta(ctx context.Context, cid int64) (videoMeta *model.VideoCMS, err error) {
if videoMeta, err = d.VideoMetaCache(ctx, cid); err != nil { // mc error
log.Error("LoadVideoMeta Get Cid [%d] from CMS Error (%v)", cid, err)
return
}
if videoMeta != nil { // mc found
return
}
if videoMeta, err = d.VideoMetaDB(ctx, cid); err != nil { // db error
log.Error("LoadArcMedia ArcMetaDB Aid ERROR (%d) (%v)", cid, err)
return
}
if videoMeta == nil { // db not found
err = ecode.NothingFound
return
}
d.AddVideoMetaCache(videoMeta) // db found, re-fill the cache
return
}

View File

@@ -0,0 +1,97 @@
package cms
import (
"context"
"fmt"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsLoadArcsMediaMap(t *testing.T) {
var ctx = context.Background()
convey.Convey("LoadArcsMediaMap", t, func(c convey.C) {
aids, errPick := pickIDs(d.db, _pickAids)
if errPick != nil || len(aids) == 0 {
fmt.Println("Empty aids ", errPick)
return
}
resMetas, err := d.LoadArcsMediaMap(ctx, aids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(resMetas, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadVideosMeta(t *testing.T) {
var (
ctx = context.Background()
cids = []int64{}
)
convey.Convey("LoadVideosMeta", t, func(c convey.C) {
resMetas, err := d.LoadVideosMeta(ctx, cids)
c.Convey("Then err should be nil.resMetas should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(resMetas, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadArcsMedia(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("LoadArcsMedia", t, func(c convey.C) {
aids, errPick := pickIDs(d.db, _pickAids)
if errPick != nil || len(aids) == 0 {
fmt.Println("Empty aids ", errPick)
return
}
arcs, err := d.LoadArcsMedia(ctx, aids)
c.Convey("Then err should be nil.arcs should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(arcs, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadArcMeta(t *testing.T) {
var (
ctx = context.Background()
aid = int64(0)
)
convey.Convey("LoadArcMeta", t, func(c convey.C) {
aids, errPick := pickIDs(d.db, _pickAids)
if errPick != nil || len(aids) == 0 {
fmt.Println("Empty aids ", errPick)
return
}
aid = aids[0]
arcMeta, err := d.LoadArcMeta(ctx, aid)
c.Convey("Then err should be nil.arcMeta should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(arcMeta, convey.ShouldNotBeNil)
})
})
}
func TestCmsLoadVideoMeta(t *testing.T) {
var (
ctx = context.Background()
cid = int64(0)
)
convey.Convey("LoadVideoMeta", t, func(c convey.C) {
aids, errPick := pickIDs(d.db, _pickCids)
if errPick != nil || len(aids) == 0 {
fmt.Println("Empty aids ", errPick)
return
}
cid = aids[0]
videoMeta, err := d.LoadVideoMeta(ctx, cid)
c.Convey("Then err should be nil.videoMeta should not be nil.", func(c convey.C) {
c.So(err, convey.ShouldBeNil)
c.So(videoMeta, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,99 @@
package cms
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
// ArcMetaCache get arc cms cache.
func (d *Dao) ArcMetaCache(c context.Context, aid int64) (s *model.ArcCMS, err error) {
var (
key = d.ArcCMSCacheKey(aid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
missedCount.Add("tv-meta", 1)
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &s); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
cachedCount.Add("tv-meta", 1)
return
}
// SetArcMetaCache save model.ArcCMS to memcache
func (d *Dao) SetArcMetaCache(c context.Context, s *model.ArcCMS) (err error) {
var (
key = d.ArcCMSCacheKey(s.AID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: s, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}
// AddArcMetaCache add view relates
func (d *Dao) AddArcMetaCache(arc *model.ArcCMS) {
d.addCache(func() {
d.SetArcMetaCache(context.TODO(), arc)
})
}
// VideoMetaCache get video cms cache.
func (d *Dao) VideoMetaCache(c context.Context, cid int64) (s *model.VideoCMS, err error) {
var (
key = d.VideoCMSCacheKey(cid)
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
missedCount.Add("tv-meta", 1)
} else {
log.Error("conn.Get(%s) error(%v)", key, err)
}
return
}
if err = conn.Scan(item, &s); err != nil {
log.Error("conn.Get(%s) error(%v)", key, err)
}
cachedCount.Add("tv-meta", 1)
return
}
// SetVideoMetaCache save model.VideoCMS to memcache
func (d *Dao) SetVideoMetaCache(c context.Context, s *model.VideoCMS) (err error) {
var (
key = d.VideoCMSCacheKey(s.CID)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Set(&memcache.Item{Key: key, Object: s, Flags: memcache.FlagJSON, Expiration: d.expireCMS}); err != nil {
log.Error("conn.Set error(%v)", err)
return
}
return
}
// AddVideoMetaCache add view relates
func (d *Dao) AddVideoMetaCache(video *model.VideoCMS) {
d.addCache(func() {
d.SetVideoMetaCache(context.TODO(), video)
})
}

View File

@@ -0,0 +1,105 @@
package cms
import (
"context"
"fmt"
"testing"
"go-common/app/interface/main/tv/model"
"github.com/smartystreets/goconvey/convey"
)
func TestCmsArcMetaCache(t *testing.T) {
var (
c = context.Background()
aid = int64(0)
)
convey.Convey("ArcMetaCache", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.s should not be nil.", func(ctx convey.C) {
sids, err := pickIDs(d.db, _pickAids)
if err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
aid = sids[0]
d.LoadArcMeta(c, aid)
s, err := d.ArcMetaCache(c, aid)
ctx.So(err, convey.ShouldBeNil)
ctx.So(s, convey.ShouldNotBeNil)
})
ctx.Convey("mc not found Error", func(ctx convey.C) {
_, err := d.ArcMetaCache(c, 0)
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestCmsSetArcMetaCache(t *testing.T) {
var (
c = context.Background()
s = &model.ArcCMS{}
)
convey.Convey("SetArcMetaCache", t, func(ctx convey.C) {
err := d.SetArcMetaCache(c, s)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestCmsAddArcMetaCache(t *testing.T) {
var (
arc = &model.ArcCMS{}
)
convey.Convey("AddArcMetaCache", t, func(ctx convey.C) {
d.AddArcMetaCache(arc)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}
func TestCmsVideoMetaCache(t *testing.T) {
var (
c = context.Background()
cid = int64(0)
)
convey.Convey("VideoMetaCache", t, func(ctx convey.C) {
sids, err := pickIDs(d.db, _pickCids)
if err != nil || len(sids) == 0 {
fmt.Println("Empty Sids ", err)
return
}
cid = sids[0]
d.LoadVideoMeta(c, cid)
s, err := d.VideoMetaCache(c, cid)
ctx.Convey("Then err should be nil.s should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(s, convey.ShouldNotBeNil)
})
})
}
func TestCmsSetVideoMetaCache(t *testing.T) {
var (
c = context.Background()
s = &model.VideoCMS{}
)
convey.Convey("SetVideoMetaCache", t, func(ctx convey.C) {
err := d.SetVideoMetaCache(c, s)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestCmsAddVideoMetaCache(t *testing.T) {
var (
video = &model.VideoCMS{}
)
convey.Convey("AddVideoMetaCache", t, func(ctx convey.C) {
d.AddVideoMetaCache(video)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
}

View File

@@ -0,0 +1,52 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["dao_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf: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/interface/main/tv/dao/favorite",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/service/main/favorite/api/gorpc:go_default_library",
"//app/service/main/favorite/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,103 @@
package favorite
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/model"
favrpc "go-common/app/service/main/favorite/api/gorpc"
favmdl "go-common/app/service/main/favorite/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// Dao is account dao.
type Dao struct {
favRPC *favrpc.Service // rpc
conf *conf.Config
client *bm.Client
}
// New account dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
favRPC: favrpc.New2(c.FavoriteRPC),
conf: c,
client: bm.NewClient(c.HTTPClient),
}
return
}
const (
_FavBusiness = 2
_DefaultFav = 0
)
// FavoriteV3 picks favorite info from rpc
func (d *Dao) FavoriteV3(ctx context.Context, mid int64, pn int) (res *favmdl.Favorites, err error) {
var ip = metadata.String(ctx, metadata.RemoteIP)
arg := &favmdl.ArgFavs{
Type: _FavBusiness,
Mid: mid,
Fid: _DefaultFav,
Tv: 1,
Pn: pn,
Ps: d.conf.Cfg.FavPs,
RealIP: ip,
}
if res, err = d.favRPC.Favorites(ctx, arg); err != nil {
err = errors.Wrapf(err, "%v", arg)
}
return
}
// favAct adds/deletes favorite into/from the default folder
func (d *Dao) favAct(ctx context.Context, mid int64, aid int64, host string) (err error) {
var (
ip = metadata.String(ctx, metadata.RemoteIP)
params = url.Values{}
res = model.RespFavAct{}
)
params.Set("mid", fmt.Sprintf("%d", mid))
params.Set("aid", fmt.Sprintf("%d", aid))
if err = d.client.Post(ctx, host, ip, params, &res); err != nil {
log.Error("FavAdd Aid %d, Mid %d, Err %v", aid, mid, err)
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), fmt.Sprintf("Fav AID %d, Mid %d, API Error %s", aid, mid, res.Message))
log.Error("FavAdd ERROR:%v, URL: %s", err, host+"?"+params.Encode())
return
}
return
}
// FavAdd def.
func (d *Dao) FavAdd(ctx context.Context, mid, aid int64) (err error) {
host := d.conf.Host.FavAdd
return d.favAct(ctx, mid, aid, host)
}
// FavDel deletes favorite from the default folder
func (d *Dao) FavDel(ctx context.Context, mid int64, aid int64) (err error) {
host := d.conf.Host.FavDel
return d.favAct(ctx, mid, aid, host)
}
// InDefault returns whether the aid is in Default of Mid
func (d *Dao) InDefault(ctx context.Context, mid int64, aid int64) (bool, error) {
var ip = metadata.String(ctx, metadata.RemoteIP)
arg := &favmdl.ArgInDefaultFolder{
Type: _FavBusiness,
Mid: mid,
RealIP: ip,
Oid: aid,
}
return d.favRPC.InDefault(ctx, arg)
}

View File

@@ -0,0 +1,76 @@
package favorite
import (
"context"
"encoding/json"
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/tv/conf"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
var d *Dao
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
flag.Set("conf", dir)
conf.Init()
d = New(conf.Conf)
time.Sleep(5 * time.Second)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}
func TestDao_FavoriteV3(t *testing.T) {
Convey("TestDao_FavoriteV3", t, func() {
res, err := d.FavoriteV3(context.Background(), 88894921, 1)
So(err, ShouldBeNil)
data, _ := json.Marshal(res)
Println(string(data))
})
}
func TestDao_FavAdd(t *testing.T) {
Convey("TestDao_FavAdd", t, func() {
err := d.FavAdd(context.Background(), 88894921, 10098813)
So(err, ShouldBeNil)
err = d.FavAdd(context.Background(), 88894921, 28417042)
So(err, ShouldBeNil)
})
}
func TestDao_FavDel(t *testing.T) {
Convey("TestDao_FavDel", t, func() {
err := d.FavDel(context.Background(), 88894921, 28417042)
So(err, ShouldBeNil)
})
}
func TestDao_InDefault(t *testing.T) {
Convey("TestDao_InDefault", t, WithDao(func(d *Dao) {
var mid = int64(27515418)
res, err := d.FavoriteV3(context.Background(), mid, 1)
So(err, ShouldBeNil)
if res == nil || len(res.List) == 0 {
fmt.Println("empty Fav")
return
}
exist, err2 := d.InDefault(context.Background(), mid, res.List[0].Oid)
So(err2, ShouldBeNil)
So(exist, ShouldBeTrue)
exist, err2 = d.InDefault(context.Background(), mid, 888888888888)
So(err2, ShouldBeNil)
So(exist, ShouldBeFalse)
}))
}

View File

@@ -0,0 +1,71 @@
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",
"hotword.go",
"label.go",
"splash.go",
"ugc_playurl.go",
"upgrade.go",
],
importpath = "go-common/app/interface/main/tv/dao/goblin",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/goblin:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"hotword_test.go",
"label_test.go",
"splash_test.go",
"ugc_playurl_test.go",
"upgrade_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)

View File

@@ -0,0 +1,26 @@
package goblin
import (
"go-common/app/interface/main/tv/conf"
"go-common/library/cache/memcache"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
)
// Dao .
type Dao struct {
conf *conf.Config
client *bm.Client
db *sql.DB
mc *memcache.Pool
}
// New .
func New(c *conf.Config) *Dao {
return &Dao{
conf: c,
client: bm.NewClient(c.PlayurlClient),
db: sql.NewMySQL(c.Mysql),
mc: memcache.NewPool(c.Memcache.Config),
}
}

View File

@@ -0,0 +1,49 @@
package goblin
import (
"flag"
"os"
"strings"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/h2non/gock.v1"
)
var d *Dao
func init() {
//dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
//flag.Set("conf", dir)
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.web-svr.tv-interface")
flag.Set("conf_token", "07c1826c1f39df02a1411cdd6f455879")
flag.Set("tree_id", "15326")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() {})
f(d)
}
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
d.client.SetTransport(gock.DefaultTransport)
return r
}

View File

@@ -0,0 +1,23 @@
package goblin
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
)
const _hotwordKey = "_tv_search"
// Hotword get hotword cache.
func (d *Dao) Hotword(c context.Context) (s []*model.Hotword, err error) {
var (
conn = d.mc.Get(c)
item *memcache.Item
)
defer conn.Close()
if item, err = conn.Get(_hotwordKey); err != nil {
return
}
err = conn.Scan(item, &s)
return
}

View File

@@ -0,0 +1,33 @@
package goblin
import (
"testing"
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/cache/memcache"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_Hotword(t *testing.T) {
Convey("Hotword Test", t, WithDao(func(d *Dao) {
ctx := context.TODO()
conn := d.mc.Get(ctx)
s := []*model.Hotword{
{
Keyword: "Test1",
},
{
Keyword: "Test2",
},
}
defer conn.Close()
err := conn.Set(&memcache.Item{Key: _hotwordKey, Object: s, Flags: memcache.FlagJSON, Expiration: 1200})
So(err, ShouldBeNil)
hotwordList, err := d.Hotword(ctx)
So(err, ShouldBeNil)
So(len(hotwordList), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,30 @@
package goblin
import (
"context"
gbmdl "go-common/app/interface/main/tv/model/goblin"
)
const (
_labelSQL = "SELECT id,name,param, param_name,value FROM tv_label WHERE category = ? AND cat_type = ? AND valid = 1 AND deleted = 0" +
" ORDER BY position,id ASC "
)
// Label picks one category's label
func (d *Dao) Label(c context.Context, category, catType int) (res []*gbmdl.Label, err error) {
rows, err := d.db.Query(c, _labelSQL, category, catType)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
li := &gbmdl.Label{}
if err = rows.Scan(&li.ID, &li.Name, &li.Param, &li.ParamName, &li.Value); err != nil {
return
}
res = append(res, li)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,31 @@
package goblin
import (
"context"
"testing"
"go-common/library/database/sql"
"github.com/smartystreets/goconvey/convey"
)
func TestGoblinLabel(t *testing.T) {
var (
c = context.Background()
category = int(1)
catType = int(1)
)
convey.Convey("Label", t, func(ctx convey.C) {
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
res, err := d.Label(c, category, catType)
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
ctx.Convey("db closed", func(ctx convey.C) {
d.db.Close()
_, err := d.Label(c, category, catType)
ctx.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}

View File

@@ -0,0 +1,28 @@
package goblin
import (
"context"
"go-common/app/interface/main/tv/model"
)
const (
_getChl = "SELECT id, title, `desc`, splash FROM tv_channel WHERE deleted = 0"
)
// ChlInfo .
func (d *Dao) ChlInfo(c context.Context) (chls []*model.Channel, err error) {
rows, err := d.db.Query(c, _getChl)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
li := &model.Channel{}
if err = rows.Scan(&li.ID, &li.Title, &li.Desc, &li.Splash); err != nil {
return
}
chls = append(chls, li)
}
return
}

View File

@@ -0,0 +1,29 @@
package goblin
import (
"context"
"testing"
"go-common/library/database/sql"
"github.com/smartystreets/goconvey/convey"
)
func TestGoblinChlInfo(t *testing.T) {
var (
ctx = context.Background()
)
convey.Convey("ChlInfo", t, func(c convey.C) {
c.Convey("Then err should be nil.chls should not be nil.", func(c convey.C) {
chls, err := d.ChlInfo(ctx)
c.So(err, convey.ShouldBeNil)
c.So(chls, convey.ShouldNotBeNil)
})
c.Convey("db closed", func(c convey.C) {
d.db.Close()
_, err := d.ChlInfo(ctx)
c.So(err, convey.ShouldNotBeNil)
d.db = sql.NewMySQL(d.conf.Mysql)
})
})
}

View File

@@ -0,0 +1,64 @@
package goblin
import (
"context"
"encoding/json"
xhttp "net/http"
"net/url"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
const (
_httpHeaderRemoteIP = "x-backend-bili-real-ip"
)
// UgcPlayurl is use for get ugc play url
func (d *Dao) UgcPlayurl(ctx context.Context, p *model.PlayURLReq) (res map[string]interface{}, resp *model.PlayURLResp, err error) {
var (
params = url.Values{}
url = d.conf.Host.UgcPlayURL
bs []byte
req *xhttp.Request
ip = metadata.String(ctx, metadata.RemoteIP)
)
res = make(map[string]interface{})
params.Set("platform", p.Platform)
params.Set("device", p.Device)
params.Set("expire", p.Expire)
params.Set("build", p.Build)
params.Set("mid", p.Mid)
params.Set("qn", p.Qn)
params.Set("npcybs", p.Npcybs)
params.Set("buvid", p.Buvid)
params.Set("otype", "json")
params.Set("trackPath", p.TrackPath)
params.Set("cid", p.Cid)
params.Set("access_key", p.AccessKey)
params.Set("platform", "tvproj")
if req, err = d.client.NewRequest(xhttp.MethodGet, url, ip, params); err != nil {
return
}
if ip != "" { // add ip into header
req.Header.Set(_httpHeaderRemoteIP, ip)
}
log.Info("ugcPlayURL Cid %d, IP %s", p.Cid, ip)
if bs, err = d.client.Raw(ctx, req); err != nil {
log.Error("ugcPl URL %s, Cid %d, Client Raw Err %v", url, p.Cid, err)
return
}
if err = json.Unmarshal(bs, &resp); err != nil { // json unmarshal to struct, to detect error
log.Error("ugcPl URL %s, Cid %d, Json Unmarshal %s, Err %v", url, p.Cid, string(bs), err)
return
}
if resp.Code != ecode.OK.Code() {
log.Error("ugcPl URL %s, Cid %d, Resp Code %d, Msg %s", url, p.Cid, resp.Code, resp.Message)
err = ecode.TvVideoNotFound
return
}
err = json.Unmarshal(bs, &res)
return
}

View File

@@ -0,0 +1,51 @@
package goblin
import (
"context"
"fmt"
"testing"
"go-common/app/interface/main/tv/model"
"github.com/smartystreets/goconvey/convey"
gock "gopkg.in/h2non/gock.v1"
)
func TestGoblinUgcPlayurl(t *testing.T) {
var (
ctx = context.Background()
p = &model.PlayURLReq{
Cid: fmt.Sprintf("%d", 10131156),
}
)
convey.Convey("UgcPlayurl", t, func(c convey.C) {
defer gock.OffAll()
c.Convey("Normal Situation, Then err should be nil.res,resp should not be nil.", func(cx convey.C) {
httpMock("GET", d.conf.Host.UgcPlayURL).Reply(200).JSON(`{
"result": "succ",
"message": "succ",
"code": 0
}`)
res, resp, err := d.UgcPlayurl(ctx, p)
fmt.Println(resp)
cx.So(err, convey.ShouldBeNil)
cx.So(resp, convey.ShouldNotBeNil)
cx.So(res, convey.ShouldNotBeNil)
})
c.Convey("Request Error", func(cx convey.C) {
httpMock("GET", d.conf.Host.UgcPlayURL).Reply(404).JSON(``)
_, _, err := d.UgcPlayurl(ctx, p)
cx.So(err, convey.ShouldNotBeNil)
})
c.Convey("Code Error", func(cx convey.C) {
httpMock("GET", d.conf.Host.UgcPlayURL).Reply(200).JSON(`{"code":-400}`)
_, _, err := d.UgcPlayurl(ctx, p)
cx.So(err, convey.ShouldNotBeNil)
})
c.Convey("Json Error", func(cx convey.C) {
httpMock("GET", d.conf.Host.UgcPlayURL).Reply(200).JSON(`{"code":-400:}`)
_, _, err := d.UgcPlayurl(ctx, p)
cx.So(err, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,42 @@
package goblin
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"github.com/pkg/errors"
)
// VerUpdate gets upgrade info.
func (d *Dao) VerUpdate(c context.Context, ver *model.VerUpdate) (result *model.HTTPData, errCode ecode.Codes, err error) {
var (
appURL = d.conf.Host.ReqURL
res struct {
Code int `json:"code"`
Data *model.HTTPData `json:"data"`
Message string `json:"message"`
}
)
params := url.Values{}
params.Set("mobi_app", ver.MobiApp)
params.Set("build", fmt.Sprintf("%d", ver.Build))
params.Set("channel", ver.Channel)
params.Set("seed", fmt.Sprintf("%d", ver.Seed))
params.Set("sdkint", fmt.Sprintf("%d", ver.Sdkint))
params.Set("model", ver.Model)
params.Set("old_id", ver.OldID)
if err = d.client.Get(c, appURL, "", params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), appURL+"?"+params.Encode())
errCode = ecode.Int(res.Code)
return
}
result = res.Data
return
}

View File

@@ -0,0 +1,39 @@
package goblin
import (
"context"
"go-common/app/interface/main/tv/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestGoblinVerUpdate(t *testing.T) {
var (
ctx = context.Background()
ver = &model.VerUpdate{}
)
convey.Convey("VerUpdate", t, func(c convey.C) {
c.Convey("Then err should be nil.result,errCode should not be nil.", func(c convey.C) {
result, errCode, err := d.VerUpdate(ctx, ver)
httpMock("GET", d.conf.Host.ReqURL).Reply(200).JSON(`{
"Data": {"ver":123},
"message": "succ",
"code": 0
}`)
c.So(err, convey.ShouldBeNil)
c.So(errCode, convey.ShouldBeNil)
c.So(result, convey.ShouldNotBeNil)
})
c.Convey("http error", func(c convey.C) {
_, _, err := d.VerUpdate(ctx, ver)
httpMock("GET", d.conf.Host.ReqURL).Reply(404).JSON(``)
c.So(err, convey.ShouldNotBeNil)
})
c.Convey("code error", func(c convey.C) {
_, _, err := d.VerUpdate(ctx, ver)
httpMock("GET", d.conf.Host.ReqURL).Reply(200).JSON(`{"code":400}`)
c.So(err, convey.ShouldNotBeNil)
})
})
}

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 = [
"cursor_test.go",
"dao_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model/history:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"cursor.go",
"dao.go",
],
importpath = "go-common/app/interface/main/tv/dao/history",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/interface/main/history/rpc/client:go_default_library",
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model/history:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/stat/prom:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

Some files were not shown because too many files have changed in this diff Show More