Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

35
app/interface/BUILD Normal file
View File

@@ -0,0 +1,35 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/bbq:all-srcs",
"//app/interface/live/app-blink:all-srcs",
"//app/interface/live/app-interface:all-srcs",
"//app/interface/live/app-room:all-srcs",
"//app/interface/live/app-ucenter:all-srcs",
"//app/interface/live/live-demo:all-srcs",
"//app/interface/live/lottery-interface:all-srcs",
"//app/interface/live/open-interface/api/http/v1:all-srcs",
"//app/interface/live/open-interface/cmd:all-srcs",
"//app/interface/live/open-interface/internal/dao:all-srcs",
"//app/interface/live/open-interface/internal/model:all-srcs",
"//app/interface/live/open-interface/internal/server/http:all-srcs",
"//app/interface/live/open-interface/internal/service:all-srcs",
"//app/interface/live/push-live:all-srcs",
"//app/interface/live/upload:all-srcs",
"//app/interface/live/web-room:all-srcs",
"//app/interface/live/web-ucenter:all-srcs",
"//app/interface/main:all-srcs",
"//app/interface/openplatform:all-srcs",
"//app/interface/video:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

5
app/interface/OWNERS Normal file
View File

@@ -0,0 +1,5 @@
# See the OWNERS docs at https://go.k8s.io/owners
labels:
- interface
- new-project

3
app/interface/README.md Normal file
View File

@@ -0,0 +1,3 @@
# go-common/app/interface
Gateway&Interface对外网关服务

26
app/interface/bbq/BUILD Normal file
View File

@@ -0,0 +1,26 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/bbq/app-bbq:all-srcs",
"//app/interface/bbq/bullet:all-srcs",
"//app/interface/bbq/comment:all-srcs",
"//app/interface/bbq/common:all-srcs",
"//app/interface/bbq/feed:all-srcs",
"//app/interface/bbq/notice:all-srcs",
"//app/interface/bbq/search:all-srcs",
"//app/interface/bbq/topic:all-srcs",
"//app/interface/bbq/user:all-srcs",
"//app/interface/bbq/video:all-srcs",
"//app/interface/bbq/wechat:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,2 @@
# Owner
daiwei

7
app/interface/bbq/OWNERS Normal file
View File

@@ -0,0 +1,7 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- daiwei
labels:
- bbq
- interface

View File

@@ -0,0 +1,22 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/bbq/app-bbq/api/http/v1:all-srcs",
"//app/interface/bbq/app-bbq/cmd:all-srcs",
"//app/interface/bbq/app-bbq/conf:all-srcs",
"//app/interface/bbq/app-bbq/dao:all-srcs",
"//app/interface/bbq/app-bbq/model:all-srcs",
"//app/interface/bbq/app-bbq/server/http:all-srcs",
"//app/interface/bbq/app-bbq/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,229 @@
# v1.0.64
去除banner和热词的保护
# v1.0.63
创作中心黑名单文案
# v1.0.62
话题联想词
# v1.0.61
发现页请求banner和hot words
# v1.0.60
app setting配置文件
# v1.0.59
关注页为空访问upsrecservice接口
# v1.0.58
图片上传接口增加type区分
# v1.0.57
banner中url->scheme
# v1.0.56
推荐接口兜底逻辑修改
fix
# v1.0.55
app动效资源增加版本区分
增加话题详情页中视频的hot_type现在只有置顶
# v1.0.54
fake数据discovery
# v1.0.53
允许-1状态可取消点赞
# v1.0.52
修复发现页没有话题的bug
# v1.0.51
1. 修复个人空间视频的bug当原先视频空时无法返回prepare_list
2. 增加审核中状态
# v1.0.50
搜索切换成cursor方式同时兼容老方式
# v1.0.49
个人空间视频列表接口增加转码中的prepare_list
# v1.0.48
用户封禁功能过期时间fix
# v1.0.47
增加发现页的几个接口
VideoResponse中增加extension
# v1.0.46
关注页的推荐逻辑
# v1.0.45
1. 搜索埋点接口fix
# v1.0.44
1. 视频删除接口
2. 视频不感兴趣接口
# v1.0.43
playlist接口过滤已下线视频
# v1.0.42
创作中心白名单接口
# v1.0.41
不存在video_statistics的时候插入一条
# v1.0.40
用户点赞列表play字段中添加SVID
# v1.0.39
评论举报理由修改
app全局设置接口做客户端系统兼容
# v1.0.38
修复关注feed流的has_more问题
允许对下架视频取消点赞
# v1.0.37
dao层删除es client
# v1.0.36
整理了sv state的使用
整理了dao/sv的一些使用
对一些接口做视频是否可见的校验(评论、点赞、分享等)
# v1.0.35
单个视频放出状态为 -2~5
# v1.0.34
评论举报功能
# v1.0.33
修复评论plat参数传错的问题
# v1.0.32
修复对视频的评论没有rootid的问题
# v1.0.31
增加封禁用户功能
# v1.0.30
评论敏感词接入
# v1.0.29
移除tag相关代码
修复用户列表的bug
# v1.0.28
去除无用的代码邀请码、taglist等
修改一些函数名
# v1.0.27
修复关注页、个人空间下,视频状态的展示
# v1.0.26
增加评论的两个开关
# v1.0.25
把判断视频是否存在的地方换成调用video service服务点赞、评论
# v1.0.24
用户头像filename修改
修复重复点赞有概率多计数的bug
# v1.0.23
修改评论了评论-》回复了评论
修改举报配置
# v1.0.22
1. 为评论接口添加手机绑定校验
# v1.0.21
1. 推送注册接口修复
# v1.0.20
1. App资源字段修改
# v1.0.19
使用user-service移除对user相关的db的直接操作
# v1.0.18
1. App资源下载
2. App版本接口
# v1.0.17
1. 搜索接口埋点
# v1.0.16
90|女 -》 [90, 女]
详情页访问用户视频列表,进行去重处理
# v1.0.15
修复热评丢失是否已点赞的逻辑
# v1.0.14
1. 拉黑埋点
# v1.0.13
1. 推荐接口数据降级
# v1.0.12
1. 推送回调
# v1.0.11
1. app版本更新检查
2. push注册接口fix
# v1.0.10
1. 推送注册&回调接口
2. 举报接口字段fix
# v1.0.9
1. 分享接口fix
# v1.0.8
1. config client切换到paladin
2. 增加App全局配置
3. Android版本升级检查
# v1.0.7
1. 举报接口
# v1.0.6
1. 用户登陆
2. fix bug
# v1.0.5
1. 推荐接口增加queryID
2. 播放时长上报接口增加type
# v1.0.4
1. 分享接口fix
2. 接口基础字段名修改
# v1.0.3
1. 推荐接口增加降级方案
# v1.0.2
1. 接口infoc日志增加客户端IP和location
# v1.0.1
1. 接口infoc日志收集
# v1.0.0
1. 上线功能xxx
2. 用户空间
# v1.0.1
1. 推荐列表活动信息拆分
2. 修复用户信息拉来源
3. 评论列表修复
# v1.0.2
1. playurl重构
2. playurl容错
# v1.0.3
1. upload video
2. upload video debug
3. home img init

View File

@@ -0,0 +1,11 @@
# Owner
luxiaowei
daiwei
jiangdongqi
# Author
luxiaowei
# Reviewer
daiwei
jiangdongqi

View File

@@ -0,0 +1,17 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- daiwei
- jiangdongqi
- luxiaowei
labels:
- bbq
- interface
- interface/bbq/app-bbq
- interface/bbq/video
options:
no_parent_owners: true
reviewers:
- daiwei
- jiangdongqi
- luxiaowei

View File

