248 lines
7.5 KiB
Go
248 lines
7.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"go-common/app/admin/main/dm/conf"
|
|
"go-common/app/admin/main/dm/dao"
|
|
oplogDao "go-common/app/admin/main/dm/dao/oplog"
|
|
"go-common/app/admin/main/dm/model"
|
|
"go-common/app/admin/main/dm/model/oplog"
|
|
accountApi "go-common/app/service/main/account/api"
|
|
archive "go-common/app/service/main/archive/api/gorpc"
|
|
"go-common/library/log"
|
|
"go-common/library/log/infoc"
|
|
"go-common/library/sync/pipeline/fanout"
|
|
)
|
|
|
|
// Service define Service struct
|
|
type Service struct {
|
|
// dao
|
|
dao *dao.Dao
|
|
oplogDao *oplogDao.Dao
|
|
// rpc
|
|
accountRPC accountApi.AccountClient
|
|
arcRPC *archive.Service2
|
|
dmOperationLogSvc *infoc.Infoc
|
|
bakInfoc *infoc.Infoc
|
|
opsLogCh chan *oplog.Infoc
|
|
reduceMoralChan chan *model.ReduceMoral
|
|
blockUserChan chan *model.BlockUser
|
|
msgReporterChan chan *model.ReportMsg
|
|
msgPosterChan chan *model.ReportMsg
|
|
actionChan chan *model.Action
|
|
// async proc
|
|
cache *fanout.Fanout
|
|
moniOidMap map[int64]struct{}
|
|
oidLock sync.Mutex
|
|
}
|
|
|
|
// New new a Service and return.
|
|
func New(c *conf.Config) *Service {
|
|
s := &Service{
|
|
// dao
|
|
dao: dao.New(c),
|
|
oplogDao: oplogDao.New(c),
|
|
// rpc
|
|
arcRPC: archive.New2(c.ArchiveRPC),
|
|
dmOperationLogSvc: infoc.New(c.Infoc2),
|
|
bakInfoc: infoc.New(c.InfocBak),
|
|
reduceMoralChan: make(chan *model.ReduceMoral, 1024),
|
|
blockUserChan: make(chan *model.BlockUser, 1024),
|
|
msgReporterChan: make(chan *model.ReportMsg, 1024),
|
|
msgPosterChan: make(chan *model.ReportMsg, 1024),
|
|
actionChan: make(chan *model.Action, 1024),
|
|
opsLogCh: make(chan *oplog.Infoc, 1024),
|
|
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
|
|
moniOidMap: make(map[int64]struct{}),
|
|
}
|
|
accountRPC, err := accountApi.NewClient(c.AccountRPC)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
s.accountRPC = accountRPC
|
|
go s.changeReportStatProc()
|
|
go s.actionproc()
|
|
go s.oplogproc()
|
|
go s.monitorproc()
|
|
return s
|
|
}
|
|
|
|
// Ping check server ok
|
|
func (s *Service) Ping(c context.Context) (err error) {
|
|
if err = s.dao.Ping(c); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Service) addAction(action *model.Action) {
|
|
select {
|
|
case s.actionChan <- action:
|
|
default:
|
|
log.Error("action channel is full,action(%v) is discard", action)
|
|
}
|
|
}
|
|
|
|
func (s *Service) actionproc() {
|
|
for action := range s.actionChan {
|
|
if err := s.dao.SendAction(context.TODO(), fmt.Sprint(action.Oid), action); err != nil {
|
|
log.Error("dao.SendAction(%v) error(%v)", action, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Service) changeReportStatProc() {
|
|
for {
|
|
select {
|
|
case msg := <-s.reduceMoralChan:
|
|
if err := s.dao.ReduceMoral(context.TODO(), msg); err != nil {
|
|
log.Error("s.dao.ReduceMoral(msg:%v) error(%v)", msg, err)
|
|
}
|
|
case msg := <-s.msgReporterChan:
|
|
if err := s.dao.SendMsgToReporter(context.TODO(), msg); err != nil {
|
|
log.Error("s.dao.SendMsgToReporter(msg:%v) error(%v)", msg, err)
|
|
}
|
|
case msg := <-s.msgPosterChan:
|
|
if err := s.dao.SendMsgToPoster(context.TODO(), msg); err != nil {
|
|
log.Error("s.dao.SendMsgToPoster(msg:%v) error(%v)", msg, err)
|
|
}
|
|
case msg := <-s.blockUserChan:
|
|
if err := s.dao.BlockUser(context.TODO(), msg); err != nil {
|
|
log.Error("s.dao.BlockUser(msg:%v) error(%v)", msg, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Service) oplogproc() {
|
|
for opLog := range s.opsLogCh {
|
|
if len(opLog.Subject) == 0 || len(opLog.CurrentVal) == 0 || opLog.Source <= 0 ||
|
|
opLog.Operator <= 0 || opLog.OperatorType <= 0 {
|
|
log.Warn("oplogproc() it is an illegal log, warn(%v, %v, %v)", opLog.Subject, opLog.Subject, opLog.CurrentVal)
|
|
continue
|
|
} else {
|
|
for _, dmid := range opLog.DMIds {
|
|
s.dmOperationLogSvc.Info(opLog.Subject, strconv.FormatInt(opLog.Oid, 10), fmt.Sprint(opLog.Type),
|
|
strconv.FormatInt(dmid, 10), opLog.Source.String(), opLog.OriginVal,
|
|
opLog.CurrentVal, strconv.FormatInt(opLog.Operator, 10), opLog.OperatorType.String(),
|
|
opLog.OperationTime, opLog.Remark)
|
|
// 将管理员操作日志额外上报一份(用于验证数据报表完整性)
|
|
s.bakInfoc.Info(opLog.Subject, strconv.FormatInt(opLog.Oid, 10), fmt.Sprint(opLog.Type),
|
|
strconv.FormatInt(dmid, 10), opLog.Source.String(), opLog.OriginVal,
|
|
opLog.CurrentVal, strconv.FormatInt(opLog.Operator, 10), opLog.OperatorType.String(),
|
|
opLog.OperationTime, opLog.Remark)
|
|
if strings.Contains(opLog.Subject, "\n") {
|
|
log.Error("\n found in opLog.Subject(%s)", opLog.Source)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// OpLog put a new infoc format operation log into the channel
|
|
func (s *Service) OpLog(c context.Context, cid, operator int64, typ int32, dmids []int64, subject, originVal, currentVal, remark string, source oplog.Source, operatorType oplog.OperatorType) (err error) {
|
|
infoLog := new(oplog.Infoc)
|
|
infoLog.Oid = cid
|
|
infoLog.Type = typ
|
|
infoLog.DMIds = dmids
|
|
infoLog.Subject = subject
|
|
infoLog.OriginVal = originVal
|
|
infoLog.CurrentVal = currentVal
|
|
infoLog.OperationTime = strconv.FormatInt(time.Now().Unix(), 10)
|
|
infoLog.Source = source
|
|
infoLog.OperatorType = operatorType
|
|
infoLog.Operator = operator
|
|
infoLog.Remark = remark
|
|
select {
|
|
case s.opsLogCh <- infoLog:
|
|
default:
|
|
err = fmt.Errorf("opsLogCh full")
|
|
log.Error("opsLogCh full (%v)", infoLog)
|
|
}
|
|
return
|
|
}
|
|
|
|
// QueryOpLogs query operation logs of damku equals dmid
|
|
func (s *Service) QueryOpLogs(c context.Context, dmid int64) (infos []*oplog.InfocResult, err error) {
|
|
result, err := s.oplogDao.QueryOpLogs(c, dmid)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, logVal := range result {
|
|
var (
|
|
tmp = &oplog.InfocResult{}
|
|
)
|
|
val, err := strconv.Atoi(logVal.CurrentVal)
|
|
if err != nil {
|
|
err = nil
|
|
continue
|
|
}
|
|
switch logVal.Subject {
|
|
case "status":
|
|
tmp.Subject = model.StateDesc(int32(val))
|
|
case "pool":
|
|
if val == 0 {
|
|
tmp.Subject = "普通弹幕池"
|
|
} else if val == 1 {
|
|
tmp.Subject = "字幕弹幕池"
|
|
} else if val == 2 {
|
|
tmp.Subject = "特殊弹幕池"
|
|
}
|
|
case "attribute":
|
|
if val == 2 || (int32(val)>>model.AttrProtect)&int32(1) == 1 {
|
|
tmp.Subject = "弹幕保护"
|
|
} else if val == 3 || (int32(val)>>model.AttrProtect)&int32(1) == 0 {
|
|
tmp.Subject = "取消弹幕保护"
|
|
}
|
|
default:
|
|
tmp.Subject = logVal.Subject
|
|
}
|
|
tmp.CurrentVal = logVal.CurrentVal
|
|
tmp.OperatorType = logVal.OperatorType
|
|
if logVal.OperatorType == "用户" || logVal.OperatorType == "UP主" {
|
|
mid, _ := strconv.ParseInt(logVal.Operator, 10, 64)
|
|
arg3 := &accountApi.MidReq{Mid: mid}
|
|
uInfo, err := s.accountRPC.Info3(c, arg3)
|
|
if err != nil {
|
|
tmp.Operator = logVal.Operator
|
|
log.Error("s.accRPC.Info2(%v) error(%v)", arg3, err)
|
|
err = nil
|
|
} else {
|
|
tmp.Operator = uInfo.GetInfo().GetName()
|
|
}
|
|
} else {
|
|
tmp.Operator = logVal.Operator
|
|
}
|
|
OperationTimeStamp, _ := strconv.ParseInt(logVal.OperationTime, 10, 64)
|
|
tmp.OperationTime = time.Unix(OperationTimeStamp, 0).Format("2006-01-02 15:04:05")
|
|
tmp.Remark = "操作来源:" + logVal.Source + ";操作员身份:" + logVal.OperatorType + ";备注:" + logVal.Remark
|
|
infos = append(infos, tmp)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Service) monitorproc() {
|
|
for {
|
|
time.Sleep(3 * time.Second)
|
|
s.oidLock.Lock()
|
|
oidMap := s.moniOidMap
|
|
s.moniOidMap = make(map[int64]struct{})
|
|
s.oidLock.Unlock()
|
|
for oid := range oidMap {
|
|
sub, err := s.dao.Subject(context.TODO(), model.SubTypeVideo, oid)
|
|
if err != nil || sub == nil {
|
|
continue
|
|
}
|
|
if err := s.updateMonitorCnt(context.TODO(), sub); err != nil {
|
|
log.Error("s.updateMonitorCnt(%+v) error(%v)", sub, err)
|
|
}
|
|
}
|
|
}
|
|
}
|