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

View File

@@ -0,0 +1,33 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/reply/cmd:all-srcs",
"//app/interface/main/reply/conf:all-srcs",
"//app/interface/main/reply/dao/bigdata:all-srcs",
"//app/interface/main/reply/dao/drawyoo:all-srcs",
"//app/interface/main/reply/dao/fans:all-srcs",
"//app/interface/main/reply/dao/reply:all-srcs",
"//app/interface/main/reply/dao/search:all-srcs",
"//app/interface/main/reply/dao/vip:all-srcs",
"//app/interface/main/reply/dao/workflow:all-srcs",
"//app/interface/main/reply/http:all-srcs",
"//app/interface/main/reply/model/activity:all-srcs",
"//app/interface/main/reply/model/adminlog:all-srcs",
"//app/interface/main/reply/model/drawyoo:all-srcs",
"//app/interface/main/reply/model/reply:all-srcs",
"//app/interface/main/reply/model/topic:all-srcs",
"//app/interface/main/reply/model/vip:all-srcs",
"//app/interface/main/reply/model/xreply:all-srcs",
"//app/interface/main/reply/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,857 @@
### 评论的Gateway服务
#### Version 7.0.11
> 1.修复filter过滤cd导致的bug
#### Version 7.0.9
> 1.增加调整热评
#### Version 7.0.8
> 1.修复report oid和rpid 不一致
#### Version 7.0.7
> 1.fix -400
#### Version 7.0.6
> 1.fix reist bug
#### Version 7.0.5
> 1.fix set state bug
#### Version 7.0.4
> 1.优化依赖服务调用
#### Version 7.0.3
> 1.增加bnj视频热评数
#### Version 7.0.2
> 1.fix duplicate
#### Version 7.0.1
> 1.修复判断逻辑
#### Version 7.0.0
> 1.折叠评论及评论新接口
#### Version 6.1.12
> 1.account grpc
#### Version 6.1.11
> 1. 相簿去掉topics
#### Version 6.1.9
> 1. remove user action table
#### Version 6.1.8
> 1.no topics for type=17
#### Version 6.1.7
> 1.fil out deleted reply
#### Version 6.1.6
> 1.max_id
#### Version 6.1.5
> 1.下掉limit
#### Version 6.1.4
> 1.修改hots batch接口 mid非必传
#### Version 6.1.3
> 1.修改hots batch接口增加mid
#### Version 6.1.2
> 1.abtest
#### Version 6.0.41
> 1.国际版接口返回空取消
#### Version 6.0.40
> 1.bnj topics and reply_admin_log
#### Version 6.0.39
> 1.国际版表情限制取消
#### Version 6.0.38
> 1.bbq批量热评接口
#### Version 6.0.37
> 1.火鸟举报
#### Version 6.0.34
> 1.workflow接入火鸟
#### Version 6.0.33
> 1.notice判断
#### Version 6.0.32
> 1.增加对热门评论点赞数的判断
#### Version 6.0.31
> 1.up置顶被管理员删除后仅up可见
#### Version 6.0.30
> 1.up置顶被删除后不可见
#### Version 6.0.29
> 1.小黄条支持模板
#### Version 6.0.28
> 1.location的Zone方法改为Info方法
#### Version 6.0.27
> 1.新增是否是热评的接口
#### Version 6.0.26
> 1.fix bug
#### Version 6.0.25
> 1.针对国际版做表情兼容
#### Version 6.0.24
> 1.针对BBQ和火鸟放开账号限制
#### Version 6.0.23
> 1.修复根评论小于20条时不显示热门评论
#### Version 6.0.22
> 1.评论cd调整删除cd code如果人证成功
#### Version 6.0.15
> 1.已删除的评论不被举报
#### Version 6.0.12
> 1.add new type
#### Version 6.0.11
> 1.行为日志增加端口信息
#### Version 6.0.7
> 1.remove like user action
#### Version 6.0.6
> 1.reply workflow
#### Version 6.0.3
> 1.新的type
#### Version 6.0.2
> 1.remove emoji for ios
#### Version 6.0.1
> 1.reply dialog, 修复parent
#### Version 6.0.0
> 1.reply dialog, 5.33
#### Version 5.28.17
> 1.去掉spam检查为了测试
#### Version 5.28.16
> 1.reply_record es
#### Version 5.28.16
> 1.reply_record es
#### Version 5.28.15
> 1.新算法5条
#### Version 5.28.13
> 1.热评5条
#### Version 5.28.12
> 1.MC回源改成从库
#### Version 5.28.10
> 1.去掉5条热评ABtest
#### Version 5.28.7
> 1.点赞增加upmid字段
#### Version 5.28.5
> 1.log.Warn
#### Version 5.28.4
> 1.去掉remote ip改为context metadata
#### Version 5.28.3
> 1.为reply record增加html escape功能
#### Version 5.28.2
> 1.添加过虑空白字符
#### Version 5.28.1
> 1.增加火鸟项目type
#### Version 5.28.0
> 1.增加feature flag
#### Version 5.27.18
> 1.去掉remote ip改为context metadata
#### Version 5.27.17
> 1.new trace
#### Version 5.27.16
> 1.子评论游标接口定位时兼容rootID
> 2.升级bm server
#### Version 5.27.16
> 1.灰度trace
#### Version 5.27.14
> 1.change identify to grpc
#### Version 5.27.13
> 1.配置文件
> 2.xint move to model
#### Version 5.27.11
> 1.fix buvid get method
#### Version 5.27.10
> 1.reply hots ab test
#### Version 5.27.8
> 1.fix golint
#### Version 5.27.7
> 1.注册用户开放评论点赞点踩
#### Version 5.27.6
> 1.过滤评论新增mid
#### Version 5.27.5
> 1.点赞去重
#### Version 5.27.4
> 1.过滤评论入库
#### Version 5.27.3
> 1.新增行为日志
#### Version 5.27.2
> 1.修复子游标定位不足一页时展示够
#### Version 5.27.1
> 1.修复子游标定位传根评论
#### Version 5.27.0
> 1.修复子游标接口返回非根评论
> 2.添加db从库读取
#### Version 5.26.3
> 1.修复表情包remark
#### Version 5.26.2
> 1.修复表情包为null
#### Version 5.26.1
> 1.vip评论表情由请求http改为查询本地DB
#### Version 5.25.7
> 1.rebuild master
#### Version 5.25.6
> 1.es迁移v3
#### Version 5.25.5
> 1.rebuild master
#### Version 5.25.4
> 1.fix
#### Version 5.25.3
> 1.删除非块级配置
#### Version 5.25.2
> 1.添加大数据过虑开关
#### Version 5.25.1
> 1.fix点赞新平台
#### Version 5.25.0
> 1.点赞新平台
#### Version 5.24.22
> 1.添加vegas限流
#### Version 5.24.21
> 1.平台日志修改配置删除rpc common conf
#### Version 5.24.20
> 1.平台日志换用Databus
#### Version 5.24.19
> 1.点赞双写
#### Version 5.24.18
> 1.修复bug
#### Version 5.24.17
> 1.新增business支持
#### Version 5.24.16
> 1.撤销点赞双写
#### Version 5.24.15
> 1.修复context deadline
#### Version 5.24.14
> 1.增加漫画类型
#### Version 5.24.13
> 1.点赞报错忽略
#### Version 5.24.12
> 1.点赞双写
#### Version 5.24.11
> 1.修复已经删除评论可以添加
#### Version 5.24.10
> 1.修复冻结ecode
#### Version 5.24.9
> 1.修复bm internal context mid取不到
#### Version 5.24.8
> 1.修复重构中出现的问题
#### Version 5.24.7
> 1.修复迁移到bm
#### Version 5.24.6
> 1.迁移到bm
#### Version 5.24.5
> 1.迁移到databus
#### Version 5.24.4
> 1.修复举报日志index类型
#### Version 5.24.3
> 1.修复举报日志content
#### Version 5.24.2
> 1.增加用户举报日志以及用户置顶评论和取消置顶评论日志
#### Version 5.24.0
> 1.修复基础库no rpc client
#### Version 5.24.0
> 1.游标model重构
> 1.添加子评论列表游标接口
##### Version 5.23.5
> 1.增加日志输出用于统计
#### Version 5.23.4
> 1.修复state接口-404
#### Version 5.23.3
> 1.使用discovery
#### Version 5.23.2
> 1.HTTP新鉴权修复
#### Version 5.23.1
> 1.HTTP使用新鉴权
#### Version 5.23.0
> 1.评论分段回源
#### Version 5.22.1
> 1.修复子评论已经删除跳转还出现
#### Version 5.22.0
> 1.增加对-1的兼容
#### Version 5.21.7
> 1.修复up稿件对等级、rank限制
#### Version 5.21.6
> 1.使用account-service v7
#### Version 5.21.5
> 1.添加评论举报理由新增“青少年不良信息”
> 2.用户等级需大于等于Lv2才可发表评论up主不受限制发评论
#### Version 5.21.4
> 1.修改已经删除评论还在列表中
#### Version 5.21.3
> 1.修复空缓存时间为30s
#### Version 5.21.2
> 1.修复reply/log显示问题
#### Version 5.21.1
> 1.修复游标热门跟分页不一致
#### Version 5.21.0
> 1.添加topic标签支持
> 2.删除无用配置
#### Version 5.20.7
> 1.Clone reply fix
> 2.Call new captch api
#### Version 5.20.6
> 1.CacheSave context fix
#### Version 5.20.5
> 1.添加依赖服务白名单
#### Version 5.20.4
> 1.添加依赖服务白名单
#### Version 5.20.3
> 1.更换myinfo为userinfo rpc
#### Version 5.20.2
> 1.修复add mc为nil
#### Version 5.20.1
> 1.添加hmget参数校验
> 2.fix reply roots为null的问题
#### Version 5.20.0
> 1.优化置顶评论
#### Version 5.19.12
> 1.添加size小于等于0时校验
#### Version 5.19.11
> 1.修复置顶评论和根评论覆盖bug
#### Version 5.19.10
> 1.游标优化第三方请求次数
#### Version 5.19.9
> 1.游标避免子评论为空时回源
#### Version 5.19.8
> 1.游标插入楼层排序修复
#### Version 5.19.7
> 1.修复prom nouser
#### Version 5.19.6
> 1.游标角标越界修复
#### Version 5.19.5
> 1.游标角标越界修复
#### Version 5.19.4
> 1.记录脏数据日志
#### Version 5.19.3
> 1.游标修改空指针bug
#### Version 5.19.2
> 1.游标删除多余返回参数(previous/next link)
#### Version 5.19.1
> 1.游标添加跳转接口
> 2.添加批量返回评论数新接口
> 3.internal router添加热门评论接口
#### Version 5.19.0
> 1.添加一二审转审
> 2.增加举报日志
#### Version 5.18.4
> 1.接入filter-service新接口
#### Version 5.18.3
> 1.支持seq rpc sharing.
#### Version 5.18.1
> 1.使用取号器id int32
#### Version 5.18.0
> 1.使用新seq取号器
#### Version 5.17.0
> 1.添加热评SEO接口
> 2.添加音乐播单类型
#### Version 5.16.4
> 1.游标接口修复返回错误码
#### Version 5.16.3
> 1.游标接口添加评论数
> 2.游标sql去掉attr
#### Version 5.16.2
> 1. 将多次断言合并为一次
#### Version 5.16.1
> 1. 删除多余的 ecode.NoLogin
#### Version 5.16.0
> 1.添加评论记录列表接口
> 2.修复relation的mid为0传透
#### Version 5.15.1
> 1.数据库回源优化
#### Version 5.15.0
> 1.添加举报信用评分
#### Version 5.14.0
> 1.公告添加海外判断
#### Version 5.13.1
> 1.添加游标接口
#### Version 5.12.2
> 1.修复验证码mc close
#### Version 5.12.1
> 1.修复定位评论分页数据
#### Version 5.12.0
> 1.定位评论添加热门和置顶
#### Version 5.11.0
> 1.二级评论楼主和热评楼主加关注关系
#### Version 5.10.4
> 1.修复发评论效验无效
#### Version 5.10.0
> 1.添加举报相关接口
#### Version 5.9.2
> 1.修复过虑ecode check
#### Version 5.9.1
> 1.修复账号active判断
#### Version 5.9.0
> 1.添加实名认证
#### Version 5.8.1
> 1.修复是评论注册缓存
#### Version 5.8.0
> 1.迁移大仓库更新memcache
> 2.修复重复举报ttl
#### Version 5.7.1
> 1.修复内容为空
> 2.修复Version ip冻结状态下无法评论
#### Version 5.7.0
> 1.去除topic老库依赖
#### Version 5.6.2
> 1.修复过虑限制
#### Version 5.6.1
> 1.修复评论列表
#### Version 5.6.0
> 1.重构评论列表
> 2.添加先审后发
#### Version 5.4.0
> 1.添加粉丝勋章名称及其他参数
#### Version 5.3.3
> 1.修改过滤逻辑
#### Version 5.3.1
> 1.升级filter-serVersion ice ecode
#### Version 5.3.0
> 1.添加对反垃圾支持
#### Version 5.2.2
> 1.修复关闭评论区up主无法删除评论
#### Version 5.2.1
> 1.添加获取评论删除日志配置入口
> 2.新增内部接口(info,count,minfo,mcount)
#### Version 5.2.0
> 1.评论日志删除管理入口
> 2.去掉老黑名单http接口
> 3.协管批量获取接口替换
#### Version 5.1.0
> 1.新增web端jsonp调用emojs表情列表接口(CDN缓存优化)
#### Version 5.0.5
> 1.修复后台批量通过接口
#### Version 5.0.4
> 1.修复评论内容表情转码问题
#### Version 5.0.3
> 1.粉丝列表intimacy类型值溢出
#### Version 5.0.2
> 1.评论项目int值溢出粉丝接口普罗米修斯监控
#### Version 5.0.1
> 1.添加获取粉丝勋章列表日志和业务类型
#### Version 5.0.0
> 1.评论添加获取粉丝勋章列表
#### Version 4.9.1
> 1.修复评论添加协管员日志
#### Version 4.9.0
> 1.评论添加协管员删除评论逻辑
#### Version 4.8.7
> 1.修改Version endor中的Shopify依赖冲突
#### Version 4.8.6
> 1.升级go-common,go-business依赖普罗米修斯logagent
#### Version 4.8.5
> 1.添加文章类型
#### Version 4.8.4
> 1.修复数据年份
#### Version 4.8.3
> 1.修复mcount接口返回禁止评论
#### Version 4.8.2
> 1.修复敏感词打码
#### Version 4.8.1
> 1.敏感视频关闭评论&优化
#### Version 4.8.0
> 1.添加周年庆所需用户信息字段
#### Version 4.7.9
> 1.添加稿件发评论黑名单列表
#### Version 4.7.8
> 1.网监需求,我和你一样也不能理解
#### Version 4.7.7
> 1.黑名单rpc降级
#### Version 4.7.6
> 1.评论获取黑名单调用rpc接口
#### Version 4.7.5
> 1.降级黑名单
#### Version 4.7.4
> 1.修改分页获取黑名单数量
#### Version 4.7.3
> 1.修改热评Version 2.0逻辑
#### Version 4.7.2
> 1.新增评论业务类型
#### Version 4.7.1
> 1.接入配置中心
#### Version 4.7.0
> 1.评论新增黑名单过滤接口
#### Version 4.6.7
> 1.去除稿件依赖
#### Version 4.6.3/6
> 1. csrf日志
#### Version 4.6.2
> 1.添加大数过虑降级
#### Version 4.6.1
> 1.大数据过虑改为POST
#### Version 4.6.0
> 1.添加举报记录
#### Version 4.5.4
> 1.修复注册状态覆盖
> 2.修复ipad版本公告奔溃
#### Version 4.5.3
> 1.tw升级新版本需要重新打包上线
#### Version 4.5.2
> 1.过虑过多回车和空格
> 2.修复子楼层设置踩时添加到热门
#### Version 4.5.1
> 1.添加修改赞踩数
#### Version 4.5.0
> 1.更新热评、举报理由
#### Version 4.4.2
> 1.添加用户信息降级
#### Version 4.4.1
> 1.修复更新subject状态时还没注册
#### Version 4.4.0
> 1.添加举报一二审
#### Version 4.3.7
> 1.更新go-business整理接口命名规范
#### Version 4.3.6
> 1.添加internal内网流量接口
#### Version 4.3.5
> 1.更新http接口支持内外网流量
#### Version 4.3.3/4
> 1.本地TW docker.
#### Version 4.3.2
> 1.更新golang/go-common/go-business
#### Version 4.3.1
> 1.修复监控评论错误码
#### Version 4.3.0
> 1.过虑添加评论ID
> 2.添加up主mid更新
#### Version 4.2.6
> 1.添加日志封禁时间
#### Version 4.2.5
> 1.批量获取info
#### Version 4.2.4
> 1.删除先审后发
> 2.注册subject置顶state
#### Version 4.2.3
> 1.修复缓存miss判断处理bug
#### Version 4.2.2
> 1.合并二级评论请求为pipeline
> 2.修复楼层重复
> 3.修改先审后发为先发后审核
#### Version 4.2.1
> 1.合并sub请求
#### Version 4.2.0
> 1.优化reply获取
#### Version 4.1.0
> 1.增加realip
> 2.更新Version endor
#### Version 4.0.5
> 1.是否点赞使用HMGET获取
#### Version 4.0.4
> 1.修改点赞过期时间
#### Version 4.0.3
> 1.允许待审评论被删除
#### Version 4.0.2
> 1.修复json panic
#### Version 4.0.1
> 1.修改top置顶为异步加缓存
#### Version 4.0.0
> 1.添加评论踩
> 2.添加up置顶评论
> 3.添加up删除评论
> 4.添加评论attr属性字段隔离属性和状态
> 5.添加subject主体attr字段记录是否存在置顶等属性
> 6.更新基础库依赖
#### Version 3.3.0
> 1.接入新的过滤服务
> 2.更新go-common依赖
#### Version 3.2.1
> 1.更新Version endor依赖
#### Version 3.2.0
> 1.更新Version endor依赖
#### Version 3.1.1
> 1.add window0 plat
#### Version 3.1.0
> 1.subject 自动注册
#### Version 3.0.1
> 1.修复up主无法显示
#### Version 3.0.0
> 1.评论root是否点赞处理
> 2.置顶评论不允许删除
> 3.接入错误码管理后台
> 4.goVersion endor支持
> 5.修改go-business依赖
#### Version 2.4.1
> 1.评论门槛等级提高
#### Version 2.4.0
> 1.up主举报自身视频自身评论视为删除
> 2.支持移动端定位跳转
#### Version 2.3.1
> 1.修改json字段deleted
> 2.增加表情删除状态判断
#### Version 2.3.0
> 1.新增Version ip表情包接口
> 2.Version ip添加评论处理
> 3.评论门槛限制
#### Version 2.2.2
> 1.过滤异常utf8编码
> 2.添加subject状态查询接口
> 3.允许隐藏疑似垃圾评论
> 4.subject增加禁止评论状态判断
#### Version 2.2.1
> 1.up主显示隐藏评论
#### Version 2.2.0
> 1.批量举报和隐藏和显示
> 2.修复大数据过滤
#### Version 2.1.6
> 1.修复三个热门评论不显示子评论
#### Version 2.1.5
> 1.修复top评论子回复数
#### Version 2.1.4
> 1.action不存在时增加无action标志缓存
#### Version 2.1.3
> 1.修复查询评论无内容bug
#### Version 2.1.2
> 1.修复无top评论稿件查询db
#### Version 2.1.1
> 1.修复二级评论查询db
#### Version 2.1.0
> 1.支持画站类型
> 2.置顶评论是否点赞
#### Version 2.0.0
> 1.评论重构

View File

@@ -0,0 +1,13 @@
# Owner
chenzhihui
caoguoliang
zhapuyu
wangxu01
# Author
chenzhihui
caoguoliang
# Reviewer
chenzhihui
caoguoliang

View File

@@ -0,0 +1,16 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- caoguoliang
- chenzhihui
- wangxu01
- zhapuyu
labels:
- interface
- interface/main/reply
- main
options:
no_parent_owners: true
reviewers:
- caoguoliang
- chenzhihui

View File