@@ -0,0 +1,12 @@
# open-bbq-interface
# 项目简介
1.
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,51 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"base.go",
"comment.go",
"feed.go",
"invite.go",
"location.go",
"notice.go",
"push.go",
"report.go",
"search.go",
"share.go",
"sv.go",
"topic.go",
"upload.go",
"user.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/api/http/v1",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/model:go_default_library",
"//app/service/bbq/notice-service/api/v1:go_default_library",
"//app/service/bbq/topic/api:go_default_library",
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/video/api/grpc/v1:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/time: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,454 @@
# HTTP API文档
导航
---
* [版本](#版本)
* [说明](#说明)
* [接口说明](#接口说明)
+ [[首页视频列表]](#首页视频列表)
+ [[批量视频地址]](#批量视频地址接口)
版本
---
| 版本 | 时间 | 修订者 | 备注 |
| :--- | :--------- | :----- | :------------- |
| v0.1 | 2018.10.12 | Cheney | 初始化接口列表 |
说明
---
BBQ视频相关接口
域名
---
* **线上:** bbq.bilibili.com
* **自动化文档路径:** http://172.16.38.91/swagger
接口说明
-------
### 首页视频列表
***接口说明***
提供视频列表相关基础信息和播放信息
***请求URL***
http://DOMAIN/bbq/app-bbq/sv/list
***请求方式***
HTTP/GET
***请求参数***
| 参数名 | 必选 | 类型 | 说明 |
| :------- | :--- | :--- | :---------------------- |
| qn | 是 | int | 首选清晰度 |
| pagesize | 是 | int | 返回数据量range:0-20) |
***qn参数枚举***
目前PlayUrl支持UGC、PGC和Mobile H5的业务, 对各个业务的支持情况如下:
***<span style="color:red">Note: 对于分组为同一个的清晰度, PlayUrl服务端只会给出其中一个清晰度作为结果返回.***
##### UGC
| 分组 | 清晰度qn | 类型 | 名称 | 描述 | 访问权限 |
| :--- | :------- | :--- | :--------- | :----------- | :------------------- |
| 1# | 116 | FLV | flv_p60 | 高清 1080P60 | 大会员或此视频的UP主 |
| 1# | 112 | FLV | hdflv2 | 高清 1080P+ | 大会员或此视频的UP主 |
| 2# | 74 | FLV | flv720_p60 | 高清 720P60 | 大会员或此视频的UP主 |
| 3# | 80 | FLV | flv | 高清 1080 | 都可访问 |
| 4# | 64 | FLV | flv720 | 高清 720P | 都可访问 |
| 4# | 48 | MP4 | hdmp4 | 高清 720P | 都可访问 |
| 5# | 32 | FLV | flv480 | 清晰 480P | 都可访问 |
| 6# | 15 | FLV | flv360 | 流畅 360P | 都可访问 |
| 6# | 16 | MP4 | mp4 | 流畅 360P | 都可访问 |
| 7# | 6 | MP4 | mp4 | 极速 240P | 当且仅当type=mp4访问 |
##### PGC
| 分组 | 清晰度qn | 类型 | 名称 | 描述 | 访问权限 |
| :--- | :------- | :--- | :----- | :---------- | :------------------- |
| 1# | 112 | FLV | hdflv2 | 高清 1080P+ | 都可访问 |
| 2# | 80 | FLV | flv | 高清 1080 | 都可访问 |
| 3# | 64 | FLV | flv720 | 高清 720P | 都可访问 |
| 3# | 48 | MP4 | hdmp4 | 高清 720P | 都可访问 |
| 4# | 32 | FLV | flv480 | 清晰 480P | 都可访问 |
| 5# | 15 | FLV | flv360 | 流畅 360P | 都可访问 |
| 5# | 16 | MP4 | mp4 | 流畅 360P | 都可访问 |
| 6# | 6 | MP4 | mp4 | 极速 240P | 当且仅当type=mp4访问 |
##### Mobile H5
| 分组 | 清晰度qn | 类型 | 名称 | 描述 | 访问权限 |
| :--- | :------- | :--- | :----- | :-------- | :------------------- |
| 1# | 15 | FLV | flv360 | 流畅 360P | 都可访问 |
| 1# | 16 | MP4 | mp4 | 流畅 360P | 都可访问 |
| 2# | 6 | MP4 | mp4 | 极速 240P | 当且仅当type=mp4访问 |
*** 返回字段说明 ***
| 字段名 | 字段类型 | 字段说明 |
| :-------: | :----------------------: | :--------------: |
| svid | int | bbq视频id |
| title | string | 视频标题 |
| mid | int | 发布用户uid |
| duration | int | 时长 |
| pubtime | string | 发布时间 |
| ctime | int | 创建时间 |
| avid | int | avid |
| cid | int | cid |
| from | int | 来源渠道 |
| tag | string | 后端首选tag |
| tags | [][VideoTag](#VideoTag) | 全部tag |
| pic | string | 图片预留字段 |
| like | int | 点赞数 |
| reply | int | 评论数 |
| share | int | 分享数 |
| user_info | *UserCard(object) | 发布用户信息 |
| play | *[VideoPlay](#VideoPlay) | 播放信息 |
| is_like | bool | 当前用户是否点赞 |
***返回字段示例***
```javascript
{
"code": 0,
"message": "0",
"ttl": 1,
"data": [
{
"svid": 198,
"title": "体重88kg,深蹲200KG成功了,用了2年时间,记录一下",
"content": "",
"mid": 98628543,
"duration": 0,
"pubtime": "2018-10-12 12:45:51",
"ctime": 1539319551,
"avid": 27406598,
"cid": 47265445,
"from": 0,
"tag": "",
"tags": [],
"pic": "",
"like": 0,
"reply": 0,
"share": 0,
"user_info": {
"mid": 98628543,
"name": "笑圣贤",
"sex": "保密",
"rank": 10000,
"face": "http://i0.hdslb.com/bfs/face/fac0c8dc176ce9f8d3e176d759378233788994d4.jpg",
"sign": "一个练力量举的UP,1981年的大叔,体重88kg,三大项540KG",
"level": 0,
"vip_info": {
"type": 0,
"status": 0,
"due_date": 0
}
},
"play": {
"cid": 47265445,
"expire_time": 1539330912,
"file_info": [
{
"ahead": "Egg=",
"filesize": 2332329,
"timelength": 40128,
"vhead": "AWQAHv/hABpnZAAerNlBcFHlkhAAAAMAEAAAAwMg8WLZYAEABWjr7PI8"
}
],
"fnval": 0,
"fnver": 0,
"quality": 15,
"support_description": [
"高清 720P",
"清晰 480P",
"流畅 360P"
],
"support_formats": [
"flv720",
"flv480",
"flv360"
],
"support_quality": [
64,
32,
15
],
"url": "http://upos-hz-mirrorcos.acgvideo.com/upgcxcode/45/54/47265445/47265445-1-15.flv?um_deadline=1539334512&platform=&rate=98807&oi=0&um_sign=45b60b850c9e577261feb079ced1bb3d&gen=playurl&os=cos&trid=3db463de4aa2476cb56900e87f733323",
"video_codecid": 7,
"video_project": true,
"current_time": 1539327312
},
"is_like": false
},
{
"svid": 1740,
"title": "【朱一龙舔屏】全部都是你(๑> ᴗ<๑)❤【竖屏丨手机福利】",
"content": "手机观看效果最佳!送闺蜜@灰 的居仔舔屏视频,熬夜终于在七夕搞定了,就是想摸鱼剪剪竖屏玩。祝大家七夕情人节快乐!快点进来收获爱情吧!ヾ(๑╹◡╹)ノ\"",
"mid": 761997,
"duration": 0,
"pubtime": "2018-10-12 12:52:06",
"ctime": 1539319926,
"avid": 29574294,
"cid": 51427230,
"from": 0,
"tag": "",
"tags": [
{
"id": 797,
"name": "明星",
"type": 3
},
{
"id": 798,
"name": "娱乐",
"type": 1
},
{
"id": 805,
"name": "朱一龙",
"type": 3
},
{
"id": 1729,
"name": "剪辑",
"type": 3
},
{
"id": 4103,
"name": "七夕",
"type": 3
}
],
"pic": "",
"like": 0,
"reply": 0,
"share": 0,
"user_info": {
"mid": 761997,
"name": "無駄無駄",
"sex": "保密",
"rank": 10000,
"face": "http://i2.hdslb.com/bfs/face/fbe53ad204dd36d268a9e6e887918787956914fd.gif",
"sign": "【渣渣水平,蜗牛手速,感谢赏脸!】微博:@-6plus7- 【B站ID是抢注的舍不得改没错是隐藏的JOJO厨】",
"level": 0,
"vip_info": {
"type": 2,
"status": 1,
"due_date": 1570809600000
}
},
"play": {
"cid": 51427230,
"expire_time": 1539330912,
"file_info": [
{
"ahead": "EZA=",
"filesize": 6204058,
"timelength": 102254,
"vhead": "AWQAHv/hABlnZAAerNlBcFHl4QAAAwABAAADADwPFi2WAQAFaOvs8jw="
}
],
"fnval": 0,
"fnver": 0,
"quality": 15,
"support_description": [
"高清 1080P60",
"高清 720P60",
"高清 1080P",
"高清 720P",
"清晰 480P",
"流畅 360P"
],
"support_formats": [
"flv_p60",
"flv720_p60",
"flv",
"flv720",
"flv480",
"flv360"
],
"support_quality": [
116,
74,
80,
64,
32,
15
],
"url": "http://upos-hz-mirrorcos.acgvideo.com/upgcxcode/30/72/51427230/51427230-1-15.flv?um_deadline=1539334512&platform=&rate=103144&oi=0&um_sign=305264b6bc32d61fd1020a71c24f3544&gen=playurl&os=cos&trid=3db463de4aa2476cb56900e87f733323",
"video_codecid": 7,
"video_project": true,
"current_time": 1539327312
},
"is_like": false
}
]
}
```
### 批量视频地址接口
***接口说明***
提供可替换视频地址服务
***请求URL***
http://DOMAIN/bbq/app-bbq/sv/playlist
***请求方式***
HTTP/GET
***请求参数***
| 参数名 | 必选 | 类型 | 说明 |
| :----- | :--- | :----- | :------------------ |
| qn | 是 | int | 首选清晰度 |
| cids | 是 | string | 请求cid逗号分隔 |
*** 返回字段说明 ***
| 字段名 | 字段类型 | 字段说明 |
| :----: | :------------------------: | :---------------: |
| data | []*[VideoPlay](#VideoPlay) | bvc playurl 结构 |
***返回字段示例***
```javascript
{
"code": 0,
"message": "0",
"ttl": 1,
"data": [
{
"cid": 49159587,
"expire_time": 1539331184,
"file_info": [
{
"ahead": "EZBW5QA=",
"filesize": 12879106,
"timelength": 258118,
"vhead": "AWQAH//hAB1nZAAfrNnA2D3n//AoACfxAAADA+kAAOpgDxgxngEABWjpuyyL"
}
],
"fnval": 0,
"fnver": 0,
"quality": 32,
"support_description": [
"高清 1080P",
"高清 720P",
"清晰 480P",
"流畅 360P"
],
"support_formats": [
"flv",
"flv720",
"flv480",
"flv360"
],
"support_quality": [
80,
64,
32,
15
],
"url": "http://upos-hz-mirrorcos.acgvideo.com/upgcxcode/87/95/49159587/49159587-1-32.flv?um_deadline=1539334784&platform=&rate=84823&oi=0&um_sign=2969b624cf3abf67505e60d78babbc70&gen=playurl&os=cos&trid=60941200ead244fbbcbd6090c5b337d0",
"video_codecid": 7,
"video_project": true,
"current_time": 1539327585
},
{
"cid": 52172330,
"expire_time": 1539331184,
"file_info": [
{
"ahead": "EZA=",
"filesize": 2377519,
"timelength": 15181,
"vhead": "AWQAHv/hABlnZAAerNlA2D3n4QAAAwABAAADADIPFi2WAQAFaOvs8jw="
}
],
"fnval": 0,
"fnver": 0,
"quality": 32,
"support_description": [
"高清 1080P+",
"高清 1080P",
"高清 720P",
"清晰 480P",
"流畅 360P"
],
"support_formats": [
"hdflv2",
"flv",
"flv720",
"flv480",
"flv360"
],
"support_quality": [
112,
80,
64,
32,
15
],
"url": "http://upos-hz-mirrorcos.acgvideo.com/upgcxcode/30/23/52172330/52172330-1-32.flv?um_deadline=1539334784&platform=&rate=266239&oi=0&um_sign=1533acfdd923bffab96f65d63f64ad9c&gen=playurl&os=cos&trid=60941200ead244fbbcbd6090c5b337d0",
"video_codecid": 7,
"video_project": true,
"current_time": 1539327585
}
]
}
```
*附
--
***通用字段***
##### VideoPlay
| 字段名 | 字段类型 | 字段说明 |
| :-----------------: | :----------------------: | :-------------------------------------------------: |
| cid | int64 | cid |
| expire_time | int64 | 过期时间 |
| file_info | []*[FileInfo](#FileInfo) | 分片信息 |
| fnval | int64 | 播放器请求端使用的, 功能标识, 每位(为1)标识一个功能 |
| fnver | int64 | 播放器请求端使用的, 功能版本号 |
| quality | int64 | 清晰度 |
| support_description | []string | 支持清晰度描述 |
| support_formats | []string | 支持格式 |
| support_quality | []int64 | 支持清晰度 |
| url | string | 基础url |
| video_codecid | int64 | 对应视频调度的结果编码值 |
| video_project | bool | 是否可投影 |
| current_time | int64 | 当前时间戳 |
##### FileInfo
| 字段名 | 字段类型 | 字段说明 |
| :--------: | :------: | :----------------------------------: |
| ahead | int | 视频分片文件的音频头信息, BASE64编码 |
| filesize | int | 视频的对应分片文件的大小 |
| timelength | int | 视频的对应分片文件的时长 |
| vhead | int | 视频分片文件的视频头信息, BASE64编码 |
##### VideoTag
| 字段名 | 字段类型 | 字段说明 |
| :----: | :------: | :------: |
| id | int | tagid |
| name | int | tag名字 |
| type | int | tag类型 |

View File

@@ -0,0 +1,41 @@
package v1
import (
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/time"
)
// AppSettingRequest .
type AppSettingRequest struct {
Base
VersionCode int `json:"version_code" form:"version_code" validate:"required"`
}
// AppUpdate .
type AppUpdate struct {
NewVersion uint8 `json:"new_version"`
Info *model.AppVersion `json:"info,omitempty"`
}
// AppSettingResponse .
type AppSettingResponse struct {
Public map[string]interface{} `json:"public"`
Update *AppUpdate `json:"update"`
Resources []*model.AppResource `json:"resources"`
}
// AppPackage .
type AppPackage struct {
ID int64 `json:"id"`
Platform uint8 `json:"platform"`
VersionName string `json:"version_name"`
VersionCode uint32 `json:"version_code"`
Title string `json:"title"`
Content string `json:"content"`
Download string `json:"download"`
MD5 string `json:"md5"`
Size int32 `json:"size"`
Force uint8 `json:"force"`
Status uint8 `json:"status"`
CTime time.Time `json:"ctime"`
}

View File

@@ -0,0 +1,23 @@
package v1
// Base .
type Base struct {
App string `json:"mobi_app" form:"mobi_app"`
Client string `json:"platform" form:"platform"`
Version string `json:"version" form:"version"`
Channel string `json:"channel" form:"channel"`
Location string `json:"location" form:"location"`
QueryID string `json:"query_id" form:"query_id"`
Module int `json:"module_id" form:"module_id"`
BUVID string
}
// DataReport 埋点上报字段
type DataReport struct {
Base
SVID int `json:"svid" form:"svid"`
TotalDuration int `json:"total_duration" form:"total_duration"`
PlayDuration int `json:"duration" form:"duration"`
DataType int `json:"data_type" form:"data_type"`
Page int `json:"page_id" form:"page_id"`
}

View File

@@ -0,0 +1,87 @@
package v1
import (
"go-common/app/interface/bbq/app-bbq/model"
)
//CommentList 评论列表
type CommentList struct {
OReply *model.CursorRes `json:"oreply,omitempty"`
}
//CommentCursorReq 游标获取评论请求参数
type CommentCursorReq struct {
SvID int64 `form:"oid" validate:"gt=0,required"`
RpID int64 `form:"rpid"`
Sort int16 `form:"sort"`
MaxID int64 `form:"max_id"`
MinID int64 `form:"min_id"`
Size int64 `form:"size" validate:"min=0"`
Plat int64 `form:"plat"`
Build int64 `form:"build"`
Access string `form:"access_key"`
Type int64
MID int64
}
//CommentAddReq 发表评论请求参数
type CommentAddReq struct {
SvID int64 `form:"oid" validate:"gt=0,required"`
Root int64 `form:"root"`
Parent int64 `form:"parent"`
At string `form:"at"`
Message string `form:"message" validate:"required"`
Plat int16 `form:"plat"`
Device string `form:"device"`
Code string `form:"code"`
Type int64
AccessKey string
}
//CommentLikeReq 评论点怎/取消请求参数
type CommentLikeReq struct {
SvID int64 `form:"oid" validate:"gt=0,required"`
RpID int64 `form:"rpid" validate:"gt=0,required"`
Action int16 `form:"action" validate:"min=0"`
Type int64
AccessKey string
}
//CommentReportReq 评论举报参数
type CommentReportReq struct {
SvID int64 `form:"oid" validate:"required"`
RpID int64 `form:"rpid" validate:"gt=0,required"`
Reason int16 `form:"reason" validate:"min=0"`
Content string `form:"content" validate:"min=2,max=200"`
Type int64
AccessKey string
}
//CommentListReq 评论列表请求参数
type CommentListReq struct {
SvID int64 `form:"oid" validate:"gt=0,required"`
Sort int16 `form:"sort"`
NoHot int16 `form:"nohot"`
Pn int64 `form:"pn"`
Ps int64 `form:"ps"`
Plat int64 `form:"plat"`
Build int64 `form:"build"`
Access string `form:"access_key"`
Type int64
MID int64
}
//CommentSubCursorReq 游标获取子回复及子评论定位参数
type CommentSubCursorReq struct {
SvID int64 `form:"oid" validate:"gt=0,required"`
Sort int16 `form:"sort" validate:"min=0"`
Root int64 `form:"root" validate:"gt=0,required"`
RpID int64 `form:"rpid" validate:"min=0"`
Size int64 `form:"size" validate:"min=0"`
MaxID int64 `form:"max_id" validate:"min=0"`
MinID int64 `form:"min_id" validate:"min=0"`
Type int64
Plat int64 `form:"plat"`
Build int64 `form:"build"`
Access string `form:"access_key"`
}

View File

@@ -0,0 +1,63 @@
package v1
import (
"go-common/app/interface/bbq/app-bbq/model"
video "go-common/app/service/bbq/video/api/grpc/v1"
bm "go-common/library/net/http/blademaster"
)
// HotReply 热评
type HotReply struct {
Hots []*model.Reply `json:"hots,omitempty"`
}
// SvDetail one video detail
type SvDetail struct {
VideoResponse
CursorValue string `json:"cursor_value"` // 透传给客户端,标记在列表中的位置
ElapsedTime int64 `json:"elapsed_time"` // 从发布到现在时间
HotReply HotReply `json:"hot_reply"` // 热评
}
// FeedListRequest feed/list request
type FeedListRequest struct {
MID int64
Device *bm.Device
BUVID string
Mark string `json:"mark" form:"mark"`
Page int `json:"page" form:"page" validate:"required"`
Qn int64 `json:"qn" form:"qn" validate:"required"`
}
// FeedListResponse feed/list request
type FeedListResponse struct {
Mark string `json:"mark" form:"mark"`
HasMore bool `json:"has_more" form:"has_more"`
List []*SvDetail `json:"list,omitempty" form:"list"`
RecList []*SvDetail `json:"rec_list,omitempty" form:"list"`
}
// FeedUpdateNumResponse feed/list request
type FeedUpdateNumResponse struct {
Num int64 `json:"num"`
}
// SpaceSvListRequest feed/list request
// 所有在空间中的视频列表,都复用该请求,同理回包
type SpaceSvListRequest struct {
MID int64
Size int
Device *bm.Device
DeviceID string `json:"device_id" form:"device_id"`
Qn int64 `json:"qn" form:"qn" validate:"required"`
UpMid int64 `json:"up_mid" form:"up_mid" validate:"required"`
CursorPrev string `json:"cursor_prev" form:"cursor_prev"` // CursorValue
CursorNext string `json:"cursor_next" form:"cursor_next"`
}
// SpaceSvListResponse feed/list request
type SpaceSvListResponse struct {
HasMore bool `json:"has_more" form:"has_more"`
List []*SvDetail `json:"list,omitempty" form:"list"`
PrepareList []*video.UploadingVideo `json:"prepare_list,omitempty"`
}

View File

@@ -0,0 +1,19 @@
package v1
// InvitationRequest .
type InvitationRequest struct {
MID int64 `json:"mid" form:"mid" validate:"required"`
}
// Download .
type Download struct {
URL string `json:"url"`
PWD string `json:"pwd"`
Type int8 `json:"type"`
}
// InvitationResponse .
type InvitationResponse struct {
Data []*Download `json:"download,omitempty"`
InviteCode int32 `json:"invite_code"`
}

View File

@@ -0,0 +1,15 @@
package v1
import (
"go-common/app/interface/bbq/app-bbq/model"
)
// LocationRequest .
type LocationRequest struct {
PID int32 `form:"pid"`
}
// LocationResponse .
type LocationResponse struct {
List []*model.Location
}

View File

@@ -0,0 +1,47 @@
package v1
import (
notice "go-common/app/service/bbq/notice-service/api/v1"
)
// NoticeNumResponse .
type NoticeNumResponse struct {
RedDot int64 `json:"red_dot"`
}
// NoticeOverviewResponse .
type NoticeOverviewResponse struct {
Notices []*NoticeOverview `json:"notices,omitempty"`
}
// NoticeOverview .
type NoticeOverview struct {
UnreadNum int64 `json:"unread_num"`
Name string `json:"name"`
NoticeType int32 `json:"notice_type"`
ShowType int32 `json:"show_type"`
}
// NoticeListRequest .
type NoticeListRequest struct {
Mid int64
NoticeType int32 `form:"notice_type" validated:"required"`
CursorNext string `form:"cursor_next" validated:"required"`
}
// NoticeListResponse .
type NoticeListResponse struct {
HasMore bool `json:"has_more"`
List []*NoticeMsg `json:"list,omitempty"`
}
// NoticeMsg .
type NoticeMsg struct {
*notice.NoticeBase
ShowType int32 `json:"show_type"`
State int32 `json:"state"`
UserInfo *UserInfo `json:"user_info,omitempty"`
Pic string `json:"pic"`
CursorValue string `json:"cursor_value"`
ErrMsg string `json:"err_msg"`
}

View File

@@ -0,0 +1,21 @@
package v1
// PushRegisterRequest .
type PushRegisterRequest struct {
RegID string `json:"register_id" form:"register_id" validate:"required"`
Platform string `json:"platform" form:"platform" validate:"required"`
SDK uint8 `json:"sdk" form:"sdk"`
}
// PushRegisterResponse .
type PushRegisterResponse struct{}
// PushCallbackRequest .
type PushCallbackRequest struct {
Base
TID string `json:"tid" form:"tid"`
NID string `json:"nid" form:"nid"`
}
// PushCallbackResponse .
type PushCallbackResponse struct{}

View File

@@ -0,0 +1,19 @@
package v1
import "go-common/app/interface/bbq/app-bbq/model"
// ReportConfigResponse .
type ReportConfigResponse struct {
Report []*model.ReportConfig `json:"report_config,omitempty"`
Reasons []*model.ReasonConfig `json:"reason_config,omitempty"`
}
// ReportRequest .
type ReportRequest struct {
Type int16 `json:"type" form:"type" validate:"required"`
UpMID int64 `json:"up_mid" form:"up_mid"`
SVID int64 `json:"svid" form:"svid"`
RpID int64 `json:"rpid" form:"rpid"`
Danmu int64 `json:"danmu" form:"danmu"`
Reason int16 `json:"reason" form:"reason"`
}

View File

@@ -0,0 +1,82 @@
package v1
//HotWordRequest .
type HotWordRequest struct {
}
//HotWordResponse .
type HotWordResponse struct {
List []string `json:"list,omitempty"`
}
// VideoSearchList 搜索视频结构
type VideoSearchList struct {
VideoResponse
TitleHighlight string `json:"title_highlight"`
HitColumns []string `json:"hit_columns,omitempty"`
CursorValue string `json:"cursor_value"`
Offset int64 // 这里不返回给客户端,只是为了代码方便处理
}
// VideoSearchRes 搜索用视频结果
type VideoSearchRes struct {
List []*VideoSearchList `json:"list,omitempty"`
NumPage int64 `json:"numPages"`
Page int64 `json:"page"`
HasMore bool `json:"has_more"`
}
// UserSearchRes 搜索用户结果
type UserSearchRes struct {
List []*UserSearchList `json:"list,omitempty"`
NumPage int64 `json:"numPages"`
Page int64 `json:"page"`
HasMore bool `json:"has_more"`
}
// UserSearchList 搜索用户结构
type UserSearchList struct {
UserInfo
UserStatic *UserStatic `json:"user_statistics"`
UnameHighlight string `json:"uname_highlight"`
HitColumns []string `json:"hit_columns"`
CursorValue string `json:"cursor_value"`
Offset int64 // 这里不返回给客户端,只是为了代码方便处理
}
// UserStatic 用户统计信息
type UserStatic struct {
Fan int64 `json:"fan"`
Follow int64 `json:"follow"`
Like int64 `json:"like"`
Liked int64 `json:"liked"`
FollowState int8 `json:"follow_state"`
}
// BaseSearchReq 基础搜索请求
type BaseSearchReq struct {
Key string `form:"keyword" validate:"required"`
Page int64 `form:"page"`
PageSize int64 `form:"pagesize"`
Highlight int8 `form:"highlight"`
Qn int64 `form:"qn"`
// TODO:v2接口当page=0时生效由于不久会拆接口因此这里就复用老接口
CursorPrev string `form:"cursor_prev"`
CursorNext string `form:"cursor_next"`
}
// SugTag sug tag结构
type SugTag struct {
Value string `json:"value"`
Name string `json:"name" `
Type string `json:"type"`
Ref int64 `json:"ref"`
}
// SugReq sug请求
type SugReq struct {
KeyWord string `form:"keyword" validate:"required"`
PageSize int64 `form:"pagesize"`
Highlight int8 `form:"highlight"`
}

View File

@@ -0,0 +1,33 @@
package v1
// Tuple .
type Tuple struct {
Key string
Val string
}
// ShareRequest .
type ShareRequest struct {
Svid int64 `form:"svid"`
Channel int32 `form:"share_channel"`
}
// ShareResponse .
type ShareResponse struct {
URL []*Tuple `json:"url"`
Params []*Tuple `json:"params"`
}
// ShareCallbackRequest .
type ShareCallbackRequest struct {
Svid int64 `form:"svid"`
URL string `form:"url"`
Type string `form:"type"`
Ctime int64 `form:"ctime"`
Channel int32 `form:"share_channel"`
}
// ShareCallbackResponse struct
type ShareCallbackResponse struct {
ShareCount int32 `json:"share"`
}

View File

@@ -0,0 +1,111 @@
package v1
import (
"go-common/app/interface/bbq/app-bbq/model"
user "go-common/app/service/bbq/user/api"
bm "go-common/library/net/http/blademaster"
)
// SvListReq 列表参数
type SvListReq struct {
MID int64
Base *Base
RemoteIP string
PageSize int64 `form:"pagesize" validate:"gt=0,max=20,required"`
}
// SvDetailReq 视频详情参数
type SvDetailReq struct {
SVID int64 `form:"svid" validate:"gt=0,required"`
}
// SvDetailResponse 视频详情返回
type SvDetailResponse struct {
IsLike bool `json:"is_like"`
}
//PlayListReq 批量获取playurl接口
type PlayListReq struct {
CIDs string `form:"cids" validate:"required"`
MID int64
Device *bm.Device
RemoteIP string
}
// VideoResponse 返回视频结构
type VideoResponse struct {
model.SvInfo
model.SvStInfo
SVID int64 `json:"svid"`
Tag string `json:"tag"` // 首选标签
Tags []VideoTag `json:"tags,omitempty"` // 所有标签
UserInfo user.UserBase `json:"user_info,omitempty"` // 用户信息
Play VideoPlay `json:"play,omitempty"` // 播放信息
IsLike bool `json:"is_like"` // 是否点赞
QueryID string `json:"query_id"` // 推荐播放埋点字段,勿删!
Extension string `json:"extension"` // 扩展字段如title_extra等
}
//VideoTag 视屏标签
type VideoTag struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type int16 `json:"type"`
}
// VideoPlay playinfo
type VideoPlay struct {
SVID int64 `json:"svid"`
ExpireTime int64 `json:"expire_time"` //过期时间
FileInfo []*FileInfo `json:"file_info"` //分片信息
Quality int64 `json:"quality"` //清晰度
SupportQuality []int64 `json:"support_quality"` //支持清晰度
URL string `json:"url"` //基础url
CurrentTime int64 `json:"current_time"` //当前时间戳
}
// CVideo bvc playurl结构
type CVideo struct {
CID int64 `json:"cid"`
ExpireTime int64 `json:"expire_time"`
FileInfo map[string][]*FileInfo `json:"file_info,omitempty"`
Fnval int64 `json:"fnval"`
Fnver int64 `json:"fnver"`
Quality int64 `json:"quality"`
SupportDescription []string `json:"support_description,omitempty"`
SupportFormats []string `json:"support_formats,omitempty"`
SupportQuality []int64 `json:"support_quality,omitempty"`
URL string `json:"url"`
VideoCodeCID int64 `json:"video_codecid"`
VideoProject bool `json:"video_project"`
}
// FileInfo bvc fileinfo
type FileInfo struct {
Ahead string `json:"ahead"`
FileSize int64 `json:"filesize"`
TimeLength int64 `json:"timelength"`
Vhead string `json:"vhead"`
Path string `json:"path"`
URL string `json:"url"`
URLBc string `json:"url_bc"`
}
// SvStatRes 视频统计数据返回
type SvStatRes struct {
model.SvStInfo
IsLike bool `json:"is_like"`
FollowState int8 `json:"follow_state"`
}
//SvRelReq 相关推荐请求
type SvRelReq struct {
SVID int64 `form:"svid"`
QueryID string `form:"query_id"`
MID int64
APP string
APPVersion string
Limit int32
Offset int32
BUVID string
}

View File

@@ -0,0 +1,51 @@
package v1
import topic "go-common/app/service/bbq/topic/api"
// TopicVideo 话题视频的结构
type TopicVideo struct {
*VideoResponse
CursorValue string `json:"cursor_value"` // 透传给客户端,标记在列表中的位置
HotType int64 `json:"hot_type"` // 热门类型直接用topic给的数据
}
//TopicDetail 话题详情页可作为详情页回包也可作为发现页话题列表的item
type TopicDetail struct {
HasMore bool `json:"has_more"`
TopicInfo *topic.TopicInfo `json:"topic_info,omitempty"`
List []*TopicVideo `json:"list,omitempty"`
}
// DiscoveryRes 发现页返回结构
type DiscoveryRes struct {
BannerList []*Banner `json:"banner_list"`
HotWords []string `json:"hot_words"`
TopicList []*TopicDetail `json:"topic_list"`
HasMore bool `json:"has_more"`
}
// Banner Banner结构
type Banner struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type int16 `json:"type"`
Scheme string `json:"scheme"`
PIC string `json:"pic"`
}
//DiscoveryReq 发现页请求
type DiscoveryReq struct {
Page int32 `form:"page" validate:"gt=0,required"`
}
// TopicSearchReq 话题搜索请求
type TopicSearchReq struct {
Page int32 `form:"page" validate:"gt=0,required"`
Keyword string `from:"keyword"`
}
// TopicSearchResponse 话题搜索回包
type TopicSearchResponse struct {
HasMore bool `json:"has_more"`
List []*topic.TopicInfo `json:"list,omitempty"`
}

View File

@@ -0,0 +1,36 @@
package v1
// ImgUploadRequest .
type ImgUploadRequest struct {
Type int `json:"type" form:"type"`
}
//PreUploadRequest ...
type PreUploadRequest struct {
Title string `json:"title" form:"title" validate:"required"`
Extension string `json:"extension" form:"extension"`
FileExt string `json:"file_ext" form:"file_ext" validate:"required"`
}
//CallBackRequest ..
type CallBackRequest struct {
Svid int64 `json:"biz_id" form:"biz_id" validate:"required"`
URL string `json:"url" form:"url" validate:"required"`
Profile string `json:"profile" form:"profile" validate:"required"`
UploadID string `json:"upload_id" form:"upload_id" validate:"required"`
Auth string `json:"auth" form:"auth" validate:"required"`
}
// UploadCheckResponse 创作中心上传过滤
type UploadCheckResponse struct {
Msg string `json:"msg"`
IsAllow bool `json:"is_allow"`
}
//HomeImgRequest ...
type HomeImgRequest struct {
SVID int64 `json:"biz_id" form:"biz_id" validate:"required"`
URL string `json:"url" form:"url" validate:"required"`
Width int64 `json:"width" form:"width" validate:"required"`
Height int64 `json:"height" form:"height" validate:"required"`
}

View File

@@ -0,0 +1,77 @@
package v1
import (
user "go-common/app/service/bbq/user/api"
)
//LoginRequest 登陆
type LoginRequest struct {
NewTag int8 `json:"new_tag" form:"new_tag"`
}
//PhoneCheckResponse ...
type PhoneCheckResponse struct {
TELStatus int32 `json:"tel_status"`
}
// SpaceUserProfileRequest ...
type SpaceUserProfileRequest struct {
Upmid int64 `json:"up_mid" form:"up_mid" validate:"required"`
}
// NumResponse 空返回值
type NumResponse struct {
Num int64 `json:"num"`
}
//UserRelationRequest .
type UserRelationRequest struct {
UPMID int64 `json:"up_mid" form:"up_mid" validate:"required"`
// 见上述RelationAction
Action int32 `json:"action" form:"action"`
}
// UserRelationListResponse 关注、粉丝、拉黑列表结构
type UserRelationListResponse struct {
HasMore bool `json:"has_more"`
List []*UserInfo `json:"list,omitempty"`
}
//UserLikeAddRequest .
type UserLikeAddRequest struct {
SVID int64 `json:"svid" form:"svid" validate:"required"`
}
//UserLikeCancelRequest .
type UserLikeCancelRequest struct {
SVID int64 `json:"svid" form:"svid" validate:"required"`
}
//InviteCodeRequest .
type InviteCodeRequest struct {
Num int64 `json:"num" form:"num" validate:"required"`
Type string `json:"type" form:"type" validate:"required"`
Digit int64 `json:"digit" form:"digit" validate:"required"`
Author int64 `json:"author" form:"author" validate:"required"`
}
//CheckInviteCodeRequest .
type CheckInviteCodeRequest struct {
Code int64 `json:"code" form:"code" validate:"required"`
DeviceID string `json:"device_id" form:"device_id" validate:"required"`
Uname string `json:"uname" form:"uname"`
}
// UserInfo 用户相关信息,统一提供对外结构
type UserInfo struct {
user.UserBase
user.UserStat
FollowState int8 `json:"follow_state"`
CursorValue string `json:"cursor_value"`
}
// UnLikeReq 不感兴趣
type UnLikeReq struct {
MID int64 `json:"mid" form:"mid"`
SVID int64 `json:"svid" form:"svid"`
}

View File

@@ -0,0 +1,45 @@
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 = [
"app_setting.toml",
"upload_filter.toml",
"video-c.toml",
],
importpath = "go-common/app/interface/bbq/app-bbq/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/conf:go_default_library",
"//app/interface/bbq/app-bbq/server/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,4 @@
[global]
name = "qing"
version = "1.0.0"
default = "test"

View File

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

View File

@@ -0,0 +1,3 @@
[midFilter]
white = []
black = []

View File

@@ -0,0 +1,218 @@
[log]
stdout = true
v = 5
[mysql]
addr = "172.16.38.91:3306"
dsn = "root:123456@tcp(172.16.38.91:3306)/bbq?allowNativePasswords=true&timeout=800ms&readTimeout=200ms&writeTimeout=800ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["root:123456@tcp(172.16.38.91:3306)/bbq?allowNativePasswords=true&timeout=800ms&readTimeout=200ms&writeTimeout=800ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "800ms"
execTimeout = "800ms"
tranTimeout = "100ms"
[dmmysql]
addr = "172.16.38.91:3306"
dsn = "root:123456@tcp(172.16.38.91:3306)/bbq?allowNativePasswords=true&timeout=800ms&readTimeout=1200ms&writeTimeout=800ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
readDSN = ["root:123456@tcp(172.16.38.91:3306)/bbq?allowNativePasswords=true&timeout=800ms&readTimeout=1200ms&writeTimeout=800ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
active = 20
idle = 10
idleTimeout ="4h"
queryTimeout = "800ms"
execTimeout = "800ms"
tranTimeout = "1000ms"
[redis]
name = "bbq-dev"
proto = "tcp"
addr = "172.16.38.91:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"
[httpClient]
[httpClient.normal]
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
key = "c8c48e784e05acfb"
secret = "aa63ee0a10afa358d02a07e7abcec546"
[httpClient.normal.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.slow]
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
key = "c8c48e784e05acfb"
secret = "aa63ee0a10afa358d02a07e7abcec546"
[httpClient.slow.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[grpcClient]
[grpcClient.account]
addr = "discovery://default/account.service"
[grpcClient.account.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.recsys]
addr = "discovery://default/bbq.service.recsys"
[grpcClient.recsys.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.bbqes]
addr = "discovery://default/bbq.service.search"
[grpcClient.bbqes.wardenconf]
dial = "100ms"
timeout = "5000ms"
[grpcClient.videoimage]
addr = "discovery://default/bbq.service.image"
[grpcClient.videoimage.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.bvcplay]
addr = "172.16.39.21:14500"
[grpcClient.bvcplay.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.notice]
addr = "discovery://default/bbq.service.notice"
[grpcClient.notice.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.user]
addr = "discovery://default/bbq.service.user"
[grpcClient.user.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.sysmsg]
addr = "discovery://default/bbq.service.sysmsg"
[grpcClient.sysmsg.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.video]
addr = "discovery://default/bbq.service.video"
[grpcClient.video.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[grpcClient.filter]
addr = "discovery://default/filter.service"
[grpcClient.filter.wardenconf]
dial = "3000ms"
timeout = "5000ms"
[antiSpam]
[antiSpam.relation]
on=true
second=3
n=3
hour=1
m=500
[antiSpam.like]
on=true
second=1
n=2
hour=1
m=500
[antiSpam.reply]
on=true
second=10
n=8
hour=1
m=100
[antiSpam.upload]
on=true
second=3
n=1
hour=1
m=20
[antiSpam.report]
on=true
second=1
n=5
hour=1
m=30
[subTitleCfg]
user_base = 1
user_collection = 1
user_fan = 1
user_follow = 1
user_like = 1
[tmap]
1= "动画"
3= "音乐"
4= "游戏"
5= "娱乐"
11= "电视剧"
13= "番剧"
23= "电影"
36= "科技"
119= "鬼畜"
129= "舞蹈"
155= "时尚"
160= "生活"
165= "广告"
167= "国创"
177= "纪录片"
181= "影视"
[urls]
bvc_batch = "http://bvc-vod.bilibili.co/playurl/batch"
reply_cursor = "http://uat-api.bilibili.co/x/v2/reply/cursor"
reply_add = "http://uat-api.bilibili.co/x/v2/reply/add"
reply_list = "http://uat-api.bilibili.co/x/v2/reply"
reply_subcursor = "http://uat-api.bilibili.co/x/v2/reply/reply/cursor"
reply_report = "http://uat-api.bilibili.co/x/v2/reply/reply/report"
reply_like = "http://uat-api.bilibili.co/x/v2/reply/action"
account = "http://uat-api.bilibili.co/x/internal/v3/account/info"
reply_counts = "http://uat-api.bilibili.co/x/internal/v2/reply/counts"
reply_minfo = "http://uat-api.bilibili.co/x/internal/v2/reply/minfo"
reply_hots = "http://uat-api.bilibili.co/x/internal/v2/reply/hots"
[comment]
type = 1
[infoc]
taskID = "001639"
proto = "tcp"
addr = "172.18.33.124:15140"
chanSize = 10240
[search]
host = "s.search.bilibili.co"
[[notices]]
noticeType = 1
showType = 1
name = "赞"
[[notices]]
noticeType = 2
showType = 2
name = "评论"
[[notices]]
noticeType = 3
showType = 3
name = "粉丝"
[[notices]]
noticeType = 4
showType = 4
name = "系统"
[upload]
httpschema = "http://"

View File

@@ -0,0 +1,46 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"conf.go",
"filter.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/api/http/v1:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/antispam:go_default_library",
"//library/net/http/blademaster/middleware/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace: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,14 @@
package conf
import "github.com/BurntSushi/toml"
// AppSetting .
type AppSetting map[string]interface{}
// Set .
func (a *AppSetting) Set(text string) error {
if _, err := toml.Decode(text, a); err != nil {
panic(err)
}
return nil
}

View File

@@ -0,0 +1,111 @@
package conf
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/library/cache/redis"
"go-common/library/conf/paladin"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/log/infoc"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/antispam"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"github.com/BurntSushi/toml"
)
var (
// Conf config
Conf = &Config{}
// App setting
App = &AppSetting{}
// Filter .
Filter = &UploadFilter{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Verify *verify.Config
Auth *auth.Config
Tracer *trace.Config
Redis *redis.Config
MySQL *sql.Config
DMMySQL *sql.Config
Ecode *ecode.Config
HTTPClient *HTTPClient
GRPCClient map[string]*GRPCConf
AntiSpam map[string]*antispam.Config
Tmap map[string]string
URLs map[string]string
Comment *Comment
Infoc *infoc.Config
Search *Search
Notices []*v1.NoticeOverview
Upload *Upload
}
//Upload ..
type Upload struct {
HTTPSchema string
}
// Set .
func (c *Config) Set(text string) error {
if _, err := toml.Decode(text, c); err != nil {
panic(err)
}
if c.Redis != nil {
for _, anti := range c.AntiSpam {
anti.Redis = c.Redis
}
}
return nil
}
// Comment 评论配置
type Comment struct {
Type int64
DebugID int64
CloseRead bool
CloseWrite bool
}
// Search 搜索配置
type Search struct {
Host string
}
// HTTPClient conf
type HTTPClient struct {
Normal *bm.ClientConfig
Slow *bm.ClientConfig
}
//GRPCConf .
type GRPCConf struct {
WardenConf *warden.ClientConfig
Addr string
}
// Init init conf
func Init() (err error) {
if err = paladin.Init(); err != nil {
return
}
if err = paladin.Watch("video-c.toml", Conf); err != nil {
return
}
if err = paladin.Watch("app_setting.toml", App); err != nil {
return
}
if err = paladin.Watch("upload_filter.toml", Filter); err != nil {
return
}
return
}

View File

@@ -0,0 +1,30 @@
package conf
import "github.com/BurntSushi/toml"
// UploadFilter 创作中心上传过滤
type UploadFilter struct {
MidFilter *UploadMidFilter
}
// UploadMidFilter 创作中心上传用户mid过滤
type UploadMidFilter struct {
White []int64
Black []int64
}
// Set .
func (uf *UploadFilter) Set(text string) error {
if _, err := toml.Decode(text, uf); err != nil {
panic(err)
}
return nil
}
// Set .
func (umf *UploadMidFilter) Set(text string) error {
if _, err := toml.Decode(text, umf); err != nil {
panic(err)
}
return nil
}

View File

@@ -0,0 +1,83 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["user_like_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = ["//app/interface/bbq/app-bbq/conf:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"comment.go",
"danmu.go",
"dao.go",
"feed.go",
"filter.go",
"image.go",
"location.go",
"notice.go",
"push.go",
"redundance.go",
"report.go",
"search.go",
"share.go",
"sv.go",
"sv_stat.go",
"upload.go",
"user.go",
"user_like.go",
"user_relation.go",
"video.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/api/http/v1:go_default_library",
"//app/interface/bbq/app-bbq/conf:go_default_library",
"//app/interface/bbq/app-bbq/model:go_default_library",
"//app/interface/bbq/app-bbq/model/grpc:go_default_library",
"//app/service/bbq/common:go_default_library",
"//app/service/bbq/notice-service/api/v1:go_default_library",
"//app/service/bbq/recsys/api/grpc/v1:go_default_library",
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/video-image/api/grpc/v1:go_default_library",
"//app/service/bbq/video/api/grpc/v1:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/filter/api/grpc/v1: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/net/metadata:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"@org_golang_google_grpc//: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,58 @@
package dao
import (
"context"
"time"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/database/sql"
)
const (
_queryNewAppVersion = "select `id`, `platform`, `ver_name`, `ver_code`, `title`, `content`, `download`, `md5`, `size`, `force`, `status` from `app_package` where platform = ? and ver_code > ? and status = 1 order by ver_code desc;"
_queryAppPackage = "select `id`, `platform`, `ver_name`, `ver_code`, `title`, `content`, `download`, `md5`, `size`, `force`, `status`, `ctime` from `app_package` where status>0;"
_queryAppResource = "select `id`, `platform`, `name`, `version`, `md5`, `download`, `status`, `start_time`, `end_time` from `app_resource` where `platform` in (0, ?) and `version` = ? and `status` = ? and `end_time` > ?;"
)
// FetchNewAppVersion .
func (d *Dao) FetchNewAppVersion(c context.Context, platform int, vCode int) (result *model.AppVersion, err error) {
result = &model.AppVersion{}
err = d.db.QueryRow(c, _queryNewAppVersion, platform, vCode).Scan(&result.ID, &result.Platform, &result.Name, &result.Code, &result.Title, &result.Content, &result.Download, &result.MD5, &result.Size, &result.Force, &result.Status)
if err == sql.ErrNoRows {
err = nil
}
return
}
// FetchAppPackage .
func (d *Dao) FetchAppPackage(c context.Context) (result []*v1.AppPackage, err error) {
rows, err := d.db.Query(c, _queryAppPackage)
for rows.Next() {
tmp := &v1.AppPackage{}
err = rows.Scan(&tmp.ID, &tmp.Platform, &tmp.VersionName, &tmp.VersionCode, &tmp.Title, &tmp.Content, &tmp.Download, &tmp.MD5, &tmp.Size, &tmp.Force, &tmp.Status, &tmp.CTime)
if err != nil {
continue
}
result = append(result, tmp)
}
return
}
// FetchAppResource .
func (d *Dao) FetchAppResource(c context.Context, plat int, ver int) (result []*model.AppResource, err error) {
result = make([]*model.AppResource, 0)
rows, err := d.db.Query(c, _queryAppResource, plat, ver, 1, time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
return
}
for rows.Next() {
tmp := new(model.AppResource)
err = rows.Scan(&tmp.ID, &tmp.Platform, &tmp.Name, &tmp.Code, &tmp.MD5, &tmp.Download, &tmp.Status, &tmp.StartTime, &tmp.EndTime)
if err != nil {
continue
}
result = append(result, tmp)
}
return
}

View File

@@ -0,0 +1,425 @@
package dao
import (
"bytes"
"context"
"fmt"
"strconv"
"strings"
"go-common/app/interface/bbq/app-bbq/model"
user "go-common/app/service/bbq/user/api"
"go-common/library/log"
"go-common/library/net/metadata"
"github.com/json-iterator/go"
)
// ReplySubCursor http请求子评论游标获取评论
func (d *Dao) ReplySubCursor(c context.Context, req map[string]interface{}) (res *model.SubCursorRes, err error) {
var r []byte
ip := metadata.String(c, metadata.RemoteIP)
res = new(model.SubCursorRes)
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_subcursor"], "GET", req, ip)
if err != nil {
log.Infov(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
// err = json.Unmarshal(r, &res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
return
}
//获取所有mid
var mid []int64
if res.Root != nil {
mid = append(mid, res.Root.Mid)
}
if res.Root.Replies != nil {
mid = append(mid, d.getMidInReplys(res.Root.Replies)...)
}
//批量获取userinfo
var userinfo map[int64]*user.UserBase
userinfo, err = d.JustGetUserBase(c, mid)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("d.UserBase err[%v]", err)))
return
}
if res.Root != nil {
d.replaceMemberInReply(res.Root, userinfo)
}
return
}
// ReplyCursor http请求游标获取评论
func (d *Dao) ReplyCursor(c context.Context, req map[string]interface{}) (res *model.CursorRes, err error) {
var r []byte
ip := metadata.String(c, metadata.RemoteIP)
res = new(model.CursorRes)
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_cursor"], "GET", req, ip)
if err != nil {
log.Infov(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
return
}
//获取所有mid
var mid []int64
if res.Replies != nil {
mid = append(mid, d.getMidInReplys(res.Replies)...)
}
if res.Hots != nil {
mid = append(mid, d.getMidInReplys(res.Hots)...)
}
if res.Top != nil {
if res.Top.Admin != nil {
mid = append(mid, res.Top.Admin.Mid)
}
if res.Top.Upper != nil {
mid = append(mid, res.Top.Upper.Mid)
}
}
//批量获取userinfo
var userinfo map[int64]*user.UserBase
userinfo, err = d.JustGetUserBase(c, mid)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("d.UserBase err[%v]", err)))
return
}
if res.Replies != nil {
d.replaceMemberInReplys(res.Replies, userinfo)
}
if res.Hots != nil {
d.replaceMemberInReplys(res.Hots, userinfo)
}
if res.Top != nil {
if res.Top.Admin != nil {
d.replaceMemberInReplys(res.Top.Admin.Replies, userinfo)
}
if res.Top.Upper != nil {
d.replaceMemberInReplys(res.Top.Upper.Replies, userinfo)
}
}
return
}
// getMidInReplys 批量获取评论列表中mid
func (d *Dao) getMidInReplys(r []*model.Reply) (l []int64) {
for _, v := range r {
l = append(l, v.Mid)
for _, c := range v.Content.Members {
cmid, err := strconv.ParseInt(c.Mid, 10, 64)
if err != nil {
log.Errorv(
context.Background(),
log.KV("log", fmt.Sprintf("strconv err [%v] data[%s]", err, c.Mid)),
)
continue
}
l = append(l, cmid)
}
if len(v.Replies) > 0 {
sl := d.getMidInReplys(v.Replies)
l = append(l, sl...)
}
}
return
}
// replaceMemberInReplys 批量替换评论列表中用户信息
func (d *Dao) replaceMemberInReplys(r []*model.Reply, umap map[int64]*user.UserBase) {
for _, v := range r {
if u, ok := umap[v.Mid]; ok {
v.Member.BInfo = u
} else {
v.Member.BInfo = new(user.UserBase)
v.Member.BInfo.Mid = v.Mid
v.Member.BInfo.Uname = v.Member.Name
v.Member.BInfo.Face = v.Member.Avatar
}
for _, c := range v.Content.Members {
cmid, err := strconv.ParseInt(c.Mid, 10, 64)
if err != nil {
log.Errorv(
context.Background(),
log.KV("log", fmt.Sprintf("strconv err [%v] data[%s]", err, c.Mid)),
)
continue
}
if u, ok := umap[cmid]; ok {
c.BInfo = u
} else {
c.BInfo = new(user.UserBase)
c.BInfo.Mid = cmid
c.BInfo.Uname = c.Name
c.BInfo.Face = c.Avatar
}
}
if len(v.Replies) > 0 {
d.replaceMemberInReplys(v.Replies, umap)
}
}
}
// replaceMemberInReply 替换评论中用户信息
func (d *Dao) replaceMemberInReply(r *model.Reply, umap map[int64]*user.UserBase) {
if u, ok := umap[r.Mid]; ok {
r.Member.BInfo = u
} else {
r.Member.BInfo = new(user.UserBase)
r.Member.BInfo.Mid = r.Mid
r.Member.BInfo.Uname = r.Member.Name
r.Member.BInfo.Face = r.Member.Avatar
}
for _, c := range r.Content.Members {
cmid, err := strconv.ParseInt(c.Mid, 10, 64)
if err != nil {
log.Errorv(
context.Background(),
log.KV("log", fmt.Sprintf("strconv err [%v] data[%s]", err, c.Mid)),
)
continue
}
if u, ok := umap[cmid]; ok {
c.BInfo = u
} else {
c.BInfo = new(user.UserBase)
c.BInfo.Mid = cmid
c.BInfo.Uname = c.Name
c.BInfo.Face = c.Avatar
}
}
if len(r.Replies) > 0 {
d.replaceMemberInReplys(r.Replies, umap)
}
}
// ReplyAdd http请求添加评论
func (d *Dao) ReplyAdd(c context.Context, req map[string]interface{}) (res *model.AddRes, err error) {
ip := metadata.String(c, metadata.RemoteIP)
res = new(model.AddRes)
var r []byte
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_add"], "POST", req, ip)
if err != nil {
log.Infov(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(r, &res)
return
}
//ReplyLike http请求评论点赞
func (d *Dao) ReplyLike(c context.Context, req map[string]interface{}) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
_, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_like"], "POST", req, ip)
return
}
// ReplyList 评论列表
func (d *Dao) ReplyList(c context.Context, req map[string]interface{}) (res *model.ReplyList, err error) {
ip := metadata.String(c, metadata.RemoteIP)
res = new(model.ReplyList)
var r []byte
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_list"], "GET", req, ip)
if err != nil {
log.Infov(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
return
}
//获取所有mid
var mid []int64
if res.Replies != nil {
mid = append(mid, d.getMidInReplys(res.Replies)...)
}
if res.Hots != nil {
mid = append(mid, d.getMidInReplys(res.Hots)...)
}
if res.Top != nil {
mid = append(mid, res.Top.Mid)
}
//批量获取userinfo
var userinfo map[int64]*user.UserBase
userinfo, err = d.JustGetUserBase(c, mid)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("d.UserBase err[%v]", err)))
return
}
if res.Replies != nil {
d.replaceMemberInReplys(res.Replies, userinfo)
}
if res.Hots != nil {
d.replaceMemberInReplys(res.Hots, userinfo)
}
if res.Top != nil {
d.replaceMemberInReplys(res.Top.Replies, userinfo)
}
return
}
// ReplyReport 评论举报
func (d *Dao) ReplyReport(c context.Context, req map[string]interface{}) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
_, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_report"], "POST", req, ip)
return
}
// ReplyCounts 批量评论数
func (d *Dao) ReplyCounts(c context.Context, ids []int64, t int64) (res map[int64]*model.ReplyCount, err error) {
ip := metadata.String(c, metadata.RemoteIP)
oidStr := strings.Replace(strings.Trim(fmt.Sprint(ids), "[]"), " ", ",", -1)
req := map[string]interface{}{
"type": t,
"oid": oidStr,
}
res = make(map[int64]*model.ReplyCount)
var r []byte
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_counts"], "GET", req, ip)
if err != nil {
log.Infov(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
}
return
}
// ReplyMinfo 批量请求评论
func (d *Dao) ReplyMinfo(c context.Context, svID int64, rpids []int64) (res map[int64]*model.Reply, err error) {
res = make(map[int64]*model.Reply)
if len(rpids) == 0 {
return
}
ip := metadata.String(c, metadata.RemoteIP)
var rpidStr string
for _, rpid := range rpids {
if len(rpidStr) != 0 {
rpidStr += fmt.Sprintf(",%d", rpid)
} else {
rpidStr += fmt.Sprintf("%d", rpid)
}
}
log.V(1).Infov(c, log.KV("log", "reply minfo: rpids="+rpidStr))
req := map[string]interface{}{
"type": model.DefaultCmType,
"oid": svID,
"rpid": rpidStr,
}
var r []byte
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_minfo"], "GET", req, ip)
if err != nil {
log.Errorv(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
}
log.V(1).Infov(c, log.KV("log", "get reply minfo"), log.KV("req_size", len(rpids)), log.KV("rsp_size", len(res)))
return
}
// ReplyHot 获取批量热评
func (d *Dao) ReplyHot(c context.Context, mid int64, oids []int64) (res map[int64][]*model.Reply, err error) {
res = make(map[int64][]*model.Reply)
if len(oids) == 0 {
return
}
ip := metadata.String(c, metadata.RemoteIP)
var oidStr string
for _, oid := range oids {
if len(oidStr) != 0 {
oidStr += fmt.Sprintf(",%d", oid)
} else {
oidStr += fmt.Sprintf("%d", oid)
}
}
log.V(1).Infov(c, log.KV("log", "reply minfo: oids="+oidStr))
req := map[string]interface{}{
"type": model.DefaultCmType,
"oids": oidStr,
"mid": mid,
}
var r []byte
r, err = replyHTTPCommon(c, d.httpClient, d.c.URLs["reply_hots"], "GET", req, ip)
if err != nil {
log.Errorv(c,
log.KV("log", fmt.Sprintf("replyHTTPCommon err [%v]", err)),
)
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
decoder := json.NewDecoder(bytes.NewBuffer(r))
decoder.UseNumber()
err = decoder.Decode(&res)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("json unmarlshal err data[%s]", string(r))))
}
log.V(1).Infov(c, log.KV("log", "get reply hots"), log.KV("req_size", len(oids)), log.KV("rsp_size", len(res)))
// 替换bbq的用户信息
var mIDs []int64
for _, replies := range res {
for _, reply := range replies {
mIDs = append(mIDs, reply.Mid)
}
}
var userBases map[int64]*user.UserBase
userBases, err = d.JustGetUserBase(c, mIDs)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("d.UserBase err[%v]", err)))
return
}
for _, replies := range res {
if len(replies) > 0 {
d.replaceMemberInReplys(replies, userBases)
}
}
return
}

View File

@@ -0,0 +1,21 @@
package dao
import (
"context"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/log"
)
const (
_queryDanmu = "select oid,mid,offset,content from bullet_content where id = ?"
)
//RawBullet ...
func (d *Dao) RawBullet(ctx context.Context, danmu int64) (v *model.Danmu, err error) {
v = new(model.Danmu)
if err = d.dbDM.QueryRow(ctx, _queryDanmu, danmu).Scan(&v.OID, &v.MID, &v.Offset, &v.Content); err != nil {
log.Errorw(ctx, "content", "query danmu", "err", err, "danmu id", danmu)
return
}
return
}

View File

@@ -0,0 +1,218 @@
package dao
import (
"context"
"encoding/json"
"flag"
"fmt"
"go-common/library/sync/pipeline/fanout"
"net/url"
"reflect"
"strconv"
"go-common/app/interface/bbq/app-bbq/conf"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/app/interface/bbq/app-bbq/model/grpc"
notice "go-common/app/service/bbq/notice-service/api/v1"
recsys "go-common/app/service/bbq/recsys/api/grpc/v1"
user "go-common/app/service/bbq/user/api"
image "go-common/app/service/bbq/video-image/api/grpc/v1"
video "go-common/app/service/bbq/video/api/grpc/v1"
acc "go-common/app/service/main/account/api"
filter "go-common/app/service/main/filter/api/grpc/v1"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/rpc/warden"
jsoniter "github.com/json-iterator/go"
)
var (
videoPath string
)
// Dao dao
type Dao struct {
c *conf.Config
cache *fanout.Fanout
redis *redis.Pool
db *xsql.DB
dbDM *xsql.DB
httpClient *bm.Client
httpslowClient *bm.Client
accountClient acc.AccountClient
recsysClient recsys.RecsysClient
imageClient image.VideoImageClient
bvcPlayClient grpc.PlayurlServiceClient
redundanceVideos []*model.RVideo
noticeClient notice.NoticeClient
userClient user.UserClient
videoClient video.VideoClient
filterClient filter.FilterClient
}
func init() {
flag.StringVar(&videoPath, "video_json", "./video.json", "接口冗余降级video数据")
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
dao = &Dao{
c: c,
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
redis: redis.NewPool(c.Redis),
db: xsql.NewMySQL(c.MySQL),
dbDM: xsql.NewMySQL(c.DMMySQL),
httpClient: bm.NewClient(c.HTTPClient.Normal),
httpslowClient: bm.NewClient(c.HTTPClient.Slow),
accountClient: newAccountClient(c.GRPCClient["account"]),
recsysClient: newRecsysClient(c.GRPCClient["recsys"]),
imageClient: newVideoImageClient(c.GRPCClient["videoimage"]),
bvcPlayClient: newBVCPlayClient(c.GRPCClient["bvcplay"]),
redundanceVideos: model.RedundanceVideo(),
noticeClient: newNoticeClient(c.GRPCClient["notice"]),
userClient: newUserClient(c.GRPCClient["user"]),
videoClient: newVideoClient(c.GRPCClient["video"]),
filterClient: newFilterClient(c.GRPCClient["filter"]),
}
return
}
// newVideoClient .
func newVideoClient(cfg *conf.GRPCConf) video.VideoClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return video.NewVideoClient(cc)
}
// newUserClient .
func newUserClient(cfg *conf.GRPCConf) user.UserClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return user.NewUserClient(cc)
}
// newNoticeClient .
func newNoticeClient(cfg *conf.GRPCConf) notice.NoticeClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return notice.NewNoticeClient(cc)
}
// newBVCPlayClient .
func newBVCPlayClient(cfg *conf.GRPCConf) grpc.PlayurlServiceClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return grpc.NewPlayurlServiceClient(cc)
}
// newVideoImageClient .
func newVideoImageClient(cfg *conf.GRPCConf) image.VideoImageClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return image.NewVideoImageClient(cc)
}
//newAccountClient .
func newAccountClient(cfg *conf.GRPCConf) acc.AccountClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return acc.NewAccountClient(cc)
}
//newRecsysClient .
func newRecsysClient(cfg *conf.GRPCConf) recsys.RecsysClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return recsys.NewRecsysClient(cc)
}
// newUserClient .
func newFilterClient(cfg *conf.GRPCConf) filter.FilterClient {
cc, err := warden.NewClient(cfg.WardenConf).Dial(context.Background(), cfg.Addr)
if err != nil {
panic(err)
}
return filter.NewFilterClient(cc)
}
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
return d.db.Ping(c)
}
// BeginTran begin mysql transaction
func (d *Dao) BeginTran(c context.Context) (*xsql.Tx, error) {
return d.db.Begin(c)
}
// ReplyHTTPCommon 评论公用请求
func replyHTTPCommon(c context.Context, httpClient *bm.Client, path string, method string, data map[string]interface{}, ip string) (r []byte, err error) {
params := url.Values{}
t := reflect.TypeOf(data).Kind()
if t == reflect.Map {
for k, v := range data {
// params.Set(k, v.(string))
switch reflect.TypeOf(v).Kind() {
case reflect.Int64:
params.Set(k, strconv.FormatInt(v.(int64), 10))
case reflect.Int16:
params.Set(k, strconv.FormatInt(int64(v.(int16)), 10))
case reflect.String:
params.Set(k, v.(string))
case reflect.Int:
params.Set(k, strconv.FormatInt(int64(v.(int)), 10))
}
}
}
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("reply req url(%s)", path+"?"+params.Encode())))
req, err := httpClient.NewRequest(method, path, ip, params)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("reply url(%s) error(%v)", path+"?"+params.Encode(), err)))
return
}
var res struct {
Code int `json:"code"`
Msg string `json:"message"`
Data json.RawMessage `json:"data"`
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
if err = httpClient.Do(c, req, &res); err != nil {
str, _ := json.Marshal(res)
log.Errorv(c, log.KV("log", fmt.Sprintf("reply ret data(%s) err[%v]", str, err)))
return
}
str, _ := json.Marshal(res)
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("reply ret data(%s)", str)))
if res.Code != 0 {
err = ecode.Int(res.Code)
log.Warnv(c, log.KV("log", fmt.Sprintf("reply url(%s) error(%v)", path+"?"+params.Encode(), err)))
}
r = res.Data
return
}

