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,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/reply/cmd:all-srcs",
"//app/admin/main/reply/conf:all-srcs",
"//app/admin/main/reply/dao:all-srcs",
"//app/admin/main/reply/http:all-srcs",
"//app/admin/main/reply/model:all-srcs",
"//app/admin/main/reply/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,314 @@
#### reply-admin
#### Version 4.0.2
> 1.不允许减赞
#### Version 4.0.1
> 1.fix fanout
#### Version 4.0.0
> 1.折叠评论
#### Version 3.0.22
> 1.account grpc
#### Version 3.0.21
> 1. remove user action table
#### Version 3.0.20
> 1.thumbup grpc
#### Version 3.0.19
> 1.del reply pubevent
#### Version 3.0.18
> 1.置顶子评论
#### Version 3.0.17
> 1.特殊删除逻辑
#### Version 3.0.16
> 1.精确搜索排序, 特定稿件下不允许删评论
#### Version 3.0.15
> 1.特殊推送逻辑
#### Version 3.0.14
> 1.新增reply_recover,report_del, reply_del, reply_top, reply_untop的消息
#### Version 3.0.13
> 1.fix report log
#### Version 3.0.12
> 1.fix report log
#### Version 3.0.11
> 1.fix reply list bug
#### Version 3.0.8
> 1.add rid del callback
#### Version 3.0.5
> 1.fix del report
#### Version 3.0.3
> 1.reply workflow
#### Version 3.0.0
> 1.reply dialog, 5.33
#### Version 2.4.22
> 1.context update
#### Version 2.4.20
> 1.返回粉丝相关信息
#### Version 2.4.19
> 1.fix update report
#### Version 2.4.16
> 1.删除热评的index
#### Version 2.4.16
> 1.retag错了
#### Version 2.4.15
> 1.oid str
#### Version 2.4.14
> 1.hot fix for ip format
#### Version 2.4.13
> 1.hot fix for search
#### Version 2.4.10
> 1.修复动态ID
#### Version 2.4.9
> 1.fix ecode
#### Version 2.4.8
> 1.oid str
#### Version 2.4.7
> 1.oid str
#### Version 2.4.6
> 1.log.Warn
#### Version 2.4.5
> 1.增加feature flag
#### Version 2.4.3
> 1.去掉remote ip改为context metadata
#### Version 2.4.2
> 1.reply_monitor搜索迁移sdk
#### Version 2.4.1
> 1.评论支持导出csv
#### Version 2.3.6
> 1.change identify to grpc
#### Version 2.3.5
> 1.配置文件
> 2.xint move to model
#### Version 2.3.4
> 1.fix lint
#### Version 2.3.3
> 1.appkey改为app_key
#### Version 2.3.2
> 1.修复appkey冲突
#### Version 2.3.1
> 1.去掉子评论老缓存index
#### Version 2.3.0
> 1.双写子评论缓存index
#### Version 2.2.1
> 1.VIP表情后台管理迁移
#### Version 2.1.6
> 1.下线老Stat-T
#### Version 2.1.5
> 1.修改business的router, 修复标记为垃圾的日志
#### Version 2.1.4
> 1.修复container/pool bug
#### Version 2.1.2
> 1.删除非块级配置
#### Version 2.1.1
> 1.修复spam参数
#### Version 2.1.0
> 1.点赞新平台
#### Version 2.0.34
> 1.众裁新增日志
> 2.评论和举报搜索接口针对稿件类别返回值增加Title
> 3.新增举报删除接口中的默认举报理由
#### Version 2.0.33
> 1.平台日志修改配置删除rpc common conf
#### Version 2.0.32
> 1.平台日志换用Databus
#### Version 2.0.31
> 1.新增删除评论失败返回码
#### Version 2.0.30
> 1.新增business
#### Version 2.0.29
> 1.修改封禁理由内容
#### Version 2.0.28
> 1.修复EndTime问题
#### Version 2.0.27
> 1.修复举报处理通知文案
#### Version 2.0.26
> 1.增加漫画类型
#### Version 2.0.25
> 1.删除endTime为了搜索更快
#### Version 2.0.24
> 1.增加标记某评论为垃圾以及删除的接口供AI调用
#### Version 2.0.23
> 1.修复commit error
#### Version 2.0.22
> 1.修复赞踩缓存更新
#### Version 2.0.21
> 1.修复修改subject mcount可能造成的deadlock
#### Version 2.0.20
> 1.增加subject mcount
#### Version 2.0.19
> 1.调整顺序,解决死锁问题
##### Version 2.0.18
> 1.增加ecode
##### Version 2.0.17
> 1.批量修改状态添加返回errors
##### Version 2.0.16
> 1.修复修改状态没有删除缓存
##### Version 2.0.15
> 1.增加修改subject state的接口
##### Version 2.0.14
> 1.增加举报日志输出到日志平台
##### Version 2.0.13
> 1.小黑屋接口变动
##### Version 2.0.12
> 1.修复被过滤评论无法显示问题
##### Version 2.0.11
> 1.删除子评论oid index
##### Version 2.0.10
> 1.fix index
##### Version 2.0.9
> 1.兼容新子评论缓存key
##### Version 2.0.8
> 1.修复stats发送到新databus
##### Version 2.0.7
> 1.修复add redis之前没有expire的问题
##### Version 2.0.6
> 1.增加日志输出用于统计
##### Version 2.0.5
> 1.修复moniter监控不能获取admin name的问题
##### Version 2.0.4
> 1.修复评论置顶没有Del subjcet cache问题
##### Version 2.0.3
> 1.修复监控日志权限问题
##### Version 2.0.2
> 1.修复通过举报恢复评论跟新search错误的问题
##### Version 2.0.1
> 1.修复bug
##### Version 2.0.0
> 1.评论管理后台重构
##### Version 1.9.3
> 1.删除statsd
##### Version 1.9.2
> 1.添加监控打开关闭日志
##### Version 1.9.0
> 1.添加评论日志列表接口
##### Version 1.8.0
> 1.添加公告接口
##### Version 1.7.1
> 1.修复举报score参数
##### Version 1.7.0
> 1.迁移大仓库
##### Version 1.6.0
> 1.添加先审后发监控
##### Version 1.5.1
> 1.修复统计adminids
##### Version 1.5.0
> 1.添加监控统计接口
##### Version 1.4.1
> 1.修改搜索URL
##### Version 1.4.0
> 1.提供评论配置服务端功能(删除日志配置)
##### Version 1.3.2
> 1.去掉双写监控状态
##### Version 1.3.1
> 1.docker tw.
##### Version 1.3.0
> 1.添加监控接口
##### Version 1.2.1
> 1.添加adminname
##### Version 1.2.0
> 1.添加评论列表搜索
> 2.添加监控列表搜索
##### Version 1.1.1
> 1.fix 审核理由为0的情况
##### Version 1.1.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:
- admin
- admin/main/reply
- main
options:
no_parent_owners: true
reviewers:
- caoguoliang
- chenzhihui

View File