@@ -0,0 +1,13 @@
#### reply
##### 项目简介
> 评论,用于各业务方需要评论功能接入。
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common
##### 特别说明
> 1.model目录可能会被其他项目引用请谨慎请改并通知各方。

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["reply-test.toml"],
importpath = "go-common/app/interface/main/reply/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/exp/feature:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus/report: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,52 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/http"
ecode "go-common/library/ecode/tip"
"go-common/library/exp/feature"
"go-common/library/log"
"go-common/library/net/trace"
"go-common/library/queue/databus/report"
)
func main() {
feature.DefaultGate.AddFlag(flag.CommandLine)
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
log.Init(conf.Conf.XLog)
ecode.Init(conf.Conf.Ecode)
trace.Init(conf.Conf.Tracer)
defer trace.Close()
defer log.Close()
log.Info("reply start")
// init report agent
report.InitUser(conf.Conf.UserReport)
// init http
http.Init(conf.Conf)
// init pprof
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("reply get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("reply exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,234 @@
[host]
search = "http://uat-manager.bilibili.co/"
[XLog]
stdout=true
[infoc]
taskID = "000308"
proto = "tcp"
addr = "172.19.100.20:5401"
chanSize = 10240
[bm]
addr = "0.0.0.0:9000"
timeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
[seq]
businessID = 6
token = "RA8yy0RjDCBTGgFUha4hPOnhxfXvM8hR"
[appkeytype]
"c1a1cb2d89c33794" = [1] # 稿件
"53e2fa226f5ad348" = [1] # 稿件
"33ac033ce123e590" = [4,9] # 活动,活动稿件
"1320e897e00d4c1c" = [5] # 直播小视频、直播文章
"903f486ae7dc87fa" = [6,7,15] # 封禁信息、风纪委
"90e40332a3d3ba15" = [7] # 公告信息
"58344a540b61aebf" = [8,10] # 直播活动、直播公告
"9edfa647fbfde3e2" = [11] # 有文画站
"9cfc54570033cd61" = [12] # 文章
"7d9f6f6fe2a898e8" = [13] # 票务
"0c4b8fe3ff35a4b6" = [14,19] # 音乐、音乐播单
"27773a832a67e55f" = [16,20,21] # 点评、漫画部评论、漫画话评论
"fb06a25c6338edbc" = [17] #庐山动态
"b1014d7c339a5649" = [18] # 播单
[identify]
whiteAccessKey = "nKUPGzuhU|vTG58H!HPKLlktz{8}3^"
whiteMid = 88889082
csrf = true
[identify.app]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.co"
secret = "http://open.bilibili.co"
[identify.httpClient]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
dial = "30ms"
timeout = "100ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[reply]
maxPageSize = 20
minConLen = 2
maxConLen = 1000
secondDefSize = 5
secondDefPageNum = 1
maxEmoji = 20
emojiExpire = "1m"
aitopicurl = "http://172.22.33.111:8100/topicrec"
bigdataurl = "http://172.18.18.11:8888/comment"
filter ="http://uat-api.bilibili.co"
vipUrl ="http://uat-vip.bilibili.co"
fansreceivedlisturl = "http://uat-api.live.bilibili.co/fans_medal/v1/fans_medal/get_list_weared"
captchaTokenURL = "http://api.bilibili.co/x/internal/v1/captcha/token"
captchaVerifyURL = "http://api.bilibili.co/x/internal/v1/captcha/verify"
blockstatusurl="http://uat-account.bilibili.co/api/member/getBlockAndMoralStatus"
credituserurl = "http://uat-blocked.bilibili.co/internal/v1/jury/info"
forbidList = [11191454,9685876]
[reply.SortByHotOids]
"10100715"=1
[reply.BnjAidHotNum]
"10098208"=10
[reply.HotReplyConfig]
"1" = {"10098208" = 10}
[httpClient]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[drawyooHTTPClient]
key = "49df034922d68827"
secret = "92ce984f5c8b8415366838c6ee3e039f"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[drawyooHTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.1
request = 100
[mysql]
[mysql.reply]
addr= "172.22.34.101:3306"
dsn = "test_3306:UJPZaGKjpb2ylFx3HNhmLuwOYft4MCAi@tcp(172.22.34.101:3306)/bilibili_reply?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "200ms"
execTimeout = "200ms"
tranTimeout = "200ms"
[mysql.reply.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[mysql.replySlave]
addr= "172.22.34.101:3306"
dsn = "test_3306:UJPZaGKjpb2ylFx3HNhmLuwOYft4MCAi@tcp(172.22.34.101:3306)/bilibili_reply?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 10
idle = 2
idleTimeout ="4h"
queryTimeout = "500ms"
execTimeout = "500ms"
tranTimeout = "500ms"
[mysql.replySlave.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[supervision]
startTime = "2017-06-02 22:15:00"
endTime = "2017-06-05 08:00:00"
completed = false
location = "中国"
[assistConfig]
startTime = "2017-07-11 22:15:00"
[identification]
switchon = true
[databus]
key = "170e302355453683"
secret = "3d0e8db7bed0503949e545a469789279"
group = "ReplyAdd-MainCommunity-P"
topic = "ReplyAdd-T"
action ="pub"
name = "reply/interface"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 2
active = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1h"
[databus.discovery]
domain = "api.bilibili.co"
key = "0c4b8fe3ff35a4b6"
secret = "b370880d1aca7d3a289b9b9a7f4d6812"
[memcache]
proto = "tcp"
addr = "172.18.33.61:11213"
idle = 10
active = 10
dialTimeout = "2s"
readTimeout = "2s"
writeTimeout = "2s"
idleTimeout = "7h"
expire = "24h"
[redis]
proto = "tcp"
addr = "172.18.33.60:6889"
idle = 10
active = 10
dialTimeout = "500ms"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
indexExpire = "24h"
reportExpire = "24h"
userCntExpire = "30s"
userActExpire= "1h"
notifyExpire= "24h"
[rpcClient2]
[rpcClient2.account]
timeout = "1s"
color = "blueandred"
[rpcClient2.filter]
timeout = "1s"
[rpcClient2.location]
timeout = "1s"
[rpcClient2.assist]
timeout = "1s"
[rpcClient2.figure]
timeout = "1s"
[rpcClient2.seq]
timeout = "1s"
[rpcClient2.thumbup]
timeout = "1s"

View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["reply.go"],
importpath = "go-common/app/interface/main/reply/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/elastic: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/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,209 @@
package conf
import (
"flag"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/elastic"
"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/auth"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/queue/databus"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// global var
var (
ConfPath string
Conf *Config
)
// Config represent service conf
type Config struct {
BM *bm.ServerConfig
//reply
Reply *Reply
// HTTPClinet
HTTPClient *bm.ClientConfig
DrawyooHTTPClient *bm.ClientConfig
FilterGRPCClient *warden.ClientConfig
FeedGRPCClient *warden.ClientConfig
AccountGRPCClient *warden.ClientConfig
// rpc
RPCClient2 *RPCClient2
// mysql
MySQL *MySQL
// redis
Redis *Redis
// mc
Memcache *Memcache
// seq conf
Seq *Seq
// kafka
Databus *databus.Config
// tracer
Tracer *trace.Config
// XLog
XLog *log.Config
// auth
Auth *auth.Config
// verify
Verify *verify.Config
// ecode
Ecode *ecode.Config
Host *Host
// appkey type
AppkeyType map[string][]int8
// supervision conf
Supervision *Supervision
AssistConfig *AssistConfig
Identification *Identification
ReportAgent *log.AgentConfig
UserReport *databus.Config
// es config
Es *elastic.Config
// info config
Infoc *infoc.Config
}
//Seq Conf
type Seq struct {
BusinessID int64
Token string
}
// Reply represents reply conf
type Reply struct {
HotReply int
MaxPageSize int
MinConLen int
MaxConLen int
SecondDefSize int
SecondDefPageNum int
EmojiExpire time.Duration
MaxEmoji int
BigdataFilter bool
// url
BigdataURL string
AiTopicURL string
VipURL string
FansReceivedListURL string
BlockStatusURL string
CaptchaTokenURL string
CaptchaVerifyURL string
CreditUserURL string
ReplyLogSearchURL string
AidWhiteList []int64
ForbidList []int64
BnjAidList []int64
// 默认排序开关
SortByHotOids map[string]int8
SortByTimeOids map[string]int8
HideFloorOids map[string]int8
// 拜年祭的一些视频默认热评数目需要调整到N个
HotReplyConfig map[string]map[string]int
}
// Host host.
type Host struct {
API string
Search string
}
// MySQL represent mysql conf
type MySQL struct {
Reply *sql.Config
ReplySlave *sql.Config
}
// Redis represent redis conf
type Redis struct {
*redis.Config
IndexExpire time.Duration
ReportExpire time.Duration
UserCntExpire time.Duration
UserActExpire time.Duration
}
// Memcache represent mc conf
type Memcache struct {
*memcache.Config
Expire time.Duration
EmptyExpire time.Duration
}
// RPCClient2 represent rpc conf
type RPCClient2 struct {
Account *rpc.ClientConfig
Filter *rpc.ClientConfig
Location *rpc.ClientConfig
Assist *rpc.ClientConfig
Figure *rpc.ClientConfig
Seq *rpc.ClientConfig
Thumbup *rpc.ClientConfig
Archive *rpc.ClientConfig
Article *rpc.ClientConfig
}
func init() {
flag.StringVar(&ConfPath, "conf", "", "config path")
}
// Supervision supervision .
type Supervision struct {
StartTime string
EndTime string
Completed bool
Location string
ReportAgent *log.AgentConfig
}
// AssistConfig Assist configurations .
type AssistConfig struct {
StartTime string
}
// Identification identification configurations.
type Identification struct {
SwitchOn bool
}
// Init init conf
func Init() (err error) {
if ConfPath == "" {
return configCenter()
}
_, err = toml.DecodeFile(ConfPath, &Conf)
return
}
func configCenter() (err error) {
var (
ok bool
value string
client *conf.Client
)
if client, err = conf.New(); err != nil {
return
}
if value, ok = client.Toml2(); !ok {
panic(err)
}
_, err = toml.Decode(value, &Conf)
return
}

View File

@@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"ai_test.go",
"bigdata_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"ai.go",
"bigdata.go",
],
importpath = "go-common/app/interface/main/reply/dao/bigdata",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster: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,57 @@
package bigdata
import (
"bytes"
"context"
"encoding/json"
"net/http"
"go-common/library/log"
"github.com/pkg/errors"
)
type topicResponse struct {
Code int `json:"error_code"`
Message string `json:"error_message"`
Topics []string `json:"topics"`
}
type topicReq struct {
Mid int64 `json:"mid"`
Oid int64 `json:"oid"`
Type int8 `json:"type"`
Message string `json:"message"`
}
// Topics return topics
func (dao *Dao) Topics(c context.Context, mid int64, oid int64, typ int8, msg string) ([]string, error) {
res := &topicResponse{}
content, err := json.Marshal(&topicReq{
Mid: mid,
Oid: oid,
Type: typ,
Message: msg,
})
if err != nil {
err = errors.WithStack(err)
return nil, err
}
req, err := http.NewRequest("POST", dao.topicURL, bytes.NewReader(content))
if err != nil {
log.Error("bigdata.Topics(%d,%d,%d,%s) url(%s) req(%s)send POST error(%v)", mid, oid, typ, msg, dao.topicURL, string(content), err)
err = errors.WithStack(err)
return nil, err
}
req.Header.Set("Content-Type", "application/json")
err = dao.httpClient.Do(c, req, res)
if err != nil {
log.Error("bigdata.Topics(%d,%d,%d,%s) url(%s) req(%s) error(%v)", mid, oid, typ, msg, dao.topicURL, string(content), err)
return nil, err
}
if res.Code != 0 {
log.Error("bigdata.Topics(%d,%d,%d,%s) url(%s) req(%s) return not success,error_msg(%d,%v)", mid, oid, typ, msg, dao.topicURL, string(content), res.Code, res.Message)
return nil, err
}
return res.Topics, nil
}

View File

@@ -0,0 +1,56 @@
package bigdata
import (
"context"
"flag"
"go-common/app/interface/main/reply/conf"
"os"
"testing"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/reply-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func TestBigdataTopics(t *testing.T) {
convey.Convey("Topics", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
oid = int64(0)
typ = int8(0)
msg = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Topics(c, mid, oid, typ, msg)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,48 @@
package bigdata
import (
"context"
"net/url"
"go-common/app/interface/main/reply/conf"
"go-common/library/ecode"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
type result struct {
Code int `json:"code"`
}
// Dao bigdata dao
type Dao struct {
url string
topicURL string
httpClient *httpx.Client
}
// New return a bigdata dao
func New(c *conf.Config) *Dao {
d := &Dao{
httpClient: httpx.NewClient(c.HTTPClient),
url: c.Reply.BigdataURL,
topicURL: c.Reply.AiTopicURL,
}
return d
}
// Filter Filter
func (dao *Dao) Filter(c context.Context, msg string) (err error) {
params := url.Values{}
res := &result{}
params.Set("comment", msg)
if err = dao.httpClient.Post(c, dao.url, "", params, res); err != nil {
log.Error("Bigdata url(%s) error(%v)", dao.url+"?"+params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("Bigdata url(%s) error(%v)", dao.url+"?"+params.Encode(), res.Code)
err = ecode.ReplyDeniedAsGarbage
}
return
}

View File

@@ -0,0 +1,39 @@
package bigdata
import (
"context"
"testing"
"go-common/app/interface/main/reply/conf"
"github.com/smartystreets/goconvey/convey"
)
func TestBigdataNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := New(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestBigdataFilter(t *testing.T) {
convey.Convey("Filter", t, func(ctx convey.C) {
var (
c = context.Background()
msg = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Filter(c, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["drawyoo_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["drawyoo.go"],
importpath = "go-common/app/interface/main/reply/dao/drawyoo",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/drawyoo:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,73 @@
package drawyoo
import (
"context"
"net/url"
"strconv"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/model/drawyoo"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
// Dao Dao
type Dao struct {
url string
http *httpx.Client
}
// New New
func New(c *conf.Config) *Dao {
d := &Dao{
url: "http://h.bilibili.com/api/pushS",
http: httpx.NewClient(c.DrawyooHTTPClient),
}
return d
}
// Info Info
func (dao *Dao) Info(c context.Context, hid int64) (info *drawyoo.Drawyoo, err error) {
params := url.Values{}
params.Set("act", "getHidInfo")
params.Set("hid", strconv.FormatInt(hid, 10))
var res struct {
State int `json:"state"`
Data []*drawyoo.Drawyoo `json:"data"`
}
if err = dao.http.Post(c, dao.url, "", params, &res); err != nil {
log.Error("drawyoo url(%v),err (%v)", dao.url+"?"+params.Encode(), err)
return
}
if res.State != 200 || len(res.Data) == 0 {
log.Error("drawyoo url (%v),err (%v)", dao.url+"?"+params.Encode(), err)
return
}
info = res.Data[0]
return
}
// Infos Infos
func (dao *Dao) Infos(c context.Context, hids []int64) (info map[int64]interface{}, err error) {
params := url.Values{}
params.Set("act", "getHidInfo")
params.Set("hid", xstr.JoinInts(hids))
var res struct {
State int `json:"state"`
Data []*drawyoo.Drawyoo `json:"data"`
}
if err = dao.http.Post(c, dao.url, "", params, &res); err != nil {
log.Error("drawyoo url(%v),err (%v)", dao.url+"?"+params.Encode(), err)
return
}
if res.State != 200 || len(res.Data) == 0 {
log.Error("drawyoo url (%v),err (%v)", dao.url+"?"+params.Encode(), err)
return
}
info = make(map[int64]interface{}, len(res.Data))
for _, r := range res.Data {
info[r.Hid] = r
}
return
}

View File

@@ -0,0 +1,84 @@
package drawyoo
import (
"context"
"flag"
"go-common/app/interface/main/reply/conf"
"os"
"testing"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/reply-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func TestDrawyooNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := New(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDrawyooInfo(t *testing.T) {
convey.Convey("Info", t, func(ctx convey.C) {
var (
c = context.Background()
hid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
info, err := d.Info(c, hid)
ctx.Convey("Then err should be nil.info should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(info, convey.ShouldBeNil)
})
})
})
}
func TestDrawyooInfos(t *testing.T) {
convey.Convey("Infos", t, func(ctx convey.C) {
var (
c = context.Background()
hids = []int64{}
d = New(conf.Conf)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
info, err := d.Infos(c, hids)
ctx.Convey("Then err should be nil.info should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(info, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["fans_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["fans.go"],
importpath = "go-common/app/interface/main/reply/dao/fans",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,58 @@
package fans
import (
"context"
"net/url"
"strconv"
"time"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
// Dao Dao
type Dao struct {
fansReceivedListURL string
fansReceivedListHTTPClient *httpx.Client
}
// New New
func New(c *conf.Config) *Dao {
d := &Dao{
fansReceivedListURL: c.Reply.FansReceivedListURL,
fansReceivedListHTTPClient: httpx.NewClient(c.HTTPClient),
}
return d
}
// Fetch Fetch
func (dao *Dao) Fetch(c context.Context, uids []int64, mid int64, now time.Time) (map[int64]*reply.FansDetail, error) {
fansMap := make(map[int64]*reply.FansDetail)
if len(uids) == 0 {
return fansMap, nil
}
params := url.Values{}
params.Set("target_id", strconv.FormatInt(mid, 10))
params.Set("source", strconv.FormatInt(2, 10))
for index := range uids {
params.Add("uid[]", strconv.FormatInt(uids[index], 10))
}
var res struct {
Code int `json:"code"`
Message string `json:"msg"`
Data []*reply.FansDetail `json:"data"`
}
if err := dao.fansReceivedListHTTPClient.Get(c, dao.fansReceivedListURL, "", params, &res); err != nil {
log.Error("fansFetch url(%v),err (%v)", dao.fansReceivedListURL+"?"+params.Encode(), err)
return fansMap, err
}
if res.Code != 0 {
return fansMap, nil
}
for _, d := range res.Data {
fansMap[d.UID] = d
}
return fansMap, nil
}

View File

@@ -0,0 +1,70 @@
package fans
import (
"context"
"flag"
"go-common/app/interface/main/reply/conf"
"os"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/reply-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func TestFansNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := New(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestFansFetch(t *testing.T) {
convey.Convey("Fetch", t, func(ctx convey.C) {
var (
c = context.Background()
uids = []int64{}
mid = int64(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Fetch(c, uids, mid, now)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,96 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"admin_test.go",
"block_status_test.go",
"business_test.go",
"captcha_test.go",
"config_test.go",
"content_test.go",
"credit_user_test.go",
"cursor_test.go",
"dao_test.go",
"databus_test.go",
"emoji_test.go",
"memcache_test.go",
"notice_test.go",
"redis_test.go",
"reply_test.go",
"report_test.go",
"subject_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//library/database/sql:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/queue/databus:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"admin.go",
"block_status.go",
"business.go",
"captcha.go",
"config.go",
"content.go",
"credit_user.go",
"cursor.go",
"dao.go",
"databus.go",
"emoji.go",
"memcache.go",
"notice.go",
"redis.go",
"reply.go",
"report.go",
"subject.go",
],
importpath = "go-common/app/interface/main/reply/dao/reply",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//app/interface/main/reply/model/xreply:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,47 @@
package reply
import (
"context"
"time"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_inAdminSQL = "INSERT INTO reply_admin_log (oid,type,rpid,adminid,result,remark,isnew,isreport,state,ctime,mtime) VALUES(?,?,?,?,?,?,?,?,?,?,?)"
_upAdminSQL = "UPDATE reply_admin_log SET isnew=0,mtime=? WHERE rpid=? AND isnew=1"
)
// AdminDao AdminDao
type AdminDao struct {
mysql *sql.DB
}
// NewAdminDao new ReplyReportDao and return.
func NewAdminDao(db *sql.DB) (dao *AdminDao) {
dao = &AdminDao{
mysql: db,
}
return
}
// Insert insert reply report.
func (dao *AdminDao) Insert(c context.Context, adminid, oid, rpID int64, tp int8, result, remark string, isnew, isreport, state int8, now time.Time) (id int64, err error) {
res, err := dao.mysql.Exec(c, _inAdminSQL, oid, tp, rpID, adminid, result, remark, isnew, isreport, state, now, now)
if err != nil {
log.Error("adminDao.Exec error(%v)", err)
return
}
return res.LastInsertId()
}
// UpIsNotNew update reply report.
func (dao *AdminDao) UpIsNotNew(c context.Context, rpID int64, now time.Time) (rows int64, err error) {
res, err := dao.mysql.Exec(c, _upAdminSQL, now, rpID)
if err != nil {
log.Error("adminDao.Exec error(%v)", err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,66 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewAdminDao(t *testing.T) {
convey.Convey("NewAdminDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewAdminDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyAdminInsert(t *testing.T) {
convey.Convey("Insert", t, func(ctx convey.C) {
var (
c = context.Background()
adminid = int64(0)
oid = int64(0)
rpID = int64(0)
tp = int8(0)
result = ""
remark = ""
isnew = int8(0)
isreport = int8(0)
state = int8(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.Admin.Insert(c, adminid, oid, rpID, tp, result, remark, isnew, isreport, state, now)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyUpIsNotNew(t *testing.T) {
convey.Convey("UpIsNotNew", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Admin.UpIsNotNew(c, rpID, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,70 @@
package reply
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/reply/conf"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
// BlockStatusDao BlockStatusDao
type BlockStatusDao struct {
requestURL string
httpClient *httpx.Client
}
// NewBlockStatusDao NewBlockStatusDao
func NewBlockStatusDao(c *conf.Config) *BlockStatusDao {
d := &BlockStatusDao{
httpClient: httpx.NewClient(c.HTTPClient),
requestURL: c.Reply.BlockStatusURL,
}
return d
}
// BlockInfo BlockInfo
type BlockInfo struct {
// block_status 变成了 PassTest,原因不可描述
PassTest int `json:"block_status"` // 1: fail 0: succ
ForeverBlock bool `json:"blocked_forever"`
BlockUntil int64 `json:"blocked_end"`
Moral int `json:"moral"`
}
// BlockStatusResp BlockStatusResp
type BlockStatusResp struct {
Code int `json:"code"`
Data BlockInfo `json:"data"`
Msg string `json:"msg"`
}
// BlockInfo BlockInfo
func (dao *BlockStatusDao) BlockInfo(c context.Context, mid int64) (*BlockInfo, error) {
var res BlockStatusResp
params := url.Values{}
params.Set("mid", fmt.Sprintf("%d", mid))
err := dao.httpClient.Get(c, dao.requestURL, "", params, &res)
// WARNING: must put urlStr after httpClient.Get()
// since params will be modified
urlStr := dao.requestURL + "?" + params.Encode()
if err != nil {
log.Error("call 账号系统小黑屋(%s) error(%v)", urlStr, err)
return nil, err
}
if res.Code != 0 {
err = fmt.Errorf("call 账号系统小黑屋(%s) error, return code(%v)", urlStr, res.Code)
log.Error("%v", err)
return nil, err
}
log.Info("call 账号系统小黑屋(%s) successful. resp(%+v)", urlStr, res.Data)
resData := res.Data
if resData.PassTest != 0 && resData.PassTest != 1 {
err = fmt.Errorf("call 账号系统小黑屋(%s) successful. but got error resp(blocked_status=%d), want 0 or 1", urlStr, resData.PassTest)
log.Error("%v", err)
return nil, err
}
return &res.Data, nil
}

View File

@@ -0,0 +1,24 @@
package reply
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyBlockInfo(t *testing.T) {
convey.Convey("BlockInfo", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.BlockStatus.BlockInfo(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,46 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_selBussinessSQL = "SELECT type, alias, appkey FROM business WHERE state=0"
)
// BusinessDao business dao.
type BusinessDao struct {
db *sql.DB
}
// NewBusinessDao new BusinessDao and return.
func NewBusinessDao(db *sql.DB) (dao *BusinessDao) {
dao = &BusinessDao{
db: db,
}
return
}
// ListBusiness gets all business records
func (dao *BusinessDao) ListBusiness(c context.Context) (business []*reply.Business, err error) {
rows, err := dao.db.Query(c, _selBussinessSQL)
if err != nil {
log.Error("sql query error(%v)", err)
return
}
defer rows.Close()
business = make([]*reply.Business, 0)
for rows.Next() {
b := new(reply.Business)
if err = rows.Scan(&b.Type, &b.Alias, &b.Appkey); err != nil {
return
}
business = append(business, b)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,38 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewBusinessDao(t *testing.T) {
convey.Convey("NewBusinessDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewBusinessDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyListBusiness(t *testing.T) {
convey.Convey("ListBusiness", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
business, err := d.Business.ListBusiness(c)
ctx.Convey("Then err should be nil.business should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(business, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,68 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
"go-common/library/ecode"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"net/url"
)
// NewCaptchaDao NewCaptchaDao
func NewCaptchaDao(c *httpx.ClientConfig) *CaptchaDao {
return &CaptchaDao{httpClient: httpx.NewClient(c)}
}
// CaptchaDao CaptchaDao
type CaptchaDao struct {
httpClient *httpx.Client
}
// Captcha Captcha.
func (s *CaptchaDao) Captcha(c context.Context) (string, string, error) {
params := url.Values{}
params.Set("bid", "reply")
res := &struct {
Code int `json:"code"`
Data struct {
Token string `json:"token"`
URL string `json:"url"`
} `json:"data"`
Msg string `json:"message"`
TTL int `json:"ttl"`
}{}
ip := metadata.String(c, metadata.RemoteIP)
if err := s.httpClient.Get(c, conf.Conf.Reply.CaptchaTokenURL, ip, params, res); err != nil {
log.Error("s.httpClient.Get(%s) error(%v)", conf.Conf.Reply.CaptchaTokenURL+"?"+params.Encode(), err)
return "", "", err
}
if res.Code != 0 {
log.Error("s.httpClient.Get(%s?%s) code:%d", conf.Conf.Reply.CaptchaTokenURL, params.Encode(), res.Code)
return "", "", ecode.Int(res.Code)
}
return res.Data.Token, res.Data.URL, nil
}
// Verify Verify.
func (s *CaptchaDao) Verify(c context.Context, token, code string) error {
params := url.Values{}
params.Set("token", token)
params.Set("code", code)
res := &struct {
Code int `json:"code"`
Msg string `json:"message"`
TTL int `json:"ttl"`
}{}
ip := metadata.String(c, metadata.RemoteIP)
if err := s.httpClient.Post(c, conf.Conf.Reply.CaptchaVerifyURL, ip, params, res); err != nil {
log.Error("s.httpClient.POST(%s) error(%v)", conf.Conf.Reply.CaptchaVerifyURL+"?"+params.Encode(), err)
return err
}
if res.Code != 0 {
log.Error("s.httpClient.POST(%s?%s) code:%d", conf.Conf.Reply.CaptchaVerifyURL, params.Encode(), res.Code)
return ecode.Int(res.Code)
}
return nil
}

View File

@@ -0,0 +1,64 @@
package reply
import (
"context"
httpx "go-common/library/net/http/blademaster"
xtime "go-common/library/time"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewCaptchaDao(t *testing.T) {
convey.Convey("NewCaptchaDao", t, func(ctx convey.C) {
var (
c = &httpx.ClientConfig{
App: &httpx.App{
Key: "test",
Secret: "test",
},
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := NewCaptchaDao(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyCaptcha(t *testing.T) {
convey.Convey("Captcha", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.Captcha.Captcha(c)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyVerify(t *testing.T) {
convey.Convey("Verify", t, func(ctx convey.C) {
var (
c = context.Background()
token = ""
code = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Captcha.Verify(c, token, code)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,49 @@
package reply
import (
"context"
"encoding/json"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_loadConfigSQL = "SELECT id, type, oid, adminid, operator, category, config, ctime, mtime FROM reply_config WHERE type=? AND oid=? AND category = ?"
)
// ConfigDao ConfigDao
type ConfigDao struct {
mysql *sql.DB
}
// NewConfigDao new ConfigDao and return.
func NewConfigDao(db *sql.DB) (dao *ConfigDao) {
dao = &ConfigDao{
mysql: db,
}
return
}
// LoadConfig :load a config record
func (dao *ConfigDao) LoadConfig(c context.Context, oid int64, tp, category int8) (m *reply.Config, err error) {
m = &reply.Config{}
row := dao.mysql.QueryRow(c, _loadConfigSQL, tp, oid, category)
if err = row.Scan(&m.ID, &m.Type, &m.Oid, &m.AdminID, &m.Operator, &m.Category, &m.Config, &m.CTime, &m.MTime); err != nil {
if err == sql.ErrNoRows {
m = nil
err = nil
return
}
log.Error("row.Scan error(%v)", err)
}
if m.ID > 0 {
var dat reply.Config
if err := json.Unmarshal([]byte(m.Config), &dat); err == nil {
m.ShowEntry = dat.ShowEntry
m.ShowAdmin = dat.ShowAdmin
}
}
return
}

View File

@@ -0,0 +1,41 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewConfigDao(t *testing.T) {
convey.Convey("NewConfigDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewConfigDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyLoadConfig(t *testing.T) {
convey.Convey("LoadConfig", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
category = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
m, err := d.Config.LoadConfig(c, oid, tp, category)
ctx.Convey("Then err should be nil.m should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(m, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,98 @@
package reply
import (
"context"
"fmt"
"time"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_contSharding int64 = 200
)
const (
_selContSQL = "SELECT rpid,message,ats,ip,plat,device,topics FROM reply_content_%d WHERE rpid=?"
_selContsSQL = "SELECT rpid,message,ats,ip,plat,device,topics FROM reply_content_%d WHERE rpid IN (%s)"
_upContMsgSQL = "UPDATE reply_content_%d SET message=?,mtime=? WHERE rpid=?"
)
// ContentDao ContentDao
type ContentDao struct {
upContMsgStmts []*sql.Stmt
db *sql.DB
dbSlave *sql.DB
}
// NewContentDao new replyDao and return.
func NewContentDao(db *sql.DB, dbSlave *sql.DB) (dao *ContentDao) {
dao = &ContentDao{
db: db,
dbSlave: dbSlave,
upContMsgStmts: make([]*sql.Stmt, _contSharding),
}
for i := int64(0); i < _contSharding; i++ {
dao.upContMsgStmts[i] = dao.db.Prepared(fmt.Sprintf(_upContMsgSQL, i))
}
return
}
func (dao *ContentDao) hit(oid int64) int64 {
return oid % int64(_contSharding)
}
// UpMessage update content's message.
func (dao *ContentDao) UpMessage(c context.Context, oid int64, rpID int64, msg string, now time.Time) (rows int64, err error) {
res, err := dao.upContMsgStmts[dao.hit(oid)].Exec(c, msg, now, rpID)
if err != nil {
log.Error("contentDao.UpMessage error(%v)", err)
return
}
return res.RowsAffected()
}
// Get get reply content.
func (dao *ContentDao) Get(c context.Context, oid int64, rpID int64) (rc *reply.Content, err error) {
row := dao.db.QueryRow(c, fmt.Sprintf(_selContSQL, dao.hit(oid)), rpID)
rc = &reply.Content{}
if err = row.Scan(&rc.RpID, &rc.Message, &rc.Ats, &rc.IP, &rc.Plat, &rc.Device, &rc.Topics); err != nil {
if err == sql.ErrNoRows {
err = nil
rc = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// GetByIds get reply contents by reply ids.
func (dao *ContentDao) GetByIds(c context.Context, oid int64, rpIds []int64) (rcMap map[int64]*reply.Content, err error) {
if len(rpIds) == 0 {
return
}
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selContsSQL, dao.hit(oid), xstr.JoinInts(rpIds)))
if err != nil {
log.Error("contentDao.Query error(%v)", err)
return
}
defer rows.Close()
rcMap = make(map[int64]*reply.Content, len(rpIds))
for rows.Next() {
rc := &reply.Content{}
if err = rows.Scan(&rc.RpID, &rc.Message, &rc.Ats, &rc.IP, &rc.Plat, &rc.Device, &rc.Topics); err != nil {
log.Error("row.Scan error(%v)", err)
return
}
rcMap[rc.RpID] = rc
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,94 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
"go-common/library/database/sql"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewContentDao(t *testing.T) {
convey.Convey("NewContentDao", t, func(ctx convey.C) {
var (
db = sql.NewMySQL(conf.Conf.MySQL.Reply)
dbSlave = sql.NewMySQL(conf.Conf.MySQL.ReplySlave)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewContentDao(db, dbSlave)
recover()
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyContenthit(t *testing.T) {
convey.Convey("hit", t, func(ctx convey.C) {
var (
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.Content.hit(oid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyUpMessage(t *testing.T) {
convey.Convey("UpMessage", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
msg = ""
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Content.UpMessage(c, oid, rpID, msg, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyContentGet(t *testing.T) {
convey.Convey("Get", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rc, err := d.Content.Get(c, oid, rpID)
ctx.Convey("Then err should be nil.rc should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rc, convey.ShouldBeNil)
})
})
})
}
func TestReplyContentGetByIds(t *testing.T) {
convey.Convey("GetByIds", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpIds = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rcMap, err := d.Content.GetByIds(c, oid, rpIds)
ctx.Convey("Then err should be nil.rcMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rcMap, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,55 @@
package reply
import (
"context"
"fmt"
"net/url"
"go-common/app/interface/main/reply/conf"
"go-common/library/log"
httpx "go-common/library/net/http/blademaster"
)
// CreditUserDao CreditUserDao
type CreditUserDao struct {
requestURL string
httpClient *httpx.Client
}
// NewCreditDao NewCreditDao
func NewCreditDao(c *conf.Config) *CreditUserDao {
d := &CreditUserDao{
httpClient: httpx.NewClient(c.HTTPClient),
requestURL: c.Reply.CreditUserURL,
}
return d
}
// Result Result
type Result struct {
Code int `json:"code"`
Data map[int64]struct {
Mid int64 `json:"mid"`
Expired int64 `json:"expired"`
Status int `json:"status"`
} `json:"data"`
Msg string `json:"msg"`
}
// IsCreditUser IsCreditUser
func (dao *CreditUserDao) IsCreditUser(c context.Context, mid int64) (bool, error) {
var res Result
params := url.Values{}
params.Set("mids", fmt.Sprintf("%d", mid))
if err := dao.httpClient.Get(c, dao.requestURL, "", params, &res); err != nil {
log.Error("call 风纪委身份验证 url(%s) error(%v)", dao.requestURL+"?"+params.Encode(), err)
return false, err
}
if res.Code != 0 {
err := fmt.Errorf("call 风纪委身份验证 url(%s) error(%v)", dao.requestURL+"?"+params.Encode(), res.Code)
log.Error("%v", err)
return false, err
}
log.Info("call 风纪委身份验证 successful. url(%s)", dao.requestURL+"?"+params.Encode())
return res.Data[mid].Status == 1, nil
}

View File

@@ -0,0 +1,39 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewCreditDao(t *testing.T) {
convey.Convey("NewCreditDao", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := NewCreditDao(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyIsCreditUser(t *testing.T) {
convey.Convey("IsCreditUser", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.CreditUser.IsCreditUser(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,409 @@
package reply
import (
"context"
"errors"
"fmt"
"strconv"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
selectRootIDsByLatestFloorSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY floor DESC limit 0,%d"
selectRootIDsByCursorOnFloorSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) AND floor %s %d ORDER BY floor limit 0,%d"
selectRootIDsByRootStateSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state in (0,1,2,5,6) ORDER BY floor limit ?,?"
selectChildrenIDsByLatestFloorSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state in (0,1,2,5,6) ORDER BY floor ASC limit 0,%d"
selectChildrenIDsByCursorOnFloorSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state in (0,1,2,5,6) AND floor %s %d ORDER BY floor limit 0,%d"
)
// ErrCursorDirection ErrCursorDirection
var ErrCursorDirection = errors.New("error cursor direction")
// ChildrenIDsOfRootReply ChildrenIDsOfRootReply
func (dao *RpDao) ChildrenIDsOfRootReply(ctx context.Context,
oid, rootID int64, tp int8, offset, limit int) ([]int64, error) {
rows, err := dao.dbSlave.Query(ctx,
fmt.Sprintf(selectRootIDsByRootStateSQL, dao.hit(oid)),
oid, tp, rootID, offset, limit)
if err != nil {
log.Error("%v", err)
return nil, err
}
defer rows.Close()
ids := make([]int64, 0)
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("%v", err)
return nil, err
}
ids = append(ids, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return nil, err
}
return ids, nil
}
// CacheKeyRootReplyIDs CacheKeyRootReplyIDs
func (dao *RedisDao) CacheKeyRootReplyIDs(oid int64, tp, sort int8) string {
// subject:root_reply_ids
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d_%d", _prefixIdx, oid, tp, sort)
}
return _prefixIdx + strconv.FormatInt((oid<<16)|(int64(tp)<<8)|int64(sort), 10)
}
// ParentChildrenReplyIDMap ParentChildrenReplyIDMap
func (dao *RedisDao) ParentChildrenReplyIDMap(ctx context.Context,
parentIDs []int64, start, end int) (parentChildrenMap map[int64][]int64, missedIDs []int64, err error) {
parentChildrenMap = make(map[int64][]int64)
arrayOfChildrenIDs, missedKeys, err := dao.RangeChildrenReplyIDs(ctx,
genChildrenKeyByRootReplyIDs(parentIDs), start, end)
if err != nil {
return nil, nil, err
}
m := genChildrenKeyParentIDMap(parentIDs)
for _, k := range missedKeys {
if pid, ok := m[k]; ok {
missedIDs = append(missedIDs, pid)
}
}
for i, pid := range parentIDs {
parentChildrenMap[pid] = arrayOfChildrenIDs[i]
}
return parentChildrenMap, missedIDs, nil
}
// RangeChildrenReplyIDs RangeChildrenReplyIDs
func (dao *RedisDao) RangeChildrenReplyIDs(ctx context.Context,
keys []string, start, end int) (arrOfChildrenReplyIDs [][]int64, missedKeys []string, err error) {
if len(keys) == 0 {
return
}
conn := dao.redis.Get(ctx)
defer conn.Close()
for _, key := range keys {
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("%v", err)
return nil, nil, err
}
if err = conn.Send("ZRANGE", key, start, end); err != nil {
log.Error("%v", err)
return nil, nil, err
}
}
if err = conn.Flush(); err != nil {
log.Error("%v", err)
return nil, nil, err
}
arrOfChildrenReplyIDs = make([][]int64, 0)
missedKeys = make([]string, 0)
for _, key := range keys {
if exists, err := redis.Bool(conn.Receive()); err != nil {
log.Error("%v", err)
return nil, nil, err
} else if !exists {
missedKeys = append(missedKeys, key)
}
values, err := redis.Values(conn.Receive())
if err != nil {
log.Error("%v", err)
return nil, nil, err
}
if len(values) == 0 {
arrOfChildrenReplyIDs = append(arrOfChildrenReplyIDs, []int64{})
continue
}
var ids []int64
if err = redis.ScanSlice(values, &ids); err != nil {
log.Error("%v ", err)
return nil, nil, err
}
arrOfChildrenReplyIDs = append(arrOfChildrenReplyIDs, ids)
}
return arrOfChildrenReplyIDs, missedKeys, nil
}
// RangeChildrenIDByCursorScore RangeChildrenIDByCursorScore
func (dao *RedisDao) RangeChildrenIDByCursorScore(ctx context.Context, key string, cursor *model.Cursor) ([]int64, error) {
conn := dao.redis.Get(ctx)
defer conn.Close()
var (
vals []interface{}
err error
)
if cursor.Latest() {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, 0, "+inf", "LIMIT", 0, cursor.Len()))
} else if cursor.Descrease() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, cursor.Current(), "-inf", "LIMIT", 0, cursor.Len()))
} else if cursor.Increase() {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, cursor.Current(), "+inf", "LIMIT", 0, cursor.Len()))
} else {
err = ErrCursorDirection
}
if err != nil {
log.Error("%v", err)
return nil, err
}
replyIDs := make([]int64, 0)
if err = redis.ScanSlice(vals, &replyIDs); err != nil {
return nil, err
}
if cursor.Descrease() {
// ZREVRANGEBYSCORE to ASC
for i, j := 0, len(replyIDs)-1; i < j; i, j = i+1, j-1 {
replyIDs[i], replyIDs[j] = replyIDs[j], replyIDs[i]
}
}
return replyIDs, nil
}
// RangeRootIDByCursorScore RangeRootIDByCursorScore
func (dao *RedisDao) RangeRootIDByCursorScore(ctx context.Context, key string, cursor *model.Cursor) ([]int64, bool, error) {
conn := dao.redis.Get(ctx)
defer conn.Close()
var (
vals []interface{}
err error
)
if cursor.Latest() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, "+inf", 0, "LIMIT", 0, cursor.Len()))
} else if cursor.Increase() {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, cursor.Current(), "+inf", "LIMIT", 0, cursor.Len()))
} else if cursor.Descrease() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, cursor.Current(), "-inf", "LIMIT", 0, cursor.Len()))
} else {
err = ErrCursorDirection
}
if err != nil {
log.Error("%v", err)
return nil, false, err
}
replyIDs := make([]int64, 0)
if err = redis.ScanSlice(vals, &replyIDs); err != nil {
return nil, false, err
}
if len(replyIDs) > 0 && replyIDs[len(replyIDs)-1] == -1 {
replyIDs = replyIDs[:len(replyIDs)-1]
return replyIDs, true, nil
}
return replyIDs, false, nil
}
// RangeRootReplyIDs RangeRootReplyIDs
func (dao *RedisDao) RangeRootReplyIDs(ctx context.Context, key string, start, end int) ([]int64, error) {
conn := dao.redis.Get(ctx)
defer conn.Close()
vals, err := redis.Values(conn.Do("ZREVRANGE", key, start, end))
if err != nil {
log.Error("%v", err)
return nil, err
}
replyIDs := make([]int64, 0)
if err = redis.ScanSlice(vals, &replyIDs); err != nil {
log.Error("%v", err)
return nil, err
}
return replyIDs, nil
}
// ExpireCache ExpireCache
func (dao *RedisDao) ExpireCache(ctx context.Context, key string) (bool, error) {
conn := dao.redis.Get(ctx)
defer conn.Close()
ok, err := redis.Bool(conn.Do("EXPIRE", key, dao.expireRdsIdx))
if err != nil {
log.Error("%v", err)
return false, err
}
return ok, nil
}
// genChildrenKeyParentIDMap genChildrenKeyParentIDMap
func genChildrenKeyParentIDMap(ids []int64) map[string]int64 {
m := make(map[string]int64)
for _, id := range ids {
m[GenNewChildrenKeyByRootReplyID(id)] = id
}
return m
}
// genChildrenKeyByRootReplyIDs genChildrenKeyByRootReplyIDs
func genChildrenKeyByRootReplyIDs(ids []int64) []string {
ks := make([]string, len(ids))
for i, id := range ids {
ks[i] = GenNewChildrenKeyByRootReplyID(id)
}
return ks
}
// GenNewChildrenKeyByRootReplyID GenNewChildrenKeyByRootReplyID
func GenNewChildrenKeyByRootReplyID(id int64) string {
return _prefixRtIdx + strconv.FormatInt(id, 10)
}
// genChildrenKeyByRootReplyID genChildrenKeyByRootReplyID
func genChildrenKeyByRootReplyID(id int64) string {
// score: timestamp
// reply:id:ids
return _prefixRtIdx + strconv.FormatInt(id, 10)
}
// genReplyKeyByID genReplyKeyByID
func genReplyKeyByID(id int64) string {
// "subject:reply:ids"
return _prefixRp + strconv.FormatInt(id, 10)
}
func contains(arr []string, b string) bool {
for _, a := range arr {
if a == b {
return true
}
}
return false
}
// GetReplyByIDs GetReplyByIDs
func (dao *MemcacheDao) GetReplyByIDs(ctx context.Context, ids []int64) ([]*model.Reply, []int64, error) {
if len(ids) == 0 {
return []*model.Reply{}, []int64{}, nil
}
keys := make([]string, len(ids))
keyIDMap := make(map[string]int64)
for i, id := range ids {
key := genReplyKeyByID(id)
keys[i] = key
keyIDMap[key] = id
}
conn := dao.mc.Get(ctx)
defer conn.Close()
items, err := conn.GetMulti(keys)
if err != nil {
log.Error("%v", err)
return nil, nil, err
}
foundKeys := make([]string, 0)
for _, item := range items {
foundKeys = append(foundKeys, item.Key)
}
missedKeys := make([]string, 0)
if len(foundKeys) < len(keys) {
for _, key := range keys {
if !contains(foundKeys, key) {
missedKeys = append(missedKeys, key)
}
}
}
missedIDs := make([]int64, 0)
for _, mk := range missedKeys {
missedIDs = append(missedIDs, keyIDMap[mk])
}
var rs = make([]*model.Reply, 0)
for _, item := range items {
rp := new(model.Reply)
if err := conn.Scan(item, rp); err != nil {
log.Error("%v", err)
missedIDs = append(missedIDs, keyIDMap[item.Key])
continue
}
rs = append(rs, rp)
}
return rs, missedIDs, nil
}
// ChildrenIDSortByFloorCursor ChildrenIDSortByFloorCursor
func (dao *RpDao) ChildrenIDSortByFloorCursor(ctx context.Context, oid int64, tp int8, rootID int64, cursor *model.Cursor) ([]int64, error) {
var rawSQL string
if cursor.Latest() {
rawSQL = fmt.Sprintf(selectChildrenIDsByLatestFloorSQL, dao.hit(oid), cursor.Len())
} else if cursor.Descrease() {
rawSQL = fmt.Sprintf(selectChildrenIDsByCursorOnFloorSQL, dao.hit(oid), "<=", cursor.Current(), cursor.Len())
} else if cursor.Increase() {
rawSQL = fmt.Sprintf(selectChildrenIDsByCursorOnFloorSQL, dao.hit(oid), ">=", cursor.Current(), cursor.Len())
} else {
log.Error("%v", ErrCursorDirection)
return nil, ErrCursorDirection
}
rows, err := dao.dbSlave.Query(ctx, rawSQL, oid, tp, rootID)
if err != nil {
log.Error("%v", err)
return nil, err
}
defer rows.Close()
var id int64
res := make([]int64, 0, cursor.Len())
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("%v", err)
return nil, err
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return nil, err
}
return res, nil
}
// RootIDSortByFloorCursor RootIDSortByFloorCursor
func (dao *RpDao) RootIDSortByFloorCursor(ctx context.Context, oid int64, tp int8, cursor *model.Cursor) ([]int64, error) {
var rawSQL string
if cursor.Latest() {
rawSQL = fmt.Sprintf(selectRootIDsByLatestFloorSQL, dao.hit(oid), cursor.Len())
} else if cursor.Increase() {
rawSQL = fmt.Sprintf(selectRootIDsByCursorOnFloorSQL, dao.hit(oid), ">=", cursor.Current(), cursor.Len())
} else if cursor.Descrease() {
rawSQL = fmt.Sprintf(selectRootIDsByCursorOnFloorSQL, dao.hit(oid), "<=", cursor.Current(), cursor.Len())
} else {
log.Error("%v", ErrCursorDirection)
return nil, ErrCursorDirection
}
rows, err := dao.dbSlave.Query(ctx, rawSQL, oid, tp)
if err != nil {
log.Error("%v", err)
return nil, err
}
defer rows.Close()
var id int64
ids := make([]int64, 0, cursor.Len())
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("%v", err)
return nil, err
}
ids = append(ids, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return nil, err
}
return ids, nil
}

View File

@@ -0,0 +1,277 @@
package reply
import (
"context"
model "go-common/app/interface/main/reply/model/reply"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyChildrenIDsOfRootReply(t *testing.T) {
convey.Convey("ChildrenIDsOfRootReply", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rootID = int64(0)
tp = int8(0)
offset = int(0)
limit = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Reply.ChildrenIDsOfRootReply(c, oid, rootID, tp, offset, limit)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyCacheKeyRootReplyIDs(t *testing.T) {
convey.Convey("CacheKeyRootReplyIDs", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.Redis.CacheKeyRootReplyIDs(oid, tp, sort)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyParentChildrenReplyIDMap(t *testing.T) {
convey.Convey("ParentChildrenReplyIDMap", t, func(ctx convey.C) {
var (
c = context.Background()
parentIDs = []int64{}
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
parentChildrenMap, missedIDs, err := d.Redis.ParentChildrenReplyIDMap(c, parentIDs, start, end)
ctx.Convey("Then err should be nil.parentChildrenMap,missedIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missedIDs, convey.ShouldBeNil)
ctx.So(parentChildrenMap, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRangeChildrenReplyIDs(t *testing.T) {
convey.Convey("RangeChildrenReplyIDs", t, func(ctx convey.C) {
var (
c = context.Background()
keys = []string{}
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
arrOfChildrenReplyIDs, missedKeys, err := d.Redis.RangeChildrenReplyIDs(c, keys, start, end)
ctx.Convey("Then err should be nil.arrOfChildrenReplyIDs,missedKeys should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missedKeys, convey.ShouldBeNil)
ctx.So(arrOfChildrenReplyIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyRangeChildrenIDByCursorScore(t *testing.T) {
convey.Convey("RangeChildrenIDByCursorScore", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
cursor = &model.Cursor{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Redis.RangeChildrenIDByCursorScore(c, key, cursor)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRangeRootIDByCursorScore(t *testing.T) {
convey.Convey("RangeRootIDByCursorScore", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
cursor = &model.Cursor{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.Redis.RangeRootIDByCursorScore(c, key, cursor)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRangeRootReplyIDs(t *testing.T) {
convey.Convey("RangeRootReplyIDs", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Redis.RangeRootReplyIDs(c, key, start, end)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyExpireCache(t *testing.T) {
convey.Convey("ExpireCache", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Redis.ExpireCache(c, key)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplygenChildrenKeyParentIDMap(t *testing.T) {
convey.Convey("genChildrenKeyParentIDMap", t, func(ctx convey.C) {
var (
ids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := genChildrenKeyParentIDMap(ids)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplygenChildrenKeyByRootReplyIDs(t *testing.T) {
convey.Convey("genChildrenKeyByRootReplyIDs", t, func(ctx convey.C) {
var (
ids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := genChildrenKeyByRootReplyIDs(ids)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplygenNewChildrenKeyByRootReplyID(t *testing.T) {
convey.Convey("GenNewChildrenKeyByRootReplyID", t, func(ctx convey.C) {
var (
id = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := GenNewChildrenKeyByRootReplyID(id)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplygenReplyKeyByID(t *testing.T) {
convey.Convey("genReplyKeyByID", t, func(ctx convey.C) {
var (
id = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := genReplyKeyByID(id)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplycontains(t *testing.T) {
convey.Convey("contains", t, func(ctx convey.C) {
var (
arr = []string{}
b = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := contains(arr, b)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetReplyByIDs(t *testing.T) {
convey.Convey("GetReplyByIDs", t, func(ctx convey.C) {
var (
c = context.Background()
ids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, p2, err := d.Mc.GetReplyByIDs(c, ids)
ctx.Convey("Then err should be nil.p1,p2 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p2, convey.ShouldNotBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyChildrenIDSortByFloorCursor(t *testing.T) {
convey.Convey("ChildrenIDSortByFloorCursor", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rootID = int64(0)
cursor = &model.Cursor{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Reply.ChildrenIDSortByFloorCursor(c, oid, tp, rootID, cursor)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRootIDSortByFloorCursor(t *testing.T) {
convey.Convey("RootIDSortByFloorCursor", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
cursor = &model.Cursor{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Reply.RootIDSortByFloorCursor(c, oid, tp, cursor)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,83 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
"go-common/library/database/sql"
)
// Dao Dao
type Dao struct {
// memcache
Mc *MemcacheDao
// mysql
mysql *sql.DB
dbSlave *sql.DB
Admin *AdminDao
Content *ContentDao
Report *ReportDao
Reply *RpDao
Captcha *CaptchaDao
Notice *NoticeDao
CreditUser *CreditUserDao
Subject *SubjectDao
Config *ConfigDao
BlockStatus *BlockStatusDao
Business *BusinessDao
// redis
Redis *RedisDao
// kafka
Databus *DatabusDao
Emoji *EmoDao
}
// New New
func New(c *conf.Config) (d *Dao) {
d = &Dao{
// memchache
Mc: NewMemcacheDao(c.Memcache),
// mysql
mysql: sql.NewMySQL(c.MySQL.Reply),
dbSlave: sql.NewMySQL(c.MySQL.ReplySlave),
// redis
Redis: NewRedisDao(c.Redis),
Databus: NewDatabusDao(c.Databus),
}
d.Admin = NewAdminDao(d.mysql)
d.Content = NewContentDao(d.mysql, d.dbSlave)
d.Reply = NewReplyDao(d.mysql, d.dbSlave)
d.Report = NewReportDao(d.mysql)
d.Subject = NewSubjectDao(d.mysql)
d.Notice = NewNoticeDao(d.mysql)
d.Config = NewConfigDao(d.mysql)
d.Captcha = NewCaptchaDao(c.HTTPClient)
d.CreditUser = NewCreditDao(c)
d.BlockStatus = NewBlockStatusDao(c)
d.Business = NewBusinessDao(d.mysql)
d.Emoji = NewEmojiDao(d.mysql)
return
}
// Ping Ping
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.mysql.Ping(c); err != nil {
return
}
if err = d.Redis.Ping(c); err != nil {
return
}
return d.Mc.Ping(c)
}
// Close Close
func (d *Dao) Close() {
if d.Mc.mc != nil {
d.Mc.mc.Close()
}
if d.Redis.redis != nil {
d.Redis.redis.Close()
}
d.mysql.Close()
}

View File

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

View File

@@ -0,0 +1,559 @@
package reply
import (
"context"
"strconv"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/queue/databus"
)
// DatabusDao DatabusDao
type DatabusDao struct {
topic string
databus *databus.Databus
}
type kafkadata struct {
Op string `json:"op,omitempty"`
Mid int64 `json:"mid,omitempty"`
Adid int64 `json:"adid,omitempty"`
Oid int64 `json:"oid,omitempty"`
Rpid int64 `json:"rpid,omitempty"`
Root int64 `json:"root,omitempty"`
Dialog int64 `json:"dialog,omitempty"`
Remark string `json:"remark,omitempty"`
Adname string `json:"adname,omitempty"`
Mtime int64 `json:"mtime,omitempty"`
Action int8 `json:"action,omitempty"`
Sort int8 `json:"sort,omitempty"`
Tp int8 `json:"tp,omitempty"`
Moral int `json:"moral,omitempty"`
Notify bool `json:"notify,omitempty"`
Top uint32 `json:"top,omitempty"`
Ftime int64 `json:"ftime,omitempty"`
State int8 `json:"state,omitempty"`
Audit int8 `json:"audit,omitempty"`
Reason int8 `json:"reason,omitempty"`
Content string `json:"content,omitempty"`
FReason int8 `json:"freason,omitempty"`
Assist bool `json:"assist,omitempty"`
Count int `json:"count,omitempty"`
Floor int `json:"floor,omitempty"`
IsUp bool `json:"is_up,omitempty"`
}
// NewDatabusDao new ReplyKafkaDao and return.
func NewDatabusDao(c *databus.Config) (dao *DatabusDao) {
dao = &DatabusDao{
topic: c.Topic,
databus: databus.New(c),
}
return
}
// PubEvent pub reply event.
func (dao *DatabusDao) push(c context.Context, key string, value interface{}) error {
return dao.databus.Send(c, key, value)
}
// RecoverFixDialogIdx ...
func (dao *DatabusDao) RecoverFixDialogIdx(c context.Context, oid int64, tp int8, root int64) {
var message = map[string]interface{}{}
message["action"] = "fix_dialog"
message["data"] = kafkadata{
Oid: oid,
Tp: tp,
Root: root,
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// RecoverFolderIdx ...
func (dao *DatabusDao) RecoverFolderIdx(c context.Context, oid int64, tp int8, root int64) {
var message = map[string]interface{}{}
message["action"] = "folder"
message["data"] = kafkadata{
Op: "re_idx",
Oid: oid,
Tp: tp,
Root: root,
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// RecoverDialogIdx Recover dialog index
func (dao *DatabusDao) RecoverDialogIdx(c context.Context, oid int64, tp int8, root, dialog int64) {
var message = map[string]interface{}{}
message["action"] = "idx_dialog"
message["data"] = kafkadata{
Oid: oid,
Tp: tp,
Root: root,
Dialog: dialog,
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// RecoverFloorIdx RecoverFloorIdx
func (dao *DatabusDao) RecoverFloorIdx(c context.Context, oid int64, tp int8, num int, isFloor bool) {
var (
message = map[string]interface{}{}
)
message["action"] = "idx_floor"
if isFloor {
message["data"] = kafkadata{
Oid: oid,
Tp: tp,
Floor: num,
}
} else {
message["data"] = kafkadata{
Oid: oid,
Tp: tp,
Count: num,
}
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AddTop AddTop
func (dao *DatabusDao) AddTop(c context.Context, oid int64, tp int8, top uint32) {
var (
message = make(map[string]interface{})
)
message["action"] = "add_top"
message["data"] = kafkadata{
Oid: oid,
Tp: tp,
Top: top,
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AddReply push event message into kafka.
func (dao *DatabusDao) AddReply(c context.Context, oid int64, rp *reply.Reply) {
var (
message = make(map[string]interface{})
)
message["action"] = "add"
message["data"] = rp
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AddSpam push event message into kafka.
func (dao *DatabusDao) AddSpam(c context.Context, oid, mid int64, isUp bool, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "spam"
data := kafkadata{
Mid: mid,
IsUp: isUp,
Tp: tp,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AddReport push event message into kafka.
func (dao *DatabusDao) AddReport(c context.Context, oid, rpID int64, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "rpt"
data := kafkadata{
Oid: oid,
Rpid: rpID,
Tp: tp,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// Like push event message into kafka.
func (dao *DatabusDao) Like(c context.Context, oid, rpID, mid int64, action int8, ts int64) {
var (
message = make(map[string]interface{})
)
message["action"] = "act"
data := kafkadata{
Oid: oid,
Mid: mid,
Rpid: rpID,
Action: action,
Op: "like",
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// Hate push event message into kafka.
func (dao *DatabusDao) Hate(c context.Context, oid, rpID, mid int64, action int8, ts int64) {
var (
message = make(map[string]interface{})
)
message["action"] = "act"
data := kafkadata{
Oid: oid,
Mid: mid,
Rpid: rpID,
Action: action,
Op: "hate",
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// RecoverIndex push event message into kafka.
func (dao *DatabusDao) RecoverIndex(c context.Context, oid int64, tp, sort int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "re_idx"
data := kafkadata{
Oid: oid,
Tp: tp,
Sort: sort,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// RecoverIndexByRoot push event message into kafka.
func (dao *DatabusDao) RecoverIndexByRoot(c context.Context, oid, root int64, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "re_rt_idx"
data := kafkadata{
Oid: oid,
Tp: tp,
Root: root,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// Hide push event message into kafka.
func (dao *DatabusDao) Hide(c context.Context, oid, rpID int64, tp int8, ts int64) {
var (
message = make(map[string]interface{})
)
message["action"] = "up"
data := kafkadata{
Oid: oid,
Rpid: rpID,
Mtime: ts,
Tp: tp,
Op: "hide",
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// Show push event message into kafka.
func (dao *DatabusDao) Show(c context.Context, oid, rpID int64, tp int8, ts int64) {
var (
message = make(map[string]interface{})
)
message["action"] = "up"
data := kafkadata{
Op: "show",
Oid: oid,
Tp: tp,
Rpid: rpID,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// Delete push event message into kafka.
func (dao *DatabusDao) Delete(c context.Context, mid, oid, rpID int64, ts int64, tp int8, assist bool) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "del_up",
Oid: oid,
Mid: mid,
Rpid: rpID,
Tp: tp,
Mtime: ts,
Assist: assist,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminEdit push event message into kafka.
func (dao *DatabusDao) AdminEdit(c context.Context, oid, rpID int64, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "edit",
Oid: oid,
Tp: tp,
Rpid: rpID,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminAddTop push event message into kafka.
func (dao *DatabusDao) AdminAddTop(c context.Context, adid, oid, rpID, ts int64, act, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "top_add",
Oid: oid,
Adid: adid,
Rpid: rpID,
Tp: tp,
Action: act,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// UpperAddTop push event message into kafka.
func (dao *DatabusDao) UpperAddTop(c context.Context, mid, oid, rpID, ts int64, act, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "up"
data := kafkadata{
Op: "top_add",
Oid: oid,
Tp: tp,
Mid: mid,
Rpid: rpID,
Action: act,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminDelete push event message into kafka.
func (dao *DatabusDao) AdminDelete(c context.Context, adid, oid, rpID, ftime int64, moral int, notify bool, adname, remark string, ts int64, tp, reason, freason int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "del",
Adid: adid,
Rpid: rpID,
Oid: oid,
Moral: moral,
Notify: notify,
Tp: tp,
Adname: adname,
Remark: remark,
Mtime: ts,
Ftime: ftime,
Reason: reason,
FReason: freason,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminDeleteByReport push event message into kafka.
func (dao *DatabusDao) AdminDeleteByReport(c context.Context, adid, oid, rpID, mid, ftime int64, moral int, notify bool, adname, remark string, ts int64, tp, audit, reason int8, content string, freason int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "del_rpt",
Adid: adid,
Oid: oid,
Rpid: rpID,
Mid: mid,
Moral: moral,
Tp: tp,
Notify: notify,
Adname: adname,
Remark: remark,
Mtime: ts,
Ftime: ftime,
Audit: audit,
Reason: reason,
Content: content,
FReason: freason,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminRecover push event message into kafka.
func (dao *DatabusDao) AdminRecover(c context.Context, adid, oid, rpID int64, remark string, ts int64, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "re",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
Remark: remark,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminPass push pass event message into kafka.
func (dao *DatabusDao) AdminPass(c context.Context, adid, oid, rpID int64, remark string, ts int64, tp int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "pass",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
Remark: remark,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminStateSet push event message into kafka.
func (dao *DatabusDao) AdminStateSet(c context.Context, adid, oid, rpID, ts int64, tp, state int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "stateset",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
State: state,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminTransfer push event message into kafka.
func (dao *DatabusDao) AdminTransfer(c context.Context, adid, oid, rpID, ts int64, tp, audit int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
message["data"] = kafkadata{
Op: "transfer",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
Audit: audit,
Mtime: ts,
}
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminIgnore push event message into kafka.
func (dao *DatabusDao) AdminIgnore(c context.Context, adid, oid, rpID, ts int64, tp, audit int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "ignore",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
Audit: audit,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}
// AdminReportRecover push event message into kafka.
func (dao *DatabusDao) AdminReportRecover(c context.Context, adid, oid, rpID int64, remark string, ts int64, tp, audit int8) {
var (
message = make(map[string]interface{})
)
message["action"] = "admin"
data := kafkadata{
Op: "rpt_re",
Adid: adid,
Oid: oid,
Rpid: rpID,
Tp: tp,
Audit: audit,
Remark: remark,
Mtime: ts,
}
message["data"] = data
key := strconv.FormatInt(oid, 10)
dao.push(c, key, message)
}

View File

@@ -0,0 +1,496 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/queue/databus"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewDatabusDao(t *testing.T) {
convey.Convey("NewDatabusDao", t, func(ctx convey.C) {
var (
c = &databus.Config{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewDatabusDao(c)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplypush(t *testing.T) {
convey.Convey("push", t, func(ctx convey.C) {
var (
c = context.Background()
key = ""
value = interface{}(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Databus.push(c, key, value)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyRecoverFixDialogIdx(t *testing.T) {
convey.Convey("RecoverFixDialogIdx", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.RecoverFixDialogIdx(c, oid, tp, root)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyRecoverDialogIdx(t *testing.T) {
convey.Convey("RecoverDialogIdx", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
dialog = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.RecoverDialogIdx(c, oid, tp, root, dialog)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyRecoverFloorIdx(t *testing.T) {
convey.Convey("RecoverFloorIdx", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
num = int(0)
isFloor bool
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.RecoverFloorIdx(c, oid, tp, num, isFloor)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyDatabusAddTop(t *testing.T) {
convey.Convey("AddTop", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
top = uint32(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AddTop(c, oid, tp, top)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyDatabusAddReply(t *testing.T) {
convey.Convey("AddReply", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rp = &reply.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AddReply(c, oid, rp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAddSpam(t *testing.T) {
convey.Convey("AddSpam", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
mid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AddSpam(c, oid, mid, false, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAddReport(t *testing.T) {
convey.Convey("AddReport", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AddReport(c, oid, rpID, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyLike(t *testing.T) {
convey.Convey("Like", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
mid = int64(0)
action = int8(0)
ts = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.Like(c, oid, rpID, mid, action, ts)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyHate(t *testing.T) {
convey.Convey("Hate", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
mid = int64(0)
action = int8(0)
ts = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.Hate(c, oid, rpID, mid, action, ts)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyRecoverIndex(t *testing.T) {
convey.Convey("RecoverIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.RecoverIndex(c, oid, tp, sort)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyRecoverIndexByRoot(t *testing.T) {
convey.Convey("RecoverIndexByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
root = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.RecoverIndexByRoot(c, oid, root, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyHide(t *testing.T) {
convey.Convey("Hide", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
tp = int8(0)
ts = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.Hide(c, oid, rpID, tp, ts)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyShow(t *testing.T) {
convey.Convey("Show", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
tp = int8(0)
ts = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.Show(c, oid, rpID, tp, ts)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyDelete(t *testing.T) {
convey.Convey("Delete", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
tp = int8(0)
assist bool
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.Delete(c, mid, oid, rpID, ts, tp, assist)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminEdit(t *testing.T) {
convey.Convey("AdminEdit", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminEdit(c, oid, rpID, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminAddTop(t *testing.T) {
convey.Convey("AdminAddTop", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
act = int8(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminAddTop(c, adid, oid, rpID, ts, act, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyUpperAddTop(t *testing.T) {
convey.Convey("UpperAddTop", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
act = int8(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.UpperAddTop(c, mid, oid, rpID, ts, act, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminDelete(t *testing.T) {
convey.Convey("AdminDelete", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
ftime = int64(0)
moral = int(0)
notify bool
adname = ""
remark = ""
ts = int64(0)
tp = int8(0)
reason = int8(0)
freason = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminDelete(c, adid, oid, rpID, ftime, moral, notify, adname, remark, ts, tp, reason, freason)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminDeleteByReport(t *testing.T) {
convey.Convey("AdminDeleteByReport", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
mid = int64(0)
ftime = int64(0)
moral = int(0)
notify bool
adname = ""
remark = ""
ts = int64(0)
tp = int8(0)
audit = int8(0)
reason = int8(0)
content = ""
freason = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminDeleteByReport(c, adid, oid, rpID, mid, ftime, moral, notify, adname, remark, ts, tp, audit, reason, content, freason)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminRecover(t *testing.T) {
convey.Convey("AdminRecover", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
remark = ""
ts = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminRecover(c, adid, oid, rpID, remark, ts, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminPass(t *testing.T) {
convey.Convey("AdminPass", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
remark = ""
ts = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminPass(c, adid, oid, rpID, remark, ts, tp)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminStateSet(t *testing.T) {
convey.Convey("AdminStateSet", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
tp = int8(0)
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminStateSet(c, adid, oid, rpID, ts, tp, state)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminTransfer(t *testing.T) {
convey.Convey("AdminTransfer", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
tp = int8(0)
audit = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminTransfer(c, adid, oid, rpID, ts, tp, audit)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminIgnore(t *testing.T) {
convey.Convey("AdminIgnore", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
ts = int64(0)
tp = int8(0)
audit = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminIgnore(c, adid, oid, rpID, ts, tp, audit)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestReplyAdminReportRecover(t *testing.T) {
convey.Convey("AdminReportRecover", t, func(ctx convey.C) {
var (
c = context.Background()
adid = int64(0)
oid = int64(0)
rpID = int64(0)
remark = ""
ts = int64(0)
tp = int8(0)
audit = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.Databus.AdminReportRecover(c, adid, oid, rpID, remark, ts, tp, audit)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}

View File

@@ -0,0 +1,81 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
)
const (
_selEmojiSQL = "select id,package_id,name,url,state,remark from emoji where state=0 order by sort"
_selEmoByPidSQL = "select id,package_id,name,url,state,remark from emoji where state=0 and package_id=? order by sort"
_selEmojiPackageSQL = "select id,name,url,state from emoji_package where state=0 order by sort"
)
// EmoDao emoji dao
type EmoDao struct {
db *sql.DB
}
// NewEmojiDao NewEmojiDao
func NewEmojiDao(db *sql.DB) (dao *EmoDao) {
dao = &EmoDao{
db: db,
}
return
}
// EmojiList get all emoji
func (dao *EmoDao) EmojiList(c context.Context) (emo []*reply.Emoji, err error) {
rows, err := dao.db.Query(c, _selEmojiSQL)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
emoji := &reply.Emoji{}
if err = rows.Scan(&emoji.ID, &emoji.PackageID, &emoji.Name, &emoji.URL, &emoji.State, &emoji.Remark); err != nil {
return
}
emo = append(emo, emoji)
}
err = rows.Err()
return
}
// EmojiListByPid get emoji by package_id
func (dao *EmoDao) EmojiListByPid(c context.Context, pid int64) (emo []*reply.Emoji, err error) {
rows, err := dao.db.Query(c, _selEmoByPidSQL, pid)
if err != nil {
return
}
defer rows.Close()
emo = make([]*reply.Emoji, 0)
for rows.Next() {
emoji := &reply.Emoji{}
if err = rows.Scan(&emoji.ID, &emoji.PackageID, &emoji.Name, &emoji.URL, &emoji.State, &emoji.Remark); err != nil {
return
}
emo = append(emo, emoji)
}
err = rows.Err()
return
}
// ListEmojiPack get all emojipack
func (dao *EmoDao) ListEmojiPack(c context.Context) (packs []*reply.EmojiPackage, err error) {
rows, err := dao.db.Query(c, _selEmojiPackageSQL)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
p := &reply.EmojiPackage{}
if err = rows.Scan(&p.ID, &p.Name, &p.URL, &p.State); err != nil {
return
}
packs = append(packs, p)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,69 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewEmojiDao(t *testing.T) {
convey.Convey("NewEmojiDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewEmojiDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyEmojiList(t *testing.T) {
convey.Convey("EmojiList", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
emo, err := d.Emoji.EmojiList(c)
ctx.Convey("Then err should be nil.emo should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(emo, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyEmojiListByPid(t *testing.T) {
convey.Convey("EmojiListByPid", t, func(ctx convey.C) {
var (
c = context.Background()
pid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
emo, err := d.Emoji.EmojiListByPid(c, pid)
ctx.Convey("Then err should be nil.emo should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(emo, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyListEmojiPack(t *testing.T) {
convey.Convey("ListEmojiPack", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
packs, err := d.Emoji.ListEmojiPack(c)
ctx.Convey("Then err should be nil.packs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(packs, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,380 @@
package reply
import (
"context"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/reply/conf"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_prefixSub = "s_"
_prefixRp = "r_"
_prefixAdminTop = "at_"
_prefixUpperTop = "ut_"
_prefixConfig = "c_%d_%d_%d"
_prefixCaptcha = "pc_%d"
)
// MemcacheDao memcache dao.
type MemcacheDao struct {
mc *memcache.Pool
expire int32
emptyExpire int32
}
// NewMemcacheDao new a memcache dao and return.
func NewMemcacheDao(c *conf.Memcache) *MemcacheDao {
m := &MemcacheDao{
mc: memcache.NewPool(c.Config),
expire: int32(time.Duration(c.Expire) / time.Second),
emptyExpire: int32(time.Duration(c.EmptyExpire) / time.Second),
}
return m
}
func keyCaptcha(mid int64) string { return fmt.Sprintf(_prefixCaptcha, mid) }
func keyAdminTop(oid int64, tp int8) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixAdminTop, oid, tp)
}
return _prefixAdminTop + strconv.FormatInt((oid<<8)|int64(tp), 10)
}
func keyUpperTop(oid int64, tp int8) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixUpperTop, oid, tp)
}
return _prefixUpperTop + strconv.FormatInt((oid<<8)|int64(tp), 10)
}
func keySub(oid int64, tp int8) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixSub, oid, tp)
}
return _prefixSub + strconv.FormatInt((oid<<8)|int64(tp), 10)
}
func keyRp(rpID int64) string {
return _prefixRp + strconv.FormatInt(rpID, 10)
}
func keyConfig(oid int64, typ, category int8) string {
return fmt.Sprintf(_prefixConfig, oid, typ, category)
}
// Ping check connection success.
func (dao *MemcacheDao) Ping(c context.Context) (err error) {
conn := dao.mc.Get(c)
item := memcache.Item{Key: "ping", Value: []byte{1}, Expiration: dao.expire}
err = conn.Set(&item)
conn.Close()
return
}
// CaptchaToken CaptchaToken
func (dao *MemcacheDao) CaptchaToken(c context.Context, mid int64) (string, error) {
conn := dao.mc.Get(c)
defer conn.Close()
item, err := conn.Get(keyCaptcha(mid))
if err == memcache.ErrNotFound {
return "", nil
}
if err != nil {
return "", err
}
var token string
if err = conn.Scan(item, &token); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
return "", err
}
return token, nil
}
// SetCaptchaToken SetCaptchaToken
func (dao *MemcacheDao) SetCaptchaToken(c context.Context, mid int64, token string) error {
conn := dao.mc.Get(c)
defer conn.Close()
return conn.Set(&memcache.Item{
Key: keyCaptcha(mid),
Value: []byte(token),
Expiration: int32(time.Minute * 5 / time.Second),
})
}
// GetSubject get subject from memcache.
func (dao *MemcacheDao) GetSubject(c context.Context, oid int64, tp int8) (sub *model.Subject, err error) {
key := keySub(oid, tp)
conn := dao.mc.Get(c)
defer conn.Close()
item, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
return
}
sub = new(model.Subject)
if err = conn.Scan(item, sub); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
sub = nil
}
return
}
// GetMultiSubject get subject from memcache.
func (dao *MemcacheDao) GetMultiSubject(c context.Context, oids []int64, tp int8) (res map[int64]*model.Subject, missed []int64, err error) {
var (
keys = make([]string, len(oids))
missKeys = make(map[string]int64, len(oids))
)
for i, oid := range oids {
key := keySub(oid, tp)
keys[i] = key
missKeys[key] = oid
}
conn := dao.mc.Get(c)
defer conn.Close()
items, err := conn.GetMulti(keys)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
missed = oids
return
}
res = make(map[int64]*model.Subject, len(items))
for _, item := range items {
sub := new(model.Subject)
if err = conn.Scan(item, sub); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
continue
}
res[sub.Oid] = sub
delete(missKeys, item.Key)
}
missed = make([]int64, 0, len(missKeys))
for _, oid := range missKeys {
missed = append(missed, oid)
}
return
}
// DeleteSubject delete subject from memcache.
func (dao *MemcacheDao) DeleteSubject(c context.Context, oid int64, tp int8) (err error) {
key := keySub(oid, tp)
conn := dao.mc.Get(c)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
} else {
log.Error("conn.Delete(%s) error(%v)", key, err)
}
}
conn.Close()
return
}
// AddSubject add subject into memcache.
func (dao *MemcacheDao) AddSubject(c context.Context, subs ...*model.Subject) (err error) {
if len(subs) == 0 {
return
}
conn := dao.mc.Get(c)
for _, sub := range subs {
exp := dao.expire
if sub.ID == -1 {
exp = dao.emptyExpire
}
key := keySub(sub.Oid, sub.Type)
item := &memcache.Item{Key: key, Object: sub, Expiration: exp, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, sub, err)
}
}
conn.Close()
return
}
// AddReply add reply into memcache.
func (dao *MemcacheDao) AddReply(c context.Context, rs ...*model.Reply) (err error) {
if len(rs) == 0 {
return
}
conn := dao.mc.Get(c)
for _, r := range rs {
if r == nil {
continue
}
key := keyRp(r.RpID)
item := &memcache.Item{Key: key, Object: r, Expiration: dao.expire, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, r, err)
}
}
conn.Close()
return
}
// AddTop add top reply into memcache.
func (dao *MemcacheDao) AddTop(c context.Context, oid int64, tp int8, rp *model.Reply) (err error) {
if rp == nil {
return
}
var key string
if rp.AttrVal(model.ReplyAttrAdminTop) == 1 {
key = keyAdminTop(oid, tp)
} else if rp.AttrVal(model.ReplyAttrUpperTop) == 1 {
key = keyUpperTop(oid, tp)
} else {
return
}
conn := dao.mc.Get(c)
defer conn.Close()
item := &memcache.Item{Key: key, Object: rp, Expiration: dao.expire, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, rp, err)
}
return
}
// DeleteReply delete reply from memcache.
func (dao *MemcacheDao) DeleteReply(c context.Context, rpID int64) (err error) {
key := keyRp(rpID)
conn := dao.mc.Get(c)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
} else {
log.Error("conn.Delete(%s) error(%v)", key, err)
}
}
conn.Close()
return
}
// GetTop get subject top reply from memcache
func (dao *MemcacheDao) GetTop(c context.Context, oid int64, tp int8, top uint32) (rp *model.Reply, err error) {
var key string
if top == model.ReplyAttrUpperTop {
key = keyUpperTop(oid, tp)
} else if top == model.ReplyAttrAdminTop {
key = keyAdminTop(oid, tp)
} else {
return
}
conn := dao.mc.Get(c)
defer conn.Close()
item, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
return
}
rp = new(model.Reply)
if err = conn.Scan(item, &rp); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
rp = nil
}
return
}
// GetReply get reply from memcache.
func (dao *MemcacheDao) GetReply(c context.Context, rpID int64) (rp *model.Reply, err error) {
key := keyRp(rpID)
conn := dao.mc.Get(c)
defer conn.Close()
item, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
return
}
rp = new(model.Reply)
if err = conn.Scan(item, rp); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
rp = nil
}
return
}
// GetMultiReply multi get replies from memcache.
func (dao *MemcacheDao) GetMultiReply(c context.Context, rpIDs []int64) (rpMap map[int64]*model.Reply, missed []int64, err error) {
if len(rpIDs) == 0 {
return
}
rpMap = make(map[int64]*model.Reply, len(rpIDs))
keys := make([]string, len(rpIDs))
mm := make(map[string]int64, len(rpIDs))
for i, rpID := range rpIDs {
key := keyRp(rpID)
keys[i] = key
mm[key] = rpID
}
conn := dao.mc.Get(c)
defer conn.Close()
items, err := conn.GetMulti(keys)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
return
}
for _, item := range items {
rp := new(model.Reply)
if err = conn.Scan(item, rp); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
continue
}
rpMap[mm[item.Key]] = rp
delete(mm, item.Key)
}
missed = make([]int64, 0, len(mm))
for _, valIn := range mm {
missed = append(missed, valIn)
}
return
}
// GetReplyConfig get reply configuration from memocache by oid and type value
func (dao *MemcacheDao) GetReplyConfig(c context.Context, oid int64, typ, category int8) (config *model.Config, err error) {
key := keyConfig(oid, typ, 1)
conn := dao.mc.Get(c)
defer conn.Close()
item, err := conn.Get(key)
if err != nil {
if err == memcache.ErrNotFound {
err = nil
}
return
}
config = new(model.Config)
if err = conn.Scan(item, config); err != nil {
log.Error("conn.Scan(%s) error(%v)", item.Value, err)
config = nil
}
return
}
// AddReplyConfigCache add/update reply configuration cache from memcache
func (dao *MemcacheDao) AddReplyConfigCache(c context.Context, m *model.Config) (err error) {
key := keyConfig(m.Oid, m.Type, m.Category)
conn := dao.mc.Get(c)
item := &memcache.Item{Key: key, Object: m, Expiration: dao.expire, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, m, err)
}
conn.Close()
return
}

View File

@@ -0,0 +1,356 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
model "go-common/app/interface/main/reply/model/reply"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewMemcacheDao(t *testing.T) {
convey.Convey("NewMemcacheDao", t, func(ctx convey.C) {
var (
c = conf.Conf.Memcache
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := NewMemcacheDao(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyCaptcha(t *testing.T) {
convey.Convey("keyCaptcha", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyCaptcha(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyAdminTop(t *testing.T) {
convey.Convey("keyAdminTop", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyAdminTop(oid, tp)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyUpperTop(t *testing.T) {
convey.Convey("keyUpperTop", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyUpperTop(oid, tp)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeySub(t *testing.T) {
convey.Convey("keySub", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keySub(oid, tp)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyRp(t *testing.T) {
convey.Convey("keyRp", t, func(ctx convey.C) {
var (
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyRp(rpID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyConfig(t *testing.T) {
convey.Convey("keyConfig", t, func(ctx convey.C) {
var (
oid = int64(0)
typ = int8(0)
category = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyConfig(oid, typ, category)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyMcPing(t *testing.T) {
convey.Convey("Ping", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.Ping(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyMcCaptchaToken(t *testing.T) {
convey.Convey("CaptchaToken", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1, err := d.Mc.CaptchaToken(c, mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySetCaptchaToken(t *testing.T) {
convey.Convey("SetCaptchaToken", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
token = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.SetCaptchaToken(c, mid, token)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetSubject(t *testing.T) {
convey.Convey("GetSubject", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
sub, err := d.Mc.GetSubject(c, oid, tp)
ctx.Convey("Then err should be nil.sub should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(sub, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetMultiSubject(t *testing.T) {
convey.Convey("GetMultiSubject", t, func(ctx convey.C) {
var (
c = context.Background()
oids = []int64{1322313213123}
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, missed, err := d.Mc.GetMultiSubject(c, oids, tp)
ctx.Convey("Then err should be nil.res,missed should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missed, convey.ShouldNotBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyDeleteSubject(t *testing.T) {
convey.Convey("DeleteSubject", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.DeleteSubject(c, oid, tp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddSubject(t *testing.T) {
convey.Convey("AddSubject", t, func(ctx convey.C) {
var (
c = context.Background()
subs = &model.Subject{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.AddSubject(c, subs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddReply(t *testing.T) {
convey.Convey("AddReply", t, func(ctx convey.C) {
var (
c = context.Background()
rs = &model.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.AddReply(c, rs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddTop(t *testing.T) {
convey.Convey("AddTop", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rp = &model.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.AddTop(c, oid, tp, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyDeleteReply(t *testing.T) {
convey.Convey("DeleteReply", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.DeleteReply(c, rpID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetTop(t *testing.T) {
convey.Convey("GetTop", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
top = uint32(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rp, err := d.Mc.GetTop(c, oid, tp, top)
ctx.Convey("Then err should be nil.rp should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rp, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetReply(t *testing.T) {
convey.Convey("GetReply", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rp, err := d.Mc.GetReply(c, rpID)
ctx.Convey("Then err should be nil.rp should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rp, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetMultiReply(t *testing.T) {
convey.Convey("GetMultiReply", t, func(ctx convey.C) {
var (
c = context.Background()
rpIDs = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpMap, missed, err := d.Mc.GetMultiReply(c, rpIDs)
ctx.Convey("Then err should be nil.rpMap,missed should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(missed, convey.ShouldBeNil)
ctx.So(rpMap, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetReplyConfig(t *testing.T) {
convey.Convey("GetReplyConfig", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
typ = int8(0)
category = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
config, err := d.Mc.GetReplyConfig(c, oid, typ, category)
ctx.Convey("Then err should be nil.config should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(config, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddReplyConfigCache(t *testing.T) {
convey.Convey("AddReplyConfigCache", t, func(ctx convey.C) {
var (
c = context.Background()
m = &model.Config{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Mc.AddReplyConfigCache(c, m)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,52 @@
package reply
import (
"context"
"time"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_selAllNoticeSQL = `SELECT id,plat,condi,build,title,content,link,client_type FROM notice WHERE stime <? and etime> ? and status=1 `
)
// NoticeDao notice dao.
type NoticeDao struct {
db *sql.DB
}
// NewNoticeDao new a notice dao and return.
func NewNoticeDao(db *sql.DB) (dao *NoticeDao) {
dao = &NoticeDao{
db: db,
}
return
}
// ReplyNotice get reply notice infos from db
func (dao *NoticeDao) ReplyNotice(c context.Context) (nts []*reply.Notice, err error) {
now := time.Now()
rows, err := dao.db.Query(c, _selAllNoticeSQL, now, now)
if err != nil {
log.Error("dao.selAllResStmt query error (%v)", err)
return
}
defer rows.Close()
nts = make([]*reply.Notice, 0)
for rows.Next() {
nt := &reply.Notice{}
if err = rows.Scan(&nt.ID, &nt.Plat, &nt.Condition, &nt.Build, &nt.Title, &nt.Content, &nt.Link, &nt.ClientType); err != nil {
log.Error("rows.Scan err (%v)", err)
return
}
nts = append(nts, nt)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,38 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewNoticeDao(t *testing.T) {
convey.Convey("NewNoticeDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewNoticeDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReplyNotice(t *testing.T) {
convey.Convey("ReplyNotice", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
nts, err := d.Notice.ReplyNotice(c)
ctx.Convey("Then err should be nil.nts should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(nts, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,813 @@
package reply
import (
"context"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/model/reply"
"go-common/app/interface/main/reply/model/xreply"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_prefixIdx = "i_"
_prefixRtIdx = "ri_"
_prefixRpt = "rt_"
_prefixLike = "l_"
_prefixAuditIdx = "ai_%d_%d"
_prefixDialogIdx = "d_%d"
// f_{折叠类型,根评论还是评论区}_{评论区ID或者根评论ID}
_foldedReplyFmt = "f_%s_%d"
_prefixSpamRec = "sr_"
_prefixSpamDaily = "sd_"
_prefixSpamAct = "sa_"
_prefixTopOid = "tro_"
)
const (
_oidOverflow = 1 << 48
)
// RedisDao RedisDao
type RedisDao struct {
redis *redis.Pool
expireRdsIdx int
expireRdsRpt int
expireRdsUC int
expireUserAct int
}
// NewRedisDao NewRedisDao
func NewRedisDao(c *conf.Redis) *RedisDao {
r := &RedisDao{
redis: redis.NewPool(c.Config),
expireRdsIdx: int(time.Duration(c.IndexExpire) / time.Second),
expireRdsRpt: int(time.Duration(c.ReportExpire) / time.Second),
expireRdsUC: int(time.Duration(c.UserCntExpire) / time.Second),
expireUserAct: int(time.Duration(c.UserActExpire) / time.Second),
}
return r
}
func keyRcntCnt(mid int64) string {
return "rc_" + strconv.FormatInt(mid, 10)
}
func keyUpRcntCnt(mid int64) string {
return "urc_" + strconv.FormatInt(mid, 10)
}
func keyDialogIdx(dialogID int64) string {
return fmt.Sprintf(_prefixDialogIdx, dialogID)
}
func keyFolderIdx(kind string, ID int64) string {
return fmt.Sprintf(_foldedReplyFmt, kind, ID)
}
func keyIdx(oid int64, tp, sort int8) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d_%d", _prefixIdx, oid, tp, sort)
}
return _prefixIdx + strconv.FormatInt((oid<<16)|(int64(tp)<<8)|int64(sort), 10)
}
func keyAuditIdx(oid int64, tp int8) string {
return fmt.Sprintf(_prefixAuditIdx, oid, tp)
}
func keyRtIdx(rpID int64) string {
return _prefixRtIdx + strconv.FormatInt(rpID, 10)
}
func keyRpt(mid int64, now time.Time) string {
return _prefixRpt + strconv.FormatInt(mid, 10) + "_" + strconv.Itoa(now.Day())
}
func keyLike(rpID int64) string {
return _prefixLike + strconv.FormatInt(rpID, 10)
}
func keySpamRpRec(mid int64) string {
return _prefixSpamRec + strconv.FormatInt(mid, 10)
}
func keySpamRpDaily(mid int64) string {
return _prefixSpamDaily + strconv.FormatInt(mid, 10)
}
func keySpamActRec(mid int64) string {
return _prefixSpamAct + strconv.FormatInt(mid, 10)
}
func keyTopOid(tp int8) string {
return _prefixTopOid + strconv.FormatInt(int64(tp), 10)
}
// Ping check connection success.
func (dao *RedisDao) Ping(c context.Context) (err error) {
conn := dao.redis.Get(c)
defer conn.Close()
_, err = conn.Do("GET", "PING")
return
}
// AddFloorIndex add index by floor.
func (dao *RedisDao) AddFloorIndex(c context.Context, oid int64, tp int8, rs ...*reply.Reply) (err error) {
if len(rs) == 0 {
return
}
key := keyIdx(oid, tp, reply.SortByFloor)
conn := dao.redis.Get(c)
defer conn.Close()
for _, r := range rs {
if err = conn.Send("ZADD", key, r.Floor, r.RpID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
}
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < len(rs)+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddCountIndex add index by count.
func (dao *RedisDao) AddCountIndex(c context.Context, oid int64, tp int8, rs ...*reply.Reply) (err error) {
if len(rs) == 0 {
return
}
key := keyIdx(oid, tp, reply.SortByCount)
conn := dao.redis.Get(c)
defer conn.Close()
for _, r := range rs {
if err = conn.Send("ZADD", key, int64(r.RCount)<<32|(int64(r.Floor)&0xFFFFFFFF), r.RpID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
}
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < len(rs)+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddLikeIndex add index by like.
func (dao *RedisDao) AddLikeIndex(c context.Context, oid int64, tp int8, r *reply.Reply, rpt *reply.Report) (err error) {
var (
count int
rptCnt int
)
key := keyIdx(oid, tp, reply.SortByLike)
conn := dao.redis.Get(c)
defer conn.Close()
if r.Like >= 3 && (r.Attr&0x3 == 0) {
if rpt != nil {
rptCnt = rpt.Count
}
score := int64((float32(r.Like+2) / float32(r.Hate+rptCnt+4)) * 100)
if err = conn.Send("ZADD", key, score<<32|(int64(r.RCount)&0xFFFFFFFF), r.RpID); err != nil {
log.Error("conn.Send(ZADD %s,%d) error(%v)", key, r.RpID, err)
return
}
count++
} else if r.Like < 3 {
if err = conn.Send("ZREM", key, r.RpID); err != nil {
log.Error("conn.Send(ZREM %s,%d) error(%v)", key, r.RpID, err)
return
}
count++
}
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < count+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// DelIndex delete reply index.
func (dao *RedisDao) DelIndex(c context.Context, rp *reply.Reply) (err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if rp.Root == 0 {
key := keyIdx(rp.Oid, rp.Type, reply.SortByFloor)
err = conn.Send("ZREM", key, rp.RpID)
key = keyIdx(rp.Oid, rp.Type, reply.SortByCount)
err = conn.Send("ZREM", key, rp.RpID)
key = keyIdx(rp.Oid, rp.Type, reply.SortByLike)
err = conn.Send("ZREM", key, rp.RpID)
} else {
key := keyRtIdx(rp.Root)
err = conn.Send("ZREM", key, rp.RpID)
}
if err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
if rp.Root == 0 {
for i := 0; i < 3; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
return
}
}
} else {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
}
}
return
}
// AddFloorIndexByRoot add root reply index by floor.
func (dao *RedisDao) AddFloorIndexByRoot(c context.Context, root int64, rs ...*reply.Reply) (err error) {
if len(rs) == 0 {
return
}
key := keyRtIdx(root)
conn := dao.redis.Get(c)
defer conn.Close()
for _, r := range rs {
if err = conn.Send("ZADD", key, r.CTime, r.RpID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
}
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < len(rs)+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// AddLike add actions into redis
func (dao *RedisDao) AddLike(c context.Context, rpID int64, ras ...*reply.Action) (err error) {
if len(ras) == 0 {
return
}
key := keyLike(rpID)
conn := dao.redis.Get(c)
defer conn.Close()
for _, r := range ras {
if err = conn.Send("ZADD", key, r.CTime, r.Mid); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
}
if err = conn.Send("EXPIRE", key, dao.expireRdsIdx); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < len(ras)+1; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// DelLike add actions into redis
func (dao *RedisDao) DelLike(c context.Context, rpID int64, ra *reply.Action) (err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("ZREM", keyLike(rpID), ra.Mid); err != nil {
log.Error("conn.Receive() error(%v)", err)
}
return
}
// ExpireLike set expire time for action.
func (dao *RedisDao) ExpireLike(c context.Context, rpID int64) (ok bool, err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyLike(rpID), dao.expireRdsIdx)); err != nil {
log.Error("conn.Receive() error(%v)", err)
}
return
}
func (dao *RedisDao) Range(c context.Context, oid int64, tp, sort int8, start, end int) (rpIds []int64, isEnd bool, err error) {
key := keyIdx(oid, tp, sort)
conn := dao.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZREVRANGE", key, start, end))
if err != nil {
log.Error("conn.Do(ZREVRANGE, %s) error(%v)", key, err)
return
}
if len(values) == 0 {
return
}
err = redis.ScanSlice(values, &rpIds)
if len(rpIds) > 0 && rpIds[len(rpIds)-1] == -1 {
rpIds = rpIds[:len(rpIds)-1]
isEnd = true
}
return
}
// CountReplies CountReplies
func (dao *RedisDao) CountReplies(c context.Context, oid int64, tp, sort int8) (count int, err error) {
key := keyIdx(oid, tp, sort)
conn := dao.redis.Get(c)
defer conn.Close()
if count, err = redis.Int(conn.Do("ZCARD", key)); err != nil {
log.Error("conn.Do(ZCARD, %s) error(%v)", key, err)
}
return
}
// UserAuditReplies return user audit replies.
func (dao *RedisDao) UserAuditReplies(c context.Context, mid, oid int64, tp int8) (rpIds []int64, err error) {
key := keyAuditIdx(oid, tp)
conn := dao.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZRANGEBYSCORE", key, mid, mid))
if err != nil {
log.Error("conn.Do(RANGE, %s) error(%v)", key, err)
return
}
if len(values) == 0 {
return
}
err = redis.ScanSlice(values, &rpIds)
return
}
// RangeByRoot range root's replyies.
func (dao *RedisDao) RangeByRoot(c context.Context, root int64, start, end int) (rpIds []int64, err error) {
key := keyRtIdx(root)
conn := dao.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZRANGE", key, start, end))
if err != nil {
log.Error("conn.Do(ZRANGE, %s) error(%v)", key, err)
return
}
if len(values) == 0 {
return
}
err = redis.ScanSlice(values, &rpIds)
return
}
// RangeByOids range oids
func (dao *RedisDao) RangeByOids(c context.Context, oids []int64, tp, sort, start, end int8) (oidMap map[int64][]int64, miss []int64, err error) {
oidMap = make(map[int64][]int64)
conn := dao.redis.Get(c)
defer conn.Close()
for _, oid := range oids {
if err = conn.Send("EXPIRE", keyIdx(oid, tp, sort), dao.expireRdsIdx); err != nil {
log.Error("conn.Send(EXPIRE) err(%v)", err)
return
}
if err = conn.Send("ZREVRANGE", keyIdx(oid, tp, sort), start, end-1); err != nil {
log.Error("conn.Send(ZREVRANGE) err(%v)", err)
return
}
}
if err = conn.Flush(); err != nil {
log.Error("conn.SEND(FLUSH) err(%v)", err)
return
}
for _, oid := range oids {
var (
rpids []int64
values []interface{}
)
if _, err = conn.Receive(); err != nil {
log.Error("redis.Bool() err(%v)", err)
return
}
if values, err = redis.Values(conn.Receive()); err != nil {
log.Error("redis.Values() err(%v)", err)
return
}
if len(values) == 0 {
miss = append(miss, oid)
continue
}
if err = redis.ScanSlice(values, &rpids); err != nil {
log.Error("redis.ScanSlice() err(%v) ", err)
return
}
oidMap[oid] = rpids
}
return
}
// RangeByRoots range roots's replyies.
func (dao *RedisDao) RangeByRoots(c context.Context, roots []int64, start, end int) (mrpids map[int64][]int64, idx, miss []int64, err error) {
conn := dao.redis.Get(c)
defer conn.Close()
for _, root := range roots {
// if exist delay expire time
if err = conn.Send("EXPIRE", keyRtIdx(root), dao.expireRdsIdx); err != nil {
log.Error("conn.Send(EXPIRE) err(%v)", err)
return
}
if err = conn.Send("ZRANGE", keyRtIdx(root), start, end); err != nil {
log.Error("conn.Send(ZRANGE) err(%v)", err)
return
}
}
if err = conn.Flush(); err != nil {
log.Error("conn.SEND(FLUSH) err(%v)", err)
return
}
mrpids = make(map[int64][]int64, len(roots))
for _, root := range roots {
var (
rpids []int64
values []interface{}
)
if _, err = conn.Receive(); err != nil {
log.Error("redis.Bool() err(%v)", err)
return
}
if values, err = redis.Values(conn.Receive()); err != nil {
log.Error("redis.Values() err(%v)", err)
return
}
if len(values) == 0 {
miss = append(miss, root)
continue
}
if err = redis.ScanSlice(values, &rpids); err != nil {
log.Error("redis.ScanSlice() err(%v) ", err)
return
}
idx = append(idx, rpids...)
mrpids[root] = rpids
}
return
}
// ExpireIndex set expire time for index.
func (dao *RedisDao) ExpireIndex(c context.Context, oid int64, tp, sort int8) (ok bool, err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyIdx(oid, tp, sort), dao.expireRdsIdx)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// ExpireIndexByRoot set expire time for root's index.
func (dao *RedisDao) ExpireIndexByRoot(c context.Context, root int64) (ok bool, err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyRtIdx(root), dao.expireRdsIdx)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// SetUserReportCnt set user report count.
func (dao *RedisDao) SetUserReportCnt(c context.Context, mid int64, count int, now time.Time) (err error) {
key := keyRpt(mid, now)
conn := dao.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("SETEX", key, dao.expireRdsRpt, count); err != nil {
log.Error("conn.Do(SETEX) error(%v)", err)
}
return
}
// GetUserReportCnt get user report count.
func (dao *RedisDao) GetUserReportCnt(c context.Context, mid int64, now time.Time) (count int, err error) {
key := keyRpt(mid, now)
conn := dao.redis.Get(c)
defer conn.Close()
if count, err = redis.Int(conn.Do("GET", key)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("conn.Do(GET) error(%v)", err)
}
}
return
}
// GetUserReportTTL get TTL of user report count redis.
func (dao *RedisDao) GetUserReportTTL(c context.Context, mid int64, now time.Time) (ttl int, err error) {
key := keyRpt(mid, now)
conn := dao.redis.Get(c)
defer conn.Close()
if ttl, err = redis.Int(conn.Do("TTL", key)); err != nil {
log.Error("conn.Do(TTl) error(%v)", err)
}
return
}
// RankIndex get rank from reply index.
func (dao *RedisDao) RankIndex(c context.Context, oid int64, tp int8, rpID int64, sort int8) (rank int, err error) {
key := keyIdx(oid, tp, sort)
conn := dao.redis.Get(c)
defer conn.Close()
if rank, err = redis.Int(conn.Do("ZREVRANK", key, rpID)); err != nil {
if err == redis.ErrNil {
rank = -1
err = nil
} else {
log.Error("conn.Do(ZREVRANK) error(%v)", err)
}
}
return
}
// RankIndexByRoot get rank from root reply index.
func (dao *RedisDao) RankIndexByRoot(c context.Context, root int64, rpID int64) (rank int, err error) {
key := keyRtIdx(root)
conn := dao.redis.Get(c)
defer conn.Close()
if rank, err = redis.Int(conn.Do("ZRANK", key, rpID)); err != nil {
if err == redis.ErrNil {
rank = -1
err = nil
} else {
log.Error("conn.Do(ZRANK) error(%v)", err)
}
}
return
}
// OidHaveTop OidHaveTop
func (dao *RedisDao) OidHaveTop(c context.Context, oid int64, tp int8) (ok bool, err error) {
key := keyTopOid(tp)
conn := dao.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("SISMEMBER", key, oid)); err != nil {
log.Error("OidHavaTop.Do error(%v)", err)
}
return
}
// DelReplyIncr reply cd key
func (dao *RedisDao) DelReplyIncr(c context.Context, mid int64, isUp bool) (err error) {
key := keyRcntCnt(mid)
if isUp {
key = keyUpRcntCnt(mid)
}
conn := dao.redis.Get(c)
defer conn.Close()
_, err = conn.Do("DEL", key)
if err != nil {
log.Error("DelReplyIncr redis failed!err:=%v key:%s", err, key)
}
return
}
// DelReplyIncr reply cd key
func (dao *RedisDao) DelReplySpam(c context.Context, mid int64) (err error) {
key := keySpamRpRec(mid)
conn := dao.redis.Get(c)
defer conn.Close()
_, err = conn.Do("DEL", key)
if err != nil {
log.Error("DelReplySpam redis failed!err:=%v key:%s", err, key)
}
return
}
// SpamReply SpamReply
func (dao *RedisDao) SpamReply(c context.Context, mid int64) (recent, daily int, err error) {
rkey, dkey := keySpamRpRec(mid), keySpamRpDaily(mid)
conn := dao.redis.Get(c)
defer conn.Close()
ii, err := redis.Ints(conn.Do("MGET", rkey, dkey))
if err != nil {
log.Error("conn.Do(MGET, %s, %s) error(%v)", rkey, dkey, err)
// no need for redis.ErrNil check
return
}
if len(ii) != 2 {
err = fmt.Errorf("ReplySpam redis result: %v, len not 2", ii)
log.Error("%v", err)
return
}
recent = ii[0]
daily = ii[1]
return
}
// SpamAction SpamAction
func (dao *RedisDao) SpamAction(c context.Context, mid int64) (code int, err error) {
key := keySpamActRec(mid)
conn := dao.redis.Get(c)
defer conn.Close()
if code, err = redis.Int(conn.Do("GET", key)); err != nil {
if err == redis.ErrNil {
err = nil
} else {
log.Error("conn.Do(GET, %s, ), err (%v)", key, err)
}
}
return
}
// ExpireDialogIndex expire time for dialog index
func (dao *RedisDao) ExpireDialogIndex(c context.Context, dialogID int64) (ok bool, err error) {
conn := dao.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyDialogIdx(dialogID), dao.expireRdsIdx)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// RangeRpsByDialog return replies by dialog ID
func (dao *RedisDao) RangeRpsByDialog(c context.Context, dialog int64, start, end int) (rpIDs []int64, err error) {
key := keyDialogIdx(dialog)
conn := dao.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZRANGE", key, start, end))
if err != nil {
log.Error("conn.Do(ZRANGE, %s) error(%v)", key, err)
return
}
if len(values) == 0 {
return
}
err = redis.ScanSlice(values, &rpIDs)
if err != nil {
log.Error("redis.ScanSlice Error (%v)", err)
}
return
}
// DialogBySide ...
func (dao *RedisDao) DialogDesc(c context.Context, dialog int64, floor, size int) (rpIDs []int64, err error) {
var vals []interface{}
key := keyDialogIdx(dialog)
conn := dao.redis.Get(c)
defer conn.Close()
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, floor, "-inf", "LIMIT", 0, size))
if err = redis.ScanSlice(vals, &rpIDs); err != nil {
log.Error("redis.ScanSlice Error (%v)", err)
return nil, err
}
return
}
// DialogMaxMinFloor return min and max floor
func (dao *RedisDao) DialogMinMaxFloor(c context.Context, dialog int64) (minFloor, maxFloor int, err error) {
var RpID int64
key := keyDialogIdx(dialog)
conn := dao.redis.Get(c)
defer conn.Close()
err = conn.Send("ZRANGE", key, 0, 0, "WITHSCORES")
if err != nil {
log.Error("redis.Send key(%s) error(%v)", key, err)
return
}
err = conn.Send("ZRANGE", key, -1, -1, "WITHSCORES")
if err != nil {
log.Error("redis.Send key(%s) error(%v)", key, err)
return
}
err = conn.Flush()
if err != nil {
log.Error("redis.Flush (%s) error(%v)", key, err)
return
}
minValue, err := redis.Values(conn.Receive())
if err != nil {
log.Error("redis.Values key(%s) error(%v)", key, err)
return
}
if _, err = redis.Scan(minValue, &RpID, &minFloor); err != nil {
log.Error("redis.Scan() error(%v)", err)
return
}
maxValue, err := redis.Values(conn.Receive())
if err != nil {
log.Error("redis.Values key(%s) error(%v)", key, err)
return
}
if _, err = redis.Scan(maxValue, &RpID, &maxFloor); err != nil {
log.Error("redis.Scan() error(%v)", err)
return
}
return
}
// DialogByCursor return replies by dialog
func (dao *RedisDao) DialogByCursor(c context.Context, dialog int64, cursor *reply.Cursor) (rpIDs []int64, err error) {
var vals []interface{}
key := keyDialogIdx(dialog)
conn := dao.redis.Get(c)
defer conn.Close()
if cursor.Latest() {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, 0, "+inf", "LIMIT", 0, cursor.Len()))
} else if cursor.Descrease() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, cursor.Current(), "-inf", "LIMIT", 0, cursor.Len()))
} else if cursor.Increase() {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, cursor.Current(), "+inf", "LIMIT", 0, cursor.Len()))
} else {
err = ErrCursorDirection
}
if err = redis.ScanSlice(vals, &rpIDs); err != nil {
log.Error("redis.ScanSlice() error(%v)", err)
return nil, err
}
return
}
// ExpireFolder ...
func (dao *RedisDao) ExpireFolder(c context.Context, kind string, ID int64) (ok bool, err error) {
var (
conn = dao.redis.Get(c)
key = keyFolderIdx(kind, ID)
)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", key, dao.expireRdsIdx)); err != nil {
log.Error("redis EXPIRE(%s) error(%v)", key, err)
}
return
}
// FolderByCursor ...
func (dao *RedisDao) FolderByCursor(c context.Context, kind string, ID int64, cursor *xreply.Cursor) (rpIDs []int64, err error) {
var (
conn = dao.redis.Get(c)
key = keyFolderIdx(kind, ID)
vals []interface{}
)
defer conn.Close()
if cursor.Latest() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, "+inf", "-inf", "LIMIT", 0, cursor.Ps))
} else if cursor.Forward() {
vals, err = redis.Values(conn.Do("ZREVRANGEBYSCORE", key, fmt.Sprintf("(%d", cursor.Next), "-inf", "LIMIT", 0, cursor.Ps))
} else {
vals, err = redis.Values(conn.Do("ZRANGEBYSCORE", key, fmt.Sprintf("(%d", cursor.Prev), "+inf", "LIMIT", 0, cursor.Ps))
// 这里保持一致都是降序的输出
for left, right := 0, len(vals)-1; left < right; left, right = left+1, right-1 {
vals[left], vals[right] = vals[right], vals[left]
}
}
if err = redis.ScanSlice(vals, &rpIDs); err != nil {
log.Error("redis.ScanSlice() error(%v)", err)
}
return
}

View File

@@ -0,0 +1,692 @@
package reply
import (
"context"
"testing"
"time"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/model/reply"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewRedisDao(t *testing.T) {
convey.Convey("NewRedisDao", t, func(ctx convey.C) {
var (
c = conf.Conf.Redis
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := NewRedisDao(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyDialogIdx(t *testing.T) {
convey.Convey("keyDialogIdx", t, func(ctx convey.C) {
var (
dialogID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyDialogIdx(dialogID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyIdx(t *testing.T) {
convey.Convey("keyIdx", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyIdx(oid, tp, sort)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyAuditIdx(t *testing.T) {
convey.Convey("keyAuditIdx", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyAuditIdx(oid, tp)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyRtIdx(t *testing.T) {
convey.Convey("keyRtIdx", t, func(ctx convey.C) {
var (
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyRtIdx(rpID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyRpt(t *testing.T) {
convey.Convey("keyRpt", t, func(ctx convey.C) {
var (
mid = int64(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyRpt(mid, now)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyLike(t *testing.T) {
convey.Convey("keyLike", t, func(ctx convey.C) {
var (
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyLike(rpID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeySpamRpRec(t *testing.T) {
convey.Convey("keySpamRpRec", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keySpamRpRec(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeySpamRpDaily(t *testing.T) {
convey.Convey("keySpamRpDaily", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keySpamRpDaily(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeySpamActRec(t *testing.T) {
convey.Convey("keySpamActRec", t, func(ctx convey.C) {
var (
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keySpamActRec(mid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplykeyTopOid(t *testing.T) {
convey.Convey("keyTopOid", t, func(ctx convey.C) {
var (
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := keyTopOid(tp)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyPing(t *testing.T) {
convey.Convey("Ping", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.Ping(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddFloorIndex(t *testing.T) {
convey.Convey("AddFloorIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rs = &reply.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.AddFloorIndex(c, oid, tp, rs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddCountIndex(t *testing.T) {
convey.Convey("AddCountIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rs = &reply.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.AddCountIndex(c, oid, tp, rs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddLikeIndex(t *testing.T) {
convey.Convey("AddLikeIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
r = &reply.Reply{}
rpt = &reply.Report{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.AddLikeIndex(c, oid, tp, r, rpt)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyDelIndex(t *testing.T) {
convey.Convey("DelIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &reply.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.DelIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddFloorIndexByRoot(t *testing.T) {
convey.Convey("AddFloorIndexByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
root = int64(0)
rs = &reply.Reply{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.AddFloorIndexByRoot(c, root, rs)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyAddLike(t *testing.T) {
convey.Convey("AddLike", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
ras = &reply.Action{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.AddLike(c, rpID, ras)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyDelLike(t *testing.T) {
convey.Convey("DelLike", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
ra = &reply.Action{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.DelLike(c, rpID, ra)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyExpireLike(t *testing.T) {
convey.Convey("ExpireLike", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Redis.ExpireLike(c, rpID)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRange(t *testing.T) {
convey.Convey("Range", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
sort = int8(0)
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIds, isEnd, err := d.Redis.Range(c, oid, tp, sort, start, end)
ctx.Convey("Then err should be nil.rpIds,isEnd should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(isEnd, convey.ShouldNotBeNil)
if len(rpIds) <= 0 {
ctx.So(rpIds, convey.ShouldBeEmpty)
}
})
})
})
}
func TestReplyCountReplies(t *testing.T) {
convey.Convey("CountReplies", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
count, err := d.Redis.CountReplies(c, oid, tp, sort)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyUserAuditReplies(t *testing.T) {
convey.Convey("UserAuditReplies", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(1)
oid = int64(1)
tp = int8(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIds, err := d.Redis.UserAuditReplies(c, mid, oid, tp)
ctx.Convey("Then err should be nil.rpIds should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIds, convey.ShouldBeEmpty)
})
})
})
}
func TestReplyRangeByRoot(t *testing.T) {
convey.Convey("RangeByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
root = int64(0)
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIds, err := d.Redis.RangeByRoot(c, root, start, end)
ctx.Convey("Then err should be nil.rpIds should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIds, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRangeByRoots(t *testing.T) {
convey.Convey("RangeByRoots", t, func(ctx convey.C) {
var (
c = context.Background()
roots = []int64{}
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
mrpids, idx, miss, err := d.Redis.RangeByRoots(c, roots, start, end)
ctx.Convey("Then err should be nil.mrpids,idx,miss should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(miss, convey.ShouldBeNil)
ctx.So(idx, convey.ShouldBeNil)
ctx.So(mrpids, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyExpireIndex(t *testing.T) {
convey.Convey("ExpireIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Redis.ExpireIndex(c, oid, tp, sort)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyExpireIndexByRoot(t *testing.T) {
convey.Convey("ExpireIndexByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
root = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Redis.ExpireIndexByRoot(c, root)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySetUserReportCnt(t *testing.T) {
convey.Convey("SetUserReportCnt", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
count = int(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.SetUserReportCnt(c, mid, count, now)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetUserReportCnt(t *testing.T) {
convey.Convey("GetUserReportCnt", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
count, err := d.Redis.GetUserReportCnt(c, mid, now)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetUserReportTTL(t *testing.T) {
convey.Convey("GetUserReportTTL", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ttl, err := d.Redis.GetUserReportTTL(c, mid, now)
ctx.Convey("Then err should be nil.ttl should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ttl, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRankIndex(t *testing.T) {
convey.Convey("RankIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rpID = int64(0)
sort = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rank, err := d.Redis.RankIndex(c, oid, tp, rpID, sort)
ctx.Convey("Then err should be nil.rank should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rank, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRankIndexByRoot(t *testing.T) {
convey.Convey("RankIndexByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
root = int64(0)
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rank, err := d.Redis.RankIndexByRoot(c, root, rpID)
ctx.Convey("Then err should be nil.rank should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rank, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyOidHaveTop(t *testing.T) {
convey.Convey("OidHaveTop", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Redis.OidHaveTop(c, oid, tp)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySpamReply(t *testing.T) {
convey.Convey("SpamReply", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
recent, daily, err := d.Redis.SpamReply(c, mid)
ctx.Convey("Then err should be nil.recent,daily should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(daily, convey.ShouldNotBeNil)
ctx.So(recent, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySpamAction(t *testing.T) {
convey.Convey("SpamAction", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
code, err := d.Redis.SpamAction(c, mid)
ctx.Convey("Then err should be nil.code should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(code, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyExpireDialogIndex(t *testing.T) {
convey.Convey("ExpireDialogIndex", t, func(ctx convey.C) {
var (
c = context.Background()
dialogID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ok, err := d.Redis.ExpireDialogIndex(c, dialogID)
ctx.Convey("Then err should be nil.ok should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ok, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyRangeRpsByDialog(t *testing.T) {
convey.Convey("RangeRpsByDialog", t, func(ctx convey.C) {
var (
c = context.Background()
dialog = int64(0)
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Redis.RangeRpsByDialog(c, dialog, start, end)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyDialogDesc(t *testing.T) {
convey.Convey("DialogDesc", t, func(ctx convey.C) {
var (
c = context.Background()
dialog = int64(0)
floor = int(0)
size = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Redis.DialogDesc(c, dialog, floor, size)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyDialogMinMaxFloor(t *testing.T) {
convey.Convey("DialogMinMaxFloor", t, func(ctx convey.C) {
var (
c = context.Background()
dialog = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
minFloor, maxFloor, err := d.Redis.DialogMinMaxFloor(c, dialog)
ctx.Convey("Then err should be nil.minFloor,maxFloor should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(maxFloor, convey.ShouldBeZeroValue)
ctx.So(minFloor, convey.ShouldBeZeroValue)
})
})
})
}
func TestReplyDialogByCursor(t *testing.T) {
convey.Convey("DialogByCursor", t, func(ctx convey.C) {
var (
c = context.Background()
dialog = int64(0)
cursor = &reply.Cursor{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Redis.DialogByCursor(c, dialog, cursor)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestDelReplyIncr(t *testing.T) {
convey.Convey("test DelReplyIncr", t, func(ctx convey.C) {
var (
mid = int64(2233)
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Redis.DelReplyIncr(c, mid, true)
ctx.Convey("d.Redis.DelReplyIncr up error should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
err = d.Redis.DelReplyIncr(c, mid, false)
ctx.Convey("d.Redis.DelReplyIncr error should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
err = d.Redis.DelReplySpam(c, mid)
ctx.Convey("d.Redis.DelReplySpam error should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,444 @@
package reply
import (
"context"
"fmt"
"time"
"go-common/app/interface/main/reply/model/reply"
"go-common/app/interface/main/reply/model/xreply"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_replySharding int64 = 200
)
const (
_selByIdsSQL = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,hate,floor,state,attr,ctime,mtime FROM reply_%d WHERE id IN (%s)"
_selSQL = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,hate,floor,state,attr,ctime,mtime FROM reply_%d WHERE id=?"
_selByDialogSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state IN (0,1,2,5,6) AND dialog=? ORDER BY floor ASC limit ?,?"
_selByDialogDescSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state IN (0,1,2,5,6) AND dialog=? AND floor<=? ORDER BY floor DESC limit ?"
_selByDialogAscSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state IN (0,1,2,5,6) AND dialog=? AND floor>=? ORDER BY floor ASC limit ?"
_selMinMaxFloorDialogSQL = "SELECT IFNULL(MIN(floor),0), IFNULL(MAX(floor),0) FROM reply_%d WHERE dialog=? AND oid=? AND type=? AND root=? AND state IN (0,1,2,5,6)"
_selIdsByFloorSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY floor DESC limit ?,?"
_selIdsByCountSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY rcount DESC limit ?,?"
_selIdsByLikeSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY `like` DESC limit ?,?" // like >= 3
_selCountByLikeSQL = "SELECT COUNT(*) FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) AND attr&1!=1 AND attr>>1&1!=1" // like >= 3
_selIdsByRootStateSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state in (0,1,2,5,6) ORDER BY floor limit ?,?"
_selIdsByRootSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? ORDER BY floor limit ?,?"
_selIdsByFloorOffsetSQL = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,3,4,5,6,7,8,9,10,11) AND floor >= ? AND floor <= ? ORDER BY floor"
_setHateSQL = "UPDATE reply_%d SET `hate`=?,mtime=? WHERE id=?"
_setLikeSQL = "UPDATE reply_%d SET `like`=?,mtime=? WHERE id=?"
_insFilteredReply = "INSERT INTO reply_filtered (rpid, oid, mid, type, message, level, ctime, mtime) VALUES (?,?,?,?,?,?,?,?)"
_selFilteredReply = "select rpid,message FROM reply_filtered WHERE rpid in (%s)"
// 针对折叠评论
_foldedReplyLatest = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state=12 ORDER BY floor DESC LIMIT ?"
_foldedReplyDesc = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state=12 AND floor<? ORDER BY floor DESC LIMIT ?"
_foldedReplyAsc = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=? AND state=12 AND floor>? ORDER BY floor DESC LIMIT ?"
)
// RpDao reply dao.
type RpDao struct {
db *sql.DB
dbSlave *sql.DB
}
// NewReplyDao new replyDao and return.
func NewReplyDao(db, dbSlave *sql.DB) (dao *RpDao) {
dao = &RpDao{
db: db,
dbSlave: dbSlave,
}
return
}
func (dao *RpDao) hit(oid int64) int64 {
return oid % _replySharding
}
// FilterContents get filtered contents from db
func (d *Dao) FilterContents(ctx context.Context, rpMaps map[int64]string) error {
if len(rpMaps) == 0 {
return nil
}
var rpids []int64
for k := range rpMaps {
rpids = append(rpids, k)
}
rows, err := d.dbSlave.Query(ctx, fmt.Sprintf(_selFilteredReply, xstr.JoinInts(rpids)))
if err != nil {
log.Error("mysql.Query error(%v)", err)
return err
}
defer rows.Close()
for rows.Next() {
var id int64
var message string
if err = rows.Scan(&id, &message); err != nil {
if err == sql.ErrNoRows {
continue
} else {
log.Error("row.Scan error(%v)", err)
return err
}
}
rpMaps[id] = message
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return err
}
return nil
}
// CountLike get count of reply like
func (dao *RpDao) CountLike(c context.Context, oid int64, tp int8) (count int, err error) {
row := dao.dbSlave.QueryRow(c, fmt.Sprintf(_selCountByLikeSQL, dao.hit(oid)), oid, tp)
if err = row.Scan(&count); err != nil {
log.Error("row.scan err(%v)", err)
return
}
return
}
// Get get reply.
func (dao *RpDao) Get(c context.Context, oid, rpID int64) (r *reply.Reply, err error) {
r = &reply.Reply{}
row := dao.db.QueryRow(c, fmt.Sprintf(_selSQL, dao.hit(oid)), rpID)
if err = row.Scan(&r.RpID, &r.Oid, &r.Type, &r.Mid, &r.Root, &r.Parent, &r.Dialog, &r.Count, &r.RCount, &r.Like, &r.Hate, &r.Floor, &r.State, &r.Attr, &r.CTime, &r.MTime); err != nil {
if err == sql.ErrNoRows {
r = nil
err = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// GetIDsByDialogAsc ...
func (dao *RpDao) GetIDsByDialogAsc(c context.Context, oid int64, tp int8, root, dialog, maxFloor int64, count int) (rpIDs []int64, err error) {
query := fmt.Sprintf(_selByDialogAscSQL, dao.hit(oid))
rows, err := dao.dbSlave.Query(c, query, oid, tp, root, dialog, maxFloor, count)
if err != nil {
log.Error("mysql.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("row.Scan error(%v)", err)
return
}
rpIDs = append(rpIDs, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetDialogMinMaxFloor ...
func (dao *RpDao) GetDialogMinMaxFloor(c context.Context, oid int64, tp int8, root, dialog int64) (minFloor, maxFloor int, err error) {
query := fmt.Sprintf(_selMinMaxFloorDialogSQL, dao.hit(oid))
err = dao.dbSlave.QueryRow(c, query, dialog, oid, tp, root).Scan(&minFloor, &maxFloor)
if err != nil {
log.Error("row.scan err(%v)", err)
return
}
return
}
// GetIDsByDialogDesc ...
func (dao *RpDao) GetIDsByDialogDesc(c context.Context, oid int64, tp int8, root, dialog, minFloor int64, count int) (rpIDs []int64, err error) {
query := fmt.Sprintf(_selByDialogDescSQL, dao.hit(oid))
rows, err := dao.dbSlave.Query(c, query, oid, tp, root, dialog, minFloor, count)
if err != nil {
log.Error("mysql.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("row.Scan error(%v)", err)
return
}
rpIDs = append(rpIDs, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIDsByDialog get replies by dialog
func (dao *RpDao) GetIDsByDialog(c context.Context, oid int64, tp int8, root, dialog int64, offset, count int) (rpIDs []int64, err error) {
query := fmt.Sprintf(_selByDialogSQL, dao.hit(oid))
rows, err := dao.dbSlave.Query(c, query, oid, tp, root, dialog, offset, count)
if err != nil {
log.Error("mysql.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("row.Scan error(%v)", err)
return
}
rpIDs = append(rpIDs, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetByIds get replies by reply ids.
func (dao *RpDao) GetByIds(c context.Context, oid int64, tp int8, rpIds []int64) (rpMap map[int64]*reply.Reply, err error) {
if len(rpIds) == 0 {
return
}
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selByIdsSQL, dao.hit(oid), xstr.JoinInts(rpIds)))
if err != nil {
log.Error("mysql.Query error(%v)", err)
return
}
defer rows.Close()
rpMap = make(map[int64]*reply.Reply, len(rpIds))
for rows.Next() {
r := &reply.Reply{}
if err = rows.Scan(&r.RpID, &r.Oid, &r.Type, &r.Mid, &r.Root, &r.Parent, &r.Dialog, &r.Count, &r.RCount, &r.Like, &r.Hate, &r.Floor, &r.State, &r.Attr, &r.CTime, &r.MTime); err != nil {
if err == sql.ErrNoRows {
r = nil
} else {
log.Error("row.Scan error(%v)", err)
return
}
}
rpMap[r.RpID] = r
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIdsSortFloor limit get reply ids and order by floor desc.
func (dao *RpDao) GetIdsSortFloor(c context.Context, oid int64, tp int8, offset, count int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByFloorSQL, dao.hit(oid)), oid, tp, offset, count)
if err != nil {
log.Error("dao.selIdsByFloorStmt query err(%v)", err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, count)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v)", err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIdsSortCount limit get reply ids and order by rcount desc.
func (dao *RpDao) GetIdsSortCount(c context.Context, oid int64, tp int8, offset, count int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByCountSQL, dao.hit(oid)), oid, tp, offset, count)
if err != nil {
log.Error("dao.selIdsByCountStmt query err(%v)", err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, count)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v)", err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIdsSortLike limit get reply ids and order by like desc.
func (dao *RpDao) GetIdsSortLike(c context.Context, oid int64, tp int8, offset, count int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByLikeSQL, dao.hit(oid)), oid, tp, offset, count)
if err != nil {
log.Error(" dao.selIdsByLikeStmt query err(%v)", err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, count)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v)", err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIdsByRoot limit get reply ids of root reply and order by floor.
func (dao *RpDao) GetIdsByRoot(c context.Context, oid, root int64, tp int8, offset, count int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByRootStateSQL, dao.hit(oid)), oid, tp, root, offset, count)
if err != nil {
log.Error("dao.selIdsByRtStmt,oid(%d),root(%d),tp(%d),offset(%d),query err(%v)", oid, root, tp, offset, err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, count)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v,%v,%v,%v,%v)", oid, root, offset, count, err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIDsByRootWithoutState limit get reply ids of root reply and order by floor.
func (dao *RpDao) GetIDsByRootWithoutState(c context.Context, oid, root int64, tp int8, offset, count int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByRootSQL, dao.hit(oid)), oid, tp, root, offset, count)
if err != nil {
log.Error("dao.selIdsByRtStmt,oid(%d),root(%d),tp(%d),offset(%d),query err(%v)", oid, root, tp, offset, err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, count)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v,%v,%v,%v,%v)", oid, root, offset, count, err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// GetIDsByFloorOffset get reply ids in floor range
func (dao *RpDao) GetIDsByFloorOffset(c context.Context, oid int64, tp int8, start, end int) (res []int64, err error) {
rows, err := dao.dbSlave.Query(c, fmt.Sprintf(_selIdsByFloorOffsetSQL, dao.hit(oid)), oid, tp, start, end)
if err != nil {
log.Error(" dao.db.Query err(%v)", err)
return
}
defer rows.Close()
var id int64
res = make([]int64, 0, end-start)
for rows.Next() {
if err = rows.Scan(&id); err != nil {
log.Error("rows.scan err is (%v)", err)
return
}
res = append(res, id)
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// SetHateCount set a reply hate count.
func (dao *RpDao) SetHateCount(c context.Context, oid, rpID int64, count int32, now time.Time) (rows int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_setHateSQL, dao.hit(oid)), count, now, rpID)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// SetLikeCount set a reply hate count.
func (dao *RpDao) SetLikeCount(c context.Context, oid, rpID int64, count int32, now time.Time) (rows int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_setLikeSQL, dao.hit(oid)), count, now, rpID)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// AddFilteredReply AddFilteredReply
func (dao *RpDao) AddFilteredReply(c context.Context, rpID, oid, mid int64, tp, level int8, message string, now time.Time) (err error) {
_, err = dao.db.Exec(c, _insFilteredReply, rpID, oid, mid, tp, message, level, now, now)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return
}
// FoldedRepliesCursor ...
func (dao *RpDao) FoldedRepliesCursor(c context.Context, oid int64, tp int8, root int64, cursor *xreply.Cursor) (ids []int64, err error) {
var (
rows *sql.Rows
)
if cursor.Latest() {
rows, err = dao.dbSlave.Query(c, fmt.Sprintf(_foldedReplyLatest, dao.hit(oid)), oid, tp, root, cursor.Ps)
} else if cursor.Forward() {
rows, err = dao.dbSlave.Query(c, fmt.Sprintf(_foldedReplyDesc, dao.hit(oid)), oid, tp, root, cursor.Next, cursor.Ps)
} else {
rows, err = dao.dbSlave.Query(c, fmt.Sprintf(_foldedReplyAsc, dao.hit(oid)), oid, tp, root, cursor.Prev, cursor.Ps)
return
}
if err != nil {
log.Error("mysql Query() error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("mysql Scan() error(%v)", err)
return
}
ids = append(ids, id)
}
if err = rows.Err(); err != nil {
log.Error("mysql rows.Err() error(%v)", err)
}
return
}

View File

@@ -0,0 +1,350 @@
package reply
import (
"context"
"go-common/library/database/sql"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewReplyDao(t *testing.T) {
convey.Convey("NewReplyDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
dbSlave = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewReplyDao(db, dbSlave)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReplyhit(t *testing.T) {
convey.Convey("hit", t, func(ctx convey.C) {
var (
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.Reply.hit(oid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyCountLike(t *testing.T) {
convey.Convey("CountLike", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
count, err := d.Reply.CountLike(c, oid, tp)
ctx.Convey("Then err should be nil.count should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(count, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReplyGet(t *testing.T) {
convey.Convey("Get", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
r, err := d.Reply.Get(c, oid, rpID)
ctx.Convey("Then err should be nil.r should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(r, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetIDsByDialogAsc(t *testing.T) {
convey.Convey("GetIDsByDialogAsc", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
dialog = int64(0)
maxFloor = int64(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Reply.GetIDsByDialogAsc(c, oid, tp, root, dialog, maxFloor, count)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetDialogMinMaxFloor(t *testing.T) {
convey.Convey("GetDialogMinMaxFloor", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
dialog = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
minFloor, maxFloor, err := d.Reply.GetDialogMinMaxFloor(c, oid, tp, root, dialog)
ctx.Convey("Then err should be nil.minFloor,maxFloor should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(maxFloor, convey.ShouldNotBeNil)
ctx.So(minFloor, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIDsByDialogDesc(t *testing.T) {
convey.Convey("GetIDsByDialogDesc", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
dialog = int64(0)
minFloor = int64(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Reply.GetIDsByDialogDesc(c, oid, tp, root, dialog, minFloor, count)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetIDsByDialog(t *testing.T) {
convey.Convey("GetIDsByDialog", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
root = int64(0)
dialog = int64(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpIDs, err := d.Reply.GetIDsByDialog(c, oid, tp, root, dialog, offset, count)
ctx.Convey("Then err should be nil.rpIDs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpIDs, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetByIds(t *testing.T) {
convey.Convey("GetByIds", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
rpIds = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpMap, err := d.Reply.GetByIds(c, oid, tp, rpIds)
ctx.Convey("Then err should be nil.rpMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpMap, convey.ShouldBeNil)
})
})
})
}
func TestReplyGetIdsSortFloor(t *testing.T) {
convey.Convey("GetIdsSortFloor", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIdsSortFloor(c, oid, tp, offset, count)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIdsSortCount(t *testing.T) {
convey.Convey("GetIdsSortCount", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIdsSortCount(c, oid, tp, offset, count)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIdsSortLike(t *testing.T) {
convey.Convey("GetIdsSortLike", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIdsSortLike(c, oid, tp, offset, count)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIdsByRoot(t *testing.T) {
convey.Convey("GetIdsByRoot", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
root = int64(0)
tp = int8(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIdsByRoot(c, oid, root, tp, offset, count)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIDsByRootWithoutState(t *testing.T) {
convey.Convey("GetIDsByRootWithoutState", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
root = int64(0)
tp = int8(0)
offset = int(0)
count = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIDsByRootWithoutState(c, oid, root, tp, offset, count)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGetIDsByFloorOffset(t *testing.T) {
convey.Convey("GetIDsByFloorOffset", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
start = int(0)
end = int(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Reply.GetIDsByFloorOffset(c, oid, tp, start, end)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySetHateCount(t *testing.T) {
convey.Convey("SetHateCount", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
count = int32(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Reply.SetHateCount(c, oid, rpID, count, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySetLikeCount(t *testing.T) {
convey.Convey("SetLikeCount", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
count = int32(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Reply.SetLikeCount(c, oid, rpID, count, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyAddFilteredReply(t *testing.T) {
convey.Convey("AddFilteredReply", t, func(ctx convey.C) {
var (
c = context.Background()
rpID = int64(-1)
oid = int64(0)
mid = int64(0)
tp = int8(0)
level = int8(0)
message = ""
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.Reply.AddFilteredReply(c, rpID, oid, mid, tp, level, message, now)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
d.mysql.Exec(context.Background(), "DELETE FROM reply_filtered WHERE rpid=-1")
})
}

View File

@@ -0,0 +1,83 @@
package reply
import (
"context"
"fmt"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_repSharding int64 = 200
)
const (
// report
_inRptSQL = "INSERT INTO reply_report_%d (oid,type,rpid,mid,reason,content,count,score,state,ctime,mtime) VALUES(?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE count=count+1, score=score+?, mtime=?"
_selRptSQL = "SELECT oid,type,rpid,mid,reason,content,count,state,ctime,mtime,attr FROM reply_report_%d WHERE rpid=?"
// report user
_inRptUserSQL = "INSERT IGNORE INTO reply_report_user_%d (oid,type,rpid,mid,reason,content,state,ctime,mtime) VALUES(?,?,?,?,?,?,?,?,?)"
)
// ReportDao report dao.
type ReportDao struct {
inStmts []*sql.Stmt
inUserStmts []*sql.Stmt
db *sql.DB
}
// NewReportDao new ReplyReportDao and return.
func NewReportDao(db *sql.DB) (dao *ReportDao) {
dao = &ReportDao{
db: db,
inStmts: make([]*sql.Stmt, _repSharding),
inUserStmts: make([]*sql.Stmt, _repSharding),
}
for i := int64(0); i < _repSharding; i++ {
dao.inStmts[i] = dao.db.Prepared(fmt.Sprintf(_inRptSQL, i))
dao.inUserStmts[i] = dao.db.Prepared(fmt.Sprintf(_inRptUserSQL, i))
}
return
}
func (dao *ReportDao) hit(oid int64) int64 {
return oid % _repSharding
}
// Insert insert a report to mysql.
func (dao *ReportDao) Insert(c context.Context, rpt *model.Report) (id int64, err error) {
res, err := dao.inStmts[dao.hit(rpt.Oid)].Exec(c, rpt.Oid, rpt.Type, rpt.RpID, rpt.Mid, rpt.Reason, rpt.Content, rpt.Count, rpt.Score, rpt.State, rpt.CTime, rpt.MTime, rpt.Score, rpt.MTime)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return res.LastInsertId()
}
// Get return a report from mysql.
func (dao *ReportDao) Get(c context.Context, oid, rpID int64) (rpt *model.Report, err error) {
row := dao.db.QueryRow(c, fmt.Sprintf(_selRptSQL, dao.hit(oid)), rpID)
rpt = &model.Report{}
err = row.Scan(&rpt.Oid, &rpt.Type, &rpt.RpID, &rpt.Mid, &rpt.Reason, &rpt.Content, &rpt.Count, &rpt.State, &rpt.CTime, &rpt.MTime, &rpt.Attr)
if err != nil {
if err == sql.ErrNoRows {
rpt = nil
err = nil
} else {
log.Error("Mysql error(%v)", err)
}
}
return
}
// InsertUser inser a report user to mysql.
func (dao *ReportDao) InsertUser(c context.Context, ru *model.ReportUser) (id int64, err error) {
res, err := dao.inUserStmts[dao.hit(ru.Oid)].Exec(c, ru.Oid, ru.Type, ru.RpID, ru.Mid, ru.Reason, ru.Content, ru.State, ru.CTime, ru.MTime)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return res.LastInsertId()
}

View File

@@ -0,0 +1,88 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/conf"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewReportDao(t *testing.T) {
convey.Convey("NewReportDao", t, func(ctx convey.C) {
var (
db = sql.NewMySQL(conf.Conf.MySQL.Reply)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewReportDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReporthit(t *testing.T) {
convey.Convey("hit", t, func(ctx convey.C) {
var (
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.Report.hit(oid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReportInsert(t *testing.T) {
convey.Convey("Insert", t, func(ctx convey.C) {
var (
c = context.Background()
rpt = &model.Report{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.Report.Insert(c, rpt)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyReportGet(t *testing.T) {
convey.Convey("Get", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rpt, err := d.Report.Get(c, oid, rpID)
ctx.Convey("Then err should be nil.rpt should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rpt, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyInsertUser(t *testing.T) {
convey.Convey("InsertUser", t, func(ctx convey.C) {
var (
c = context.Background()
ru = &model.ReportUser{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.Report.InsertUser(c, ru)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,136 @@
package reply
import (
"context"
"fmt"
"time"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_subSharding int64 = 50
)
const (
_inSubSQL = "INSERT IGNORE INTO reply_subject_%d (oid,type,mid,state,ctime,mtime) VALUES(?,?,?,?,?,?)"
_upSubStateSQL = "UPDATE reply_subject_%d SET state=?,mtime=? WHERE oid=? AND type=?"
_upSubMidSQL = "UPDATE reply_subject_%d SET mid=?,mtime=? WHERE oid=? AND type=?"
_selSubjectSQL = "SELECT oid,type,mid,count,rcount,acount,state,attr,ctime,mtime,meta FROM reply_subject_%d WHERE oid=? AND type=?"
_selSubjectsSQL = "SELECT oid,type,mid,count,rcount,acount,state,attr,ctime,mtime,meta FROM reply_subject_%d WHERE type=? AND oid IN(%s)"
_setSubjectSQL = "INSERT INTO reply_subject_%d (oid,type,mid,state,ctime,mtime) VALUES(?,?,?,?,?,?) ON DUPLICATE KEY UPDATE state=?,mid=?,mtime=?"
)
// SubjectDao subject dao.
type SubjectDao struct {
db *sql.DB
}
// NewSubjectDao new ReplySubjectDao and return.
func NewSubjectDao(db *sql.DB) (dao *SubjectDao) {
dao = &SubjectDao{
db: db,
}
return
}
func (dao *SubjectDao) hit(oid int64) int64 {
return oid % _subSharding
}
// Set insert or update subject state.
func (dao *SubjectDao) Set(c context.Context, sub *reply.Subject) (id int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_setSubjectSQL, dao.hit(sub.Oid)), sub.Oid, sub.Type, sub.Mid, sub.State, sub.CTime, sub.MTime, sub.State, sub.Mid, sub.MTime)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.LastInsertId()
}
// Insert insert a reply subject.
func (dao *SubjectDao) Insert(c context.Context, sub *reply.Subject) (id int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_inSubSQL, dao.hit(sub.Oid)), sub.Oid, sub.Type, sub.Mid, sub.State, sub.CTime, sub.MTime)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.LastInsertId()
}
// UpState update subject state.
func (dao *SubjectDao) UpState(c context.Context, oid int64, tp int8, state int8, now time.Time) (rows int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_upSubStateSQL, dao.hit(oid)), state, now, oid, tp)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// UpMid update subject mid.
func (dao *SubjectDao) UpMid(c context.Context, mid, oid int64, tp int8, now time.Time) (rows int64, err error) {
res, err := dao.db.Exec(c, fmt.Sprintf(_upSubMidSQL, dao.hit(oid)), mid, now, oid, tp)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// Get get a subject.
func (dao *SubjectDao) Get(c context.Context, oid int64, tp int8) (sub *reply.Subject, err error) {
sub = &reply.Subject{}
row := dao.db.QueryRow(c, fmt.Sprintf(_selSubjectSQL, dao.hit(oid)), oid, tp)
if err = row.Scan(&sub.Oid, &sub.Type, &sub.Mid, &sub.Count, &sub.RCount, &sub.ACount, &sub.State, &sub.Attr, &sub.CTime, &sub.MTime, &sub.Meta); err != nil {
if err == sql.ErrNoRows {
sub = nil
err = nil
} else {
log.Error("row.Scan error(%v)", err)
}
}
return
}
// Gets get a subject.
func (dao *SubjectDao) Gets(c context.Context, oids []int64, tp int8) (res map[int64]*reply.Subject, err error) {
hits := make(map[int64][]int64)
for _, oid := range oids {
hit := dao.hit(oid)
hits[hit] = append(hits[hit], oid)
}
res = make(map[int64]*reply.Subject, len(oids))
for hit, oids := range hits {
var rows *sql.Rows
if rows, err = dao.db.Query(c, fmt.Sprintf(_selSubjectsSQL, hit, xstr.JoinInts(oids)), tp); err != nil {
log.Error("dao.db.Query error(%v)", err)
return
}
for rows.Next() {
sub := new(reply.Subject)
if err = rows.Scan(&sub.Oid, &sub.Type, &sub.Mid, &sub.Count, &sub.RCount, &sub.ACount, &sub.State, &sub.Attr, &sub.CTime, &sub.MTime, &sub.Meta); err != nil {
if err == sql.ErrNoRows {
sub = nil
err = nil
continue
} else {
log.Error("row.Scan error(%v)", err)
rows.Close()
return
}
}
res[sub.Oid] = sub
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
rows.Close()
return
}
rows.Close()
}
return
}

View File

@@ -0,0 +1,143 @@
package reply
import (
"context"
"go-common/app/interface/main/reply/model/reply"
"go-common/library/database/sql"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestReplyNewSubjectDao(t *testing.T) {
convey.Convey("NewSubjectDao", t, func(ctx convey.C) {
var (
db = &sql.DB{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
dao := NewSubjectDao(db)
ctx.Convey("Then dao should not be nil.", func(ctx convey.C) {
ctx.So(dao, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySubjecthit(t *testing.T) {
convey.Convey("hit", t, func(ctx convey.C) {
var (
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.Subject.hit(oid)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestReplySet(t *testing.T) {
convey.Convey("Set", t, func(ctx convey.C) {
var (
c = context.Background()
sub = &reply.Subject{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.Subject.Set(c, sub)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyInsert(t *testing.T) {
convey.Convey("Insert", t, func(ctx convey.C) {
var (
c = context.Background()
sub = &reply.Subject{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
id, err := d.Subject.Insert(c, sub)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyUpState(t *testing.T) {
convey.Convey("UpState", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
state = int8(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Subject.UpState(c, oid, tp, state, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyUpMid(t *testing.T) {
convey.Convey("UpMid", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
oid = int64(0)
tp = int8(0)
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.Subject.UpMid(c, mid, oid, tp, now)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGet(t *testing.T) {
convey.Convey("Get", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
sub, err := d.Subject.Get(c, oid, tp)
ctx.Convey("Then err should be nil.sub should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(sub, convey.ShouldNotBeNil)
})
})
})
}
func TestReplyGets(t *testing.T) {
convey.Convey("Gets", t, func(ctx convey.C) {
var (
c = context.Background()
oids = []int64{}
tp = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
res, err := d.Subject.Gets(c, oids, tp)
ctx.Convey("Then err should be nil.res should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,58 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"log_test.go",
"record_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"log.go",
"record.go",
],
importpath = "go-common/app/interface/main/reply/dao/search",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/adminlog:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//library/database/elastic:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,31 @@
package search
import (
"go-common/app/interface/main/reply/conf"
es "go-common/library/database/elastic"
bm "go-common/library/net/http/blademaster"
)
const (
_searchURL = "/x/internal/search/reply"
_searchLogURL = "/api/reply/external/search"
)
// Dao search dao.
type Dao struct {
logURL string
searchURL string
httpCli *bm.Client
es *es.Elastic
}
// New new a dao and return.
func New(c *conf.Config) *Dao {
d := &Dao{
logURL: c.Host.Search + _searchLogURL,
searchURL: c.Host.API + _searchURL,
httpCli: bm.NewClient(c.HTTPClient),
es: es.NewElastic(c.Es),
}
return d
}

View File

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

View File

@@ -0,0 +1,65 @@
package search
import (
"context"
"net/url"
"strconv"
"time"
"go-common/app/interface/main/reply/model/adminlog"
"go-common/library/log"
"go-common/library/xstr"
)
type searchAdminLog struct {
ReplyID int64 `json:"rpid"` // 评论id
AdminID int64 `json:"adminid"` // 操作人
State int32 `json:"state"` // 操作人身份
ReplyMid int64 `json:"replymid"` // 评论人
CTime string `json:"ctime"` // 删除时间
}
// LogPaginate paginating the admin logs for size of 'pageSize', and returning the number of reporting, the number of admin logs delete by administrator
func (dao *Dao) LogPaginate(c context.Context, oid int64, tp int, states []int64, curPage, pageSize int, startTime string, now time.Time) (logs []*adminlog.AdminLog, replyCount, reportCount, pageCount, total int64, err error) {
params := url.Values{}
params.Set("appid", "replylog")
params.Set("oid", strconv.FormatInt(oid, 10))
params.Set("type", strconv.Itoa(tp))
params.Set("delstats", xstr.JoinInts(states))
params.Set("start_time", startTime)
params.Set("pagesize", strconv.Itoa(pageSize))
params.Set("page", strconv.Itoa(curPage))
var res struct {
Code int `json:"code"`
Logs []*searchAdminLog `json:"result"`
ReplyCount int64 `json:"adminDeletedNum"`
ReportCount int64 `json:"reportNum"`
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
PageCount int64 `json:"pagecount"`
Total int64 `json:"total"`
}
if err = dao.httpCli.Get(c, dao.logURL, "", params, &res); err != nil {
log.Error("adminlog url(%v),err (%v)", dao.logURL+"?"+params.Encode(), err)
return
}
if res.Logs == nil {
logs = make([]*adminlog.AdminLog, 0)
}
for _, log := range res.Logs {
var (
tmp = &adminlog.AdminLog{}
)
tmp.ReplyID = log.ReplyID
tmp.State = log.State
tmp.AdminID = log.AdminID
tmp.CTime = log.CTime
tmp.ReplyMid = log.ReplyMid
logs = append(logs, tmp)
}
replyCount = res.ReplyCount
reportCount = res.ReportCount
pageCount = res.PageCount
total = res.Total
return
}

View File

@@ -0,0 +1,35 @@
package search
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestSearchLogPaginate(t *testing.T) {
convey.Convey("LogPaginate", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int(0)
states = []int64{}
curPage = int(0)
pageSize = int(0)
startTime = ""
now = time.Now()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
logs, replyCount, reportCount, pageCount, total, err := d.LogPaginate(c, oid, tp, states, curPage, pageSize, startTime, now)
ctx.Convey("Then err should be nil.logs,replyCount,reportCount,pageCount,total should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(total, convey.ShouldNotBeNil)
ctx.So(pageCount, convey.ShouldNotBeNil)
ctx.So(reportCount, convey.ShouldNotBeNil)
ctx.So(replyCount, convey.ShouldNotBeNil)
ctx.So(logs, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,45 @@
package search
import (
"context"
"time"
"fmt"
model "go-common/app/interface/main/reply/model/reply"
es "go-common/library/database/elastic"
"go-common/library/log"
)
var (
recordStates = []int64{0, 1, 2, 5, 6, 7, 9, 11}
)
type recordResult struct {
Page struct {
Num int32 `json:"num"`
Size int32 `json:"size"`
Total int32 `json:"total"`
} `json:"page"`
Result []*model.Record `json:"result"`
}
// RecordPaginate return a page of records from es.
func (d *Dao) RecordPaginate(c context.Context, types []int64, mid, stime, etime int64, order, sort string, pn, ps int32) (records []*model.Record, total int32, err error) {
r := d.es.NewRequest("reply_record").Index(fmt.Sprintf("%s_%d", "replyrecord", mid%100)).
WhereRange("ctime", time.Unix(stime, 0).Format(model.RecordTimeLayout), time.Unix(etime, 0).Format(model.RecordTimeLayout), es.RangeScopeLcRc).
WhereIn("state", recordStates).
WhereEq("mid", mid).
Order(order, sort).Pn(int(pn)).Ps(int(ps))
if len(types) > 0 {
r = r.WhereIn("type", types)
}
var res recordResult
err = r.Scan(c, &res)
if err != nil {
log.Error("r.Scan(%v) error(%v)", c, err)
return
}
records = res.Result
total = int32(res.Page.Total)
return
}

View File

@@ -0,0 +1,32 @@
package search
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestSearchRecordPaginate(t *testing.T) {
convey.Convey("RecordPaginate", t, func(ctx convey.C) {
var (
c = context.Background()
types = []int64{}
mid = int64(0)
stime = int64(0)
etime = int64(0)
order = ""
sort = ""
pn = int32(0)
ps = int32(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
records, total, err := d.RecordPaginate(c, types, mid, stime, etime, order, sort, pn, ps)
ctx.Convey("Then err should be nil.records,total should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(total, convey.ShouldNotBeNil)
ctx.So(records, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["vip_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["vip.go"],
importpath = "go-common/app/interface/main/reply/dao/vip",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/vip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,87 @@
package vip
import (
"context"
"net/url"
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/model/vip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
const (
_emojiURL = "/internal/v1/emoji/list"
)
type result struct {
Code int `json:"code"`
Data []*struct {
ID int64 `json:"id"`
Name string `json:"name"`
URL string `json:"path"`
State int8 `json:"deleted"`
Emojis []*struct {
ID int64 `json:"id"`
Name string `json:"name"`
URL string `json:"path"`
State int8 `json:"deleted"`
Remark string `json:"remark"`
} `json:"emojiList"`
} `json:"data"`
}
// Dao dao.
type Dao struct {
emojiURL string
httpClient *bm.Client
}
// New new a dao and return.
func New(c *conf.Config) *Dao {
d := &Dao{
httpClient: bm.NewClient(c.HTTPClient),
emojiURL: c.Reply.VipURL + _emojiURL,
}
return d
}
// Emoji return emojis to cache
func (dao *Dao) Emoji(c context.Context) (emjs []*vip.Emoji, emjM map[string]int64, err error) {
var res result
params := url.Values{}
if err = dao.httpClient.Get(c, dao.emojiURL, "", params, &res); err != nil {
log.Error("vip url(%s) error(%v)", dao.emojiURL+"?"+params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("vip url(%s) error(%v)", dao.emojiURL+"?"+params.Encode(), res.Code)
return
}
emjs = make([]*vip.Emoji, 0, len(res.Data))
emjM = make(map[string]int64)
for _, d := range res.Data {
var tmp = &vip.Emoji{
Pid: d.ID,
Pname: d.Name,
Purl: d.URL,
Pstate: d.State,
}
emoTmps := make([]*vip.Face, 0, len(d.Emojis))
for _, e := range d.Emojis {
var emoTmp = &vip.Face{
ID: e.ID,
Name: e.Name,
URL: e.URL,
State: e.State,
}
emoTmps = append(emoTmps, emoTmp)
if d.State == 0 && e.State == 0 {
emjM[e.Name] = e.ID
}
}
tmp.Emojis = emoTmps
emjs = append(emjs, tmp)
}
return
}

View File

@@ -0,0 +1,67 @@
package vip
import (
"context"
"flag"
"go-common/app/interface/main/reply/conf"
"os"
"testing"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "")
flag.Set("conf_token", "")
flag.Set("tree_id", "")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/reply-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func TestVipNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := New(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestVipEmoji(t *testing.T) {
convey.Convey("Emoji", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
emjs, emjM, err := d.Emoji(c)
ctx.Convey("Then err should be nil.emjs,emjM should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(emjM, convey.ShouldNotBeNil)
ctx.So(emjs, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["workflow.go"],
importpath = "go-common/app/interface/main/reply/dao/workflow",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["workflow_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,454 @@
package workflow
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"go-common/app/interface/main/reply/conf"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
const (
workflowHost = "api.bilibili.co"
_apiTopics = "http://matsuri.bilibili.co/activity/pages"
_apiActivitySub = "http://matsuri.bilibili.co//activity/subject/url"
_apiLiveActivity = "http://api.live.bilibili.co/comment/v1/relation/get_by_id"
_apiNotice = "http://api.bilibili.co/x/internal/credit/publish/infos"
_apiBan = "http://api.bilibili.co/x/internal/credit/blocked/infos"
_apiCredit = "http://api.bilibili.co/x/internal/credit/blocked/cases"
_apiLiveVideo = "http://api.vc.bilibili.co/clip/v1/video/detail"
_apiLiveNotice = "http://api.vc.bilibili.co/news/v1/notice/info"
_apiLivePicture = "http://api.vc.bilibili.co/link_draw/v1/doc/detail"
_apiTopic = "http://matsuri.bilibili.co/activity/page/one/%d"
_apiDynamic = "http://api.vc.bilibili.co/dynamic_repost/v0/dynamic_repost/ftch_rp_cont?dynamic_ids[]=%d"
_apiHuoniao = "http://manga.bilibili.co/twirp/verify.v0.Verify/InfoC"
// link
_linkBan = "https://www.bilibili.com/blackroom/ban/%d"
_linkNotice = "https://www.bilibili.com/blackroom/notice/%d"
_linkCredit = "https://www.bilibili.com/judgement/case/%d"
_linkLiveVideo = "http://vc.bilibili.com/video/%d"
_linkLiveNotice = "http://link.bilibili.com/p/eden/news#/newsdetail?id=%d"
_linkLivePicture = "http://h.bilibili.com/ywh/%d"
_linkDynamic = "http://t.bilibili.com/%d"
_urlLiveNotice = "http://api.vc.bilibili.co/news/v1/notice/info"
_linkHuoniao = "http://manga.bilibili.com/m/detail/mc%d"
)
type result struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
ChallengeNo int64 `json:"challengeNo"`
}
}
// Dao dao.
type Dao struct {
httpClient *bm.Client
}
// New new a dao and return.
func New(c *conf.Config) *Dao {
d := &Dao{
httpClient: bm.NewClient(c.HTTPClient),
}
return d
}
type extra struct {
Like int `json:"like"`
Link string `json:"link"`
Title string `json:"title"`
}
func (dao *Dao) AddReport(c context.Context, oid int64, typ int8, typeid int32, rpid int64, score int, reason int8, reporter int64, reported int64, like int, content string, link string, title string) (err error) {
var res result
rid := "1"
params := url.Values{}
params.Add("business", "13")
params.Add("fid", strconv.FormatInt(int64(typ), 10))
if model.GetReportType(reason) == model.ReportStateNewTwo {
rid = "2"
}
params.Add("rid", rid)
params.Add("eid", strconv.FormatInt(int64(rpid), 10))
params.Add("score", strconv.FormatInt(int64(score), 10))
params.Add("tid", strconv.FormatInt(int64(reason), 10))
params.Add("oid", strconv.FormatInt(int64(oid), 10))
params.Add("mid", strconv.FormatInt(int64(reporter), 10))
params.Add("business_typeid", strconv.FormatInt(int64(typeid), 10))
params.Add("business_title", content)
params.Add("business_mid", strconv.FormatInt(int64(reported), 10))
var ex extra
ex.Like = like
ex.Link = link
ex.Title = title
businessExtra, _ := json.Marshal(&ex)
params.Add("business_extra", string(businessExtra))
url := fmt.Sprintf("http://%s/%s", workflowHost, "x/internal/workflow/appeal/v3/add")
if err = dao.httpClient.Post(c, url, "", params, &res); err != nil {
log.Error("AddReport url(%s,%s) error(%v)", url, params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("AddReport url(%s,%s) error(%v)", url, params.Encode(), res.Code)
err = ecode.Int(res.Code)
return
}
return
}
// TopicsLink get topic info.
func (d *Dao) TopicsLink(c context.Context, links map[int64]string, isTopic bool) (err error) {
if len(links) == 0 {
return
}
var res struct {
Code int `json:"code"`
Data *struct {
List []struct {
ID int64 `json:"id"`
PCURL string `json:"pc_url"`
H5URL string `json:"h5_url"`
} `json:"list"`
} `json:"data"`
}
var ids []int64
for oid := range links {
ids = append(ids, oid)
}
params := url.Values{}
params.Set("pids", xstr.JoinInts(ids))
params.Set("all", "isOne")
if isTopic {
params.Set("mold", "1")
}
if err = d.httpClient.Get(c, _apiTopics, "", params, &res); err != nil {
log.Error("TopicsTitle(%s) error(%v)", _apiTopics, err)
return
}
if res.Data == nil {
err = fmt.Errorf("url:%s code:%d", _apiTopics, res.Code)
return
}
for _, data := range res.Data.List {
if data.PCURL != "" {
links[data.ID] = data.PCURL
} else {
links[data.ID] = data.H5URL
}
}
return
}
// ActivitySub return activity sub link.
func (d *Dao) ActivitySub(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("oid", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Title string `json:"name"`
Link string `json:"act_url"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _apiActivitySub, "", params, &res); err != nil {
log.Error("d.httpClient.Get(%s?%s) error(%v)", _apiActivitySub, params.Encode(), err)
return
}
if res.Data == nil {
err = fmt.Errorf("url:%s code:%d", _apiActivitySub, res.Code)
return
}
title = res.Data.Title
link = res.Data.Link
return
}
// LiveNotice return link.
func (d *Dao) LiveNotice(c context.Context, oid int64) (title string, err error) {
params := url.Values{}
params.Set("id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Title string `json:"title"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _urlLiveNotice, "", params, &res); err != nil {
log.Error("d.httpClient.Get(%s?%s) error(%v)", _urlLiveNotice, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil {
err = fmt.Errorf("url:%s?%s code:%d", _urlLiveNotice, params.Encode(), res.Code)
return
}
title = res.Data.Title
return
}
// LiveActivityTitle get live activity info.
func (d *Dao) LiveActivityTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _apiLiveActivity, "", params, &res); err != nil {
log.Error("httpLiveActivityTitle(%s?%s) error(%v)", _apiLiveActivity, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil {
err = fmt.Errorf("url:%s?%s code:%d", _apiLiveActivity, params.Encode(), res.Code)
return
}
title = res.Data.Name
link = res.Data.URL
return
}
type notice struct {
Title string `json:"title"`
}
type ban struct {
Title string `json:"punishTitle"`
}
type credit struct {
Title string `json:"punishTitle"`
}
// NoticeTitle get blackromm notice info.
func (d *Dao) NoticeTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("ids", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data map[int64]*notice `json:"data"`
}
if err = d.httpClient.Get(c, _apiNotice, "", params, &res); err != nil {
log.Error("httpNotice(%s) error(%v)", _apiNotice, err)
return
}
if r := res.Data[oid]; r != nil {
title = r.Title
}
link = fmt.Sprintf(_linkNotice, oid)
return
}
// BanTitle get ban info.
func (d *Dao) BanTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("ids", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data map[int64]*ban `json:"data"`
}
if err = d.httpClient.Get(c, _apiBan, "", params, &res); err != nil {
log.Error("httpBan(%s) error(%v)", _apiBan, err)
return
}
if r := res.Data[oid]; r != nil {
title = r.Title
}
link = fmt.Sprintf(_linkBan, oid)
return
}
// CreditTitle return link.
func (d *Dao) CreditTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("ids", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data map[int64]*credit `json:"data"`
}
if err = d.httpClient.Get(c, _apiCredit, "", params, &res); err != nil {
log.Error("d.httpClient.Get(%s?%s) error(%v)", _apiCredit, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil {
err = fmt.Errorf("url:%s?%s code:%d", _apiCredit, params.Encode(), res.Code)
return
}
if r := res.Data[oid]; r != nil {
title = r.Title
}
link = fmt.Sprintf(_linkCredit, oid)
return
}
// LiveVideoTitle get live video title.
func (d *Dao) LiveVideoTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("video_id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Item *struct {
Description string `json:"description"`
} `json:"item"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _apiLiveVideo, "", params, &res); err != nil {
log.Error("httpLiveVideoTitle(%s?%s) error(%v)", _apiLiveVideo, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil || res.Data.Item == nil {
err = fmt.Errorf("url:%s?%s code:%d", _apiLiveVideo, params.Encode(), res.Code)
return
}
title = res.Data.Item.Description
link = fmt.Sprintf(_linkLiveVideo, oid)
return
}
// LiveNoticeTitle get live notice info.
func (d *Dao) LiveNoticeTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Title string `json:"title"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _apiLiveNotice, "", params, &res); err != nil {
log.Error("LiveNoticeTitle(%s?%s) error(%v)", _apiLiveNotice, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil {
err = fmt.Errorf("url:%s?%s code:%d", _apiLiveNotice, params.Encode(), res.Code)
return
}
title = res.Data.Title
link = fmt.Sprintf(_linkLiveNotice, oid)
return
}
// LivePictureTitle get live picture info.
func (d *Dao) LivePictureTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("doc_id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Item *struct {
Title string `json:"title"`
Desc string `json:"description"`
} `json:"item"`
} `json:"data"`
}
if err = d.httpClient.Get(c, _apiLivePicture, "", params, &res); err != nil {
log.Error("LivePictureTitle(%s?%s) error(%v)", _apiLivePicture, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil || res.Data.Item == nil {
err = fmt.Errorf("url:%s?%s code:%d", _apiLivePicture, params.Encode(), res.Code)
return
}
title = res.Data.Item.Title
if title == "" {
title = res.Data.Item.Desc
}
link = fmt.Sprintf(_linkLivePicture, oid)
return
}
// TopicTitle get topic info.
func (d *Dao) TopicTitle(c context.Context, oid int64) (title, link string, err error) {
var res struct {
Code int `json:"code"`
Data *struct {
Title string `json:"name"`
PCLink string `json:"pc_url"`
H5Link string `json:"h5_url"`
} `json:"data"`
}
if err = d.httpClient.Get(c, fmt.Sprintf(_apiTopic, oid), "", nil, &res); err != nil {
log.Error("TopicTitle(%s) error(%v)", fmt.Sprintf(_apiTopic, oid), err)
return
}
if res.Data == nil {
err = fmt.Errorf("url:%s code:%d", fmt.Sprintf(_apiTopic, oid), res.Code)
return
}
title = res.Data.Title
link = res.Data.PCLink
if link == "" {
link = res.Data.H5Link
}
return
}
// DynamicTitle return link and title.
func (d *Dao) DynamicTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
uri := fmt.Sprintf(_apiDynamic, oid)
var res struct {
Code int `json:"code"`
Data *struct {
Pairs []struct {
DynamicID int64 `json:"dynamic_id"`
Content string `json:"rp_cont"`
Type int32 `json:"type"`
} `json:"pairs"`
TotalCount int64 `json:"total_count"`
} `json:"data,omitempty"`
Message string `json:"message"`
}
if err = d.httpClient.Get(c, uri, "", params, &res); err != nil {
log.Error("d.httpClient.Get(%s?%s) error(%v)", uri, params.Encode(), err)
return
}
if res.Code != 0 || res.Data == nil || len(res.Data.Pairs) == 0 {
err = fmt.Errorf("get dynamic failed!url:%s?%s code:%d message:%s pairs:%v", uri, params.Encode(), res.Code, res.Message, res.Data.Pairs)
return
}
title = res.Data.Pairs[0].Content
link = fmt.Sprintf(_linkDynamic, oid)
return
}
// HuoniaoTitle title and link
func (d *Dao) HuoniaoTitle(c context.Context, oid int64) (title, link string, err error) {
params := url.Values{}
params.Set("id", strconv.FormatInt(oid, 10))
var res struct {
Code int `json:"code"`
Data *struct {
Title string `json:"title"`
JumpURL string `json:"jump_url"`
} `json:"data,omitempty"`
Message string `json:"msg"`
}
if err = d.httpClient.Post(c, _apiHuoniao, "", params, &res); err != nil {
log.Error("httpClient.Post(%s) error(%v)", params.Encode(), err)
return
}
if res.Code != ecode.OK.Code() {
err = ecode.Int(res.Code)
return
}
if res.Data != nil {
title = res.Data.Title
link = res.Data.JumpURL
}
return
}

View File

@@ -0,0 +1,260 @@
package workflow
import (
"context"
"flag"
"os"
"testing"
"go-common/app/interface/main/reply/conf"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.community.reply")
flag.Set("conf_token", "54e85e3ab609f79ae908b9ea3e3f0775")
flag.Set("tree_id", "2125")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../../cmd/reply-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
os.Exit(m.Run())
}
func TestWorkflowNew(t *testing.T) {
convey.Convey("New", t, func(ctx convey.C) {
var (
c = conf.Conf
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := New(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowAddReport(t *testing.T) {
convey.Convey("AddReport", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
typ = int8(0)
typeid = int32(0)
rpid = int64(0)
score = int(0)
reason = int8(0)
reporter = int64(0)
reported = int64(0)
like = int(0)
content = ""
link = ""
title = ""
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddReport(c, oid, typ, typeid, rpid, score, reason, reporter, reported, like, content, link, title)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowTopicsLink(t *testing.T) {
convey.Convey("TopicsLink", t, func(ctx convey.C) {
var (
c = context.Background()
links map[int64]string
isTopic bool
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TopicsLink(c, links, isTopic)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestWorkflowActivitySub(t *testing.T) {
convey.Convey("ActivitySub", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.ActivitySub(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowLiveNotice(t *testing.T) {
convey.Convey("LiveNotice", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, err := d.LiveNotice(c, oid)
ctx.Convey("Then err should be nil.title should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowLiveActivityTitle(t *testing.T) {
convey.Convey("LiveActivityTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.LiveActivityTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowNoticeTitle(t *testing.T) {
convey.Convey("NoticeTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, _ := d.NoticeTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowBanTitle(t *testing.T) {
convey.Convey("BanTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, _ := d.BanTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowCreditTitle(t *testing.T) {
convey.Convey("CreditTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, _ := d.CreditTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowLiveNoticeTitle(t *testing.T) {
convey.Convey("LiveNoticeTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.LiveNoticeTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowLivePictureTitle(t *testing.T) {
convey.Convey("LivePictureTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.LivePictureTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowDynamicTitle(t *testing.T) {
convey.Convey("DynamicTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.DynamicTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestWorkflowHuoniaoTitle(t *testing.T) {
convey.Convey("HuoniaoTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
title, link, err := d.HuoniaoTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"cursor.go",
"http.go",
"local.go",
"reply.go",
"reply_admin.go",
"reply_record.go",
"reply_report.go",
"xreply.go",
],
importpath = "go-common/app/interface/main/reply/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/reply/conf:go_default_library",
"//app/interface/main/reply/model/adminlog:go_default_library",
"//app/interface/main/reply/model/reply:go_default_library",
"//app/interface/main/reply/model/xreply:go_default_library",
"//app/interface/main/reply/service: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/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/metadata:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,418 @@
package http
import (
"strconv"
"go-common/app/interface/main/reply/conf"
model "go-common/app/interface/main/reply/model/reply"
xmodel "go-common/app/interface/main/reply/model/xreply"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
)
func subReplyByCursor(ctx *bm.Context) {
params := ctx.Request.Form
oid, err := strconv.ParseInt(params.Get("oid"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
var build int64
appStr := params.Get("mobi_app")
buildStr := params.Get("build")
if buildStr != "" {
build, err = strconv.ParseInt(buildStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(build %s) err(%v)", buildStr, err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
otyp, err := strconv.ParseInt(params.Get("type"), 10, 8)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
var size int64
if params.Get("size") != "" {
size, err = strconv.ParseInt(params.Get("size"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
}
if size <= 0 || size > int64(conf.Conf.Reply.MaxPageSize) {
size = int64(conf.Conf.Reply.MaxPageSize)
}
var (
rootID int64
replyID int64
cursor *model.Cursor
)
if params.Get("rpid") != "" {
// jump subReply
replyID, err = strconv.ParseInt(params.Get("rpid"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
rootID, cursor, err = rpSvr.NewSubCursorByReplyID(ctx, oid, int8(otyp), replyID, int(size), model.OrderASC)
if err != nil {
ctx.JSON(nil, err)
return
}
} else {
var maxID, minID int64
if params.Get("min_id") != "" {
minID, err = strconv.ParseInt(params.Get("min_id"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
}
if params.Get("max_id") != "" {
maxID, err = strconv.ParseInt(params.Get("max_id"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
}
cursor, err = model.NewCursor(maxID, minID, int(size), model.OrderASC)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
}
if rootID <= 0 {
rootID, err = strconv.ParseInt(params.Get("root"), 10, 64)
if err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
}
var (
showFolded bool
scene int64
)
// 消息中心折叠的评论也要给加回来这里约定他们传一个scene=1
if params.Get("scene") != "" {
scene, _ = strconv.ParseInt(params.Get("scene"), 10, 64)
}
if model.ShouldShowFolded(params.Get("mobi_app"), build, scene) {
showFolded = true
}
// 这里老版本折叠评论也要显示
cursorParams := &model.CursorParams{
IP: metadata.String(ctx, metadata.RemoteIP),
Oid: oid,
RootID: rootID,
OTyp: int8(otyp),
HTMLEscape: params.Get("mobi_app") == "",
Cursor: cursor,
ShowFolded: showFolded,
}
if m, ok := ctx.Get("mid"); ok {
cursorParams.Mid = m.(int64)
}
cursorRes, err := rpSvr.GetSubReplyListByCursor(ctx, cursorParams)
if err != nil {
ctx.JSON(nil, err)
return
}
var blacklist, assist int
if mid := cursorParams.Mid; mid > 0 {
if !rpSvr.IsWhiteAid(cursorParams.Oid, int8(cursorParams.OTyp)) {
if rpSvr.RelationBlocked(ctx, cursorRes.Subject.Mid, mid) {
blacklist = 1
}
if ok, _ := rpSvr.CheckAssist(ctx, cursorRes.Subject.Mid, mid); ok {
assist = 1
}
}
}
var config xmodel.ReplyConfig
config.ShowFloor = 1
if !rpSvr.ShowFloor(cursorParams.Oid, cursorParams.OTyp) {
config.ShowFloor = 0
}
rootReply := cursorRes.Roots[0]
if showFolded {
rootReply.Folder.HasFolded = false
}
rpSvr.EmojiReplaceI(appStr, build, rootReply)
data := map[string]interface{}{
"assist": assist,
"blacklist": blacklist,
"upper": map[string]interface{}{
"mid": cursorRes.Subject.Mid,
},
"root": rootReply,
"cursor": map[string]interface{}{
"all_count": rootReply.RCount,
"max_id": cursorRes.CursorRangeMax,
"min_id": cursorRes.CursorRangeMin,
"size": len(rootReply.Replies),
},
"config": config,
}
ctx.JSON(data, err)
}
func replyByCursor(ctx *bm.Context) {
params := ctx.Request.Form
buvid := ctx.Request.Header.Get("buvid")
oid, err := strconv.ParseInt(params.Get("oid"), 10, 64)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
otyp, err := strconv.ParseInt(params.Get("type"), 10, 8)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
var sort int64
if params.Get("sort") != "" {
sort, err = strconv.ParseInt(params.Get("sort"), 10, 8)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
plat := int64(model.PlatWeb)
if params.Get("plat") != "" {
plat, err = strconv.ParseInt(params.Get("plat"), 10, 8)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
var build int64
if params.Get("build") != "" {
build, err = strconv.ParseInt(params.Get("build"), 10, 64)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
var size int64
if params.Get("size") != "" {
size, err = strconv.ParseInt(params.Get("size"), 10, 32)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
if size <= 0 || size > int64(conf.Conf.Reply.MaxPageSize) {
size = int64(conf.Conf.Reply.MaxPageSize)
}
var (
replyID int64
cursor *model.Cursor
)
if params.Get("rpid") != "" {
// jump root reply
replyID, err = strconv.ParseInt(params.Get("rpid"), 10, 64)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
cursor, err = rpSvr.NewCursorByReplyID(ctx, oid, int8(otyp), replyID, int(size), model.OrderDESC)
} else {
var maxID, minID int64
if params.Get("min_id") != "" {
minID, err = strconv.ParseInt(params.Get("min_id"), 10, 64)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
if params.Get("max_id") != "" {
maxID, err = strconv.ParseInt(params.Get("max_id"), 10, 64)
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
}
cursor, err = model.NewCursor(maxID, minID, int(size), model.OrderDESC)
}
if err != nil {
log.Warn("%v", err)
err = ecode.RequestErr
ctx.JSON(nil, err)
return
}
appStr := params.Get("mobi_app")
var showFolded bool
// 对于根评论 scene传0, 只需要做版本的兼容即可
if model.ShouldShowFolded(params.Get("mobi_app"), build, 0) {
showFolded = true
}
cursorParams := &model.CursorParams{
IP: metadata.String(ctx, metadata.RemoteIP),
Oid: oid,
OTyp: int8(otyp),
Sort: int8(sort),
HTMLEscape: appStr == "",
Cursor: cursor,
HotSize: 5,
Mid: metadata.Int64(ctx, metadata.Mid),
ShowFolded: showFolded,
}
if m, ok := ctx.Get("mid"); ok {
cursorParams.Mid = m.(int64)
}
cursorRes, err := rpSvr.GetRootReplyListByCursor(ctx, cursorParams)
if err != nil {
ctx.JSON(nil, err)
return
}
var blacklist, assist int
if mid := cursorParams.Mid; mid > 0 {
if !rpSvr.IsWhiteAid(cursorParams.Oid, int8(cursorParams.OTyp)) {
if rpSvr.RelationBlocked(ctx, cursorRes.Subject.Mid, mid) {
blacklist = 1
}
if ok, _ := rpSvr.CheckAssist(ctx, cursorRes.Subject.Mid, mid); ok {
assist = 1
}
}
}
rpSvr.EmojiReplace(int8(plat), build, cursorRes.Roots...)
rpSvr.EmojiReplaceI(appStr, build, cursorRes.Roots...)
cursorRes.Roots = rpSvr.FilDelReply(cursorRes.Roots)
m := map[string]interface{}{
"assist": assist,
"blacklist": blacklist,
"replies": cursorRes.Roots,
"upper": map[string]interface{}{
"mid": cursorRes.Subject.Mid,
},
"cursor": map[string]interface{}{
"all_count": cursorRes.Subject.ACount,
"max_id": cursorRes.CursorRangeMax,
"min_id": cursorRes.CursorRangeMin,
"size": len(cursorRes.Roots),
},
}
if cursorRes.Header != nil {
rpSvr.EmojiReplace(int8(plat), build, cursorRes.Header.Hots...)
rpSvr.EmojiReplace(int8(plat), build, cursorRes.Header.TopAdmin)
rpSvr.EmojiReplace(int8(plat), build, cursorRes.Header.TopUpper)
rpSvr.EmojiReplaceI(appStr, build, cursorRes.Header.Hots...)
rpSvr.EmojiReplaceI(appStr, build, cursorRes.Header.TopAdmin)
rpSvr.EmojiReplaceI(appStr, build, cursorRes.Header.TopUpper)
cursorRes.Header.Hots = rpSvr.FilDelReply(cursorRes.Header.Hots)
showEntry, showAdmin, showFloor := 1, 1, 1
if config, _ := rpSvr.GetReplyLogConfig(ctx, cursorRes.Subject, 1); config != nil {
showEntry = int(config.ShowEntry)
showAdmin = int(config.ShowAdmin)
}
if !rpSvr.ShowFloor(cursorParams.Oid, cursorParams.OTyp) {
showFloor = 0
}
m["config"] = map[string]int{
"showentry": showEntry,
"showadmin": showAdmin,
"showfloor": showFloor,
}
if cursorRes.Subject.RCount <= 20 && len(cursorRes.Header.Hots) > 0 {
cursorRes.Header.Hots = cursorRes.Header.Hots[:0]
}
m["hots"] = cursorRes.Header.Hots
m["notice"] = rpSvr.RplyNotice(ctx, int8(plat), build, appStr, buvid)
m["top"] = map[string]interface{}{
"admin": cursorRes.Header.TopAdmin,
"upper": cursorRes.Header.TopUpper,
}
}
ctx.JSON(m, err)
}
func dialogByCursor(c *bm.Context) {
var mid int64
v := new(struct {
Oid int64 `form:"oid" validate:"required"`
Type int8 `form:"type" validate:"required"`
Root int64 `form:"root" validate:"required"`
Dialog int64 `form:"dialog" validate:"required"`
Size int `form:"size" validate:"min=1"`
MinFloor int64 `form:"min_floor"`
MaxFloor int64 `form:"max_floor"`
Plat int64 `form:"plat"`
Build int64 `form:"build"`
MobiApp string `form:"mobi_app"`
})
// buvid := c.Request.Header.Get("buvid")
var err error
err = c.Bind(v)
if err != nil {
return
}
if v.Size > conf.Conf.Reply.MaxPageSize {
v.Size = conf.Conf.Reply.MaxPageSize
}
if m, ok := c.Get("mid"); ok {
mid = m.(int64)
}
cursor, err := model.NewCursor(v.MaxFloor, v.MinFloor, v.Size, model.OrderASC)
if err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rps, dialogCursor, dialogMeta, err := rpSvr.DialogByCursor(c, mid, v.Oid, v.Type, v.Root, v.Dialog, cursor)
if err != nil {
log.Error("rpSvr.DialogByCursor error (%v)", err)
c.JSON(nil, err)
return
}
rpSvr.EmojiReplaceI(v.MobiApp, v.Build, rps...)
var config xmodel.ReplyConfig
config.ShowFloor = 1
if !rpSvr.ShowFloor(v.Oid, v.Type) {
config.ShowFloor = 0
}
data := map[string]interface{}{
"cursor": dialogCursor,
"dialog": dialogMeta,
"replies": rps,
"config": config,
}
c.JSON(data, nil)
}

View File

@@ -0,0 +1,106 @@
package http
import (
"go-common/app/interface/main/reply/conf"
"go-common/app/interface/main/reply/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
cnf *conf.Config
rpSvr *service.Service
authSvc *auth.Auth
verifySvc *verify.Verify
)
// Init init http
func Init(c *conf.Config) {
initService(c)
engine := bm.DefaultServer(c.BM)
outerRouter(engine)
interRouter(engine)
if err := engine.Start(); err != nil {
log.Error("xhttp.Serve error(%v)", err)
panic(err)
}
}
func initService(c *conf.Config) {
cnf = c
authSvc = auth.New(c.Auth)
verifySvc = verify.New(c.Verify)
rpSvr = service.New(conf.Conf)
}
func outerRouter(e *bm.Engine) {
// init api
e.GET("/monitor/ping", ping)
// reply
group := e.Group("/x/v2/reply")
{
group.GET("", authSvc.Guest, reply)
group.GET("/cursor", authSvc.Guest, replyByCursor)
group.GET("/reply/cursor", authSvc.Guest, subReplyByCursor)
group.GET("/hot", replyHots)
group.GET("/emojis", emojis)
group.GET("/web/emojis", emojis)
group.GET("/info", authSvc.Guest, replyInfo)
group.GET("/minfo", authSvc.Guest, replyMultiInfo)
group.GET("/reply", authSvc.Guest, replyReply)
group.GET("/jump", authSvc.Guest, jumpReply)
group.GET("/count", authSvc.Guest, replyCount)
group.GET("/mcount", authSvc.Guest, replyMultiCount)
group.GET("/log", authSvc.Guest, replyAdminLog)
group.POST("/add", authSvc.User, addReply)
group.POST("/action", authSvc.User, likeReply)
group.POST("/hate", authSvc.User, hateReply)
group.POST("/show", authSvc.User, showReply)
group.POST("/hide", authSvc.User, hideReply)
group.POST("/del", authSvc.User, delReply)
group.POST("/top", authSvc.User, AddTopReply)
group.POST("/report", authSvc.User, reportReply)
group.GET("/topics", authSvc.Guest, getTopics)
group.GET("/report/related", authSvc.Guest, reportRelated)
group.GET("/report/reply", authSvc.Guest, reportSndReply)
group.GET("/dialog", authSvc.Guest, dialog)
group.GET("/dialog/cursor", authSvc.Guest, dialogByCursor)
// 5.37需求
group.GET("/main", authSvc.Guest, xreply)
group.GET("/folded", authSvc.Guest, subFolder)
group.GET("/reply/folded", authSvc.Guest, rootFolder)
}
}
func interRouter(e *bm.Engine) {
// internal admin
group := e.Group("/x/internal/v2/reply")
{
group.GET("/subject", verifySvc.Verify, adminSubject)
group.POST("/subject/mid", verifySvc.Verify, adminSubjectMid)
group.GET("/hot", verifySvc.Verify, replyHots)
group.POST("/subject/state", verifySvc.Verify, adminSubjectState)
group.POST("/subject/regist", verifySvc.Verify, adminSubRegist)
group.POST("/audit", verifySvc.Verify, adminAuditSub)
group.POST("/pass", verifySvc.Verify, adminPassReply)
group.POST("/recover", verifySvc.Verify, adminRecoverReply)
group.POST("/edit", verifySvc.Verify, adminEditReply)
group.POST("/del", verifySvc.Verify, adminDelReply)
group.POST("/top", verifySvc.Verify, adminAddTopReply)
group.POST("/report/del", verifySvc.Verify, adminDelReplyByReport)
group.POST("/report/ignore", verifySvc.Verify, adminIgnoreReport)
group.POST("/report/recover", verifySvc.Verify, adminReportRecover)
group.POST("/report/transfer", verifySvc.Verify, adminTransferReport)
group.POST("/report/state", verifySvc.Verify, adminReportStateSet)
group.GET("/info", verifySvc.Verify, replyInfo)
group.GET("/count", verifySvc.Verify, replyCount)
group.GET("/counts", verifySvc.Verify, replyCounts)
group.GET("/minfo", verifySvc.Verify, replyMultiInfo)
group.GET("/mcount", verifySvc.Verify, replyMultiCount)
group.GET("/record", verifySvc.Verify, replyRecord)
group.GET("/hots", verifySvc.Verify, hotsBatch)
group.GET("/ishot", isHotReply)
}
}

View File

@@ -0,0 +1,16 @@
package http
import (
"net/http"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// ping check server ok.
func ping(c *bm.Context) {
if err := rpSvr.Ping(c); err != nil {
log.Error("reply interface ping error")
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,782 @@
package http
import (
"strconv"
"strings"
"text/template"
"go-common/app/interface/main/reply/conf"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
const (
_remarkLength = 200
)
func adminSubjectMid(c *bm.Context) {
var (
err error
tp int64
oid int64
mid int64
adid int64
)
params := c.Request.Form
tpStr := params.Get("type")
oidStr := params.Get("oid")
midStr := params.Get("mid")
adidStr := params.Get("adid")
remark := params.Get("remark")
if tp, err = strconv.ParseInt(tpStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", midStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if adid, err = strconv.ParseInt(adidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
err = rpSvr.AdminSubjectMid(c, adid, mid, oid, int8(tp), remark)
c.JSON(nil, err)
}
func adminSubRegist(c *bm.Context) {
var (
err error
oid int64
tp int64
state int64
mid int64
)
params := c.Request.Form
midStr := params.Get("mid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
stateStr := params.Get("state")
appkey := params.Get("appkey")
if oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", midStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if stateStr != "" {
if state, err = strconv.ParseInt(stateStr, 10, 8); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", stateStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
if tpStr != "" {
if tp, err = strconv.ParseInt(tpStr, 10, 8); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
err = rpSvr.AdminSubRegist(c, oid, mid, int8(tp), int8(state), appkey)
c.JSON(nil, err)
}
func adminSubject(c *bm.Context) {
params := c.Request.Form
oidStr := params.Get("oid")
tpStr := params.Get("type")
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
sub, err := rpSvr.AdminGetSubject(c, oid, int8(tp))
if err != nil {
log.Warn("rpSvr.AdminGetSubjectState(oid%d,tp,%d)error(%v)", oid, int8(tp))
c.JSON(nil, err)
return
}
c.JSON(sub, nil)
}
// adminModifySubject modify subject state.
func adminSubjectState(c *bm.Context) {
var (
err error
mid int64
)
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
stateStr := params.Get("state")
midStr := params.Get("mid")
remark := params.Get("remark")
// check params
remark = strings.TrimSpace(remark)
rml := len([]rune(remark))
if rml > _remarkLength {
log.Warn("remark(%s) length %d, max %d", remark, rml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
state, err := strconv.ParseInt(stateStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", stateStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if midStr != "" {
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", midStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
err = rpSvr.AdminSubjectState(c, adid, oid, mid, int8(tp), int8(state), remark)
c.JSON(nil, err)
}
func adminAuditSub(c *bm.Context) {
}
// adminPassReply pass reply normal.
func adminPassReply(c *bm.Context) {
var err error
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
remark := params.Get("remark")
// check params
remark = strings.TrimSpace(remark)
rml := len([]rune(remark))
if rml > _remarkLength {
log.Warn("remark(%s) length %d, max %d", remark, rml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
// remark = template.JSEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if len(oids) != len(rpIDs) {
log.Warn("oids:%s rpIDs:%s ", oids, rpIDs)
return
}
for i := 0; i < len(oids); i++ {
err = rpSvr.AdminPass(c, adid, oids[i], rpIDs[i], int8(tp), remark)
}
c.JSON(nil, err)
}
// adminRecoverReply recover reply normal.
func adminRecoverReply(c *bm.Context) {
var err error
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
remark := params.Get("remark")
// check params
remark = strings.TrimSpace(remark)
rml := len([]rune(remark))
if rml > _remarkLength {
log.Warn("remark(%s) length %d, max %d", remark, rml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpID, err := strconv.ParseInt(rpIDStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
err = rpSvr.AdminRecover(c, adid, oid, rpID, int8(tp), remark)
c.JSON(nil, err)
}
// adminEditReply edit reply content by admin.
func adminEditReply(c *bm.Context) {
var err error
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
msg := params.Get("message")
remark := params.Get("remark")
// check params
msg = strings.TrimSpace(msg)
ml := len([]rune(msg))
if conf.Conf.Reply.MaxConLen < ml || ml < conf.Conf.Reply.MinConLen {
log.Warn("content(%s) length %d, max %d, min %d", msg, ml, conf.Conf.Reply.MaxConLen, conf.Conf.Reply.MinConLen)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
msg = template.HTMLEscapeString(msg)
remark = strings.TrimSpace(remark)
rml := len([]rune(remark))
if rml > _remarkLength {
log.Warn("remark(%s) length %d, max %d", remark, rml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpID, err := strconv.ParseInt(rpIDStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
err = rpSvr.AdminEdit(c, adid, oid, rpID, int8(tp), msg, remark)
c.JSON(nil, err)
}
// report reason
const (
TypeReportReasonOthers = 0
)
// _delReply delete reply, this call be limited to this file.
func _delReply(c *bm.Context, isReport bool) {
var (
err error
reason int64
freason int64
)
params := c.Request.Form
adidStr := params.Get("adid")
adname := params.Get("adname")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
morStr := params.Get("moral")
notStr := params.Get("notify")
remark := params.Get("remark")
ftimeStr := params.Get("ftime")
auditStr := params.Get("audit")
reasonStr := params.Get("reason")
content := params.Get("reason_content")
freasonStr := params.Get("freason")
// check params
remark = strings.TrimSpace(remark)
ml := len([]rune(remark))
if ml > _remarkLength {
log.Warn("remark (%s) length %d, max %d", remark, ml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tps, err := xstr.SplitInts(tpStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
moral, err := strconv.ParseInt(morStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
notify, err := strconv.ParseBool(notStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if reasonStr != "" {
if reason, err = strconv.ParseInt(reasonStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", reasonStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
if freasonStr != "" {
if freason, err = strconv.ParseInt(freasonStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", freasonStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
ftime, _ := strconv.ParseInt(ftimeStr, 10, 64)
audit, _ := strconv.ParseInt(auditStr, 10, 8)
if len(oids) == 0 || len(rpIDs) != len(oids) || len(tps) != len(oids) {
log.Warn("admin del reply oids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if reason != TypeReportReasonOthers {
content = ""
}
for i := 0; i < len(oids); i++ {
if isReport {
err = rpSvr.AdminDeleteByReport(c, adid, oids[i], rpIDs[i], ftime, int8(tps[i]), int(moral), notify, adname, remark, int8(audit), int8(reason), content, int8(freason))
} else {
err = rpSvr.AdminDelete(c, adid, oids[i], rpIDs[i], ftime, int8(tps[i]), int(moral), notify, adname, remark, int8(reason), int8(freason))
}
}
c.JSON(nil, err)
}
// adminDelReply delete reply by admin.
func adminDelReply(c *bm.Context) {
_delReply(c, false)
}
// adminDelReplyByReport delete a report reply by admin.
func adminDelReplyByReport(c *bm.Context) {
_delReply(c, true)
}
func adminReportStateSet(c *bm.Context) {
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
stateStr := params.Get("state")
var adid int64
if adidStr != "" {
var err error
adid, err = strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tps, err := xstr.SplitInts(tpStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if len(oids) == 0 || len(rpIDs) != len(oids) || len(tps) != len(oids) {
log.Warn("admin set report oids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
state, err := strconv.ParseInt(stateStr, 10, 8)
if err != nil {
log.Warn("admin set report soids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
for i := 0; i < len(oids); i++ {
err = rpSvr.AdminReportStateSet(c, adid, oids[i], rpIDs[i], int8(tps[i]), int8(state))
}
c.JSON(nil, err)
}
func adminTransferReport(c *bm.Context) {
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
auditStr := params.Get("audit")
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.SplitInts(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.SplitInts(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tps, err := xstr.SplitInts(tpStr)
if err != nil {
log.Warn("strconv.SplitInts(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if len(oids) == 0 || len(rpIDs) != len(oids) || len(tps) != len(oids) {
log.Warn("admin del reply oids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
audit, _ := strconv.ParseInt(auditStr, 10, 8)
for i := 0; i < len(oids); i++ {
err = rpSvr.AdminReportTransfer(c, adid, oids[i], rpIDs[i], int8(tps[i]), int8(audit))
}
c.JSON(nil, err)
}
// adminIgnoreReport ignore a report.
func adminIgnoreReport(c *bm.Context) {
var err error
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
auditStr := params.Get("audit")
remark := params.Get("remark")
// check params
remark = strings.TrimSpace(remark)
ml := len([]rune(remark))
if ml > _remarkLength {
log.Warn("remark (%s) length %d, max %d", remark, ml, 255)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tps, err := xstr.SplitInts(tpStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
audit, _ := strconv.ParseInt(auditStr, 10, 8)
if len(oids) == 0 || len(rpIDs) != len(oids) || len(tps) != len(oids) {
log.Warn("admin del reply oids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
for i := 0; i < len(oids); i++ {
err = rpSvr.AdminReportIgnore(c, adid, oids[i], rpIDs[i], int8(tps[i]), int8(audit), remark)
}
c.JSON(nil, err)
}
// adminAddTopReply add top reply
func adminAddTopReply(c *bm.Context) {
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
actStr := params.Get("action")
// check params
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpID, err := strconv.ParseInt(rpIDStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
act, err := strconv.ParseInt(actStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(actStr :%s) err(%v)", actStr, err)
act = 1
}
err = rpSvr.AdminAddTop(c, adid, oid, rpID, int8(tp), int8(act))
c.JSON(nil, err)
}
func adminReportRecover(c *bm.Context) {
var err error
params := c.Request.Form
adidStr := params.Get("adid")
oidStr := params.Get("oid")
tpStr := params.Get("type")
rpIDStr := params.Get("rpid")
auditStr := params.Get("audit")
remark := params.Get("remark")
// check params
remark = strings.TrimSpace(remark)
rml := len([]rune(remark))
if rml > _remarkLength {
log.Warn("remark(%s) length %d, max %d", remark, rml, _remarkLength)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
remark = template.HTMLEscapeString(remark)
adid, err := strconv.ParseInt(adidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", adidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
oids, err := xstr.SplitInts(oidStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIDs, err := xstr.SplitInts(rpIDStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpIDStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tps, err := xstr.SplitInts(tpStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
audit, err := strconv.ParseInt(auditStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if len(oids) == 0 || len(rpIDs) != len(oids) || len(tps) != len(oids) {
log.Warn("admin del reply oids: %v, rpIDs: %v, tps: %v", oids, rpIDs, tps)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
for i := 0; i < len(oids); i++ {
err = rpSvr.AdminReportRecover(c, adid, oids[i], rpIDs[i], int8(tps[i]), int8(audit), remark)
}
c.JSON(nil, err)
}

View File

@@ -0,0 +1,84 @@
package http
import (
"strconv"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
func replyRecord(c *bm.Context) {
var (
err error
types []int64
mid, stime, etime int64
pn = int64(1)
ps = int64(10)
)
params := c.Request.Form
stimeStr := params.Get("stime")
etimeStr := params.Get("etime")
typesStr := params.Get("types")
sortStr := params.Get("sort")
orderStr := params.Get("order")
pnStr := params.Get("pn")
psStr := params.Get("ps")
midStr := params.Get("mid")
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if typesStr != "" {
if types, err = xstr.SplitInts(typesStr); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
if stime, err = strconv.ParseInt(stimeStr, 10, 64); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if etime, err = strconv.ParseInt(etimeStr, 10, 64); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if pnStr != "" {
if pn, err = strconv.ParseInt(pnStr, 10, 32); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
if psStr != "" {
if ps, err = strconv.ParseInt(psStr, 10, 32); err != nil {
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
if orderStr == "" {
orderStr = "ctime"
}
if sortStr == "" {
sortStr = "desc"
}
records, total, err := rpSvr.Records(c, types, mid, stime, etime, orderStr, sortStr, int32(pn), int32(ps))
if err != nil {
c.JSON(nil, err)
return
}
data := map[string]interface{}{
"page": map[string]int64{
"num": pn,
"size": ps,
"total": int64(total),
},
"records": records,
}
c.JSON(data, err)
}

View File

@@ -0,0 +1,230 @@
package http
import (
"strconv"
"strings"
"text/template"
model "go-common/app/interface/main/reply/model/reply"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
// reportReply report a reply.
func reportReply(c *bm.Context) {
params := c.Request.Form
mid, _ := c.Get("mid")
oidsStr := params.Get("oid")
rpsStr := params.Get("rpid")
tpStr := params.Get("type")
reaStr := params.Get("reason")
cont := params.Get("content")
platform := params.Get("platform")
buildStr := params.Get("build")
buvid := c.Request.Header.Get("buvid")
var build int64
var err error
if buildStr != "" {
build, err = strconv.ParseInt(buildStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", buildStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
}
// check params
oids, err := xstr.SplitInts(oidsStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", oidsStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpIds, err := xstr.SplitInts(rpsStr)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", rpsStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
if len(oids) != len(rpIds) {
log.Warn("oids(%s) not equal rpids(%s)", oidsStr, rpsStr)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
reaTmp, err := strconv.ParseInt(reaStr, 10, 8)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", reaStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rea := int8(reaTmp)
if rea == model.ReportReasonOther {
cont = strings.TrimSpace(cont)
cl := len([]rune(cont))
if 200 < cl || cl < 2 {
log.Warn("content(%s) length %d, max 200, min 2", cont, cl)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
cont = template.HTMLEscapeString(cont)
} else {
cont = ""
}
for i := 0; i < len(oids); i++ {
cd, err := rpSvr.AddReport(c, mid.(int64), oids[i], rpIds[i], int8(tp), rea, cont, platform, build, buvid)
if err != nil {
var data map[string]int
if err == ecode.ReplyReportDeniedAsCD {
data = map[string]int{"ttl": cd}
} else {
log.Warn("rpSvr.AddReport(%d, %d, %d, %d, %d, %s, %s, %d, %s) error(%v)", mid, oids[i], rpIds[i], tp, rea, cont, err, platform, build, buvid)
}
c.JSON(data, err)
return
}
}
c.JSON(nil, nil)
}
func reportRelated(c *bm.Context) {
var (
mid int64
escape = true
)
params := c.Request.Form
oidStr := params.Get("oid")
rpidStr := params.Get("root")
tpStr := params.Get("type")
midIf, ok := c.Get("mid")
if ok {
mid = midIf.(int64)
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(oid:%d) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpid, err := strconv.ParseInt(rpidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(rpid:%d) error(%v)", rpidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(type:%d) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
// check android and ios appkey
// if mobile, no html escape
if isMobile(params) {
escape = false
}
sub, root, rels, err := rpSvr.ReportRelated(c, mid, oid, rpid, int8(tp), escape)
if err != nil {
log.Warn("rpSvr.ReportRelated(%d,%d,%d) error(%v)", oid, rpid, tp, err)
c.JSON(nil, err)
return
}
data := map[string]interface{}{
"root": root,
"related": rels,
"upper": map[string]interface{}{
"mid": sub.Mid,
},
}
c.JSON(data, err)
}
func reportSndReply(c *bm.Context) {
var (
mid int64
escape = true
)
params := c.Request.Form
oidStr := params.Get("oid")
rpidStr := params.Get("root")
tpStr := params.Get("type")
pnStr := params.Get("pn")
psStr := params.Get("ps")
midIf, ok := c.Get("mid")
if ok {
mid = midIf.(int64)
}
oid, err := strconv.ParseInt(oidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(oid:%d) error(%v)", oidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
rpid, err := strconv.ParseInt(rpidStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(rpid:%d) error(%v)", rpidStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(type:%d) error(%v)", tpStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
pn, err := strconv.ParseInt(pnStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(pn:%d) error(%v)", pnStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
ps, err := strconv.ParseInt(psStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(ps:%d) error(%v)", psStr, err)
err = ecode.RequestErr
c.JSON(nil, err)
return
}
// check android and ios appkey
// if mobile, no html escape
if isMobile(params) {
escape = false
}
sub, root, rs, err := rpSvr.ReportReply(c, mid, oid, rpid, int8(tp), int(pn), int(ps), escape)
if err != nil {
c.JSON(nil, err)
return
}
data := make(map[string]interface{}, 2)
data["page"] = map[string]int{
"num": int(pn),
"size": int(ps),
"count": root.Count,
}
data["upper"] = map[string]interface{}{
"mid": sub.Mid,
}
data["root"] = root
data["replies"] = rs
c.JSON(data, err)
}

View File

@@ -0,0 +1,50 @@
package http
import (
xmodel "go-common/app/interface/main/reply/model/xreply"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
)
func xreply(c *bm.Context) {
v := new(xmodel.ReplyReq)
if err := c.Bind(v); err != nil {
return
}
v.Mid = metadata.Int64(c, metadata.Mid)
v.IP = metadata.String(c, metadata.RemoteIP)
if !v.Cursor.Legal() {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(rpSvr.Xreply(c, v))
}
func subFolder(c *bm.Context) {
v := new(xmodel.SubFolderReq)
if err := c.Bind(v); err != nil {
return
}
v.Mid = metadata.Int64(c, metadata.Mid)
v.IP = metadata.String(c, metadata.RemoteIP)
if !v.Cursor.Legal() || v.Cursor.Backward() {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(rpSvr.SubFoldedReply(c, v))
}
func rootFolder(c *bm.Context) {
v := new(xmodel.RootFolderReq)
if err := c.Bind(v); err != nil {
return
}
v.Mid = metadata.Int64(c, metadata.Mid)
v.IP = metadata.String(c, metadata.RemoteIP)
if !v.Cursor.Legal() || v.Cursor.Backward() {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(rpSvr.RootFoldedReply(c, v))
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["activity.go"],
importpath = "go-common/app/interface/main/reply/model/activity",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,9 @@
package activity
// Act Act
type Act struct {
Aid int64 `json:"id"`
Title string `json:"name"`
Link string `json:"pc_url"`
Cover string `json:"pc_cover"`
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["admin_log.go"],
importpath = "go-common/app/interface/main/reply/model/adminlog",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,13 @@
package adminlog
// AdminLog AdminLog
type AdminLog struct {
ReplyID int64 `json:"reply_id"` //操作人
AdminID int64 `json:"adminid"` //操作人
Operator string `json:"operator"` //操作人昵称
State int32 `json:"operator_type"` //操作人身份
ReplyMid int64 `json:"replymid"` //评论人
ReplyUser string `json:"reply_user"` //评论人昵称
ReplyFacePic string `json:"reply_face_pic"` //评论人头像
CTime string `json:"operation_time"` //删除时间
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["drawyoo.go"],
importpath = "go-common/app/interface/main/reply/model/drawyoo",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,11 @@
package drawyoo
// Drawyoo Drawyoo
type Drawyoo struct {
Hid int64 `json:"hid"`
Mid int64 `json:"mid"`
Title string `json:"title"`
Link string `json:"link"`
Cover string `json:"cover"`
Msg string `json:"msg"`
}

View File

@@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["cursor_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"config.go",
"cursor.go",
"emoji.go",
"member.go",
"notice.go",
"page.go",
"record.go",
"reply.go",
"sort.go",
"sql.go",
],
importpath = "go-common/app/interface/main/reply/model/reply",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,16 @@
package reply
// Config reply config info.
type Config struct {
ID int64 `json:"id"`
Type int8 `json:"type"`
Oid int64 `json:"oid"`
AdminID int64 `json:"adminid"`
Operator string `json:"operator"`
Category int8 `json:"category"`
Config string `json:"config"`
ShowEntry int8 `json:"showentry"`
ShowAdmin int8 `json:"showadmin"`
CTime string `json:"ctime"`
MTime string `json:"mtime"`
}

View File

@@ -0,0 +1,138 @@
package reply
import (
"errors"
"fmt"
"sort"
"go-common/library/log"
)
// RootReplyListHeader RootReplyListHeader
type RootReplyListHeader struct {
TopAdmin *Reply
TopUpper *Reply
Hots []*Reply
}
// RootReplyList RootReplyList
type RootReplyList struct {
Subject *Subject
Header *RootReplyListHeader
TopAdmin *Reply
TopUpper *Reply
Hots []*Reply
Roots []*Reply
CursorRangeMax, CursorRangeMin int64
}
// CursorParams CursorParams
type CursorParams struct {
Mid int64
Oid int64
OTyp int8
Sort int8
HTMLEscape bool
IP string
RootID int64
Cursor *Cursor
HotSize int
ShowFolded bool
}
// Comp Comp
type Comp func(x, y int64) bool
// order
var (
OrderDESC = func(x, y int64) bool { return x > y }
OrderASC = func(x, y int64) bool { return x < y }
)
// SortArr SortArr
func SortArr(arr []int64, cmp Comp) []int64 {
if cmp == nil {
log.Warn("unexpected: cmp is nil")
cmp = OrderDESC
}
less := func(i, j int) bool { return cmp(arr[i], arr[j]) }
if sort.SliceIsSorted(arr, less) {
return arr
}
sort.Slice(arr, less)
return arr
}
// Len Len
func (c *Cursor) Len() int {
return c.length
}
// Current Current
func (c *Cursor) Current() int64 {
if c.maxID == 0 {
return c.minID
}
return c.maxID
}
// Latest Latest
func (c *Cursor) Latest() bool {
return c.maxID == 0 && c.minID == 0
}
// Descrease Descrease
func (c *Cursor) Descrease() bool {
return c.maxID > 0
}
// Increase Increase
func (c *Cursor) Increase() bool {
return c.minID > 0
}
// Max return maxID
func (c *Cursor) Max() int64 {
return c.maxID
}
// Min return minID
func (c *Cursor) Min() int64 {
return c.minID
}
// Sort Sort
func (c *Cursor) Sort(arr []int64) []int64 {
return SortArr(arr, c.comp)
}
// String String
func (c *Cursor) String() string {
return fmt.Sprintf("current: %d, growIncr: %v, size: %d", c.Current(), c.Increase(), c.length)
}
// NewCursor NewCursor
func NewCursor(maxID int64, minID int64, size int, cmp Comp) (*Cursor, error) {
if maxID < 0 || minID < 0 || cmp == nil {
return nil, fmt.Errorf("either max_id(%d) or min_id(%d) < 0 or cmp = nil", maxID, minID)
}
if (minID * maxID) != 0 {
return nil, errors.New("both max_id and max_id > 0")
}
return &Cursor{
length: size,
comp: cmp,
maxID: maxID,
minID: minID,
}, nil
}
// Cursor Cursor
type Cursor struct {
maxID, minID int64
length int
comp Comp
}

View File

@@ -0,0 +1,44 @@
package reply
import (
"reflect"
"testing"
)
func TestSort(t *testing.T) {
cases := []struct {
inputIds []int64
comp Comp
expected []int64
}{
{
inputIds: []int64{6, 5, 3, 2, 1},
comp: OrderASC,
expected: []int64{1, 2, 3, 5, 6},
},
{
inputIds: []int64{5, 3, 6, 1, 2},
comp: OrderDESC,
expected: []int64{6, 5, 3, 2, 1},
},
{
inputIds: []int64{2, 1},
comp: nil,
expected: []int64{2, 1},
},
{
inputIds: []int64{},
comp: OrderDESC,
expected: []int64{},
},
}
for _, c := range cases {
t.Run("", func(t *testing.T) {
got := SortArr(c.inputIds, c.comp)
if !reflect.DeepEqual(c.expected, got) {
t.Errorf("err sort, want %v, got %v", c.expected, got)
}
})
}
}

View File

@@ -0,0 +1,20 @@
package reply
// Emoji Emoji
type Emoji struct {
ID int64 `json:"id"`
PackageID int64 `json:"-"`
Name string `json:"name"`
URL string `json:"url"`
State int8 `json:"state"`
Remark string `json:"remark"`
}
// EmojiPackage EmojiPackage
type EmojiPackage struct {
ID int64 `json:"pid"`
Name string `json:"pname"`
State int8 `json:"pstate"`
URL string `json:"purl"`
Emojis []*Emoji `json:"emojis"`
}

View File

@@ -0,0 +1,20 @@
package reply
// Member ReplyMember
type Member struct {
*Info
FansDetail *FansDetail `json:"fans_detail"`
Following int16 `json:"following"` //是否关注
}
// FansDetail 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:已领取
}

View File

@@ -0,0 +1,36 @@
package reply
// eq
const (
EQ = int8(0) // condition equal
GT = int8(1) // greater
LT = int8(2) // less
)
// 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:"-"`
}
// CheckBuild CheckBuild
func (n *Notice) CheckBuild(plat int8, build int64, clientType string) bool {
if plat == PlatWeb {
return true
}
//如果公告的clientType不为空说明是针对特定的客户端类型需要特别检查一下
if n.ClientType != "" && n.ClientType != clientType {
return false
}
// PC OR GT OR LT OR EQUAL
if (build >= n.Build && n.Condition == GT) || (build <= n.Build && n.Condition == LT) || (build == n.Build && n.Condition == EQ) {
return true
}
return false
}

View File

@@ -0,0 +1,34 @@
package reply
const (
// AttrNo attribute no
AttrNo = uint32(0)
// AttrYes attribute yes
AttrYes = uint32(1)
)
// PageParams reply page param.
type PageParams struct {
Mid int64
Oid int64
Type int8
Sort int8
PageNum int
PageSize int
NeedHot bool
NeedSecond bool
Escape bool
}
// PageResult reply page result.
type PageResult struct {
Subject *Subject
TopAdmin *Reply
TopUpper *Reply
Roots []*Reply
Hots []*Reply
Total int
AllCount int
}

View File

@@ -0,0 +1,41 @@
package reply
import (
accmdl "go-common/app/service/main/account/model"
"go-common/library/xstr"
)
// time layout
const (
RecordTimeLayout = "2006-01-02 15:04:05"
)
// Record reply record.
type Record struct {
RpID int64 `json:"id"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Floor int32 `json:"floor"`
Like int32 `json:"like"`
RCount int32 `json:"rcount"`
Mid int64 `json:"mid"`
State int32 `json:"state"`
Message string `json:"message"`
CTime string `json:"ctime"`
Ats string `json:"ats,omitempty"`
Members []*Info `json:"members"`
}
// FillAts fill member info of ats.
func (rc *Record) FillAts(cards map[int64]*accmdl.Card) {
rc.Members = make([]*Info, 0, len(rc.Ats))
ats, _ := xstr.SplitInts(rc.Ats)
for _, at := range ats {
if card, ok := cards[at]; ok {
i := &Info{}
i.FromCard(card)
rc.Members = append(rc.Members, i)
}
}
rc.Ats = ""
}

View File

@@ -0,0 +1,633 @@
package reply
import (
"database/sql/driver"
"strconv"
"strings"
"text/template"
accmdl "go-common/app/service/main/account/api"
"go-common/library/ecode"
xtime "go-common/library/time"
)
// subtype
const (
Rule = "https://www.bilibili.com/blackboard/foldingreply.html"
UserLevelFirst = 1
UserLevelSnd = 2
SubTypeArchive = int8(1)
SubTypeTopic = int8(2)
SubTypeDrawyoo = int8(3)
SubTypeActivity = int8(4)
SubTypeLive = int8(5)
SubTypeForbiden = int8(6) // reply forbiden info
SubTypeNotice = int8(7) //reply notice info
SubTypeLiveAct = int8(8)
SubTypeActArc = int8(9)
SubTypeLiveNotice = int8(10)
SubTypeLivePicture = int8(11) // 文画
SubTypeArticle = int8(12) // 文章
SubTypeTicket = int8(13) // 票务
SubTypeMusic = int8(14) // 音乐
SubTypeCredit = int8(15) // 风纪委
SubTypePgcCmt = int8(16) // pgc点评
SubTypeDynamic = int8(17) // 庐山动态
SubTypePlaylist = int8(18) // 播单
SubTypeMusicList = int8(19) // 音乐播单
SubTypeComicSeason = int8(20) // 漫画部评论
SubTypeComicEpisode = int8(21) // 漫画话评论
SubTypeHuoniao = int8(22) // 火鸟
SubTypeBBQ = int8(23) // BBQ
SubTypePGC = int8(24) // PGC 全网落地页
SubTypeGame = int8(25) // 赛事库电竞
SubTypeMedialist = int8(26) // 播单(收藏夹)
SubTypeEsportsInfo = int8(27) // 电竞项目比赛数据页
SubStateNormal = int8(0)
SubStateForbid = int8(1)
// Sub attr bit
SubAttrAdminTop = uint32(0)
SubAttrUpperTop = uint32(1)
SubAttrMonitor = uint32(2)
SubAttrConfig = uint32(3)
SubAttrAudit = uint32(4)
SubAttrFrozen = uint32(5)
// 标识有被折叠的根评论
SubAttrFolded = uint32(7)
ReplyStateNormal = int8(0) // normal
ReplyStateHidden = int8(1) // hidden by up
ReplyStateFiltered = int8(2) // filtered
ReplyStateAdminDel = int8(3) // delete by admin
ReplyStateUserDel = int8(4) // delete by user
ReplyStateMonitor = int8(5) // reply after audit
ReplyStateGarbage = int8(6) // spam reply
ReplyStateTop = int8(7) // top
ReplyStateUpDel = int8(8) // delete by up
ReplyStateBlacklist = int8(9) // in a blacklist
ReplyStateAssistDel = int8(10) // delete by assistant
ReplyStateAudit = int8(11) // 监管中
ReplyStateFolded = int8(12) // 被折叠
// reply attr bit
ReplyAttrAdminTop = uint32(0)
ReplyAttrUpperTop = uint32(1)
ReplyAttrGarbage = uint32(2)
ReplyAttrFilter = uint32(3)
// 标识有被折叠的子评论
ReplyAttrFolded = uint32(7)
SortByFloor = int8(0)
SortByCount = int8(1)
SortByLike = int8(2)
ReportStateNew = int8(0) // 待一审
ReportStateDelete = int8(1) // 移除,已废弃
ReportStateIgnore = int8(2) // 忽略,已废弃
ReportStateDeleteOne = int8(3) // 一审移除
ReportStateNewTwo = int8(4) // 待二审
ReportStateDeleteTwo = int8(5) // 二审移除
ReportStateIgnoreTwo = int8(6) // 二审忽略
ReportStateIgnoreOne = int8(7) // 一审忽略
ReportStateTransferred = int8(8) // 举报转移风纪委
ReportAttrTransferred = uint(0) //标识举报State是否曾今从待一审\二审转换成待二审\一审
ReportUserStateNew = int8(0) // 新增
ReportUserStateReported = int8(1) // 已反馈
OpCancel = int8(0) // 取消赞踩
OpAdd = int8(1) // 添加赞踩
ActionNormal = int8(0) // 未踩赞
ActionLike = int8(1) // 赞
ActionHate = int8(2) // 踩
UpperOpHide = int8(1)
UpperOpShow = int8(2)
ReportReasonAd = int8(1) // 广告
ReportReasonPorn = int8(2) // 色情
ReportReasonMeaningless = int8(3) // 刷屏
ReportReasonProvoke = int8(4) // 引战
ReportReasonSpoiler = int8(5) // 剧透
ReportReasonPolitic = int8(6) // 政治
ReportReasonAttack = int8(7) // 人身攻击
ReportReasonUnrelated = int8(8) // 视频不相关
ReportReasonProhibited = int8(9) // 违禁
ReportReasonVulgar = int8(10) // 低俗
ReportReasonIllegalWebsite = int8(11) // 非法网站
ReportReasonGamblingFraud = int8(12) // 赌博诈骗
ReportReasonRumor = int8(13) // 传播不实信息
ReportReasonAbetting = int8(14) // 怂恿教唆信息
ReportReasonPrivacyInvasion = int8(15) // 侵犯隐私
ReportReasonUnlimitedSign = int8(16) // 抢楼
ReportReasonYouthInappropriate = int8(17) // 青少年不良信息
ReportReasonOther = int8(0) // 其他
PlatUnknow = int8(0)
PlatWeb = int8(1)
PlatAndroid = int8(2)
PlatIPhone = int8(3)
PlatWpM = int8(4) // wp mobile
PlatIPad = int8(5)
PlatPadHd = int8(6) // ipad hd
PlatWpPc = int8(7) // win10
PlatAndroidI = int8(8) // 国际版安卓
AdminOperDelete = int8(0) // admin delete
AdminOperDeleteByReport = int8(1) // admin delete by report
AdminOperIgnoreReport = int8(2) // admin ignore report
AdminOperRecover = int8(3) // admin recover
AdminOperEdit = int8(4) // admin edit reply content
AdminOperPass = int8(5) // admin pass
AdminOperSubState = int8(6) // admin change subject state
AdminOperSubTop = int8(7) // top reply
AdminOperSubMid = int8(8) // admin change subject mid
AdminOperRptIgnore1 = int8(9) // admin report ignore 1
AdminOperRptIgnore2 = int8(10) // admin report ignore 2
AdminOperRptDel1 = int8(11) // admin report del 1
AdminOperRptDel2 = int8(12) // admin report del 2
AdminOperRptRecover1 = int8(13) // admin report recover 1
AdminOperRptRecover2 = int8(14) // admin report recover 2
AdminOperActionSet = int8(15) // admin action set
AdminIsNotReport = int8(0)
AdminIsReport = int8(1)
AdminIsNotNew = int8(0)
AdminIsNew = int8(1)
AuditTypeOne = int8(1) // 一审
AuditTypeTwo = int8(2) // 二审
BlacklistRelation = int16(-1)
ReportReplyAdd = "reply_add"
ReportReplyDel = "reply_del"
ReportReplyLike = "reply_like"
ReportReplyHate = "reply_hate"
ReportReplyCancelLike = "reply_cancel_like"
ReportReplyCancelHate = "reply_cancel_hate"
ReportReplyTop = "reply_top"
ReportReplyUntop = "reply_untop"
ReportReplyReport = "reply_report"
)
// ActionCount ActionCount
type ActionCount struct {
Like int32 `json:"like"`
Hate int32 `json:"hate"`
}
// Subject ReplySubject
type Subject struct {
ID int64 `json:"-"`
Oid int64 `json:"oid"`
Type int8 `json:"type"`
Mid int64 `json:"mid"`
Count int `json:"count"`
RCount int `json:"rcount"`
ACount int `json:"acount"`
State int8 `json:"state"`
Attr uint32 `json:"attr"`
Meta string `json:"meta"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"-"`
}
// AttrVal return val of subject'attr.
func (s *Subject) AttrVal(bit uint32) uint32 {
return (s.Attr >> bit) & uint32(1)
}
// Folder ...
func (s *Subject) Folder() (f Folder) {
f.HasFolded = s.HasFolded()
f.Rule = Rule
return
}
// HasFolded ...
func (s *Subject) HasFolded() bool {
return s.AttrVal(SubAttrFolded) == AttrYes
}
// SubjectMeta SubjectMeta
type SubjectMeta struct {
AdminTop int64 `json:"atop,omitempty"`
UpperTop int64 `json:"utop,omitempty"`
}
// IsNormal IsNormal
func (s *Subject) IsNormal() bool {
return s.State == SubStateNormal
}
// LegalSubjectType LegalSubjectType
func LegalSubjectType(tp int8) bool {
return SubTypeArchive <= tp && tp <= SubTypeEsportsInfo
}
// CheckSubForbid CheckSubForbid
func CheckSubForbid(state int8) (err error) {
if state == SubStateForbid {
err = ecode.ReplyForbidReply
}
return
}
// CheckSubState CheckSubState
func CheckSubState(state int8) (err error) {
if state < SubStateNormal || state > SubStateForbid {
err = ecode.ReplyIllegalSubState
}
return
}
// Counts ReplyCounts
type Counts struct {
SubjectState int8 `json:"sub_state"`
Counts int64 `json:"count"`
}
// Reply Reply
type Reply struct {
RpID int64 `json:"rpid"`
Oid int64 `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,omitempty"`
State int8 `json:"state"`
FansGrade int8 `json:"fansgrade"`
Attr uint32 `json:"attr"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"-"`
// string
RpIDStr string `json:"rpid_str,omitempty"`
RootStr string `json:"root_str,omitempty"`
ParentStr string `json:"parent_str,omitempty"`
DialogStr string `json:"dialog_str",omitempty`
// action count, from ReplyAction count
Like int `json:"like"`
Hate int `json:"-"`
Action int8 `json:"action"`
// member info
Member *Member `json:"member"`
// other
Content *Content `json:"content"`
Replies []*Reply `json:"replies"`
Assist int `json:"assist"`
// 是否有折叠评论
Folder Folder `json:"folder"`
}
type Folder struct {
HasFolded bool `json:"has_folded"`
IsFolded bool `json:"is_folded"`
Rule string `json:"rule"`
}
// FillFolder ...
func (r *Reply) FillFolder() {
if r.IsRoot() {
r.Folder.HasFolded = r.HasFolded()
r.Folder.IsFolded = r.IsFolded()
r.Folder.Rule = Rule
}
}
// HasFolded ...
func (r *Reply) HasFolded() bool {
return r.AttrVal(ReplyAttrFolded) == AttrYes
}
// IsFolded ...
func (r *Reply) IsFolded() bool {
return r.State == ReplyStateFolded
}
// UnmarkHasFolded ...
func (r *Reply) UnmarkHasFolded() {
r.AttrSet(AttrNo, ReplyAttrFolded)
}
// AttrSet set attr of reply'attr
func (r *Reply) AttrSet(v uint32, bit uint32) {
r.Attr = r.Attr&(^(1 << bit)) | (v << bit)
}
// AttrVal return val of reply'attr
func (r *Reply) AttrVal(bit uint32) uint32 {
if r.Attr == 0 {
return uint32(0)
}
return (r.Attr >> bit) & uint32(1)
}
// IsRoot IsRoot
func (r *Reply) IsRoot() bool {
return r.Root == 0 && r.Parent == 0
}
// IsTop IsTop
func (r *Reply) IsTop() bool {
return r.AttrVal(ReplyAttrAdminTop) == AttrYes || r.AttrVal(ReplyAttrUpperTop) == AttrYes
}
// IsNormal IsNormal
func (r *Reply) IsNormal() bool {
return r.State == ReplyStateNormal || r.State == ReplyStateHidden || r.State == ReplyStateFiltered || r.State == ReplyStateGarbage || r.State == ReplyStateMonitor || r.State == ReplyStateTop || r.State == ReplyStateFolded
}
// IsDeleted IsDeleted
func (r *Reply) IsDeleted() bool {
return r.State == ReplyStateUserDel || r.State == ReplyStateUpDel || r.State == ReplyStateAdminDel || r.State == ReplyStateAssistDel
}
// FillStr FillStr
func (r *Reply) FillStr(isEscape bool) {
r.RpIDStr = strconv.FormatInt(r.RpID, 10)
r.RootStr = strconv.FormatInt(r.Root, 10)
r.ParentStr = strconv.FormatInt(r.Parent, 10)
if r.Content != nil {
if isEscape {
r.Content.Message = template.HTMLEscapeString(r.Content.Message)
}
r.Content.IP = 0
r.Content.Version = ""
r.Content.Members = []*Info{}
}
}
// Clone clone a reply content.
func (r *Reply) Clone() (res *Reply) {
content := new(Content)
if r.Content != nil {
*content = *(r.Content)
}
res = new(Reply)
*res = *r
res.Content = content
return
}
// WithinSortRange WithinSortRange
func WithinSortRange(sort int8) bool {
return SortByFloor <= sort && sort <= SortByLike
}
// CheckSort WithinSortRange
func CheckSort(sort int8) bool {
return SortByFloor <= sort && sort <= SortByLike
}
// CheckPlat CheckPlat
func CheckPlat(plat int8) bool {
return PlatUnknow <= plat && plat <= PlatWpPc
}
// Content ReplyContent
type Content struct {
RpID int64 `json:"-"`
Message string `json:"message"`
Ats Int64Bytes `json:"ats,omitempty"`
Topics Mstr `json:"topics,omitempty"`
IP uint32 `json:"ipi,omitempty"`
Plat int8 `json:"plat"`
Device string `json:"device"`
Version string `json:"version,omitempty"`
CTime xtime.Time `json:"-"`
MTime xtime.Time `json:"-"`
// ats member info
Members []*Info `json:"members"`
}
// FillAts FillAts
func (rc *Content) FillAts(cards map[int64]*accmdl.Card) {
rc.Members = make([]*Info, 0, len(rc.Ats))
for _, at := range rc.Ats {
if card, ok := cards[at]; ok {
i := &Info{}
i.FromCard(card)
rc.Members = append(rc.Members, i)
}
}
rc.Ats = nil
}
// Action Action
type Action struct {
ID int64 `json:"-"`
RpID int64 `json:"rpid"`
Action int8 `json:"action"`
Mid int64 `json:"mid"`
CTime xtime.Time `json:"-"`
}
// CheckAction CheckAction
func CheckAction(act int8) (err error) {
if act != OpAdd && act != OpCancel {
err = ecode.ReplyIllegalAction
}
return
}
// Report Report
type Report struct {
ID int64 `json:"id"`
RpID int64 `json:"rpid"`
Oid int64 `json:"oid"`
Type int8 `json:"type"`
Mid int64 `json:"mid"`
Reason int8 `json:"reason"`
Content string `json:"content"`
Count int `json:"count"`
Score int `json:"score"`
State int8 `json:"state"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"-"`
Attr int8 `json:"attr"`
}
// AttrVal AttrVal
func (rpt Report) AttrVal(bit uint) int8 {
return (rpt.Attr >> bit) & int8(1)
}
// IsTransffered IsTransffered
func (rpt Report) IsTransffered() bool {
return rpt.AttrVal(ReportAttrTransferred) == 1
}
// ReportUser report user.
type ReportUser struct {
ID int64 `json:"id"`
Oid int64 `json:"oid"`
Type int8 `json:"type"`
RpID int64 `json:"rpid"`
Mid int64 `json:"mid"`
Reason int8 `json:"reason"`
Content string `json:"content"`
State int8 `json:"state"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// CheckReportReason CheckReportReason
func CheckReportReason(reason int8) (err error) {
if !(ReportReasonOther <= reason && reason <= ReportReasonYouthInappropriate) {
err = ecode.ReplyIllegalReport
}
return
}
// GetReportType GetReportType
func GetReportType(reason int8) int8 {
if (reason >= ReportReasonMeaningless && reason <= ReportReasonSpoiler) || reason == ReportReasonUnrelated || reason == ReportReasonOther {
return ReportStateNewTwo
}
return ReportStateNew
}
// Mstr Mstr
type Mstr []string
// Scan Scan
func (ms *Mstr) Scan(src interface{}) (err error) {
switch sc := src.(type) {
case []byte:
if len(sc) == 0 {
return
}
res := strings.Split(string(sc), ",")
for i := range res {
res[i] = strings.Replace(res[i], "%2c", ",", -1)
}
*ms = res
}
return
}
// Value Value
func (ms Mstr) Value() (driver.Value, error) {
return ms.Bytes(), nil
}
// Bytes Bytes
func (ms Mstr) Bytes() []byte {
var res string
for i := range ms {
str := strings.Replace(ms[i], ",", "%2c", -1)
res += str
if i != (len(ms) - 1) {
res += ","
}
}
return []byte(res)
}
// 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 accmdl.PendantInfo `json:"pendant"`
Nameplate accmdl.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"`
}
// FromCard FromCard
func (i *Info) FromCard(c *accmdl.Card) {
i.Mid = strconv.FormatInt(c.Mid, 10)
i.Name = c.Name
i.Sex = c.Sex
i.Sign = c.Sign
i.Avatar = c.Face
i.Rank = strconv.FormatInt(int64(c.Rank), 10)
i.DisplayRank = "0"
i.LevelInfo.Cur = int(c.Level)
i.Pendant = c.Pendant
i.Nameplate = c.Nameplate
if c.Official.Role == 0 {
i.OfficialVerify.Type = -1
i.OfficialVerify.Desc = ""
} else {
if c.Official.Role <= 2 {
i.OfficialVerify.Type = 0
i.OfficialVerify.Desc = c.Official.Title
} else {
i.OfficialVerify.Type = 1
i.OfficialVerify.Desc = c.Official.Title
}
}
i.Vip.Type = int(c.Vip.Type)
i.Vip.VipStatus = int(c.Vip.Status)
i.Vip.DueDate = c.Vip.DueDate
}
// Business Business
type Business struct {
Type int32 `json:"type"`
Alias string `json:"alias"`
Appkey string `json:"appkey"`
}
type DialogCursor struct {
MinFloor int `json:"min_floor"`
MaxFloor int `json:"max_floor"`
Size int `json:"size"`
}
type DialogMeta struct {
MinFloor int `json:"min_floor"`
MaxFloor int `json:"max_floor"`
}
// ShouldShowFolded ...
func ShouldShowFolded(mobi_app string, build, scene int64) bool {
if mobi_app == "android" && build < 5365000 {
return true
}
if mobi_app == "iphone" && build < 8310 {
return true
}
if scene == 1 {
return true
}
return false
}

View File

@@ -0,0 +1,31 @@
package reply
// AscFloors floors sort.
type AscFloors []int64
func (f AscFloors) Len() int {
return len(f)
}
func (f AscFloors) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func (f AscFloors) Less(i, j int) bool {
return f[i] < f[j]
}
// DescFloors floors sort.
type DescFloors []int64
func (f DescFloors) Len() int {
return len(f)
}
func (f DescFloors) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func (f DescFloors) Less(i, j int) bool {
return f[i] > f[j]
}

View File

@@ -0,0 +1,64 @@
package reply
import (
"database/sql/driver"
"encoding/binary"
)
// Int64Bytes implements the Scanner interface.
type Int64Bytes []int64
// Scan parse the data into int64 slice
func (is *Int64Bytes) Scan(src interface{}) (err error) {
switch sc := src.(type) {
case []byte:
var res []int64
for i := 0; i < len(sc) && i+8 <= len(sc); i += 8 {
ui := binary.BigEndian.Uint64(sc[i : i+8])
res = append(res, int64(ui))
}
*is = res
}
return
}
// Value marshal int64 slice to driver.Value,each int64 will occupy Fixed 8 bytes
func (is Int64Bytes) Value() (driver.Value, error) {
return is.Bytes(), nil
}
// Bytes marshal int64 slice to bytes,each int64 will occupy Fixed 8 bytes
func (is Int64Bytes) Bytes() []byte {
res := make([]byte, 0, 8*len(is))
for _, i := range is {
bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, uint64(i))
res = append(res, bs...)
}
return res
}
// Evict get rid of the sepcified num from the slice
func (is *Int64Bytes) Evict(e int64) (ok bool) {
res := make([]int64, len(*is)-1)
for _, v := range *is {
if v != e {
res = append(res, v)
} else {
ok = true
}
}
*is = res
return
}
// Exist judge the sepcified num is in the slice or not
func (is Int64Bytes) Exist(i int64) (e bool) {
for _, v := range is {
if v == i {
e = true
return
}
}
return
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["topic.go"],
importpath = "go-common/app/interface/main/reply/model/topic",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,9 @@
package topic
// Topic Topic
type Topic struct {
TpID int64 `json:"tp_id"`
Title string `json:"title"`
Mid int64 `json:"mid"`
Cover string `json:"cover"`
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["emojis.go"],
importpath = "go-common/app/interface/main/reply/model/vip",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,19 @@
package vip
// Emoji Emoji
type Emoji struct {
Pid int64 `json:"pid"`
Pname string `json:"pname"`
Pstate int8 `json:"pstate"`
Purl string `json:"purl"`
Emojis []*Face `json:"emojis"`
}
// Face Face
type Face struct {
ID int64 `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
State int8 `json:"state"`
Remark string `json:"remark"`
}

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