View File

@@ -0,0 +1,102 @@
package dao
import (
"context"
"fmt"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/cache/redis"
"go-common/library/time"
"go-common/library/xstr"
"strconv"
"go-common/library/log"
)
// FetchAvailableOutboxList 根据提供的up主mids去获取比lastSvID还小的svid
// @param CursorID 函数并不判断SvID正确性由调用方保证
// @param cursorNext 表明fetch的方向同时会影响排序顺序
// true 则有如下条件sql"... and id < {{svid}} order by id desc..."
// false 则有如下条件sql"... and id > {{svid}} order by id asc..."
// @return svIDs 注意svid的返回有顺序
func (d *Dao) FetchAvailableOutboxList(c context.Context, fetchStates string, mids []int64, cursorNext bool, cursorSvID int64, cursorPubtime time.Time, size int) (svIDs []int64, err error) {
if len(mids) == 0 {
return
}
compareSymbol := string(">=")
orderDirection := "asc"
if cursorNext {
compareSymbol = "<="
orderDirection = "desc"
}
midStr := xstr.JoinInts(mids)
// 多个字段order by每个字段都要指定desc否则不带的字段为asc
querySQL := fmt.Sprintf("select svid, pubtime from video where mid in (%s) and state in (%s) and "+
"pubtime %s ? order by pubtime %s, svid %s limit %d",
midStr, fetchStates, compareSymbol, orderDirection, orderDirection, size)
log.V(1).Infov(c, log.KV("event", "mysql_select"), log.KV("table", "video"),
log.KV("sql", querySQL))
rows, err := d.db.Query(c, querySQL, cursorPubtime)
if err != nil {
log.Errorv(c, log.KV("event", "mysql_select"), log.KV("table", "video"),
log.KV("sql", querySQL))
return
}
defer rows.Close()
var svID int64
var pubTIme time.Time
conflict := bool(true)
for rows.Next() {
if err = rows.Scan(&svID, &pubTIme); err != nil {
log.Errorv(c, log.KV("event", "mysql_scan"), log.KV("table", "video"),
log.KV("sql", querySQL))
return
}
// 为了解决同一个pubtime的冲突问题
if pubTIme == cursorPubtime && conflict {
if svID == cursorSvID {
conflict = false
}
continue
}
svIDs = append(svIDs, svID)
}
log.Infov(c, log.KV("event", "mysql_select"), log.KV("table", "video"),
log.KV("desc", "fetch outbox"), log.KV("size", len(svIDs)))
return
}
// SetMIDLastPubtime redis设置
func (d *Dao) SetMIDLastPubtime(c context.Context, mid int64, pubtime int64) (err error) {
key := fmt.Sprintf(model.CacheKeyLastPubtime, mid)
conn := d.redis.Get(c)
defer conn.Close()
_, err = conn.Do("set", key, pubtime, "ex", model.CacheExpireLastPubtime)
if err != nil {
log.Errorv(c, log.KV("event", "redis_set"), log.KV("key", key), log.KV("value", pubtime))
}
return
}
// GetMIDLastPubtime 获取该用户feed中的最新浏览svid
func (d *Dao) GetMIDLastPubtime(c context.Context, mid int64) (pubtime int64, err error) {
pubtime = 0
key := fmt.Sprintf(model.CacheKeyLastPubtime, mid)
conn := d.redis.Get(c)
defer conn.Close()
var data []byte
if data, err = redis.Bytes(conn.Do("get", key)); err != nil {
if err == redis.ErrNil {
err = nil
log.V(1).Infov(c, log.KV("event", "redis_get"), log.KV("key", key), log.KV("result", "not_found"))
} else {
log.Errorv(c, log.KV("event", "redis_get"), log.KV("key", key))
}
return
}
pubtime, err = strconv.ParseInt(string(data), 10, 0)
if err != nil {
log.Errorv(c, log.KV("event", "redis_get"), log.KV("key", key), log.KV("value", data))
}
return
}

