You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
9.9 KiB
324 lines
9.9 KiB
package service |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
"time" |
|
|
|
"go-common/app/interface/main/dm2/conf" |
|
"go-common/app/interface/main/dm2/dao" |
|
"go-common/app/interface/main/dm2/model" |
|
"go-common/app/interface/main/dm2/model/oplog" |
|
accountCli "go-common/app/service/main/account/api" |
|
arcCli "go-common/app/service/main/archive/api/gorpc" |
|
assMdl "go-common/app/service/main/assist/model/assist" |
|
assCli "go-common/app/service/main/assist/rpc/client" |
|
coinCli "go-common/app/service/main/coin/api/gorpc" |
|
figureCli "go-common/app/service/main/figure/rpc/client" |
|
filterCli "go-common/app/service/main/filter/api/grpc/v1" |
|
locCli "go-common/app/service/main/location/rpc/client" |
|
memberCli "go-common/app/service/main/member/api/gorpc" |
|
relCli "go-common/app/service/main/relation/rpc/client" |
|
seqMdl "go-common/app/service/main/seq-server/model" |
|
seqCli "go-common/app/service/main/seq-server/rpc/client" |
|
spyCli "go-common/app/service/main/spy/rpc/client" |
|
thumbupApi "go-common/app/service/main/thumbup/api" |
|
|
|
ugcPayCli "go-common/app/service/main/ugcpay/api/grpc/v1" |
|
seasonCli "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
"go-common/library/log/infoc" |
|
"go-common/library/sync/pipeline/fanout" |
|
"go-common/library/xstr" |
|
|
|
"golang.org/x/sync/singleflight" |
|
) |
|
|
|
func keySubject(tp int32, oid int64) string { |
|
return fmt.Sprintf("sub_local_%d_%d", tp, oid) |
|
} |
|
|
|
func keyXML(tp int32, oid int64) string { |
|
return fmt.Sprintf("%d_%d", tp, oid) |
|
} |
|
|
|
func keySeg(tp int32, oid, cnt, num int64) string { |
|
return fmt.Sprintf("%d_%d_%d_%d", tp, oid, cnt, num) |
|
} |
|
|
|
func keyDuration(tp int32, oid int64) string { |
|
return fmt.Sprintf("d_%d_%d", tp, oid) |
|
} |
|
|
|
type broadcast struct { |
|
Aid int64 |
|
Rnd int64 |
|
*model.DM |
|
} |
|
|
|
// Service dm2 service |
|
type Service struct { |
|
conf *conf.Config |
|
dao *dao.Dao |
|
arcRPC *arcCli.Service2 |
|
accountRPC accountCli.AccountClient |
|
assRPC *assCli.Service |
|
coinRPC *coinCli.Service |
|
relRPC *relCli.Service |
|
thumbupRPC thumbupApi.ThumbupClient |
|
locationRPC *locCli.Service |
|
cache *fanout.Fanout |
|
realname map[int64]int64 // key:分区id,value:cid,即该分区中大于cid的视频开启实名制 |
|
singleGroup singleflight.Group |
|
arcTypes map[int16]int16 |
|
broadcastChan chan *broadcast |
|
// seq serer |
|
seqDmArg *seqMdl.ArgBusiness |
|
seqSubtitleArg *seqMdl.ArgBusiness |
|
seqRPC *seqCli.Service2 |
|
// dm xml and dm seg local cache |
|
localCache map[string][]byte |
|
assistLogChan chan *assMdl.ArgAssistLogAdd |
|
// send operation log with infoc2 |
|
dmOperationLogSvc *infoc.Infoc |
|
opsLogCh chan *oplog.Infoc |
|
// dm monitor merge proc |
|
moniOidMap map[int64]struct{} |
|
oidLock sync.Mutex |
|
// subtitle singleFlight |
|
subtitleSingleGroup singleflight.Group |
|
subtitleLans model.SubtitleLans |
|
// block |
|
memberRPC *memberCli.Service |
|
// filter |
|
filterRPC filterCli.FilterClient |
|
//figure |
|
figureRPC *figureCli.Service |
|
// ugc pay |
|
ugcPayRPC ugcPayCli.UGCPayClient |
|
// spy RPC |
|
spyRPC *spyCli.Service |
|
// season |
|
seasonRPC seasonCli.SeasonClient |
|
// garbageDanmu |
|
garbageDanmu bool |
|
// broadcast limit |
|
broadcastLimit int |
|
broadcastlimitInterval int |
|
// view localcache |
|
localViewCache map[string]*model.ViewDm |
|
// bnj shield |
|
aidSheild map[int64]struct{} |
|
midsSheild map[int64]struct{} |
|
} |
|
|
|
// New return a service instance. |
|
func New(c *conf.Config) (s *Service) { |
|
s = &Service{ |
|
conf: c, |
|
dao: dao.New(c), |
|
arcRPC: arcCli.New2(c.ArchiveRPC), |
|
assRPC: assCli.New(c.AssistRPC), |
|
coinRPC: coinCli.New(c.CoinRPC), |
|
relRPC: relCli.New(c.RelationRPC), |
|
locationRPC: locCli.New(c.LocationRPC), |
|
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)), |
|
realname: make(map[int64]int64), |
|
arcTypes: make(map[int16]int16), |
|
broadcastChan: make(chan *broadcast, 1024), |
|
localCache: make(map[string][]byte), |
|
seqDmArg: &seqMdl.ArgBusiness{BusinessID: c.Seq.DM.BusinessID, Token: c.Seq.DM.Token}, |
|
seqSubtitleArg: &seqMdl.ArgBusiness{BusinessID: c.Seq.Subtitle.BusinessID, Token: c.Seq.Subtitle.Token}, |
|
seqRPC: seqCli.New2(c.SeqRPC), |
|
assistLogChan: make(chan *assMdl.ArgAssistLogAdd, 1024), |
|
dmOperationLogSvc: infoc.New(c.Infoc2), |
|
opsLogCh: make(chan *oplog.Infoc, 1024), |
|
moniOidMap: make(map[int64]struct{}), |
|
memberRPC: memberCli.New(c.MemberRPC), |
|
figureRPC: figureCli.New(c.FigureRPC), |
|
spyRPC: spyCli.New(c.SpyRPC), |
|
garbageDanmu: c.Switch.GarbageDanmu, |
|
broadcastLimit: c.BroadcastLimit.Limit, |
|
broadcastlimitInterval: c.BroadcastLimit.Interval, |
|
localViewCache: make(map[string]*model.ViewDm), |
|
aidSheild: make(map[int64]struct{}), |
|
midsSheild: make(map[int64]struct{}), |
|
} |
|
for idStr, cid := range s.conf.Realname.Threshold { |
|
ids, err := xstr.SplitInts(idStr) |
|
if err != nil { |
|
panic(err) |
|
} |
|
for _, id := range ids { |
|
if _, ok := s.realname[id]; !ok { |
|
s.realname[id] = cid |
|
} |
|
} |
|
} |
|
accountRPC, err := accountCli.NewClient(c.AccountRPC) |
|
if err != nil { |
|
panic(err) |
|
} |
|
s.accountRPC = accountRPC |
|
ugcPayRPC, err := ugcPayCli.NewClient(c.UgcPayRPC) |
|
if err != nil { |
|
panic(err) |
|
} |
|
s.ugcPayRPC = ugcPayRPC |
|
filterRPC, err := filterCli.NewClient(s.conf.FilterRPC) |
|
if err != nil { |
|
panic(err) |
|
} |
|
s.filterRPC = filterRPC |
|
seasonRPC, err := seasonCli.NewClient(c.SeasonRPC) |
|
if err != nil { |
|
panic(fmt.Sprintf("seasonCli.NewClient.error(%v)", err)) |
|
} |
|
s.seasonRPC = seasonRPC |
|
thumbupRPC, err := thumbupApi.NewClient(c.ThumbupRPC) |
|
if err != nil { |
|
panic(err) |
|
} |
|
s.thumbupRPC = thumbupRPC |
|
subtitleLans, err := s.dao.SubtitleLans(context.Background()) |
|
if err != nil { |
|
panic(err) |
|
} |
|
s.subtitleLans = model.SubtitleLans(subtitleLans) |
|
go s.broadcastproc() |
|
go s.archiveTypeproc() |
|
go s.localcacheproc() |
|
go s.assistLogproc() |
|
go s.oplogproc() |
|
go s.monitorproc() |
|
go s.viewProc() |
|
go s.shieldProc() |
|
return |
|
} |
|
|
|
// Ping service ping. |
|
func (s *Service) Ping(c context.Context) (err error) { |
|
return s.dao.Ping(c) |
|
} |
|
|
|
func (s *Service) archiveTypeproc() { |
|
var mu sync.Mutex |
|
for { |
|
rmap, err := s.dao.TypeMapping(context.TODO()) |
|
if err != nil { |
|
log.Error("load archive types error(%v)", err) |
|
} else { |
|
mu.Lock() |
|
s.arcTypes = rmap |
|
mu.Unlock() |
|
} |
|
time.Sleep(5 * time.Minute) |
|
} |
|
} |
|
|
|
func (s *Service) broadcastproc() { |
|
broadcastFmt := `["%.2f,%d,%d,%d,%d,%d,%d,%s,%d","%s"]` |
|
for m := range s.broadcastChan { |
|
if m.Pool == model.PoolSpecial { |
|
continue |
|
} |
|
if m.State != model.StateNormal && m.State != model.StateMonitorAfter { |
|
continue |
|
} |
|
if err := s.dao.BroadcastLimit(context.TODO(), m.Oid, m.Type, s.broadcastLimit, s.broadcastlimitInterval); err != nil { |
|
if err != ecode.LimitExceed { |
|
log.Error("dao.BroadcastLimit(oid:%v) error(%v)", m.Oid, err) |
|
} |
|
continue |
|
} |
|
hash := model.Hash(m.Mid, uint32(m.Content.IP)) |
|
msg := strings.Replace(m.Content.Msg, `\`, `\\`, -1) |
|
msg = strings.Replace(msg, `"`, `\"`, -1) |
|
info := fmt.Sprintf(broadcastFmt, float32(m.Progress)/1000.0, m.Content.Mode, m.Content.FontSize, |
|
m.Content.Color, m.Ctime, m.Rnd, m.Pool, hash, m.ID, msg) |
|
if err := s.dao.BroadcastInGoim(context.TODO(), m.Oid, m.Aid, []byte(info)); err != nil { |
|
log.Error("dao.BroadcastInGoim(cid:%d,aid:%d,info:%s) error(%v)", m.Oid, m.Aid, info, err) |
|
} else { |
|
log.Info("BroadcastInGoim(%s) succeed", info) |
|
} |
|
if err := s.dao.Broadcast(context.Background(), m.Oid, m.Aid, info); err != nil { |
|
log.Error("dao.Broadcast(cid:%d,aid:%d,info:%s) error(%v)", m.Oid, m.Aid, info, err) |
|
} else { |
|
log.Info("broadcast(%s) succeed", info) |
|
} |
|
} |
|
} |
|
|
|
func (s *Service) localcacheproc() { |
|
for { |
|
s.loadLocalcache(s.conf.Localcache.Oids) |
|
time.Sleep(time.Duration(s.conf.Localcache.Expire)) |
|
} |
|
} |
|
|
|
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 { |
|
if dmid > 0 { |
|
s.dmOperationLogSvc.Info(opLog.Subject, strconv.FormatInt(opLog.Oid, 10), strconv.Itoa(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) |
|
} else { |
|
log.Warn("oplogproc() it is an illegal log, for dmid value, warn(%d, %+v)", dmid, opLog) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// OpLog put a new infoc format operation log into the channel |
|
func (s *Service) OpLog(c context.Context, cid, operator, OperationTime int64, typ int, 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(OperationTime, 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 |
|
} |
|
|
|
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) |
|
} |
|
} |
|
} |
|
}
|
|
|