@@ -0,0 +1,10 @@
#### reply-admin
##### 项目简介
> reply-admin
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common

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-admin-test.toml"],
importpath = "go-common/app/admin/main/reply/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/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,51 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/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()
// init conf,log,trace,stat,perf
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
report.InitManager(conf.Conf.ManagerReport)
// service init
http.Init(conf.Conf)
// perf init
log.Info("reply-admin start")
// signal handler
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("reply-admin get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("reply-admin exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,270 @@
[log]
stdout=true
[httpServer]
addr = "0.0.0.0:10001"
timeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
[reply]
pageNum = 1
pageSize = 30
adminName = ["yangjiankun", "xuyiming"]
[identify]
whiteAccessKey = "a2a1eb0ac97d6ba08b85aa0151528f34"
whiteMid = 23675773
csrfOn = true
csrf = true
[identify.app]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
[identify.memcache]
name = "go-business/identify"
active = 1024
idle = 64
dialTimeout = "100ms"
readTimeout = "200ms"
writeTimeout = "200ms"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.co"
secret = "http://open.bilibili.co"
[identify.httpClient]
key = "7c7ac0db1aa05587"
secret = "9a6d62d93290c5f771ad381e9ca23f26"
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 = "1s"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "1s"}
"http://open.bilibili.co/api/getsecret" = {timeout = "1s"}
[db]
[db.reply]
addr = "172.22.34.101:3306"
dsn = "test_3306:UJPZaGKjpb2ylFx3HNhmLuwOYft4MCAi@tcp(172.22.34.101:3306)/bilibili_reply?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
active = 5
idle = 1
idleTimeout ="4h"
queryTimeout = "500ms"
execTimeout = "500ms"
tranTimeout = "500ms"
[db.reply.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[db.replySlave]
addr = "172.22.34.101:3306"
dsn = "test_3306:UJPZaGKjpb2ylFx3HNhmLuwOYft4MCAi@tcp(172.22.34.101:3306)/bilibili_reply?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
active = 5
idle = 1
idleTimeout ="4h"
queryTimeout = "500ms"
execTimeout = "500ms"
tranTimeout = "500ms"
[db.replySlave.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
dial = "3s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[[stats]]
key = "0QNB0ZgFozbKUCQhbTq8"
secret = "0QNB0ZgFozbKUCQhbTq9"
group = "Stat-UGC-P"
topic = "Stat-T"
action ="pub"
name = "archive-service/stat"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
type = 1
field = "aid"
[[stats]]
key = "0QNB0ZgFozbKUCQhbTq8"
secret = "0QNB0ZgFozbKUCQhbTq9"
group = "ArticleStat-Article-P"
topic = "ArticleStat-T"
action ="pub"
name = "archive-service/stat"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
type = 12
field = "aid"
[[stats]]
key = "0QNB0ZgFozbKUCQhbTq8"
secret = "0QNB0ZgFozbKUCQhbTq9"
group= "PlaylistStat-Playlist-P"
topic= "PlaylistStat-T"
action="pub"
name = "playlist-service/stat"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 100
active = 100
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1h"
type = 18
field = "pid"
[rpcClient2]
[rpcClient2.account]
timeout = "30s"
[rpcClient2.archive]
timeout = "30s"
[rpcClient2.assist]
timeout = "30s"
[rpcClient2.article]
timeout = "30s"
[rpcClient2.thumbup]
timeout = "30s"
[databus]
[databus.event]
key = "170e302355453683"
secret = "3d0e8db7bed0503949e545a469789279"
group = "Reply-MainCommunity-P"
topic = "Reply-T"
action ="pub"
name = "reply/event"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 2
active = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1h"
[databus.stats]
key = "170e302355453683"
secret = "3d0e8db7bed0503949e545a469789279"
group = "StatReply-MainCommunity-P"
topic = "StatReply-T"
action ="pub"
name = "reply/stats"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 2
active = 5
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1h"
[host]
search = "http://uat-bili-search.bilibili.co"
[managerAuth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "manager-go"
[managerAuth.dsHTTPClient]
key = "manager-go"
secret = "949bbb2dd3178252638c2407578bc7ad"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[managerAuth.dsHTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[managerAuth.maHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[managerAuth.maHTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[managerAuth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[managerAuth.session.Memcache]
name = "bm/auth"
proto = "unix"
addr = "/tmp/uat-reply-mc.sock"
active = 5
idle = 2
dialTimeout = "10ms"
readTimeout = "200ms"
writeTimeout = "200ms"
idleTimeout = "80s"
[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 = "8h"
userActExpire="1h"
reportExpire = "24h"
userCntExpire = "30s"
notifyExpire = "24h"
expire = "24h"

View File

@@ -0,0 +1,46 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/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/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit: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,164 @@
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"
bm "go-common/library/net/http/blademaster"
bauth "go-common/library/net/http/blademaster/middleware/permit"
"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"
)
var (
// ConfPath config toml file path for reply admin project
ConfPath string
// Conf global config instance
Conf *Config
)
// Config config.
type Config struct {
// base
// ecode
Ecode *ecode.Config
// log
Log *log.Config
// tracer
Tracer *trace.Config
// verify
Verify *verify.Config
// http
HTTPServer *bm.ServerConfig
// db
DB *DB
// memcache
Memcache *Memcache
// Redis
Redis *Redis
// http client
HTTPClient *bm.ClientConfig
DrawyooHTTPClient *bm.ClientConfig
// reply
Reply *Reply
// host
Host *Host
// Stats
StatTypes map[string]int32
Weight *Weight
RPCClient2 *RPCClient2
Databus *Databus
ManagerAuth *bauth.Config
ManagerReport *databus.Config
Es *elastic.Config
ThumbupClient *warden.ClientConfig
AccountClient *warden.ClientConfig
}
// Databus databus.
type Databus struct {
Event *databus.Config
Stats *databus.Config
}
// RPCClient2 rpc client.
type RPCClient2 struct {
Account *rpc.ClientConfig
Archive *rpc.ClientConfig
Article *rpc.ClientConfig
Assist *rpc.ClientConfig
Thumbup *rpc.ClientConfig
Relation *rpc.ClientConfig
}
// Weight weight.
type Weight struct {
Like int32
Hate int32
}
// Stats stats.
type Stats struct {
*databus.Config
Type int32
Field string
}
// Redis redis.
type Redis struct {
*redis.Config
Expire time.Duration
}
// Reply reply.
type Reply struct {
PageNum int
PageSize int
LikeWeight int32
HateWeight int32
// 大忽悠事件删除评论的管理员
AdminName []string
// 针对大忽悠的跳转链接
Link string
// 针对大忽悠时间的特殊稿件
Oids []int64
Tps []int32
}
// Host host.
type Host struct {
Search string
}
// DB db.
type DB struct {
Reply *sql.Config
ReplySlave *sql.Config
}
// Memcache memcache.
type Memcache struct {
*memcache.Config
Expire time.Duration
}
func init() {
flag.StringVar(&ConfPath, "conf", "", "config path")
}
// Init inti config.
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,109 @@
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_test.go",
"business_test.go",
"config_test.go",
"dao_test.go",
"emoji_package_test.go",
"emoji_test.go",
"event_test.go",
"fold_test.go",
"http_filter_test.go",
"http_search_test.go",
"http_title_test.go",
"logReport_test.go",
"memcache_test.go",
"message_test.go",
"notice_test.go",
"redis_test.go",
"reply_content_test.go",
"reply_test.go",
"report_test.go",
"search_test.go",
"stat_test.go",
"subject_test.go",
"workflow_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/main/reply/model:go_default_library",
"//library/conf/env:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/go-sql-driver/mysql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"admin.go",
"block.go",
"business.go",
"config.go",
"dao.go",
"emoji.go",
"emoji_package.go",
"event.go",
"fold.go",
"http_filter.go",
"http_search.go",
"http_title.go",
"logReport.go",
"memcache.go",
"message.go",
"notice.go",
"redis.go",
"reply.go",
"reply_content.go",
"report.go",
"stat.go",
"subject.go",
"workflow.go",
],
importpath = "go-common/app/admin/main/reply/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/main/reply/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/elastic: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/queue/databus:go_default_library",
"//library/sync/errgroup: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,113 @@
package dao
import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
"time"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_adminNameURL = "http://manager.bilibili.co/x/admin/manager/users/unames"
_selAdminLogByRpIDSQL = "SELECT id,oid,type,rpid,adminid,result,remark,isnew,isreport,state,ctime,mtime FROM reply_admin_log WHERE rpid=? and isnew=1"
_selAdminLogsByRpIDSQL = "SELECT id,oid,type,rpid,adminid,result,remark,isnew,isreport,state,ctime,mtime FROM reply_admin_log WHERE rpid=? order by ctime desc"
_upAdminLogSQL = "UPDATE reply_admin_log SET isnew=0,mtime=? WHERE rpID IN(%s) AND isnew=1"
_addAdminLogSQL = "INSERT INTO reply_admin_log (oid,type,rpid,adminid,result,remark,isnew,isreport,state,ctime,mtime) VALUES %s"
_addAdminLogFormat = `(%d,%d,%d,%d,'%s','%s',%d,%d,%d,'%s','%s')`
_logTimeLayout = "2006-01-02 15:04:05"
)
// AddAdminLog add admin log to mysql.
func (d *Dao) AddAdminLog(c context.Context, oids, rpIDs []int64, adminID int64, typ, isNew, isReport, state int32, result, remark string, now time.Time) (rows int64, err error) {
var vals []string
for i := range oids {
vals = append(vals, fmt.Sprintf(_addAdminLogFormat, oids[i], typ, rpIDs[i], adminID, result, remark, isNew, isReport, state, now.Format(_logTimeLayout), now.Format(_logTimeLayout)))
}
insertSQL := strings.Join(vals, ",")
res, err := d.db.Exec(c, fmt.Sprintf(_addAdminLogSQL, insertSQL))
if err != nil {
return
}
return res.RowsAffected()
}
// UpAdminNotNew update admin log to not new.
func (d *Dao) UpAdminNotNew(c context.Context, rpID []int64, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_upAdminLogSQL, xstr.JoinInts(rpID)), now)
if err != nil {
return
}
return res.RowsAffected()
}
// AdminLog return admin log by rpid
func (d *Dao) AdminLog(c context.Context, rpID int64) (r *model.AdminLog, err error) {
row := d.db.QueryRow(c, _selAdminLogByRpIDSQL, rpID)
r = new(model.AdminLog)
if err = row.Scan(&r.ID, &r.Oid, &r.Type, &r.ReplyID, &r.AdminID, &r.Result, &r.Remark, &r.IsNew, &r.IsReport, &r.State, &r.CTime, &r.MTime); err != nil {
return
}
return
}
// AdminLogsByRpID return operation log list.
func (d *Dao) AdminLogsByRpID(c context.Context, rpID int64) (res []*model.AdminLog, err error) {
rows, err := d.db.Query(c, _selAdminLogsByRpIDSQL, rpID)
if err != nil {
log.Error("query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
r := new(model.AdminLog)
if err = rows.Scan(&r.ID, &r.Oid, &r.Type, &r.ReplyID, &r.AdminID, &r.Result, &r.Remark, &r.IsNew, &r.IsReport, &r.State, &r.CTime, &r.MTime); err != nil {
return
}
res = append(res, r)
}
err = rows.Err()
return
}
// AdminName get admin name by id.
func (d *Dao) AdminName(c context.Context, admins map[int64]string) (err error) {
if len(admins) == 0 {
return
}
var res struct {
Code int `json:"code"`
Data map[string]string `json:"data"`
}
var uids []int64
for key := range admins {
uids = append(uids, key)
}
params := url.Values{}
params.Set("uids", xstr.JoinInts(uids))
if err = d.httpClient.Get(c, _adminNameURL, "", params, &res); err != nil {
log.Error("ReportLog httpClient.Get(%s) error(%v) res(%v)", _adminNameURL, err, res)
return
}
if ec := ecode.Int(res.Code); !ecode.OK.Equal(ec) {
log.Error("ReportLog not ok(%s) error(%v) res(%v)", _adminNameURL, ec, res)
err = ec
return
}
for k, v := range res.Data {
id, err := strconv.ParseInt(k, 10, 64)
if err == nil {
admins[id] = v
} else {
log.Error("ReportLog(%s) strconv error(%v) res(%v)", _adminNameURL, err, res)
}
}
return
}

View File

@@ -0,0 +1,34 @@
package dao
import (
"context"
"testing"
"time"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestAdminLog(t *testing.T) {
var (
oids = []int64{1, 2, 3}
rpIDs = []int64{10, 20, 30}
adminID = int64(100)
typ = int32(1)
c = context.Background()
now = time.Now()
)
Convey("add admin log", t, WithDao(func(d *Dao) {
rows, err := d.AddAdminLog(c, oids, rpIDs, adminID, typ, model.AdminIsNew, model.AdminIsReport, model.AdminOperDelete, "result", "remark", now)
So(err, ShouldBeNil)
So(rows, ShouldNotEqual, 0)
t.Log(rows)
rows, err = d.UpAdminNotNew(c, rpIDs, now)
So(err, ShouldBeNil)
So(rows, ShouldNotEqual, 0)
res, err := d.AdminLogsByRpID(c, rpIDs[0])
So(err, ShouldBeNil)
So(len(res), ShouldNotEqual, 0)
}))
}

View File

@@ -0,0 +1,117 @@
package dao
import (
"context"
"encoding/json"
"net/url"
"strconv"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_blockURL string = "http://account.bilibili.co/api/member/blockAccountWithTime"
_transferURL string = "http://api.bilibili.co/x/internal/credit/blocked/case/add"
)
// BlockAccount ban an account.
func (d *Dao) BlockAccount(c context.Context, mid int64, ftime int64, notify bool, freason int32, originTitle string, originContent string, redirectURL string, adname string, remark string) (err error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
if ftime == -1 {
params.Set("blockTimeLength", "0")
params.Set("blockForever", "1")
} else {
params.Set("blockForever", "0")
params.Set("blockTimeLength", strconv.FormatInt(ftime, 10))
}
params.Set("blockRemark", remark)
params.Set("operator", adname)
params.Set("originType", "1")
params.Set("originContent", originContent)
params.Set("reasonType", strconv.FormatInt(int64(freason), 10))
if notify {
params.Set("isNotify", "1")
} else {
params.Set("isNotify", "0")
}
params.Set("originTitle", originTitle)
params.Set("originUrl", redirectURL)
var res struct {
Code int `json:"code"`
}
if err = d.httpClient.Post(c, _blockURL, "", params, &res); err != nil {
log.Error("sendMsg error(%v)", err)
return
}
if res.Code != ecode.OK.Code() {
err = model.ErrMsgSend
log.Error("sendMsg failed(%v) error(%v)", _apiMsgSend+"?"+params.Encode(), res.Code)
}
return
}
// TransferData transefer data
type TransferData struct {
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Mid int64 `json:"mid"`
RpID int64 `json:"rp_id"`
Operator string `json:"operator"`
OperatorID int64 `json:"oper_id"`
Content string `json:"origin_content"`
Reason int32 `json:"reason_type"`
Title string `json:"origin_title"`
Link string `json:"origin_url"`
Ctime int64 `json:"business_time"`
OriginType int32 `json:"origin_type"`
}
// TransferArbitration transfer report to Arbitration.
func (d *Dao) TransferArbitration(c context.Context, rps map[int64]*model.Reply, rpts map[int64]*model.Report, adid int64, adname string, titles map[int64]string, links map[int64]string) (err error) {
var data []TransferData
for _, rp := range rps {
if rpts[rp.ID] == nil {
continue
}
rpt := rpts[rp.ID]
d := TransferData{
RpID: rp.ID,
Oid: rp.Oid,
Type: rp.Type,
Mid: rp.Mid,
Operator: adname,
OperatorID: adid,
Content: rp.Content.Message,
Reason: rpt.Reason,
Ctime: int64(rp.CTime),
OriginType: 1,
Title: titles[rp.ID],
Link: links[rp.ID],
}
data = append(data, d)
}
content, err := json.Marshal(data)
if err != nil {
return
}
params := url.Values{}
params.Set("data", string(content))
var res struct {
Code int `json:"code"`
}
if err = d.httpClient.Post(c, _transferURL, "", params, &res); err != nil {
log.Error("sendMsg error(%v)", err)
return
}
if res.Code != ecode.OK.Code() {
err = model.ErrMsgSend
log.Error("sendMsg failed(%v) error(%v)", _transferURL+"?"+params.Encode(), res.Code)
}
return
}

View File

@@ -0,0 +1,52 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBlockAccount(t *testing.T) {
convey.Convey("BlockAccount", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(0)
ftime = int64(0)
notify bool
freason = int32(0)
originTitle = ""
originContent = ""
redirectURL = ""
adname = ""
remark = ""
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.BlockAccount(c, mid, ftime, notify, freason, originTitle, originContent, redirectURL, adname, remark)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTransferArbitration(t *testing.T) {
convey.Convey("TransferArbitration", t, func(ctx convey.C) {
var (
c = context.Background()
rps map[int64]*model.Reply
rpts map[int64]*model.Report
adid = int64(0)
adname = ""
titles map[int64]string
links map[int64]string
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.TransferArbitration(c, rps, rpts, adid, adname, titles, links)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,76 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"go-common/library/database/sql"
)
const (
_inBussinessSQL = "INSERT INTO business (type, name, appkey, remark, alias) VALUES (?,?,?,?,?)"
_upBussinessSQL = "UPDATE business SET name=?, appkey=?, remark=?, alias=? WHERE type=?"
_upBussinessSteteSQL = "UPDATE business SET state=? WHERE type=?"
_selBussinessSQL = "SELECT type, name, appkey, remark, alias FROM business WHERE state=?"
_selOneBusinessSQL = "SELECT type, name, appkey, remark, alias FROM business WHERE type=?"
)
// InBusiness insert a business record
func (dao *Dao) InBusiness(c context.Context, tp int32, name, appkey, remark, alias string) (id int64, err error) {
res, err := dao.db.Exec(c, _inBussinessSQL, tp, name, appkey, remark, alias)
if err != nil {
return
}
return res.LastInsertId()
}
// UpBusiness update business by type
func (dao *Dao) UpBusiness(c context.Context, name, appkey, remark, alias string, tp int32) (id int64, err error) {
res, err := dao.db.Exec(c, _upBussinessSQL, name, appkey, remark, alias, tp)
if err != nil {
return
}
return res.RowsAffected()
}
// UpBusinessState logical delete a business record by set state to StateDelete
func (dao *Dao) UpBusinessState(c context.Context, state, tp int32) (id int64, err error) {
res, err := dao.db.Exec(c, _upBussinessSteteSQL, state, tp)
if err != nil {
return
}
return res.RowsAffected()
}
// Business return one business instance by type
func (dao *Dao) Business(c context.Context, tp int32) (business *model.Business, err error) {
row := dao.db.QueryRow(c, _selOneBusinessSQL, tp)
business = new(model.Business)
err = row.Scan(&business.Type, &business.Name, &business.Appkey, &business.Remark, &business.Alias)
if err != nil {
if err == sql.ErrNoRows {
err = nil
business = nil
}
}
return
}
// ListBusiness Gets gets all business records
func (dao *Dao) ListBusiness(c context.Context, state int32) (business []*model.Business, err error) {
rows, err := dao.db.Query(c, _selBussinessSQL, state)
if err != nil {
return
}
defer rows.Close()
business = make([]*model.Business, 0)
for rows.Next() {
b := new(model.Business)
if err = rows.Scan(&b.Type, &b.Name, &b.Appkey, &b.Remark, &b.Alias); err != nil {
return
}
business = append(business, b)
}
err = rows.Err()
return
}

View File

@@ -0,0 +1,81 @@
package dao
import (
"context"
"fmt"
"testing"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func delBusiness(d *Dao, id int64) error {
_delBsSQL := "Delete from business where id = %d"
_, err := d.db.Exec(context.Background(), fmt.Sprintf(_delBsSQL, id))
if err != nil {
return err
}
return nil
}
func TestBusiness(t *testing.T) {
var (
id1 int64
id2 int64
err error
bs1 = model.Business{
Type: 244,
Name: "TestName",
Appkey: "TestAppKey",
Remark: "TestRemark",
Alias: "TestAlias",
}
bs2 = model.Business{
Type: 245,
Name: "TestName",
Appkey: "TestAppKey",
Remark: "TestRemark",
Alias: "TestAlias2",
}
b []*model.Business
bu *model.Business
)
c := context.Background()
Convey("test dao business", t, WithDao(func(d *Dao) {
id1, err = d.InBusiness(c, bs1.Type, bs1.Name, bs1.Appkey, bs1.Remark, bs1.Alias)
So(err, ShouldBeNil)
So(id1, ShouldBeGreaterThan, 0)
defer delBusiness(d, id1)
id2, err = d.InBusiness(c, bs2.Type, bs2.Name, bs2.Appkey, bs2.Remark, bs2.Alias)
So(err, ShouldBeNil)
So(id2, ShouldBeGreaterThan, 0)
defer delBusiness(d, id2)
Convey("list business", WithDao(func(d *Dao) {
b, err = d.ListBusiness(c, 0)
So(err, ShouldBeNil)
So(len(b), ShouldBeGreaterThan, 0)
}))
Convey("update business", WithDao(func(d *Dao) {
bu, err = d.Business(c, 245)
So(err, ShouldBeNil)
So(bu.Name, ShouldEqual, "TestName")
_, err = d.UpBusiness(c, "TestChangeName", bu.Appkey, bu.Remark, bu.Alias, bu.Type)
So(err, ShouldBeNil)
bu, err = d.Business(c, 245)
So(err, ShouldBeNil)
So(bu.Name, ShouldEqual, "TestChangeName")
}))
Convey("update business state", WithDao(func(d *Dao) {
_, err = d.UpBusinessState(c, 1, bu.Type)
So(err, ShouldBeNil)
bu, err = d.Business(c, 245)
So(err, ShouldBeNil)
So(bu.Type, ShouldEqual, 245)
}))
}))
}

View File

@@ -0,0 +1,137 @@
package dao
import (
"bytes"
"context"
"database/sql"
"encoding/json"
"time"
"go-common/app/admin/main/reply/model"
)
const (
_addConfigSQL = "INSERT IGNORE INTO reply_config (type, oid, adminid, operator, category, config, ctime, mtime) VALUES(?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE adminid = ?, operator = ?, config=?, mtime = ?"
_paginationConfigSQL = "SELECT id, type, oid, adminid, operator, category, config, ctime, mtime FROM reply_config"
_paginationConfigCountSQL = "SELECT count(id) FROM reply_config"
_loadConfigSQL = "SELECT id, type, oid, adminid, operator, category, config, ctime, mtime FROM reply_config WHERE type=? AND oid=? AND category = ?"
_loadConfigByIDSQL = "SELECT id, type, oid, adminid, operator, category, config, ctime, mtime FROM reply_config WHERE id = ?"
_deleteConfigSQL = "DELETE FROM reply_config where id = ?"
)
// AddConfig create a new config.
func (d *Dao) AddConfig(c context.Context, typ, category int32, oid, adminid int64, operator, config string, now time.Time) (id int64, err error) {
res, err := d.db.Exec(c, _addConfigSQL, typ, oid, adminid, operator, category, config, now, now, adminid, operator, config, now)
if err != nil {
return
}
return res.LastInsertId()
}
// LoadConfig load a config record.
func (d *Dao) LoadConfig(c context.Context, typ, category int32, oid int64) (m *model.Config, err error) {
m = new(model.Config)
row := d.db.QueryRow(c, _loadConfigSQL, typ, 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
}
}
if m.ID > 0 && len(m.Config) > 0 {
dat := new(model.Config)
if err = json.Unmarshal([]byte(m.Config), dat); err == nil {
m.ShowEntry = dat.ShowEntry
m.ShowAdmin = dat.ShowAdmin
}
}
return
}
// LoadConfigByID load a config record by id.
func (d *Dao) LoadConfigByID(c context.Context, id int64) (m *model.Config, err error) {
m = new(model.Config)
row := d.db.QueryRow(c, _loadConfigByIDSQL, id)
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 {
return nil, nil
}
}
if m.ID > 0 {
dat := new(model.Config)
if err = json.Unmarshal([]byte(m.Config), dat); err == nil {
m.ShowEntry = dat.ShowEntry
m.ShowAdmin = dat.ShowAdmin
}
}
return
}
// PaginateConfig paginate config list of records indexing from start(offset) to end(offset+count) by conditions.
func (d *Dao) PaginateConfig(c context.Context, typ, category int32, oid int64, operator string, offset, count int) (configs []*model.Config, err error) {
var paginationSQLBuffer bytes.Buffer
paginationSQLBuffer.WriteString(_paginationConfigSQL)
sqlwhere, queryParams := d.constructPaginationSQLWhere(typ, category, oid, operator)
paginationSQLBuffer.WriteString(sqlwhere)
paginationSQLBuffer.WriteString(" limit ?, ?")
queryParams = append(queryParams, offset, count-1)
rows, err := d.db.Query(c, paginationSQLBuffer.String(), queryParams...)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
m := new(model.Config)
if err = rows.Scan(&m.ID, &m.Type, &m.Oid, &m.AdminID, &m.Operator, &m.Category, &m.Config, &m.CTime, &m.MTime); err != nil {
return
}
if m.ID > 0 {
dat := new(model.Config)
if err = json.Unmarshal([]byte(m.Config), dat); err == nil {
m.ShowEntry = dat.ShowEntry
m.ShowAdmin = dat.ShowAdmin
}
}
configs = append(configs, m)
}
return
}
// PaginateConfigCount returns a total count of records by conditions.
func (d *Dao) PaginateConfigCount(c context.Context, typ, category int32, oid int64, operator string) (totalCount int64, err error) {
var paginationCountSQLBuffer bytes.Buffer
paginationCountSQLBuffer.WriteString(_paginationConfigCountSQL)
sqlwhere, countParams := d.constructPaginationSQLWhere(typ, category, oid, operator)
paginationCountSQLBuffer.WriteString(sqlwhere)
row := d.db.QueryRow(c, paginationCountSQLBuffer.String(), countParams...)
if err = row.Scan(&totalCount); err != nil {
if err == sql.ErrNoRows {
err = nil
}
}
return
}
func (d *Dao) constructPaginationSQLWhere(tp, category int32, oid int64, operator string) (sqlWhere string, queryParams []interface{}) {
var sqlBuffer bytes.Buffer
sqlBuffer.WriteString(" where 1 = 1 ")
if tp > 0 && oid > 0 && category > 0 {
sqlBuffer.WriteString(" and type = ? and oid = ? and category = ?")
queryParams = append(queryParams, tp, oid, category)
}
if len(operator) > 0 {
sqlBuffer.WriteString(" and operator = ?")
queryParams = append(queryParams, operator)
}
return sqlBuffer.String(), queryParams
}
// DeleteConfig delete a reply config record by id.
func (d *Dao) DeleteConfig(c context.Context, id int64) (rows int64, err error) {
res, err := d.db.Exec(c, _deleteConfigSQL, id)
if err != nil {
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,85 @@
package dao
import (
"context"
"encoding/json"
"testing"
"time"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestAddConfig(t *testing.T) {
var (
c = context.Background()
now = time.Now()
config = &model.Config{
Oid: 1,
Type: 1,
Category: 1,
AdminID: 1,
Operator: "admin",
}
)
Convey("add a config", t, WithDao(func(d *Dao) {
configValue := map[string]int64{
"showentry": 0,
"showadmin": 1,
}
bs, err := json.Marshal(configValue)
So(err, ShouldBeNil)
config.Config = string(bs)
_, err = d.AddConfig(c, config.Type, config.Category, config.Oid, config.AdminID, config.Operator, config.Config, now)
So(err, ShouldBeNil)
}))
}
func TestLoadConfig(t *testing.T) {
var (
c = context.Background()
config = &model.Config{
Oid: 1,
Type: 1,
Category: 1,
AdminID: 1,
Operator: "admin",
}
)
Convey("load a config", t, WithDao(func(d *Dao) {
var err error
config, err = d.LoadConfig(c, config.Type, config.Category, config.Oid)
So(err, ShouldBeNil)
So(config, ShouldNotBeNil)
}))
}
func TestPaginateConfig(t *testing.T) {
var (
config = &model.Config{
Oid: 1,
Type: 1,
Category: 1,
AdminID: 1,
Operator: "admin",
}
c = context.Background()
)
Convey("load a config", t, WithDao(func(d *Dao) {
configs, err := d.PaginateConfig(c, config.Type, config.Category, config.Oid, config.Operator, 0, 20)
So(err, ShouldBeNil)
So(len(configs), ShouldNotEqual, 0)
}))
}
func TestDeleteConfig(t *testing.T) {
var (
id = int64(1)
c = context.Background()
)
Convey("load a config", t, WithDao(func(d *Dao) {
_, err := d.DeleteConfig(c, id)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,79 @@
package dao
import (
"context"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
es "go-common/library/database/elastic"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"go-common/library/queue/databus"
)
// Dao dao.
type Dao struct {
c *conf.Config
// db
db *sql.DB
dbSlave *sql.DB
// cache
redis *redis.Pool
redisExpire int32
mc *memcache.Pool
mcExpire int32
// http
httpClient *bm.Client
// databus
eventBus *databus.Databus
// new databus stats
statsBus *databus.Databus
statsTypes map[int32]string
es *es.Elastic
}
// New new a dao and return.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
// db
db: sql.NewMySQL(c.DB.Reply),
dbSlave: sql.NewMySQL(c.DB.ReplySlave),
// cache
redis: redis.NewPool(c.Redis.Config),
redisExpire: int32(time.Duration(c.Redis.Expire) / time.Second),
mc: memcache.NewPool(c.Memcache.Config),
mcExpire: int32(time.Duration(c.Memcache.Expire) / time.Second),
// http
httpClient: bm.NewClient(c.HTTPClient),
// databus
eventBus: databus.New(c.Databus.Event),
es: es.NewElastic(c.Es),
}
// new databus stats
d.statsTypes = make(map[int32]string)
for name, typ := range c.StatTypes {
d.statsTypes[typ] = name
}
d.statsBus = databus.New(c.Databus.Stats)
return
}
func hit(id int64) int64 {
return id % 200
}
// BeginTran begin transaction.
func (d *Dao) BeginTran(c context.Context) (*sql.Tx, error) {
return d.db.Begin(c)
}
// Ping ping resouces is ok.
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
return
}
return d.pingMC(c)
}

View File

@@ -0,0 +1,56 @@
package dao
import (
"flag"
"os"
"testing"
"go-common/app/admin/main/reply/conf"
"go-common/library/conf/env"
_ "github.com/go-sql-driver/mysql"
. "github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
_d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.community.reply-admin")
flag.Set("conf_token", "7c141ae4ff3f31aade1c51556fd11e8a")
flag.Set("tree_id", "2124")
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 {
env.DeployEnv = "uat"
env.Zone = "sh001"
flag.Set("conf", "../cmd/reply-admin-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
_d = New(conf.Conf)
d = _d
m.Run()
os.Exit(0)
}
func CleanCache() {
}
func WithDao(f func(d *Dao)) func() {
return func() {
Reset(func() { CleanCache() })
f(_d)
}
}

View File

@@ -0,0 +1,134 @@
package dao
import (
"context"
"strings"
"time"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
)
const (
//state 0-上线 1-下线
_selEmojiSQL = "SELECT id,package_id,name,url,sort,state,remark from emoji order by package_id,sort"
_selEmojiByPidSQL = "SELECT id,package_id,name,url,sort,state,remark from emoji where package_id=? order by sort"
_selEmojiByNameSQL = "SELECT id from emoji where name=? "
_insertEmojiSQL = "INSERT INTO emoji (package_id, name, url, sort, state, remark, ctime, mtime ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
_upEmojiSortSQL = "UPDATE emoji SET sort=?, mtime=? where id=?"
_upEmojiStateSQL = "UPDATE emoji SET state=?, mtime=? WHERE id=?"
_upEmojiSQL = "UPDATE emoji SET name=?, remark=?, url=?, mtime=? WHERE id=?"
_delEmojiByIDSQL = "DELETE from emoji where id=?"
_delEmojiByPidSQL = "DELETE from emoji where package_id=?"
)
// EmojiList get all emoji
func (d *Dao) EmojiList(c context.Context) (emojis []*model.Emoji, err error) {
rows, err := d.db.Query(c, _selEmojiSQL)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
emo := &model.Emoji{}
if err = rows.Scan(&emo.ID, &emo.PackageID, &emo.Name, &emo.URL, &emo.Sort, &emo.State, &emo.Remark); err != nil {
return
}
emojis = append(emojis, emo)
}
err = rows.Err()
return
}
// EmojiListByPid get emoji by package_id
func (d *Dao) EmojiListByPid(c context.Context, pid int64) (emojis []*model.Emoji, err error) {
rows, err := d.db.Query(c, _selEmojiByPidSQL, pid)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
emo := &model.Emoji{}
if err = rows.Scan(&emo.ID, &emo.PackageID, &emo.Name, &emo.URL, &emo.Sort, &emo.State, &emo.Remark); err != nil {
return
}
emojis = append(emojis, emo)
}
err = rows.Err()
return
}
// EmojiByName get emoji by name
func (d *Dao) EmojiByName(c context.Context, name string) (emojis []*model.Emoji, err error) {
rows, err := d.db.Query(c, _selEmojiByNameSQL, name)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
emo := &model.Emoji{}
if err = rows.Scan(&emo.ID); err != nil {
return
}
emojis = append(emojis, emo)
}
err = rows.Err()
return
}
// CreateEmoji insert a emoji into db
func (d *Dao) CreateEmoji(c context.Context, pid int64, name string, url string, sort int32, state int32, remark string) (id int64, err error) {
result, err := d.db.Exec(c, _insertEmojiSQL, pid, name, url, sort, state, remark, time.Now(), time.Now())
if err != nil {
return
}
return result.LastInsertId()
}
// UpEmojiSort udpate emoji sort
func (d *Dao) UpEmojiSort(tx *xsql.Tx, ids string) (err error) {
idx := strings.Split(ids, ",")
for sort, id := range idx {
_, err = tx.Exec(_upEmojiSortSQL, sort, time.Now(), id)
if err != nil {
return
}
}
return nil
}
// UpEmojiStateByID update emoji state
func (d *Dao) UpEmojiStateByID(c context.Context, state int32, id int64) (idx int64, err error) {
result, err := d.db.Exec(c, _upEmojiStateSQL, state, time.Now(), id)
if err != nil {
return
}
return result.RowsAffected()
}
// UpEmoji update emoji name and remark by id
func (d *Dao) UpEmoji(c context.Context, name string, remark string, url string, id int64) (idx int64, err error) {
result, err := d.db.Exec(c, _upEmojiSQL, name, remark, url, time.Now(), id)
if err != nil {
return
}
return result.RowsAffected()
}
// DelEmojiByID delete emoji by id
func (d *Dao) DelEmojiByID(c context.Context, id int64) (idx int64, err error) {
result, err := d.db.Exec(c, _delEmojiByIDSQL, id)
if err != nil {
return
}
return result.RowsAffected()
}
// DelEmojiByPid delete emoji by package_id
func (d *Dao) DelEmojiByPid(c context.Context, pid int64) (idx int64, err error) {
result, err := d.db.Exec(c, _delEmojiByPidSQL, pid)
if err != nil {
return
}
return result.RowsAffected()
}

View File

@@ -0,0 +1,75 @@
package dao
import (
"context"
"strings"
"time"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
)
const (
_selEmojiPackageSQL = "SELECT id,name,url,remark,sort,state from emoji_package order by sort"
_addEmojiPackageSQL = "INSERT INTO emoji_package (name, url, sort, state, remark, ctime, mtime ) VALUES (?, ?, ?, ?, ?, ?, ?)"
_upEmojiPackageSQL = "UPDATE emoji_package SET `name`=?, url=?, remark=?, state=?, mtime=? WHERE id=?"
_upEmojiPackageSortSQL = "UPDATE emoji_package SET sort=?, mtime=? where id=?"
_delEmojiPackageSQL = "DELETE from emoji_package where id=?"
)
// EmojiPackageList get all emoji_package
func (d *Dao) EmojiPackageList(c context.Context) (packs []*model.EmojiPackage, err error) {
rows, err := d.db.Query(c, _selEmojiPackageSQL)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
pack := &model.EmojiPackage{}
if err = rows.Scan(&pack.ID, &pack.Name, &pack.URL, &pack.Remark, &pack.Sort, &pack.State); err != nil {
return
}
packs = append(packs, pack)
}
err = rows.Err()
return
}
// CreateEmojiPackage insert a emoji_package into db
func (d *Dao) CreateEmojiPackage(c context.Context, name string, url string, sort int32, remark string, state int32) (id int64, err error) {
result, err := d.db.Exec(c, _addEmojiPackageSQL, name, url, sort, state, remark, time.Now(), time.Now())
if err != nil {
return
}
return result.LastInsertId()
}
// UpEmojiPackageSort udpate emojipack sort
func (d *Dao) UpEmojiPackageSort(tx *xsql.Tx, ids string) (err error) {
idx := strings.Split(ids, ",")
for sort, id := range idx {
_, err = tx.Exec(_upEmojiPackageSortSQL, sort, time.Now(), id)
if err != nil {
return
}
}
return nil
}
//UpEmojiPackage update emojipack by id
func (d *Dao) UpEmojiPackage(c context.Context, name string, url string, remark string, state int32, id int64) (idx int64, err error) {
result, err := d.db.Exec(c, _upEmojiPackageSQL, name, url, remark, state, time.Now(), id)
if err != nil {
return
}
return result.RowsAffected()
}
// DelEmojiPackage delete emoji_package by id
func (d *Dao) DelEmojiPackage(c context.Context, id int64) (idx int64, err error) {
result, err := d.db.Exec(c, _delEmojiPackageSQL, id)
if err != nil {
return
}
return result.RowsAffected()
}

View File

@@ -0,0 +1,43 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_EmojiPack(t *testing.T) {
Convey("CreateEmojiPackage", t, WithDao(func(d *Dao) {
id, err := d.CreateEmojiPackage(context.Background(), "[2233娘]", "www.baidu.com", 0, "", 1)
So(err, ShouldBeNil)
So(id, ShouldNotEqual, 0)
d.DelEmojiPackage(context.Background(), id)
}))
Convey("EmojiPackageList", t, WithDao(func(d *Dao) {
packs, err := d.EmojiPackageList(context.Background())
So(err, ShouldBeNil)
for _, v := range packs {
t.Logf("v.Id= %d, v.Name= %s, v.Url= %s, v.Remark= %s, v.State= %d, v.Sort= %d",
v.ID, v.Name, v.URL, v.Remark, v.State, v.Sort)
}
}))
Convey("UpEmojiPackage", t, WithDao(func(d *Dao) {
id, err := d.UpEmojiPackage(context.Background(), "[小电视x]", "xxxx", "xx", 1, 1)
So(err, ShouldBeNil)
t.Logf("id= %d", id)
}))
Convey("UpEmojiPackageSort", t, WithDao(func(d *Dao) {
tx, _ := d.BeginTran(context.Background())
err := d.UpEmojiPackageSort(tx, "1")
if err != nil {
tx.Rollback()
t.Errorf("UpEmojiPackageSort err (%v)", err)
return
}
tx.Commit()
}))
}

View File

@@ -0,0 +1,82 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestDao_Emoji(t *testing.T) {
// insert emoji
Convey("CreateEmoji", t, WithDao(func(d *Dao) {
id, err := d.CreateEmoji(context.Background(), 1, "[小电视_1]", "baidu.com", 0, 0, "ssss")
So(err, ShouldBeNil)
So(id, ShouldNotEqual, 0)
defer d.DelEmojiByID(context.Background(), id)
insertID := id
//get all emoji
Convey("EmojiList", WithDao(func(d *Dao) {
data, err := d.EmojiList(context.Background())
So(err, ShouldBeNil)
for _, v := range data {
t.Logf("v.Id= %d, v.PackageID= %d, v.Name= %s, v.Url= %s, v.Remark= %s, v.State= %d, v.Sort= %d",
v.ID, v.PackageID, v.Name, v.URL, v.Remark, v.State, v.Sort)
}
}))
// get emoji by package_id
Convey("EmojiListByPid", WithDao(func(d *Dao) {
data, err := d.EmojiListByPid(context.Background(), 1)
So(err, ShouldBeNil)
for _, v := range data {
t.Logf("v.Id= %d, v.PackageID= %d, v.Name= %s, v.Url= %s, v.Remark= %s, v.State= %d, v.Sort= %d",
v.ID, v.PackageID, v.Name, v.URL, v.Remark, v.State, v.Sort)
}
}))
//update emoji sort
Convey("UpEmojiSort", WithDao(func(d *Dao) {
tx, _ := d.BeginTran(context.Background())
err := d.UpEmojiSort(tx, "2,1")
if err != nil {
tx.Rollback()
t.Errorf("UpEmojiSort err (%v)", err)
return
}
tx.Commit()
}))
//update emoji state
Convey("test UpdateEmojis", WithDao(func(d *Dao) {
id, err := d.UpEmojiStateByID(context.Background(), 1, 70)
So(err, ShouldBeNil)
So(id, ShouldNotEqual, 0)
t.Logf("id= %d", id)
}))
Convey("test select emoji by name", WithDao(func(d *Dao) {
emojis, err := d.EmojiByName(context.Background(), "[小电视_1]")
So(err, ShouldBeNil)
for _, v := range emojis {
t.Logf("v.ID= %d", v.ID)
}
}))
// update emoji remark
Convey("test SortEmojis", WithDao(func(d *Dao) {
id, err := d.UpEmoji(context.Background(), "[小电视]", "cccxxx", "google.com", insertID)
So(err, ShouldBeNil)
So(id, ShouldNotEqual, 0)
t.Logf("id= %d", id)
}))
Convey("test delEmoji", WithDao(func(d *Dao) {
id, err := d.DelEmojiByID(context.Background(), insertID)
So(err, ShouldBeNil)
So(id, ShouldNotEqual, 0)
t.Logf("id= %d", id)
}))
}))
}

View File

@@ -0,0 +1,33 @@
package dao
import (
"context"
"fmt"
"go-common/app/admin/main/reply/model"
"go-common/library/log"
)
type event struct {
Action string `json:"action"`
Mid int64 `json:"mid"`
Subject *model.Subject `json:"subject"`
Reply *model.Reply `json:"reply"`
Report *model.Report `json:"report,omitempty"`
}
// PubEvent pub reply event.
func (d *Dao) PubEvent(c context.Context, action string, mid int64, sub *model.Subject, rp *model.Reply, report *model.Report) error {
e := &event{
Action: action,
Mid: mid,
Subject: sub,
Reply: rp,
Report: report,
}
if sub == nil {
log.Error("PubEvent failed,sub is nil!value: %v %v %v %v", action, mid, rp, report)
return nil
}
return d.eventBus.Send(c, fmt.Sprint(sub.Oid), &e)
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestEvent(t *testing.T) {
var (
mid = int64(1)
sub = &model.Subject{}
rp = &model.Reply{Content: &model.ReplyContent{}}
report = &model.Report{}
c = context.Background()
)
Convey("pub a event", t, WithDao(func(d *Dao) {
err := d.PubEvent(c, model.EventReportAdd, mid, sub, rp, report)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,102 @@
package dao
import (
"context"
"fmt"
"go-common/app/admin/main/reply/model"
"go-common/library/database/sql"
)
const (
_foldedReplies = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,floor,state,attr,ctime,mtime FROM reply_%d WHERE oid=? AND type=? AND root=? AND state=12"
_countFoldedReplies = "SELECT COUNT(*) FROM reply_%d WHERE oid=? AND type=? AND root=? AND state=12"
)
// TxCountFoldedReplies ...
func (d *Dao) TxCountFoldedReplies(tx *sql.Tx, oid int64, tp int32, root int64) (count int, err error) {
if err = tx.QueryRow(fmt.Sprintf(_countFoldedReplies, hit(oid)), oid, tp, root).Scan(&count); err != nil {
if err == sql.ErrNoRows {
err = nil
}
return
}
return
}
// FoldedReplies ...
func (d *Dao) FoldedReplies(ctx context.Context, oid int64, tp int32, root int64) (rps []*model.Reply, err error) {
rows, err := d.dbSlave.Query(ctx, fmt.Sprintf(_foldedReplies, hit(oid)), oid, tp, root)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
r := new(model.Reply)
if err = rows.Scan(&r.ID, &r.Oid, &r.Type, &r.Mid, &r.Root, &r.Parent, &r.Dialog, &r.Count, &r.RCount, &r.Like, &r.Floor, &r.State, &r.Attr, &r.CTime, &r.MTime); err != nil {
return
}
rps = append(rps, r)
}
if err = rows.Err(); err != nil {
return
}
return
}
// RemRdsByFold ...
func (d *Dao) RemRdsByFold(ctx context.Context, roots []int64, childMap map[int64][]int64, sub *model.Subject, rpMap map[int64]*model.Reply) {
var (
keyMap = make(map[string][]int64)
)
// 评论列表缓存
keyMap[keyMainIdx(sub.Oid, sub.Type, model.SortByFloor)] = roots
keyMap[keyMainIdx(sub.Oid, sub.Type, model.SortByCount)] = roots
keyMap[keyMainIdx(sub.Oid, sub.Type, model.SortByLike)] = roots
for root, children := range childMap {
// 评论详情页缓存
keyMap[keyRootIdx(root)] = children
for _, child := range children {
// 对话列表的缓存
if rp, ok := rpMap[child]; ok && rp.Dialog != 0 {
keyMap[keyDialogIdx(rp.Dialog)] = append(keyMap[keyDialogIdx(rp.Dialog)], rp.ID)
}
}
}
d.RemReplyFromRedis(ctx, keyMap)
}
// AddRdsByFold ...
func (d *Dao) AddRdsByFold(ctx context.Context, roots []int64, childMap map[int64][]int64, sub *model.Subject, rpMap map[int64]*model.Reply) {
var (
ok bool
err error
keyMapping = make(map[string][]*model.Reply)
)
if ok, err = d.ExpireFolder(ctx, model.FolderKindSub, sub.Oid); err != nil {
return
}
if ok {
key := keyFolderIdx(model.FolderKindSub, sub.Oid)
for _, root := range roots {
if rp, ok := rpMap[root]; ok {
keyMapping[key] = append(keyMapping[key], rp)
}
}
}
// 这里不回源
for root, children := range childMap {
if ok, err = d.ExpireFolder(ctx, model.FolderKindRoot, root); err != nil {
return
}
if ok {
key := keyFolderIdx(model.FolderKindRoot, root)
for _, child := range children {
if rp, ok := rpMap[child]; ok {
keyMapping[key] = append(keyMapping[key], rp)
}
}
}
}
d.AddFolder(ctx, keyMapping)
}

View File

@@ -0,0 +1,79 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoTxCountFoldedReplies(t *testing.T) {
convey.Convey("TxCountFoldedReplies", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
oid = int64(0)
tp = int32(0)
root = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
count, err := d.TxCountFoldedReplies(tx, oid, tp, root)
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 TestDaoFoldedReplies(t *testing.T) {
convey.Convey("FoldedReplies", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int32(0)
root = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
rps, err := d.FoldedReplies(c, oid, tp, root)
ctx.Convey("Then err should be nil.rps should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rps, convey.ShouldBeNil)
})
})
})
}
func TestDaoRemRdsByFold(t *testing.T) {
convey.Convey("RemRdsByFold", t, func(ctx convey.C) {
var (
c = context.Background()
roots = []int64{}
childMap map[int64][]int64
sub = &model.Subject{}
rpMap map[int64]*model.Reply
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
d.RemRdsByFold(c, roots, childMap, sub, rpMap)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestDaoAddRdsByFold(t *testing.T) {
convey.Convey("AddRdsByFold", t, func(ctx convey.C) {
var (
c = context.Background()
roots = []int64{}
childMap map[int64][]int64
sub = &model.Subject{}
rpMap map[int64]*model.Reply
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
d.AddRdsByFold(c, roots, childMap, sub, rpMap)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"fmt"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_selFilteredReply = "select rpid,message FROM reply_filtered WHERE rpid in (%s)"
_filterSearch = "http://api.bilibili.co/x/admin/filter/origins"
)
// 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.db.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
}

View File

@@ -0,0 +1,22 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoFilterContents(t *testing.T) {
convey.Convey("FilterContents", t, func(ctx convey.C) {
var (
rpMaps map[int64]string
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.FilterContents(context.Background(), rpMaps)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,476 @@
package dao
import (
"context"
"encoding/binary"
"encoding/json"
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/model"
"go-common/library/database/elastic"
"go-common/library/log"
)
const (
// api
_apiSearch = "/api/reply/internal/search"
_apiSearchUpdate = "/api/reply/internal/update"
// index
_searchIdxReply = "reply"
_searchIdxReport = "replyreport"
_searchIdxMonitor = "replymonitor"
_searchIdxTimeFormat = "2006-01-02 15:03:04"
)
var zeroTime = time.Time{}
func (d *Dao) SearchReplyV3(c context.Context, sp *model.SearchParams, page, pageSize int64) (res *model.SearchResult, err error) {
var (
end = sp.End
begin = sp.Begin
business = "reply_list"
)
if end == zeroTime {
end = time.Now()
}
if begin == zeroTime {
begin = end.Add(-time.Hour * 24 * 30)
}
r := d.es.NewRequest(business).IndexByTime("reply_list", elastic.IndexTypeWeek, begin, end).
WhereEq("type", fmt.Sprint(sp.Type)).Order(sp.Order, sp.Sort).Pn(int(page)).Ps(int(pageSize)).
WhereRange("ctime", begin.Format("2006-01-02 15:04:05"), end.Format("2006-01-02 15:04:05"), elastic.RangeScopeLcRc)
if sp.Oid != 0 {
r = r.WhereEq("oid", strconv.FormatInt(sp.Oid, 10))
}
if sp.TypeIds != "" {
r = r.WhereIn("typeid", strings.Split(sp.TypeIds, ","))
}
if sp.Keyword != "" {
r = r.WhereLike([]string{"message"}, []string{sp.Keyword}, true, elastic.LikeLevelLow)
}
if sp.KeywordHigh != "" {
r = r.WhereLike([]string{"message_middle"}, []string{sp.KeywordHigh}, true, elastic.LikeLevelMiddle)
r = r.OrderScoreFirst(false)
}
if sp.UID != 0 {
r = r.WhereEq("mid", sp.UID)
}
if sp.Uname != "" {
r = r.WhereEq("replier", sp.Uname)
}
if sp.AdminID != 0 {
r = r.WhereEq("adminid", sp.AdminID)
}
if sp.States != "" {
r = r.WhereIn("state", strings.Split(sp.States, ","))
}
if sp.IP != 0 {
var ip = make([]byte, 4)
binary.BigEndian.PutUint32(ip, uint32(sp.IP))
r = r.WhereEq("ip", net.IPv4(ip[0], ip[1], ip[2], ip[3]).String())
}
if sp.Attr != "" {
r = r.WhereIn("attr", strings.Split(sp.Attr, ","))
}
if sp.AdminName != "" {
r = r.WhereEq("admin_name", sp.AdminName)
}
result := new(struct {
Code int
Page *model.Page
Order string
Sort string
Result []*model.SearchReply
Message string
})
log.Warn("search params: %s", r.Params())
err = r.Scan(c, &result)
if err != nil || result.Code != 0 {
log.Error("SearchReplyV3 r.Scan(%v) error:(%v)", c, err)
return
}
res = new(model.SearchResult)
res.Result = result.Result
res.Code = result.Code
res.Page = result.Page.Num
res.PageSize = result.Page.Size
if res.PageSize > 0 {
res.PageCount = result.Page.Total / result.Page.Size
}
res.Total = result.Page.Total
res.Order = result.Order
res.Message = result.Message
return
}
// SearchAdminLog search adminlog
func (d *Dao) SearchAdminLog(c context.Context, rpids []int64) (res []*model.SearchAdminLog, err error) {
if len(rpids) == 0 {
return
}
r := d.es.NewRequest("reply_admin_log").Index("replyadminlog").Pn(int(1)).Ps(len(rpids)).WhereIn("rpid", rpids)
result := new(struct {
Code int
Page *model.Page
Order string
Sort string
Result []*model.SearchAdminLog
Message string
})
log.Warn("search params: %s", r.Params())
err = r.Scan(c, &result)
if err != nil || result.Code != 0 {
log.Error("SearchAdminLog r.Scan(%v) error:(%v)", c, err)
return
}
res = result.Result
return
}
// SearchReply search reply from ES.
func (d *Dao) SearchReply(c context.Context, p *model.SearchParams, page, pageSize int64) (res *model.SearchResult, err error) {
params := url.Values{}
params.Set("appid", _searchIdxReply)
params.Set("type", fmt.Sprint(p.Type))
params.Set("sort", p.Sort)
params.Set("order", p.Order)
params.Set("page", fmt.Sprint(page))
params.Set("pagesize", fmt.Sprint(pageSize))
if p.Oid != 0 {
params.Set("oid", strconv.FormatInt(p.Oid, 10))
}
if p.TypeIds != "" {
params.Set("typeids", p.TypeIds)
}
if p.Keyword != "" {
params.Set("keyword", p.Keyword)
}
if p.UID != 0 {
params.Set("uid", strconv.FormatInt(p.UID, 10))
}
if p.Uname != "" {
params.Set("nickname", p.Uname)
}
if p.AdminID != 0 {
params.Set("adminid", strconv.FormatInt(p.AdminID, 10))
}
if p.Begin != zeroTime {
params.Set("start_time", p.Begin.Format(model.DateFormat))
}
if p.End != zeroTime {
params.Set("end_time", p.End.Format(model.DateFormat))
}
if p.States != "" {
params.Set("states", p.States)
}
if p.IP != 0 {
params.Set("ip", strconv.FormatInt(p.IP, 10))
}
if p.Attr != "" {
params.Set("attr", p.Attr)
}
if p.AdminName != "" {
params.Set("admin_name", p.AdminName)
}
res = &model.SearchResult{}
uri := conf.Conf.Host.Search + _apiSearch
if err = d.httpClient.Get(c, uri, "", params, res); err != nil {
log.Error("searchReply error(%v)", err)
return
}
if res.Code != 0 {
err = model.ErrSearchReply
log.Error("searchReply:%+v error(%v)", res, err)
}
return
}
// SearchMonitor return search monitor reply from ES.
func (d *Dao) SearchMonitor(c context.Context, sp *model.SearchMonitorParams, page, pageSize int64) (res *model.SearchMonitorResult, err error) {
var (
fields []string
keywords []string
order string
sort = "desc"
)
// NOTE:这里之前order 跟 sort 搞反了
if sp.Sort != "" {
order = sp.Sort
}
if sp.Order != "" {
sort = sp.Order
}
r := d.es.NewRequest("reply_monitor").Index(_searchIdxMonitor).
WhereEq("type", fmt.Sprint(sp.Type)).
Order(order, sort).Pn(int(page)).Ps(int(pageSize))
// mode=0 所有监控方式, mode=1 monitor, mode=2, 先审后发
if sp.Mode == 0 {
r = r.WhereOr("monitor", true).WhereOr("audit", true)
} else if sp.Mode == 1 {
r = r.WhereEq("monitor", true)
} else if sp.Mode == 2 {
r = r.WhereEq("audit", true)
}
if sp.Oid > 0 {
r = r.WhereEq("oid", fmt.Sprint(sp.Oid))
}
if sp.UID > 0 {
r = r.WhereEq("mid", fmt.Sprint(sp.UID))
}
if sp.NickName != "" {
fields = append(fields, "uname")
keywords = append(keywords, sp.NickName)
}
if sp.Keyword != "" {
fields = append(fields, "title")
keywords = append(keywords, sp.Keyword)
}
if fields != nil && keywords != nil {
r = r.WhereLike(fields, keywords, true, elastic.LikeLevelLow)
}
result := new(struct {
Code int
Page *model.Page
Order string
Sort string
Result []*model.SearchMonitor
Message string
})
res = &model.SearchMonitorResult{}
log.Warn(r.Params())
err = r.Scan(c, &result)
if err != nil || result.Code != 0 {
log.Error("r.Scan(%v) error:(%v)", c, err)
return
}
res.Result = result.Result
res.Code = result.Code
res.Page = result.Page.Num
res.PageSize = result.Page.Size
if res.PageSize > 0 {
res.PageCount = result.Page.Total / result.Page.Size
}
res.Total = result.Page.Total
res.Order = result.Order
res.Message = result.Message
oids := make([]int64, len(res.Result))
var tp int32
for idx, r := range res.Result {
oids[idx] = r.Oid
tp = int32(r.Type)
}
results, err := d.SubMCount(c, oids, tp)
if err != nil {
log.Error("SubMCount(%v,%v) error", oids, tp)
return
}
for i, reply := range res.Result {
res.Result[i].MCount = results[reply.Oid]
res.Result[i].OidStr = strconv.FormatInt(res.Result[i].Oid, 10)
}
return
}
// UpSearchMonitor update monitor to search data.
func (d *Dao) UpSearchMonitor(c context.Context, sub *model.Subject, remark string) (err error) {
m := make(map[string]interface{})
m["oid"] = sub.Oid
m["type"] = sub.Type
if sub.AttrVal(model.SubAttrMonitor) == model.AttrYes {
m["monitor"] = true
} else {
m["monitor"] = false
}
if sub.AttrVal(model.SubAttrAudit) == model.AttrYes {
m["audit"] = true
} else {
m["audit"] = false
}
m["remark"] = remark
us := d.es.NewUpdate("reply_monitor").Insert()
us.AddData("replymonitor", m)
err = us.Do(c)
if err != nil {
err = model.ErrSearchReport
log.Error("upSearchMonitor error(%v)", err)
return
}
return
}
// SearchReport search reports from ES.
func (d *Dao) SearchReport(c context.Context, sp *model.SearchReportParams, page, pageSize int64) (res *model.SearchReportResult, err error) {
params := url.Values{}
params.Set("appid", "replyreport")
params.Set("type", fmt.Sprint(sp.Type))
params.Set("page", fmt.Sprint(page))
params.Set("pagesize", fmt.Sprint(pageSize))
if sp.Oid != 0 {
params.Set("oid", fmt.Sprint(sp.Oid))
}
if sp.UID != 0 {
params.Set("uid", fmt.Sprint(sp.UID))
}
if sp.Reason != "" {
params.Set("reason", sp.Reason)
}
if sp.Typeids != "" {
params.Set("typeids", sp.Typeids)
}
if sp.Keyword != "" {
params.Set("keyword", sp.Keyword)
}
if sp.Nickname != "" {
params.Set("nickname", sp.Nickname)
}
if sp.States != "" {
params.Set("states", sp.States)
}
if sp.StartTime != "" {
params.Set("start_time", sp.StartTime)
}
if sp.EndTime != "" {
params.Set("end_time", sp.EndTime)
}
if sp.Order != "" {
params.Set("order", sp.Order)
}
if sp.Sort != "" {
params.Set("sort", sp.Sort)
}
res = &model.SearchReportResult{}
uri := conf.Conf.Host.Search + _apiSearch
if err = d.httpClient.Get(c, uri, "", params, res); err != nil {
log.Error("searchReport error(%v)", err)
return
}
if res.Code != 0 {
err = model.ErrSearchReport
log.Error("searchReport:%+v error(%v)", res, err)
}
return
}
// MonitorStats return search monitor stats from ES.
func (d *Dao) MonitorStats(c context.Context, mode, page, pageSize int64, adminIDs, sort, order, startTime, endTime string) (res *model.StatsMonitorResult, err error) {
params := url.Values{}
params.Set("appid", "replymonista")
params.Set("mode", fmt.Sprint(mode))
params.Set("page", fmt.Sprint(page))
params.Set("pagesize", fmt.Sprint(pageSize))
if adminIDs != "" {
params.Set("adminids", adminIDs)
params.Set("typeid", fmt.Sprint(model.MonitorStatsUser))
} else {
params.Set("typeid", fmt.Sprint(model.MonitorStatsAll))
}
if sort != "" {
params.Set("sort", sort)
}
if order != "" {
params.Set("order", order)
}
if startTime != "" {
params.Set("start_time", startTime)
}
if endTime != "" {
params.Set("end_time", endTime)
}
res = &model.StatsMonitorResult{}
uri := conf.Conf.Host.Search + _apiSearch
if err = d.httpClient.Get(c, uri, "", params, res); err != nil {
log.Error("monitorStats error(%v)", err)
return
}
if res.Code != 0 {
err = model.ErrSearchMonitor
log.Error("searchStats:%+v error(%v)", res, err)
}
return
}
// UpSearchReply update search reply index.
func (d *Dao) UpSearchReply(c context.Context, rps map[int64]*model.Reply, newState int32) (err error) {
if len(rps) <= 0 {
return
}
stales := d.es.NewUpdate("reply_list")
for _, rp := range rps {
m := make(map[string]interface{})
m["id"] = rp.ID
m["state"] = newState
m["mtime"] = rp.MTime.Time().Format("2006-01-02 15:04:05")
m["oid"] = rp.Oid
m["type"] = rp.Type
if rp.Content != nil {
m["message"] = rp.Content.Message
}
stales = stales.AddData(d.es.NewUpdate("reply_list").IndexByTime("reply_list", elastic.IndexTypeWeek, rp.CTime.Time()), m)
}
err = stales.Do(c)
if err != nil {
log.Error("upSearchReply update stales(%s) failed!err:=%v", stales.Params(), err)
return
}
log.Info("upSearchReply:stale:%s ret:%+v", stales.Params(), err)
return
}
// UpSearchReport update search report index.
func (d *Dao) UpSearchReport(c context.Context, rpts map[int64]*model.Report, rpState *int32) (err error) {
var res struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
params := url.Values{}
params.Set("appid", _searchIdxReport)
values := make([]map[string]interface{}, 0)
rps := make(map[int64]*model.Reply)
for _, rpt := range rpts {
if int64(rpt.ReplyCtime) != 0 && rpState != nil {
rps[rpt.RpID] = &model.Reply{
ID: rpt.RpID,
Oid: rpt.Oid,
Type: rpt.Type,
MTime: rpt.MTime,
CTime: rpt.ReplyCtime,
State: *rpState,
}
}
v := make(map[string]interface{})
v["id"] = fmt.Sprintf("%d_%d_%d", rpt.RpID, rpt.Oid, rpt.Type)
v["content"] = rpt.Content
v["reason"] = rpt.Reason
v["state"] = rpt.State
v["mtime"] = rpt.MTime.Time().Format(_searchIdxTimeFormat)
v["index_time"] = rpt.CTime.Time().Format(_searchIdxTimeFormat)
if rpt.Attr == 1 {
v["attr"] = []int{1}
} else {
v["attr"] = []int{}
}
if rpState != nil {
v["reply_state"] = *rpState
}
values = append(values, v)
}
b, _ := json.Marshal(values)
params.Set("val", string(b))
// http post
uri := conf.Conf.Host.Search + _apiSearchUpdate
if err = d.httpClient.Post(c, uri, "", params, &res); err != nil {
log.Error("upSearchReport error(%v)", err)
}
log.Info("upSearchReport:%s post:%s ret:%+v", uri, params.Encode(), res)
if len(rps) != 0 && rpState != nil {
err = d.UpSearchReply(c, rps, *rpState)
}
return
}

View File

@@ -0,0 +1,71 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/app/admin/main/reply/model"
xtime "go-common/library/time"
. "github.com/smartystreets/goconvey/convey"
)
func TestUpdateReply(t *testing.T) {
Convey("update search reply", t, WithDao(func(d *Dao) {
err := d.UpSearchReply(context.Background(), map[int64]*model.Reply{111852176: &model.Reply{
ID: 111852176,
Oid: 10098544,
Type: 1,
CTime: xtime.Time(1534412702),
MTime: xtime.Time(time.Now().Unix()),
State: 0,
}}, 3)
So(err, ShouldBeNil)
fmt.Println(err)
}))
}
func TestSearchAdminLog(t *testing.T) {
Convey("test search adminlog", t, WithDao(func(d *Dao) {
res, err := d.SearchAdminLog(context.TODO(), []int64{111843721})
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
fmt.Printf("%+v", res[0])
}))
}
func TestSearchMonitor(t *testing.T) {
var (
c = context.Background()
sp = &model.SearchMonitorParams{
Mode: 0,
Type: 1,
Oid: 10099866,
Sort: "unverify_num",
}
oid int64 = 10099866
typ int32 = 1
remark = "remark"
)
Convey("test search monitor", t, WithDao(func(d *Dao) {
res, err := d.SearchMonitor(c, sp, 1, 20)
So(err, ShouldBeNil)
So(len(res.Result), ShouldBeGreaterThan, 0)
So(res.Result[0].Oid, ShouldEqual, sp.Oid)
}))
Convey("test add monitor", t, WithDao(func(d *Dao) {
sub, _ := d.Subject(c, oid, typ)
sub.AttrSet(model.AttrYes, model.SubAttrMonitor)
err := d.UpSearchMonitor(c, sub, remark)
So(err, ShouldBeNil)
}))
time.Sleep(5 * time.Second)
Convey("test search monitor", t, WithDao(func(d *Dao) {
res, err := d.SearchMonitor(c, sp, 1, 1)
So(err, ShouldBeNil)
So(len(res.Result), ShouldBeGreaterThan, 0)
So(res.Result[0].Remark, ShouldEqual, remark)
}))
}

View File

@@ -0,0 +1,330 @@
package dao
import (
"context"
"fmt"
"net/url"
"strconv"
"go-common/library/log"
"go-common/library/xstr"
)
const (
// api
_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"
_apiLiveActivity = "http://api.live.bilibili.co/comment/v1/relation/get_by_id"
_apiLiveNotice = "http://api.vc.bilibili.co/news/v1/notice/info"
_apiLivePicture = "http://api.vc.bilibili.co/link_draw/v1/doc/detail"
_apiActivitySub = "http://matsuri.bilibili.co//activity/subject/url"
_apiTopic = "http://matsuri.bilibili.co/activity/page/one/%d"
_apiTopics = "http://matsuri.bilibili.co/activity/pages"
_apiDynamic = "http://api.vc.bilibili.co/dynamic_repost/v0/dynamic_repost/ftch_rp_cont?dynamic_ids[]=%d"
// 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"
)
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
}
// 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
}
// 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
}
// 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
}
// 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
}

View File

@@ -0,0 +1,196 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoNoticeTitle(t *testing.T) {
convey.Convey("NoticeTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
title, link, err := _d.NoticeTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBanTitle(t *testing.T) {
convey.Convey("BanTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
title, link, err := _d.BanTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCreditTitle(t *testing.T) {
convey.Convey("CreditTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
title, link, err := _d.CreditTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(link, convey.ShouldNotBeNil)
ctx.So(title, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoLiveVideoTitle(t *testing.T) {
convey.Convey("LiveVideoTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
title, link, err := _d.LiveVideoTitle(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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoLiveActivityTitle(t *testing.T) {
convey.Convey("LiveActivityTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose 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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoLiveNoticeTitle(t *testing.T) {
convey.Convey("LiveNoticeTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose 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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoLivePictureTitle(t *testing.T) {
convey.Convey("LivePictureTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose 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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoTopicTitle(t *testing.T) {
convey.Convey("TopicTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
title, link, err := _d.TopicTitle(c, oid)
ctx.Convey("Then err should be nil.title,link should not be nil.", func(ctx convey.C) {
if err == nil {
ctx.So(err, convey.ShouldBeNil)
}
ctx.So(link, convey.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoTopicsLink(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 gose 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 TestDaoActivitySub(t *testing.T) {
convey.Convey("ActivitySub", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose 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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}
func TestDaoDynamicTitle(t *testing.T) {
convey.Convey("DynamicTitle", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
)
ctx.Convey("When everything gose 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.ShouldBeBlank)
ctx.So(title, convey.ShouldBeBlank)
})
})
})
}

View File

@@ -0,0 +1,103 @@
package dao
import (
"context"
"strconv"
"go-common/app/admin/main/reply/model"
"go-common/library/database/elastic"
"go-common/library/ecode"
"go-common/library/log"
"strings"
"time"
)
const (
// log api
_logURL = "/x/admin/search/log"
)
// ReportLogData report log data
type ReportLogData struct {
Sort string `json:"sort"`
Order string `json:"order"`
Page model.Page `json:"page"`
Result []*ReportLogResult `json:"result"`
}
// ReportLogResult ReportLogResult
type ReportLogResult struct {
AdminName string `json:"uname"`
AdminID int64 `json:"uid"`
Business int64 `json:"business"`
Type int32 `json:"type"`
Oid int64 `json:"oid"`
OidStr string `json:"oid_str"`
Action string `json:"action"`
Ctime string `json:"ctime"`
Index0 int64 `json:"int_0"`
Index1 int64 `json:"int_1"`
Index2 int64 `json:"int_2"`
Content string `json:"extra_data"`
}
// ReportLog get notice info.
func (d *Dao) ReportLog(c context.Context, sp model.LogSearchParam) (data *ReportLogData, err error) {
var (
// log_audit评论的索引是按时间分区的最早的数据是2018年的所以这里最早时间是写死的2018年
stime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
etime = time.Now()
)
if sp.CtimeFrom != "" {
if stime, err = time.Parse(model.DateFormat, sp.CtimeFrom); err != nil {
log.Error("time.Parse(%v) error", sp.CtimeFrom)
stime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
}
}
if sp.CtimeTo != "" {
if etime, err = time.Parse(model.DateFormat, sp.CtimeTo); err != nil {
log.Error("time.Parse(%v) error", sp.CtimeTo)
etime = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
}
}
action := strings.Split(sp.Action, ",")
r := d.es.NewRequest("log_audit").IndexByTime("log_audit_41", elastic.IndexTypeYear, stime, etime).WhereIn("action", action).
WhereRange("ctime", stime.Format(model.DateFormat), etime.Format(model.DateFormat), elastic.RangeScopeLcRc)
if sp.Pn <= 0 {
sp.Pn = 1
}
if sp.Ps <= 0 {
sp.Ps = 20
}
if sp.Order == "" {
sp.Order = "ctime"
}
if sp.Sort == "" {
sp.Sort = "desc"
}
r = r.Order(sp.Order, sp.Sort).Pn(int(sp.Pn)).Ps(int(sp.Ps))
if sp.Oid > 0 {
r = r.WhereEq("oid", strconv.FormatInt(sp.Oid, 10))
}
if sp.Mid > 0 {
r = r.WhereEq("int_0", sp.Mid)
}
if sp.Type > 0 {
r = r.WhereEq("type", sp.Type)
}
if sp.Other > 0 {
r = r.WhereEq("int_1", sp.Other)
}
log.Warn(r.Params())
err = r.Scan(c, &data)
if err != nil {
log.Error("r.Scan(%v) error(%v)", c, err)
return
}
if data == nil {
err = ecode.ServerErr
log.Error("log_audit error")
return
}
return
}

View File

@@ -0,0 +1,44 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestReportLog(t *testing.T) {
sp := model.LogSearchParam{
Oid: 10099866,
Type: 1,
}
c := context.Background()
Convey("test top ReportLog", t, WithDao(func(d *Dao) {
sp.Action = "top"
data, err := d.ReportLog(c, sp)
So(err, ShouldBeNil)
So(data.Page.Total, ShouldEqual, 1)
}))
Convey("test monitor ReportLog", t, WithDao(func(d *Dao) {
sp.Action = "monitor"
data, err := d.ReportLog(c, sp)
So(err, ShouldBeNil)
So(data.Page.Total, ShouldEqual, 14)
}))
Convey("test subject groupby ReportLog", t, WithDao(func(d *Dao) {
sp.Action = "subject_allow,subject_forbid,subject_frozen,subject_unfrozen_allow,subject_unfrozen_forbid"
sp.Oid = 0
data, err := d.ReportLog(c, sp)
So(err, ShouldBeNil)
So(data.Page.Total, ShouldEqual, 67)
}))
Convey("test one subject ReportLog", t, WithDao(func(d *Dao) {
sp.Action = "subject_allow,subject_forbid,subject_frozen,subject_unfrozen_allow,subject_unfrozen_forbid"
sp.Appid = "log_audit"
sp.Oid = 10099866
data, err := d.ReportLog(c, sp)
So(err, ShouldBeNil)
So(data.Page.Total, ShouldEqual, 2)
}))
}

View File

@@ -0,0 +1,266 @@
package dao
import (
"context"
"fmt"
"strconv"
"go-common/app/admin/main/reply/model"
"go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_prefixSub = "s_" // sub_oid<<8|type
_prefixReply = "r_" // r_rpID
_prefixAdminTop = "at_" // at_rpID
_prefixUpperTop = "ut_" // ut_rpID
_prefixConfig = "c_%d_%d_%d" // oid_type_category
_oidOverflow = 1 << 48
)
func keyReply(rpID int64) string {
return _prefixReply + strconv.FormatInt(rpID, 10)
}
func keySubject(oid int64, typ int32) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixSub, oid, typ)
}
return _prefixSub + strconv.FormatInt((oid<<8)|int64(typ), 10)
}
func keyConfig(oid int64, typ, category int32) string {
return fmt.Sprintf(_prefixConfig, oid, typ, category)
}
func keyAdminTop(oid int64, attr uint32) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixAdminTop, oid, attr)
}
return _prefixAdminTop + strconv.FormatInt((oid<<8)|int64(attr), 10)
}
func keyUpperTop(oid int64, attr uint32) string {
if oid > _oidOverflow {
return fmt.Sprintf("%s_%d_%d", _prefixUpperTop, oid, attr)
}
return _prefixUpperTop + strconv.FormatInt((oid<<8)|int64(attr), 10)
}
// PingMC check connection success.
func (d *Dao) pingMC(c context.Context) (err error) {
conn := d.mc.Get(c)
item := memcache.Item{Key: "ping", Value: []byte{1}, Expiration: d.mcExpire}
err = conn.Set(&item)
conn.Close()
return
}
// SubjectCache get subject from memcache.
func (d *Dao) SubjectCache(c context.Context, oid int64, typ int32) (sub *model.Subject, err error) {
key := keySubject(oid, typ)
conn := d.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
}
// AddSubjectCache add subject into memcache.
func (d *Dao) AddSubjectCache(c context.Context, subs ...*model.Subject) (err error) {
conn := d.mc.Get(c)
for _, sub := range subs {
key := keySubject(sub.Oid, sub.Type)
item := &memcache.Item{Key: key, Object: sub, Expiration: d.mcExpire, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, sub, err)
}
}
conn.Close()
return
}
// DelSubjectCache delete subject from memcache.
func (d *Dao) DelSubjectCache(c context.Context, oid int64, typ int32) (err error) {
key := keySubject(oid, typ)
conn := d.mc.Get(c)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
}
}
conn.Close()
return
}
// ReplyCache get a reply from memcache.
func (d *Dao) ReplyCache(c context.Context, rpID int64) (rp *model.Reply, err error) {
key := keyReply(rpID)
conn := d.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 {
rp = nil
}
return
}
// RepliesCache multi get replies from memcache.
func (d *Dao) RepliesCache(c context.Context, rpIDs []int64) (rpMap map[int64]*model.Reply, missed []int64, err error) {
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 := keyReply(rpID)
keys[i] = key
mm[key] = rpID
}
conn := d.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
}
// AddReplyCache add reply into memcache.
func (d *Dao) AddReplyCache(c context.Context, rps ...*model.Reply) (err error) {
conn := d.mc.Get(c)
defer conn.Close()
for _, rp := range rps {
item := &memcache.Item{
Key: keyReply(rp.ID),
Object: rp,
Expiration: d.mcExpire,
Flags: memcache.FlagJSON,
}
if err = conn.Set(item); err != nil {
return
}
}
return
}
// DelReplyCache delete reply from memcache.
func (d *Dao) DelReplyCache(c context.Context, rpID int64) (err error) {
conn := d.mc.Get(c)
if err = conn.Delete(keyReply(rpID)); err != nil {
if err == memcache.ErrNotFound {
err = nil
}
}
conn.Close()
return
}
// DelConfigCache delete reply config from memcache.
func (d *Dao) DelConfigCache(c context.Context, oid int64, typ, category int32) (err error) {
key := keyConfig(oid, typ, category)
conn := d.mc.Get(c)
if err = conn.Delete(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
}
}
conn.Close()
return
}
// TopCache get a reply from memcache.
func (d *Dao) TopCache(c context.Context, oid int64, attr uint32) (rp *model.Reply, err error) {
var key string
if attr == model.SubAttrTopAdmin {
key = keyAdminTop(oid, attr)
} else if attr == model.SubAttrTopUpper {
key = keyUpperTop(oid, attr)
}
conn := d.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 {
rp = nil
}
return
}
// DelTopCache delete topreply from memcache.
func (d *Dao) DelTopCache(c context.Context, oid int64, attr uint32) (err error) {
var key string
if attr == model.SubAttrTopAdmin {
key = keyAdminTop(oid, attr)
} else if attr == model.SubAttrTopUpper {
key = keyUpperTop(oid, attr)
}
conn := d.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
}
// AddTopCache add top reply into memcache.
func (d *Dao) AddTopCache(c context.Context, rp *model.Reply) (err error) {
var key string
if rp.AttrVal(model.AttrTopAdmin) == model.AttrYes {
key = keyAdminTop(rp.Oid, model.AttrTopAdmin)
} else if rp.AttrVal(model.AttrTopUpper) == model.AttrYes {
key = keyUpperTop(rp.Oid, model.AttrTopUpper)
} else {
return
}
conn := d.mc.Get(c)
defer conn.Close()
item := &memcache.Item{Key: key, Object: rp, Expiration: d.mcExpire, Flags: memcache.FlagJSON}
if err = conn.Set(item); err != nil {
log.Error("conn.Set(%s,%v) error(%v)", key, rp, err)
}
return
}

View File

@@ -0,0 +1,84 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestSubjectCache(t *testing.T) {
var (
sub = &model.Subject{
Oid: 1,
Type: 1,
}
c = context.Background()
)
Convey("test subject cache", t, WithDao(func(d *Dao) {
// add
err := d.AddSubjectCache(c, sub)
So(err, ShouldBeNil)
// get
cache, err := d.SubjectCache(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(cache.Oid, ShouldEqual, sub.Oid)
// del
err = d.DelSubjectCache(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
// get
cache, err = d.SubjectCache(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(cache, ShouldBeNil)
}))
}
func TestReplyCache(t *testing.T) {
var (
rp = &model.Reply{
ID: 1,
Oid: 1,
Type: 1,
}
c = context.Background()
)
Convey("test reply cache", t, WithDao(func(d *Dao) {
// add
err := d.AddReplyCache(c, rp)
So(err, ShouldBeNil)
// get
cache, err := d.ReplyCache(c, rp.ID)
So(err, ShouldBeNil)
So(cache.ID, ShouldEqual, rp.ID)
// get
caches, miss, err := d.RepliesCache(c, []int64{rp.ID})
So(err, ShouldBeNil)
So(len(caches), ShouldEqual, 1)
So(len(miss), ShouldEqual, 0)
// del
err = d.DelReplyCache(c, rp.ID)
So(err, ShouldBeNil)
// get
cache, err = d.ReplyCache(c, rp.ID)
So(err, ShouldBeNil)
So(cache, ShouldBeNil)
}))
Convey("test top reply cache", t, WithDao(func(d *Dao) {
rp.AttrSet(model.AttrYes, model.AttrTopAdmin)
// add
err := d.AddTopCache(c, rp)
So(err, ShouldBeNil)
// get
cache, err := d.TopCache(c, rp.Oid, model.SubAttrTopAdmin)
So(err, ShouldBeNil)
So(cache.ID, ShouldEqual, rp.ID)
// del
err = d.DelTopCache(c, rp.Oid, model.SubAttrTopAdmin)
// get
cache, err = d.TopCache(c, rp.Oid, model.SubAttrTopAdmin)
So(err, ShouldBeNil)
So(cache, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"net/url"
"strconv"
"time"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_msgReplyDel = "1_1_2"
_msgReportAccept = "1_3_1"
_msgTypeSystem = 4
// api
_apiMsgSend = "http://message.bilibili.co/api/notify/send.user.notify.do"
)
// SendReplyDelMsg send delete reply message.
func (d *Dao) SendReplyDelMsg(c context.Context, mid int64, title, msg string, now time.Time) (err error) {
return d.sendMsg(c, _msgReplyDel, title, msg, _msgTypeSystem, 0, []int64{mid}, "", now.Unix())
}
// SendReportAcceptMsg send report message.
func (d *Dao) SendReportAcceptMsg(c context.Context, mid int64, title, msg string, now time.Time) (err error) {
return d.sendMsg(c, _msgReportAccept, title, msg, _msgTypeSystem, 0, []int64{mid}, "", now.Unix())
}
func (d *Dao) sendMsg(c context.Context, mc, title, msg string, typ int, pub int64, mids []int64, info string, ts int64) (err error) {
params := url.Values{}
params.Set("type", "json")
params.Set("source", "1")
params.Set("mc", mc)
params.Set("title", title)
params.Set("data_type", strconv.Itoa(typ))
params.Set("context", msg)
params.Set("mid_list", xstr.JoinInts(mids))
params.Set("publisher", strconv.FormatInt(pub, 10))
params.Set("ext_info", info)
var res struct {
Code int `json:"code"`
}
if err = d.httpClient.Post(c, _apiMsgSend, "", params, &res); err != nil {
log.Error("sendMsg error(%v) params(%v)", err, params)
return
}
if res.Code != ecode.OK.Code() {
err = model.ErrMsgSend
log.Error("sendMsg failed(%v) error(%v)", _apiMsgSend+"?"+params.Encode(), res.Code)
}
log.Info("sendMsg(mc:%s title:%s msg:%s type:%d mids:%v error(%v)", mc, title, msg, typ, mids, err)
return
}

View File

@@ -0,0 +1,25 @@
package dao
import (
"context"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestMessage(t *testing.T) {
var (
mid = int64(1)
title = "test title"
msg = "test msg"
now = time.Now()
c = context.Background()
)
Convey("test send a message", t, WithDao(func(d *Dao) {
d.SendReplyDelMsg(c, mid, title, msg, now)
So(msg, ShouldEqual, "test msg")
d.SendReportAcceptMsg(c, mid, title, msg, now)
So(title, ShouldEqual, "test title")
}))
}

View File

@@ -0,0 +1,113 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"go-common/library/database/sql"
xtime "go-common/library/time"
)
const (
_selOneNotice = "SELECT id,plat,version,condi,build,title,content,link,stime,etime,status,ctime,mtime,client_type FROM notice where id=?"
_selCountNoticeSQL = "SELECT count(*) as count FROM notice"
_selAllNoticeSQL = "SELECT id,plat,version,condi,build,title,content,link,stime,etime,status,ctime,mtime,client_type FROM notice ORDER BY stime DESC limit ?,?"
_addNoticeSQL = "INSERT INTO notice (plat,version,condi,build,title,content,link,status,stime,etime,client_type) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
_updateNotcieSQL = "UPDATE notice set plat=?,version=?,condi=?,build=?,title=?,content=?,link=?,stime=?,etime=?,client_type=? where id=?"
_updateNoticeStatusSQL = "UPDATE notice set status=? where id=?"
_delNoticeSQL = "DELETE FROM notice where id=?"
_selOnlineNoticeSQL = "SELECT id,plat,version,condi,build,title,content,link,stime,etime,status,ctime,mtime,client_type FROM notice WHERE plat=? and (stime<=? and etime>?) and status=1"
)
// RangeNotice 获取发布时间与某一时间范围有交集的公告.
func (dao *Dao) RangeNotice(c context.Context, plat model.NoticePlat, stime xtime.Time, etime xtime.Time) (nts []*model.Notice, err error) {
rows, err := dao.db.Query(c, _selOnlineNoticeSQL, plat, etime.Time(), stime.Time())
if err != nil {
return
}
defer rows.Close()
nts = make([]*model.Notice, 0)
for rows.Next() {
nt := new(model.Notice)
if err = rows.Scan(&nt.ID, &nt.Plat, &nt.Version, &nt.Condition, &nt.Build, &nt.Title, &nt.Content, &nt.Link, &nt.StartTime, &nt.EndTime, &nt.Status, &nt.CreateTime, &nt.ModifyTime, &nt.ClientType); err != nil {
return
}
nts = append(nts, nt)
}
return
}
// Notice get one notice detail.
func (dao *Dao) Notice(c context.Context, id uint32) (nt *model.Notice, err error) {
row := dao.db.QueryRow(c, _selOneNotice, id)
nt = new(model.Notice)
if err = row.Scan(&nt.ID, &nt.Plat, &nt.Version, &nt.Condition, &nt.Build, &nt.Title, &nt.Content, &nt.Link, &nt.StartTime, &nt.EndTime, &nt.Status, &nt.CreateTime, &nt.ModifyTime, &nt.ClientType); err != nil {
if err == sql.ErrNoRows {
nt = nil
err = nil
}
}
return
}
// CountNotice return notice count.
func (dao *Dao) CountNotice(c context.Context) (count int64, err error) {
row := dao.db.QueryRow(c, _selCountNoticeSQL)
err = row.Scan(&count)
return
}
// ListNotice retrive reply's notice list from db by offset and count.
func (dao *Dao) ListNotice(c context.Context, offset int64, count int64) (nts []*model.Notice, err error) {
rows, err := dao.db.Query(c, _selAllNoticeSQL, offset, count)
if err != nil {
return
}
defer rows.Close()
nts = make([]*model.Notice, 0)
for rows.Next() {
nt := new(model.Notice)
if err = rows.Scan(&nt.ID, &nt.Plat, &nt.Version, &nt.Condition, &nt.Build, &nt.Title, &nt.Content, &nt.Link, &nt.StartTime, &nt.EndTime, &nt.Status, &nt.CreateTime, &nt.ModifyTime, &nt.ClientType); err != nil {
return
}
nts = append(nts, nt)
}
err = rows.Err()
return
}
// CreateNotice insert a notice into db.
func (dao *Dao) CreateNotice(c context.Context, nt *model.Notice) (rows int64, err error) {
res, err := dao.db.Exec(c, _addNoticeSQL, nt.Plat, nt.Version, nt.Condition, nt.Build, nt.Title, nt.Content, nt.Link, nt.Status, nt.StartTime, nt.EndTime, nt.ClientType)
if err != nil {
return
}
return res.LastInsertId()
}
// UpdateNotice update main notice's main fileds.
func (dao *Dao) UpdateNotice(c context.Context, nt *model.Notice) (rows int64, err error) {
res, err := dao.db.Exec(c, _updateNotcieSQL, nt.Plat, nt.Version, nt.Condition, nt.Build, nt.Title, nt.Content, nt.Link, nt.StartTime, nt.EndTime, nt.ClientType, nt.ID)
if err != nil {
return
}
return res.RowsAffected()
}
// UpdateNoticeStatus change notice's status to offline\online.
func (dao *Dao) UpdateNoticeStatus(c context.Context, status model.NoticeStatus, id uint32) (rows int64, err error) {
res, err := dao.db.Exec(c, _updateNoticeStatusSQL, status, id)
if err != nil {
return
}
return res.RowsAffected()
}
// DeleteNotice delete a notice entry from db.
func (dao *Dao) DeleteNotice(c context.Context, id uint32) (rows int64, err error) {
res, err := dao.db.Exec(c, _delNoticeSQL, id)
if err != nil {
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,120 @@
package dao
import (
"context"
"testing"
"time"
"go-common/app/admin/main/reply/model"
xtime "go-common/library/time"
. "github.com/smartystreets/goconvey/convey"
)
var (
mid int64 = 1
nowTs = time.Now().Unix()
lastID int64
lastID2 int64
)
func Test_AddNotice(t *testing.T) {
c := context.Background()
nt := model.Notice{
Plat: model.PlatAndroid,
Condition: model.ConditionGT,
Build: 1113,
Title: "测试",
Status: model.StatusOffline,
Content: "测试内容",
Link: "http://www.bilibili.com",
StartTime: xtime.Time(nowTs),
EndTime: xtime.Time(nowTs + 10*3600),
ClientType: "android",
}
nt2 := model.Notice{
Plat: model.PlatAndroid,
Condition: model.ConditionLT,
Build: 2233,
Title: "测试2",
Status: model.StatusOffline,
Content: "测试内容2",
Link: "http://www.bilibili.com",
StartTime: xtime.Time(nowTs),
EndTime: xtime.Time(nowTs + 10*3600),
ClientType: "",
}
Convey("add notice", t, WithDao(func(d *Dao) {
var err error
lastID, err = d.CreateNotice(c, &nt)
So(err, ShouldBeNil)
So(lastID, ShouldBeGreaterThan, 0)
lastID2, err = d.CreateNotice(c, &nt2)
So(err, ShouldBeNil)
So(lastID2, ShouldBeGreaterThan, 0)
}))
}
func Test_ListNotice(t *testing.T) {
c := context.Background()
Convey("list notice", t, WithDao(func(d *Dao) {
nts, err := d.ListNotice(c, 1, 100)
So(err, ShouldBeNil)
So(len(nts), ShouldBeGreaterThan, 0)
So(nts[0].StartTime.Time().Unix(), ShouldBeGreaterThanOrEqualTo, nowTs)
count, err := d.CountNotice(c)
So(err, ShouldBeNil)
So(count, ShouldBeGreaterThan, 1)
}))
}
func Test_UpdateNotice(t *testing.T) {
c := context.Background()
Convey("update notice", t, WithDao(func(d *Dao) {
data, err := d.Notice(c, uint32(lastID2))
So(err, ShouldBeNil)
So(data.Title, ShouldEqual, "测试2")
data.ID = uint32(lastID2)
data.Title = "测试3"
rows, err := d.UpdateNotice(c, data)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
nt2, err := d.Notice(c, uint32(lastID2))
So(err, ShouldBeNil)
So(nt2.Title, ShouldEqual, "测试3")
rows, err = d.UpdateNoticeStatus(c, model.StatusOnline, uint32(lastID))
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
nts, err := d.RangeNotice(c, model.PlatAndroid, xtime.Time(nowTs)-3600, xtime.Time(nowTs+5*3600))
So(err, ShouldBeNil)
So(len(nts), ShouldBeGreaterThan, 0)
var isFound bool
for _, data = range nts {
if data.ID == uint32(lastID) {
isFound = true
}
}
So(isFound, ShouldBeTrue)
}))
}
func Test_DeleteNotice(t *testing.T) {
c := context.Background()
Convey("delete notice", t, WithDao(func(d *Dao) {
rows, err := d.DeleteNotice(c, uint32(lastID))
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
rows, err = d.DeleteNotice(c, uint32(lastID2))
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,452 @@
package dao
import (
"context"
"fmt"
"strconv"
"go-common/app/admin/main/reply/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_prefixIdx = "i_"
_prefixNewRootIdx = "ri_"
_prefixAuditIdx = "ai_%d_%d"
// 针对大忽悠时间被删除评论的人的MID
_prefixAdminDelMid = "mid_%d"
// f_{折叠类型,根评论还是评论区}_{评论区ID或者根评论ID}
_foldedReplyFmt = "f_%s_%d"
// dialog
_prefixDialogIdx = "d_%d"
_maxCount = 20000
)
func keyFolderIdx(kind string, ID int64) string {
return fmt.Sprintf(_foldedReplyFmt, kind, ID)
}
func keyDelMid(mid int64) string {
return fmt.Sprintf(_prefixAdminDelMid, mid)
}
// KeyMainIdx ...
func keyMainIdx(oid int64, tp, sort int32) 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)
}
// keyDialogIdx ...
func keyDialogIdx(dialog int64) string {
return fmt.Sprintf(_prefixDialogIdx, dialog)
}
// KeyRootIdx ...
func keyRootIdx(root int64) string {
return _prefixNewRootIdx + strconv.FormatInt(root, 10)
}
func keyIdx(oid int64, tp, sort int32) 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 keyNewRootIdx(rpID int64) string {
return _prefixNewRootIdx + strconv.FormatInt(rpID, 10)
}
func keyAuditIdx(oid int64, tp int32) string {
return fmt.Sprintf(_prefixAuditIdx, oid, tp)
}
func (d *Dao) ExsistsDelMid(c context.Context, mid int64) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := keyDelMid(mid)
if ok, err = redis.Bool(conn.Do("EXISTS", key)); err != nil {
log.Error("conn.Do(EXISTS, %s) error(%v)", key, err)
}
return
}
func (d *Dao) SetDelMid(c context.Context, mid int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
// 15天内
if err = conn.Send("SETEX", keyDelMid(mid), 86400*15, 1); err != nil {
log.Error("redis SETEX(%s) error(%v)", keyDelMid(mid), err)
}
return
}
// ExpireIndex set expire time for index.
func (d *Dao) ExpireIndex(c context.Context, oid int64, typ, sort int32) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyIdx(oid, typ, sort), d.redisExpire)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// ExpireNewChildIndex set expire time for root's index.
func (d *Dao) ExpireNewChildIndex(c context.Context, root int64) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyNewRootIdx(root), d.redisExpire)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// TopChildReply ...
func (d *Dao) TopChildReply(c context.Context, root, child int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("ZADD", keyNewRootIdx(root), 0, child); err != nil {
return
}
return
}
// CountReplies get count of reply.
func (d *Dao) CountReplies(c context.Context, oid int64, tp, sort int32) (count int, err error) {
key := keyIdx(oid, tp, sort)
conn := d.redis.Get(c)
defer conn.Close()
if count, err = redis.Int(conn.Do("ZCARD", key)); err != nil {
log.Error("CountReplies error(%v)", err)
}
return
}
// MinScore get the lowest score from sorted set
func (d *Dao) MinScore(c context.Context, oid int64, tp int32, sort int32) (score int32, err error) {
key := keyIdx(oid, tp, sort)
conn := d.redis.Get(c)
defer conn.Close()
values, err := redis.Values(conn.Do("ZRANGE", key, 0, 0, "WITHSCORES"))
if err != nil {
log.Error("conn.Do(ZREVRANGE, %s) error(%v)", key, err)
return
}
if len(values) != 2 {
err = fmt.Errorf("redis zrange items(%v) length not 2", values)
return
}
var id int64
redis.Scan(values, &id, &score)
return
}
// AddFloorIndex add index by floor.
func (d *Dao) AddFloorIndex(c context.Context, rp *model.Reply) (err error) {
min, err := d.MinScore(c, rp.Oid, rp.Type, model.SortByFloor)
if err != nil {
log.Error("s.dao.Redis.AddFloorIndex failed , oid(%d) type(%d) err(%v)", rp.Oid, rp.Type, err)
} else if rp.Floor <= min {
return
}
key := keyIdx(rp.Oid, rp.Type, model.SortByFloor)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", key, rp.Floor, rp.ID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisExpire); 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 _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
return
}
return
}
// AddCountIndex add index by count.
func (d *Dao) AddCountIndex(c context.Context, rp *model.Reply) (err error) {
if rp.IsTop() {
return
}
var count int
if count, err = d.CountReplies(c, rp.Oid, rp.Type, model.SortByCount); err != nil {
return
} else if count >= _maxCount {
var min int32
if min, err = d.MinScore(c, rp.Oid, rp.Type, model.SortByCount); err != nil {
return
}
if rp.RCount <= min {
return
}
}
key := keyIdx(rp.Oid, rp.Type, model.SortByCount)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", key, int64(rp.RCount)<<32|(int64(rp.Floor)&0xFFFFFFFF), rp.ID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisExpire); 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 _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
return
}
return
}
// AddLikeIndex add index by like.
func (d *Dao) AddLikeIndex(c context.Context, rp *model.Reply, rpt *model.Report) (err error) {
if rp.IsTop() {
return
}
var rptCn int32
if rpt != nil {
rptCn = rpt.Count
}
score := int64((float32(rp.Like+model.WeightLike) / float32(rp.Hate+model.WeightHate+rptCn)) * 100)
score = score<<32 | (int64(rp.RCount) & 0xFFFFFFFF)
var count int
if count, err = d.CountReplies(c, rp.Oid, rp.Type, model.SortByLike); err != nil {
return
} else if count >= _maxCount {
var min int32
if min, err = d.MinScore(c, rp.Oid, rp.Type, model.SortByLike); err != nil {
return
}
if score <= int64(min) {
return
}
}
key := keyIdx(rp.Oid, rp.Type, model.SortByLike)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", key, score, rp.ID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisExpire); 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 _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
return
}
// DelIndexBySort delete index by sort.
func (d *Dao) DelIndexBySort(c context.Context, rp *model.Reply, sort int32) (err error) {
key := keyIdx(rp.Oid, rp.Type, sort)
conn := d.redis.Get(c)
defer conn.Close()
if _, err = conn.Do("ZREM", key, rp.ID); err != nil {
log.Error("conn.Do(ZREM) error(%v)", err)
}
return
}
// DelReplyIndex delete reply index.
func (d *Dao) DelReplyIndex(c context.Context, rp *model.Reply) (err error) {
var (
key string
n int
)
conn := d.redis.Get(c)
defer conn.Close()
if rp.Root == 0 {
key = keyIdx(rp.Oid, rp.Type, model.SortByFloor)
err = conn.Send("ZREM", key, rp.ID)
key = keyIdx(rp.Oid, rp.Type, model.SortByCount)
err = conn.Send("ZREM", key, rp.ID)
key = keyIdx(rp.Oid, rp.Type, model.SortByLike)
err = conn.Send("ZREM", key, rp.ID)
n += 3
} else {
if rp.Dialog != 0 {
key = keyDialogIdx(rp.Dialog)
err = conn.Send("ZREM", key, rp.ID)
n++
}
key = keyNewRootIdx(rp.Root)
err = conn.Send("ZREM", key, rp.ID)
n++
}
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
}
for i := 0; i < n; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive error(%v)", err)
return
}
}
return
}
// AddNewChildIndex add root reply index by floor.
func (d *Dao) AddNewChildIndex(c context.Context, rp *model.Reply) (err error) {
key := keyNewRootIdx(rp.Root)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", key, rp.Floor, rp.ID); err != nil {
log.Error("conn.Send error(%v)", err)
return
}
if err = conn.Send("EXPIRE", key, d.redisExpire); 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 _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
return
}
// DelAuditIndex delete audit reply cache.
func (d *Dao) DelAuditIndex(c context.Context, rp *model.Reply) (err error) {
key := keyAuditIdx(rp.Oid, rp.Type)
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZREM", key, rp.ID); err != nil {
log.Error("conn.Send(ZREM %s) error(%v)", key, err)
return
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
return
}
// RemReplyFromRedis ...
func (d *Dao) RemReplyFromRedis(c context.Context, keyMapping map[string][]int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
count := 0
for key, rpIDs := range keyMapping {
if len(rpIDs) == 0 {
continue
}
args := make([]interface{}, 0, 1+len(rpIDs))
args = append(args, key)
for _, rpID := range rpIDs {
args = append(args, rpID)
}
if err = conn.Send("ZREM", args...); err != nil {
log.Error("conn.Send(ZREM %s) error(%v)", key, err)
return
}
count++
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// ExpireFolder ...
func (d *Dao) ExpireFolder(c context.Context, kind string, ID int64) (ok bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if ok, err = redis.Bool(conn.Do("EXPIRE", keyFolderIdx(kind, ID), d.redisExpire)); err != nil {
log.Error("conn.Do(EXPIRE) error(%v)", err)
}
return
}
// AddFolder ...
func (d *Dao) AddFolder(c context.Context, keyMapping map[string][]*model.Reply) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
count := 0
for key, rps := range keyMapping {
var args []interface{}
args = append(args, key)
for _, rp := range rps {
args = append(args, rp.Floor)
args = append(args, rp.ID)
}
if err = conn.Send("ZADD", args...); err != nil {
log.Error("conn.Send(ZADD %s) error(%v)", key, err)
return
}
count++
if err = conn.Send("EXPIRE", key, d.redisExpire); err != nil {
return
}
count++
}
if err = conn.Flush(); err != nil {
log.Error("conn.Flush error(%v)", err)
return
}
for i := 0; i < count; i++ {
if _, err = conn.Receive(); err != nil {
log.Error("conn.Receive() error(%v)", err)
return
}
}
return
}
// RemFolder ...
func (d *Dao) RemFolder(c context.Context, kind string, ID, rpID int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := keyFolderIdx(kind, ID)
if _, err = conn.Do("ZREM", key, rpID); err != nil {
log.Error("conn.Do(ZREM) error(%v)", err)
}
return
}

View File

@@ -0,0 +1,233 @@
package dao
import (
"context"
"go-common/app/admin/main/reply/model"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaokeyIdx(t *testing.T) {
convey.Convey("keyIdx", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int32(0)
sort = int32(0)
)
ctx.Convey("When everything gose 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 TestDaokeyNewRootIdx(t *testing.T) {
convey.Convey("keyNewRootIdx", t, func(ctx convey.C) {
var (
rpID = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
p1 := keyNewRootIdx(rpID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaokeyAuditIdx(t *testing.T) {
convey.Convey("keyAuditIdx", t, func(ctx convey.C) {
var (
oid = int64(0)
tp = int32(0)
)
ctx.Convey("When everything gose 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 TestDaoExpireIndex(t *testing.T) {
convey.Convey("ExpireIndex", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
typ = int32(0)
sort = int32(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
ok, err := _d.ExpireIndex(c, oid, typ, 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 TestDaoExpireNewChildIndex(t *testing.T) {
convey.Convey("ExpireNewChildIndex", t, func(ctx convey.C) {
var (
c = context.Background()
root = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
ok, err := _d.ExpireNewChildIndex(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 TestDaoCountReplies(t *testing.T) {
convey.Convey("CountReplies", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int32(0)
sort = int32(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
count, err := _d.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 TestDaoMinScore(t *testing.T) {
convey.Convey("MinScore", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
tp = int32(0)
sort = int32(1)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
score, err := _d.MinScore(c, oid, tp, sort)
ctx.Convey("Then err should be nil.score should not be nil.", func(ctx convey.C) {
if err != nil {
ctx.So(err, convey.ShouldNotBeNil)
}
ctx.So(score, convey.ShouldEqual, 0)
})
})
})
}
func TestDaoAddFloorIndex(t *testing.T) {
convey.Convey("AddFloorIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.AddFloorIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddCountIndex(t *testing.T) {
convey.Convey("AddCountIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.AddCountIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddLikeIndex(t *testing.T) {
convey.Convey("AddLikeIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
rpt = &model.Report{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.AddLikeIndex(c, rp, rpt)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelIndexBySort(t *testing.T) {
convey.Convey("DelIndexBySort", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
sort = int32(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.DelIndexBySort(c, rp, sort)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelReplyIndex(t *testing.T) {
convey.Convey("DelReplyIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.DelReplyIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddNewChildIndex(t *testing.T) {
convey.Convey("AddNewChildIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.AddNewChildIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelAuditIndex(t *testing.T) {
convey.Convey("DelAuditIndex", t, func(ctx convey.C) {
var (
c = context.Background()
rp = &model.Reply{}
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.DelAuditIndex(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,182 @@
package dao
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_inSQL = "INSERT IGNORE INTO reply_%d (id,oid,type,mid,root,parent,floor,state,attr,ctime,mtime) VALUES(?,?,?,?,?,?,?,?,?,?,?)"
_selReplySQL = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,hate,floor,state,attr,ctime,mtime FROM reply_%d WHERE id=?"
_txSelReplySQLForUpdate = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,hate,floor,state,attr,ctime,mtime FROM reply_%d WHERE id=? for update"
_selRepliesSQL = "SELECT id,oid,type,mid,root,parent,dialog,count,rcount,`like`,floor,state,attr,ctime,mtime FROM reply_%d WHERE id IN (%s)"
_incrRCountSQL = "UPDATE reply_%d SET rcount=rcount+1,mtime=? WHERE id=?"
_upReplyStateSQL = "UPDATE reply_%d SET state=?,mtime=? WHERE id=?"
_decrCntSQL = "UPDATE reply_%d SET rcount=rcount-1,mtime=? WHERE id=? AND rcount > 0"
_upAttrSQL = "UPDATE reply_%d SET attr=?,mtime=? WHERE id=?"
_selExportRepliesSQL = "SELECT id,oid,type,mid,root,parent,count,rcount,`like`,hate,floor,state,attr,T1.ctime,message from reply_%d as T1 inner join reply_content_%d as T2 on id=rpid where oid=? and type=? and T1.ctime>=? and T2.ctime<=?"
)
// InsertReply insert reply by transaction.
func (d *Dao) InsertReply(c context.Context, r *model.Reply) (id int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_inSQL, hit(r.Oid)), r.ID, r.Oid, r.Type, r.Mid, r.Root, r.Parent, r.Floor, r.State, r.Attr, r.CTime, r.MTime)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.LastInsertId()
}
// Reply get a reply from database.
func (d *Dao) Reply(c context.Context, oid, rpID int64) (r *model.Reply, err error) {
r = new(model.Reply)
row := d.db.QueryRow(c, fmt.Sprintf(_selReplySQL, hit(oid)), rpID)
if err = row.Scan(&r.ID, &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 == xsql.ErrNoRows {
r = nil
err = nil
}
}
return
}
// Replies get replies by reply ids.
func (d *Dao) Replies(c context.Context, oids []int64, rpIds []int64) (rpMap map[int64]*model.Reply, err error) {
hitMap := make(map[int64][]int64)
for i, oid := range oids {
hitMap[hit(oid)] = append(hitMap[hit(oid)], rpIds[i])
}
rpMap = make(map[int64]*model.Reply, len(rpIds))
for hit, ids := range hitMap {
var rows *xsql.Rows
rows, err = d.db.Query(c, fmt.Sprintf(_selRepliesSQL, hit, xstr.JoinInts(ids)))
if err != nil {
return
}
for rows.Next() {
r := &model.Reply{}
if err = rows.Scan(&r.ID, &r.Oid, &r.Type, &r.Mid, &r.Root, &r.Parent, &r.Dialog, &r.Count, &r.RCount, &r.Like, &r.Floor, &r.State, &r.Attr, &r.CTime, &r.MTime); err != nil {
rows.Close()
return
}
rpMap[r.ID] = r
}
if err = rows.Err(); err != nil {
rows.Close()
return
}
rows.Close()
}
return
}
// UpdateReplyState update reply state.
func (d *Dao) UpdateReplyState(c context.Context, oid, rpID int64, state int32) (rows int64, err error) {
now := time.Now()
res, err := d.db.Exec(c, fmt.Sprintf(_upReplyStateSQL, hit(oid)), state, now, rpID)
if err != nil {
return
}
return res.RowsAffected()
}
// TxUpdateReplyState tx update reply state.
func (d *Dao) TxUpdateReplyState(tx *xsql.Tx, oid, rpID int64, state int32, mtime time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_upReplyStateSQL, hit(oid)), state, mtime, rpID)
if err != nil {
return
}
return res.RowsAffected()
}
// TxIncrReplyRCount incr rcount of reply by transaction.
func (d *Dao) TxIncrReplyRCount(tx *xsql.Tx, oid, rpID int64, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_incrRCountSQL, hit(oid)), now, rpID)
if err != nil {
return
}
return res.RowsAffected()
}
// TxDecrReplyRCount decr rcount of reply by transaction.
func (d *Dao) TxDecrReplyRCount(tx *xsql.Tx, oid, rpID int64, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_decrCntSQL, hit(oid)), now, rpID)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// TxUpReplyAttr update subject attr.
func (d *Dao) TxUpReplyAttr(tx *xsql.Tx, oid, rpID int64, attr uint32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_upAttrSQL, hit(oid)), attr, now, rpID)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// TxReplyForUpdate get a reply from database.
func (d *Dao) TxReplyForUpdate(tx *xsql.Tx, oid, rpID int64) (r *model.Reply, err error) {
r = new(model.Reply)
row := tx.QueryRow(fmt.Sprintf(_txSelReplySQLForUpdate, hit(oid)), rpID)
if err = row.Scan(&r.ID, &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 == xsql.ErrNoRows {
r = nil
err = nil
}
}
return
}
// TxReply get a reply from database.
func (d *Dao) TxReply(tx *xsql.Tx, oid, rpID int64) (r *model.Reply, err error) {
r = new(model.Reply)
row := tx.QueryRow(fmt.Sprintf(_selReplySQL, hit(oid)), rpID)
if err = row.Scan(&r.ID, &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 == xsql.ErrNoRows {
r = nil
err = nil
}
}
return
}
// ExportReplies export replies
func (d *Dao) ExportReplies(c context.Context, oid, mid int64, tp int8, state string, startTime, endTime time.Time) (data [][]string, err error) {
var rows *xsql.Rows
title := []string{"id", "oid", "type", "mid", "root", "parent", "count", "rcount", "like", "hate", "floor", "state", "attr", "ctime", "message"}
data = append(data, title)
query := fmt.Sprintf(_selExportRepliesSQL, hit(oid), hit(oid))
if state != "" {
query += fmt.Sprintf(" and state in (%s)", state)
}
if mid != 0 {
query += fmt.Sprintf(" and mid=%d", mid)
}
rows, err = d.dbSlave.Query(c, query, oid, tp, startTime, endTime)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
r := &model.ExportedReply{}
if err = rows.Scan(&r.ID, &r.Oid, &r.Type, &r.Mid, &r.Root, &r.Parent, &r.Count, &r.RCount, &r.Like, &r.Hate, &r.Floor, &r.State, &r.Attr, &r.CTime, &r.Message); err != nil {
rows.Close()
return
}
data = append(data, r.String())
}
if err = rows.Err(); err != nil {
return
}
return
}

View File

@@ -0,0 +1,71 @@
package dao
import (
"context"
"fmt"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
"go-common/library/xstr"
"time"
)
const _contSharding int64 = 200
const (
_selReplyContentSQL = "SELECT rpid,message,ats,ip,plat,device,ctime,mtime FROM reply_content_%d WHERE rpid=?"
_selReplyContentsSQL = "SELECT rpid,message,ats,ip,plat,device,ctime,mtime FROM reply_content_%d WHERE rpid IN (%s)"
_selContSQL = "SELECT rpid,message,ats,ip,plat,device FROM reply_content_%d WHERE rpid=?"
_selContsSQL = "SELECT rpid,message,ats,ip,plat,device FROM reply_content_%d WHERE rpid IN (%s)"
_upContMsgSQL = "UPDATE reply_content_%d SET message=?,mtime=? WHERE rpid=?"
)
// UpReplyContent update reply content's message.
func (d *Dao) UpReplyContent(c context.Context, oid int64, rpID int64, msg string, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_upContMsgSQL, hit(oid)), msg, now, rpID)
if err != nil {
return
}
return res.RowsAffected()
}
// ReplyContent get a ReplyContent from database.
func (d *Dao) ReplyContent(c context.Context, oid, rpID int64) (rc *model.ReplyContent, err error) {
rc = new(model.ReplyContent)
row := d.db.QueryRow(c, fmt.Sprintf(_selReplyContentSQL, hit(oid)), rpID)
if err = row.Scan(&rc.ID, &rc.Message, &rc.Ats, &rc.IP, &rc.Plat, &rc.Device, &rc.CTime, &rc.MTime); err != nil {
if err == xsql.ErrNoRows {
rc = nil
err = nil
}
}
return
}
// ReplyContents get reply contents by ids.
func (d *Dao) ReplyContents(c context.Context, oids []int64, rpIds []int64) (rcMap map[int64]*model.ReplyContent, err error) {
hitMap := make(map[int64][]int64)
for i, oid := range oids {
hitMap[hit(oid)] = append(hitMap[hit(oid)], rpIds[i])
}
rcMap = make(map[int64]*model.ReplyContent, len(rpIds))
for hit, ids := range hitMap {
var rows *xsql.Rows
rows, err = d.db.Query(c, fmt.Sprintf(_selReplyContentsSQL, hit, xstr.JoinInts(ids)))
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
rc := &model.ReplyContent{}
if err = rows.Scan(&rc.ID, &rc.Message, &rc.Ats, &rc.IP, &rc.Plat, &rc.Device, &rc.CTime, &rc.MTime); err != nil {
return
}
rcMap[rc.ID] = rc
}
if err = rows.Err(); err != nil {
return
}
}
return
}

View File

@@ -0,0 +1,82 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/app/admin/main/reply/model"
xtime "go-common/library/time"
. "github.com/smartystreets/goconvey/convey"
)
func deleteReplyContent(d *Dao, oid int64, rpid int64) error {
_delSQL := "Delete from reply_content_%d where rpid = ?"
_, err := d.db.Exec(context.Background(), fmt.Sprintf(_delSQL, hit(oid)), rpid)
if err != nil {
return err
}
return nil
}
func TestReplyContent(t *testing.T) {
_inContSQL := "INSERT IGNORE INTO reply_content_%d (rpid,message,ats,ip,plat,device,version,ctime,mtime) VALUES(?,?,?,?,?,?,?,?,?)"
d := _d
c := context.Background()
now := time.Now()
rc := model.ReplyContent{
ID: 1,
Message: "test",
Device: "iphone",
Version: "beta",
CTime: xtime.Time(now.Unix()),
MTime: xtime.Time(now.Unix()),
}
_, err := d.db.Exec(c, fmt.Sprintf(_inContSQL, hit(6)), rc.ID, rc.Message, rc.Ats, rc.IP, rc.Plat, rc.Device, rc.Version, rc.CTime, rc.MTime)
if err != nil {
t.Logf("insert reply content %v", err)
t.FailNow()
}
defer deleteReplyContent(d, 6, 1)
rc2 := model.ReplyContent{
ID: 2,
Message: "test2",
Device: "iphone2",
Version: "beta2",
CTime: xtime.Time(now.Unix()),
MTime: xtime.Time(now.Unix()),
}
_, err = d.db.Exec(c, fmt.Sprintf(_inContSQL, hit(86)), rc2.ID, rc2.Message, rc2.Ats, rc2.IP, rc2.Plat, rc2.Device, rc2.Version, rc2.CTime, rc2.MTime)
if err != nil {
t.Logf("insert reply content %v", err)
t.FailNow()
}
defer deleteReplyContent(d, 86, 2)
Convey("reply_content test", t, WithDao(func(d *Dao) {
Convey("get update content", WithDao(func(d *Dao) {
now = time.Now()
rows, err := d.UpReplyContent(c, 86, 2, "test3", now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
}))
Convey("get reply_content", WithDao(func(d *Dao) {
content, err := d.ReplyContent(c, 86, 2)
So(err, ShouldBeNil)
So(content, ShouldNotBeNil)
So(content.ID, ShouldEqual, 2)
So(content.CTime, ShouldEqual, now.Unix())
So(content.Message, ShouldEqual, "test3")
}))
Convey("find reply_contents", WithDao(func(d *Dao) {
cMap, err := d.ReplyContents(c, []int64{6, 86}, []int64{1, 2})
So(err, ShouldBeNil)
So(len(cMap), ShouldEqual, 2)
So(cMap[1].ID, ShouldEqual, 1)
So(cMap[1].Message, ShouldEqual, "test")
So(cMap[2].ID, ShouldEqual, 2)
So(cMap[2].Message, ShouldEqual, "test3")
}))
}))
}

View File

@@ -0,0 +1,144 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/app/admin/main/reply/model"
xtime "go-common/library/time"
. "github.com/smartystreets/goconvey/convey"
)
func deleteReply(d *Dao, oid int64, rpid int64) error {
_delSQL := "Delete from reply_%d where id = ?"
_, err := d.db.Exec(context.Background(), fmt.Sprintf(_delSQL, hit(oid)), rpid)
if err != nil {
return err
}
return nil
}
func TestDataReply(t *testing.T) {
d := _d
c := context.Background()
now := time.Now()
r := model.Reply{
ID: 2,
Oid: 86,
Type: 1,
Mid: 2233,
Floor: 1,
CTime: xtime.Time(now.Unix()),
MTime: xtime.Time(now.Unix()),
}
d.InsertReply(c, &r)
defer deleteReply(d, r.Oid, r.ID)
r2 := model.Reply{
ID: 1,
Oid: 6,
Type: 1,
Mid: 2233,
Floor: 1,
CTime: xtime.Time(now.Unix()),
MTime: xtime.Time(now.Unix()),
}
d.InsertReply(c, &r2)
defer deleteReply(d, r2.Oid, r2.ID)
Convey("reply test", t, WithDao(func(d *Dao) {
Convey("get reply", WithDao(func(d *Dao) {
rp, err := d.Reply(c, 86, r.ID)
So(err, ShouldBeNil)
So(rp, ShouldNotBeNil)
So(rp.ID, ShouldEqual, r.ID)
So(rp.Mid, ShouldEqual, 2233)
}))
Convey("increase reply RCount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
now = time.Now()
rows, err := d.TxIncrReplyRCount(t, 86, r.ID, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
rp, err := d.Reply(c, 86, r.ID)
So(err, ShouldBeNil)
So(rp, ShouldNotBeNil)
So(rp.RCount, ShouldEqual, 1)
So(rp.MTime, ShouldEqual, now.Unix())
Convey("decrease reply RCount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
now = time.Now()
rows, err := d.TxDecrReplyRCount(t, 86, r.ID, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
rp, err := d.Reply(c, 86, r.ID)
So(err, ShouldBeNil)
So(rp, ShouldNotBeNil)
So(rp.RCount, ShouldEqual, 0)
So(rp.MTime, ShouldEqual, now.Unix())
}))
}))
Convey("set reply attr", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
now = time.Now()
rows, err := d.TxUpReplyAttr(t, 86, r.ID, 3, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
rp, err := d.Reply(c, 86, r.ID)
So(err, ShouldBeNil)
So(rp, ShouldNotBeNil)
So(rp.Attr, ShouldEqual, 3)
So(rp.MTime, ShouldEqual, now.Unix())
}))
Convey("set reply state", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
now = time.Now()
rows, err := d.TxUpdateReplyState(t, 86, r.ID, 2, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
rp, err := d.Reply(c, 86, r.ID)
So(err, ShouldBeNil)
So(rp, ShouldNotBeNil)
So(rp.State, ShouldEqual, 2)
So(rp.MTime, ShouldEqual, now.Unix())
}))
Convey("get replies", WithDao(func(d *Dao) {
rpMap, err := d.Replies(c, []int64{6, 86}, []int64{1, 2})
So(err, ShouldBeNil)
So(len(rpMap), ShouldEqual, 2)
So(rpMap[1].ID, ShouldEqual, 1)
So(rpMap[1].Oid, ShouldEqual, 6)
So(rpMap[1].Mid, ShouldEqual, 2233)
So(rpMap[2].ID, ShouldEqual, 2)
So(rpMap[2].Oid, ShouldEqual, 86)
So(rpMap[2].Mid, ShouldEqual, 2233)
}))
Convey("export replies test", WithDao(func(d *Dao) {
var (
ctime time.Time
etime time.Time
err error
)
ctime, err = time.Parse("2006-01-02", "2008-03-03")
So(err, ShouldBeNil)
etime, err = time.Parse("2006-01-02", "2018-03-03")
So(err, ShouldBeNil)
_, err = d.ExportReplies(c, 3400, 0, 3, "", ctime, etime)
So(err, ShouldBeNil)
}))
}))
}

View File

@@ -0,0 +1,207 @@
package dao
import (
"context"
sql "database/sql"
"fmt"
"time"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
)
const (
// report
_upRptStateSQL = "UPDATE reply_report_%d SET state=?,mtime=? WHERE rpid IN (%s)"
_upRptReasonSQL = "UPDATE reply_report_%d SET state=?,reason=?,content=?,mtime=? WHERE rpid IN (%s)"
_upRptAttrBitSQL = "UPDATE reply_report_%d SET attr=attr&(~(1<<?))|(?<<?),mtime=? WHERE rpid IN (%s)"
_selReportSQL = "SELECT oid,type,rpid,mid,reason,content,count,score,state,attr,ctime,mtime FROM reply_report_%d WHERE rpid=?"
_selReportsSQL = "SELECT oid,type,rpid,mid,reason,content,count,score,state,attr,ctime,mtime FROM reply_report_%d WHERE rpid IN(%s)"
_selReportOidsSQL = "SELECT oid,type,rpid,mid,reason,content,count,score,state,attr,ctime,mtime FROM reply_report_%d WHERE oid IN(?) and type=?"
// report_user
_setRptUserStateSQL = "UPDATE reply_report_user_%d SET state=?,mtime=? WHERE rpid=?"
_selRptUsersSQL = "SELECT oid,type,rpid,mid,reason,content,state,ctime,mtime FROM reply_report_user_%d WHERE rpid=? and state=?"
)
// Report get a reply report.
func (d *Dao) Report(c context.Context, oid, rpID int64) (rpt *model.Report, err error) {
row := d.db.QueryRow(c, fmt.Sprintf(_selReportSQL, hit(oid)), rpID)
rpt = new(model.Report)
if err = row.Scan(&rpt.Oid, &rpt.Type, &rpt.RpID, &rpt.Mid, &rpt.Reason, &rpt.Content, &rpt.Count, &rpt.Score, &rpt.State, &rpt.Attr, &rpt.CTime, &rpt.MTime); err != nil {
if err == xsql.ErrNoRows {
rpt = nil
err = nil
}
}
return
}
// Reports return report map by oid.
func (d *Dao) Reports(c context.Context, oids, rpIDs []int64) (res map[int64]*model.Report, err error) {
hits := make(map[int64][]int64)
for i, oid := range oids {
hit := hit(oid)
hits[hit] = append(hits[hit], rpIDs[i])
}
res = make(map[int64]*model.Report)
for hit, ids := range hits {
var rows *xsql.Rows
if rows, err = d.db.Query(c, fmt.Sprintf(_selReportsSQL, hit, xstr.JoinInts(ids))); err != nil {
return
}
for rows.Next() {
rpt := new(model.Report)
if err = rows.Scan(&rpt.Oid, &rpt.Type, &rpt.RpID, &rpt.Mid, &rpt.Reason, &rpt.Content, &rpt.Count, &rpt.Score, &rpt.State, &rpt.Attr, &rpt.CTime, &rpt.MTime); err != nil {
rows.Close()
return
}
res[rpt.RpID] = rpt
}
if err = rows.Err(); err != nil {
rows.Close()
return
}
rows.Close()
}
return
}
// ReportByOids return report map by oid.
func (d *Dao) ReportByOids(c context.Context, typ int32, oids ...int64) (res map[int64]*model.Report, err error) {
hits := make(map[int64][]int64)
for _, oid := range oids {
hit := hit(oid)
hits[hit] = append(hits[hit], oid)
}
res = make(map[int64]*model.Report)
for hit, oids := range hits {
var rows *xsql.Rows
if rows, err = d.db.Query(c, fmt.Sprintf(_selReportOidsSQL, hit), xstr.JoinInts(oids), typ); err != nil {
return
}
for rows.Next() {
rpt := new(model.Report)
if err = rows.Scan(&rpt.Oid, &rpt.Type, &rpt.RpID, &rpt.Mid, &rpt.Reason, &rpt.Content, &rpt.Count, &rpt.Score, &rpt.State, &rpt.Attr, &rpt.CTime, &rpt.MTime); err != nil {
rows.Close()
return
}
res[rpt.RpID] = rpt
}
if err = rows.Err(); err != nil {
rows.Close()
return
}
rows.Close()
}
return
}
// UpReportsState update the report state.
func (d *Dao) UpReportsState(c context.Context, oids, rpIDs []int64, state int32, now time.Time) (rows int64, err error) {
hitMap := make(map[int64][]int64)
for i, oid := range oids {
hitMap[hit(oid)] = append(hitMap[hit(oid)], rpIDs[i])
}
for hit, ids := range hitMap {
var res sql.Result
res, err = d.db.Exec(c, fmt.Sprintf(_upRptStateSQL, hit, xstr.JoinInts(ids)), state, now)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
var row int64
row, err = res.RowsAffected()
if err != nil {
log.Error("res.RowsAffected error(%v)", err)
return
}
rows += row
}
return
}
// UpReportsStateWithReason update the report state.
func (d *Dao) UpReportsStateWithReason(c context.Context, oids, rpIDs []int64, state, reason int32, content string, now time.Time) (rows int64, err error) {
hitMap := make(map[int64][]int64)
for i, oid := range oids {
hitMap[hit(oid)] = append(hitMap[hit(oid)], rpIDs[i])
}
for hit, ids := range hitMap {
var res sql.Result
res, err = d.db.Exec(c, fmt.Sprintf(_upRptReasonSQL, hit, xstr.JoinInts(ids)), state, reason, content, now)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
var row int64
row, err = res.RowsAffected()
if err != nil {
log.Error("res.RowsAffected error(%v)", err)
return
}
rows += row
}
return
}
// UpReportsAttrBit update the report attr.
func (d *Dao) UpReportsAttrBit(c context.Context, oids, rpIDs []int64, bit uint32, val uint32, now time.Time) (rows int64, err error) {
hitMap := make(map[int64][]int64)
for i, oid := range oids {
hitMap[hit(oid)] = append(hitMap[hit(oid)], rpIDs[i])
}
for hit, ids := range hitMap {
var res sql.Result
res, err = d.db.Exec(c, fmt.Sprintf(_upRptAttrBitSQL, hit, xstr.JoinInts(ids)), bit, val, bit, now)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
var row int64
row, err = res.RowsAffected()
if err != nil {
log.Error("res.RowsAffected error(%v)", err)
return
}
rows += row
}
return
}
// ReportUsers return a report users from mysql.
func (d *Dao) ReportUsers(c context.Context, oid int64, tp int32, rpID int64) (res map[int64]*model.ReportUser, err error) {
rows, err := d.db.Query(c, fmt.Sprintf(_selRptUsersSQL, hit(oid)), rpID, model.ReportUserStateNew)
if err != nil {
log.Error("db.Query error(%v)", err)
return
}
defer rows.Close()
res = make(map[int64]*model.ReportUser)
for rows.Next() {
rpt := &model.ReportUser{}
if err = rows.Scan(&rpt.Oid, &rpt.Type, &rpt.RpID, &rpt.Mid, &rpt.Reason, &rpt.Content, &rpt.State, &rpt.CTime, &rpt.MTime); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
res[rpt.Mid] = rpt
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
return
}
return
}
// SetUserReported set a user report state by rpID.
func (d *Dao) SetUserReported(c context.Context, oid int64, tp int32, rpID int64, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_setRptUserStateSQL, hit(oid)), model.ReportUserStateReported, now, rpID)
if err != nil {
log.Error("db.Exec error(%v)", err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,57 @@
package dao
import (
"context"
"testing"
"time"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestDataReport(t *testing.T) {
var (
typ = int32(12)
oid = int64(909)
rpID = int64(111845851)
oids = []int64{909}
rpIDs = []int64{111845851}
now = time.Now()
c = context.Background()
)
Convey("report info", t, WithDao(func(d *Dao) {
rpt, err := d.Report(c, oid, rpID)
So(err, ShouldBeNil)
So(rpt.RpID, ShouldEqual, rpID)
rpts, err := d.Reports(c, oids, rpIDs)
So(err, ShouldBeNil)
So(len(rpts), ShouldEqual, 1)
rptm, err := d.ReportByOids(c, typ, oids...)
So(err, ShouldBeNil)
So(len(rptm), ShouldNotEqual, 0)
}))
Convey("report state update", t, WithDao(func(d *Dao) {
_, err := d.UpReportsState(c, oids, rpIDs, model.ReportStateDelete, now)
So(err, ShouldBeNil)
rpt, err := d.Report(c, oids[0], rpIDs[0])
So(err, ShouldBeNil)
So(rpt.State, ShouldEqual, model.ReportStateDelete)
}))
Convey("report reason update", t, WithDao(func(d *Dao) {
_, err := d.UpReportsStateWithReason(c, oids, rpIDs, model.ReportStateDelete, 1, "test", now)
So(err, ShouldBeNil)
rpt, err := d.Report(c, oids[0], rpIDs[0])
So(err, ShouldBeNil)
So(rpt.State, ShouldEqual, model.ReportStateDelete)
So(rpt.Reason, ShouldEqual, 1)
So(rpt.Content, ShouldEqual, "test")
}))
Convey("report attr update", t, WithDao(func(d *Dao) {
_, err := d.UpReportsAttrBit(c, oids, rpIDs, model.ReportAttrTransferred, model.AttrYes, now)
So(err, ShouldBeNil)
rpt, err := d.Report(c, oids[0], rpIDs[0])
So(err, ShouldBeNil)
So(rpt.AttrVal(model.ReportAttrTransferred), ShouldEqual, model.AttrYes)
}))
}

View File

@@ -0,0 +1 @@
package dao

View File

@@ -0,0 +1,33 @@
package dao
import (
"context"
"strconv"
"time"
"go-common/library/log"
)
type statMsg struct {
ID int64 `json:"id"`
Timestamp int64 `json:"timestamp"`
Count int32 `json:"count"`
Type string `json:"type"`
}
// SendStats update stat.
func (d *Dao) SendStats(c context.Context, typ int32, oid int64, cnt int32) (err error) {
// new databus stats
if name, ok := d.statsTypes[typ]; ok {
m := &statMsg{
ID: oid,
Type: name,
Count: cnt,
Timestamp: time.Now().Unix(),
}
if err = d.statsBus.Send(c, strconv.FormatInt(oid, 10), m); err != nil {
log.Error("d.databus.Send(%d,%d,%d) error(%v)", typ, oid, cnt, err)
}
}
return
}

View File

@@ -0,0 +1,21 @@
package dao
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestStats(t *testing.T) {
var (
oid = int64(1)
typ = int32(1)
cnt = int32(1)
c = context.Background()
)
Convey("test stats count", t, WithDao(func(d *Dao) {
err := d.SendStats(c, typ, oid, cnt)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,282 @@
package dao
import (
"context"
"fmt"
"time"
"go-common/app/admin/main/reply/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/sync/errgroup"
"go-common/library/xstr"
"sync"
)
const _subShard int64 = 50
const (
_selSubjectSQL = "SELECT id,oid,type,mid,count,rcount,acount,state,attr,ctime,mtime FROM reply_subject_%d WHERE oid=? AND type=?"
_selSubjectForUpdateSQL = "SELECT id,oid,type,mid,count,rcount,acount,state,attr,ctime,mtime FROM reply_subject_%d WHERE oid=? AND type=? FOR UPDATE"
_selSubjectsSQL = "SELECT id,oid,type,mid,count,rcount,acount,state,attr,ctime,mtime FROM reply_subject_%d WHERE oid IN (%s) AND type=?"
_inSubjectSQL = "INSERT INTO reply_subject_%d (oid,type,mid,state,ctime,mtime) VALUES(?,?,?,?,?,?) ON DUPLICATE KEY UPDATE state=?,mid=?,mtime=?"
_selSubjectMCountSQL = "SELECT oid, mcount FROM reply_subject_%d WHERE oid IN(%s) AND type=?"
_upSubStateSQL = "UPDATE reply_subject_%d SET state=?,mtime=? WHERE oid=? AND type=?"
_upSubAttrSQL = "UPDATE reply_subject_%d SET attr=?,mtime=? WHERE oid=? AND type=?"
_upSubStateAndAttrSQL = "UPDATE reply_subject_%d SET state=?,attr=?,mtime=? WHERE oid=? AND type=?"
_upSubMetaSQL = "UPDATE reply_subject_%d SET meta=?,mtime=? WHERE oid=? AND type=?"
_incrSubCountSQL = "UPDATE reply_subject_%d SET count=count+1,rcount=rcount+1,acount=acount+1,mtime=? WHERE oid=? AND type=?"
_incrSubFCountSQL = "UPDATE reply_subject_%d SET count=count+1,mtime=? WHERE oid=? AND type=?"
_incrSubRCountSQL = "UPDATE reply_subject_%d SET rcount=rcount+1,mtime=? WHERE oid=? AND type=?"
_incrSubACountSQL = "UPDATE reply_subject_%d SET acount=acount+?,mtime=? WHERE oid=? AND type=?"
_decrSubRCountSQL = "UPDATE reply_subject_%d SET rcount=rcount-1,mtime=? WHERE oid=? AND type=?"
_decrSubACountSQL = "UPDATE reply_subject_%d SET acount=acount-?,mtime=? WHERE oid=? AND type=?"
_decrSubMCountSQL = "UPDATE reply_subject_%d SET mcount=mcount-1,mtime=? WHERE oid=? AND type=? AND mcount>0"
)
func subHit(id int64) int64 {
return id % _subShard
}
// SubMCount get subject mcount from mysql
func (d *Dao) SubMCount(c context.Context, oids []int64, typ int32) (res map[int64]int32, err error) {
hits := make(map[int64][]int64)
for _, oid := range oids {
hit := subHit(oid)
hits[hit] = append(hits[hit], oid)
}
res = make(map[int64]int32, len(oids))
wg, ctx := errgroup.WithContext(c)
var lock = sync.RWMutex{}
for idx, oids := range hits {
o := oids
i := idx
wg.Go(func() (err error) {
var rows *xsql.Rows
if rows, err = d.db.Query(ctx, fmt.Sprintf(_selSubjectMCountSQL, i, xstr.JoinInts(o)), typ); err != nil {
log.Error("dao.db.Query error(%v)", err)
return
}
var mcount int32
var oid int64
for rows.Next() {
if err = rows.Scan(&oid, &mcount); err != nil {
if err == xsql.ErrNoRows {
mcount = 0
oid = 0
err = nil
continue
} else {
log.Error("row.Scan error(%v)", err)
rows.Close()
return
}
}
lock.Lock()
res[oid] = mcount
lock.Unlock()
}
if err = rows.Err(); err != nil {
log.Error("rows.err error(%v)", err)
rows.Close()
return
}
rows.Close()
return
})
}
if err = wg.Wait(); err != nil {
return
}
return
}
// Subjects get subjects from mysql.
func (d *Dao) Subjects(c context.Context, oids []int64, typ int32) (subMap map[int64]*model.Subject, err error) {
hitMap := make(map[int64][]int64)
for _, oid := range oids {
hitMap[subHit(oid)] = append(hitMap[subHit(oid)], oid)
}
subMap = make(map[int64]*model.Subject)
for hit, ids := range hitMap {
var rows *xsql.Rows
rows, err = d.db.Query(c, fmt.Sprintf(_selSubjectsSQL, hit, xstr.JoinInts(ids)), typ)
if err != nil {
return
}
for rows.Next() {
m := new(model.Subject)
if err = rows.Scan(&m.ID, &m.Oid, &m.Type, &m.Mid, &m.Count, &m.RCount, &m.ACount, &m.State, &m.Attr, &m.CTime, &m.MTime); err != nil {
rows.Close()
return
}
subMap[m.Oid] = m
}
if err = rows.Err(); err != nil {
rows.Close()
return
}
rows.Close()
}
return
}
// Subject get a subject from mysql.
func (d *Dao) Subject(c context.Context, oid int64, typ int32) (m *model.Subject, err error) {
m = new(model.Subject)
row := d.db.QueryRow(c, fmt.Sprintf(_selSubjectSQL, subHit(oid)), oid, typ)
if err = row.Scan(&m.ID, &m.Oid, &m.Type, &m.Mid, &m.Count, &m.RCount, &m.ACount, &m.State, &m.Attr, &m.CTime, &m.MTime); err != nil {
if err == xsql.ErrNoRows {
m = nil
err = nil
}
}
return
}
// TxSubject get a subject from mysql.
func (d *Dao) TxSubject(tx *xsql.Tx, oid int64, typ int32) (m *model.Subject, err error) {
m = new(model.Subject)
row := tx.QueryRow(fmt.Sprintf(_selSubjectSQL, subHit(oid)), oid, typ)
if err = row.Scan(&m.ID, &m.Oid, &m.Type, &m.Mid, &m.Count, &m.RCount, &m.ACount, &m.State, &m.Attr, &m.CTime, &m.MTime); err != nil {
if err == xsql.ErrNoRows {
m = nil
err = nil
}
}
return
}
// TxSubjectForUpdate get a subject from mysql for update.
func (d *Dao) TxSubjectForUpdate(tx *xsql.Tx, oid int64, typ int32) (m *model.Subject, err error) {
m = new(model.Subject)
row := tx.QueryRow(fmt.Sprintf(_selSubjectForUpdateSQL, subHit(oid)), oid, typ)
if err = row.Scan(&m.ID, &m.Oid, &m.Type, &m.Mid, &m.Count, &m.RCount, &m.ACount, &m.State, &m.Attr, &m.CTime, &m.MTime); err != nil {
if err == xsql.ErrNoRows {
m = nil
err = nil
}
}
return
}
// AddSubject insert or update subject state.
func (d *Dao) AddSubject(c context.Context, mid, oid int64, typ, state int32, now time.Time) (id int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_inSubjectSQL, subHit(oid)), oid, typ, mid, state, now, now, state, mid, now)
if err != nil {
return
}
return res.LastInsertId()
}
// UpSubjectState update subject state.
func (d *Dao) UpSubjectState(c context.Context, oid int64, typ, state int32, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_upSubStateSQL, subHit(oid)), state, now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// UpSubjectAttr update subject attr.
func (d *Dao) UpSubjectAttr(c context.Context, oid int64, typ int32, attr uint32, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_upSubAttrSQL, subHit(oid)), attr, now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// UpStateAndAttr update subject state and attr
func (d *Dao) UpStateAndAttr(c context.Context, oid int64, typ, state int32, attr uint32, now time.Time) (rows int64, err error) {
res, err := d.db.Exec(c, fmt.Sprintf(_upSubStateAndAttrSQL, subHit(oid)), state, attr, now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxIncrSubCount incr subject count and rcount by transaction.
func (d *Dao) TxIncrSubCount(tx *xsql.Tx, oid int64, typ int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_incrSubCountSQL, subHit(oid)), now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxIncrSubFCount incr subject count and rcount by transaction.
func (d *Dao) TxIncrSubFCount(tx *xsql.Tx, oid int64, typ int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_incrSubFCountSQL, subHit(oid)), now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxIncrSubRCount incr subject rcount by transaction
func (d *Dao) TxIncrSubRCount(tx *xsql.Tx, oid int64, typ int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_incrSubRCountSQL, subHit(oid)), now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxDecrSubRCount decr subject count by transaction.
func (d *Dao) TxDecrSubRCount(tx *xsql.Tx, oid int64, typ int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_decrSubRCountSQL, subHit(oid)), now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxIncrSubACount incr subject acount by transaction.
func (d *Dao) TxIncrSubACount(tx *xsql.Tx, oid int64, typ int32, count int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_incrSubACountSQL, subHit(oid)), count, now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxSubDecrACount decr subject rcount by transaction.
func (d *Dao) TxSubDecrACount(tx *xsql.Tx, oid int64, typ int32, count int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_decrSubACountSQL, subHit(oid)), count, now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxSubDecrMCount decr subject mcount by transaction.
func (d *Dao) TxSubDecrMCount(tx *xsql.Tx, oid int64, typ int32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_decrSubMCountSQL, subHit(oid)), now, oid, typ)
if err != nil {
return
}
return res.RowsAffected()
}
// TxUpSubAttr update subject attr.
func (d *Dao) TxUpSubAttr(tx *xsql.Tx, oid int64, tp int32, attr uint32, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_upSubAttrSQL, subHit(oid)), attr, now, oid, tp)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}
// TxUpSubMeta update subject meta.
func (d *Dao) TxUpSubMeta(tx *xsql.Tx, oid int64, tp int32, meta string, now time.Time) (rows int64, err error) {
res, err := tx.Exec(fmt.Sprintf(_upSubMetaSQL, subHit(oid)), meta, now, oid, tp)
if err != nil {
log.Error("mysqlDB.Exec error(%v)", err)
return
}
return res.RowsAffected()
}

View File

@@ -0,0 +1,139 @@
package dao
import (
"context"
"fmt"
"testing"
"time"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func deleteSubject(d *Dao, oid int64, typ int32) error {
_delSQL := "Delete from reply_subject_%d where oid=? and type=?"
_, err := d.db.Exec(context.Background(), fmt.Sprintf(_delSQL, subHit(oid)), oid, typ)
if err != nil {
return err
}
return nil
}
func TestSubjectAttr(t *testing.T) {
var (
sub = &model.Subject{
Oid: 1,
Type: 1,
}
now = time.Now()
c = context.Background()
)
Convey("get and update a subject", t, WithDao(func(d *Dao) {
_, err := d.AddSubject(c, sub.Mid, sub.Oid, sub.Type, sub.State, now)
So(err, ShouldBeNil)
sub.AttrSet(model.AttrYes, model.SubAttrMonitor)
_, err = d.UpSubjectAttr(c, sub.Oid, sub.Type, sub.Attr, now)
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.AttrVal(model.SubAttrMonitor), ShouldEqual, model.AttrYes)
}))
Convey("get and update a subject", t, WithDao(func(d *Dao) {
_, err := d.AddSubject(c, sub.Mid, sub.Oid, sub.Type, sub.State, now)
So(err, ShouldBeNil)
sub.AttrSet(model.AttrNo, model.SubAttrMonitor)
_, err = d.UpSubjectAttr(c, sub.Oid, sub.Type, sub.Attr, now)
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.AttrVal(model.SubAttrMonitor), ShouldEqual, model.AttrNo)
}))
}
func TestSubjectState(t *testing.T) {
var (
sub = &model.Subject{
Oid: 1,
Type: 1,
}
now = time.Now()
c = context.Background()
)
Convey("get and update a subject", t, WithDao(func(d *Dao) {
_, err := d.AddSubject(c, sub.Mid, sub.Oid, sub.Type, sub.State, now)
So(err, ShouldBeNil)
_, err = d.UpSubjectState(c, sub.Oid, sub.Type, model.SubStateForbid, now)
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(sub.State, ShouldEqual, model.SubStateForbid)
}))
}
func TestSubjectCount(t *testing.T) {
d := _d
var (
sub = &model.Subject{
Oid: 2,
Type: 3,
}
now = time.Now()
c = context.Background()
)
_, err := d.AddSubject(c, sub.Mid, sub.Oid, sub.Type, sub.State, now)
if err != nil {
t.Logf("add subject failed!%v", err)
t.FailNow()
}
defer deleteSubject(d, 2, 3)
Convey("increase and decrease subject", t, WithDao(func(d *Dao) {
Convey("increase subject rcount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
rows, err := d.TxIncrSubRCount(t, 2, 3, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.RCount, ShouldEqual, 1)
}))
Convey("decrease subject rcount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
rows, err := d.TxDecrSubRCount(t, 2, 3, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.RCount, ShouldEqual, 0)
}))
Convey("increase subject acount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
rows, err := d.TxIncrSubACount(t, 2, 3, 123, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.ACount, ShouldEqual, 123)
}))
Convey("decrease subject acount", WithDao(func(d *Dao) {
t, err := d.BeginTran(c)
So(err, ShouldBeNil)
rows, err := d.TxSubDecrACount(t, 2, 3, 23, now)
So(err, ShouldBeNil)
So(rows, ShouldBeGreaterThan, 0)
err = t.Commit()
So(err, ShouldBeNil)
sub, err = d.Subject(c, sub.Oid, sub.Type)
So(err, ShouldBeNil)
So(sub.ACount, ShouldEqual, 100)
}))
}))
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"context"
"go-common/library/ecode"
"net/url"
"strconv"
"go-common/library/log"
)
const (
_workflowDel = "http://api.bilibili.co/x/internal/workflow/appeal/v3/delete"
)
// FilterContent get filtered contents by ids.
func (d *Dao) DelReport(c context.Context, oid int64, rpid int64) (err error) {
var res struct {
Code int `json:"code"`
}
params := url.Values{}
params.Set("business", "13")
params.Set("oid", strconv.FormatInt(oid, 10))
params.Set("eid", strconv.FormatInt(rpid, 10))
if err = d.httpClient.Post(c, _workflowDel, "", params, &res); err != nil {
log.Error("DelReport(%s,%s) error(%v)", _workflowDel, params.Encode(), err)
return
}
if res.Code != 0 {
log.Error("DelReport(%s,%s) error(%v)", _workflowDel, params.Encode(), err)
err = ecode.Int(res.Code)
return
}
log.Info("DelReport success!(%s,%s,%d)", _workflowDel, params.Encode())
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoDelReport(t *testing.T) {
convey.Convey("DelReport", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(0)
rpid = int64(0)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := _d.DelReport(c, oid, rpid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,69 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["http_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/time:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"action.go",
"business.go",
"emoji.go",
"emoji_package.go",
"http.go",
"log.go",
"monitor.go",
"notice.go",
"reply.go",
"report.go",
"subject.go",
],
importpath = "go-common/app/admin/main/reply/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/main/reply/model:go_default_library",
"//app/admin/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/binding:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/ip: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,74 @@
package http
import (
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
func actionCount(c *bm.Context) {
var (
err error
v = new(struct {
RpID int64 `form:"rpid" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
Type int32 `form:"type" validate:"required"`
AdminID int64 `form:"admin_id"`
})
)
if err = c.Bind(v); err != nil {
return
}
like, hate, err := rpSvc.ActionCount(c, v.RpID, v.Oid, v.AdminID, v.Type)
if err != nil {
log.Warn("svc.ActionInfo(%+v) error(%v)", v, err)
c.JSON(nil, err)
return
}
data := map[string]interface{}{
"like": like,
"hate": hate,
}
c.JSONMap(data, nil)
}
func actionUpdate(c *bm.Context) {
var (
err error
v = new(struct {
RpID int64 `form:"rpid" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
Type int32 `form:"type" validate:"required"`
Action int32 `form:"action" validate:"required,min=1,max=2"`
Count int32 `form:"count"`
AdminID int64 `form:"admin_id"`
Remark string `form:"remark"`
})
)
if err = c.Bind(v); err != nil {
return
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
switch v.Action {
case model.ActionLike:
if v.Count < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
err = rpSvc.UpActionLike(c, v.RpID, v.Oid, adid, v.Type, v.Count, v.Remark)
case model.ActionHate:
err = rpSvc.UpActionHate(c, v.RpID, v.Oid, adid, v.Type, v.Count, v.Remark)
default:
err = ecode.RequestErr
}
if err != nil {
log.Warn("rpSvc.ActionUpdate(%v) error(%v)", v, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}

View File

@@ -0,0 +1,77 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
func listBusiness(c *bm.Context) {
v := new(struct {
State int32 `form:"state"`
})
err := c.Bind(v)
if err != nil {
return
}
res, err := rpSvc.ListBusiness(c, v.State)
c.JSON(res, err)
}
func getBusiness(c *bm.Context) {
v := new(struct {
Type int32 `form:"type"`
})
err := c.Bind(v)
if err != nil {
return
}
res, err := rpSvc.GetBusiness(c, v.Type)
c.JSON(res, err)
}
func addBusiness(c *bm.Context) {
v := new(struct {
Type int32 `form:"type" validate:"required"`
Name string `form:"name" validate:"required"`
Appkey string `form:"app_key" validate:"required"`
Remark string `form:"remark" validate:"required"`
Alias string `form:"alias" validate:"required"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
_, err = rpSvc.AddBusiness(c, v.Type, v.Name, v.Appkey, v.Remark, v.Alias)
c.JSON(nil, err)
}
func upBusiness(c *bm.Context) {
v := new(struct {
Type int32 `form:"type" validate:"required"`
Name string `form:"name" validate:"required"`
Appkey string `form:"app_key" validate:"required"`
Remark string `form:"remark" validate:"required"`
Alias string `form:"alias" validate:"required"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
_, err = rpSvc.UpBusiness(c, v.Name, v.Appkey, v.Remark, v.Alias, v.Type)
c.JSON(nil, err)
}
func upBusiState(c *bm.Context) {
v := new(struct {
State int32 `form:"state"`
Type int32 `form:"type" validate:"required"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
_, err = rpSvc.UpBusinessState(c, v.State, v.Type)
c.JSON(nil, err)
}

View File

@@ -0,0 +1,93 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
func listEmoji(c *bm.Context) {
v := new(struct {
Pid int64 `form:"pid" default:"0"`
})
if err := c.Bind(v); err != nil {
return
}
emojis, err := rpSvc.EmojiList(c, v.Pid)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(emojis, nil)
}
func createEmoji(c *bm.Context) {
v := new(struct {
Pid int64 `form:"pid" validate:"min=0"`
Name string `form:"name" validate:"required"`
URL string `form:"url" validate:"required"`
Sort int32 `form:"sort" validate:"min=0"`
Remark string `form:"remark" validate:"required"`
State int32 `form:"state" validate:"min=0"`
})
if err := c.Bind(v); err != nil {
return
}
if err := rpSvc.EmojiByName(c, v.Name); err != nil {
c.JSON(nil, err)
return
}
id, err := rpSvc.CreateEmoji(c, v.Pid, v.Name, v.URL, v.Sort, v.State, v.Remark)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(id, nil)
}
func upEmojiSort(c *bm.Context) {
v := new(struct {
IDs string `form:"ids" validate:"required"`
})
if err := c.Bind(v); err != nil {
return
}
err := rpSvc.UpEmojiSort(c, v.IDs)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func upEmojiState(c *bm.Context) {
v := new(struct {
ID int64 `form:"id" validate:"min=0"`
State int32 `form:"state" validate:"min=0"`
})
if err := c.Bind(v); err != nil {
return
}
id, err := rpSvc.UpEmojiState(c, v.State, v.ID)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(id, nil)
}
func upEmoji(c *bm.Context) {
v := new(struct {
ID int64 `form:"id" validate:"min=0"`
Name string `form:"name" validate:"required"`
Remark string `form:"remark" validate:"required"`
URL string `form:"url" validate:"required"`
})
if err := c.Bind(v); err != nil {
return
}
id, err := rpSvc.UpEmoji(c, v.Name, v.Remark, v.URL, v.ID)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(id, nil)
}

View File

@@ -0,0 +1,71 @@
package http
import (
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
func listEmojiPacks(c *bm.Context) {
packs, err := rpSvc.EmojiPackageList(c)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(packs, nil)
}
func createEmojiPackage(c *bm.Context) {
v := new(struct {
Name string `form:"name" validate:"required"`
URL string `form:"url" validate:"required"`
Sort int32 `form:"sort" validate:"min=0"`
Remark string `form:"remark" default:""`
State int32 `form:"state" validate:"min=0"`
})
if err := c.Bind(v); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
id, err := rpSvc.CreateEmojiPackage(c, v.Name, v.URL, v.Sort, v.Remark, v.State)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(id, nil)
}
func upEmojiPackageSort(c *bm.Context) {
v := new(struct {
IDs string `form:"ids" validate:"required"`
})
if err := c.Bind(v); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
err := rpSvc.UpEmojiPackageSort(c, v.IDs)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func editEmojiPack(c *bm.Context) {
v := new(struct {
ID int64 `form:"id" validate:"min=0"`
Name string `form:"name" default:""`
URL string `form:"url" default:""`
Remark string `form:"remark" default:""`
State int32 `form:"state" validate:"min=0"`
})
if err := c.Bind(v); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
id, err := rpSvc.UpEmojiPackage(c, v.Name, v.URL, v.Remark, v.State, v.ID)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(id, nil)
}

View File

@@ -0,0 +1,165 @@
package http
import (
"net/http"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
rpSvc *service.Service
verifySvc *verify.Verify
)
// Init init http.
func Init(c *conf.Config) {
// init services
rpSvc = service.New(c)
verifySvc = verify.New(c.Verify)
auther := permit.New(c.ManagerAuth)
engine := bm.DefaultServer(c.HTTPServer)
authRouter(engine, auther)
interRouter(engine)
verifyRouter(engine)
// serve port
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
type managerAuther interface {
Permit(permit string) bm.HandlerFunc
}
type fakeAuth struct {
}
func (a *fakeAuth) Permit(permit string) bm.HandlerFunc {
return func(ctx *bm.Context) {
ctx.Next()
}
}
func authRouter(engine *bm.Engine, auther managerAuther) {
engine.GET("/monitor/ping", ping)
group := engine.Group("/x/admin/reply")
{
// reply
group.GET("/search", auther.Permit("REPLY_READONLY"), replySearch) // 评论列表
group.GET("/export", auther.Permit("REPLY_READONLY"), replyExport) // 评论列表
group.POST("/pass", auther.Permit("REPLY_MGR"), adminPassReply) // 通过评论
group.POST("/recover", auther.Permit("REPLY_MGR"), adminRecoverReply) // 恢复评论
group.POST("/edit", auther.Permit("REPLY_MGR"), adminEditReply) // 编辑评论
group.POST("/del", auther.Permit("REPLY_MGR"), adminDelReply) // 删除评论
group.POST("/top", auther.Permit("REPLY_TOP_MGR"), adminTopReply) // 置顶评论
group.GET("/top/log", auther.Permit("REPLY_TOP_MGR"), adminTopReplyLog) // 置顶评论日志搜索
group.GET("/reply/top", auther.Permit("REPLY_MGR"), topChildReply)
// report
group.GET("/report/search", auther.Permit("REPLY_REPORT_READONLY"), reportSearch) // 举报列表
group.POST("/report/del", auther.Permit("REPLY_MGR"), reportDel) // 举报删除
group.POST("/report/ignore", auther.Permit("REPLY_MGR"), reportIgnore) // 举报忽略
group.POST("/report/recover", auther.Permit("REPLY_MGR"), reportRecover) // 举报恢复
group.POST("/report/transfer", auther.Permit("REPLY_MGR"), reportTransfer) // 举报转审
group.POST("/report/state", auther.Permit("REPLY_MGR"), reportStateSet) // 设置举报状态
// monitor
group.GET("/monitor/search", auther.Permit("REPLY_MONITOR_READONLY"), monitorSearch) // 监控列表
group.GET("/monitor/stats", auther.Permit("REPLY_MONITOR_READONLY"), monitorStats) // 监控统计
group.POST("/monitor/state", auther.Permit("REPLY_MONITOR_MGR"), monitorState) // 监控状态
group.GET("/mointor/log", auther.Permit("REPLY_MONITOR_READONLY"), monitorLog) // 监控操作日志
// config
group.POST("/config/update", auther.Permit("REPLY_MGR"), updateReplyConfig) // 配置更新
group.POST("/config/renew", auther.Permit("REPLY_MGR"), renewReplyConfig) // 配置添加
group.GET("/config/info", auther.Permit("REPLY_READONLY"), loadReplyConfig) // 配置信息
group.GET("/config/list", auther.Permit("REPLY_READONLY"), paginateReplyConfig) // 配置列表
// notice
group.GET("/notice/detail", auther.Permit("MANAGER_NOTICE"), getNotice) // 通知信息
group.GET("/notice/list", auther.Permit("MANAGER_NOTICE"), listNotice2) // 通知列表
group.POST("/notice/edit", auther.Permit("MANAGER_NOTICE"), editNotice) // 通知编辑
group.POST("/notice/delete", auther.Permit("MANAGER_NOTICE"), deleteNotice) // 通知删除
group.POST("/notice/offline", auther.Permit("MANAGER_NOTICE"), offlineNotice) // 下线通知
group.POST("/notice/online", auther.Permit("MANAGER_NOTICE"), onlineNotice) // 上线通知
// subject
group.GET("/subject/info", auther.Permit("REPLY_READONLY"), adminSubject) // 主题信息
group.POST("/subject/state", auther.Permit("REPLY_SUBJECT_FREEZE"), adminSubjectState) // 主题状态设置
group.POST("/subject/freeze", auther.Permit("REPLY_SUBJECT_FREEZE"), SubFreeze) // 主题冻结或解冻评论
group.GET("/subject/log", auther.Permit("REPLY_READONLY"), SubLogSearch) // 主题操作日志搜索
// action
group.GET("/action/count", auther.Permit("REPLY_MGR"), actionCount)
group.POST("/action/update", auther.Permit("REPLY_MGR"), actionUpdate)
// log
group.GET("/log", auther.Permit("REPLY_MGR"), logByRpID)
// emoji
group.GET("/emoji/list", auther.Permit("REPLY_EMOJI"), listEmoji)
group.POST("/emoji/add", auther.Permit("REPLY_EMOJI"), createEmoji)
group.POST("/emoji/state", auther.Permit("REPLY_EMOJI"), upEmojiState)
group.POST("/emoji/sort", auther.Permit("REPLY_EMOJI"), upEmojiSort)
group.POST("/emoji/edit", auther.Permit("REPLY_EMOJI"), upEmoji)
group.GET("/emoji/package/list", auther.Permit("REPLY_EMOJI"), listEmojiPacks)
group.POST("/emoji/package/add", auther.Permit("REPLY_EMOJI"), createEmojiPackage)
group.POST("/emoji/package/edit", auther.Permit("REPLY_EMOJI"), editEmojiPack)
group.POST("/emoji/package/sort", auther.Permit("REPLY_EMOJI"), upEmojiPackageSort)
// business
group.GET("/business/list", listBusiness)
group.GET("/business/get", getBusiness)
group.POST("/business/add", addBusiness)
group.POST("/business/update", upBusiness)
group.POST("/business/state", upBusiState)
// fold reply
group.POST("/fold", foldReply)
}
}
func interRouter(engine *bm.Engine) {
group := engine.Group("/x/internal/replyadmin", verifySvc.Verify)
{
//log
group.GET("/log", logByRpID)
//search
group.GET("/reply/search", replySearch)
group.GET("/report/search", reportSearch)
// moniter
group.GET("/monitor/stats", monitorStats)
group.GET("/monitor/search", monitorSearch)
group.POST("/monitor/state", monitorState)
// config
group.POST("/config/update", updateReplyConfig)
group.POST("/config/rewnew", renewReplyConfig)
group.GET("/config/info", loadReplyConfig)
group.GET("/config/list", paginateReplyConfig)
// notice
group.GET("/notice/detail", getNotice)
group.GET("/notice/list", listNotice)
group.POST("/notice/edit", editNotice)
group.POST("/notice/delete", deleteNotice)
group.POST("/notice/offline", offlineNotice)
group.POST("/notice/online", onlineNotice)
}
}
func verifyRouter(engine *bm.Engine) {
group := engine.Group("/x/admin/reply/internal")
{
group.POST("/spam", verifySvc.Verify, adminMarkAsSpam) // 标记评论为垃圾
group.POST("/del", verifySvc.Verify, adminDelReply) // 删除评论
group.POST("/callback/del", verifySvc.Verify, callbackDelReply) // 回调删除评论
group.GET("/reply", adminReplyList) // 获得评论列表接口
}
}
func ping(ctx *bm.Context) {
if err := rpSvc.Ping(ctx); err != nil {
log.Error("reply admin ping error(%v)", err)
ctx.JSON(nil, err)
ctx.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,61 @@
package http
import (
"context"
"net/url"
"testing"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
xtime "go-common/library/time"
)
const (
_monitorState = "http://127.0.0.1:6711/x/internal/replyadmin/monitor/state"
)
func TestHttp(t *testing.T) {
var (
err error
)
if err = conf.Init(); err != nil {
t.Errorf("conf.Init() error(%v)", err)
t.FailNow()
}
log.Init(conf.Conf.Log)
defer log.Close()
client := bm.NewClient(&bm.ClientConfig{
Dial: xtime.Duration(time.Second),
Timeout: xtime.Duration(time.Second),
})
Init(conf.Conf)
// test
testMonitorState(client, t)
}
func testMonitorState(client *bm.Client, t *testing.T) {
var err error
params := url.Values{}
params.Set("type", "1")
params.Set("oid", "1")
params.Set("state", "1")
params.Set("adid", "11")
params.Set("remark", "test")
// send
res := map[string]interface{}{}
if err = client.Post(context.Background(), _monitorState, "", params, &res); err != nil {
t.Errorf("client.Post() error(%v)", err)
t.FailNow()
}
validRes(_monitorState, params, res, t)
}
func validRes(url string, params url.Values, res map[string]interface{}, t *testing.T) {
if code, ok := res["code"]; ok && code.(float64) == 0 {
t.Logf("\nurl:%s\nparams:%s\nres:%v", url, params.Encode(), res)
} else {
t.Errorf("\nurl:%s\nparams:%s\nres:%v", url, params.Encode(), res)
}
}

View File

@@ -0,0 +1,25 @@
package http
import (
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
func logByRpID(c *bm.Context) {
var (
err error
v = new(struct {
RpID int64 `form:"rpid" validate:"required"`
})
)
if err = c.Bind(v); err != nil {
return
}
list, err := rpSvc.LogsByRpID(c, v.RpID)
if err != nil {
log.Error("svc.ActionInfo(%+v) error(%v)", v, err)
c.JSON(nil, err)
return
}
c.JSON(list, nil)
}

View File

@@ -0,0 +1,236 @@
package http
import (
"strconv"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
func monitorStats(c *bm.Context) {
var (
err error
mode int64
startTime int64
endTime int64
page = int64(conf.Conf.Reply.PageNum)
pageSize = int64(conf.Conf.Reply.PageSize)
)
params := c.Request.Form
modeStr := params.Get("mode")
sortStr := params.Get("sort")
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
adminIDsStr := params.Get("adminids")
startTimeStr := params.Get("start_time")
endTimeStr := params.Get("end_time")
orderStr := params.Get("order_time")
if mode, err = strconv.ParseInt(modeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(mode:%s) error(%v)", modeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(pageSize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if startTimeStr != "" {
if startTime, err = strconv.ParseInt(startTimeStr, 10, 64); err == nil {
startTimeStr = time.Unix(startTime, 0).Format(model.DateSimpleFormat)
}
}
if endTimeStr != "" {
if endTime, err = strconv.ParseInt(endTimeStr, 10, 64); err == nil {
endTimeStr = time.Unix(endTime, 0).Format(model.DateSimpleFormat)
}
} else {
t := time.Now()
endTimeStr = t.Format(model.DateSimpleFormat)
}
stats, err := rpSvc.MonitorStats(c, mode, page, pageSize, adminIDsStr, sortStr, orderStr, startTimeStr, endTimeStr)
if err != nil {
log.Error("svc.MonitorStats(%d,%d,%d,%s,%s,%s,%s,%s) error(%v)", mode, page, pageSize, adminIDsStr, sortStr, orderStr, startTimeStr, endTimeStr, err)
c.JSON(nil, err)
return
}
c.JSON(stats, nil)
}
func monitorSearch(c *bm.Context) {
var (
err error
mode, typ int64
page = int64(conf.Conf.Reply.PageNum)
pageSize = int64(conf.Conf.Reply.PageSize)
sp = &model.SearchMonitorParams{}
)
params := c.Request.Form
modeStr := params.Get("mode")
typeStr := params.Get("type")
oidStr := params.Get("oid")
uidStr := params.Get("uid")
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if modeStr != "" {
if mode, err = strconv.ParseInt(modeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(mode:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if oidStr != "" {
if sp.Oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if uidStr != "" {
if sp.UID, err = strconv.ParseInt(uidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(uid:%s) error(%v)", uidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
sp.Mode = int8(mode)
sp.Type = int8(typ)
sp.Keyword = params.Get("keyword")
sp.NickName = params.Get("nickname")
sp.Sort = params.Get("sort")
sp.Order = params.Get("order")
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil || page < 1 {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < 1 || pageSize > int64(conf.Conf.Reply.PageSize) {
log.Warn("strconv.ParseInt(pagesize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
rpts, err := rpSvc.MonitorSearch(c, sp, page, pageSize)
if err != nil {
log.Error("svc.ReportSearch(%d,%d,%v) error(%v)", page, pageSize, sp, err)
c.JSON(nil, err)
return
}
c.JSON(rpts, nil)
}
func monitorState(c *bm.Context) {
var (
err error
typ int64
state int64
adminID int64
oids []int64
)
params := c.Request.Form
typeStr := params.Get("type")
oidStr := params.Get("oid")
stateStr := params.Get("state")
adminIDStr := params.Get("adid")
remark := params.Get("remark")
if oids, err = xstr.SplitInts(oidStr); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if state, err = strconv.ParseInt(stateStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(state:%s) error(%v)", stateStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if adminIDStr != "" {
if adminID, err = strconv.ParseInt(adminIDStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(admin:%s) error(%v)", adminIDStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if uid, ok := c.Get("uid"); ok {
adminID = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
for _, oid := range oids {
if err = rpSvc.UpMonitorState(c, adminID, adName, oid, int32(typ), int32(state), remark); err != nil {
log.Error("svc.MonitorState(%d,%d,%d,%d,%s) error(%v)", oid, typ, state, adminID, remark, err)
c.JSON(nil, err)
return
}
}
c.JSON(nil, nil)
}
// monitorLog get monitor log.
func monitorLog(c *bm.Context) {
v := new(struct {
Oid int64 `form:"oid" `
Type int32 `form:"type" `
StartTime string `form:"start_time"`
EndTime string `form:"end_time"`
Page int64 `form:"pn"`
PageSize int64 `form:"ps"`
Mid int64 `form:"mid"`
Order string `form:"order"`
Sort string `form:"sort"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
sp := model.LogSearchParam{
Oid: v.Oid,
Type: v.Type,
Mid: v.Mid,
CtimeFrom: v.StartTime,
CtimeTo: v.EndTime,
Pn: v.Page,
Ps: v.PageSize,
Order: v.Order,
Sort: v.Sort,
}
data, err := rpSvc.MointorLog(c, sp)
res := map[string]interface{}{}
res["data"] = data
c.JSONMap(res, err)
return
}

View File

@@ -0,0 +1,334 @@
package http
import (
"strconv"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
xtime "go-common/library/time"
)
//panigate notice list
func listNotice(c *bm.Context) {
var (
err error
page = int64(1)
pageSize = int64(conf.Conf.Reply.PageSize)
)
params := c.Request.Form
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if page < 1 {
page = 1
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < 1 {
log.Warn("strconv.ParseInt(pagesize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
data, total, err := rpSvc.ListNotice(c, page, pageSize)
if err != nil {
log.Error("svc.ListNotcie(%d,%d) error(%v)", page, pageSize, err)
c.JSON(nil, err)
return
}
res := map[string]interface{}{}
res["data"] = data
res["pager"] = map[string]interface{}{
"num": page,
"size": pageSize,
"total": total,
}
c.JSONMap(res, nil)
return
}
//panigate notice list
func listNotice2(c *bm.Context) {
var (
err error
page int64 = 1
pageSize = int64(conf.Conf.Reply.PageSize)
)
params := c.Request.Form
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if page < 1 {
page = 1
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < 1 {
log.Warn("strconv.ParseInt(pagesize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
data, total, err := rpSvc.ListNotice(c, page, pageSize)
if err != nil {
log.Error("svc.ListNotcie(%d,%d) error(%v)", page, pageSize, err)
c.JSON(nil, err)
return
}
res := map[string]interface{}{}
res["data"] = data
res["pager"] = model.Pager{Page: page, PageSize: pageSize, Total: total}
c.JSONMap(res, nil)
return
}
//return a notice detail
func getNotice(c *bm.Context) {
var (
err error
id uint64
)
params := c.Request.Form
idStr := params.Get("id")
if idStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if id, err = strconv.ParseUint(idStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(id:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
data, err := rpSvc.GetNotice(c, uint32(id))
if err != nil {
log.Error("svc.GetNotice(%d) error(%v)", id, err)
c.JSON(nil, err)
return
}
if data == nil {
c.JSON(nil, ecode.NothingFound)
return
}
c.JSON(data, nil)
return
}
//update or create a notice
func editNotice(c *bm.Context) {
var (
err error
id uint64
plat uint64
condition uint64
version string
build uint64
title string
content string
link string
stime int64
etime int64
clientType string
)
params := c.Request.Form
idStr := params.Get("id")
platStr := params.Get("plat")
conditionStr := params.Get("condi")
version = params.Get("version")
buildStr := params.Get("build")
title = params.Get("title")
content = params.Get("content")
link = params.Get("link")
stimeStr := params.Get("stime")
etimeStr := params.Get("etime")
clientType = params.Get("client_type")
if platStr == "" || title == "" || content == "" || stimeStr == "" || etimeStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if idStr != "" {
if id, err = strconv.ParseUint(idStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(id:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if plat, err = strconv.ParseUint(platStr, 10, 8); err != nil {
log.Warn("strconv.ParseUint(plat:%s) error(%v)", platStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if conditionStr != "" {
if condition, err = strconv.ParseUint(conditionStr, 10, 8); err != nil {
log.Warn("strconv.ParseUint(condition:%s) error(%v)", conditionStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if buildStr != "" {
if build, err = strconv.ParseUint(buildStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(build:%s) error(%v)", buildStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
var tempTime time.Time
if tempTime, err = time.Parse("2006-01-02 15:04:05", stimeStr); err != nil {
//error,so try to parse as unix timestamp again
stime, err = strconv.ParseInt(stimeStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseUint(stime:%s) error(%v)", stimeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
} else {
//标准库的time.parse默认以UTC为标准转换成unix时间戳所以需要减去8小时
stime = tempTime.Unix() - 8*3600
}
if tempTime, err = time.Parse("2006-01-02 15:04:05", etimeStr); err != nil {
//error,so try to parse as unix timestamp again
etime, err = strconv.ParseInt(etimeStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseUint(etime:%s) error(%v)", etimeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
} else {
//标准库的time.parse默认以UTC为标准转换成unix时间戳所以需要减去8小时
etime = tempTime.Unix() - 8*3600
}
//开始时间必须小于等于结束时间
if stime > etime {
c.JSON(nil, ecode.RequestErr)
return
}
nt := &model.Notice{
ID: uint32(id),
Plat: model.NoticePlat(plat),
Version: version,
Condition: model.NoticeCondition(condition),
Build: uint32(build),
Title: title,
Content: content,
Link: link,
StartTime: xtime.Time(stime),
EndTime: xtime.Time(etime),
ClientType: clientType,
}
if idStr == "" {
_, err = rpSvc.CreateNotice(c, nt)
if err != nil {
log.Error("svc.CreateNotice(%v) error(%v)", *nt, err)
c.JSON(nil, err)
return
}
} else {
err = rpSvc.UpdateNotice(c, nt)
if err != nil {
log.Error("svc.UpdateNotice(%v) error(%v)", *nt, err)
c.JSON(nil, err)
return
}
}
c.JSON(nil, nil)
return
}
func deleteNotice(c *bm.Context) {
var (
err error
id uint64
)
params := c.Request.Form
idStr := params.Get("id")
if idStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if id, err = strconv.ParseUint(idStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(id:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
err = rpSvc.DeleteNotice(c, uint32(id))
if err != nil {
log.Error("svc.DeleteNotice(%d) error(%v)", id, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
return
}
func offlineNotice(c *bm.Context) {
var (
err error
id uint64
)
params := c.Request.Form
idStr := params.Get("id")
if idStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if id, err = strconv.ParseUint(idStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(id:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
err = rpSvc.UpdateNoticeStatus(c, model.StatusOffline, uint32(id))
if err != nil {
log.Error("svc.UpdateNoticeStatus(%d) error(%v)", id, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
return
}
func onlineNotice(c *bm.Context) {
var (
err error
id uint64
)
params := c.Request.Form
idStr := params.Get("id")
if idStr == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if id, err = strconv.ParseUint(idStr, 10, 32); err != nil {
log.Warn("strconv.ParseUint(id:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
err = rpSvc.UpdateNoticeStatus(c, model.StatusOnline, uint32(id))
if err != nil {
log.Error("svc.UpdateNoticeStatus(%d) error(%v)", id, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
return
}

View File

@@ -0,0 +1,721 @@
package http
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
"go-common/library/net/ip"
)
func replyExport(c *bm.Context) {
var (
err error
startTime time.Time
endTime time.Time
)
v := new(struct {
Oid int64 `form:"oid" validate:"required"`
Type int8 `form:"type" validate:"required"`
StartTime string `form:"start_time"`
EndTime string `form:"end_time"`
States []int64 `form:"states,split"`
Mid int64 `form:"uid"`
})
err = c.Bind(v)
if err != nil {
return
}
if v.StartTime == "" {
startTime = time.Now().AddDate(0, -3, 0)
v.StartTime = startTime.Format("2006-01-02")
} else {
if startTime, err = time.Parse("2006-01-02", v.StartTime); err != nil {
log.Warn("time.Parse(%s) error(%v)", v.StartTime, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if v.EndTime == "" {
endTime = time.Now()
v.EndTime = endTime.Format("2006-01-02")
} else {
if endTime, err = time.Parse("2006-01-02", v.EndTime); err != nil {
log.Warn("time.Parse(%s) error(%v)", v.EndTime, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
states := strings.Trim(strings.Join(strings.Split(fmt.Sprint(v.States), " "), ","), "[]")
data, err := rpSvc.ExportReply(c, v.Oid, v.Mid, v.Type, states, startTime, endTime)
if err != nil {
log.Error("Svc.ExportReply(%d,%d,%d,%s,%v,%v) error(%v)", v.Oid, v.Mid, v.Type, states, startTime, endTime, err)
c.Render(http.StatusInternalServerError, model.CSV{
Data: nil,
Title: "",
})
return
}
c.Render(http.StatusOK, model.CSV{
Data: data,
Title: v.StartTime + "至" + v.EndTime,
})
return
}
func replySearch(c *bm.Context) {
var (
err error
typ, startTime, endTime int64
page = int64(conf.Conf.Reply.PageNum)
pageSize = int64(conf.Conf.Reply.PageSize)
sp = &model.SearchParams{}
)
params := c.Request.Form
typeStr := params.Get("type")
oidStr := params.Get("oid")
uidStr := params.Get("uid")
adminIDStr := params.Get("adminid")
startTimeStr := params.Get("start_time")
endTimeStr := params.Get("end_time")
ipStr := params.Get("ip")
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
// parse params
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if oidStr != "" {
if sp.Oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if uidStr != "" {
if sp.UID, err = strconv.ParseInt(uidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(uid:%s) error(%v)", uidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if adminIDStr != "" {
if sp.AdminID, err = strconv.ParseInt(adminIDStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(adminid:%s) error(%v)", adminIDStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if startTimeStr != "" {
if startTime, err = strconv.ParseInt(startTimeStr, 10, 64); err == nil {
sp.Begin = time.Unix(startTime, 0)
} else {
var t time.Time
t, err = time.Parse("2006-01-02", startTimeStr)
if err == nil {
sp.Begin = t
} else {
c.JSON(nil, ecode.RequestErr)
return
}
}
}
if endTimeStr != "" {
if endTime, err = strconv.ParseInt(endTimeStr, 10, 64); err == nil {
sp.End = time.Unix(endTime, 0)
} else {
var t time.Time
t, err = time.Parse("2006-01-02", endTimeStr)
if err == nil {
sp.End = t
} else {
c.JSON(nil, ecode.RequestErr)
return
}
}
} else if startTimeStr != "" {
sp.End = time.Now()
}
if ipStr != "" {
if sp.IP, err = strconv.ParseInt(ipStr, 10, 64); err != nil {
sp.IP = int64(ip.InetAtoN(ipStr))
}
}
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil || page < 1 {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < int64(1) || pageSize > int64(conf.Conf.Reply.PageSize) {
log.Warn("strconv.ParseInt(pagesize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
sp.Type = int32(typ)
sp.TypeIds = params.Get("typeids")
sp.Keyword = params.Get("keyword")
sp.KeywordHigh = params.Get("keyword_high")
sp.Uname = params.Get("nickname")
sp.States = params.Get("states")
sp.Sort = params.Get("sort")
sp.Order = params.Get("order")
sp.Attr = params.Get("attr")
sp.AdminName = params.Get("adminname")
sp.Sort, sp.Order = sp.Order, sp.Sort
if sp.Sort == "" {
sp.Sort = "desc"
}
if sp.Order == "" {
sp.Order = "ctime"
}
rs, err := rpSvc.ReplySearch(c, sp, page, pageSize)
if err != nil {
log.Error("svc.ReplySearch(%v,%d,%d) error(%v)", sp, page, pageSize, err)
c.JSON(nil, err)
return
}
res := map[string]interface{}{}
res["data"] = rs
c.JSONMap(res, nil)
return
}
func updateReplyConfig(c *bm.Context) {
var (
err error
typ, oid, showEntry, showAdmin, adminID int64
operator string
config = &model.Config{}
bs []byte
)
params := c.Request.Form
typeStr := params.Get("type")
oidStr := params.Get("oid")
adminIDStr := params.Get("adminid")
operator = params.Get("operator")
showEntityStr := params.Get("showentry")
showAdminStr := params.Get("showadmin")
if oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if showEntry, err = strconv.ParseInt(showEntityStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(showEntry:%s) error(%v)", showEntityStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if showAdmin, err = strconv.ParseInt(showAdminStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(showAdmin:%s) error(%v)", showAdminStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
adminID, _ = strconv.ParseInt(adminIDStr, 10, 64)
if uid, ok := c.Get("uid"); ok {
adminID = uid.(int64)
}
config.Oid = oid
config.Type = int32(typ)
config.Category = 1
config.AdminID = adminID
config.Operator = operator
configValue := map[string]int64{
"showentry": showEntry,
"showadmin": showAdmin,
}
config.ShowEntry = int32(showEntry)
config.ShowAdmin = int32(showAdmin)
if bs, err = json.Marshal(configValue); err == nil {
config.Config = string(bs)
}
if _, err = rpSvc.AddReplyConfig(c, config); err != nil {
log.Error("svc.AddReplyConfig(%v) error(%v)", config, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func renewReplyConfig(c *bm.Context) {
var (
err error
id int64
)
params := c.Request.Form
idStr := params.Get("id")
if id, err = strconv.ParseInt(idStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", idStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if _, err = rpSvc.RenewReplyConfig(c, id); err != nil {
log.Error("svc.RenewReplyConfig(%d) error(%v)", id, err)
c.JSON(nil, err)
return
}
c.JSON(nil, nil)
}
func paginateReplyConfig(c *bm.Context) {
var (
err error
oid, typ, category int64
page = int64(conf.Conf.Reply.PageNum)
pageSize = int64(conf.Conf.Reply.PageSize)
)
params := c.Request.Form
typeStr := params.Get("type")
categoryStr := params.Get("category")
oidStr := params.Get("oid")
operator := params.Get("operator")
pageStr := params.Get("pn")
pageSizeStr := params.Get("ps")
if oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
}
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
}
if category, err = strconv.ParseInt(categoryStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(category:%s) error(%v)", categoryStr, err)
}
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil || page < 1 {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < 1 || pageSize > int64(conf.Conf.Reply.PageSize) {
log.Warn("strconv.ParseInt(pagesize:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
configs, totalCount, pages, err := rpSvc.PaginateReplyConfig(c, int32(typ), int32(category), oid, operator, int(page-1)*int(pageSize), int(pageSize))
if err != nil {
log.Error("svc.PaginateReplyConfig(%d,%d,%d,%v,%d,%d) error(%v)", int32(typ), 1, oid, operator, int(page), int(pageSize), err)
c.JSON(nil, err)
return
}
data := map[string]interface{}{
"page": page,
"pagesize": pageSize,
"pagecount": pages,
"total": totalCount,
"order": "default",
"result": configs,
}
c.JSONMap(data, nil)
return
}
func loadReplyConfig(c *bm.Context) {
var (
err error
oid, typ, category int64
)
params := c.Request.Form
typeStr := params.Get("type")
categoryStr := params.Get("category")
oidStr := params.Get("oid")
if oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if category, err = strconv.ParseInt(categoryStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(category:%s) error(%v)", categoryStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
config, err := rpSvc.LoadReplyConfig(c, int32(typ), int32(category), oid)
if err != nil {
log.Error("Svc.LoadReplyConfig(%d,%d,%d) error(%v)", int32(typ), 1, oid, err)
c.JSON(nil, err)
return
}
c.JSON(config, nil)
return
}
// adminPassReply pass reply normal.
func adminPassReply(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type int32 `form:"type" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
if len(v.Oid) != len(v.RpID) {
log.Warn("len(v.Oid) != len(v.RpID),%d != %d", len(v.Oid), len(v.RpID))
c.JSON(nil, ecode.RequestErr)
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
err = rpSvc.AdminPassReply(c, adid, adName, v.Oid, v.RpID, v.Type, v.Remark)
c.JSON(nil, err)
return
}
// adminRecoverReply recover reply normal.
func adminRecoverReply(c *bm.Context) {
v := new(struct {
RpID int64 `form:"rpid" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
Type int32 `form:"type" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
err = rpSvc.AdminRecoverReply(c, adid, adName, v.Oid, v.RpID, v.Type, v.Remark)
c.JSON(nil, err)
return
}
// adminEditReply edit reply normal.
func adminEditReply(c *bm.Context) {
v := new(struct {
RpID int64 `form:"rpid" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
Type int32 `form:"type" validate:"required"`
AdminID int64 `form:"adid"`
Message string `form:"message"`
Remark string `form:"remark"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
err = rpSvc.AdminEditReply(c, adid, adName, v.Oid, v.RpID, v.Type, v.Message, v.Remark)
c.JSON(nil, err)
return
}
// Compose compose struct
type Compose struct {
RpIDs []int64
Oids []int64
}
// adminDelReply del reply normal.
func adminDelReply(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
Moral int32 `form:"moral"`
Notify bool `form:"notify"`
FTime int64 `form:"ftime"`
AdName string `form:"adname"`
Reason int32 `form:"reason"`
FReason int32 `form:"freason"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
if uid, ok := c.Get("uid"); ok {
v.AdminID = uid.(int64)
}
if uname, ok := c.Get("username"); ok {
v.AdName = uname.(string)
}
for tp, com := range tMap {
err = rpSvc.AdminDeleteReply(c, v.AdminID, com.Oids, com.RpIDs, v.FTime, tp, v.Moral, v.Notify, v.AdName, v.Remark, v.Reason, v.FReason)
if !ecode.NothingFound.Equal(err) {
err = nil
}
}
c.JSON(nil, err)
return
}
// callbackDelReply del reply normal.
func callbackDelReply(c *bm.Context) {
v := new(struct {
Actor struct {
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
} `json:"actor"`
CTime int64 `json:"ctime"`
Object struct {
TagID int32 `json:"tag_id"`
DecreaseMoral int32 `json:"decrease_moral"`
BlockReasonID int32 `json:"block_reason_id"`
BlockDay int64 `json:"block_day"`
Reason string `json:"reason"`
DisposeMode int32 `json:"dispose_mode"`
State int32 `json:"state"`
RID int32 `json:"rid"`
} `json:"object"`
Target []struct {
OID int64 `json:"oid"`
FID int32 `json:"fid"`
EID int64 `json:"eid"`
State int32 `json:"state"`
} `json:"targets"`
})
var err error
err = c.BindWith(v, binding.JSON)
if err != nil {
return
}
log.Info("callbackDelReply V:%+v", *v)
if v.Object.State == 2 {
for _, t := range v.Target {
rpSvc.ReportIgnore(c, []int64{t.OID}, []int64{t.EID}, v.Actor.AdminID, v.Actor.AdminName, t.FID, v.Object.RID, v.Object.Reason, false)
}
} else if v.Object.State == 1 {
for _, t := range v.Target {
err = rpSvc.CallbackDeleteReply(c, v.Actor.AdminID, t.OID, t.EID, v.Object.BlockDay, t.FID, v.Object.DecreaseMoral, v.Actor.AdminName, v.Object.Reason, v.Object.TagID, v.Object.BlockReasonID)
}
} else if v.Object.State == 10 {
for _, t := range v.Target {
rpSvc.ReportStateSet(c, []int64{t.OID}, []int64{t.EID}, v.Actor.AdminID, v.Actor.AdminName, t.FID, 8, v.Object.Reason, false)
}
} else {
log.Warn("callbackDelReply RequestErr(%d,%d)", v.Object.State, v.Object.DisposeMode)
}
c.JSON(nil, err)
return
}
// adminTopReply top reply normal.
func adminTopReply(c *bm.Context) {
v := new(struct {
RpID int64 `form:"rpid" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
Type int32 `form:"type" validate:"required"`
AdminID int64 `form:"adid"`
Action uint32 `form:"action"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
err = rpSvc.AddTop(c, adid, adName, v.Oid, v.RpID, v.Type, v.Action)
c.JSON(nil, err)
return
}
// adminTopReplyLog get reply top log.
func adminTopReplyLog(c *bm.Context) {
v := new(struct {
Oid int64 `form:"oid" `
Type int32 `form:"type" `
StartTime string `form:"start_time"`
EndTime string `form:"end_time"`
Page int64 `form:"pn"`
PageSize int64 `form:"ps"`
Order string `form:"order"`
Sort string `form:"sort"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
sp := model.LogSearchParam{
Oid: v.Oid,
Type: v.Type,
CtimeFrom: v.StartTime,
CtimeTo: v.EndTime,
Pn: v.Page,
Ps: v.PageSize,
Order: v.Order,
Sort: v.Sort,
}
data, err := rpSvc.ReplyTopLog(c, sp)
res := map[string]interface{}{}
res["data"] = data
c.JSONMap(res, err)
return
}
// adminMarkAsSpam make a reply as spam
func adminMarkAsSpam(c *bm.Context) {
v := new(struct {
RpIDs []int64 `form:"rpid,split" validate:"required"`
Oids []int64 `form:"oid,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
err = rpSvc.MarkAsSpam(c, v.Oids, v.RpIDs, adid, adName, v.Remark)
c.JSON(nil, err)
return
}
// adminMarkAsSpam make a reply as spam
func adminReplyList(c *bm.Context) {
v := new(struct {
RpIDs []int64 `form:"eids,split" validate:"required"`
Oids []int64 `form:"oids,split" validate:"required"`
IDs []int64 `form:"ids,split" validate:"required"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
res := make(map[int64]*model.ReplyEx)
if len(v.RpIDs) == len(v.IDs) && len(v.IDs) == len(v.Oids) && len(v.RpIDs) > 0 {
for i := range v.IDs {
ex := &model.ReplyEx{}
ex.ID = v.RpIDs[i]
ex.Oid = v.Oids[i]
res[v.IDs[i]] = ex
}
var rpMap map[int64]*model.ReplyEx
rpMap, err = rpSvc.ReplyList(c, v.Oids, v.RpIDs)
for k, data := range res {
res[k] = rpMap[data.ID]
}
} else {
log.Error("adminReplyList request invalid(%v)", v)
err = ecode.RequestErr
}
c.JSON(res, err)
return
}
func topChildReply(c *bm.Context) {
v := new(struct {
Root int64 `form:"root" validate:"required"`
Child int64 `form:"child" validate:"required"`
Oid int64 `form:"oid" validate:"required"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(nil, rpSvc.TopChildReply(c, v.Root, v.Child, v.Oid))
}
func foldReply(c *bm.Context) {
v := new(struct {
Oids []int64 `form:"oids,split" validate:"required"`
RpIDs []int64 `form:"rpids,split" validate:"required"`
Types []int64 `form:"types,split" validate:"required"`
})
if err := c.Bind(v); err != nil {
return
}
if len(v.Oids) != len(v.RpIDs) || len(v.Oids) != len(v.Types) {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, rpSvc.FoldReplies(c, v.Oids, v.Types, v.RpIDs))
}

View File

@@ -0,0 +1,369 @@
package http
import (
"strconv"
"time"
"go-common/app/admin/main/reply/conf"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
func reportSearch(c *bm.Context) {
var (
err error
typ, startTime, endTime int64
page = int64(conf.Conf.Reply.PageNum)
pageSize = int64(conf.Conf.Reply.PageSize)
sp = &model.SearchReportParams{}
)
params := c.Request.Form
typeStr := params.Get("type")
oidStr := params.Get("oid")
uidStr := params.Get("uid")
startTimeStr := params.Get("start_time")
endTimeStr := params.Get("end_time")
pageStr := params.Get("page")
pageSizeStr := params.Get("pagesize")
// parse params
if typ, err = strconv.ParseInt(typeStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(type:%s) error(%v)", typeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
if oidStr != "" {
if sp.Oid, err = strconv.ParseInt(oidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(oid:%s) error(%v)", oidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if uidStr != "" {
if sp.UID, err = strconv.ParseInt(uidStr, 10, 64); err != nil {
log.Warn("strconv.ParseInt(uid:%s) error(%v)", uidStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if startTimeStr != "" {
if startTime, err = strconv.ParseInt(startTimeStr, 10, 64); err == nil {
sp.StartTime = time.Unix(startTime, 0).Format(model.DateFormat)
} else {
var t time.Time
t, err = time.Parse("2006-01-02", startTimeStr)
if err == nil {
sp.StartTime = t.Format(model.DateFormat)
} else {
sp.StartTime = startTimeStr
}
}
}
if endTimeStr != "" {
if endTime, err = strconv.ParseInt(endTimeStr, 10, 64); err == nil {
sp.EndTime = time.Unix(endTime, 0).Format(model.DateFormat)
} else {
var t time.Time
t, err = time.Parse("2006-01-02", endTimeStr)
if err == nil {
sp.EndTime = t.Format(model.DateFormat)
} else {
sp.EndTime = endTimeStr
}
}
} else if startTimeStr != "" {
t := time.Now()
sp.EndTime = t.Format(model.DateFormat)
}
if pageStr != "" {
if page, err = strconv.ParseInt(pageStr, 10, 64); err != nil || page < 1 {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
if pageSizeStr != "" {
if pageSize, err = strconv.ParseInt(pageSizeStr, 10, 64); err != nil || pageSize < 1 || pageSize > int64(conf.Conf.Reply.PageSize) {
log.Warn("strconv.ParseInt(page:%s) error(%v)", pageSizeStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
}
sp.Type = int32(typ)
sp.Reason = params.Get("reason")
sp.Typeids = params.Get("typeids")
sp.Keyword = params.Get("keyword")
sp.Nickname = params.Get("nickname")
sp.States = params.Get("states")
sp.Order = params.Get("order")
sp.Sort = params.Get("sort")
rpts, err := rpSvc.ReportSearch(c, sp, page, pageSize)
if err != nil {
log.Error("rpSvc.ReportSearch(%d,%d,%v) error(%v)", page, pageSize, sp, err)
c.JSON(nil, err)
return
}
res := map[string]interface{}{}
res["data"] = rpts
c.JSONMap(res, nil)
}
func reportDel(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
Moral int32 `form:"moral"`
Notify bool `form:"notify"`
FTime int64 `form:"ftime"`
AdName string `form:"adname"`
FReason int32 `form:"freason"`
Audit int32 `form:"audit"`
Reason int32 `form:"reason"`
Content string `form:"reason_content"`
})
err := c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
// reason没传修改为-1而不是0
params := c.Request.Form
if params.Get("reason") == "" {
v.Reason = -1
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
adname := v.AdName
if username, ok := c.Get("username"); ok {
adname = username.(string)
}
for tp, com := range tMap {
err = rpSvc.ReportDel(c, com.Oids, com.RpIDs, adid, v.FTime, tp, v.Audit, v.Moral, v.Reason, v.FReason, v.Notify, adname, v.Remark, v.Content)
if err != nil {
c.JSON(nil, err)
return
}
}
c.JSON(nil, err)
return
}
func reportIgnore(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
Audit int32 `form:"audit"`
})
err := c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
for tp, com := range tMap {
err = rpSvc.ReportIgnore(c, com.Oids, com.RpIDs, adid, adName, tp, v.Audit, v.Remark, true)
}
c.JSON(nil, err)
return
}
func reportRecover(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
Audit int32 `form:"audit"`
})
err := c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
for tp, com := range tMap {
err = rpSvc.ReportRecover(c, com.Oids, com.RpIDs, adid, tp, v.Audit, v.Remark)
}
c.JSON(nil, err)
}
func reportTransfer(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
Remark string `form:"remark"`
Audit int32 `form:"audit"`
})
err := c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
adid := v.AdminID
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
for tp, com := range tMap {
err = rpSvc.ReportTransfer(c, com.Oids, com.RpIDs, adid, adName, tp, v.Audit, v.Remark)
}
c.JSON(nil, err)
}
func reportStateSet(c *bm.Context) {
v := new(struct {
RpID []int64 `form:"rpid,split" validate:"required"`
Oid []int64 `form:"oid,split" validate:"required"`
Type []int32 `form:"type,split" validate:"required"`
AdminID int64 `form:"adid"`
AdName string `form:"adname"`
Remark string `form:"remark"`
State int32 `form:"state"`
})
err := c.Bind(v)
if err != nil {
return
}
if len(v.RpID) == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if len(v.RpID) != len(v.Oid) || len(v.RpID) != len(v.Type) {
c.JSON(nil, ecode.RequestErr)
return
}
tMap := make(map[int32]*Compose)
for i, tp := range v.Type {
if c, ok := tMap[tp]; ok {
c.Oids = append(c.Oids, v.Oid[i])
c.RpIDs = append(c.RpIDs, v.RpID[i])
} else {
c = &Compose{
Oids: []int64{v.Oid[i]},
RpIDs: []int64{v.RpID[i]},
}
tMap[tp] = c
}
}
if uid, ok := c.Get("uid"); ok {
v.AdminID = uid.(int64)
}
if uname, ok := c.Get("username"); ok {
v.AdName = uname.(string)
}
for tp, com := range tMap {
err = rpSvc.ReportStateSet(c, com.Oids, com.RpIDs, v.AdminID, v.AdName, tp, v.State, v.Remark, true)
}
c.JSON(nil, err)
}

View File

@@ -0,0 +1,133 @@
package http
import (
"strconv"
"time"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
const (
_remarkLength = 200
)
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)
c.JSON(nil, ecode.RequestErr)
return
}
tp, err := strconv.ParseInt(tpStr, 10, 64)
if err != nil {
log.Warn("strconv.ParseInt(%s) error(%v)", tpStr, err)
c.JSON(nil, ecode.RequestErr)
return
}
sub, err := rpSvc.Subject(c, oid, int32(tp))
if err != nil {
log.Error("rpSvr.AdminGetSubjectState(oid%d,tp,%d)error(%v)", oid, int32(tp))
c.JSON(nil, err)
return
}
c.JSON(sub, nil)
return
}
// adminSubjectState modify subject state
func adminSubjectState(c *bm.Context) {
v := new(struct {
Oid []int64 `form:"oid,split" validate:"required"`
Type int32 `form:"type" validate:"required"`
State int32 `form:"state"`
Remark string `form:"remark"`
})
err := c.Bind(v)
if err != nil {
return
}
var adid int64
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
fails, err := rpSvc.ModifySubState(c, adid, adName, v.Oid, v.Type, v.State, v.Remark)
c.JSON(fails, err)
return
}
// SubLogSearch returns all subjects in recent 3 months by default,
// accept start time, end time, page, pagesize, order, sort as parameters.
func SubLogSearch(c *bm.Context) {
v := new(struct {
Oid int64 `form:"oid"`
Type int32 `form:"type"`
StartTime string `form:"start_time"`
EndTime string `form:"end_time"`
Page int64 `form:"pn"`
PageSize int64 `form:"ps"`
Order string `form:"order"`
Sort string `form:"sort"`
})
var err error
err = c.Bind(v)
if err != nil {
return
}
if v.EndTime == "" {
v.EndTime = time.Now().Format(model.DateFormat)
}
// 默认只展示3个月内的数据
if v.StartTime == "" {
v.StartTime = time.Now().AddDate(0, -3, 0).Format(model.DateFormat)
}
sp := model.LogSearchParam{
Oid: v.Oid,
Type: v.Type,
CtimeFrom: v.StartTime,
CtimeTo: v.EndTime,
Pn: v.Page,
Ps: v.PageSize,
Order: v.Order,
Sort: v.Sort,
}
data, err := rpSvc.SubjectLog(c, sp)
res := map[string]interface{}{}
res["data"] = data
c.JSONMap(res, err)
return
}
// SubFreeze freeze or unfreeze the comments.
func SubFreeze(c *bm.Context) {
v := new(struct {
Oid []int64 `form:"oid,split"`
Type int32 `form:"type"`
Freeze int32 `form:"freeze"`
Remark string `form:"remark"`
})
err := c.Bind(v)
if err != nil {
return
}
var adid int64
if uid, ok := c.Get("uid"); ok {
adid = uid.(int64)
}
var adName string
if uname, ok := c.Get("username"); ok {
adName = uname.(string)
}
fails, err := rpSvc.FreezeSub(c, adid, adName, v.Oid, v.Type, v.Freeze, v.Remark)
c.JSON(fails, err)
return
}

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 = ["report_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"action.go",
"admin.go",
"business.go",
"common.go",
"config.go",
"csv.go",
"emoji.go",
"monitor.go",
"moral.go",
"notice.go",
"reply.go",
"report.go",
"sql.go",
"subject.go",
],
importpath = "go-common/app/admin/main/reply/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/relation/model:go_default_library",
"//library/ecode:go_default_library",
"//library/time: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,21 @@
package model
import (
xtime "go-common/library/time"
)
// Action 点赞或踩
const (
ActionNormal int32 = 0 // 未踩赞
ActionLike int32 = 1 // 赞
ActionHate int32 = 2 // 踩
)
// Action reply action info
type Action struct {
ID int64 `json:"-"`
RpID int64 `json:"rpid"`
Action int8 `json:"action"`
Mid int64 `json:"mid"`
CTime xtime.Time `json:"-"`
}

View File

@@ -0,0 +1,99 @@
package model
const (
// AdminIsNotReport 不是举报记录
AdminIsNotReport int32 = 0
// AdminIsReport 是举报记录
AdminIsReport = 1
// AdminIsNotNew 不是该评论最新的
AdminIsNotNew int32 = 0
// AdminIsNew 是该评论最新的
AdminIsNew = 1
// AdminOperDelete 管理员删除评论
AdminOperDelete int32 = 0
// AdminOperDeleteByReport 管理员通过举报删除 NOTE Deprecated
AdminOperDeleteByReport = 1
// AdminOperIgnoreReport 管理员忽略举报 NOTE Deprecated
AdminOperIgnoreReport = 2
// AdminOperRecover 管理员恢复评论
AdminOperRecover = 3
// AdminOperEdit 管理员编辑内容
AdminOperEdit = 4
// AdminOperPass 管理通过待审
AdminOperPass = 5
// AdminOperSubState 修改主题的状态
AdminOperSubState = 6
// AdminOperSubTop 置顶评论
AdminOperSubTop = 7
// AdminOperSubMid 修改主题的mid
AdminOperSubMid = 8
// AdminOperRptIgnore1 举报一审忽略
AdminOperRptIgnore1 = 9
// AdminOperRptIgnore2 举报二审忽略
AdminOperRptIgnore2 = 10
// AdminOperRptDel1 举报一审删除
AdminOperRptDel1 = 11
// AdminOperRptDel2 举报二审删除
AdminOperRptDel2 = 12
// AdminOperRptRecover1 举报一审恢复
AdminOperRptRecover1 = 13
// AdminOperRptRecover2 举报二审恢复
AdminOperRptRecover2 = 14
// AdminOperActionSet 对点赞点踩设置
AdminOperActionSet = 15
// AdminOperDeleteUp up主删除评论
AdminOperDeleteUp = 16
// AdminOperDeleteUser 用户删除评论
AdminOperDeleteUser = 17
// AdminOperDeleteAssist 协管删除评论
AdminOperDeleteAssist = 18
// AdminOperSubMonitor 设置监控状态
AdminOperSubMonitor = 19
// AdminOperRptTransfer1 转一审
AdminOperRptTransfer1 = 20
// AdminOperRptTransfer2 转二审
AdminOperRptTransfer2 = 21
// AdminOperRptTransferArbitration 移交仲裁
AdminOperRptTransferArbitration = 22
// AdminOperRptStateSet 设置举报状态
AdminOperRptStateSet = 23
// AdminOperSubMonitorOpen 先发后审打开
AdminOperSubMonitorOpen = 24
// AdminOperSubMonitorClose 先发后审关闭
AdminOperSubMonitorClose = 25
// AdminOperSubAuditOpen 先审后发打开
AdminOperSubAuditOpen = 26
// AdminOperSubAuditClose 先审后发关闭
AdminOperSubAuditClose = 27
// AdminOperMarkSpam 标记为垃圾
AdminOperMarkSpam = 28
)
// AdminLog log.
type AdminLog struct {
ID int64 `json:"id"`
Type int8 `json:"type"`
Oid int64 `json:"oid"`
ReplyID int64 `json:"reply_id"`
AdminID int64 `json:"admin_id"`
Result string `json:"result"`
Remark string `json:"remark"`
IsNew int8 `json:"is_new"`
IsReport int8 `json:"is_report"`
State int8 `json:"state"`
CTime string `json:"ctime"`
MTime string `json:"mtime"`
}
// SearchAdminLog log.
type SearchAdminLog struct {
Type int8 `json:"type"`
Oid int64 `json:"oid"`
ReplyID int64 `json:"rpid"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Result string `json:"opresult"`
Remark string `json:"opremark"`
State int8 `json:"state"`
CTime string `json:"opctime"`
}

View File

@@ -0,0 +1,10 @@
package model
// Business Business
type Business struct {
Type int32 `json:"type"`
Name string `json:"name"`
Appkey string `json:"app_key"`
Remark string `json:"remark"`
Alias string `json:"alias"`
}

View File

@@ -0,0 +1,49 @@
package model
import "errors"
var (
// ErrSearchReport search report error
ErrSearchReport = errors.New("search report error")
// ErrSearchReply search reply error
ErrSearchReply = errors.New("search reply error")
// ErrSearchMonitor search monitor error
ErrSearchMonitor = errors.New("search monitor error")
// ErrMsgSend send message error
ErrMsgSend = errors.New("send message error")
// AttrNo attribute no
AttrNo = uint32(0)
// AttrYes attribute yes
AttrYes = uint32(1)
// EventReportAdd event add a report
EventReportAdd = "report_add"
// EventReportDel event del a report
EventReportDel = "report_del"
// EventReportIgnore event ignore a report
EventReportIgnore = "report_ignore"
// EventReportRecover event recover a report
EventReportRecover = "report_recover"
)
const (
// WeightLike like sort weight
WeightLike = 2
// WeightHate hate sort weight
WeightHate = 4
)
// Pager page info.
type Pager struct {
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
Total int64 `json:"total"`
}
// NewPager NewPager
type NewPager struct {
Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
}

View File

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

View File

@@ -0,0 +1,45 @@
package model
import (
"encoding/csv"
"fmt"
"net/http"
"github.com/pkg/errors"
)
// CSV Comma-Separated Values struct.
type CSV struct {
Data [][]string
Title string
}
var csvContentType = []string{"text/csv; charset=utf-8"}
// Render (CSV) writes data with CSV ContentType.
func (c CSV) Render(w http.ResponseWriter) (err error) {
c.WriteContentType(w)
writer := csv.NewWriter(w)
bomUtf8 := []byte{0xEF, 0xBB, 0xBF}
writer.Write([]string{string(bomUtf8[:])})
writer.WriteAll(c.Data)
if err = writer.Error(); err != nil {
err = errors.WithStack(err)
}
return
}
func writeContentType(w http.ResponseWriter, value []string, title string) {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = value
}
if title != "" {
header["Content-Disposition"] = append(header["Content-Disposition"], fmt.Sprintf("attachment; filename=%s.csv", title))
}
}
// WriteContentType write CSV ContentType.
func (c CSV) WriteContentType(w http.ResponseWriter) {
writeContentType(w, csvContentType, c.Title)
}

View File

@@ -0,0 +1,23 @@
package model
// Emoji Emoji
type Emoji struct {
ID int64 `json:"id"`
PackageID int64 `json:"package_id"`
Name string `json:"name"`
URL string `json:"url"`
State int32 `json:"state"`
Sort int32 `json:"sort"`
Remark string `json:"remark"`
}
// EmojiPackage emoji package
type EmojiPackage struct {
ID int64 `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
State int32 `json:"state"`
Sort int32 `json:"sort"`
Remark string `json:"remark"`
Emojis []*Emoji `json:"emojis"`
}

View File

@@ -0,0 +1,113 @@
package model
const (
// MonitorClose 取消监控
MonitorClose = int32(0)
// MonitorOpen 打开监控
MonitorOpen = int32(1)
// MonitorAudit 先审后发
MonitorAudit = int32(2)
// MonitorStatsAll all
MonitorStatsAll = 1
// MonitorStatsUser all
MonitorStatsUser = 2
)
// SearchMonitorParams search params.
type SearchMonitorParams struct {
Mode int8
Type int8
Oid int64
UID int64
NickName string
Keyword string
Sort string
Order string
}
// SearchMonitor search monitor.
type SearchMonitor struct {
ID int64 `json:"id"`
Oid int64 `json:"oid"`
OidStr string `json:"oid_str"`
Type int8 `json:"type"`
Mid int64 `json:"mid"`
State int8 `json:"state"`
Attr int32 `json:"attr"`
Ctime string `json:"ctime"`
Mtime string `json:"mtime"`
Title string `json:"title"`
Uname string `json:"uname"`
UnverifyNmu int `json:"unverify_num"`
MCount int32 `json:"mcount"`
DocID string `json:"doc_id"`
Remark string `json:"remark"`
}
// SearchMonitorResult search result.
type SearchMonitorResult struct {
Code int `json:"code,omitempty"`
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
PageCount int64 `json:"pagecount"`
Total int64 `json:"total"`
Order string `json:"order"`
Result []*SearchMonitor `json:"result"`
Message string `json:"msg,omitempty"`
}
// StatsMonitor stats monitor.
type StatsMonitor struct {
Date string `json:"date"`
AdminID int64 `json:"adminid"`
MonitorTotal int64 `json:"monitor_total"`
MonitorPending int64 `json:"monitor_pending"`
MonitorPass int64 `json:"monitor_pass"`
MonitorDel int64 `json:"monitor_del"`
MonitorAvgCost string `json:"monitor_avg_cost"`
}
// StatsMonitorResult search result.
type StatsMonitorResult struct {
Code int `json:"code,omitempty"`
Page int `json:"page"`
PageSize int `json:"pagesize"`
PageCount int `json:"pagecount"`
Total int `json:"total"`
Order string `json:"order"`
Message string `json:"msg,omitempty"`
Result []*StatsMonitor `json:"result"`
}
// MonitorLogResult MonitorLogResult
type MonitorLogResult struct {
Logs []*MonitorLog `json:"logs"`
Page Page `json:"page"`
Order string `json:"order"`
Sort string `json:"sort"`
}
// Page Page
type Page struct {
Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
}
// MonitorLog MonitorLog
type MonitorLog struct {
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Oid int64 `json:"oid"`
OidStr string `json:"oid_str"`
Type int32 `json:"type"`
Title string `json:"title"`
RedirectURL string `json:"redirect_url"`
Remark string `json:"remark"`
UserName string `json:"username"`
Mid int64 `json:"mid"`
CTime string `json:"ctime"`
LogState int64 `json:"log_state"`
State int64 `json:"state"`
}

View File

@@ -0,0 +1,120 @@
package model
import "fmt"
const (
// ReportReasonOther 其他
ReportReasonOther = int32(0)
// ReportReasonAd 广告
ReportReasonAd = int32(1)
// ReportReasonPorn 色情
ReportReasonPorn = int32(2)
// ReportReasonMeaningless 刷屏
ReportReasonMeaningless = int32(3)
// ReportReasonProvoke 引站
ReportReasonProvoke = int32(4)
// ReportReasonSpoiler 剧透
ReportReasonSpoiler = int32(5)
// ReportReasonPolitic 政治
ReportReasonPolitic = int32(6)
// ReportReasonAttack 人身攻击
ReportReasonAttack = int32(7)
// ReportReasonUnrelated 视频不相关
ReportReasonUnrelated = int32(8)
// ReportReasonProhibited 违禁
ReportReasonProhibited = int32(9)
// ReportReasonVulgar 低俗
ReportReasonVulgar = int32(10)
// ReportReasonIllegalWebsite 非法网站
ReportReasonIllegalWebsite = int32(11)
// ReportReasonGamblingFraud 赌博诈骗
ReportReasonGamblingFraud = int32(12)
// ReportReasonRumor 传播不实信息
ReportReasonRumor = int32(13)
// ReportReasonAbetting 怂恿教唆信息
ReportReasonAbetting = int32(14)
// ReportReasonPrivacyInvasion 侵犯隐私
ReportReasonPrivacyInvasion = int32(15)
// ReportReasonUnlimitedSign 抢楼
ReportReasonUnlimitedSign = int32(16)
// ForbidReasonSpoiler 发布剧透信息
ForbidReasonSpoiler = int32(10)
// ForbidReasonAd 发布垃圾广告信息
ForbidReasonAd = int32(6)
// ForbidReasonUnlimitedSign 抢楼
ForbidReasonUnlimitedSign = int32(2)
// ForbidReasonMeaningless 刷屏
ForbidReasonMeaningless = int32(1)
// ForbidReasonProvoke 发布引战言论
ForbidReasonProvoke = int32(9)
// ForbidReasonVulgar 发布低俗信息
ForbidReasonVulgar = int32(14)
// ForbidReasonGamblingFraud 发布赌博诈骗信息
ForbidReasonGamblingFraud = int32(4)
// ForbidReasonPorn 发布色情信息
ForbidReasonPorn = int32(13)
// ForbidReasonRumor 发布传播不实信息
ForbidReasonRumor = int32(18)
// ForbidReasonIllegalWebsite 发布非法网站信息
ForbidReasonIllegalWebsite = int32(17)
// ForbidReasonAbetting 发布怂恿教唆信息
ForbidReasonAbetting = int32(19)
// ForbidReasonProhibited 发布违禁信息
ForbidReasonProhibited = int32(5)
// ForbidReasonPrivacyInvasion 涉及侵犯他人隐私
ForbidReasonPrivacyInvasion = int32(8)
// ForbidReasonAttack 发布人身攻击言论
ForbidReasonAttack = int32(7)
// ForbidReasonInaptitude 发布不适宜内容
ForbidReasonInaptitude = int32(28)
)
var (
// NotifyComRules 社区规则
NotifyComRules = fmt.Sprintf(`评论区是公众场所,而非私人场所,具体规范烦请参阅#{《社区规则》}{"%s"},良好的社区氛围需要大家一起维护!`, "http://www.bilibili.com/blackboard/blackroom.html")
// NotifyComRulesReport 举报
NotifyComRulesReport = "感谢您对bilibili社区秩序的维护哔哩哔哩 (゜-゜)つロ 干杯~"
// NotifyComUnrelated NotifyComUnrelated
NotifyComUnrelated = "bilibili倡导发送与视频相关的评论希望大家尊重作品尊重UP主。良好的社区氛围需要大家一起维护"
// NotifyComProvoke NotifyComProvoke
NotifyComProvoke = "bilibili倡导平等友善的交流。良好的社区氛围需要大家一起维护"
// NofityComProhibited NofityComProhibited
NofityComProhibited = fmt.Sprintf(`请自觉遵守国家相关法律法规及#{《社区规则》}{"%s"}bilibili良好的社区氛围需要大家一起维护`, "http://www.bilibili.com/blackboard/blackroom.html")
// ReportReason 举报理由类型
ReportReason = map[int32]string{
ReportReasonAd: "内容涉及垃圾广告",
ReportReasonPorn: "内容涉及色情",
ReportReasonMeaningless: "刷屏",
ReportReasonProvoke: "内容涉及引战",
ReportReasonSpoiler: "内容涉及视频剧透",
ReportReasonPolitic: "内容涉及政治相关",
ReportReasonAttack: "内容涉及人身攻击",
ReportReasonUnrelated: "视频不相关",
ReportReasonProhibited: "内容涉及违禁相关",
ReportReasonVulgar: "内容涉及低俗信息",
ReportReasonIllegalWebsite: "内容涉及非法网站信息",
ReportReasonGamblingFraud: "内容涉及赌博诈骗信息",
ReportReasonRumor: "内容涉及传播不实信息",
ReportReasonAbetting: "内容不适宜",
ReportReasonPrivacyInvasion: "内容涉及侵犯他人隐私",
ReportReasonUnlimitedSign: "抢楼",
}
// ForbidReason 封禁理由类型
ForbidReason = map[int32]string{
ForbidReasonSpoiler: "发布剧透信息",
ForbidReasonAd: "发布垃圾广告信息",
ForbidReasonUnlimitedSign: "抢楼",
ForbidReasonMeaningless: "刷屏",
ForbidReasonProvoke: "发布引战言论",
ForbidReasonVulgar: "发布低俗信息",
ForbidReasonGamblingFraud: "发布赌博诈骗信息",
ForbidReasonPorn: "发布色情信息",
ForbidReasonRumor: "发布传播不实信息",
ForbidReasonIllegalWebsite: "发布非法网站信息",
ForbidReasonAbetting: "发布怂恿教唆信息",
ForbidReasonProhibited: "发布违禁信息",
ForbidReasonPrivacyInvasion: "涉及侵犯他人隐私",
ForbidReasonAttack: "发布人身攻击言论",
ForbidReasonInaptitude: "发布不适宜内容",
}
)

View File

@@ -0,0 +1,68 @@
package model
import (
xtime "go-common/library/time"
)
// NoticeCondition NoticeCondition
type NoticeCondition uint8
// NoticeStatus NoticeStatus
type NoticeStatus uint8
// NoticePlat NoticePlat
type NoticePlat uint8
const (
// PlatUnknow PlatUnknow
PlatUnknow NoticePlat = 0
// PlatWeb PlatUnknow
PlatWeb NoticePlat = 1
// PlatAndroid PlatAndroid
PlatAndroid NoticePlat = 2
// PlatIPhone PlatIPhone
PlatIPhone NoticePlat = 3
// PlatWpM wp mobile
PlatWpM NoticePlat = 4
// PlatIPad PlatIPad
PlatIPad NoticePlat = 5
// PlatPadHd ipad hd
PlatPadHd NoticePlat = 6
// PlatWpPc win10
PlatWpPc NoticePlat = 7
)
const (
// StatusOffline StatusOffline
StatusOffline NoticeStatus = 0
// StatusOnline StatusOnline
StatusOnline NoticeStatus = 1
)
const (
// ConditionEQ ConditionEQ
ConditionEQ NoticeCondition = 0 // condition equal
// ConditionGT ConditionGT
ConditionGT NoticeCondition = 1 // greater
// ConditionLT ConditionLT
ConditionLT NoticeCondition = 2 // less
)
// Notice reply's public notice
type Notice struct {
ID uint32 `json:"id"`
Plat NoticePlat `json:"plat"`
Version string `json:"version"`
Condition NoticeCondition `json:"condi"`
Build uint32 `json:"build"`
Title string `json:"title"`
Content string `json:"content"`
Link string `json:"link"`
StartTime xtime.Time `json:"stime"`
EndTime xtime.Time `json:"etime"`
Status NoticeStatus `json:"status"`
CreateTime xtime.Time `json:"ctime"`
ModifyTime xtime.Time `json:"mtime"`
//client's program type
ClientType string `json:"client_type"`
}

View File

@@ -0,0 +1,363 @@
package model
import (
"encoding/binary"
"encoding/json"
"net"
"strconv"
"strings"
"time"
rl "go-common/app/service/main/relation/model"
xtime "go-common/library/time"
)
// State 评论状态
const (
StateNormal int32 = 0 // 正常
StateHidden int32 = 1 // up主隐藏
StateFiltered int32 = 2 // 敏感词过滤 @Deprecated to use attr 3
StateDelAdmin int32 = 3 // 管理员删除
StateDelUser int32 = 4 // 用户删除
StateMonitor int32 = 5 // 监控中
StateGarbage int32 = 6 // 大数据过虑 @Deprecated to use attr 2
StateTopAdmin int32 = 7 // 管理员置顶 @Deprecated to use attr 1
StateDelUpper int32 = 8 // up主删除
StateBlacklist int32 = 9 // 黑名单屏蔽
StateDelAssist int32 = 10 // 协管删除
StatePending int32 = 11 // 先审后发
StateFolded int32 = 12 // 被折叠
)
// Attr 评论属性
const (
AttrTopAdmin uint32 = 0 // 管理员置顶
AttrTopUpper uint32 = 1 // up主置顶
AttrGarbage uint32 = 2 // 大数据过滤
AttrFiltered uint32 = 3 // 敏感词过滤
// 有子评论被折叠
AttrFolded uint32 = 7
)
// SortBy 排序方式
const (
SortByFloor int32 = 0 // 按楼层排序
SortByCount int32 = 1 // 按评论数排序
SortByLike int32 = 2 // 按点赞数排序
)
// 折叠评论类型
const (
FolderKindSub = "s"
FolderKindRoot = "r"
)
// SearchParams reply params.
type SearchParams struct {
Type int32
Oid int64
TypeIds string
Keyword string
KeywordHigh string
UID int64
Uname string
AdminID int64
AdminName string
Begin time.Time
End time.Time
States string
IP int64
Attr string
Sort string
Order string
}
// ReplySearchResponse ReplySearchResponse
type ReplySearchResponse struct {
SearchResult
Pager Pager `json:"pager"`
}
// SearchResult search result.
type SearchResult struct {
Code int `json:"code"`
Message string `json:"msg,omitempty"`
Order string `json:"order"`
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
PageCount int64 `json:"pagecount"`
Total int64 `json:"total"`
Result []*SearchReply `json:"result"`
}
// SearchReply search reply.
type SearchReply struct {
// 评论基础信息
ID int64 `json:"id"`
Type int8 `json:"type"`
Oid int64 `json:"oid"`
OidStr string `json:"oid_str"`
State int8 `json:"state"`
Floor int64 `json:"floor"`
Ctime string `json:"ctime"`
Mtime string `json:"mtime"`
Attr Attr `json:"attr"`
Title string `json:"title"`
// 评论人的相关信息
Mid int64 `json:"mid"`
Stat *rl.Stat `json:"stat"`
Replier string `json:"replier"`
IP IP `json:"ip"`
Message string `json:"message"`
Typeid int `json:"typeid"`
Root int `json:"root"`
// 后台操作信息
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Opremark string `json:"opremark"`
Opresult string `json:"opresult"`
OpCtime string `json:"opctime"`
RedirectURL string `json:"redirect_url"`
// 搜索返回的额外数据
DocID string `json:"doc_id"`
}
type IP int64
func (ip *IP) UnmarshalJSON(b []byte) error {
if string(b) == "" {
return nil
}
str := strings.Trim(string(b), `"`)
ipI := net.ParseIP(str).To4()
if ipI == nil {
return nil
}
*ip = IP(int64(binary.BigEndian.Uint32(ipI)))
return nil
}
type Attr []int32
func (a *Attr) UnmarshalJSON(b []byte) error {
var s []int32
err := json.Unmarshal(b, &s)
if err != nil {
attr, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
return err
}
var i int32 = 1
for attr != 0 && i < 64 {
if attr&1 == 1 {
*a = append(*a, i)
}
attr = attr >> 1
i++
}
} else {
*a = s
}
return nil
}
// Reply info.b
type ReplyEx struct {
Reply
IsUp bool `json:"is_up"`
RootFloor int32 `json:"root_floor"`
}
// Reply info.
type Reply struct {
ID int64 `json:"rpid"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Mid int64 `json:"mid"`
Root int64 `json:"root"`
Parent int64 `json:"parent"`
Dialog int64 `json:"dialog"`
Count int32 `json:"count"`
MCount int32 `json:"mcount"`
RCount int32 `json:"rcount"`
Floor int32 `json:"floor"`
State int32 `json:"state"`
Attr uint32 `json:"attr"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"-"`
// action info
Like int32 `json:"like"`
Hate int32 `json:"hate"`
Action int32 `json:"action"`
Content *ReplyContent `json:"content"`
}
// HasFolded ...
func (r *Reply) HasFolded() bool {
return r.AttrVal(AttrFolded) == AttrYes
}
// MarkHasFolded ...
func (r *Reply) MarkHasFolded() {
r.AttrSet(AttrYes, AttrFolded)
}
// UnmarkHasFolded ...
func (r *Reply) UnmarkHasFolded() {
r.AttrSet(AttrNo, AttrFolded)
}
// DenyFolded ...
func (r *Reply) DenyFolded() bool {
return r.IsTop() || !r.AllowFoldState() || r.Type == SubTypeArticle
}
// AllowFoldState ...
func (r *Reply) AllowFoldState() bool {
return r.State == StateNormal || r.State == StateHidden || r.State == StateFiltered || r.State == StateGarbage
}
// IsFolded .
func (r *Reply) IsFolded() bool {
return r.State == StateFolded
}
// IsRoot root.
func (r *Reply) IsRoot() bool {
return r.Root == 0
}
// IsTop top.
func (r *Reply) IsTop() bool {
if r.Attr != 0 && (r.AttrVal(AttrTopAdmin) == 1 || r.AttrVal(AttrTopUpper) == 1) {
return true
}
return false
}
// IsDeleted deleted.
func (r *Reply) IsDeleted() bool {
return r.State == StateDelUser || r.State == StateDelUpper || r.State == StateDelAdmin
}
// 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)
}
// AttrSet set attr of reply'attr
func (r *Reply) AttrSet(v uint32, bit uint32) {
r.Attr = r.Attr&(^(1 << bit)) | (v << bit)
}
// IsNormal IsNormal
func (r *Reply) IsNormal() bool {
return r.State == StateNormal || r.State == StateHidden || r.State == StateFiltered || r.State == StateMonitor || r.State == StateGarbage || r.State == StateTopAdmin || r.State == StateFolded
}
func (r *Reply) IsPending() bool {
return r.State == StatePending
}
// LegalSubjectType LegalSubjectType
func LegalSubjectType(tp int32) bool {
return SubTypeArchive <= tp && tp <= SubTypeComicEpisode
}
// ReplyContent define reply content
type ReplyContent struct {
ID int64 `json:"-"`
Message string `json:"message"`
Ats Int64Bytes `json:"ats,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:"-"`
}
// LogSearchParam LogSearchParam
type LogSearchParam struct {
Oid int64
Type int32
Mid int64
CtimeFrom string
CtimeTo string
Action string
Pn int64
Ps int64
Other int64
Sort string
Order string
Group string
Appid string
}
// ReplyTopLogResult ReplyTopLogResult
type ReplyTopLogResult struct {
Logs []*ReplyTopLog `json:"logs"`
Page Page `json:"page"`
Order string `json:"order"`
Sort string `json:"sort"`
}
// ReplyTopLog ReplyTopLog
type ReplyTopLog struct {
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Title string `json:"title"`
RedirectURL string `json:"redirect_url"`
Remark string `json:"remark"`
UserName string `json:"username"`
Mid int64 `json:"mid"`
CTime string `json:"ctime"`
RpID int64 `json:"rpid"`
Action int64 `json:"action"`
}
// ExportedReply exported reply struct
type ExportedReply struct {
ID int64 `json:"rpid"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Mid int64 `json:"mid"`
Root int64 `json:"root"`
Parent int64 `json:"parent"`
Count int32 `json:"count"`
RCount int32 `json:"rcount"`
Like int32 `json:"like"`
Hate int32 `json:"hate"`
Floor int32 `json:"floor"`
State int32 `json:"state"`
Attr int32 `json:"attr"`
CTime time.Time `json:"ctime"`
Message string `json:"message"`
}
// String convert ExportedReply to string
func (e *ExportedReply) String() (s []string) {
s = append(s, strconv.FormatInt(e.ID, 10))
s = append(s, strconv.FormatInt(e.Oid, 10))
s = append(s, strconv.FormatInt(int64(e.Type), 10))
s = append(s, strconv.FormatInt(e.Mid, 10))
s = append(s, strconv.FormatInt(e.Root, 10))
s = append(s, strconv.FormatInt(e.Parent, 10))
s = append(s, strconv.FormatInt(int64(e.Count), 10))
s = append(s, strconv.FormatInt(int64(e.RCount), 10))
s = append(s, strconv.FormatInt(int64(e.Like), 10))
s = append(s, strconv.FormatInt(int64(e.Hate), 10))
s = append(s, strconv.FormatInt(int64(e.Floor), 10))
s = append(s, strconv.FormatInt(int64(e.State), 10))
s = append(s, strconv.FormatInt(int64(e.Attr), 10))
s = append(s, e.CTime.String())
s = append(s, e.Message)
return
}

View File

@@ -0,0 +1,183 @@
package model
import xtime "go-common/library/time"
var (
// DateFormat date time format
DateFormat = "2006-01-02 15:04:05"
// DateSimpleFormat date simple format
DateSimpleFormat = "2006-01-02"
)
const (
// ReportStateNew 待一审
ReportStateNew = int32(0)
// ReportStateDelete 移除
ReportStateDelete = int32(1)
// ReportStateIgnore 忽略
ReportStateIgnore = int32(2)
// ReportStateDelete1 一审移除
ReportStateDelete1 = int32(3)
// ReportStateNew2 待二审
ReportStateNew2 = int32(4)
// ReportStateDelete2 二审移除
ReportStateDelete2 = int32(5)
// ReportStateIgnore2 二审忽略
ReportStateIgnore2 = int32(6)
// ReportStateIgnore1 一审忽略
ReportStateIgnore1 = int32(7)
// ReportStateTransferred 举报转移风纪委
ReportStateTransferred = int32(8)
// ReportUserStateNew 新增
ReportUserStateNew = int32(0)
// ReportUserStateReported 已反馈
ReportUserStateReported = int32(1)
// ReportAttrTransferred 是否从待一审\二审转换成待二审\一审
ReportAttrTransferred = uint32(0)
// AuditTypeFirst 一审
AuditTypeFirst = int32(1)
// AuditTypeSecond 二审
AuditTypeSecond = int32(2)
// ReportActionReplyPass action reply_pass
ReportActionReplyPass = "reply_pass"
// ReportActionReplyDel action reply_del
ReportActionReplyDel = "reply_del"
// ReportActionReplyEdit action reply_edit
ReportActionReplyEdit = "reply_edit"
// ReportActionReplyRecover action reply_recover
ReportActionReplyRecover = "reply_recover"
// ReportActionReplyTop action 置顶
ReportActionReplyTop = "top"
// ReportActionReplyMonitor action 监控
ReportActionReplyMonitor = "monitor"
// ReportActionReplyGarbage action reply_garbage
ReportActionReplyGarbage = "reply_garbage"
// ReportActionReportIgnore1 action report_ignore_1
ReportActionReportIgnore1 = "report_ignore_1"
// ReportActionReportIgnore2 action report_ignore_2
ReportActionReportIgnore2 = "report_ignore_2"
// ReportActionReportDel1 action report_del_1
ReportActionReportDel1 = "report_del_1"
// ReportActionReportDel2 action report_del_2
ReportActionReportDel2 = "report_del_2"
// ReportActionReport1To2 action report_1to2
ReportActionReport1To2 = "report_1to2"
// ReportActionReport2To1 action report_2to1
ReportActionReport2To1 = "report_2to1"
// ReportActionReportArbitration action 众裁
ReportActionReportArbitration = "report_arbitration"
)
// Report report info.
type Report struct {
ID int64 `json:"id"`
RpID int64 `json:"rpid"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Mid int64 `json:"mid"`
Reason int32 `json:"reason"`
Content string `json:"content"`
Count int32 `json:"count"`
Score int `json:"score"`
State int32 `json:"state"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
Attr uint32 `json:"attr"`
ReplyCtime xtime.Time `json:"-"`
}
// AttrVal return attr val.
func (r *Report) AttrVal(bit uint32) uint32 {
return (r.Attr >> bit) & uint32(1)
}
// AttrSet set attr of ReplyReport'attr
func (r *Report) AttrSet(v uint32, bit uint32) {
r.Attr = r.Attr&(^(1 << bit)) | (v << bit)
}
// SearchReportParams search params.
type SearchReportParams struct {
Type int32
Oid int64
UID int64
StartTime string
EndTime string
Reason string
Typeids string
Keyword string
Nickname string
States string
Order string
Sort string
}
// 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 int32 `json:"reason"`
Content string `json:"content"`
State int32 `json:"state"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
}
// SearchReport search report.
type SearchReport struct {
ID int64 `json:"id"`
Oid int64 `json:"oid"`
OidStr string `json:"oid_str"`
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 string `json:"ctime"`
MTime string `json:"mtime"`
Parent int64 `json:"parent"`
Like int64 `json:"like"`
ReplyState int64 `json:"reply_state"`
Opremark string `json:"opremark"`
Count int64 `json:"count"`
Message string `json:"message"`
Title string `json:"title"`
Opresult string `json:"opresult"`
ReplyMid int64 `json:"reply_mid"`
Floor int64 `json:"floor"`
Root int64 `json:"root"`
ReportMid int64 `json:"report_mid"`
ArcMid int64 `json:"arc_mid"`
Reporter string `json:"reporter"`
Replier string `json:"replier"`
IsUp int64 `json:"is_up"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Opctime string `json:"opctime"`
DocID string `json:"doc_id"`
Score int64 `json:"score"`
Attr []int8 `json:"attr"`
RedirectURL string `json:"redirect_url"`
}
// SearchReportResult search result.
type SearchReportResult struct {
Code int `json:"code,omitempty"`
Page int64 `json:"page"`
PageSize int64 `json:"pagesize"`
PageCount int `json:"pagecount"`
Total int64 `json:"total"`
Order string `json:"order"`
Result []*SearchReport `json:"result"`
Message string `json:"msg,omitempty"`
}
// ReportSearchResponse search result.
type ReportSearchResponse struct {
SearchReportResult
Pager Pager `json:"pager"`
}

View File

@@ -0,0 +1,17 @@
package model
import (
"encoding/json"
"fmt"
"testing"
)
func TestReport(t *testing.T) {
s := `{"code":0,"order":"[{\"ctime\":{\"order\":\"asc\"}}]","page":1,"pagesize":50,"pagecount":1,"total":1,"result":[{"id":121002129,"oid":10098721,"type":1,"report_mid":27515245,"reason":8,"content":"","count":1,"state":0,"score":0,"ctime":"2017-12-14 10:22:03","mtime":"2017-12-14 10:22:03","adminid":null,"opresult":null,"opremark":null,"opctime":null,"reply_mid":27515232,"root":0,"parent":0,"floor":2,"like":0,"reply_state":0,"message":"(=\u30fb\u03c9\u30fb=)","typeid":76,"arc_mid":27515615,"reporter":"Test000011","replier":"\u53ee\u5f53\u732b3333777","doc_id":"121002129_10098721_1"}]}`
var data SearchReportResult
err := json.Unmarshal([]byte(s), &data)
if err != nil {
panic(err)
}
fmt.Println(data)
}

View File

@@ -0,0 +1,64 @@
package model
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,197 @@
package model
import (
"encoding/json"
"fmt"
"go-common/library/ecode"
"go-common/library/time"
)
const (
// SubTypeArchive 稿件
SubTypeArchive int32 = 1
// SubTypeTopic 话题
SubTypeTopic = 2
// SubTypeDrawyoo 画站 NOTE Deprecated
SubTypeDrawyoo = 3
// SubTypeActivity 活动
SubTypeActivity = 4
// SubTypeLiveVideo 直播小视频
SubTypeLiveVideo = 5
// SubTypeForbiden 封禁信息
SubTypeForbiden = 6
// SubTypeNotice 公告信息
SubTypeNotice = 7
// SubTypeLiveAct 直播活动
SubTypeLiveAct = 8
// SubTypeActArc 活动稿件
SubTypeActArc = 9
// SubTypeLiveNotice 直播公告
SubTypeLiveNotice = 10
// SubTypeLivePicture 直播有文画
SubTypeLivePicture = 11
// SubTypeArticle 文章
SubTypeArticle = 12
// SubTypeTicket 票务
SubTypeTicket = 13
// SubTypeMusic 音乐
SubTypeMusic = 14
// SubTypeCredit 风纪委
SubTypeCredit = 15
// SubTypePgcCmt 点评
SubTypePgcCmt = 16
// SubTypeDynamic 动态
SubTypeDynamic = 17
// SubTypePlaylist 播单
SubTypePlaylist = 18
// SubTypeMusicList 音乐播单
SubTypeMusicList = 19
// SubTypeComicSeason 漫画部评论
SubTypeComicSeason = 20
// SubTypeComicEpisode 漫画话评论
SubTypeComicEpisode = 21
// SubStateNormal 正常
SubStateNormal int32 = 0
// SubStateForbid 关闭
SubStateForbid = 1
// SubStateMonitor 监控中 NOTE Deprecated to use attr 2
SubStateMonitor = 2
// SubAttrTopAdmin 管理员置顶
SubAttrTopAdmin uint32 = 0
// SubAttrTopUpper up主置顶
SubAttrTopUpper = 1
// SubAttrMonitor 监控中
SubAttrMonitor = 2
// SubAttrConfig 存在配置
SubAttrConfig = 3
// SubAttrAudit 先审后发
SubAttrAudit = 4
// SubAttrFrozen 冻结评论
SubAttrFrozen = 5
// SubAttrFolded 有折叠的根评论
SubAttrFolded = 7
// SujectAllow subject_allow
SujectAllow = "subject_allow"
// SujectForbid SujectForbid
SujectForbid = "subject_forbid"
// SujectFrozen SujectFrozen
SujectFrozen = "subject_frozen"
// SujectUnfrozenAllow SujectUnfrozenAllow
SujectUnfrozenAllow = "subject_unfrozen_allow"
// SujectUnfrozenForbid SujectUnfrozenForbid
SujectUnfrozenForbid = "subject_unfrozen_forbid"
)
// Subject reply subject.
type Subject struct {
ID int64 `json:"-"`
Oid int64 `json:"oid"`
Type int32 `json:"type"`
Mid int64 `json:"mid"`
Count int32 `json:"count"`
RCount int32 `json:"rcount"`
ACount int32 `json:"acount"`
State int32 `json:"state"`
Attr uint32 `json:"attr"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"-"`
Meta string `json:"meta"`
}
// HasFolded ...
func (s *Subject) HasFolded() bool {
return s.AttrVal(AttrFolded) == AttrYes
}
// MarkHasFolded ...
func (s *Subject) MarkHasFolded() {
s.AttrSet(AttrYes, AttrFolded)
}
// UnmarkHasFolded ...
func (s *Subject) UnmarkHasFolded() {
s.AttrSet(AttrNo, AttrFolded)
}
// SubjectMeta SubjectMeta
type SubjectMeta struct {
AdminTop int64 `json:"atop,omitempty"`
UpperTop int64 `json:"utop,omitempty"`
}
// TopSet TopSet
func (s *Subject) TopSet(top int64, typ uint, act uint32) (err error) {
var meta SubjectMeta
if s.Meta != "" {
err = json.Unmarshal([]byte(s.Meta), &meta)
if err != nil {
return
}
}
if act == 1 {
if typ == 0 {
if meta.AdminTop == top {
return fmt.Errorf("subject already have the same top")
}
meta.AdminTop = top
} else {
if meta.UpperTop == top {
return fmt.Errorf("subject already have the same top")
}
meta.UpperTop = top
}
} else {
if typ == 0 {
meta.AdminTop = 0
} else {
meta.UpperTop = 0
}
}
var content []byte
content, err = json.Marshal(meta)
if err != nil {
return err
}
s.Meta = string(content)
return
}
// AttrVal return val of subject'attr.
func (s *Subject) AttrVal(bit uint32) uint32 {
return (s.Attr >> bit) & uint32(1)
}
// AttrSet set val of subject'attr.
func (s *Subject) AttrSet(v uint32, bit uint32) {
s.Attr = s.Attr&(^(1 << bit)) | (v << bit)
}
// CheckSubState check subject state is ok.
func CheckSubState(state int32) (err error) {
if state < SubStateNormal || state > SubStateForbid {
err = ecode.ReplyIllegalSubState
}
return
}
// SubjectLog info
type SubjectLog struct {
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Oid string `json:"oid"`
Type int32 `json:"type"`
Remark string `json:"remark"`
Action string `json:"action"`
CTime string `json:"ctime"`
State int32 `json:"state"`
}
// SubjectLogRes format
type SubjectLogRes struct {
Logs []*SubjectLog `json:"logs"`
Page Page `json:"page"`
Order string `json:"order"`
Sort string `json:"sort"`
}

View File

@@ -0,0 +1,98 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"action_test.go",
"admin_test.go",
"business_test.go",
"config_test.go",
"event_test.go",
"moral_test.go",
"notice_test.go",
"reply_test.go",
"report_test.go",
"service_test.go",
"subject_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/main/reply/model:go_default_library",
"//library/log:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/go-sql-driver/mysql:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"action.go",
"admin.go",
"business.go",
"config.go",
"emoji.go",
"emoji_package.go",
"event.go",
"fold.go",
"monitor.go",
"moral.go",
"notice.go",
"reply.go",
"report.go",
"service.go",
"subject.go",
],
importpath = "go-common/app/admin/main/reply/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/reply/conf:go_default_library",
"//app/admin/main/reply/dao:go_default_library",
"//app/admin/main/reply/model:go_default_library",
"//app/interface/openplatform/article/model:go_default_library",
"//app/interface/openplatform/article/rpc/client:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/api/gorpc:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//app/service/main/relation/model:go_default_library",
"//app/service/main/relation/rpc/client:go_default_library",
"//app/service/main/thumbup/api:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/sync/errgroup.v2:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,98 @@
package service
import (
"context"
"fmt"
"go-common/app/admin/main/reply/model"
thumbup "go-common/app/service/main/thumbup/api"
"go-common/library/ecode"
"go-common/library/log"
)
// ActionCount return action exact count.
func (s *Service) ActionCount(c context.Context, rpID, oid, adminID int64, typ int32) (like, hate int32, err error) {
rp, err := s.dao.Reply(c, oid, rpID)
if err != nil {
return
}
if rp == nil {
err = ecode.ReplyNotExist
return
}
like = rp.Like
hate = rp.Hate
return
}
// UpActionLike update action like.
func (s *Service) UpActionLike(c context.Context, rpID, oid, adminID int64, typ, count int32, remark string) (err error) {
rp, err := s.dao.Reply(c, oid, rpID)
if err != nil {
return
}
if rp == nil {
err = ecode.ReplyNotExist
return
}
if _, err = s.thumbupClient.UpdateCount(c, &thumbup.UpdateCountReq{
Business: "reply",
OriginID: rp.Oid,
MessageID: rpID,
LikeChange: int64(count),
Operator: fmt.Sprintf("%d", adminID),
}); err != nil {
log.Error("s.thumbupClient.UpdateCount (%d,%d,%d) failed!err:=%v", oid, rpID, int64(count), err)
return
}
rp.Like += count
if rp.Like < 0 {
rp.Like = 0
}
if err = s.addReplyIndex(c, rp); err != nil {
log.Error("s.addReplyIndex(%d,%d,%d) error(%v)", rp.ID, rp.Oid, rp.Type, err)
}
if err = s.dao.DelReplyCache(c, rp.ID); err != nil {
log.Error("s.dao.DeleteReplyCache(%d) error(%v)", rp.ID, err)
}
s.cache.Do(c, func(ctx context.Context) {
s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, rp.State)
})
return
}
// UpActionHate update action hate.
func (s *Service) UpActionHate(c context.Context, rpID, oid, adminID int64, typ, count int32, remark string) (err error) {
rp, err := s.dao.Reply(c, oid, rpID)
if err != nil {
return
}
if rp == nil {
err = ecode.ReplyNotExist
return
}
if _, err = s.thumbupClient.UpdateCount(c, &thumbup.UpdateCountReq{
Business: "reply",
OriginID: rp.Oid,
MessageID: rpID,
DislikeChange: int64(count),
Operator: fmt.Sprintf("%d", adminID),
}); err != nil {
log.Error("s.thumbupClient.UpdateCount (%d,%d,%d) failed!err:=%v", oid, rpID, int64(count), err)
return
}
rp.Hate += count
if rp.Hate < 0 {
rp.Hate = 0
}
if err = s.addReplyIndex(c, rp); err != nil {
log.Error("s.addReplyIndex(%d,%d,%d) error(%v)", rp.ID, rp.Oid, rp.Type, err)
}
if err = s.dao.DelReplyCache(c, rp.ID); err != nil {
log.Error("s.dao.DeleteReplyCache(%d) error(%v)", rp.ID, err)
}
s.cache.Do(c, func(ctx context.Context) {
s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, rp.State)
})
return
}

View File

@@ -0,0 +1,21 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestAction(t *testing.T) {
c := context.Background()
Convey("action set", t, WithService(func(s *Service) {
err := s.UpActionLike(c, 894717392, 5464686, 23, 1, 32, "test")
So(err, ShouldBeNil)
like, hate, err := s.ActionCount(c, 894717392, 5464686, 23, 1)
So(err, ShouldBeNil)
So(hate, ShouldEqual, 0)
So(like, ShouldEqual, 32)
}))
}

View File

@@ -0,0 +1,72 @@
package service
import (
"go-common/app/admin/main/reply/model"
"go-common/library/log"
"context"
"time"
)
func (s *Service) addAdminLog(c context.Context, oid, rpID, adminID int64, typ, isNew, isReport, state int32, result, remark string, now time.Time) (err error) {
rpMap := map[int64]*model.Reply{rpID: &model.Reply{
Oid: oid,
ID: rpID,
}}
return s.addAdminLogs(c, rpMap, adminID, typ, isNew, isReport, state, result, remark, now)
}
func (s *Service) addAdminIDLogs(c context.Context, oids []int64, rpIDs []int64, adminID int64, typ, isNew, isReport, state int32, result, remark string, now time.Time) (err error) {
if _, err = s.dao.UpAdminNotNew(c, rpIDs, now); err != nil {
log.Error("s.dao.UpAdminNotNew(%v) error(%v)", rpIDs, err)
}
if _, err = s.dao.AddAdminLog(c, oids, rpIDs, adminID, typ, isNew, isReport, state, result, remark, now); err != nil {
log.Error("s.dao.AddAdminLog(admin:%d, oid:%v rpID:%v type:%d result:%s remark:%s isReport:%d state:%d) error(%v)", adminID, oids, rpIDs, typ, result, remark, isReport, state, err)
}
return
}
func (s *Service) addAdminLogs(c context.Context, rps map[int64]*model.Reply, adminID int64, typ, isNew, isReport, state int32, result, remark string, now time.Time) (err error) {
if len(rps) == 0 {
return
}
rpIDs := make([]int64, 0, len(rps))
oids := make([]int64, 0, len(rps))
for _, rp := range rps {
rpIDs = append(rpIDs, rp.ID)
oids = append(oids, rp.Oid)
}
if _, err = s.dao.UpAdminNotNew(c, rpIDs, now); err != nil {
log.Error("s.dao.UpAdminNotNew(%v) error(%v)", rpIDs, err)
}
if _, err = s.dao.AddAdminLog(c, oids, rpIDs, adminID, typ, isNew, isReport, state, result, remark, now); err != nil {
log.Error("s.dao.AddAdminLog(admin:%d, oid:%v rpID:%v type:%d result:%s remark:%s isReport:%d state:%d) error(%v)", adminID, oids, rpIDs, typ, result, remark, isReport, state, err)
}
return
}
// ReplyAdminLog ReplyAdminLog
type ReplyAdminLog struct {
model.AdminLog
AdminName string `json:"admin_name"`
}
// LogsByRpID get log by reply id.
func (s *Service) LogsByRpID(c context.Context, rpID int64) (res []ReplyAdminLog, err error) {
var resDao []*model.AdminLog
res = []ReplyAdminLog{}
if resDao, err = s.dao.AdminLogsByRpID(c, rpID); err != nil {
log.Error("s.dao.AdminLogsByRpID(%d) error(%v)", rpID, err)
return
}
admins := map[int64]string{}
for _, data := range resDao {
admins[data.AdminID] = ""
res = append(res, ReplyAdminLog{*data, ""})
}
s.dao.AdminName(c, admins)
for i := range res {
res[i].AdminName = admins[res[i].AdminID]
}
return
}

View File

@@ -0,0 +1,36 @@
package service
import (
"context"
"go-common/app/admin/main/reply/model"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestAdminLog(t *testing.T) {
var (
ok bool
oid = int64(1)
rpID = int64(1)
adminID = int64(1)
typ = int32(4)
now = time.Now()
c = context.Background()
)
Convey("action set", t, WithService(func(s *Service) {
err := s.addAdminLog(c, oid, rpID, adminID, typ, model.AdminIsNew, model.AdminIsReport, model.AdminOperDelete, "test", "remark", now)
So(err, ShouldBeNil)
list, err := s.LogsByRpID(c, rpID)
So(err, ShouldBeNil)
So(len(list), ShouldNotEqual, 0)
for _, log := range list {
if log.ReplyID == rpID {
ok = true
}
}
So(ok, ShouldEqual, true)
}))
}

View File

@@ -0,0 +1,32 @@
package service
import (
"context"
"go-common/app/admin/main/reply/model"
)
// ListBusiness return all business
func (s *Service) ListBusiness(c context.Context, state int32) (business []*model.Business, err error) {
return s.dao.ListBusiness(c, state)
}
// GetBusiness return a business by type
func (s *Service) GetBusiness(c context.Context, tp int32) (business *model.Business, err error) {
return s.dao.Business(c, tp)
}
// AddBusiness add a business
func (s *Service) AddBusiness(c context.Context, tp int32, name, appkey, remark, alias string) (id int64, err error) {
return s.dao.InBusiness(c, tp, name, appkey, remark, alias)
}
// UpBusiness update a business's name appkey and remark
func (s *Service) UpBusiness(c context.Context, name, appkey, remark, alias string, tp int32) (id int64, err error) {
return s.dao.UpBusiness(c, name, appkey, remark, alias, tp)
}
// UpBusinessState update a business's state
func (s *Service) UpBusinessState(c context.Context, state, tp int32) (id int64, err error) {
return s.dao.UpBusinessState(c, state, tp)
}

View File

@@ -0,0 +1,30 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_Business(t *testing.T) {
c := context.Background()
Convey("test service business", c, WithService(func(s *Service) {
Convey("test service add business", c, WithService(func(s *Service) {
_, err := s.AddBusiness(c, -3, "abc", "abc", "abc", "abc")
So(err, ShouldBeNil)
}))
Convey("test service list business", c, WithService(func(s *Service) {
_, err := s.ListBusiness(c, 0)
So(err, ShouldBeNil)
}))
Convey("test service update business", c, WithService(func(s *Service) {
_, err := s.UpBusiness(c, "test", "test", "test", "abc", -3)
So(err, ShouldBeNil)
}))
Convey("test service update business state", c, WithService(func(s *Service) {
_, err := s.UpBusinessState(c, 1, -3)
So(err, ShouldBeNil)
}))
}))
}

View File

@@ -0,0 +1,86 @@
package service
import (
"context"
"math"
"time"
"go-common/app/admin/main/reply/model"
"go-common/library/log"
)
// AddReplyConfig create a new administrator configuration for reply business
func (s *Service) AddReplyConfig(c context.Context, m *model.Config) (id int64, err error) {
sub, err := s.subject(c, m.Oid, m.Type)
if err != nil {
return
}
now := time.Now()
if _, err = s.dao.AddConfig(c, m.Type, m.Category, m.Oid, m.AdminID, m.Operator, m.Config, now); err != nil {
return
}
if m.ShowEntry == 1 && m.ShowAdmin == 1 {
sub.AttrSet(model.AttrNo, model.SubAttrConfig)
} else {
sub.AttrSet(model.AttrYes, model.SubAttrConfig)
}
if _, err = s.dao.UpSubjectAttr(c, m.Oid, m.Type, sub.Attr, now); err != nil {
log.Error("s.dao.UpSubjectAttr(%d,%d,%d,%d) error(%v)", m.Type, m.Oid, model.SubAttrConfig, m.ShowEntry, err)
return
}
if err = s.dao.DelSubjectCache(c, m.Oid, m.Type); err != nil {
log.Error("ReplyConfig del subject cache error(%v)", err)
}
if err = s.dao.DelConfigCache(c, m.Oid, m.Type, m.Category); err != nil {
log.Error("ReplyConfig del config cache error(%v)", err)
}
return
}
// LoadReplyConfig load a configuration record of reply business.
func (s *Service) LoadReplyConfig(c context.Context, typ, category int32, oid int64) (m *model.Config, err error) {
m, err = s.dao.LoadConfig(c, typ, category, oid)
return
}
//PaginateReplyConfig paginate configuration list of records indexing from start to end, and a total count of records
func (s *Service) PaginateReplyConfig(c context.Context, typ, category int32, oid int64, operator string, offset, count int) (configs []*model.Config, totalCount, pages int64, err error) {
configs, _ = s.dao.PaginateConfig(c, typ, category, oid, operator, offset, count)
totalCount, _ = s.dao.PaginateConfigCount(c, typ, category, oid, operator)
pages = int64(math.Ceil(float64(totalCount) / float64(count)))
return
}
//RenewReplyConfig reset reply configuration by default, with deleting the detail configurations from db
func (s *Service) RenewReplyConfig(c context.Context, id int64) (result bool, err error) {
now := time.Now()
config, err := s.dao.LoadConfigByID(c, id)
if err != nil {
log.Error("s.dao.LoadConfigByID(%d) error(%v)", id, err)
}
if config == nil {
return false, nil
}
sub, err := s.dao.Subject(c, config.Oid, config.Type)
if err != nil {
return
}
sub.AttrSet(model.AttrNo, model.SubAttrConfig)
_, err = s.dao.UpSubjectAttr(c, config.Oid, config.Type, sub.Attr, now)
if err != nil {
log.Error("s.dao.UpSubjectAttr(%d,%d,%d,%d) error(%v)", config.Type, config.Oid, model.SubAttrConfig, config.ShowEntry, err)
return
}
if _, err = s.dao.DeleteConfig(c, id); err != nil {
log.Error("s.dao.DeleteConfig(%d) error(%v)", id, err)
return
}
if err = s.dao.DelSubjectCache(c, config.Oid, config.Type); err != nil {
log.Error("ReplyConfig del subject cache error(%v)", err)
}
if err = s.dao.DelConfigCache(c, config.Oid, config.Type, config.Category); err != nil {
log.Error("ReplyConfig del config cache error(%v)", err)
}
result = true
return
}

View File

@@ -0,0 +1,37 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestConfig(t *testing.T) {
var (
m = &model.Config{
Oid: 1,
Type: 4,
ShowEntry: 1,
ShowAdmin: 1,
}
c = context.Background()
)
Convey("test config ", t, WithService(func(s *Service) {
_, err := s.AddReplyConfig(c, m)
So(err, ShouldBeNil)
cfg, err := s.LoadReplyConfig(c, m.Type, m.Category, m.Oid)
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
list, total, pages, err := s.PaginateReplyConfig(c, m.Type, m.Category, m.Oid, "", 0, 10)
So(err, ShouldBeNil)
So(len(list), ShouldNotEqual, 0)
So(total, ShouldNotEqual, 0)
So(pages, ShouldNotEqual, 0)
ok, err := s.RenewReplyConfig(c, cfg.ID)
So(err, ShouldBeNil)
So(ok, ShouldEqual, true)
}))
}

View File

@@ -0,0 +1,90 @@
package service
import (
"context"
"strings"
"go-common/app/admin/main/reply/model"
"go-common/library/ecode"
"go-common/library/log"
)
// EmojiList EmojiList
func (s *Service) EmojiList(c context.Context, pid int64) (emojis []*model.Emoji, err error) {
if pid != 0 {
emojis, err = s.dao.EmojiListByPid(c, pid)
} else {
emojis, err = s.dao.EmojiList(c)
}
if err != nil {
log.Error("service.EmojiList error (%v)", err)
}
return
}
// CreateEmoji CreateEmoji
func (s *Service) CreateEmoji(c context.Context, pid int64, name string, url string, sort int32, state int32, remark string) (id int64, err error) {
if !strings.HasPrefix(name, "[") {
name = "[" + name
}
if !strings.HasSuffix(name, "]") {
name = name + "]"
}
id, err = s.dao.CreateEmoji(c, pid, name, url, sort, state, remark)
if err != nil {
log.Error("service.CreateEmoji error (%v)", err)
}
return
}
// UpEmojiSort UpEmojiSort
func (s *Service) UpEmojiSort(c context.Context, ids string) (err error) {
tx, err := s.dao.BeginTran(c)
if err != nil {
return
}
err = s.dao.UpEmojiSort(tx, ids)
if err != nil {
tx.Rollback()
log.Error("service.UpEmojiSort error (%v)", err)
return
}
return tx.Commit()
}
// UpEmojiState UpEmojiState
func (s *Service) UpEmojiState(c context.Context, state int32, id int64) (idx int64, err error) {
if state == 2 { // delete emoji
idx, err = s.dao.DelEmojiByID(c, id)
} else {
idx, err = s.dao.UpEmojiStateByID(c, state, id)
}
if err != nil {
log.Error("service.UpEmojiState error (%v)", err)
}
return
}
// UpEmoji UpEmoji
func (s *Service) UpEmoji(c context.Context, name string, remark string, url string, id int64) (idx int64, err error) {
idx, err = s.dao.UpEmoji(c, name, remark, url, id)
if err != nil {
log.Error("service.UpEmojiState error (%v)", err)
}
return
}
// EmojiByName EmojiByName
func (s *Service) EmojiByName(c context.Context, name string) (err error) {
emojis, e := s.dao.EmojiByName(c, name)
if e != nil {
log.Error("service.CreateEmoji EmojiByName err (%v)", e)
err = e
return
}
if len(emojis) > 0 {
err = ecode.ReplyEmojiExits
return
}
return
}

View File

@@ -0,0 +1,68 @@
package service
import (
"context"
"go-common/app/admin/main/reply/model"
"go-common/library/log"
)
// EmojiPackageList return all emojipackages and emojis
func (s *Service) EmojiPackageList(c context.Context) (resp []*model.EmojiPackage, err error) {
packs, err := s.dao.EmojiPackageList(c)
if err != nil {
log.Error("service.EmojiPackageList err (%v)", err)
return
}
for _, pack := range packs {
eList, err := s.dao.EmojiListByPid(c, pack.ID)
if err != nil {
return nil, err
}
pack.Emojis = eList
resp = append(resp, pack)
}
return
}
// CreateEmojiPackage CreateEmojiPackage
func (s *Service) CreateEmojiPackage(c context.Context, name string, url string, sort int32, remark string, state int32) (id int64, err error) {
id, err = s.dao.CreateEmojiPackage(c, name, url, sort, remark, state)
if err != nil {
log.Error("service.CreateEmojiPackage err (%v)", err)
}
return
}
// UpEmojiPackageSort UpEmojiPackageSort
func (s *Service) UpEmojiPackageSort(c context.Context, ids string) (err error) {
tx, err := s.dao.BeginTran(c)
if err != nil {
return
}
err = s.dao.UpEmojiPackageSort(tx, ids)
if err != nil {
tx.Rollback()
log.Error("service.UpEmojiPackageSort err (%v)", err)
return
}
return tx.Commit()
}
// UpEmojiPackage UpEmojiPackage
func (s *Service) UpEmojiPackage(c context.Context, name string, url string, remark string, state int32, id int64) (idx int64, err error) {
if state == 2 { // delete package
_, err = s.dao.DelEmojiPackage(c, id)
if err != nil {
log.Error("service.DelEmojiPackage err (%v)", err)
return 0, err
}
idx, err = s.dao.DelEmojiByPid(c, id)
} else {
idx, err = s.dao.UpEmojiPackage(c, name, url, remark, state, id)
}
if err != nil {
log.Error("service.UpEmojiPackage err (%v)", err)
}
return
}

View File

@@ -0,0 +1,29 @@
package service
import (
"context"
"go-common/app/admin/main/reply/model"
"go-common/library/log"
)
func (s *Service) pubEvent(c context.Context, action string, mid int64, sub *model.Subject, rp *model.Reply, rpt *model.Report) (err error) {
if err = s.dao.PubEvent(c, action, mid, sub, rp, rpt); err != nil {
log.Error("s.dao.PubEvent(%s,%d,%v,%v,%v) error(%v)", action, mid, sub, rp, rpt, err)
}
return
}
func (s *Service) pubSearchReply(c context.Context, rps map[int64]*model.Reply, newState int32) (err error) {
if err = s.dao.UpSearchReply(c, rps, newState); err != nil {
log.Error("s.dao.UpSearchReply(%v,%d) error(%v)", rps, newState, err)
}
return
}
func (s *Service) pubSearchReport(c context.Context, rpts map[int64]*model.Report, rpState *int32) (err error) {
if err = s.dao.UpSearchReport(c, rpts, rpState); err != nil {
log.Error("s.dao.UpSearchReport(%v,%d) error(%v)", rpts, rpState, err)
}
return
}

View File

@@ -0,0 +1,35 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/reply/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestEvent(t *testing.T) {
var (
sub = &model.Subject{}
rp = &model.Reply{}
rpt = &model.Report{}
c = context.Background()
)
Convey("test pub a event for reply", t, WithService(func(s *Service) {
err := s.pubEvent(c, "test", 0, sub, rp, rpt)
So(err, ShouldBeNil)
}))
Convey("test pub a event for search index", t, WithService(func(s *Service) {
rps := map[int64]*model.Reply{}
rps[rp.ID] = rp
err := s.pubSearchReply(c, rps, 0)
So(err, ShouldBeNil)
}))
Convey("test pub a event for search index", t, WithService(func(s *Service) {
rpts := map[int64]*model.Report{}
rpts[rpt.RpID] = rpt
err := s.pubSearchReport(c, rpts, nil)
So(err, ShouldBeNil)
}))
}

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