View File

@@ -0,0 +1,33 @@
package dao
import (
"context"
filter "go-common/app/service/main/filter/api/grpc/v1"
"go-common/library/log"
)
// 用于filter
const (
FilterAreaAccount = "BBQ_account"
FilterAreaVideo = "BBQ_video"
FilterAreaReply = "BBQ_reply"
FilterAreaSearch = "BBQ_search"
FilterAreaDanmu = "BBQ_danmu"
FilterLevel = 20
)
// Filter .
func (d *Dao) Filter(ctx context.Context, content string, area string) (level int32, err error) {
req := new(filter.FilterReq)
req.Message = content
req.Area = area
reply, err := d.filterClient.Filter(ctx, req)
if err != nil {
log.Errorv(ctx, log.KV("log", "filter fail : req="+req.String()))
return
}
level = reply.Level
log.V(1).Infov(ctx, log.KV("log", "get filter reply="+reply.String()))
return
}

View File

@@ -0,0 +1,25 @@
package dao
import (
"context"
image "go-common/app/service/bbq/video-image/api/grpc/v1"
"go-common/library/log"
)
//Upload .
func (d *Dao) Upload(c context.Context, fileName string, filePath string, file []byte) (location string, err error) {
imageReq := &image.ImgUploadRequest{
Filename: fileName,
Dir: filePath,
File: file,
}
imageRes, err := d.imageClient.ImgUpload(c, imageReq)
if err != nil {
log.Errorv(c, log.KV("event", "grpc/imageupload"), log.KV("err", err))
return
}
if imageRes != nil {
location = imageRes.Location
}
return
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"go-common/app/interface/bbq/app-bbq/model"
)
var (
locationQueryChild = "select `loc_id`, `pid`, `name` from `bbq_location` where `pid` = ?;"
locationQueryAll = "select `loc_id`, `pid`, `name` from `bbq_location`;"
)
// GetLocationAll .
func (d *Dao) GetLocationAll(c context.Context) (*map[int32][]*model.Location, error) {
rows, err := d.db.Query(c, locationQueryAll)
if err != nil {
return nil, err
}
defer rows.Close()
m := make(map[int32][]*model.Location)
var id, pid int32
var name string
for rows.Next() {
rows.Scan(&id, &pid, &name)
m[pid] = append(m[pid], &model.Location{
ID: id,
PID: pid,
Name: name,
})
}
return &m, err
}
// GetLocationChild .
func (d *Dao) GetLocationChild(c context.Context, locID int32) (*map[int32][]*model.Location, error) {
rows, err := d.db.Query(c, locationQueryChild, locID)
if err != nil {
return nil, err
}
defer rows.Close()
m := make(map[int32][]*model.Location)
var id, pid int32
var name string
for rows.Next() {
rows.Scan(&id, &pid, &name)
m[pid] = append(m[pid], &model.Location{
ID: id,
PID: pid,
Name: name,
})
}
return &m, err
}

View File

@@ -0,0 +1,49 @@
package dao
import (
"context"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/library/log"
)
// NoticeList 获取通知列表
func (d *Dao) NoticeList(ctx context.Context, noticeType int32, mid, cursorID int64) (list []*notice.NoticeBase, err error) {
req := &notice.ListNoticesReq{
Mid: mid,
NoticeType: noticeType,
CursorId: cursorID,
}
res, err := d.noticeClient.ListNotices(ctx, req)
if err != nil {
log.Errorv(ctx, log.KV("log", "notice-service:ListNotices fail"), log.KV("err", err))
return
}
list = res.List
return
}
// GetNoticeUnread 获取未读情况
func (d *Dao) GetNoticeUnread(ctx context.Context, mid int64) (list []*notice.UnreadItem, err error) {
req := &notice.GetUnreadInfoRequest{Mid: mid}
res, err := d.noticeClient.GetUnreadInfo(ctx, req)
if err != nil {
log.Errorv(ctx, log.KV("log", "call notice service get unread info fail: err="+err.Error()))
return
}
list = res.List
log.V(1).Infov(ctx, log.KV("log", "call notice service get unread info: res="+res.String()))
return
}
// CreateNotice 创建通知
func (d *Dao) CreateNotice(ctx context.Context, notice *notice.NoticeBase) (err error) {
_, err = d.noticeClient.CreateNotice(ctx, notice)
if err != nil {
log.Errorv(ctx, log.KV("log", "create notice fail: notice="+notice.String()))
return
}
log.V(1).Infov(ctx, log.KV("log", "create notice: notice="+notice.String()))
return
}

View File

@@ -0,0 +1,39 @@
package dao
import (
"context"
"fmt"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/library/log"
)
// PushLogin .
func (d *Dao) PushLogin(c context.Context, req *notice.UserPushDev) (err error) {
_, err = d.noticeClient.PushLogin(c, req)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("push login fail: req=%s", req.String())))
}
return
}
// PushLogout .
func (d *Dao) PushLogout(c context.Context, req *notice.UserPushDev) (err error) {
_, err = d.noticeClient.PushLogout(c, req)
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("push logout fail: req=%s", req.String())))
}
return
}
// PushCallback .
func (d *Dao) PushCallback(c context.Context, tid string, nid string, mid int64, buvid string) (err error) {
_, err = d.noticeClient.PushCallback(c, &notice.PushCallbackRequest{
Tid: tid,
Nid: nid,
Mid: mid,
Buvid: buvid,
})
return
}

View File

@@ -0,0 +1,43 @@
package dao
import (
"math/rand"
"time"
"go-common/app/interface/bbq/app-bbq/model"
)
// GetRandVideoList .
func (d *Dao) GetRandVideoList(mid int64, limit int) []*model.SvInfo {
var result []*model.SvInfo
r := rand.New(rand.NewSource(time.Now().Unix()))
mask := len(d.redundanceVideos) - limit
cursor := r.Int() % mask
for _, v := range d.redundanceVideos[cursor : cursor+limit] {
result = append(result, &model.SvInfo{
SVID: v.Svid,
AVID: v.Avid,
CID: v.Cid,
MID: mid,
})
}
return result
}
// GetRandSvList .
func (d *Dao) GetRandSvList(limit int) []int64 {
result := make([]int64, limit)
r := rand.New(rand.NewSource(time.Now().Unix()))
mask := len(d.redundanceVideos) - limit
cursor := r.Int() % mask
for _, v := range d.redundanceVideos[cursor : cursor+limit] {
result = append(result, v.Svid)
}
return result
}

View File

@@ -0,0 +1,95 @@
package dao
import (
"context"
"errors"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/log"
xhttp "net/http"
"net/url"
"strconv"
)
//ReportUser rType=0 face;rType=1 name;
func (d *Dao) ReportUser(c context.Context, rType int, mid int64, rmid int64, reason string) (err error) {
var (
params url.Values
req *xhttp.Request
res model.HTTPRpcRes
)
params = url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("rmid", strconv.FormatInt(rmid, 10))
params.Set("type", strconv.Itoa(rType))
params.Set("reason", reason)
if req, err = d.httpClient.NewRequest("POST", d.c.URLs["cms_report"], "", params); err != nil {
log.Errorv(c, log.KV("event", "ReportUser d.httpClient.NewRequest failed"), log.KV("err", err))
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Errorv(c, log.KV("event", "cms ReportUser http req failed"), log.KV("err", err), log.KV("req", req))
return
}
if res.Code != 0 {
log.Errorv(c, log.KV("event", "cms ReportUser res.code err"), log.KV("err", err))
err = errors.New("cms ReportUser return err")
return
}
return
}
//ReportDanmu ..
func (d *Dao) ReportDanmu(c context.Context, danmu int64, mid int64, rmid int64, reason string) (err error) {
var (
params url.Values
req *xhttp.Request
res model.HTTPRpcRes
)
params = url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
params.Set("rmid", strconv.FormatInt(rmid, 10))
params.Set("bullet_id", strconv.FormatInt(danmu, 10))
params.Set("reason", reason)
if req, err = d.httpClient.NewRequest("POST", d.c.URLs["cms_report_bullet"], "", params); err != nil {
log.Errorv(c, log.KV("event", "ReportDanmu d.httpClient.NewRequest failed"), log.KV("err", err))
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Errorv(c, log.KV("event", "cms ReportDanmu http req failed"), log.KV("err", err), log.KV("req", req))
return
}
if res.Code != 0 {
log.Errorv(c, log.KV("event", "cms ReportDanmu res.code err"), log.KV("err", err))
err = errors.New("cms ReportDanmu return err")
return
}
return
}
//ReportVideo ..
func (d *Dao) ReportVideo(c context.Context, svid int64, rmid int64, reason string) (err error) {
var (
params url.Values
req *xhttp.Request
res model.HTTPRpcRes
)
params = url.Values{}
params.Set("rmid", strconv.FormatInt(rmid, 10))
params.Set("svid", strconv.FormatInt(svid, 10))
params.Set("reason", reason)
if req, err = d.httpClient.NewRequest("POST", d.c.URLs["cms_report_video"], "", params); err != nil {
log.Errorv(c, log.KV("event", "ReportVideo d.httpClient.NewRequest failed"), log.KV("err", err))
return
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Errorv(c, log.KV("event", "cms ReportVideo http req failed"), log.KV("err", err), log.KV("req", req))
return
}
if res.Code != 0 {
log.Errorv(c, log.KV("event", "cms ReportVideo res.code err"), log.KV("err", err))
err = errors.New("cms ReportVideo return err")
return
}
return
}

View File

@@ -0,0 +1,164 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"net/url"
"strconv"
"strings"
"time"
jsoniter "github.com/json-iterator/go"
)
const (
httpProto = "http://"
searchBaseURI = "/bbq/search"
sugBaseURI = "/main/suggest"
getSVIDbyRelID = "select id,svid from video where id in (%s)"
)
// SearchBBQ 搜索视频
func (d *Dao) SearchBBQ(c context.Context, sreq *model.SearchBaseReq) (ret *model.RawSearchRes, err error) {
var json = jsoniter.ConfigCompatibleWithStandardLibrary
ret = new(model.RawSearchRes)
path := httpProto + d.c.Search.Host + searchBaseURI
params := url.Values{}
d.preSetSearchParam(c, &params)
params.Set("keyword", sreq.KeyWord)
params.Set("page", strconv.FormatInt(sreq.Page, 10))
params.Set("pagesize", strconv.FormatInt(sreq.PageSize, 10))
params.Set("highlight", strconv.FormatInt(sreq.Highlight, 10))
params.Set("search_type", sreq.Type)
log.Infov(c, log.KV("log", fmt.Sprintf("search url(%s)", path+"?"+params.Encode())))
req, err := d.httpClient.NewRequest("GET", path, params.Get("ip"), params)
if err != nil {
log.Error("search url(%s) error(%v)", path+"?"+params.Encode(), err)
return
}
if err = d.httpClient.Do(c, req, &ret); err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("search url(%s) error(%v)", path+"?"+params.Encode(), err)))
return
}
if ret.Code != 0 {
err = ecode.Int(ret.Code)
log.Errorv(c, log.KV("log", fmt.Sprintf("search url(%s) error(%v)", path+"?"+params.Encode(), err)))
return
}
_str, _ := json.Marshal(ret)
log.Infov(c,
log.KV("log", fmt.Sprintf("Search req[%s] ret[%s]", path+"?"+params.Encode(), _str)))
return
}
// SugBBQ 搜索视频
func (d *Dao) SugBBQ(c context.Context, sreq *model.SugBaseReq) (ret json.RawMessage, err error) {
path := httpProto + d.c.Search.Host + sugBaseURI
params := url.Values{}
d.preSetSearchParam(c, &params)
params.Set("term", sreq.Term)
params.Set("suggest_type", sreq.SuggestType)
params.Set("highlight", strconv.FormatInt(sreq.Highlight, 10))
params.Set("main_ver", sreq.MainVer)
params.Set("ts", strconv.FormatInt(time.Now().Unix(), 10))
log.Infov(c, log.KV("log", fmt.Sprintf("sug url(%s)", path+"?"+params.Encode())))
req, err := d.httpClient.NewRequest("GET", path, params.Get("ip"), params)
if err != nil {
log.Error("sug url(%s) error(%v)", path+"?"+params.Encode(), err)
return
}
var res struct {
Code int `json:"code"`
Stoken string `json:"stoken"`
Res struct {
Tag json.RawMessage `json:"tag"`
} `json:"result"`
}
if err = d.httpClient.Do(c, req, &res); err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("sug url(%s) error(%v)", path+"?"+params.Encode(), err)))
return
}
if res.Code != 0 {
err = ecode.Int(res.Code)
log.Errorv(c, log.KV("log", fmt.Sprintf("sug url(%s) error(%v)", path+"?"+params.Encode(), err)))
return
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
_str, _ := json.Marshal(ret)
log.Infov(c, log.KV("log", fmt.Sprintf("Sug req[%s] ret[%s]", path+"?"+params.Encode(), _str)))
ret = res.Res.Tag
return
}
func (d *Dao) preSetSearchParam(c context.Context, params *url.Values) {
device := c.Value("device")
if device != nil {
dev := device.(*bm.Device)
if dev.RawPlatform != "" {
params.Set("platform", dev.RawPlatform)
}
if dev.RawMobiApp != "" {
params.Set("mobi_app", dev.RawMobiApp)
}
if dev.Device != "" {
params.Set("device", dev.Device)
}
if dev.Build > 0 {
params.Set("device", strconv.FormatInt(dev.Build, 10))
}
}
ip := metadata.String(c, metadata.RemoteIP)
if ip != "" {
params.Set("clientip", ip)
}
}
// ConvID2SVID 转换搜索相对id到svid
func (d *Dao) ConvID2SVID(c context.Context, ids []int64) (res map[int64]int64, err error) {
var idList []string
res = make(map[int64]int64)
if len(ids) == 0 {
return
}
for _, id := range ids {
idList = append(idList, strconv.FormatInt(id, 10))
}
if len(idList) == 0 {
log.Warn("empty query list relID [%v]", ids)
return
}
idStr := strings.Join(idList, ",")
sql := fmt.Sprintf(getSVIDbyRelID, idStr)
rows, err := d.db.Query(c, sql)
if err != nil {
log.Warn("Query Err [%v]", err)
return
}
defer rows.Close()
for rows.Next() {
var svid int64
var id int64
err = rows.Scan(&id, &svid)
if err != nil {
log.Warn("Scan Err [%v]", err)
return
}
res[id] = svid
}
return
}
// ParseRel2ID 转换搜索相对id到自增id
func (d *Dao) ParseRel2ID(relID []int32) (idList []int64) {
for _, id := range relID {
idList = append(idList, int64(id/100))
}
return
}

View File

@@ -0,0 +1,54 @@
package dao
import (
"context"
"fmt"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
var (
_selectUserShareToken = "select token from user_share_token where mid = ?;"
_insertUserShareToken = "insert into user_share_token (mid, token) values (?, ?);"
)
// GetUserShareToken .
func (d *Dao) GetUserShareToken(ctx context.Context, mid int64) string {
conn := d.redis.Get(ctx)
defer conn.Close()
raw, err := redis.Bytes(conn.Do("GET", fmt.Sprintf(model.CacheKeyUserShareToken, mid)))
if err == redis.ErrNil || raw == nil {
rows, err := d.db.Query(ctx, _selectUserShareToken, mid)
if err != nil {
log.Errorv(ctx, log.KV("GetUserShareToken", err))
return ""
}
var token string
for rows.Next() {
rows.Scan(&token)
}
return token
}
return string(raw)
}
// SetUserShareToken .
func (d *Dao) SetUserShareToken(ctx context.Context, mid int64, token string) (int64, error) {
result, err := d.db.Exec(ctx, _insertUserShareToken, mid, token)
fmt.Println(result, err)
if err != nil {
return 0, err
}
if n, _ := result.RowsAffected(); n > 0 {
conn := d.redis.Get(ctx)
defer conn.Close()
conn.Do("SET", fmt.Sprintf(model.CacheKeyUserShareToken, mid), token)
}
return result.LastInsertId()
}

View File

@@ -0,0 +1,386 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/url"
"sort"
"strconv"
"strings"
"time"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/app/interface/bbq/app-bbq/model/grpc"
rec "go-common/app/service/bbq/recsys/api/grpc/v1"
video "go-common/app/service/bbq/video/api/grpc/v1"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
xgrpc "google.golang.org/grpc"
)
const (
_defaultPlatform = "html5"
_playBcNum = 1
_queryList = "select `avid`, `cid`, `svid`, `title`, `mid`, `content`, `pubtime`,`duration`,`tid`,`sub_tid`,`ctime`,`cover_url`,`cover_width`,`cover_height`,`state` from video where svid in (%s)"
_queryRand = "select `svid` from video where state in (4,5) order by mtime desc limit 400;"
_queryStatisticsList = "select `svid`, `play`, `subtitles`, `like`, `share`, `report` from video_statistics where svid in (%s)"
_querySvPlay = "select `svid`,`path`,`resolution_retio`,`code_rate`,`video_code`,`file_size`,`duration` from %s where svid in (%s) and is_deleted = 0 order by code_rate desc"
)
// GetList 获取列表按照db排序按page返回用于推荐的降级
func (d *Dao) GetList(c context.Context, pageSize int64) (result []int64, err error) {
rows, err := d.db.Query(c, _queryRand)
if err != nil {
log.Error("Query(%s) error(%v)", _queryRand, err)
return
}
defer rows.Close()
var list []int64
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.SVID); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag, ok := d.c.Tmap[strconv.FormatInt(sv.TID, 10)]; ok {
sv.Tag = tag
}
list = append(list, sv.SVID)
}
size := len(list)
if size > int(pageSize) {
size = int(pageSize)
}
rand.Seed(time.Now().Unix())
sort.Slice(list, func(i, j int) bool {
return rand.Float32() > 0.5
})
result = list[:size]
return
}
// AttentionRecList 关注页为空时的推荐
func (d *Dao) AttentionRecList(ctx context.Context, size int64, mid int64, buvid string) (svIDs []int64, err error) {
svIDs, err = d.abstractRawRecList(ctx, d.recsysClient.UpsRecService, size, mid, buvid)
log.V(1).Infow(ctx, "log", "get ups rec service", "method", "UpsRecService")
return
}
// RawRecList 获取推荐列表
func (d *Dao) RawRecList(ctx context.Context, size int64, mid int64, buvid string) (svIDs []int64, err error) {
svIDs, err = d.abstractRawRecList(ctx, d.recsysClient.RecService, size, mid, buvid)
log.V(1).Infow(ctx, "log", "get ups rec service", "method", "RecService")
return
}
type recsysFunc func(ctx context.Context, in *rec.RecsysRequest, opts ...xgrpc.CallOption) (*rec.RecsysResponse, error)
// abstractRawRecList 由于访问recsys的方法都是同样的请求&回包,同时过程也一样,因此
func (d *Dao) abstractRawRecList(ctx context.Context, f recsysFunc, size int64, mid int64, buvid string) (svIDs []int64, err error) {
var (
res *rec.RecsysResponse
)
req := &rec.RecsysRequest{
MID: mid,
BUVID: buvid,
Limit: int32(size),
}
if tmp, ok := ctx.(*bm.Context).Get("BBQBase"); ok && tmp != nil {
switch tmp.(type) {
case *v1.Base:
base := tmp.(*v1.Base)
req.App = base.App
req.AppVersion = base.Version
}
}
if tmp, ok := ctx.(*bm.Context).Get("QueryID"); ok && tmp != nil {
switch tmp.(type) {
case string:
req.QueryID = tmp.(string)
}
}
log.Info("Rec请求 params: [%v]", req)
//res, err = d.recsysClient.RecService(ctx, req)
res, err = f(ctx, req)
debug := ctx.(*bm.Context).Request.Header.Get("debug")
if err != nil {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService err [%v]", err)),
)
// 降级(推荐服务已挂)
svIDs = d.GetRandSvList(int(size))
return
} else if len(res.List) == 0 || debug == "1" {
log.Warnv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService return empty [%v]", res.List)),
)
// 降级(推荐接口返回空)
svIDs = d.GetRandSvList(int(size))
return
} else {
num := len(res.List)
if int64(num) != size {
log.Warnv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService return num[%d] not match size[%d]", num, size)),
)
}
for n, sv := range res.List {
if int64(n) > size {
break
}
svIDs = append(svIDs, sv.Svid)
}
}
return
}
// GetVideoDetail 从数据库video中获取svid相应的信息
func (d *Dao) GetVideoDetail(ctx context.Context, svIDs []int64) (list []*model.SvInfo, retIDs []int64, err error) {
list = make([]*model.SvInfo, 0)
num := len(svIDs)
if num == 0 {
return
}
var IDsStr string
for i, id := range svIDs {
if i < num-1 {
IDsStr += strconv.FormatInt(id, 10) + ","
} else {
IDsStr += strconv.FormatInt(id, 10)
}
}
query := fmt.Sprintf(_queryList, IDsStr)
rows, err := d.db.Query(ctx, query)
if err != nil {
log.Error("Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.AVID, &sv.CID, &sv.SVID, &sv.Title, &sv.MID, &sv.Content, &sv.Pubtime, &sv.Duration, &sv.TID, &sv.SubTID, &sv.Ctime, &sv.CoverURL, &sv.CoverWidth, &sv.CoverHeight, &sv.State); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag, ok := d.c.Tmap[strconv.FormatInt(sv.TID, 10)]; ok {
sv.Tag = tag
}
retIDs = append(retIDs, sv.SVID)
list = append(list, sv)
}
return
}
// RawVideos 从数据库批量获取视频信息
func (d *Dao) RawVideos(ctx context.Context, svIDs []int64) (res map[int64]*model.SvInfo, err error) {
res = make(map[int64]*model.SvInfo)
num := len(svIDs)
if num == 0 {
return
}
var IDsStr string
for i, id := range svIDs {
if i < num-1 {
IDsStr += strconv.FormatInt(id, 10) + ","
} else {
IDsStr += strconv.FormatInt(id, 10)
}
}
query := fmt.Sprintf(_queryList, IDsStr)
rows, err := d.db.Query(ctx, query)
if err != nil {
log.Error("Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.AVID, &sv.CID, &sv.SVID, &sv.Title, &sv.MID, &sv.Content, &sv.Pubtime, &sv.Duration, &sv.TID, &sv.SubTID, &sv.Ctime, &sv.CoverURL, &sv.CoverWidth, &sv.CoverHeight, &sv.State); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
res[sv.SVID] = sv
}
return
}
// RawVideoStatistic get video statistics
func (d *Dao) RawVideoStatistic(c context.Context, svids []int64) (res map[int64]*model.SvStInfo, err error) {
const maxIDNum = 20
var (
idStr string
)
res = make(map[int64]*model.SvStInfo)
if len(svids) > maxIDNum {
svids = svids[:maxIDNum]
}
l := len(svids)
for k, svid := range svids {
if k < l-1 {
idStr += strconv.FormatInt(svid, 10) + ","
} else {
idStr += strconv.FormatInt(svid, 10)
}
res[svid] = &model.SvStInfo{}
}
rows, err := d.db.Query(c, fmt.Sprintf(_queryStatisticsList, idStr))
if err != nil {
log.Error("query error(%s)", err.Error())
return
}
defer rows.Close()
for rows.Next() {
ssv := new(model.SvStInfo)
if err = rows.Scan(&ssv.SVID, &ssv.Play, &ssv.Subtitles, &ssv.Like, &ssv.Share, &ssv.Report); err != nil {
log.Error("RawVideoStatistic rows.Scan() error(%v)", err)
return
}
res[ssv.SVID] = ssv
}
cmtCount, _ := d.ReplyCounts(c, svids, model.DefaultCmType)
for id, cmt := range cmtCount {
if _, ok := res[id]; ok {
res[id].Reply = cmt.Count
}
}
return
}
// RawPlayURLs 批量获取cid playurl
func (d *Dao) RawPlayURLs(c context.Context, cids []int64, qn int64, plat string) (res map[int64]*v1.CVideo, err error) {
res = make(map[int64]*v1.CVideo)
var cs string
// transfer cid array to string
l := len(cids)
for i := 0; i < l; i++ {
if i != l-1 {
cs += strconv.FormatInt(cids[i], 10) + ","
} else {
cs += strconv.FormatInt(cids[i], 10)
}
}
ip := metadata.String(c, metadata.RemoteIP)
params := url.Values{}
params.Set("cid", cs)
params.Set("qn", strconv.FormatInt(qn, 10))
params.Set("platform", plat)
var ret struct {
Code int `json:"code"`
Data map[string]map[string]*v1.CVideo `json:"data"`
}
err = d.httpClient.Get(c, d.c.URLs["bvc_batch"], ip, params, &ret)
if err != nil || ret.Data["cids"] == nil {
log.Error("http Get err %v", err)
return
}
for id, v := range ret.Data["cids"] {
var cid int64
cid, err = strconv.ParseInt(id, 10, 64)
if err != nil {
log.Error("strconv.ParseInt err %v", err)
}
if _, ok := res[cid]; !ok {
res[cid] = new(v1.CVideo)
}
res[cid] = v
}
return
}
// RelPlayURLs 相对地址批量获取playurl
func (d *Dao) RelPlayURLs(c context.Context, addrs []string) (res map[string]*grpc.VideoKeyItem, err error) {
res = make(map[string]*grpc.VideoKeyItem)
req := &grpc.RequestMsg{
Keys: addrs,
Backup: uint32(_playBcNum),
Platform: _defaultPlatform,
UIP: metadata.String(c, metadata.RemoteIP),
}
_str, _ := json.Marshal(req)
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("bvc play req (%s)", string(_str))))
r, err := d.bvcPlayClient.ProtobufPlayurl(c, req)
_str, _ = json.Marshal(r)
if err != nil {
log.Error("bvc play err[%v] ret[%s]", err, string(_str))
return
}
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("bvc play ret (%s)", string(_str))))
res = r.Data
return
}
//RawSVBvcKey 批量获取playurl相对地址
func (d *Dao) RawSVBvcKey(c context.Context, svids []int64) (res map[int64][]*model.SVBvcKey, err error) {
var (
tb map[string][]string
rows *sql.Rows
)
res = make(map[int64][]*model.SVBvcKey)
tb = make(map[string][]string)
tName := "video_bvc_%02d"
for _, v := range svids {
if v <= 0 {
continue
}
tbName := fmt.Sprintf(tName, v%100)
tb[tbName] = append(tb[tbName], strconv.FormatInt(v, 10))
}
for k, v := range tb {
query := fmt.Sprintf(_querySvPlay, k, strings.Join(v, ","))
if rows, err = d.db.Query(c, query); err != nil {
log.Errorv(c, log.KV("log", "RawSVBvcKey query sql"), log.KV("err", err))
continue
}
for rows.Next() {
tmp := model.SVBvcKey{}
if err = rows.Scan(&tmp.SVID, &tmp.Path, &tmp.ResolutionRetio, &tmp.CodeRate, &tmp.VideoCode, &tmp.FileSize, &tmp.Duration); err != nil {
log.Errorv(c, log.KV("log", "RawSVBvcKey scan"), log.KV("err", err))
continue
}
res[tmp.SVID] = append(res[tmp.SVID], &tmp)
}
}
return
}
// RelRecList 相关推荐列表
func (d *Dao) RelRecList(ctx context.Context, req *rec.RecsysRequest) (svIDs []int64, err error) {
log.V(1).Infov(ctx, log.KV("log", fmt.Sprintf("RelatedRecService req [%+v]", req)))
res, err := d.recsysClient.RelatedRecService(ctx, req)
if err != nil {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("RelatedRecService err [%v]", err)),
)
return
}
num := len(res.List)
if int32(num) != req.Limit {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("RelatedRecService ret num[%d] not match req size[%d]", num, req.Limit)),
)
}
for n, sv := range res.List {
if int32(n) > req.Limit {
break
}
svIDs = append(svIDs, sv.Svid)
}
log.V(1).Infov(ctx, log.KV("log", fmt.Sprintf("RelatedRecService svid [%+v]", svIDs)))
return
}
// SvDel 视频删除
func (d *Dao) SvDel(c context.Context, in *video.VideoDeleteRequest) (interface{}, error) {
return d.videoClient.VideoDelete(c, in)
}

