Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/block/cmd:all-srcs",
"//app/admin/main/block/conf:all-srcs",
"//app/admin/main/block/dao:all-srcs",
"//app/admin/main/block/http:all-srcs",
"//app/admin/main/block/model:all-srcs",
"//app/admin/main/block/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,41 @@
# v1.1.0
1. 审核日志上报
# v1.0.12
1. 优化account notify
# v1.0.11
1. fix account notify model
# v1.0.10
1.增加小黑屋封禁业务 & 理由
# v1.0.9
1.修复小黑屋封禁
# v1.0.8
1.优化封禁后台显示
# v1.0.7
1.修复封禁理由映射
# v1.0.6
1.产品变动小黑屋封禁理由
# v1.0.5
1.新增小黑屋封禁理由
# v1.0.4
1.新增小黑屋封禁理由
# v1.0.3
1.加入AccountNotify清理缓存
# v1.0.2
1.小黑屋封禁状态转换
# v1.0.1
1.update tel to tel_status
# v1.0.0
1.初始化项目

View File

@@ -0,0 +1,12 @@
# Owner
zhaogangtao
wanghuan01
zhoujiahui
# Author
muyang
# Reviewer
zhaogangtao
wanghuan01
zhoujiahui

View File

@@ -0,0 +1,18 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- muyang
- wanghuan01
- zhaogangtao
- zhoujiahui
labels:
- admin
- admin/main/block
- main
options:
no_parent_owners: true
reviewers:
- muyang
- wanghuan01
- zhaogangtao
- zhoujiahui

View File

