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,66 @@
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/member/conf:go_default_library",
"//app/admin/main/member/model/block: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",
"rpc.go",
"service.go",
],
importpath = "go-common/app/admin/main/member/service/block",
tags = ["automanaged"],
deps = [
"//app/admin/main/member/conf:go_default_library",
"//app/admin/main/member/dao/block:go_default_library",
"//app/admin/main/member/model/block:go_default_library",
"//app/service/main/account/api: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/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/sync/pipeline/fanout: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 block
import (
"context"
"strconv"
"time"
model "go-common/app/admin/main/member/model/block"
"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,320 @@
package block
import (
"context"
"sync"
"time"
model "go-common/app/admin/main/member/model/block"
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.AccountInfo(context.Background(), 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.SpyScore(context.Background(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.SpyScore = -1
}
// 5. figure 排名
if info.FigureRank, err = s.FigureRank(context.Background(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.FigureRank = -1
}
// 6. extra 额外账号信息
if info.Tel, err = s.telInfo(context.Background(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.Tel = "N/A"
}
if info.Mail, err = s.mailInfo(context.Background(), mid); err != nil {
log.Error("%+v", err)
err = nil
info.Mail = "N/A"
}
if info.Username, err = s.userID(context.Background(), 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, desc bool) (history *model.BlockDetail, err error) {
var (
start = (pn - 1) * ps
limit = ps
dbHistory []*model.DBHistory
dbUser *model.DBUser
)
history = &model.BlockDetail{}
if dbUser, err = s.dao.User(c, mid); err != nil {
return
}
if dbUser != nil {
history.Status = dbUser.Status
}
if history.Total, err = s.dao.HistoryCount(c, mid); err != nil {
return
}
if dbHistory, err = s.dao.History(c, mid, start, limit, desc); err != nil {
return
}
history.History = make([]*model.BlockHistory, 0, len(dbHistory))
for i := range dbHistory {
his := &model.BlockHistory{}
his.ParseDB(dbHistory[i])
history.History = append(history.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
stime = time.Now()
)
if tx, err = s.dao.BeginTX(c); err != nil {
return
}
if p.Source == model.BlockMgrSourceSys {
// 系统封禁
source = model.BlockSourceSys
} else if p.Source == model.BlockMgrSourceCredit {
// 小黑屋封禁
source = model.BlockSourceBlackHouse
if err = s.dao.BlackhouseBlock(context.Background(), p); err != nil {
return
}
}
for _, mid := range p.MIDs {
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 err = tx.Commit(); err != nil {
return
}
// 发送站内信
if p.Notify {
s.mission(func() {
if notifyErr := s.notifyMSG(context.Background(), p.MIDs, source, p.Action, p.Area, p.Reason, p.Duration); notifyErr != nil {
log.Error("%+v", notifyErr)
return
}
})
}
s.mission(func() {
s.AddAuditLog(context.Background(), p.Action, p.AdminID, p.AdminName, p.MIDs, duration, source, p.Area, p.Reason, p.Comment, p.Notify, stime)
})
s.asyncPurgeCache(p.MIDs)
return
}
// BatchRemove .
func (s *Service) BatchRemove(c context.Context, p *model.ParamBatchRemove) (err error) {
var (
tx *xsql.Tx
stime = time.Now()
)
if tx, err = s.dao.BeginTX(c); err != nil {
return
}
for _, mid := range p.MIDs {
if err = s.action(c, tx, mid, p.AdminID, p.AdminName, model.BlockSourceManager, model.BlockAreaNone, "", p.Comment, model.BlockActionAdminRemove, 0, p.Notify, stime); err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit(); err != nil {
return
}
// 发送站内信
if p.Notify {
s.mission(func() {
if notifyErr := s.notifyMSG(context.Background(), p.MIDs, model.BlockSourceManager, model.BlockActionAdminRemove, model.BlockAreaNone, "", 0); notifyErr != nil {
log.Error("%+v", notifyErr)
return
}
})
}
s.mission(func() {
s.AddAuditLog(context.Background(), model.BlockActionAdminRemove, p.AdminID, p.AdminName, p.MIDs, 0, model.BlockSourceManager, model.BlockAreaNone, "", p.Comment, p.Notify, stime)
})
s.asyncPurgeCache(p.MIDs)
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.Background(), 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.Background(), mid); err != nil {
log.Error("%+v", err)
}
})
case model.BlockActionForever:
blockStatus = model.BlockStatusForever
s.mission(func() {
if err = s.dao.UpdateAddBlockCount(context.Background(), 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
}
func (s *Service) asyncPurgeCache(mids []int64) {
_ = s.cache.Do(context.Background(), func(ctx context.Context) {
for _, mid := range mids {
if cacheErr := s.dao.DeleteUserCache(ctx, mid); cacheErr != nil {
log.Error("%+v", cacheErr)
}
if cacheErr := s.dao.DeleteUserDetailCache(ctx, mid); cacheErr != nil {
log.Error("%+v", cacheErr)
}
if databusErr := s.accountNotify(ctx, mid); databusErr != nil {
log.Error("%+v", databusErr)
}
}
})
}

View File

@@ -0,0 +1,85 @@
package block
import (
"fmt"
model "go-common/app/admin/main/member/model/block"
"go-common/library/log"
)
const (
_creditLimit = `抱歉,你的账号因“%s%s”现已进行封禁%d天处理账号解封需要满足以下两个条件:1.账号封禁时间已满。2.完成解封答题( #{点击进入解封答题}{"http://www.bilibili.com/blackroom/releaseexame.html"} )全部完成后解封。封禁期间将无法投稿、发送及回复消息,无法发布评论、弹幕,无法对他人评论进行回复、赞踩操作,无法进行投币、编辑标签、添加关注、添加收藏操作。解封后恢复正常,还请遵守社区规范,共同维护良好的社区氛围!`
_creditForever = `抱歉,你的账号因“%s%s”现已进行永久封禁处理。封禁期间将无法投稿、发送及回复消息无法发布评论、弹幕无法对他人评论进行回复、赞踩操作无法进行投币、编辑标签、添加关注、添加收藏操作。解封后恢复正常还请遵守社区规范共同维护良好的社区氛围`
_sysLimit = `抱歉,你的账号因“%s”现已进行封禁%d天处理账号解封需要满足以下两个条件:1.账号封禁时间已满。2.完成解封答题( #{点击进入解封答题}{"http://www.bilibili.com/blackroom/releaseexame.html"} )全部完成后解封。封禁期间将无法投稿、发送及回复消息,无法发布评论、弹幕,无法对他人评论进行回复、赞踩操作,无法进行投币、编辑标签、添加关注、添加收藏操作。解封后恢复正常,还请遵守社区规范,共同维护良好的社区氛围!`
_sysForever = `抱歉,你的账号因“%s”现已进行永久封禁处理。封禁期间将无法投稿、发送及回复消息无法发布评论、弹幕无法对他人评论进行回复、赞踩操作无法进行投币、编辑标签、添加关注、添加收藏操作。解封后恢复正常还请遵守社区规范共同维护良好的社区氛围`
_remove = `你的账号已经解除封禁,封禁期间禁止使用的各项社区功能已经恢复。请遵守社区规范,共同维护良好的社区氛围。`
)
// 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 = "2_3_2"
title = "账号违规处理通知"
content = fmt.Sprintf(_creditLimit, areaStr, s.convertReason(reason), days)
return
}
if action == model.BlockActionForever {
code = "2_3_3"
title = "账号违规处理通知"
content = fmt.Sprintf(_creditForever, areaStr, s.convertReason(reason))
return
}
}
// 系统封禁
if source == model.BlockSourceSys {
if action == model.BlockActionLimit {
code = "2_3_4"
title = "账号违规处理通知"
content = fmt.Sprintf(_sysLimit, s.convertReason(reason), days)
return
}
if action == model.BlockActionForever {
code = "2_3_5"
title = "账号违规处理通知"
content = fmt.Sprintf(_sysForever, s.convertReason(reason))
return
}
}
if action == model.BlockActionAdminRemove || action == model.BlockActionSelfRemove {
code = "2_3_6"
title = "账号封禁解除通知"
content = _remove
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 block
import (
"context"
"strconv"
"go-common/app/admin/main/member/model/block"
"github.com/pkg/errors"
)
func (s *Service) accountNotify(c context.Context, mid int64) (err error) {
msg := &block.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,72 @@
package block
import (
"context"
account "go-common/app/service/main/account/api"
mdlfigure "go-common/app/service/main/figure/model"
mdlspy "go-common/app/service/main/spy/model"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// SpyScore .
func (s *Service) SpyScore(c context.Context, mid int64) (score int8, err error) {
var (
arg = &mdlspy.ArgUserScore{
Mid: mid,
}
res *mdlspy.UserScore
)
if res, err = s.spyRPC.UserScore(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if res == nil {
return
}
score = res.Score
return
}
// FigureRank .
func (s *Service) FigureRank(c context.Context, mid int64) (rank int8, err error) {
var (
arg = &mdlfigure.ArgUserFigure{
Mid: mid,
}
res *mdlfigure.FigureWithRank
)
if res, err = s.figureRPC.UserFigure(c, arg); err != nil {
err = errors.WithStack(err)
return
}
if res == nil {
rank = 100
return
}
rank = res.Percentage
return
}
// AccountInfo .
func (s *Service) AccountInfo(c context.Context, mid int64) (nickname string, tel int32, level int32, regTime int64, err error) {
var (
arg = &account.MidReq{
Mid: mid,
RealIp: metadata.String(c, metadata.RemoteIP),
}
profileReply *account.ProfileReply
)
if profileReply, err = s.accountClient.Profile3(c, arg); err != nil {
err = errors.WithStack(err)
return
}
res := profileReply.Profile
nickname = res.Name
tel = res.TelStatus
level = res.Level
regTime = int64(res.JoinTime)
return
}

View File

@@ -0,0 +1,75 @@
package block
import (
"context"
"runtime/debug"
"go-common/app/admin/main/member/conf"
"go-common/app/admin/main/member/dao/block"
account "go-common/app/service/main/account/api"
rpcfigure "go-common/app/service/main/figure/rpc/client"
rpcspy "go-common/app/service/main/spy/rpc/client"
"go-common/library/log"
"go-common/library/queue/databus"
"go-common/library/sync/pipeline/fanout"
)
// Service struct
type Service struct {
conf *conf.Config
dao *block.Dao
cache *fanout.Fanout
spyRPC *rpcspy.Service
figureRPC *rpcfigure.Service
accountClient account.AccountClient
missch chan func()
accountNotifyPub *databus.Databus
}
// New init
func New(conf *conf.Config, dao *block.Dao, spyRPC *rpcspy.Service, figureRPC *rpcfigure.Service,
accountClient account.AccountClient, accountNotifyPub *databus.Databus) (s *Service) {
s = &Service{
conf: conf,
dao: dao,
cache: fanout.New("memberAdminCache", fanout.Worker(1), fanout.Buffer(10240)),
missch: make(chan func(), 10240),
accountNotifyPub: accountNotifyPub,
spyRPC: spyRPC,
figureRPC: figureRPC,
accountClient: accountClient,
}
go s.missproc()
return s
}
func (s *Service) missproc() {
defer func() {
if x := recover(); x != nil {
log.Error("service.missproc panic(%+v) : %s", x, debug.Stack())
go s.missproc()
}
}()
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,65 @@
package block
import (
"context"
"flag"
"os"
"testing"
"go-common/app/admin/main/member/conf"
"go-common/app/admin/main/member/model/block"
. "github.com/smartystreets/goconvey/convey"
)
var (
s *Service
c = context.Background()
)
func TestMain(m *testing.M) {
defer os.Exit(0)
flag.Set("conf", "../cmd/member-admin-test.toml")
var err error
if err = conf.Init(); err != nil {
panic(err)
}
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 = &block.ParamBatchBlock{
MIDs: []int64{1, 2, 3, 4},
AdminID: 233,
AdminName: "233",
Source: 1,
Area: block.BlockAreaNone,
Reason: "test",
Comment: "test",
Action: block.BlockActionLimit,
Duration: 1,
Notify: false,
}
pm = &block.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)
})
}