View File

@@ -0,0 +1,75 @@
package dao
import (
"context"
xsql "database/sql"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_insertVideoStatistics = "insert into video_statistics (`svid`,`play`,`subtitles`,`like`,`share`,`report`) values (?,?,?,?,?,?)"
_incrVideoStatisticsLike = "update video_statistics set `like` = `like` + 1 where `svid` = ?"
_decrVideoStatisticsLike = "update video_statistics set `like` = `like` - 1 where `svid` = ? and `like` > 0"
_insertVideoStatisticsShare = "INSERT INTO video_statistics (`svid`,`share`) VALUES (?,1) ON DUPLICATE KEY UPDATE `share` = `share` + 1;"
_incrVideoStatisticsShare = "UPDATE `video_statistics` SET `share` = `share` + 1 WHERE `svid` = ?;"
_selectVideoStatisticsLike = "select `svid`,`play`,`subtitles`,`like`,`share`,`report` from video_statistics where `svid` = ?"
_videoStatisticsShare = "select `share`+1 from video_statistics where `svid`= ?"
)
//TxIncrVideoStatisticsLike .
func (d *Dao) TxIncrVideoStatisticsLike(tx *sql.Tx, svid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_incrVideoStatisticsLike, svid); err != nil {
log.Error("update video_statistics like err(%v)", err)
return
}
return res.RowsAffected()
}
//TxDecrVideoStatisticsLike .
func (d *Dao) TxDecrVideoStatisticsLike(tx *sql.Tx, svid int64) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_decrVideoStatisticsLike, svid); err != nil {
log.Error("update video_statistics like err(%v)", err)
return
}
return res.RowsAffected()
}
//IncrVideoStatisticsShare .
func (d *Dao) IncrVideoStatisticsShare(ctx context.Context, svid int64) (num int32, err error) {
row := d.db.QueryRow(ctx, _videoStatisticsShare, svid)
err = row.Scan(&num)
if err != nil || num == 0 {
if _, err = d.db.Exec(ctx, _insertVideoStatisticsShare, svid); err != nil {
log.Error("insert video_statistics share error: [%+v] svid: [%d]", err, svid)
}
num = 1
} else if _, err = d.db.Exec(ctx, _incrVideoStatisticsShare, svid); err != nil {
log.Error("update video_statistics share error: [%+v] svid: [%d]", err, svid)
}
return
}
//TxAddVideoStatistics .
func (d *Dao) TxAddVideoStatistics(tx *sql.Tx, videoStatistics *model.VideoStatistics) (num int64, err error) {
var res xsql.Result
if res, err = tx.Exec(_insertVideoStatistics, videoStatistics.SVID, videoStatistics.Play, videoStatistics.Subtitles, videoStatistics.Like, videoStatistics.Share, videoStatistics.Report); err != nil {
log.Error("insert video_statistics err(%v)", err)
return
}
return res.LastInsertId()
}
//RawVideoStatistics .
func (d *Dao) RawVideoStatistics(c context.Context, svid int64) (res *model.VideoStatistics, err error) {
res = new(model.VideoStatistics)
row := d.db.QueryRow(c, _selectVideoStatisticsLike, svid)
err = row.Scan(&res.SVID, &res.Play, &res.Subtitles, &res.Like, &res.Share, &res.Report)
if err == sql.ErrNoRows {
err = nil
}
return
}

View File

@@ -0,0 +1,38 @@
package dao
import (
"context"
"go-common/library/ecode"
"go-common/library/log"
xhttp "net/http"
"net/url"
"strconv"
)
//MergeUploadReq ..
func (d *Dao) MergeUploadReq(c context.Context, rurl string, uploadID string, profile string, svid int64, auth string) (err error) {
var (
req *xhttp.Request
)
rurl = d.c.Upload.HTTPSchema + rurl
param := make(url.Values)
param.Set("uploadId", uploadID)
param.Set("profile", profile)
param.Set("biz_id", strconv.FormatInt(svid, 10))
rurl = rurl + "?" + param.Encode()
req, err = d.httpslowClient.NewRequest(xhttp.MethodPost, rurl, "", param)
if err != nil {
log.Errorw(c, "event", "MergeUploadReq d.httpClient.NewRequest err", "err", err)
err = ecode.UploadFailed
return
}
req.Header.Add("X-Upos-Auth", auth)
if err = d.httpClient.Do(c, req, nil); err != nil {
log.Errorw(c, "event", "MergeUploadReq d.httpClient.Do err", "err", err)
err = ecode.UploadFailed
return
}
return
}

View File

@@ -0,0 +1,99 @@
package dao
import (
"context"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
user "go-common/app/service/bbq/user/api"
accountv1 "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
)
//GetUserBProfile 获取用户全量b站信息
func (d *Dao) GetUserBProfile(c context.Context, mid int64) (res *accountv1.ProfileReply, err error) {
req := &accountv1.MidReq{
Mid: mid,
RealIp: "",
}
res, err = d.accountClient.Profile3(c, req)
return
}
// Login .
func (d *Dao) Login(c context.Context, userBase *user.UserBase) (res *user.UserBase, err error) {
res, err = d.userClient.Login(c, userBase)
if err != nil {
log.Errorv(c, log.KV("log", "login fail"))
}
return
}
// BatchUserInfo 提供批量获取UserInfo的方法
// 由于user service返回的结构和video的回包不同因此这里进行映射返回video-c的结构避免外部使用方多次映射
func (d *Dao) BatchUserInfo(c context.Context, visitorMID int64, MIDs []int64, needDesc, needStat, needFollowState bool) (res map[int64]*v1.UserInfo, err error) {
res = make(map[int64]*v1.UserInfo)
if len(MIDs) == 0 {
return
}
if len(MIDs) > model.BatchUserLen {
err = ecode.BatchUserTooLong
return
}
userReq := &user.ListUserInfoReq{Mid: visitorMID, UpMid: MIDs, NeedDesc: needDesc, NeedStat: needStat, NeedFollowState: needFollowState}
reply, err := d.userClient.ListUserInfo(c, userReq)
if err != nil {
log.Errorv(c, log.KV("log", "get user info fail: req=%s"+userReq.String()))
return
}
for _, userInfo := range reply.List {
newUserInfo := &v1.UserInfo{UserBase: *userInfo.UserBase}
if userInfo.UserStat != nil {
newUserInfo.UserStat = *userInfo.UserStat
}
newUserInfo.FollowState = userInfo.FollowState
res[userInfo.UserBase.Mid] = newUserInfo
}
return
}
//JustGetUserBase 只取UserBase不要其他
func (d *Dao) JustGetUserBase(c context.Context, mids []int64) (res map[int64]*user.UserBase, err error) {
res = make(map[int64]*user.UserBase)
userInfos, err := d.BatchUserInfo(c, 0, mids, false, false, false)
if err != nil {
log.Warnv(c, log.KV("log", "get user info fail"))
return
}
for mid, userInfo := range userInfos {
res[mid] = &userInfo.UserBase
}
return
}
// EditUserBase .
func (d *Dao) EditUserBase(c context.Context, userBase *user.UserBase) (err error) {
_, err = d.userClient.UserEdit(c, userBase)
if err != nil {
log.Warnw(c, "log", "edit user base fail: req="+userBase.String(), "err", err.Error())
return
}
return
}
// PhoneCheck .
func (d *Dao) PhoneCheck(c context.Context, mid int64) (telStatus int32, err error) {
req := &user.PhoneCheckReq{Mid: mid}
res, err := d.userClient.PhoneCheck(c, req)
if err != nil {
log.Errorw(c, "log", "call phone check fail", "err", err, "mid", mid)
return
}
telStatus = res.TelStatus
return
}

View File

@@ -0,0 +1,55 @@
package dao
import (
"context"
"fmt"
user "go-common/app/service/bbq/user/api"
"go-common/library/log"
)
// AddLike .
func (d *Dao) AddLike(c context.Context, mid, upMid, svid int64) (affectedNum int64, err error) {
reply, err := d.userClient.AddLike(c, &user.LikeReq{Mid: mid, UpMid: upMid, Opid: svid})
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("add like fail: mid=%d, up_mid=%d, svid=%d", mid, upMid, svid)))
return
}
affectedNum = reply.AffectedNum
return
}
// CancelLike .
func (d *Dao) CancelLike(c context.Context, mid, upMid, svid int64) (affectedNum int64, err error) {
reply, err := d.userClient.CancelLike(c, &user.LikeReq{Mid: mid, UpMid: upMid, Opid: svid})
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("cancel like fail: mid=%d, up_mid=%d, svid=%d", mid, upMid, svid)))
return
}
affectedNum = reply.AffectedNum
return
}
// CheckUserLike 检测用户是否点赞
func (d *Dao) CheckUserLike(c context.Context, mid int64, svids []int64) (res map[int64]bool, err error) {
res = make(map[int64]bool)
reply, err := d.userClient.IsLike(c, &user.IsLikeReq{Mid: mid, Svids: svids})
if err != nil {
log.Errorv(c, log.KV("log", "get is like info fail"))
return
}
for _, svid := range reply.List {
res[svid] = true
}
return
}
// UserLikeList .
func (d *Dao) UserLikeList(c context.Context, upMid int64, cursorPrev, cursorNext string) (res *user.ListUserLikeReply, err error) {
res, err = d.userClient.ListUserLike(c, &user.ListUserLikeReq{UpMid: upMid, CursorPrev: cursorPrev, CursorNext: cursorNext})
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("get user like list fail: up_mid=%d, cursor_prev=%s, next=%s", upMid, cursorPrev, cursorNext)))
return
}
return
}

View File

@@ -0,0 +1,33 @@
package dao
import (
"context"
"flag"
"fmt"
"go-common/app/interface/bbq/app-bbq/conf"
"testing"
)
var (
d *Dao
)
func init() {
flag.Set("conf", "../cmd/")
flag.Set("conf_name", "test.toml")
flag.Set("app_setting_name", "app_setting.toml")
flag.Set("deploy.env", "uat")
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
}
func TestUserLike(t *testing.T) {
res, err := d.CheckUserLike(context.Background(), 88895104, []int64{74850, 2222})
fmt.Printf("res: %v", res)
if err != nil && len(res) == 0 {
t.Errorf("user like fail: err(%v)", err)
}
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"fmt"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
user "go-common/app/service/bbq/user/api"
"go-common/library/log"
)
// ModifyRelation .
func (d *Dao) ModifyRelation(c context.Context, mid, upMid int64, action int32) (res *user.ModifyRelationReply, err error) {
res, err = d.userClient.ModifyRelation(c, &user.ModifyRelationReq{Mid: mid, UpMid: upMid, Action: action})
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("modify relation fail: mid=%d, up_mid=%d, action=%d", mid, upMid, action)))
}
return
}
// UserRelationList .
func (d *Dao) UserRelationList(c context.Context, userReq *user.ListRelationUserInfoReq, relationType int32) (res *v1.UserRelationListResponse, err error) {
res = new(v1.UserRelationListResponse)
var reply *user.ListUserInfoReply
switch relationType {
case user.Follow:
reply, err = d.userClient.ListFollowUserInfo(c, userReq)
case user.Fan:
reply, err = d.userClient.ListFanUserInfo(c, userReq)
case user.Black:
reply, err = d.userClient.ListBlackUserInfo(c, userReq)
}
if err != nil {
log.Errorv(c, log.KV("log", fmt.Sprintf("user relation list fail: req=%s", userReq.String())))
return
}
res.HasMore = reply.HasMore
for _, v := range reply.List {
userInfo := &v1.UserInfo{UserBase: *v.UserBase, FollowState: v.FollowState, CursorValue: v.CursorValue}
res.List = append(res.List, userInfo)
}
return
}
// FetchFollowList 获取mid的所有关注up主
func (d *Dao) FetchFollowList(c context.Context, mid int64) (upMid []int64, err error) {
res, err := d.userClient.ListFollow(c, &user.ListRelationReq{Mid: mid})
if err != nil {
log.Errorv(c, log.KV("log", "fetch follow list fail"))
return
}
upMid = res.List
return
}

View File

@@ -0,0 +1,68 @@
package dao
import (
"context"
"go-common/app/service/bbq/common"
video "go-common/app/service/bbq/video/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
)
// BatchVideoInfo .
func (d *Dao) BatchVideoInfo(c context.Context, svids []int64) (res map[int64]*video.VideoInfo, err error) {
res = make(map[int64]*video.VideoInfo)
videoReq := &video.ListVideoInfoRequest{SvIDs: svids}
reply, err := d.videoClient.ListVideoInfo(c, videoReq)
if err != nil {
log.Errorw(c, "log", "call video service list vidoe info fail", "req", videoReq.String())
return
}
for _, videoInfo := range reply.List {
res[videoInfo.VideoBase.Svid] = videoInfo
}
log.V(10).Infow(c, "log", "batch video base", "video_info", res)
return
}
// VideoBase 获取单个svid的VideoBase不存在则会返回error
func (d *Dao) VideoBase(c context.Context, mid, svid int64) (res *video.VideoBase, err error) {
videoInfos, err := d.BatchVideoInfo(c, []int64{svid})
if err != nil {
log.Warnw(c, "log", "batch fetch video info fail", "mid", mid, "svid", svid)
return
}
if len(videoInfos) == 0 {
err = ecode.VideoUnExists
log.Warnw(c, "log", "get empty video base", "mid", mid, "svid", svid)
return
}
videoInfo, exists := videoInfos[svid]
if !exists {
err = ecode.VideoUnExists
log.Infow(c, "log", "get empty video base", "mid", mid, "svid", svid)
return
}
res = videoInfo.VideoBase
if res.State == common.VideoStPassReviewReject {
log.Infow(c, "log", "video state in audit", "mid", mid, "svid", svid, "video_base", res)
err = ecode.VideoInAudit
return
}
if !common.IsSvStateAvailable(res.State) {
err = ecode.VideoUnReachable
log.Infow(c, "log", "video state not available", "mid", mid, "svid", svid, "video_base", res)
return
}
if res.State == common.VideoStPassReviewReject && mid != res.Mid {
err = ecode.VideoUnReachable
log.Infow(c, "log", "video state only owner available", "mid", mid, "svid", svid, "video_base", res)
return
}
return
}

View File