@@ -0,0 +1,10 @@
# block-admin
# 项目简介
1.封禁服务后台服务
# 编译环境
1.golang 1.8 以上
# 依赖包
go-common

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["block-admin-test.toml"],
importpath = "go-common/app/admin/main/block/cmd",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//app/admin/main/block/http:go_default_library",
"//library/ecode/tip: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,237 @@
version = "1.0.0"
user = "nobody"
dir = "./"
family = "block-admin"
address = "0.0.0.0:7490"
[log]
dir = "/data/log/block-admin"
family = "block-admin"
[tracer]
addr = "172.16.33.46:5140"
proto = "udp"
family = "block-admin"
[auth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "manager-go"
[auth.DsHTTPClient]
key = "manager-go"
secret = "949bbb2dd3178252638c2407578bc7ad"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[ecode]
domain = "172.16.33.248:6401"
all = "1h"
diff = "10m"
[ecode.clientconfig]
key = "test"
secret = "e6c4c252dc7e3d8a90805eecd7c73396"
dial = "2000ms"
timeout = "2s"
keepAlive = "10s"
timer = 128
[ecode.clientconfig.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[bm]
addr = "0.0.0.0:7491"
maxListen = 10
timeout = "1s"
[rpcClients]
[rpcClients.account]
pullInterval = "10s"
[rpcClients.account.client]
timeout = "1s"
timer = 1000
[rpcClients.account.client.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[rpcClients.account.zookeeper]
root = "/microservice/account-service/"
addrs = ["172.18.33.172:2181"]
timeout = "1s"
[rpcClients.figure]
pullInterval = "10s"
[rpcClients.figure.client]
timeout = "1s"
timer = 1000
[rpcClients.figure.client.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[rpcClients.figure.zookeeper]
root = "/microservice/figure-service/"
addrs = ["172.18.33.172:2181"]
timeout = "30s"
[rpcClients.spy]
pullInterval = "10s"
[rpcClients.spy.client]
timeout = "1s"
timer = 1000
[rpcClients.spy.client.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[rpcClients.spy.zookeeper]
root = "/microservice/spy-service/"
addrs = ["172.18.33.172:2181"]
timeout = "30s"
[memcache]
name = "block-admin"
proto = "tcp"
addr = "172.16.33.54:11214"
idle = 10
active = 10
dialTimeout = "2s"
readTimeout = "2s"
writeTimeout = "2s"
idleTimeout = "7h"
[expire]
expire = "200s"
[mysql]
addr = "127.0.0.1:3306"
dsn = "root:54985498@tcp(127.0.0.1:3306)/bilibili_block?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
[mysql.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient]
key = "7d9f6f6fe2a898e8"
secret = "4de2ccdbd9db69be0c2c6437bfe6eb69"
dial = "1s"
timeout = "5s"
keepAlive = "60s"
[httpClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[perms]
[perms.perm]
search = "BLOCK_SEARCH"
block = "BLOCK_BLOCK"
remove = "BLOCK_REMOVE"
[accountNotify]
key = "4ba46ba31f9a44ef"
secret = "99985eb4451cfb1b899ca0fbe3c4bdc8"
group = "AccountNotify-MainAccount-P"
topic = "AccountNotify-T"
action = "pub"
buffer = 128
name = "usersuit-admin/databus"
proto = "tcp"
addr = "172.16.33.158:6205"
active = 1
idle = 1
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
[property]
blackHouseURL = "http://api.bilibili.co/x/internal/credit/blocked/info/batch/add"
msgURL = "http://message.bilibili.co/api/notify/send.user.notify.do"
telURL = "http://passport.bilibili.co/intranet/acc/telInfo/mid"
mailURL = "http://passport.bilibili.co/intranet/user/mail"
[property.msg]
[property.msg.blackHouseLimit]
code = "2_3_2"
title = "账号违规处理通知"
content = '抱歉,你的账号因“%s%s”现已进行封禁%d天处理同时需要进行解封答题考试通过之后方可解除封禁#{点击答题}{"http://www.bilibili.com/blackroom/releaseexame.html\"}。封禁期间将无法投稿,无法发布评论、弹幕,无法对他人评论进行回复、赞踩操作,无法发送、回复消息,无法进行投币、编辑标签、添加关注、添加收藏等操作。请遵守社区规范,共同维护良好的社区氛围!'
[property.msg.blackHouseForever]
code = "2_3_3"
title = "账号违规处理通知"
content = "抱歉,你的账号因“%s%s”现已进行永久封禁处理。封禁期间将无法投稿无法发布评论、弹幕无法对他人评论进行回复、赞踩操作无法发送、回复消息无法进行投币、编辑标签、添加关注、添加收藏等操作。请遵守社区规范共同维护良好的社区氛围"
[property.msg.sysLimit]
code = "2_3_4"
title = "账号违规处理通知"
content = '抱歉,你的账号因“%s”现已进行封禁%d天处理同时需要进行解封答题考试通过之后方可解除封禁#{点击答题}{"http://www.bilibili.com/blackroom/releaseexame.html\"}。封禁期间将无法投稿,无法发布评论、弹幕,无法对他人评论进行回复、赞踩操作,无法发送、回复消息,无法进行投币、编辑标签、添加关注、添加收藏等操作。请遵守社区规范,共同维护良好的社区氛围!'
[property.msg.sysForever]
code = "2_3_5"
title = "账号违规处理通知"
content = "抱歉,你的账号因“%s”现已进行永久封禁处理。封禁期间将无法投稿无法发布评论、弹幕无法对他人评论进行回复、赞踩操作无法发送、回复消息无法进行投币、编辑标签、添加关注、添加收藏等操作。请遵守社区规范共同维护良好的社区氛围"
[property.msg.blockRemove]
code = "2_3_6"
title = "账号违规处理通知"
content = "你的账号已经解除封禁,封禁期间禁止使用的各项社区功能已经恢复。请遵守社区规范,共同维护良好的社区氛围。"
[managerLog]
Key = "2511663d546f1413"
Secret = "cde3b480836cc76df3d635470f991caa"
Group = "LogAudit-MainSearch-P"
Topic = "LogAudit-T"
Action = "pub"
Buffer = 10240
Name = "log-audit/log-sub"
Proto = "tcp"
Addr = "172.18.33.50:6205"
Active = 10
Idle = 5
DialTimeout = "200ms"
ReadTimeout = "200ms"
WriteTimeout = "200ms"
IdleTimeout = "80s"

View File

@@ -0,0 +1,52 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
manager "go-common/library/queue/databus/report"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("block-admin start")
// init trace
trace.Init(conf.Conf.Tracer)
defer trace.Close()
// ecode init
ecode.Init(conf.Conf.Ecode)
// service init
http.Init()
// manager log init
manager.InitManager(conf.Conf.ManagerLog)
// init pprof conf.Conf.Perf
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("block-admin get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("block-admin exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["conf_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/admin/main/block/conf",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/model:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus: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,127 @@
package conf
import (
"errors"
"flag"
"go-common/app/admin/main/block/model"
"go-common/library/cache/memcache"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"go-common/library/net/rpc"
"go-common/library/net/trace"
"go-common/library/queue/databus"
"github.com/BurntSushi/toml"
)
// global var
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config config set
type Config struct {
Log *log.Config
Tracer *trace.Config
Auth *permit.Config
Ecode *ecode.Config
BM *bm.ServerConfig
RPCClients *RPC
Memcache *memcache.Config
MySQL *sql.Config
HTTPClient *bm.ClientConfig
Perms *Perms
AccountNotify *databus.Config
Property *Property
// manager log config
ManagerLog *databus.Config
}
// Property .
type Property struct {
BlackHouseURL string
MSGURL string
TelURL string
MailURL string
MSG *MSG
}
// MSG .
type MSG struct {
BlackHouseLimit model.MSG
BlackHouseForever model.MSG
SysLimit model.MSG
SysForever model.MSG
BlockRemove model.MSG
}
// RPC .
type RPC struct {
Account *rpc.ClientConfig
Figure *rpc.ClientConfig
Spy *rpc.ClientConfig
}
// Perms .
type Perms struct {
Perm map[string]string
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,32 @@
package conf
import (
"flag"
"os"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestMain(m *testing.M) {
defer os.Exit(0)
flag.Set("conf", "../cmd/block-admin-test.toml")
var err error
if err = Init(); err != nil {
panic(err)
}
m.Run()
}
func TestConf(t *testing.T) {
Convey("conf", t, func() {
So(Conf.Property.MSG, ShouldNotBeNil)
So(Conf.Property.MSG.BlackHouseForever, ShouldNotBeNil)
So(Conf.Property.MSG.BlackHouseLimit, ShouldNotBeNil)
So(Conf.Property.MSG.BlockRemove, ShouldNotBeNil)
So(Conf.Property.MSG.SysForever, ShouldNotBeNil)
So(Conf.Property.MSG.SysLimit, ShouldNotBeNil)
t.Log(Conf.Property.MSG)
})
}

View File

@@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["dao_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"http.go",
"mc.go",
"mysql.go",
"rpc.go",
],
importpath = "go-common/app/admin/main/block/dao",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//app/admin/main/block/model:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/account/rpc/client:go_default_library",
"//app/service/main/figure/model:go_default_library",
"//app/service/main/figure/rpc/client:go_default_library",
"//app/service/main/spy/model:go_default_library",
"//app/service/main/spy/rpc/client:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,60 @@
package dao
import (
"context"
"go-common/app/admin/main/block/conf"
rpcaccount "go-common/app/service/main/account/rpc/client"
rpcfigure "go-common/app/service/main/figure/rpc/client"
rpcspy "go-common/app/service/main/spy/rpc/client"
"go-common/library/cache/memcache"
xsql "go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
// Dao .
type Dao struct {
mc *memcache.Pool
db *xsql.DB
httpClient *bm.Client
spyRPC *rpcspy.Service
figureRPC *rpcfigure.Service
accountRPC *rpcaccount.Service3
}
// New init mysql db
func New() (dao *Dao) {
dao = &Dao{
mc: memcache.NewPool(conf.Conf.Memcache),
db: xsql.NewMySQL(conf.Conf.MySQL),
httpClient: bm.NewClient(conf.Conf.HTTPClient),
spyRPC: rpcspy.New(conf.Conf.RPCClients.Spy),
figureRPC: rpcfigure.New(conf.Conf.RPCClients.Figure),
accountRPC: rpcaccount.New3(conf.Conf.RPCClients.Account),
}
return
}
// BeginTX .
func (d *Dao) BeginTX(c context.Context) (tx *xsql.Tx, err error) {
if tx, err = d.db.Begin(c); err != nil {
err = errors.WithStack(err)
}
return
}
// Close close the resource.
func (d *Dao) Close() {
d.mc.Close()
d.db.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.db.Ping(c); err != nil {
return
}
return
}

View File

@@ -0,0 +1,84 @@
package dao
import (
"context"
"flag"
"os"
"testing"
"go-common/app/admin/main/block/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
dao *Dao
ctx = context.TODO()
)
func TestMain(m *testing.M) {
defer os.Exit(0)
flag.Set("conf", "../cmd/block-admin-test.toml")
var err error
if err = conf.Init(); err != nil {
panic(err)
}
dao = New()
defer dao.Close()
m.Run()
}
func TestDB(t *testing.T) {
Convey("db", t, func() {
err := dao.SendSysMsg(ctx, "123", []int64{1, 2, 3}, "test title", "test content", "")
So(err, ShouldBeNil)
_, err = dao.HistoryCount(ctx, 46333)
So(err, ShouldBeNil)
_, err = dao.History(ctx, 46333, 0, 100)
So(err, ShouldBeNil)
_, err = dao.User(ctx, 46333)
So(err, ShouldBeNil)
_, err = dao.Users(ctx, []int64{46333})
So(err, ShouldBeNil)
_, err = dao.UserDetails(ctx, []int64{46333})
So(err, ShouldBeNil)
})
}
func TestRPC(t *testing.T) {
Convey("rpc", t, func() {
mid := int64(46333)
_, err := dao.SpyScore(ctx, mid)
So(err, ShouldBeNil)
_, err = dao.FigureRank(ctx, mid)
So(err, ShouldBeNil)
_, _, _, _, err = dao.AccountInfo(ctx, mid)
So(err, ShouldBeNil)
})
}
func TestTool(t *testing.T) {
Convey("tool", t, func() {
var (
mids = []int64{1, 2, 3, 46333, 35858}
)
str := midsToParam(mids)
So(str, ShouldEqual, "1,2,3,46333,35858")
})
}
func TestHTTP(t *testing.T) {
Convey("http", t, func() {
var (
mid int64 = 46333
telInfo string
mailInfo string
err error
)
telInfo, err = dao.TelInfo(ctx, mid)
So(err, ShouldBeNil)
mailInfo, err = dao.MailInfo(ctx, mid)
So(err, ShouldBeNil)
t.Logf("telinfo : %s , mailinfo : %s", telInfo, mailInfo)
})
}

View File

@@ -0,0 +1,202 @@
package dao
import (
"context"
"fmt"
"net/url"
"strings"
"time"
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/model"
"go-common/library/ecode"
"github.com/pkg/errors"
)
// BlackhouseBlock .
func (d *Dao) BlackhouseBlock(c context.Context, p *model.ParamBatchBlock) (err error) {
midStrs := make([]string, len(p.MIDs))
for i, mid := range p.MIDs {
midStrs[i] = fmt.Sprintf("%d", mid)
}
params := url.Values{}
params.Set("mids", strings.Join(midStrs, ","))
params.Set("oper_id", fmt.Sprintf("%d", p.AdminID))
params.Set("operator_name", p.AdminName)
params.Set("blocked_days", fmt.Sprintf("%d", p.Duration))
switch p.Action {
case model.BlockActionForever:
params.Set("blocked_forever", "1")
params.Set("punish_type", "3")
default:
params.Set("blocked_forever", "0")
params.Set("punish_type", "2")
}
params.Set("blocked_remark", p.Comment)
params.Set("origin_type", fmt.Sprintf("%d", p.Area))
params.Set("punish_time", fmt.Sprintf("%d", time.Now().Unix()))
params.Set("reason_type", fmt.Sprintf("%d", parseReasonType(p.Reason)))
var res struct {
Code int `json:"code"`
}
if err = d.httpClient.Post(c, conf.Conf.Property.BlackHouseURL, "", params, &res); err != nil {
err = errors.WithStack(err)
return
}
if res.Code != 0 {
err = errors.WithStack(ecode.Int(res.Code))
return
}
return
}
func parseReasonType(msg string) (t int) {
switch msg {
case "刷屏":
t = 1
case "抢楼":
t = 2
case "发布色情低俗信息":
t = 3
case "发布赌博诈骗信息":
t = 4
case "发布违禁相关信息", "发布违禁信息":
t = 5
case "发布垃圾广告信息":
t = 6
case "发布人身攻击言论":
t = 7
case "发布侵犯他人隐私信息":
t = 8
case "发布引战言论":
t = 9
case "发布剧透信息":
t = 10
case "恶意添加无关标签":
t = 11
case "恶意删除他人标签":
t = 12
case "发布色情信息":
t = 13
case "发布低俗信息":
t = 14
case "发布暴力血腥信息":
t = 15
case "涉及恶意投稿行为":
t = 16
case "发布非法网站信息":
t = 17
case "发布传播不实信息":
t = 18
case "发布怂恿教唆信息":
t = 19
case "恶意刷屏":
t = 20
case "账号违规":
t = 21
case "恶意抄袭":
t = 22
case "冒充自制原创":
t = 23
case "发布青少年不良内容":
t = 24
case "破坏网络安全":
t = 25
case "发布虚假误导信息":
t = 26
case "仿冒官方认证账号":
t = 27
case "发布不适宜内容":
t = 28
case "违反运营规则":
t = 29
case "恶意创建话题":
t = 30
case "发布违规抽奖":
t = 31
default:
t = 0
}
return
}
// SendSysMsg send sys msg.
func (d *Dao) SendSysMsg(c context.Context, code string, mids []int64, title string, content string, remoteIP string) (err error) {
params := url.Values{}
params.Set("mc", code)
params.Set("title", title)
params.Set("data_type", "4")
params.Set("context", content)
params.Set("mid_list", midsToParam(mids))
var res struct {
Code int `json:"code"`
Data *struct {
Status int8 `json:"status"`
Remark string `json:"remark"`
} `json:"data"`
}
if err = d.httpClient.Post(c, conf.Conf.Property.MSGURL, remoteIP, params, &res); err != nil {
err = errors.WithStack(err)
return
}
if res.Code != 0 {
err = errors.WithStack(ecode.Int(res.Code))
return
}
return
}
func midsToParam(mids []int64) (str string) {
strs := make([]string, 0, len(mids))
for _, mid := range mids {
strs = append(strs, fmt.Sprintf("%d", mid))
}
return strings.Join(strs, ",")
}
// TelInfo tel info.
func (d *Dao) TelInfo(c context.Context, mid int64) (tel string, err error) {
params := url.Values{}
params.Set("mid", fmt.Sprintf("%d", mid))
var resp struct {
Code int `json:"code"`
Data struct {
Mid int64 `json:"mid"`
Tel string `json:"tel"`
JoinIP string `json:"join_ip"`
JoinTime int64 `json:"join_time"`
} `json:"data"`
}
if err = d.httpClient.Get(c, conf.Conf.Property.TelURL, "", params, &resp); err != nil {
err = errors.Wrapf(err, "telinfo : %d", mid)
return
}
if resp.Code != 0 {
err = errors.Errorf("telinfo url(%s) res(%+v) err(%+v)", conf.Conf.Property.TelURL+"?"+params.Encode(), resp, ecode.Int(resp.Code))
return
}
tel = resp.Data.Tel
return
}
// MailInfo .
func (d *Dao) MailInfo(c context.Context, mid int64) (mail string, err error) {
params := url.Values{}
params.Set("mid", fmt.Sprintf("%d", mid))
var resp struct {
Code int `json:"code"`
Data string `json:"data"`
}
if err = d.httpClient.Get(c, conf.Conf.Property.MailURL, "", params, &resp); err != nil {
err = errors.Wrapf(err, "mailinfo : %d", mid)
return
}
if resp.Code != 0 {
err = errors.Errorf("mailinfo url(%s) res(%+v) err(%+v)", conf.Conf.Property.MailURL+"?"+params.Encode(), resp, ecode.Int(resp.Code))
return
}
mail = resp.Data
return
}

View File

@@ -0,0 +1,56 @@
package dao
import (
"context"
"fmt"
"github.com/pkg/errors"
)
func userKey(mid int64) (key string) {
key = fmt.Sprintf("u_%d", mid)
return
}
// UserCache .
// func (d *Dao) UserCache(c context.Context, mid int64) (info *model.MCBlockInfo, err error) {
// var (
// key = userKey(mid)
// conn = d.mc.Get(c)
// item *memcache.Item
// )
// defer conn.Close()
// if item, err = conn.Get(key); err != nil {
// if err == memcache.ErrNotFound {
// err = nil
// return
// }
// err = errors.WithStack(err)
// return
// }
// info = &model.MCBlockInfo{}
// if err = conn.Scan(item, info); err != nil {
// if err == memcache.ErrNotFound {
// info = nil
// err = nil
// return
// }
// err = errors.WithStack(err)
// return
// }
// return
// }
// DeleteUserCache .
func (d *Dao) DeleteUserCache(c context.Context, mid int64) (err error) {
var (
key = userKey(mid)
conn = d.mc.Get(c)
)
defer conn.Close()
if err = conn.Delete(key); err != nil {
err = errors.Wrapf(err, "key : %s", key)
return
}
return
}

View File

@@ -0,0 +1,175 @@
package dao
import (
"context"
"fmt"
"strings"
"go-common/app/admin/main/block/model"
xsql "go-common/library/database/sql"
"github.com/pkg/errors"
)
const (
_user = `SELECT id,mid,status,ctime,mtime FROM block_user WHERE mid=? LIMIT 1`
_users = `SELECT id,mid,status,ctime,mtime FROM block_user WHERE mid IN (%s)`
_upsertUser = `INSERT INTO block_user (mid,status) VALUES (?,?) ON DUPLICATE KEY UPDATE status=?`
_userDetails = `SELECT id,mid,block_count,ctime,mtime FROM block_user_detail WHERE mid IN (%s)`
_addAddBlockCount = `INSERT INTO block_user_detail (mid,block_count) VALUES (?,1) ON DUPLICATE KEY UPDATE block_count=block_count+1`
_history = `SELECT id,mid,admin_id,admin_name,source,area,reason,comment,action,start_time,duration,notify,ctime,mtime FROM block_history_%d WHERE mid=? LIMIT ?,?`
_historyCount = `SELECT count(*) FROM block_history_%d WHERE mid=? LIMIT 1`
_insertHistory = `INSERT INTO block_history_%d (mid,admin_id,admin_name,source,area,reason,comment,action,start_time,duration,notify) VALUES (?,?,?,?,?,?,?,?,?,?,?)`
)
func historyIdx(mid int64) int64 {
return mid % 10
}
// User .
func (d *Dao) User(c context.Context, mid int64) (user *model.DBUser, err error) {
user = &model.DBUser{}
row := d.db.QueryRow(c, _user, mid)
if err = row.Scan(&user.ID, &user.MID, &user.Status, &user.CTime, &user.MTime); err != nil {
err = nil
user = nil
return
}
return
}
// Users .
func (d *Dao) Users(c context.Context, mids []int64) (users []*model.DBUser, err error) {
if len(mids) == 0 {
return
}
var (
sql = fmt.Sprintf(_users, strings.Join(intsToStrs(mids), ","))
rows *xsql.Rows
)
if rows, err = d.db.Query(c, sql); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
user := &model.DBUser{}
if err = rows.Scan(&user.ID, &user.MID, &user.Status, &user.CTime, &user.MTime); err != nil {
err = errors.WithStack(err)
return
}
users = append(users, user)
}
if err = rows.Err(); err != nil {
err = errors.WithStack(err)
}
return
}
// TxUpdateUser .
func (d *Dao) TxUpdateUser(c context.Context, tx *xsql.Tx, mid int64, status model.BlockStatus) (err error) {
if _, err = tx.Exec(_upsertUser, mid, status, status); err != nil {
err = errors.WithStack(err)
return
}
return
}
// UserDetails .
func (d *Dao) UserDetails(c context.Context, mids []int64) (users []*model.DBUserDetail, err error) {
if len(mids) == 0 {
return
}
var (
sql = fmt.Sprintf(_userDetails, strings.Join(intsToStrs(mids), ","))
rows *xsql.Rows
)
if rows, err = d.db.Query(c, sql); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
user := &model.DBUserDetail{}
if err = rows.Scan(&user.ID, &user.MID, &user.BlockCount, &user.CTime, &user.MTime); err != nil {
err = errors.WithStack(err)
return
}
users = append(users, user)
}
if err = rows.Err(); err != nil {
err = errors.WithStack(err)
}
return
}
// UpdateAddBlockCount .
func (d *Dao) UpdateAddBlockCount(c context.Context, mid int64) (err error) {
if _, err = d.db.Exec(c, _addAddBlockCount, mid); err != nil {
err = errors.WithStack(err)
return
}
return
}
// History 获得mid历史封禁记录
func (d *Dao) History(c context.Context, mid int64, start, limit int) (history []*model.DBHistory, err error) {
var (
rows *xsql.Rows
sql = fmt.Sprintf(_history, historyIdx(mid))
)
if rows, err = d.db.Query(c, sql, mid, start, limit); err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
h := &model.DBHistory{}
if err = rows.Scan(&h.ID, &h.MID, &h.AdminID, &h.AdminName, &h.Source, &h.Area, &h.Reason, &h.Comment, &h.Action, &h.StartTime, &h.Duration, &h.Notify, &h.CTime, &h.MTime); err != nil {
err = errors.WithStack(err)
return
}
history = append(history, h)
}
if err = rows.Err(); err != nil {
return
}
return
}
// HistoryCount 获得历史记录总长度
func (d *Dao) HistoryCount(c context.Context, mid int64) (total int, err error) {
var (
row *xsql.Row
sql = fmt.Sprintf(_historyCount, historyIdx(mid))
)
row = d.db.QueryRow(c, sql, mid)
if err = row.Scan(&total); err != nil {
err = errors.WithStack(err)
return
}
return
}
// TxInsertHistory .
func (d *Dao) TxInsertHistory(c context.Context, tx *xsql.Tx, h *model.DBHistory) (err error) {
var (
sql = fmt.Sprintf(_insertHistory, historyIdx(h.MID))
)
if _, err = tx.Exec(sql, h.MID, h.AdminID, h.AdminName, h.Source, h.Area, h.Reason, h.Comment, h.Action, h.StartTime, h.Duration, h.Notify); err != nil {
err = errors.WithStack(err)
return
}
return
}
func intsToStrs(ints []int64) (strs []string) {
for _, i := range ints {
strs = append(strs, fmt.Sprintf("%d", i))
}
return
}

View File

@@ -0,0 +1,72 @@
package dao
import (
"context"
mdlaccount "go-common/app/service/main/account/model"
mdlfigure "go-common/app/service/main/figure/model"
mdlspy "go-common/app/service/main/spy/model"
"github.com/pkg/errors"
)
// SpyScore .
func (d *Dao) SpyScore(c context.Context, mid int64) (score int8, err error) {
var (
arg = &mdlspy.ArgUserScore{
Mid: mid,
}
res *mdlspy.UserScore
)
if res, err = d.spyRPC.UserScore(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if res == nil {
return
}
score = res.Score
return
}
// FigureRank .
func (d *Dao) FigureRank(c context.Context, mid int64) (rank int8, err error) {
var (
arg = &mdlfigure.ArgUserFigure{
Mid: mid,
}
res *mdlfigure.FigureWithRank
)
if res, err = d.figureRPC.UserFigure(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if res == nil {
rank = 100
return
}
rank = res.Percentage
return
}
// AccountInfo .
func (d *Dao) AccountInfo(c context.Context, mid int64) (nickname string, tel int32, level int32, regTime int64, err error) {
var (
arg = &mdlaccount.ArgMid{
Mid: mid,
}
res *mdlaccount.Profile
)
if res, err = d.accountRPC.Profile3(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if res == nil {
return
}
nickname = res.Name
tel = res.TelStatus
level = res.Level
regTime = int64(res.JoinTime)
return
}

View File

@@ -0,0 +1,45 @@
# 新增 block_user 表
CREATE TABLE `block_user` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
`mid` INT(11) NOT NULL COMMENT '用户mid',
`status` TINYINT(4) NOT NULL COMMENT '封禁状态',
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mid` (`mid`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='封禁服务用户表';
# 新增 block_user_detail 表 ,用户详情表,用作聚合数据用
CREATE TABLE `block_user_detail` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
`mid` INT(11) NOT NULL COMMENT '用户mid',
`block_count` INT(11) NOT NULL COMMENT '封禁计次',
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mid` (`mid`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='封禁服务用户详情表';
# 新增 block_history 表 —— 10张分表
CREATE TABLE `block_history_[0-9]` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
`mid` INT(11) NOT NULL COMMENT '用户mid',
`admin_id` INT(11) NOT NULL COMMENT '管理员id',
`admin_name` VARCHAR(16) NOT NULL COMMENT '管理员name',
`source` TINYINT(4) NOT NULL COMMENT '封禁来源',
`area` TINYINT(4) NOT NULL COMMENT '封禁业务',
`reason` VARCHAR(50) NOT NULL COMMENT '封禁理由',
`comment` VARCHAR(50) NOT NULL COMMENT '封禁备注',
`action` TINYINT(4) NOT NULL COMMENT '操作类型',
`start_time` TIMESTAMP NOT NULL COMMENT '开始生效时间',
`duration` INT(11) NOT NULL COMMENT '生效时长(秒)',
`notify` TINYINT(4) NOT NULL COMMENT '是否通知',
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `ix_mid` (`mid`),
KEY `ix_action` (`action`),
KEY `ix_mtime` (`mtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='封禁服务用户历史表';

View File

@@ -0,0 +1,53 @@
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/block/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"block.go",
"http.go",
],
importpath = "go-common/app/admin/main/block/http",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//app/admin/main/block/model:go_default_library",
"//app/admin/main/block/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/permit: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,62 @@
package http
import (
"go-common/app/admin/main/block/model"
bm "go-common/library/net/http/blademaster"
)
func blockSearch(c *bm.Context) {
var (
err error
v = &model.ParamSearch{}
)
if err = bind(c, v); err != nil {
return
}
c.JSON(svc.Search(c, v.MIDs))
}
func blockHistory(c *bm.Context) {
var (
err error
v = &model.ParamHistory{}
)
if err = bind(c, v); err != nil {
return
}
var ret struct {
Status model.BlockStatus `json:"status"`
Total int `json:"total"`
History []*model.BlockHistory `json:"history"`
}
if ret.Status, ret.Total, ret.History, err = svc.History(c, v.MID, v.PS, v.PN); err != nil {
c.JSON(nil, err)
return
}
if ret.History == nil {
ret.History = make([]*model.BlockHistory, 0)
}
c.JSON(ret, err)
}
func batchBlock(c *bm.Context) {
var (
err error
v = &model.ParamBatchBlock{}
)
if err = bind(c, v); err != nil {
return
}
c.JSON(nil, svc.BatchBlock(c, v))
}
func batchRemove(c *bm.Context) {
var (
err error
v = &model.ParamBatchRemove{}
)
if err = bind(c, v); err != nil {
return
}
c.JSON(nil, svc.BatchRemove(c, v))
}

View File

@@ -0,0 +1,65 @@
package http
import (
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/model"
"go-common/app/admin/main/block/service"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
"github.com/pkg/errors"
)
var (
authSvc *permit.Permit
svc *service.Service
)
// Init http server
func Init() {
initService()
engine := bm.DefaultServer(conf.Conf.BM)
innerRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start() error(%v)", err)
panic(err)
}
}
// initService init biz services
func initService() {
authSvc = permit.New(conf.Conf.Auth)
svc = service.New()
}
func innerRouter(e *bm.Engine) {
e.GET("/monitor/ping", func(c *bm.Context) {})
cb := e.Group("/x/admin/block", authSvc.Permit(conf.Conf.Perms.Perm["search"]))
{
cb.POST("/search", blockSearch)
cb.GET("/history", blockHistory)
}
cb = e.Group("/x/admin/block", authSvc.Permit(conf.Conf.Perms.Perm["block"]))
{
cb.POST("", batchBlock)
}
cb = e.Group("/x/admin/block", authSvc.Permit(conf.Conf.Perms.Perm["remove"]))
{
cb.POST("/remove", batchRemove)
}
}
func bind(c *bm.Context, v model.ParamValidator) (err error) {
if err = c.Bind(v); err != nil {
err = errors.WithStack(err)
return
}
if !v.Validate() {
err = ecode.RequestErr
c.JSON(nil, ecode.RequestErr)
return
}
return
}

View File

@@ -0,0 +1,25 @@
package http
import (
"flag"
"testing"
"go-common/app/admin/main/block/conf"
. "github.com/smartystreets/goconvey/convey"
)
func TestMain(m *testing.M) {
flag.Set("conf", "../cmd/block-admin-test.toml")
var err error
if err = conf.Init(); err != nil {
panic(err)
}
m.Run()
}
func TestTools(t *testing.T) {
Convey("tools", t, func() {
})
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"http.go",
"model.go",
"msg.go",
"notify.go",
],
importpath = "go-common/app/admin/main/block/model",
tags = ["automanaged"],
)
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,46 @@
package model
import (
"time"
)
// DBUser .
type DBUser struct {
ID int64
MID int64
Status BlockStatus
CTime time.Time
MTime time.Time
}
// DBUserDetail .
type DBUserDetail struct {
ID int64
MID int64
BlockCount int
CTime time.Time
MTime time.Time
}
// DBHistory .
type DBHistory struct {
ID int64
MID int64
AdminID int64
AdminName string
Source BlockSource
Area BlockArea
Reason string
Comment string
Action BlockAction
StartTime time.Time
Duration int64
Notify bool
CTime time.Time
MTime time.Time
}
// MCBlockInfo .
type MCBlockInfo struct {
BlockStatus BlockStatus `json:"bs"`
}

View File

@@ -0,0 +1,130 @@
package model
// ParamValidator .
type ParamValidator interface {
Validate() bool
}
// ParamSearch .
type ParamSearch struct {
MIDs []int64 `form:"mids,split"`
}
// Validate .
func (p *ParamSearch) Validate() bool {
p.MIDs = intsSet(p.MIDs)
if len(p.MIDs) == 0 || len(p.MIDs) > 200 {
return false
}
return true
}
// ParamHistory .
type ParamHistory struct {
MID int64 `form:"mid"`
PS int `form:"ps"`
PN int `form:"pn"`
}
// Validate .
func (p *ParamHistory) Validate() bool {
if p.MID <= 0 {
return false
}
if p.PS <= 0 || p.PS > 100 {
return false
}
if p.PN <= 0 {
return false
}
return true
}
// ParamBatchBlock .
type ParamBatchBlock struct {
MIDs []int64 `form:"mids,split"`
AdminID int64 `form:"admin_id"`
AdminName string `form:"admin_name"`
Source int `form:"source"` //1 系统封禁 2 小黑屋封禁
Area BlockArea `form:"area"`
Reason string `form:"reason"`
Comment string `form:"comment"`
Action BlockAction `form:"action"`
Duration int64 `form:"duration"` // 单位:天
Notify bool `form:"notify"`
}
// Validate .
func (p *ParamBatchBlock) Validate() bool {
p.MIDs = intsSet(p.MIDs)
if len(p.MIDs) == 0 || len(p.MIDs) > 200 {
return false
}
if p.AdminID <= 0 {
return false
}
if p.AdminName == "" {
return false
}
if p.Source != 1 && p.Source != 2 {
return false
}
if !p.Area.Contain() {
return false
}
if p.Comment == "" {
return false
}
if p.Action != BlockActionForever && p.Action != BlockActionLimit {
return false
}
if p.Action == BlockActionLimit {
if p.Duration <= 0 {
return false
}
}
return true
}
// ParamBatchRemove .
type ParamBatchRemove struct {
MIDs []int64 `form:"mids,split"`
AdminID int64 `form:"admin_id"`
AdminName string `form:"admin_name"`
Comment string `form:"comment"`
Notify bool `form:"notify"`
}
// Validate .
func (p *ParamBatchRemove) Validate() bool {
p.MIDs = intsSet(p.MIDs)
if len(p.MIDs) == 0 || len(p.MIDs) > 200 {
return false
}
if p.AdminID <= 0 {
return false
}
if p.AdminName == "" {
return false
}
if p.Comment == "" {
return false
}
return true
}
func intsSet(ints []int64) (intSet []int64) {
if len(ints) == 0 {
return
}
OUTER:
for i := range ints {
for ni := range intSet {
if ints[i] == intSet[ni] {
continue OUTER
}
}
intSet = append(intSet, ints[i])
}
return
}

View File

@@ -0,0 +1,212 @@
package model
import (
"time"
)
// BlockStatus 封禁状态 0. 未封禁 1. 永久封禁 2. 限时封禁
type BlockStatus uint8
const (
// BlockStatusFalse 未封禁
BlockStatusFalse BlockStatus = iota
// BlockStatusForever 永久封禁
BlockStatusForever
// BlockStatusLimit 限时封禁
BlockStatusLimit
// BlockStatusCredit 小黑屋封禁
BlockStatusCredit
)
// BlockSource 封禁来源 1. 小黑屋(小黑屋和manager后台封禁) 2. 系统封禁(反作弊及监控系统上报) 3.解封 (所有后台,用户前台自助的解封)
type BlockSource uint8
// Contain .
func (b BlockSource) Contain() bool {
switch b {
case BlockSourceBlackHouse, BlockSourceSys, BlockSourceRemove:
return true
default:
return false
}
}
const (
// BlockSourceBlackHouse 小黑屋封禁
BlockSourceBlackHouse BlockSource = iota + 1
// BlockSourceSys 系统封禁
BlockSourceSys
// BlockSourceRemove 解封
BlockSourceRemove
)
// String .
func (b BlockSource) String() string {
switch b {
case BlockSourceBlackHouse:
return "小黑屋封禁"
case BlockSourceSys:
return "系统封禁"
case BlockSourceRemove:
return "解封"
default:
return ""
}
}
const (
// BlockLogBizID 用户审核日志
BlockLogBizID int = 122
)
// BlockArea 封禁业务
type BlockArea uint8
// Contain .
func (b BlockArea) Contain() bool {
switch b {
case BlockAreaNone, BlockAreaReply, BlockAreaDanmaku, BlockAreaMessage, BlockAreaTag, BlockAreaProfile, BlockAreaArchive, BlockAreaMusic, BlockAreaArticle, BlockAreaSpaceBanner, BlockAreaDynamic, BlockAreaAlbum, BlockAreaQuickVideo:
return true
default:
return false
}
}
func (b BlockArea) String() string {
switch b {
case BlockAreaReply:
return "评论"
case BlockAreaDanmaku:
return "弹幕"
case BlockAreaMessage:
return "私信"
case BlockAreaTag:
return "标签"
case BlockAreaProfile:
return "个人资料"
case BlockAreaArchive:
return "投稿"
case BlockAreaMusic:
return "音频"
case BlockAreaArticle:
return "专栏"
case BlockAreaSpaceBanner:
return "空间头图"
case BlockAreaDynamic:
return "动态"
case BlockAreaAlbum:
return "相册"
case BlockAreaQuickVideo:
return "小视频"
default:
return ""
}
}
// const .
const (
BlockAreaNone BlockArea = iota
BlockAreaReply
BlockAreaDanmaku
BlockAreaMessage
BlockAreaTag
BlockAreaProfile // 个人资料
BlockAreaArchive
BlockAreaMusic
BlockAreaArticle
BlockAreaSpaceBanner // 空间头图
BlockAreaDynamic // 动态
BlockAreaAlbum // 相册
BlockAreaQuickVideo //小视频
)
// BlockAction .
type BlockAction uint8
const (
// BlockActionLimit 限时封禁
BlockActionLimit BlockAction = iota + 1
// BlockActionForever 永久封禁
BlockActionForever
// BlockActionAdminRemove 后台解封
BlockActionAdminRemove
// BlockActionSelfRemove 自动解封
BlockActionSelfRemove
)
// String .
func (b BlockAction) String() string {
switch b {
case BlockActionLimit:
return "限时封禁"
case BlockActionForever:
return "永久封禁"
case BlockActionAdminRemove:
return "后台解封"
case BlockActionSelfRemove:
return "自动解封"
default:
return ""
}
}
// BlockInfo 封禁信息
type BlockInfo struct {
MID int64 `json:"mid"`
Nickname string `json:"nickname"`
Username string `json:"username"` // 注册生成时不可更改的username
Tel string `json:"tel"`
TelStatus int32 `json:"tel_status"`
Mail string `json:"mail"` // 绑定的邮箱
Level int32 `json:"level"`
SpyScore int8 `json:"spy_score"`
FigureRank int8 `json:"figure_rank"`
RegTime int64 `json:"reg_time"`
BlockStatus BlockStatus `json:"block_status"` // blockStatus 封禁状态 0. 未封禁 1. 永久封禁 2. 限时封禁
BlockCount int `json:"block_count"`
}
// ParseStatus .
func (b *BlockInfo) ParseStatus(db *DBUser) {
switch db.Status {
case BlockStatusCredit:
b.BlockStatus = BlockStatusLimit
default:
b.BlockStatus = db.Status
}
}
// BlockHistory 封禁历史
type BlockHistory struct {
Source BlockSource `json:"type"`
Operator string `json:"operator"` // 操作人
Reason string `json:"reason"` // 封禁原因
Action BlockAction `json:"action"` // 操作类型
ActionTime int64 `json:"action_time"` // 操作时间
RemoveTime int64 `json:"remove_time"` // 解封时间
Comment string `json:"comment"`
}
// ParseDB .
func (b *BlockHistory) ParseDB(data *DBHistory) {
b.Source = data.Source
b.Operator = data.AdminName
if data.Area.String() == "" {
b.Reason = data.Reason
} else {
b.Reason = data.Area.String() + " - " + data.Reason
}
b.Action = data.Action
b.ActionTime = data.StartTime.Unix()
if b.Action == BlockActionLimit {
b.RemoveTime = data.StartTime.Add(time.Second * time.Duration(data.Duration)).Unix()
}
b.Comment = data.Comment
}
// BlockMessage 通知消息体
type BlockMessage struct {
MID int64 `json:"mid"` // 用户mid
Area BlockArea `json:"area"` // BlockArea 封禁类型 1. 小黑屋(小黑屋和manager后台封禁) 2. 系统封禁(反作弊及监控系统上报) 3.解封 (所有后台,用户前台自助的解封)
Status BlockStatus `json:"status"` // blockStatus 封禁状态 0. 未封禁 1. 永久封禁 2. 限时封禁
}

View File

@@ -0,0 +1,17 @@
package model
// MSGType .
type MSGType uint8
// const .
const (
MSGTypeBlock MSGType = iota + 1
MSGTypeBlockRemove
)
// MSG .
type MSG struct {
Code string
Title string
Content string
}

View File

@@ -0,0 +1,8 @@
package model
// AccountNotify .
type AccountNotify struct {
UID int64 `json:"mid"`
Type string `json:"type"`
Action string `json:"action"`
}

View File

@@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//app/admin/main/block/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"audit_log.go",
"block.go",
"msg.go",
"notify.go",
"service.go",
],
importpath = "go-common/app/admin/main/block/service",
tags = ["automanaged"],
deps = [
"//app/admin/main/block/conf:go_default_library",
"//app/admin/main/block/dao:go_default_library",
"//app/admin/main/block/model:go_default_library",
"//library/cache:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/sync/errgroup: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,48 @@
package service
import (
"context"
"strconv"
"time"
"go-common/app/admin/main/block/model"
"go-common/library/log"
manager "go-common/library/queue/databus/report"
)
// AddAuditLog .
func (s *Service) AddAuditLog(c context.Context, tp model.BlockAction, uid int64, uname string, oids []int64, duration time.Duration, source model.BlockSource, area model.BlockArea, reason, comment string, notify bool, stime time.Time) error {
var (
err error
dur = int64(duration / time.Second)
notifyStr = strconv.FormatBool(notify)
)
for _, oid := range oids {
managerInfo := &manager.ManagerInfo{
UID: uid,
Uname: uname,
Business: model.BlockLogBizID,
Type: int(tp),
Action: tp.String(),
Oid: oid,
Ctime: time.Now(),
Index: []interface{}{dur, uint8(source), uint8(area), reason, comment, notifyStr},
Content: map[string]interface{}{
"duration": dur,
"source": source,
"area": area,
"reason": reason,
"comment": comment,
"notify": notifyStr,
"action_time": stime.Unix(),
"remove_time": stime.Add(time.Second * time.Duration(dur)).Unix(),
},
}
if err = manager.Manager(managerInfo); err != nil {
log.Error("manager.Manager(%+v) error(%+v)", managerInfo, err)
continue
}
log.Info("s.managerSendLog(%+v)", managerInfo)
}
return err
}

View File

@@ -0,0 +1,318 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/admin/main/block/model"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/sync/errgroup"
"github.com/pkg/errors"
)
// Search .
func (s *Service) Search(c context.Context, mids []int64) (infos []*model.BlockInfo, err error) {
var (
users []*model.DBUser
userDetails []*model.DBUserDetail
eg errgroup.Group
mapMu sync.Mutex
userMap = make(map[int64]*model.BlockInfo)
)
if users, err = s.dao.Users(c, mids); err != nil {
return
}
if userDetails, err = s.dao.UserDetails(c, mids); err != nil {
return
}
infos = make([]*model.BlockInfo, 0, len(mids))
for _, m := range mids {
mid := m
eg.Go(func() (err error) {
info := &model.BlockInfo{
MID: mid,
}
// 1. account 数据
if info.Nickname, info.TelStatus, info.Level, info.RegTime, err = s.dao.AccountInfo(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
}
if info.Nickname == "" {
log.Info("user mid(%d) not found", info.MID)
return
}
// 2. 封禁状态
for i := range users {
if users[i].MID == mid {
info.ParseStatus(users[i])
break
}
}
// 3. 封禁次数
for i := range userDetails {
if userDetails[i].MID == mid {
info.BlockCount = userDetails[i].BlockCount
break
}
}
// 4. spy 分值
if info.SpyScore, err = s.dao.SpyScore(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.SpyScore = -1
}
// 5. figure 排名
if info.FigureRank, err = s.dao.FigureRank(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.FigureRank = -1
}
// 6. extra 额外账号信息
if info.Tel, err = s.telInfo(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.Tel = "N/A"
}
if info.Mail, err = s.mailInfo(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.Mail = "N/A"
}
if info.Username, err = s.userID(context.TODO(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.Username = "N/A"
}
mapMu.Lock()
userMap[info.MID] = info
mapMu.Unlock()
return
})
}
eg.Wait()
for _, mid := range mids {
if info, ok := userMap[mid]; ok {
infos = append(infos, info)
}
}
return
}
// History .
func (s *Service) History(c context.Context, mid int64, ps, pn int) (status model.BlockStatus, total int, history []*model.BlockHistory, err error) {
var (
start = (pn - 1) * ps
limit = ps
dbHistory []*model.DBHistory
dbUser *model.DBUser
)
if dbUser, err = s.dao.User(c, mid); err != nil {
return
}
if dbUser != nil {
status = dbUser.Status
}
if total, err = s.dao.HistoryCount(c, mid); err != nil {
return
}
if dbHistory, err = s.dao.History(c, mid, start, limit); err != nil {
return
}
for i := range dbHistory {
his := &model.BlockHistory{}
his.ParseDB(dbHistory[i])
history = append(history, his)
}
return
}
// BatchBlock .
func (s *Service) BatchBlock(c context.Context, p *model.ParamBatchBlock) (err error) {
var (
tx *xsql.Tx
duration = time.Duration(p.Duration) * time.Hour * 24
source model.BlockSource
mids []int64
stime = time.Now()
)
if tx, err = s.dao.BeginTX(c); err != nil {
return
}
if p.Source == 1 {
// 系统封禁
source = model.BlockSourceSys
} else if p.Source == 2 {
// 小黑屋封禁
source = model.BlockSourceBlackHouse
if err = s.dao.BlackhouseBlock(context.TODO(), p); err != nil {
return
}
}
for _, mid := range p.MIDs {
mids = append(mids, mid)
theMID := mid
s.cache.Save(func() {
if cacheErr := s.dao.DeleteUserCache(context.TODO(), theMID); cacheErr != nil {
log.Error("%+v", cacheErr)
}
if databusErr := s.accountNotify(context.TODO(), theMID); databusErr != nil {
log.Error("%+v", databusErr)
}
})
if err = s.action(c, tx, mid, p.AdminID, p.AdminName, source, p.Area, p.Reason, p.Comment, p.Action, duration, p.Notify, stime); err != nil {
tx.Rollback()
return
}
}
// 发送站内信
if p.Notify {
s.mission(func() {
if notifyErr := s.notifyMSG(context.TODO(), p.MIDs, source, p.Action, p.Area, p.Reason, p.Duration); notifyErr != nil {
log.Error("%+v", notifyErr)
return
}
})
}
s.mission(func() {
s.AddAuditLog(context.TODO(), p.Action, p.AdminID, p.AdminName, mids, duration, source, p.Area, p.Reason, p.Comment, p.Notify, stime)
})
err = tx.Commit()
return
}
// BatchRemove .
func (s *Service) BatchRemove(c context.Context, p *model.ParamBatchRemove) (err error) {
var (
tx *xsql.Tx
mids []int64
stime = time.Now()
)
if tx, err = s.dao.BeginTX(c); err != nil {
return
}
for _, mid := range p.MIDs {
mids = append(mids, mid)
theMID := mid
s.cache.Save(func() {
if cacheErr := s.dao.DeleteUserCache(context.TODO(), theMID); cacheErr != nil {
log.Error("%+v", cacheErr)
}
if databusErr := s.accountNotify(context.TODO(), theMID); databusErr != nil {
log.Error("%+v", databusErr)
}
})
if err = s.action(c, tx, mid, p.AdminID, p.AdminName, model.BlockSourceRemove, model.BlockAreaNone, "", p.Comment, model.BlockActionAdminRemove, 0, p.Notify, stime); err != nil {
tx.Rollback()
return
}
}
// 发送站内信
if p.Notify {
s.mission(func() {
if notifyErr := s.notifyMSG(context.TODO(), p.MIDs, model.BlockSourceRemove, model.BlockActionAdminRemove, model.BlockAreaNone, "", 0); notifyErr != nil {
log.Error("%+v", notifyErr)
return
}
})
}
s.mission(func() {
s.AddAuditLog(context.TODO(), model.BlockActionAdminRemove, p.AdminID, p.AdminName, mids, 0, model.BlockSourceRemove, model.BlockAreaNone, "", p.Comment, p.Notify, stime)
})
err = tx.Commit()
return
}
// notifyMSG .
func (s *Service) notifyMSG(c context.Context, mids []int64, source model.BlockSource, action model.BlockAction, area model.BlockArea, reason string, days int64) (err error) {
code, title, content := s.MSGInfo(source, action, area, reason, days)
log.Info("block admin title : %s , content : %s , mids : %+v", title, content, mids)
if err = s.dao.SendSysMsg(context.TODO(), code, mids, title, content, ""); err != nil {
return
}
return
}
func (s *Service) action(c context.Context, tx *xsql.Tx, mid int64, adminID int64, adminName string, source model.BlockSource, area model.BlockArea, reason, comment string, action model.BlockAction, duration time.Duration, notify bool, stime time.Time) (err error) {
var (
db = &model.DBHistory{
MID: mid,
AdminID: adminID,
AdminName: adminName,
Source: source,
Area: area,
Reason: reason,
Comment: comment,
Action: action,
StartTime: stime,
Duration: int64(duration / time.Second),
Notify: notify,
}
blockStatus model.BlockStatus
)
if err = s.dao.TxInsertHistory(c, tx, db); err != nil {
return
}
switch action {
case model.BlockActionAdminRemove, model.BlockActionSelfRemove:
blockStatus = model.BlockStatusFalse
case model.BlockActionLimit:
switch source {
case model.BlockSourceBlackHouse:
blockStatus = model.BlockStatusCredit
default:
blockStatus = model.BlockStatusLimit
}
s.mission(func() {
if err = s.dao.UpdateAddBlockCount(context.TODO(), mid); err != nil {
log.Error("%+v", err)
}
})
case model.BlockActionForever:
blockStatus = model.BlockStatusForever
s.mission(func() {
if err = s.dao.UpdateAddBlockCount(context.TODO(), mid); err != nil {
log.Error("%+v", err)
}
})
default:
err = errors.Errorf("unknown block action [%d]", action)
return
}
if err = s.dao.TxUpdateUser(c, tx, mid, blockStatus); err != nil {
return
}
return
}
func (s *Service) userID(c context.Context, mid int64) (id string, err error) {
return "N/A", nil
}
func (s *Service) mailInfo(c context.Context, mid int64) (mail string, err error) {
if mail, err = s.dao.MailInfo(c, mid); mail == "" {
mail = "N/A"
}
return
}
// TelInfo .
func (s *Service) telInfo(c context.Context, mid int64) (tel string, err error) {
if tel, err = s.dao.TelInfo(c, mid); err != nil {
return
}
if len(tel) == 0 {
tel = "N/A"
return
}
if len(tel) < 4 {
tel = tel[:1] + "****"
return
}
tel = tel[:3] + "****" + tel[len(tel)-4:]
return
}

View File

@@ -0,0 +1,74 @@
package service
import (
"fmt"
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/model"
"go-common/library/log"
)
// MSGInfo get msg info
func (s *Service) MSGInfo(source model.BlockSource, action model.BlockAction, area model.BlockArea, reason string, days int64) (code string, title, content string) {
if source == model.BlockSourceBlackHouse {
areaStr := area.String()
if areaStr != "" {
areaStr = fmt.Sprintf("在%s中", areaStr)
}
if action == model.BlockActionLimit {
code = conf.Conf.Property.MSG.BlackHouseLimit.Code
title = conf.Conf.Property.MSG.BlackHouseLimit.Title
content = fmt.Sprintf(conf.Conf.Property.MSG.BlackHouseLimit.Content, areaStr, s.convertReason(reason), days)
return
}
if action == model.BlockActionForever {
code = conf.Conf.Property.MSG.BlackHouseForever.Code
title = conf.Conf.Property.MSG.BlackHouseForever.Title
content = fmt.Sprintf(conf.Conf.Property.MSG.BlackHouseForever.Content, areaStr, s.convertReason(reason))
return
}
}
if source == model.BlockSourceSys {
if action == model.BlockActionLimit {
code = conf.Conf.Property.MSG.SysLimit.Code
title = conf.Conf.Property.MSG.SysLimit.Title
content = fmt.Sprintf(conf.Conf.Property.MSG.SysLimit.Content, s.convertReason(reason), days)
return
}
if action == model.BlockActionForever {
code = conf.Conf.Property.MSG.SysForever.Code
title = conf.Conf.Property.MSG.SysForever.Title
content = fmt.Sprintf(conf.Conf.Property.MSG.SysForever.Content, s.convertReason(reason))
return
}
}
if action == model.BlockActionAdminRemove || action == model.BlockActionSelfRemove {
code = conf.Conf.Property.MSG.BlockRemove.Code
title = conf.Conf.Property.MSG.BlockRemove.Title
content = conf.Conf.Property.MSG.BlockRemove.Content
return
}
log.Error("s.MSGInfo unkown source[%v] action[%v] area[%v] reason[%s] days[%d]", source, action, area, reason, days)
return
}
func (s *Service) convertReason(reason string) string {
switch reason {
case "账号资料相关违规":
return "账号资料违规"
case "作品投稿违规":
return "作品投稿违规"
case "异常注册账号":
return "异常注册"
case "异常答题账号":
return "异常答题"
case "异常数据行为":
return "异常数据行为"
case "发布违规信息":
return "发布违规信息"
case "其他自动封禁", "手动封禁":
return "违反社区规则"
default:
return reason
}
}

View File

@@ -0,0 +1,18 @@
package service
import (
"context"
"strconv"
"go-common/app/admin/main/block/model"
"github.com/pkg/errors"
)
func (s *Service) accountNotify(c context.Context, mid int64) (err error) {
msg := &model.AccountNotify{UID: mid, Action: "blockUser"}
if err = s.accountNotifyPub.Send(c, strconv.FormatInt(msg.UID, 10), msg); err != nil {
err = errors.Errorf("mid(%d) s.accountNotify.Send(%+v) error(%+v)", msg.UID, msg, err)
}
return
}

View File

@@ -0,0 +1,63 @@
package service
import (
"context"
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/dao"
"go-common/library/cache"
"go-common/library/log"
"go-common/library/queue/databus"
)
// Service struct
type Service struct {
dao *dao.Dao
cache *cache.Cache
missch chan func()
accountNotifyPub *databus.Databus
}
// New init
func New() (s *Service) {
s = &Service{
dao: dao.New(),
cache: cache.New(1, 10240),
missch: make(chan func(), 10240),
accountNotifyPub: databus.New(conf.Conf.AccountNotify),
}
go s.missproc()
return s
}
func (s *Service) missproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.missproc panic(%v)", x)
go s.missproc()
log.Info("service.missproc recover")
}
}()
for {
f := <-s.missch
f()
}
}
func (s *Service) mission(f func()) {
select {
case s.missch <- f:
default:
log.Error("s.missch full")
}
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}

View File

@@ -0,0 +1,68 @@
package service
import (
"context"
"flag"
"os"
"testing"
"go-common/app/admin/main/block/conf"
"go-common/app/admin/main/block/model"
. "github.com/smartystreets/goconvey/convey"
)
var (
s *Service
c = context.TODO()
)
func TestMain(m *testing.M) {
defer os.Exit(0)
flag.Set("conf", "../cmd/block-admin-test.toml")
var err error
if err = conf.Init(); err != nil {
panic(err)
}
s = New()
defer s.Close()
m.Run()
}
func TestService(t *testing.T) {
Convey("", t, func() {
s.Ping(c)
s.Close()
})
}
func TestBlock(t *testing.T) {
Convey("block", t, func() {
var (
p = &model.ParamBatchBlock{
MIDs: []int64{1, 2, 3, 4},
AdminID: 233,
AdminName: "233",
Source: 1,
Area: model.BlockAreaNone,
Reason: "test",
Comment: "test",
Action: model.BlockActionLimit,
Duration: 1,
Notify: false,
}
pm = &model.ParamBatchRemove{
MIDs: []int64{1, 2, 3, 4},
AdminID: 233,
AdminName: "233",
Comment: "test",
Notify: false,
}
)
err := s.BatchBlock(c, p)
So(err, ShouldBeNil)
err = s.BatchRemove(c, pm)
So(err, ShouldBeNil)
})
}