@@ -0,0 +1,47 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"cache.go",
"comment.go",
"danmu.go",
"invite_code.go",
"location.go",
"model.go",
"redundance.go",
"report.go",
"search.go",
"sv.go",
"util.go",
"video.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/bbq/user/api:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/bbq/app-bbq/model/grpc:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,39 @@
package model
import (
"go-common/library/time"
)
// AppVersion .
type AppVersion struct {
ID int `json:"id"`
Platform int `json:"platform"`
Name string `json:"name"`
Code int `json:"version"`
Title string `json:"title"`
Content string `json:"content"`
Download string `json:"download"`
MD5 string `json:"md5"`
Size int `json:"file_size"`
Force int `json:"force"`
Status int `json:"status"`
}
// AppResource .
type AppResource struct {
ID int `json:"id"`
Platform int `json:"platform"`
Name string `json:"name"`
Code int `json:"version"`
Download string `json:"download"`
MD5 string `json:"md5"`
Status int `json:"status"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
}
// DynamicEffect .
type DynamicEffect struct {
ID int `json:"rid"`
Name string `json:"rname"`
}

View File

@@ -0,0 +1,10 @@
package model
//缓存key常量
const (
CacheKeyUserShareToken = "user:share:token:%d" //用户分享token
CacheExpireUserShareToken = 600
CacheKeyLastPubtime = "%d:last:pubtime"
CacheExpireLastPubtime = 432000
)

View File

@@ -0,0 +1,259 @@
package model
import (
user "go-common/app/service/bbq/user/api"
xtime "go-common/library/time"
)
const (
//DefaultCmType bbq评论区
DefaultCmType = 23
)
// Int64Bytes implements the Scanner interface.
type Int64Bytes []int64
// Mstr Mstr
type Mstr []string
// ReplyCount /mcounts 返回字段
type ReplyCount struct {
SubState int64 `json:"sub_state"`
Count int64 `json:"count"`
}
// ReplyList /reply 返回字段
type ReplyList struct {
Page struct {
Num int `json:"num"`
Size int `json:"size"`
Count int `json:"count"`
Acount int `json:"acount"`
}
Config struct {
ShowEntry int `json:"showentry"`
ShowAdmin int `json:"showadmin"`
}
Replies []*Reply `json:"replies,omitempty"`
Hots []*Reply `json:"hots,omitempty"`
Upper struct {
MID int64 `json:"mid"`
Top *Reply `json:"top,omitempty"`
}
Top *Reply `json:"top,omitempty"`
Notice *Notice `json:"notice,omitempty"`
BlackList int16 `json:"blacklist"`
Assist int16 `json:"assist"`
}
// Reply .
type Reply struct {
RpID int64 `json:"rpid"`
Oid uint64 `json:"oid"`
Type int8 `json:"type"`
Mid int64 `json:"mid"`
Root int64 `json:"root"`
Parent int64 `json:"parent"`
Dialog int64 `json:"dialog"`
Count int `json:"count"`
RCount int `json:"rcount"`
Floor int `json:"floor"`
State int8 `json:"state"`
FansGrade int8 `json:"fansgrade"`
Attr int8 `json:"attr"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"-"`
// string
RpIDStr string `json:"rpid_str"`
RootStr string `json:"root_str"`
ParentStr string `json:"parent_str"`
DialogStr string `json:"dialog_str"`
// action count, from ReplyAction count
Like int `json:"like"`
Hate int `json:"-"`
Action int8 `json:"action"`
// member info
Member *Member `json:"member,omitempty"`
// other
Content *Content `json:"content,omitempty"`
Replies []*Reply `json:"replies,omitempty"`
Assist int `json:"assist"`
}
// Content .
type Content struct {
RpID int64 `json:"-"`
Message string `json:"message"`
Ats Int64Bytes `json:"ats"`
Topics Mstr `json:"topics"`
IP uint32 `json:"ipi"`
Plat int8 `json:"plat"`
Device string `json:"device"`
Version string `json:"version"`
CTime xtime.Time `json:"-"`
MTime xtime.Time `json:"-"`
// ats member info
Members []*Info `json:"members,omitempty"`
}
// Info Info
type Info struct {
Mid string `json:"mid"`
Name string `json:"uname"`
Sex string `json:"sex"`
Sign string `json:"sign"`
Avatar string `json:"avatar"`
Rank string `json:"rank"`
DisplayRank string `json:"DisplayRank"`
LevelInfo struct {
Cur int `json:"current_level"`
Min int `json:"current_min"`
NowExp int `json:"current_exp"`
NextExp int `json:"next_exp"`
} `json:"level_info"`
Pendant PendantInfo `json:"pendant"`
Nameplate NameplateInfo `json:"nameplate"`
OfficialVerify struct {
Type int `json:"type"`
Desc string `json:"desc"`
} `json:"official_verify"`
Vip struct {
Type int `json:"vipType"`
DueDate int64 `json:"vipDueDate"`
DueRemark string `json:"dueRemark"`
AccessStatus int `json:"accessStatus"`
VipStatus int `json:"vipStatus"`
VipStatusWarn string `json:"vipStatusWarn"`
} `json:"vip"`
BInfo *user.UserBase `json:"b_info,omitempty"`
}
// Member .
type Member struct {
Mid string `json:"mid"`
Name string `json:"uname"`
Sex string `json:"sex"`
Sign string `json:"sign"`
Avatar string `json:"avatar"`
Rank string `json:"rank"`
DisplayRank string `json:"DisplayRank"`
LevelInfo struct {
CurrentLevel int `json:"current_level"`
Min int `json:"current_min"`
NowExp int `json:"current_exp"`
NextExp int `json:"next_exp"`
} `json:"level_info"`
Pendant PendantInfo `json:"pendant"`
Nameplate NameplateInfo `json:"nameplate"`
OfficialVerify struct {
Type int `json:"type"`
Desc string `json:"desc"`
} `json:"official_verify"`
Vip struct {
Type int `json:"vipType"`
DueDate int64 `json:"vipDueDate"`
DueRemark string `json:"dueRemark"`
AccessStatus int `json:"accessStatus"`
VipStatus int `json:"vipStatus"`
VipStatusWarn string `json:"vipStatusWarn"`
} `json:"vip"`
FansDetail *FansDetail `json:"fans_detail,omitempty"`
Following int16 `json:"following"` //是否关注
BInfo *user.UserBase `json:"b_info,omitempty"`
}
// FansDetail .
type FansDetail struct {
UID int64 `json:"uid"`
MedalID int32 `json:"medal_id"` //勋章id
MedalName string `json:"medal_name"` //勋章名称
Score int32 `json:"score"` //当前总经验值
Level int8 `json:"level"` //level等级
Intimacy int32 `json:"intimacy"` //当前亲密度
Status int8 `json:"master_status"` //佩戴状态1:佩戴中0:未佩戴
Received int8 `json:"is_receive"` //是否领取0:未领取1:已领取
}
// NameplateInfo .
type NameplateInfo struct {
Nid int `json:"nid"`
Name string `json:"name"`
Image string `json:"image"`
ImageSmall string `json:"image_small"`
Level string `json:"level"`
Condition string `json:"condition"`
}
// PendantInfo .
type PendantInfo struct {
PID int `json:"pid"`
Name string `json:"name"`
Image string `json:"image"`
Expire int `json:"expire"`
}
//AddRes 添加评论返回字段
type AddRes struct {
RpID int64 `json:"rpid"`
NeedCaptcha bool `json:"need_captcha"`
URL string `json:"url"`
}
//CursorRes /reply/cusor response
type CursorRes struct {
Assist int64 `json:"assist"`
BlackList int64 `json:"blacklist"`
Replies []*Reply `json:"replies,omitempty"`
Upper *Upper `json:"upper,omitempty"`
Cursor *Cursor `json:"cursor,omitempty"`
Config *Config `json:"config,omitempty"`
Hots []*Reply `json:"hots,omitempty"`
Notice *Notice `json:"notice,omitempty"`
Top *Top `json:"top,omitempty"`
}
//SubCursorRes /reply/reply/cusor response
type SubCursorRes struct {
Assist int64 `json:"assist"`
BlackList int64 `json:"blacklist"`
Root *Reply `json:"root,omitempty"`
Upper *Upper `json:"upper,omitempty"`
Cursor *Cursor `json:"cursor,omitempty"`
}
// Notice Notice
type Notice struct {
ID int64 `json:"id"`
Plat int8 `json:"-"`
Condition int8 `json:"-"`
Build int64 `json:"-"`
Title string `json:"title"`
Content string `json:"content"`
Link string `json:"link"`
ClientType string `json:"-"`
}
//Top .
type Top struct {
Admin *Reply `json:"admin,omitempty"`
Upper *Reply `json:"upper,omitempty"`
}
//Config .
type Config struct {
ShowEntry int16 `json:"showentry"`
ShowAdmin int16 `json:"showadmin"`
}
//Cursor .
type Cursor struct {
ACount int64 `json:"all_count"`
CursorRangeMax int64 `json:"max_id"`
CursorRangeMin int64 `json:"min_id"`
Size int64 `json:"size"`
}
//Upper .
type Upper struct {
MID int64 `json:"mid"`
}

View File

@@ -0,0 +1,11 @@
package model
//Danmu ...
type Danmu struct {
ID int64
OID int64
MID int64
Offset int64
Content string
State int8
}

View File

@@ -0,0 +1,54 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
load(
"@io_bazel_rules_go//proto:def.bzl",
"go_proto_library",
)
proto_library(
name = "grpc_proto",
srcs = ["bvc.proto"],
tags = ["automanaged"],
deps = ["@gogo_special_proto//github.com/gogo/protobuf/gogoproto"],
)
go_proto_library(
name = "grpc_go_proto",
compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"],
importpath = "go-common/app/interface/bbq/app-bbq/model/grpc",
proto = ":grpc_proto",
tags = ["automanaged"],
deps = ["@com_github_gogo_protobuf//gogoproto:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [],
embed = [":grpc_go_proto"],
importpath = "go-common/app/interface/bbq/app-bbq/model/grpc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"@com_github_gogo_protobuf//gogoproto:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_gogo_protobuf//sortkeys:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_x_net//context: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"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
syntax="proto3";
package video.vod.playurlbbq;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "grpc";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
message RequestMsg {
repeated string keys = 1;
string platform = 2;
string uip = 3[(gogoproto.customname)= "UIP"];
uint32 uiplong = 4;
uint32 backup = 5;
string uuid = 6[(gogoproto.customname)= "UUID"];
}
message VideoKeyItem {
uint32 etime = 1;
repeated string url = 2[(gogoproto.customname)= "URL"];
}
message ResponseMsg {
uint32 code = 1;
map<string, VideoKeyItem> data = 2;
}
//// Following defines rpc services.
service PlayurlService {
rpc ProtobufPlayurl(RequestMsg) returns (ResponseMsg);
};

View File

@@ -0,0 +1,11 @@
package model
import "go-common/library/time"
// InviteCode 邀请码表
type InviteCode struct {
Code int64 `json:"code"`
DeviceID string `json:"device_id"`
Ctime time.Time `json:"ctime"`
Mtime time.Time `json:"mtime"`
}

View File

@@ -0,0 +1,9 @@
package model
// Location .
type Location struct {
ID int32 `json:"id"`
PID int32 `json:"pid"`
Name string `json:"name"`
Child []*Location `json:"child,omitempty"`
}

View File

@@ -0,0 +1,77 @@
package model
import (
"encoding/json"
"go-common/library/time"
)
// 接口Action定义
const (
ActionRecommend = iota
ActionPlay
ActionLike
ActionCancelLike
ActionFollow
ActionCancelFollow
ActionCommentAdd
ActionCommentLike
ActionCommentReport
ActionFeedList
ActionShare
ActionDanmaku
ActionPlayPause
ActionPushRegister
ActionPushSucced
ActionPushCallback
ActionBlack
ActionCancelBlack
ActionVideoSearch
ActionUserSearch
ActionUserUnLike
)
// App platform
const (
PlatAndroid = iota + 1
PlatIOS
)
const (
// FeedListLen 为feed list中返回的数量
FeedListLen = 10
// SpaceListLen 空间长度
SpaceListLen = 20
// MaxInt64 用于最大int64
MaxInt64 = int64(^uint64(0) >> 1)
// BatchUserLen 批量请求用户信息时最大数量
BatchUserLen = 50
)
const (
//FromBILI video.from bilibili
FromBILI = 0
//FromBBQ video.from bbq
FromBBQ = 1
//FromCMS video.from cms
FromCMS = 2
)
// FeedMark record the struct which returned to app in feed api
type FeedMark struct {
LastSvID int64 `json:"last_svid"`
LastPubtime time.Time `json:"last_pubtime"`
IsRec bool `json:"is_rec"`
}
// CursorValue 用于cursor的定位这里可以当做通用结构使用使用者自己根据需求定义cursor_id的含义
type CursorValue struct {
CursorID int64 `json:"cursor_id"`
CursorTime time.Time `json:"cursor_time"`
}
//HTTPRpcRes ..
type HTTPRpcRes struct {
Code int `json:"code"`
Msg string `json:"message"`
Data json.RawMessage `json:"data"`
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
package model
const (
//TypeVideo video type
TypeVideo = int16(10)
//TypeComment comment type
TypeComment = int16(20)
//TypeDanmu danmu type
TypeDanmu = int16(30)
//TypeUser user type
TypeUser = int16(40)
)
// ReportConfig .
type ReportConfig struct {
Type int16 `json:"type"`
Reasons []int16 `json:"reasons,omitempty"`
}
// ReasonConfig .
type ReasonConfig struct {
ReasonType int16 `json:"reason_type"`
Name string `json:"name"`
}
//Reports .
var Reports = []*ReportConfig{
{
Type: 10,
Reasons: []int16{1, 2, 7, 3, 4, 100},
},
{
Type: 20,
Reasons: []int16{1, 2, 7, 3, 100},
},
{
Type: 30,
Reasons: []int16{1, 2, 7, 3, 100},
},
{
Type: 40,
Reasons: []int16{5, 6},
},
}
//Reasons .
var Reasons = []*ReasonConfig{
{
ReasonType: 1,
Name: "违法违禁",
},
{
ReasonType: 2,
Name: "色情",
},
{
ReasonType: 3,
Name: "赌博诈骗",
},
{
ReasonType: 4,
Name: "血腥暴力",
},
{
ReasonType: 5,
Name: "昵称违规",
},
{
ReasonType: 6,
Name: "头像违规",
},
{
ReasonType: 7,
Name: "低俗",
},
{
ReasonType: 100,
Name: "其他",
},
}
// MapReasons map reasons
var MapReasons = map[int16]string{
1: "违法违禁",
2: "色情",
3: "赌博诈骗",
4: "血腥暴力",
5: "昵称违规",
6: "头像违规",
7: "低俗",
100: "其他",
}
// BiliReasonsMap 主站评论举报类型映射, key bbq value bilibili
var BiliReasonsMap = map[int16]int16{
1: 9,
2: 2,
3: 12,
4: 0,
5: 0,
6: 0,
7: 10,
100: 0,
}

View File

@@ -0,0 +1,78 @@
package model
import "encoding/json"
// SearchBaseReq 搜索基本请求
type SearchBaseReq struct {
KeyWord string `json:"keyword"`
Type string `json:"search_type"`
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
Highlight int64 `json:"highlight"`
Platform string `json:"platform"`
MobiApp string `json:"mobi_app"`
Build string `json:"build"`
Device string `json:"device"`
}
// SearchBaseRet 搜索基本返回
type SearchBaseRet struct {
Code int64 `json:"code"`
NumPages int64 `json:"numPages"`
PageSize int64 `json:"pagesize"`
Seid string `json:"seid"`
Msg string `json:"msg"`
Page int64 `json:"page"`
}
// VideoSearchRet 视频搜索结果
type VideoSearchRet struct {
SearchBaseRet
Result []*VideoSearchResult `json:"result,omitempty"`
}
// VideoSearchResult 视频搜索result
type VideoSearchResult struct {
ID int32 `json:"id"`
Title string `json:"title"`
HitColumns []string `json:"hit_columns,omitempty"`
}
// UserSearchResult 用户搜索结果
type UserSearchResult struct {
ID int64 `json:"id"`
Uname string `json:"uname"`
HitColumns []string `json:"hit_columns"`
}
// RawSearchRes .
type RawSearchRes struct {
Code int `json:"code"`
SeID string `json:"seid"`
Msg string `json:"msg"`
Page int64 `json:"page"`
PageNum int64 `json:"NumPages"`
Res json.RawMessage `json:"Result"`
}
// SugBaseReq Sug基本请求
type SugBaseReq struct {
Term string `json:"term"`
SuggestType string `json:"suggest_type"`
MainVer string `json:"main_ver"`
SugNum int64 `json:"sug_num"`
Highlight int64 `json:"highlight"`
Platform string `json:"platform"`
MobiApp string `json:"mobi_app"`
Build string `json:"build"`
Device string `json:"device"`
}
// RawSugTag SugTag结构
type RawSugTag struct {
Value string `json:"value"`
Ref int64 `json:"ref"`
Name string `json:"name"`
Spid int64 `json:"spid"`
Type string `json:"type"`
}

View File

@@ -0,0 +1,68 @@
package model
import (
"go-common/library/time"
)
//SVBvcKey ..
type SVBvcKey struct {
SVID int64 `json:"svid"`
Path string `json:"path"`
ResolutionRetio string `json:"resolution_retio"`
CodeRate int16 `json:"code_rate"`
VideoCode string `json:"video_code"`
FileSize int64 `json:"file_size"`
Duration int64 `json:"duration"`
}
// ParamScore 打分参数
type ParamScore struct {
SVID int64 `form:"svid" validate:"gt=0,required"`
Score int64 `form:"score" validate:"gt=0,required"`
}
// ParamStatistic 统计参数
type ParamStatistic struct {
SVIDs string `form:"svid" validate:"required"`
}
// SvInfo svList response
type SvInfo struct {
SVID int64 `json:"svid"`
TID int64 `json:"tid"`
SubTID int64 `json:"sub_tid"`
Title string `json:"title"`
Content string `json:"content"`
MID int64 `json:"mid"`
Duration int64 `json:"duration"`
Pubtime time.Time `json:"pubtime"`
Ctime time.Time `json:"ctime"`
AVID int64 `json:"avid"`
CID int64 `json:"cid"`
State int16 `json:"state"`
Original int16 `json:"original"`
From int16 `json:"from"`
VerID int64 `json:"ver_id"`
Ver int64 `json:"ver"`
Tag string `json:"tag"`
CoverURL string `json:"cover_url"`
CoverWidth int `json:"cover_width"`
CoverHeight int `json:"cover_height"`
}
// SvStInfo static info
type SvStInfo struct {
SVID int64 `json:"svid"`
Play int64 `json:"view"` //和上层的play重复因此改成view
Subtitles int64 `json:"subtitles"`
Like int64 `json:"like"`
Share int64 `json:"share"`
Reply int64 `json:"reply"`
Report int64 `json:"report"`
}
// SvTag SvTag struct
type SvTag struct {
SVID int64
TagID int64
}

View File

@@ -0,0 +1,20 @@
package model
import (
"math"
"strconv"
)
// CalNum2SufStr int64转换带后缀字符串K,W,E
func CalNum2SufStr(n int64) string {
var f float64
var s string
if n > 1000 {
f = float64(n) / 1000
s = strconv.FormatFloat(math.Ceil(f), 'f', 0, 64) + "k"
} else {
f = float64(n)
s = strconv.FormatFloat(f, 'f', 0, 64)
}
return s
}

View File

@@ -0,0 +1,11 @@
package model
//VideoStatistics .
type VideoStatistics struct {
SVID int64 `json:"svid"`
Play int64 `json:"play"`
Subtitles int64 `json:"subtitles"`
Like int64 `json:"like"`
Share int64 `json:"share"`
Report int64 `json:"report"`
}

View File

@@ -0,0 +1,63 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"comment.go",
"data.go",
"feed.go",
"http.go",
"location.go",
"notice.go",
"push.go",
"report.go",
"search.go",
"share.go",
"sv.go",
"topic.go",
"upload.go",
"user.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/api/http/v1:go_default_library",
"//app/interface/bbq/app-bbq/conf:go_default_library",
"//app/interface/bbq/app-bbq/model:go_default_library",
"//app/interface/bbq/app-bbq/service:go_default_library",
"//app/interface/bbq/common/auth:go_default_library",
"//app/interface/bbq/common/http:go_default_library",
"//app/service/bbq/notice-service/api/v1:go_default_library",
"//app/service/bbq/topic/api:go_default_library",
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/video/api/grpc/v1:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/antispam:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/trace:go_default_library",
"//vendor/github.com/json-iterator/go: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,49 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
bm "go-common/library/net/http/blademaster"
"strings"
"github.com/json-iterator/go"
)
func appSetting(c *bm.Context) {
args := &v1.AppSettingRequest{}
if err := c.Bind(args); err != nil {
return
}
data, err := srv.AppSetting(c, args)
if err != nil {
c.JSON(nil, err)
return
}
if strings.ToLower(args.Base.Client) == "ios" {
c.JSON(data, err)
return
}
resp := struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int `json:"ttl"`
Data interface{} `json:"data,omitempty"`
}{
Code: 0,
Message: "0",
TTL: 1,
Data: data,
}
str, _ := jsoniter.MarshalToString(resp)
c.String(0, strings.Replace(str, "\\\\", "\\", -1))
}
func appPackage(c *bm.Context) {
args := struct {
Lastest int `json:"lastest" form:"lastest"`
}{}
c.Bind(&args)
c.JSON(srv.AppPackage(c, args.Lastest))
}

View File

@@ -0,0 +1,173 @@
package http
import (
v1 "go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
var (
cmType int64
cmID int64
)
func commentInit(c *bm.Context) {
if cmType = cfg.Comment.Type; cmType == 0 {
cmType = model.DefaultCmType
}
//debug conf
if deid := cfg.Comment.DebugID; deid > 0 {
cmID = deid
}
}
func commentCursor(c *bm.Context) {
dev, _ := c.Get("device")
mid, _ := c.Get("mid")
arg := &v1.CommentCursorReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
device := dev.(*bm.Device)
if mid != nil {
arg.MID = mid.(int64)
} else {
arg.MID = 0
}
//评论区类型overwrite
arg.Type = cmType
if cmID > 0 {
arg.SvID = cmID
}
c.JSON(srv.CommentCursor(c, arg, device))
}
func commentAdd(c *bm.Context) {
dev, _ := c.Get("device")
arg := &v1.CommentAddReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
if len([]rune(arg.Message)) > 96 {
err := ecode.CommentLengthIllegal
c.JSON(nil, err)
return
}
device := dev.(*bm.Device)
arg.AccessKey = c.Request.Form.Get("access_key")
arg.Type = cmType
if cmID > 0 {
arg.SvID = cmID
}
midVal, _ := c.Get("mid")
resp, err := srv.CommentAdd(c, midVal.(int64), arg, device)
c.JSON(resp, err)
// 埋点
if err != nil {
return
}
ext := &struct {
SVID int64
Root int64
Parent int64
Type int64
}{
SVID: arg.SvID,
Root: arg.Root,
Parent: arg.Parent,
Type: arg.Type,
}
uiLog(c, model.ActionCommentAdd, ext)
}
func commentLike(c *bm.Context) {
dev, _ := c.Get("device")
arg := &v1.CommentLikeReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
arg.AccessKey = c.Request.Form.Get("access_key")
device := dev.(*bm.Device)
arg.Type = cmType
if cmID > 0 {
arg.SvID = cmID
}
mid, _ := c.Get("mid")
err := srv.CommentLike(c, mid.(int64), arg, device)
c.JSON(nil, err)
// 埋点
if err != nil {
return
}
ext := &struct {
SVID int64
RPID int64
Action int16
Type int64
}{
SVID: arg.SvID,
RPID: arg.RpID,
Action: arg.Action,
Type: arg.Type,
}
uiLog(c, model.ActionCommentLike, ext)
}
func commentList(c *bm.Context) {
dev, _ := c.Get("device")
mid, _ := c.Get("mid")
arg := &v1.CommentListReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
device := dev.(*bm.Device)
if mid != nil {
arg.MID = mid.(int64)
} else {
arg.MID = 0
}
arg.Type = cmType
if cmID > 0 {
arg.SvID = cmID
}
// 这里是转换成评论那边的平台
if device.RawPlatform == "ios" {
arg.Plat = 3
} else if device.RawPlatform == "android" {
arg.Plat = 2
arg.Pn++
}
c.JSON(srv.CommentList(c, arg, device))
}
func commentSubCursor(c *bm.Context) {
dev, _ := c.Get("device")
arg := &v1.CommentSubCursorReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
device := dev.(*bm.Device)
arg.Type = cmType
if cmID > 0 {
arg.SvID = cmID
}
var mid int64
midValue, _ := c.Get("mid")
if midValue != nil {
mid = midValue.(int64)
}
c.JSON(srv.CommentSubCursor(c, mid, arg, device))
}

View File

@@ -0,0 +1,10 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/model"
bm "go-common/library/net/http/blademaster"
)
func videoPlay(c *bm.Context) {
uiLog(c, model.ActionPlay, nil)
}

View File

@@ -0,0 +1,93 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
// @params FeedListReq
// @router get /bbq/app-bbq/feed/list/
// @response FeedListResponse
func feedList(c *bm.Context) {
arg := &v1.FeedListRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, _ := c.Get("mid")
arg.MID = mid.(int64)
dev, _ := c.Get("device")
arg.Device = dev.(*bm.Device)
b, _ := c.Get("BBQBase")
arg.BUVID = b.(*v1.Base).BUVID
resp, err := srv.FeedList(c, arg)
c.JSON(resp, err)
// 埋点
if err != nil {
return
}
var svidList []int64
for _, v := range resp.List {
svidList = append(svidList, v.SVID)
}
ext := &struct {
Svids []int64
}{
Svids: svidList,
}
uiLog(c, model.ActionFeedList, ext)
}
// @params mid
// @router get /bbq/app-bbq/feed/update_num
// @response FeedUpdateNumResponse
func feedUpdateNum(c *bm.Context) {
mid, _ := c.Get("mid")
c.JSON(srv.FeedUpdateNum(c, mid.(int64)))
}
// @params SpaceSvListRequest
// @router get /bbq/app-bbq/space/sv/list/
// @response SpaceSvListResponse
func spaceSvList(c *bm.Context) {
arg := &v1.SpaceSvListRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, exists := c.Get("mid")
if exists {
arg.MID = mid.(int64)
}
dev, _ := c.Get("device")
arg.Device = dev.(*bm.Device)
arg.Size = model.SpaceListLen
c.JSON(srv.SpaceSvList(c, arg))
}
// @params SpaceSvListRequest
// @router get /bbq/app-bbq/detail/sv/list/
// @response SpaceSvListResponse
func detailSvList(c *bm.Context) {
arg := &v1.SpaceSvListRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, exists := c.Get("mid")
if exists {
arg.MID = mid.(int64)
}
dev, _ := c.Get("device")
arg.Device = dev.(*bm.Device)
// 暂时设置size=3
arg.Size = 3
arg.CursorNext = ""
arg.CursorPrev = ""
c.JSON(srv.SpaceSvList(c, arg))
}

View File

@@ -0,0 +1,258 @@
package http
import (
"fmt"
"go-common/library/ecode"
"go-common/library/net/http/blademaster/middleware/antispam"
"net/http"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/conf"
"go-common/app/interface/bbq/app-bbq/service"
xauth "go-common/app/interface/bbq/common/auth"
chttp "go-common/app/interface/bbq/common/http"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/trace"
)
var (
srv *service.Service
vfy *verify.Verify
authSrv *xauth.BannedAuth
cfg *conf.Config
logger *chttp.UILog
likeAntiSpam *antispam.Antispam
relationAntiSpam *antispam.Antispam
replyAntiSpam *antispam.Antispam
uploadAntiSpam *antispam.Antispam
reportAntiSpam *antispam.Antispam
)
// Init init
func Init(c *conf.Config) {
cfg = c
initAntiSpam(c)
logger = chttp.New(c.Infoc)
srv = service.New(c)
vfy = verify.New(c.Verify)
authSrv = xauth.NewBannedAuth(c.Auth, c.MySQL)
engine := bm.DefaultServer(c.BM)
route(engine)
if err := engine.Start(); err != nil {
log.Error("bm Start error(%v)", err)
panic(err)
}
}
func initAntiSpam(c *conf.Config) {
var antiConfig *antispam.Config
var exists bool
if antiConfig, exists = c.AntiSpam["like"]; !exists {
panic("lose like anti_spam config")
}
relationAntiSpam = antispam.New(antiConfig)
if antiConfig, exists = c.AntiSpam["relation"]; !exists {
panic("lose relation anti_spam config")
}
likeAntiSpam = antispam.New(antiConfig)
if antiConfig, exists = c.AntiSpam["reply"]; !exists {
panic("lose reply anti_spam config")
}
replyAntiSpam = antispam.New(antiConfig)
if antiConfig, exists = c.AntiSpam["upload"]; !exists {
panic("lose upload anti_spam config")
}
uploadAntiSpam = antispam.New(antiConfig)
if antiConfig, exists = c.AntiSpam["report"]; !exists {
panic("lose report anti_spam config")
}
reportAntiSpam = antispam.New(antiConfig)
}
func route(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/bbq/app-bbq", wrapBBQ)
{
//用户登录
g.GET("/user/login", authSrv.User, login)
g.POST("/user/logout", authSrv.Guest, bm.Mobile(), pushLogout)
//用户相关
g.GET("/user/base", authSrv.User, userBase)
// 所有字段都需要携带修改
g.POST("/user/base/edit", authSrv.User, userBaseEdit)
g.POST("/user/like/add", authSrv.User, likeAntiSpam.ServeHTTP, addUserLike)
g.POST("/user/like/cancel", authSrv.User, likeAntiSpam.ServeHTTP, cancelUserLike)
g.GET("/user/like/list", userLikeList)
g.POST("/user/unlike", authSrv.User, likeAntiSpam.ServeHTTP, userUnLike)
g.GET("/user/follow/list", authSrv.Guest, userFollowList)
g.GET("/user/fan/list", authSrv.Guest, userFanList)
g.GET("/user/black/list", authSrv.User, userBlackList)
g.POST("/user/relation/modify", authSrv.User, relationAntiSpam.ServeHTTP, userRelationModify)
g.GET("/search/hot/word", hotWord)
// feed关注列表页
g.GET("/feed/list", authSrv.User, feedList)
// feed关注页红点
g.GET("/feed/update_num", authSrv.User, feedUpdateNum)
// space发布列表页
g.GET("/space/sv/list", authSrv.Guest, spaceSvList)
// space 用户详情况主/客
g.GET("/space/user/profile", authSrv.Guest, spaceUserProfile)
// 详情页up主发布列表
g.GET("/detail/sv/list", authSrv.Guest, detailSvList)
//视频相关
g.GET("/sv/list", authSrv.Guest, bm.Mobile(), svList)
g.GET("/sv/playlist", authSrv.Guest, bm.Mobile(), svPlayList) // playurl过期时候请求
g.GET("/sv/detail", authSrv.Guest, svDetail)
g.GET("/sv/stat", authSrv.Guest, bm.Mobile(), svStatistics)
g.GET("/sv/relate", authSrv.Guest, svRelList)
g.POST("/sv/del", authSrv.User, svDel)
//搜索相关
g.GET("/search/sv", authSrv.Guest, videoSearch)
g.GET("/search/user", authSrv.Guest, userSearch)
g.GET("/search/sug", authSrv.Guest, sug)
g.GET("/search/topic", authSrv.Guest, topicSearch)
//发现页
g.GET("/discovery", authSrv.Guest, discoveryList)
//话题详情页
g.GET("/topic/detail", authSrv.Guest, topicDetail)
// 用户location
g.GET("/location/all", authSrv.User, locationAll)
g.GET("/location", authSrv.User, location)
//图片上传
g.POST("/img/upload", authSrv.User, uploadAntiSpam.ServeHTTP, upload)
// 客户端分享链接
g.GET("/share", authSrv.Guest, shareURL)
g.GET("/share/callback", authSrv.Guest, shareCallback)
// 邀请函接口(内测版,公测删除)内测取消
// g.GET("/invitation/download", invitationDownload)
// App全局设置接口
g.GET("/setting", appSetting)
g.GET("/package", appPackage)
}
//评论组
r := e.Group("/bbq/app-bbq/reply", wrapBBQ, commentInit)
{
//评论相关
r.GET("/cursor", commentCloseRead, authSrv.Guest, commentCursor)
r.POST("/add", commentCloseWrite, authSrv.User, phoneCheck, replyAntiSpam.ServeHTTP, commentAdd)
r.POST("/action", commentCloseWrite, authSrv.User, likeAntiSpam.ServeHTTP, commentLike)
r.GET("/", commentCloseRead, authSrv.Guest, commentList)
r.GET("/reply/cursor", commentCloseRead, authSrv.Guest, commentSubCursor)
}
// 举报接口
report := e.Group("/bbq/app-bbq/report", wrapBBQ)
{
report.GET("/config", authSrv.Guest, bm.Mobile(), reportConfig)
report.POST("/report", authSrv.Guest, bm.Mobile(), reportAntiSpam.ServeHTTP, reportReport)
}
// 播放数据收集
d := e.Group("/bbq/app-bbq/data", wrapBBQ)
{
d.GET("/collect", authSrv.Guest, bm.Mobile(), videoPlay)
}
// 通知中心,需要登录
p := e.Group("/bbq/app-bbq/notice/center", authSrv.User, wrapBBQ)
{
p.GET("/num", noticeNum)
p.GET("/overview", noticeOverview)
p.GET("/list", noticeList)
}
// 推送相关
push := e.Group("/bbq/app-bbq/push", wrapBBQ, authSrv.Guest, bm.Mobile())
{
push.POST("/register", pushRegister)
push.GET("/callback", pushCallback)
}
//视频上传相关
upload := e.Group("/bbq/app-bbq/upload/sv", authSrv.Guest)
{
upload.POST("/preupload", perUpload)
upload.POST("/callback", callBack)
upload.GET("/check", authSrv.User, uploadCheck)
upload.POST("/homeimg", authSrv.User, homeimg)
}
}
func commentCloseWrite(ctx *bm.Context) {
if conf.Conf.Comment.CloseWrite {
ctx.JSON(struct{}{}, ecode.OK)
ctx.Abort()
}
}
func commentCloseRead(ctx *bm.Context) {
if conf.Conf.Comment.CloseRead {
ctx.JSON(struct{}{}, ecode.OK)
ctx.Abort()
}
}
//wrapRes 为返回头添加BBQ自定义字段
func wrapBBQ(ctx *bm.Context) {
chttp.WrapHeader(ctx)
// Base params
req := ctx.Request
base := new(v1.Base)
ctx.Bind(base)
base.BUVID = req.Header.Get("Buvid")
ctx.Set("BBQBase", base)
// QueryID
qid := base.QueryID
if base.QueryID == "" {
tracer, _ := trace.FromContext(ctx.Context)
qid = fmt.Sprintf("%s", tracer)
}
ctx.Set("QueryID", qid)
}
// phoneCheck 进行手机校验
func phoneCheck(ctx *bm.Context) {
midValue, exists := ctx.Get("mid")
if !exists {
ctx.JSON(nil, ecode.NoLogin)
ctx.Abort()
return
}
mid := midValue.(int64)
err := srv.PhoneCheck(ctx, mid)
if err != nil {
ctx.JSON(nil, err)
ctx.Abort()
return
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
func uiLog(ctx *bm.Context, action int, ext interface{}) {
logger.Infoc(ctx, action, ext)
}

View File

@@ -0,0 +1,27 @@
package http
import (
http "go-common/app/interface/bbq/app-bbq/api/http/v1"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func locationAll(c *bm.Context) {
arg := new(http.LocationRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.GetLocaitonAll(c, arg))
}
func location(c *bm.Context) {
arg := new(http.LocationRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.GetLocationChild(c, arg))
}

View File

@@ -0,0 +1,49 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func noticeNum(c *bm.Context) {
var mid int64
midValue, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
mid = midValue.(int64)
c.JSON(srv.GetNoticeNum(c, mid))
}
func noticeOverview(c *bm.Context) {
var mid int64
midValue, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
mid = midValue.(int64)
c.JSON(srv.NoticeOverview(c, mid))
}
func noticeList(c *bm.Context) {
var mid int64
midValue, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
mid = midValue.(int64)
req := &v1.NoticeListRequest{}
if err := c.Bind(req); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
req.Mid = mid
c.JSON(srv.NoticeList(c, req))
}

View File

@@ -0,0 +1,66 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
notice "go-common/app/service/bbq/notice-service/api/v1"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func pushRegister(c *bm.Context) {
args := &v1.PushRegisterRequest{}
if err := c.Bind(args); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
var mid int64
if tmp, ok := c.Get("mid"); ok {
mid = tmp.(int64)
}
buvid := c.Request.Header.Get("Buvid")
req := &notice.UserPushDev{
Mid: mid,
RegisterId: args.RegID,
Buvid: buvid,
Platform: 1,
Sdk: 1,
}
if args.Platform == "ios" {
req.Platform = 2
}
c.JSON(srv.PushRegister(c, req))
// 埋点
uiLog(c, model.ActionPushRegister, args)
}
func pushLogout(c *bm.Context) {
args := &notice.UserPushDev{}
var mid int64
if tmp, ok := c.Get("mid"); ok {
mid = tmp.(int64)
}
args.Mid = mid
buvid := c.Request.Header.Get("Buvid")
args.Buvid = buvid
c.JSON(srv.PushLogout(c, args))
}
func pushCallback(c *bm.Context) {
args := &v1.PushCallbackRequest{}
if err := c.Bind(args); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
var mid int64
if tmp, ok := c.Get("mid"); ok {
mid = tmp.(int64)
}
buvid := c.Request.Header.Get("Buvid")
c.JSON(srv.PushCallback(c, args, mid, buvid))
// 埋点
uiLog(c, model.ActionPushCallback, args)
}

View File

@@ -0,0 +1,38 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func reportConfig(c *bm.Context) {
arg := new(v1.Base)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
res := &v1.ReportConfigResponse{
Report: model.Reports,
Reasons: model.Reasons,
}
c.JSON(res, nil)
}
func reportReport(c *bm.Context) {
arg := new(v1.ReportRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
accessKey := c.Request.Form.Get("access_key")
c.JSON(srv.Report(c, arg, mid.(int64), accessKey))
}

View File

@@ -0,0 +1,105 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
const (
qn1080 = 80
)
//hotWord .
func hotWord(c *bm.Context) {
arg := new(v1.HotWordRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.HotWord(c, arg))
}
func videoSearch(c *bm.Context) {
arg := new(v1.BaseSearchReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
if arg.Qn == 0 {
arg.Qn = qn1080
}
if arg.PageSize == 0 || arg.PageSize > 20 {
arg.PageSize = 20
}
res, err := srv.VideoSearch(c, arg)
c.JSON(res, err)
// 埋点
if err != nil {
return
}
svidList := make([]int64, len(res.List))
for i, v := range res.List {
svidList[i] = v.SVID
}
ext := struct {
Request *v1.BaseSearchReq
Svid []int64
}{
Request: arg,
Svid: svidList,
}
uiLog(c, model.ActionVideoSearch, ext)
}
func userSearch(c *bm.Context) {
arg := new(v1.BaseSearchReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, _ := c.Get("mid")
if mid == nil {
mid = int64(0)
}
if arg.Qn == 0 {
arg.Qn = qn1080
}
if arg.PageSize == 0 || arg.PageSize > 20 {
arg.PageSize = 20
}
res, err := srv.UserSearch(c, mid.(int64), arg)
c.JSON(res, err)
// 埋点
if err != nil {
return
}
midList := make([]int64, len(res.List))
for i, v := range res.List {
midList[i] = v.Mid
}
ext := struct {
Request *v1.BaseSearchReq
MID []int64
}{
Request: arg,
MID: midList,
}
uiLog(c, model.ActionUserSearch, ext)
}
func sug(c *bm.Context) {
arg := new(v1.SugReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
if arg.PageSize == 0 || arg.PageSize > 20 {
arg.PageSize = 20
}
c.JSON(srv.BBQSug(c, arg))
}

View File

@@ -0,0 +1,56 @@
package http
import (
http "go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func shareURL(c *bm.Context) {
var device *bm.Device
if dev, _ := c.Get("device"); dev != nil {
device = dev.(*bm.Device)
}
mid := int64(0)
if v, _ := c.Get("mid"); v != nil {
mid = v.(int64)
}
arg := &http.ShareRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.GetShareURL(c, mid, device, arg))
}
func shareCallback(c *bm.Context) {
dev, _ := c.Get("device")
mid, _ := c.Get("mid")
if mid == nil {
mid = int64(0)
}
arg := &http.ShareCallbackRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
resp, err := srv.ShareCallback(c, mid.(int64), dev.(*bm.Device), arg)
c.JSON(resp, err)
// 埋点
if err != nil {
return
}
ext := struct {
Svid int64
Channel int32
}{
Svid: arg.Svid,
Channel: arg.Channel,
}
uiLog(c, model.ActionShare, ext)
}

View File

@@ -0,0 +1,166 @@
package http
import (
"strconv"
"strings"
v1 "go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
video "go-common/app/service/bbq/video/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// @params SvListReq
// @router get /bbq/app-bbq/sv/list/
// @response VideoResponse
func svList(c *bm.Context) {
b, _ := c.Get("BBQBase")
mid, _ := c.Get("mid")
arg := &v1.SvListReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
arg.Base = b.(*v1.Base)
if mid != nil {
arg.MID = mid.(int64)
} else {
arg.MID = 0
}
//获取deviceID
deviceID := c.Request.Form.Get("device_id")
log.Info("sv list Context [%+v]", c.Request.Header)
log.Info("sv list Base [%+v]", arg.Base)
arg.RemoteIP = metadata.String(c, metadata.RemoteIP)
resp, err := srv.SvList(c, arg.PageSize, arg.MID, arg.Base, deviceID)
c.JSON(resp, err)
// 埋点
if err != nil {
return
}
var svidList []int64
for _, v := range resp {
svidList = append(svidList, v.SVID)
}
ext := struct {
Svids []int64 `json:"svid_list"`
}{
Svids: svidList,
}
uiLog(c, model.ActionRecommend, ext)
}
func svDetail(c *bm.Context) {
mid := int64(0)
if res, ok := c.Get("mid"); ok {
mid = res.(int64)
}
arg := &v1.SvDetailReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.SvDetail(c, arg.SVID, mid))
}
//svStatistics 视频互动数据
func svStatistics(c *bm.Context) {
arg := new(model.ParamStatistic)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
ids := strings.Split(arg.SVIDs, ",")
if len(ids) == 0 {
err := ecode.RequestErr
errors.Wrap(err, "svid解析为空")
return
}
var svids []int64
for _, v := range ids {
id, err := strconv.ParseInt(v, 10, 64)
if err != nil {
errors.Wrap(err, "svid解析错误")
return
}
svids = append(svids, id)
}
var mid int64
if res, ok := c.Get("mid"); ok {
mid = res.(int64)
}
c.JSON(srv.SvStatistics(c, mid, svids))
}
func svPlayList(c *bm.Context) {
dev, _ := c.Get("device")
mid, _ := c.Get("mid")
arg := &v1.PlayListReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
arg.Device = dev.(*bm.Device)
if mid != nil {
arg.MID = mid.(int64)
} else {
arg.MID = 0
}
arg.RemoteIP = metadata.String(c, metadata.RemoteIP)
cidStr := strings.Split(arg.CIDs, ",")
var cids []int64
for _, v := range cidStr {
cid, err := strconv.ParseInt(v, 10, 64)
if err != nil {
errors.Wrap(err, "cid解析错误")
return
}
cids = append(cids, cid)
}
c.JSON(srv.SvCPlays(c, cids, arg.MID))
}
func svRelList(c *bm.Context) {
b, _ := c.Get("BBQBase")
mid, _ := c.Get("mid")
arg := &v1.SvRelReq{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
base := b.(*v1.Base)
if mid != nil {
arg.MID = mid.(int64)
} else {
arg.MID = 0
}
arg.BUVID = base.BUVID
arg.APP = base.App
arg.APPVersion = base.Version
arg.QueryID = base.QueryID
arg.Limit = 15
arg.Offset = 0
c.JSON(srv.SvRelRec(c, arg))
}
func svDel(c *bm.Context) {
arg := new(video.VideoDeleteRequest)
if err := c.Bind(arg); err != nil {
return
}
if mid, _ := c.Get("mid"); mid != nil {
arg.UpMid = mid.(int64)
} else {
arg.UpMid = 0
}
c.JSON(srv.SvDel(c, arg))
}

View File

@@ -0,0 +1,50 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
topic "go-common/app/service/bbq/topic/api"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
func topicDetail(c *bm.Context) {
arg := new(topic.TopicVideosReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid := int64(0)
midValue, exists := c.Get("mid")
if exists {
mid = midValue.(int64)
}
c.JSON(srv.TopicDetail(c, mid, arg))
}
func discoveryList(c *bm.Context) {
arg := new(v1.DiscoveryReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid := int64(0)
midValue, exists := c.Get("mid")
if exists {
mid = midValue.(int64)
}
c.JSON(srv.Discovery(c, mid, arg))
}
func topicSearch(c *bm.Context) {
arg := new(v1.TopicSearchReq)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.TopicSearch(c, arg))
}

View File

@@ -0,0 +1,73 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
func upload(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(v1.ImgUploadRequest)
if err := c.Bind(arg); err != nil {
c.JSON(nil, ecode.ReqParamErr)
return
}
c.JSON(srv.Upload(c, mid.(int64), arg.Type))
}
func perUpload(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
req := new(v1.PreUploadRequest)
if err := c.Bind(req); err != nil {
log.Errorw(c, "event", "bind param err")
return
}
c.JSON(srv.PreUpload(c, req, mid.(int64)))
}
func callBack(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
req := new(v1.CallBackRequest)
if err := c.Bind(req); err != nil {
log.Errorw(c, "event", "bind param err", err, "err")
return
}
c.JSON(srv.CallBack(c, req, mid.(int64)))
}
func uploadCheck(c *bm.Context) {
tmp, exists := c.Get("mid")
if !exists || tmp.(int64) == 0 {
c.JSON(nil, ecode.NoLogin)
return
}
c.JSON(srv.VideoUploadCheck(c, tmp.(int64)))
}
func homeimg(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
req := new(v1.HomeImgRequest)
if err := c.Bind(req); err != nil {
log.Errorw(c, "event", "bind param err", err, "err")
return
}
c.JSON(srv.HomeImg(c, req, mid.(int64)))
}

View File

@@ -0,0 +1,229 @@
package http
import (
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
user "go-common/app/service/bbq/user/api"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
//userBase .
func userBase(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
c.JSON(srv.UserBase(c, mid.(int64)))
}
//spaceUserProfile ...
func spaceUserProfile(c *bm.Context) {
arg := new(v1.SpaceUserProfileRequest)
mid := int64(0)
midValue, exists := c.Get("mid")
if exists {
mid = midValue.(int64)
}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.SpaceUserProfile(c, mid, arg.Upmid))
}
//userEdit
func userBaseEdit(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(user.UserBase)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
arg.Mid = mid.(int64)
c.JSON(srv.UserEdit(c, arg))
}
//addUserLike .
func addUserLike(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(v1.UserLikeAddRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
resp, err := srv.AddUserLike(c, mid.(int64), arg.SVID)
c.JSON(resp, err)
// 埋点
if err == nil {
uiLog(c, model.ActionLike, nil)
}
}
//cancelUserLike .
func cancelUserLike(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(v1.UserLikeCancelRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
resp, err := srv.CancelUserLike(c, mid.(int64), arg.SVID)
c.JSON(resp, err)
// 埋点
if err == nil {
uiLog(c, model.ActionCancelLike, nil)
}
}
//userLikeList .
func userLikeList(c *bm.Context) {
arg := &v1.SpaceSvListRequest{}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
mid, exists := c.Get("mid")
if exists {
arg.MID = mid.(int64)
}
dev, _ := c.Get("device")
arg.Device = dev.(*bm.Device)
arg.Size = model.SpaceListLen
c.JSON(srv.UserLikeList(c, arg))
}
//userFollowList .
func userFollowList(c *bm.Context) {
arg := new(user.ListRelationUserInfoReq)
mid, exists := c.Get("mid")
if exists {
arg.Mid = mid.(int64)
}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.UserFollowList(c, arg))
}
//userFanList .
func userFanList(c *bm.Context) {
arg := new(user.ListRelationUserInfoReq)
mid, exists := c.Get("mid")
if exists {
arg.Mid = mid.(int64)
}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.UserFanList(c, arg))
}
//userBlackList .
func userBlackList(c *bm.Context) {
arg := new(user.ListRelationUserInfoReq)
mid, exists := c.Get("mid")
if exists {
arg.Mid = mid.(int64)
}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
c.JSON(srv.UserBlackList(c, arg))
}
//userRelationModify .
func userRelationModify(c *bm.Context) {
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(v1.UserRelationRequest)
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
var reportAction int
switch arg.Action {
case user.FollowAdd:
reportAction = model.ActionFollow
case user.FollowCancel:
reportAction = model.ActionCancelFollow
case user.BlackAdd:
reportAction = model.ActionBlack
case user.BlackCancel:
reportAction = model.ActionCancelBlack
default:
errors.Wrap(ecode.ReqParamErr, "参数验证失败")
return
}
res, err := srv.ModifyRelation(c, mid.(int64), arg.UPMID, arg.Action)
c.JSON(res, err)
// 埋点
if err == nil && reportAction != 0 {
ext := struct {
UPMID int64 `json:"up_mid"`
}{
UPMID: arg.UPMID,
}
uiLog(c, reportAction, ext)
}
}
//login 登陆
func login(c *bm.Context) {
arg := new(user.UserBase)
mid, exists := c.Get("mid")
if !exists {
c.JSON(nil, ecode.NoLogin)
return
}
if err := c.Bind(arg); err != nil {
errors.Wrap(err, "参数验证失败")
return
}
arg.Mid = mid.(int64)
c.JSON(srv.Login(c, arg))
}
// userUnLike 不感兴趣
func userUnLike(c *bm.Context) {
tmp, exists := c.Get("mid")
if !exists || tmp == nil {
c.JSON(nil, ecode.NoLogin)
return
}
arg := new(v1.UnLikeReq)
if err := c.Bind(arg); err != nil {
return
}
arg.MID = tmp.(int64)
c.JSON(new(interface{}), nil)
// 埋点
uiLog(c, model.ActionUserUnLike, arg)
}

View File

@@ -0,0 +1,64 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"app.go",
"comment.go",
"feed.go",
"location.go",
"notice.go",
"push.go",
"report.go",
"search.go",
"service.go",
"share.go",
"sv.go",
"topic.go",
"upload.go",
"user.go",
],
importpath = "go-common/app/interface/bbq/app-bbq/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/bbq/app-bbq/api/http/v1:go_default_library",
"//app/interface/bbq/app-bbq/conf:go_default_library",
"//app/interface/bbq/app-bbq/dao:go_default_library",
"//app/interface/bbq/app-bbq/model:go_default_library",
"//app/interface/bbq/app-bbq/model/grpc:go_default_library",
"//app/service/bbq/common:go_default_library",
"//app/service/bbq/notice-service/api/v1:go_default_library",
"//app/service/bbq/recsys/api/grpc/v1:go_default_library",
"//app/service/bbq/topic/api:go_default_library",
"//app/service/bbq/user/api:go_default_library",
"//app/service/bbq/video/api/grpc/v1:go_default_library",
"//library/conf/env:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/Dai0522/go-hash/murmur3:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
],
)
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,80 @@
package service
import (
"context"
"time"
"github.com/json-iterator/go"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/conf"
)
// AppSetting .
func (s *Service) AppSetting(c context.Context, arg *v1.AppSettingRequest) (resp *v1.AppSettingResponse, err error) {
plat := 1
if arg.Base.Client == "ios" {
plat = 2
}
appVerison, err := s.dao.FetchNewAppVersion(c, plat, arg.VersionCode)
newVersion := uint8(0)
if err == nil && appVerison.ID > 0 {
newVersion = uint8(1)
}
// TODO暂时先这样快速上线以后改(也许。。)
ver := 1
if arg.Base.Client == "ios" && arg.VersionCode > 100000 {
ver = 2
} else if arg.Base.Client == "android" && arg.VersionCode > 101000 {
ver = 2
}
appResource, err := s.dao.FetchAppResource(c, plat, ver)
current := time.Now().Unix()
pubSetting := make(map[string]interface{})
b, _ := jsoniter.Marshal(conf.App)
jsoniter.Unmarshal(b, &pubSetting)
for _, v := range appResource {
if v.StartTime.Time().Unix() < current && current < v.EndTime.Time().Unix() {
pubSetting["dynamic_effect"] = v.ID
}
}
resp = &v1.AppSettingResponse{
Public: pubSetting,
Update: &v1.AppUpdate{
NewVersion: newVersion,
Info: appVerison,
},
Resources: appResource,
}
return
}
// AppPackage .
func (s *Service) AppPackage(c context.Context, lastest int) (resp []*v1.AppPackage, err error) {
resp = make([]*v1.AppPackage, 0)
if lastest > 0 {
result, e := s.dao.FetchNewAppVersion(c, 1, 0)
resp = append(resp, &v1.AppPackage{
ID: int64(result.ID),
Platform: uint8(result.Platform),
VersionName: result.Name,
VersionCode: uint32(result.Code),
Title: result.Title,
Content: result.Content,
Download: result.Download,
MD5: result.MD5,
Size: int32(result.Size),
Force: uint8(result.Force),
Status: uint8(result.Status),
})
err = e
} else {
resp, err = s.dao.FetchAppPackage(c)
}
return
}

View File

@@ -0,0 +1,292 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/dao"
"go-common/app/interface/bbq/app-bbq/model"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"net/url"
"strconv"
)
//CommentSubCursor 游标评论列表
func (s *Service) CommentSubCursor(c context.Context, mid int64, arg *v1.CommentSubCursorReq, device *bm.Device) (res *model.SubCursorRes, err error) {
res = new(model.SubCursorRes)
if _, err = s.dao.VideoBase(c, mid, arg.SvID); err != nil {
log.Warnw(c, "log", "get video base fail", "svid", arg.SvID)
return
}
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"sort": arg.Sort,
"root": arg.Root,
}
if len(arg.Access) != 0 {
req["access_key"] = arg.Access
}
if arg.RpID != 0 {
req["rpid"] = arg.RpID
}
if arg.Size != 0 {
req["size"] = arg.Size
}
if arg.MinID > 0 && arg.MaxID > 0 {
err = ecode.ParamInvalid
return
}
if arg.MinID != 0 {
req["min_id"] = arg.MinID
}
if arg.MaxID != 0 {
req["max_id"] = arg.MaxID
}
res, err = s.dao.ReplySubCursor(c, req)
return
}
//CommentList 游标评论列表
func (s *Service) CommentList(c context.Context, arg *v1.CommentListReq, device *bm.Device) (res *model.ReplyList, err error) {
res = new(model.ReplyList)
if _, err = s.dao.VideoBase(c, arg.MID, arg.SvID); err != nil {
log.Warnw(c, "log", "get video base fail", "svid", arg.SvID)
return
}
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"sort": arg.Sort,
"nohot": arg.NoHot,
}
if len(arg.Access) != 0 {
req["access_key"] = arg.Access
}
if arg.Pn != 0 {
req["pn"] = arg.Pn
}
if arg.Ps != 0 {
req["ps"] = arg.Ps
}
if device.Build != 0 {
req["build"] = arg.Build
}
if device.RawPlatform != "" {
req["plat"] = arg.Plat
}
if device.RawMobiApp != "" {
req["mobi_app"] = device.RawMobiApp
}
res, err = s.dao.ReplyList(c, req)
return
}
//CommentCursor 游标评论列表
func (s *Service) CommentCursor(c context.Context, arg *v1.CommentCursorReq, device *bm.Device) (res *model.CursorRes, err error) {
res = new(model.CursorRes)
if _, err = s.dao.VideoBase(c, arg.MID, arg.SvID); err != nil {
log.Warnw(c, "log", "get video base fail", "svid", arg.SvID)
return
}
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"sort": arg.Sort,
"max_id": arg.MaxID,
"min_id": arg.MinID,
"size": arg.Size,
}
if arg.RpID != 0 {
req["rpid"] = arg.RpID
}
if len(arg.Access) != 0 {
req["access_key"] = arg.Access
}
res, err = s.dao.ReplyCursor(c, req)
return
}
//CommentAdd 发表评论评论服务
func (s *Service) CommentAdd(c context.Context, mid int64, arg *v1.CommentAddReq, device *bm.Device) (res *model.AddRes, err error) {
res = new(model.AddRes)
// 屏蔽词
level, filterErr := s.dao.Filter(c, arg.Message, dao.FilterAreaReply)
if filterErr != nil {
log.Errorv(c, log.KV("log", "filter fail"))
} else if level >= dao.FilterLevel {
err = ecode.CommentFilterErr
log.Warnv(c, log.KV("log", fmt.Sprintf("content filter fail: content=%s, level=%d", arg.Message, level)))
return
}
var upMid int64
videoBase, err := s.dao.VideoBase(c, mid, arg.SvID)
if err != nil {
log.Warnw(c, "log", "get video base fail", "svid", arg.SvID)
return
}
upMid = videoBase.Mid
parentMid := upMid
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"message": arg.Message,
"access_key": arg.AccessKey,
}
if arg.At != "" {
req["at"] = arg.At
}
if arg.Parent != 0 {
req["parent"] = arg.Parent
req["root"] = arg.Root
} else if arg.Root != 0 {
req["root"] = arg.Root
}
if arg.Plat != 0 {
req["plat"] = arg.Plat
}
if arg.Device != "" {
req["device"] = arg.Plat
}
if arg.Code != "" {
req["code"] = arg.Code
}
res, err = s.dao.ReplyAdd(c, req)
//wrap error
switch ecode.Cause(err).Code() {
case ecode.ReplyDeniedAsCaptcha.Code():
err = ecode.CommentForbidden
case ecode.ReplyContentOver.Code():
err = ecode.CommentLengthIllegal
}
// 推送评论给通知中心
if err == nil {
title := "评论了你的作品"
bizType := int32(notice.NoticeBizTypeSv)
rootID := res.RpID
if arg.Parent != 0 {
title = "回复了你的评论"
bizType = int32(notice.NoticeBizTypeComment)
rootID = arg.Parent
// get root comment's owner
list, tmpErr := s.dao.ReplyMinfo(c, arg.SvID, []int64{arg.Parent})
if tmpErr != nil || len(list) == 0 {
log.Warnv(c, log.KV("log", "get root reply rpid info fail"), log.KV("rsp_size", len(list)))
return
}
reply, exists := list[arg.Parent]
if !exists {
log.Errorv(c, log.KV("log", "not found reply rpid's info"))
return
} else if reply.Mid == 0 {
log.Errorv(c, log.KV("log", "reply rpid's owner mid=0"))
return
}
parentMid = reply.Mid
}
if parentMid == mid {
log.V(1).Infov(c, log.KV("log", "action_mid=mid"), log.KV("mid", mid))
return
}
urlVal := make(url.Values)
urlVal.Add("svid", strconv.FormatInt(arg.SvID, 10))
urlVal.Add("rootid", strconv.FormatInt(rootID, 10))
urlVal.Add("rpid", strconv.FormatInt(res.RpID, 10))
jumpURL := fmt.Sprintf("qing://commentdetail?%s", urlVal.Encode())
notice := &notice.NoticeBase{
Mid: parentMid, ActionMid: mid, SvId: arg.SvID, NoticeType: notice.NoticeTypeComment, Title: title, Text: arg.Message,
JumpUrl: jumpURL, BizType: bizType, BizId: res.RpID}
tmpErr := s.dao.CreateNotice(c, notice)
if tmpErr != nil {
log.Error("create comment notice fail: notice_msg=%s", notice.String())
}
}
return
}
//CommentLike 评论点赞服务
func (s *Service) CommentLike(c context.Context, mid int64, arg *v1.CommentLikeReq, device *bm.Device) (err error) {
if _, err = s.dao.VideoBase(c, mid, arg.SvID); err != nil {
log.Warnw(c, "log", "get video base fail", "svid", arg.SvID)
return
}
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"rpid": arg.RpID,
"action": arg.Action,
"access_key": arg.AccessKey,
}
err = s.dao.ReplyLike(c, req)
if ecode.Cause(err).Code() == ecode.ReplyForbidAction.Code() {
err = ecode.CommentForbidLike
return
}
// TODO: 推送评论给通知中心
if arg.Action == 1 && err == nil {
// get root comment's owner
list, tmpErr := s.dao.ReplyMinfo(c, arg.SvID, []int64{arg.RpID})
if tmpErr != nil || len(list) == 0 {
log.Warnv(c, log.KV("log", "get root rpid info fail"))
return
}
reply, exists := list[arg.RpID]
if !exists {
log.Errorv(c, log.KV("log", "not found reply rpid's info"))
return
} else if reply.Mid == 0 {
log.Errorv(c, log.KV("log", "reply rpid's owner mid=0"))
return
}
parentMid := reply.Mid
if parentMid == mid {
log.V(1).Infov(c, log.KV("log", "action_mid=mid"), log.KV("mid", mid))
return
}
text := ""
if reply.Content != nil {
text = reply.Content.Message
}
title := "点赞了你的评论"
bizType := int32(notice.NoticeBizTypeComment)
notice := &notice.NoticeBase{
Mid: parentMid, ActionMid: mid, SvId: arg.SvID, NoticeType: notice.NoticeTypeLike, Title: title, Text: text,
BizType: bizType, BizId: arg.RpID}
tmpErr = s.dao.CreateNotice(c, notice)
if tmpErr != nil {
log.Errorv(c, log.KV("log", "create like notice fail: notice_msg="+notice.String()+", err="+err.Error()))
return
}
}
return
}
//CommentReport 评论举报服务
func (s *Service) CommentReport(c context.Context, arg *v1.CommentReportReq) (err error) {
req := map[string]interface{}{
"oid": arg.SvID,
"type": arg.Type,
"rpid": arg.RpID,
"reason": arg.Reason,
"access_key": arg.AccessKey,
}
if arg.Content != "" {
req["content"] = arg.Content
}
err = s.dao.ReplyReport(c, req)
return
}

View File

@@ -0,0 +1,324 @@
package service
import (
"context"
"encoding/json"
"fmt"
"go-common/app/service/bbq/common"
"time"
v1 "go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
video "go-common/app/service/bbq/video/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/trace"
xtime "go-common/library/time"
)
// FeedUpdateNum 关注页红点
func (s *Service) FeedUpdateNum(c context.Context, mid int64) (res v1.FeedUpdateNumResponse, err error) {
// 0. 获取关注链
followedMid, err := s.dao.FetchFollowList(c, mid)
if err != nil {
log.Errorv(c, log.KV("log", "fetch follow fail"), log.KV("mid", mid), log.KV("err", err))
return
}
if len(followedMid) == 0 {
res.Num = 0
log.V(1).Infov(c, log.KV("log", "no_follow"), log.KV("mid", mid))
return
}
// 1. 获取mid上次浏览点
pubTime, _ := s.dao.GetMIDLastPubtime(c, mid)
// 2. 获取新的视频
newlySvID, err := s.dao.FetchAvailableOutboxList(c, common.FeedStates, followedMid, false, 0, xtime.Time(pubTime), 1)
if err != nil {
log.Errorv(c, log.KV("log", "fetch available outbox list fail"), log.KV("mid", mid), log.KV("pubTime", pubTime))
return
}
// 3. form rsp
if len(newlySvID) == 0 {
res.Num = 0
} else {
res.Num = 1
}
return
}
// FeedList 关注页短视屏列表
func (s *Service) FeedList(c context.Context, req *v1.FeedListRequest) (res *v1.FeedListResponse, err error) {
var (
mid = req.MID
markStr = req.Mark
)
res = new(v1.FeedListResponse)
res.List = make([]*v1.SvDetail, 0)
res.RecList = make([]*v1.SvDetail, 0)
// 0.前期校验
// 解析mark获取last_svid
var mark model.FeedMark
isFirstPage := false
// 关注up的总视频列表是否空
needRecList := false
if len(markStr) != 0 {
var markData = []byte(markStr)
err = json.Unmarshal(markData, &mark)
if err != nil {
err = ecode.ReqParamErr
log.Errorv(c, log.KV("log", "mark_unmarshal"), log.KV("mark", markStr))
return
}
log.V(1).Infov(c, log.KV("mark", markData))
}
if mark.IsRec {
needRecList = true
} else if mark.LastSvID == 0 {
isFirstPage = true
mark.LastSvID = model.MaxInt64
mark.LastPubtime = xtime.Time(time.Now().Unix())
// 更新最新浏览svid
s.dao.SetMIDLastPubtime(c, mid, int64(mark.LastPubtime))
}
// 1. 需要获取详情的svid列表
var svids []int64
if !needRecList {
// 1.获取关注链
var followedMid []int64
followedMid, err = s.dao.FetchFollowList(c, mid)
if err != nil {
log.Errorv(c, log.KV("log", "fetch_follow"), log.KV("mid", mid))
return
}
// 无关注人,直接返回
if len(followedMid) == 0 {
res.HasMore = false
needRecList = true
log.V(1).Infov(c, log.KV("log", "no_follow"), log.KV("mid", mid))
} else {
// 2.获取svid列表
svids, err = s.dao.FetchAvailableOutboxList(c, common.FeedStates, followedMid, true, mark.LastSvID, mark.LastPubtime, model.FeedListLen)
if err != nil {
log.Warnw(c, "log", "fetch available outbox list fail")
return
}
// 为了保护列表,所以/2后面切换获取逻辑就可以去掉了
if len(svids) < model.FeedListLen/2 {
res.HasMore = false
// 关注人了,但是这些人没有发布过视频
if len(svids) == 0 && isFirstPage {
needRecList = true
}
} else {
res.HasMore = true
}
}
}
// 如果第一页就是empty或者mark携带了is_rec需要为用户推荐一些视频
if needRecList {
svids, err = s.dao.AttentionRecList(c, model.FeedListLen, mid, req.BUVID)
if err != nil {
log.Warnw(c, "log", "get attention feed rec fail")
return
}
}
// 2.获取sv信息列表
var list []*v1.SvDetail
// 2.0 获取sv详情
detailMap, err := s.getVideoDetail(c, req.MID, req.Qn, req.Device, svids, true)
if err != nil {
log.Warnv(c, log.KV("log", "get video detail fail"))
return
} else if len(detailMap) == 0 {
log.Warnv(c, log.KV("log", "feed list empty"), log.KV("svid_num", len(svids)))
return
}
// 2.1 获取热评
hots, hotErr := s.dao.ReplyHot(c, mid, svids)
if hotErr != nil {
log.Warnv(c, log.KV("log", "get hot reply fail"))
}
log.V(1).Infov(c, log.KV("log", "get_video_detail"), log.KV("req_size", len(svids)),
log.KV("rsp_size", len(detailMap)))
for _, svID := range svids {
v, exists := detailMap[svID]
if exists {
if hots, ok := hots[svID]; ok {
v.HotReply.Hots = hots
}
list = append(list, v)
} else {
log.Warnv(c, log.KV("log", "sv_not_found"), log.KV("mid", mid), log.KV("svid", svID))
}
}
// 3. 组装回包判断往哪个list塞数据
var nextMark model.FeedMark
if needRecList {
res.RecList = list
nextMark.IsRec = true
} else {
res.List = list
if len(list) > 0 && res.HasMore {
nextMark.LastSvID = list[len(list)-1].SVID
nextMark.LastPubtime = list[len(list)-1].Pubtime
}
}
jsonStr, _ := json.Marshal(nextMark) // marshal的时候相信库函数不做err判断
res.Mark = string(jsonStr)
return
}
// SpaceSvList 个人空间视频列表
func (s *Service) SpaceSvList(c context.Context, req *v1.SpaceSvListRequest) (res *v1.SpaceSvListResponse, err error) {
// 0.前期校验
// 这里就不校验up主是否存在
res = new(v1.SpaceSvListResponse)
res.List = make([]*v1.SvDetail, 0)
upMid := req.UpMid
if upMid == 0 {
err = ecode.ReqParamErr
log.Errorv(c, log.KV("log", "up mid is 0"), log.KV("up_mid", 0))
return
}
// parseCursor
cursor, cursorNext, err := parseCursor(req.CursorPrev, req.CursorNext)
if err != nil {
return
}
// 1. 如果是主人态其第一页则进行额外prepare_list
if req.MID == req.UpMid && len(req.CursorNext) == 0 && len(req.CursorPrev) == 0 {
prepareRes, tmpErr := s.videoClient.ListPrepareVideo(c, &video.PrepareVideoRequest{Mid: req.MID})
if tmpErr != nil {
log.Warnw(c, "log", "get prepare video fail", "mid", req.MID)
} else {
res.PrepareList = prepareRes.List
}
}
// 2. 获取svid列表
states := common.SpaceFanStates
if req.MID == req.UpMid {
states = common.SpaceOwnerStates
}
svids, err := s.dao.FetchAvailableOutboxList(c, states, []int64{upMid}, cursorNext, cursor.CursorID, cursor.CursorTime, req.Size)
if err != nil {
log.Infov(c, log.KV("log", "fetch_outbox_list"), log.KV("error", err))
return
}
if len(svids) < req.Size/2 {
res.HasMore = false
if len(svids) == 0 {
return
}
} else {
res.HasMore = true
}
// 3.获取sv详情
detailMap, err := s.svInfos(c, svids, req.MID, true)
if err != nil {
log.Errorv(c, log.KV("log", "get video detail fail"))
return
} else if len(detailMap) == 0 {
log.Warnv(c, log.KV("log", "feed list empty"), log.KV("svid_num", len(svids)))
return
}
for _, svID := range svids {
item, exists := detailMap[svID]
if exists {
sv := new(v1.SvDetail)
sv.VideoResponse = *item
res.List = append(res.List, sv)
} else {
log.Warnv(c, log.KV("log", "sv_not_found"), log.KV("svid", svID))
}
}
// query id
tracer, _ := trace.FromContext(c)
queryID := fmt.Sprintf("%s", tracer)
// 4. 后处理为每个item添加cursor值
var itemCursor model.CursorValue
for _, item := range res.List {
itemCursor.CursorID = item.SVID
itemCursor.CursorTime = item.Pubtime
jsonStr, _ := json.Marshal(itemCursor) // marshal的时候相信库函数不做err判断
item.CursorValue = string(jsonStr)
item.QueryID = queryID
}
return
}
// getVideoDetail 返回SvDetail的列表返回的list顺序和svids顺序一致但不保证svid都能出现在list中
func (s *Service) getVideoDetail(c context.Context, mid int64, qn int64, device *bm.Device, svids []int64, fullVersion bool) (res map[int64]*v1.SvDetail, err error) {
res = make(map[int64]*v1.SvDetail)
if len(svids) == 0 {
return
}
// 拉取视频详情
svRes, err := s.svInfos(c, svids, mid, true)
if err != nil {
log.Errorv(c, log.KV("log", "get video detail from dao fail"))
return
}
log.V(1).Infov(c, log.KV("log", "get_video_detail"), log.KV("req_size", len(svids)), log.KV("rsp_size", len(svRes)))
if len(svRes) == 0 {
log.Warnv(c, log.KV("log", "get_video_detail_empty"), log.KV("req_size", len(svids)), log.KV("rsp_size", len(svRes)))
return
}
// 开始组装回包
currentTs := time.Now().Unix()
for svid, svInfo := range svRes {
sv := new(v1.SvDetail)
// 组装video基础信息
sv.VideoResponse = *svInfo
if currentTs > int64(sv.Pubtime) {
sv.ElapsedTime = currentTs - int64(sv.Pubtime)
}
res[svid] = sv
}
return
}
// parseCursor从cursor_prev和cursor_next判断请求的方向以及生成cursor
func parseCursor(cursorPrev string, cursorNext string) (cursor model.CursorValue, directionNext bool, err error) {
// 判断是向前还是向后查询
directionNext = true
cursorStr := cursorNext
if len(cursorNext) == 0 && len(cursorPrev) > 0 {
directionNext = false
cursorStr = cursorPrev
}
// 解析cursor中的cursor_id
if len(cursorStr) != 0 {
var cursorData = []byte(cursorStr)
err = json.Unmarshal(cursorData, &cursor)
if err != nil {
err = ecode.ReqParamErr
return
}
}
// 第一次请求的时候携带的svid=0需要转成max传给dao层
if directionNext && cursor.CursorID == 0 {
cursor.CursorID = model.MaxInt64
cursor.CursorTime = xtime.Time(time.Now().Unix())
}
return
}

View File

@@ -0,0 +1,86 @@
package service
import (
"context"
http "go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
)
// GetLocaitonAll .
func (s *Service) GetLocaitonAll(c context.Context, arg *http.LocationRequest) (*http.LocationResponse, error) {
result := &http.LocationResponse{}
m, err := s.dao.GetLocationAll(c)
if err != nil {
return result, err
}
var coutries []*model.Location
for _, item := range (*m)[arg.PID] {
coutry := &model.Location{
ID: item.ID,
PID: item.PID,
Name: item.Name,
}
var provices []*model.Location
for _, v := range (*m)[item.ID] {
provice := &model.Location{
ID: v.ID,
PID: v.PID,
Name: v.Name,
}
var citys []*model.Location
for _, u := range (*m)[v.ID] {
city := &model.Location{
ID: u.ID,
PID: u.PID,
Name: u.Name,
}
var area []*model.Location
for _, w := range (*m)[u.ID] {
var child []*model.Location
area = append(area, &model.Location{
ID: w.ID,
PID: w.PID,
Name: w.Name,
Child: child,
})
}
city.Child = area
citys = append(citys, city)
}
provice.Child = citys
provices = append(provices, provice)
}
coutry.Child = provices
coutries = append(coutries, coutry)
}
result.List = coutries
return result, err
}
// GetLocationChild .
func (s *Service) GetLocationChild(c context.Context, arg *http.LocationRequest) (*http.LocationResponse, error) {
result := &http.LocationResponse{}
m, err := s.dao.GetLocationChild(c, arg.PID)
if err != nil {
return result, err
}
var provices []*model.Location
for _, v := range (*m)[arg.PID] {
var child []*model.Location
provice := &model.Location{
ID: v.ID,
PID: v.PID,
Name: v.Name,
Child: child,
}
provices = append(provices, provice)
}
result.List = provices
return result, err
}

View File

@@ -0,0 +1,170 @@
package service
import (
"context"
"encoding/json"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/conf"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/app/service/bbq/common"
notice "go-common/app/service/bbq/notice-service/api/v1"
"go-common/library/ecode"
"go-common/library/log"
)
var noticeTypes map[int32]bool
const (
_noticeTypeLike = 1
_noticeTypeComment = 2
_noticeTypeFan = 3
_noticeTypeSysMsg = 4
)
func init() {
noticeTypes = map[int32]bool{
_noticeTypeLike: true,
_noticeTypeComment: true,
_noticeTypeFan: true,
_noticeTypeSysMsg: true,
}
}
// GetNoticeNum 通知红点
func (s *Service) GetNoticeNum(ctx context.Context, mid int64) (res *v1.NoticeNumResponse, err error) {
res = new(v1.NoticeNumResponse)
unreadList, err := s.dao.GetNoticeUnread(ctx, mid)
if err != nil {
log.Warnv(ctx, log.KV("log", "get notice unread fail: err="+err.Error()))
return
}
var redDot int64
for _, v := range unreadList {
redDot += v.UnreadNum
}
res.RedDot = redDot
return
}
// NoticeOverview 通知中心概述
func (s *Service) NoticeOverview(ctx context.Context, mid int64) (res *v1.NoticeOverviewResponse, err error) {
res = new(v1.NoticeOverviewResponse)
unreadList, err := s.dao.GetNoticeUnread(ctx, mid)
if err != nil {
log.Warnv(ctx, log.KV("log", "get notice unread fail: err="+err.Error()))
return
}
unreadMap := make(map[int32]*notice.UnreadItem)
for _, item := range unreadList {
unreadMap[item.NoticeType] = item
}
for _, v := range conf.Conf.Notices {
var unreadNum int64
if unreadInfo, exists := unreadMap[v.NoticeType]; exists {
unreadNum = unreadInfo.UnreadNum
}
res.Notices = append(res.Notices, &v1.NoticeOverview{NoticeType: v.NoticeType, ShowType: v.ShowType, Name: v.Name, UnreadNum: unreadNum})
}
return
}
// NoticeList 请求通知列表,组装成通知消息列表
func (s *Service) NoticeList(ctx context.Context, req *v1.NoticeListRequest) (res *v1.NoticeListResponse, err error) {
res = new(v1.NoticeListResponse)
// 0. 校验请求合法性
if _, exists := noticeTypes[req.NoticeType]; !exists {
return nil, ecode.NoticeTypeErr
}
cursor, _, err := parseCursor("", req.CursorNext)
if err != nil {
log.Errorv(ctx, log.KV("log", "parse cursor fail: cursor_next="+req.CursorNext))
return
}
// 1. 请求notice列表
list, err := s.dao.NoticeList(ctx, req.NoticeType, req.Mid, cursor.CursorID)
if err != nil {
return
}
// 2. 根据notice请求相应业务方
// 2.0 整理notice列表
var actionMIDs []int64 // 不是map有可能重复
var svIDs []int64
for _, item := range list {
if item.ActionMid != 0 {
actionMIDs = append(actionMIDs, item.ActionMid)
}
if item.SvId != 0 {
svIDs = append(svIDs, item.SvId)
}
}
needUserInfo := true
needFollowState := false
needSvInfo := false
switch req.NoticeType {
case _noticeTypeLike:
needSvInfo = true
case _noticeTypeComment:
needSvInfo = true
case _noticeTypeFan:
needFollowState = true
case _noticeTypeSysMsg:
needUserInfo = false
}
// 2. 请求用户信息
var userInfos map[int64]*v1.UserInfo
if needUserInfo && len(actionMIDs) > 0 {
userInfos, err = s.dao.BatchUserInfo(ctx, req.Mid, actionMIDs, false, false, needFollowState)
if err != nil {
log.Errorv(ctx, log.KV("log", "batch fetch user infos fail"))
err = nil
}
}
// 2. 请求视频信息
svInfos := make(map[int64]*model.SvInfo)
if needSvInfo && len(svIDs) > 0 {
svList, _, err := s.dao.GetVideoDetail(ctx, svIDs)
if err != nil {
log.Warnv(ctx, log.KV("log", "batch fetch sv detail fail"))
err = nil
} else {
for _, sv := range svList {
svInfos[sv.SVID] = sv
}
}
}
// 3. 组成回包
for _, item := range list {
var noticeMsg v1.NoticeMsg
noticeMsg.NoticeBase = item
// showtype没有用这里就按照noticetype返回
// md想的好好的扩展灵活性完全没被践行
noticeMsg.ShowType = req.NoticeType
if needUserInfo && (noticeMsg.ActionMid != 0) {
if val, exists := userInfos[noticeMsg.ActionMid]; exists {
noticeMsg.UserInfo = val
}
}
if needSvInfo && (noticeMsg.SvId != 0) {
if val, exists := svInfos[noticeMsg.SvId]; exists && noticeMsg.State >= common.VideoStPendingPassReview {
noticeMsg.Pic = val.CoverURL
} else {
noticeMsg.State = 1
noticeMsg.ErrMsg = "视频不见了"
}
}
// cursor_value
var cursor model.CursorValue
cursor.CursorID = item.Id
jsonBytes, _ := json.Marshal(cursor)
noticeMsg.CursorValue = string(jsonBytes)
res.List = append(res.List, &noticeMsg)
}
return
}

View File

@@ -0,0 +1,27 @@
package service
import (
"context"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
notice "go-common/app/service/bbq/notice-service/api/v1"
)
// PushRegister .
func (s *Service) PushRegister(c context.Context, args *notice.UserPushDev) (response *v1.PushRegisterResponse, err error) {
err = s.dao.PushLogin(c, args)
response = &v1.PushRegisterResponse{}
return
}
// PushLogout .
func (s *Service) PushLogout(c context.Context, args *notice.UserPushDev) (response *v1.PushRegisterResponse, err error) {
err = s.dao.PushLogout(c, args)
response = &v1.PushRegisterResponse{}
return
}
// PushCallback .
func (s *Service) PushCallback(c context.Context, args *v1.PushCallbackRequest, mid int64, buvid string) (response *v1.PushCallbackResponse, err error) {
s.dao.PushCallback(c, args.TID, args.NID, mid, buvid)
return
}

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