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,81 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"extra_func_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/videoup/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"databus.go",
"delay.go",
"extra_func.go",
"history.go",
"result.go",
"retry.go",
"round.go",
"send.go",
"service.go",
"shot.go",
"stat.go",
"video.go",
],
importpath = "go-common/app/job/main/videoup/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/videoup/conf:go_default_library",
"//app/job/main/videoup/dao/activity:go_default_library",
"//app/job/main/videoup/dao/archive:go_default_library",
"//app/job/main/videoup/dao/bvc:go_default_library",
"//app/job/main/videoup/dao/manager:go_default_library",
"//app/job/main/videoup/dao/message:go_default_library",
"//app/job/main/videoup/dao/monitor:go_default_library",
"//app/job/main/videoup/dao/redis:go_default_library",
"//app/job/main/videoup/model/archive:go_default_library",
"//app/job/main/videoup/model/manager:go_default_library",
"//app/job/main/videoup/model/message:go_default_library",
"//app/job/main/videoup/model/redis:go_default_library",
"//app/service/main/account/api:go_default_library",
"//library/conf/env:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/stat/prom:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,446 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
xsql "go-common/library/database/sql"
"go-common/library/log"
"go-common/library/queue/databus"
)
// videoupConsumer is videoup message consumer.
func (s *Service) videoupConsumer() {
defer s.wg.Done()
var (
msgs = s.videoupSub.Messages()
err error
c = context.TODO()
)
for {
func() {
var (
msg *databus.Message
ok bool
offset int64
dbus *archive.Databus
)
msg, ok = <-msgs
if !ok {
for part, lastOffset := range s.videoupSubIdempotent {
if _, err = s.arc.UpDBus(c, s.c.VideoupSub.Group, s.c.VideoupSub.Topic, part, lastOffset); err != nil {
continue
}
}
log.Error("s.videoupSub.Messages closed")
return
}
defer s.Rescue(string(msg.Value))
msg.Commit()
s.videoupMo++
m := &message.Videoup{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", string(msg.Value), err)
return
}
offset, ok = s.videoupSubIdempotent[msg.Partition]
if !ok {
if dbus, err = s.arc.DBus(c, s.c.VideoupSub.Group, msg.Topic, msg.Partition); err != nil {
return
}
if dbus == nil {
if _, err = s.arc.AddDBus(c, s.c.VideoupSub.Group, msg.Topic, msg.Partition, msg.Offset); err != nil {
return
}
offset = msg.Offset
} else {
offset = dbus.Offset
}
}
// if last offset > current offset -> continue
if offset > msg.Offset {
log.Error("key(%s) value(%s) partition(%s) offset(%d) is too early", msg.Key, msg.Value, msg.Partition, msg.Offset)
return
}
s.videoupSubIdempotent[msg.Partition] = msg.Offset
s.promDatabus.Incr(m.Route)
log.Info("videoupMessage key(%s) value(%s) partition(%d) offset(%d) route(%s) commit start", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
switch m.Route {
case message.RouteSyncCid:
err = s.syncCid(c, m)
case message.RouteFirstRound:
err = s.firstRound(c, m)
case message.RouteUGCFirstRound:
err = s.firstRound(c, m)
case message.RouteSecondRound:
err = s.secondRound(c, m)
case message.RouteAddArchive:
err = s.addArchive(c, m)
case message.RouteModifyArchive:
err = s.modifyArchive(c, m)
case message.RouteDeleteArchive:
err = s.deleteArchive(c, m)
case message.RouteDeleteVideo:
err = s.deleteVideo(c, m)
case message.RouteModifyVideo:
log.Info("databus modifyVideo(%+v)", m)
default:
log.Warn("videoupConsumer unknown message route(%s)", m.Route)
}
if err == nil {
log.Info("videoupMessage key(%s) value(%s) partition(%d) offset(%d) end", msg.Key, msg.Value, msg.Partition, msg.Offset)
} else {
log.Error("videoupMessage key(%s) value(%s) partition(%d) offset(%d) error(%v)", msg.Key, msg.Value, msg.Partition, msg.Offset, err)
}
}()
}
}
func (s *Service) syncCid(c context.Context, m *message.Videoup) (err error) {
// make sure filename not exist in redis, otherwise videoup can not submit!!!
s.redis.DelFilename(c, m.Filename)
log.Info("filename(%s) del_filename from redis success", m.Filename)
return
}
func (s *Service) firstRound(c context.Context, m *message.Videoup) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if m.Xcode == 1 {
s.promVideoS.Incr("xcode_hd")
}
s.promVideoE.Incr("first_round")
if v, a, err = s.archiveVideoByAid(c, m.Filename, m.Aid); err != nil {
log.Error("s.archiveVideoByAid(%s, %d) error(%v)", m.Filename, m.Aid, err)
return
}
if a.State == archive.StateForbidUpDelete {
log.Warn("archive(%d) filename(%s) state(%d) is deleted", a.Aid, v.Filename, a.State)
return
}
// make sure filename not exist in redis, otherwise videoup can not submit!!!
s.redis.DelFilename(c, m.Filename)
log.Info("filename(%s) del_filename from redis success", m.Filename)
var (
tx *xsql.Tx
sChange, rChange bool
round int8
ad = archive.AuditParam{IsAudit: true}
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin first_round transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if sChange, err = s.tranArchive(c, tx, a, v, &ad); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) first_round tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if round, err = s.tranRound(c, tx, a); err != nil {
tx.Rollback()
log.Error("s.tranRound error(%v)", err)
return
}
rChange = round != a.Round
log.Info("archive(%d) firstRound upRound(%d)", a.Aid, a.Round)
a.Round = round
if sChange || rChange {
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
}
log.Info("archive(%d) filename(%s) first_round tranRound fininsh a_round(%d)", a.Aid, v.Filename, a.Round)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end first_round transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
s.sendPostFirstRound(c, message.RoutePostFirstRound, a.Aid, v.Filename, m.AdminChange)
if sChange {
if a.IsForbid() {
//todo fixbug 导致打回的再自动开放视频 16s
s.syncBVC(c, a)
s.sendAuditMsg(c, message.RouteFirstRoundForbid, a.Aid)
}
s.sendMsg(c, a, v)
if archive.NormalState(a.State) {
// add for open state after dispatch
s.sendAuditMsg(c, message.RouteAutoOpen, a.Aid)
if is, _ := s.IsUpperFirstPass(c, a.Mid, a.Aid); is {
go s.sendNewUpperMsg(c, a.Mid, a.Aid)
}
log.Info("firstRound aid(%d) pubbed. Do sync.", a.Aid)
}
}
if s.canDo(a.Mid) {
s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
}
return
}
func (s *Service) secondRound(c context.Context, m *message.Videoup) (err error) {
var (
a *archive.Archive
addit *archive.Addit
)
if a, err = s.arc.Archive(c, m.Aid); err != nil {
log.Error("s.arc.Archive(%d) error(%v)", m.Aid, err)
return
}
if addit, err = s.arc.Addit(c, m.Aid); err != nil {
//为了不影响主流程,这里只是记个错误日志
//!!!注意!!! addit可能是nil用之前需要判断
log.Error("s.arc.Addit(%d) error(%v)", m.Aid, err)
err = nil
}
if a == nil {
log.Warn("archive(%d) is not exist", m.Aid)
return
}
//check first admin audit
var had bool
if had, _ = s.redis.SetMonitorCache(c, a.Aid); had {
s.redis.DelMonitorCache(c, a.Aid)
s.promVideoE.Incr("second_round")
}
// start archive
var tx *xsql.Tx
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran error(%v)", err)
return
}
if err = s.tranSumDuration(c, tx, a); err != nil {
tx.Rollback()
log.Error("s.tranSumDuration error(%v)", err)
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error(%v)", err)
return
}
if a.State != archive.StateForbidFixed {
s.syncBVC(c, a)
}
if archive.NormalState(a.State) {
s.changeMission(c, a, m.MissionID)
} else {
s.unBindMission(c, a, m.MissionID)
}
if m.IsSendNotify {
s.sendMsg(c, a, nil)
}
//TODO 将其它发送系统通知的逻辑都放到sendMsg2Upper()里
go s.sendMsg2Upper(c, m, a, addit)
//回查阶段修改了稿件信息需要发送通知
if a.State >= archive.StateOpen && ((s.c.ChangeDebug && s.c.ChangeMid == a.Mid) || !s.c.ChangeDebug) && (m.ChangeTypeID || m.ChangeTitle || m.ChangeCopyright || m.ChangeCover) {
log.Info("archive(%d) secondRound sendChangeMsg(%+v)", a.Aid, m)
s.sendChangeMsg(c, a, nil, m)
}
if s.canDo(a.Mid) {
s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
}
return
}
// sendMsg2Upper 发送系统通知给UP主
func (s *Service) sendMsg2Upper(c context.Context, m *message.Videoup, a *archive.Archive, addit *archive.Addit) {
if archive.NormalState(a.State) && m.MissionID != 0 && addit != nil && addit.MissionID == 0 { //m.MissionID是审核人员提交修改之前的活动id
s.sendMissionMsg(c, a)
}
is, err := s.IsUpperFirstPass(c, a.Mid, a.Aid)
if err != nil {
log.Error("s.IsUpperFirstPass(%d,%d) error(%v)", a.Mid, a.Aid, err)
err = nil
} else if is {
// UP主第一次稿件过审发送
s.sendNewUpperMsg(c, a.Mid, a.Aid)
}
}
func (s *Service) addArchive(c context.Context, m *message.Videoup) (err error) {
var a *archive.Archive
if a, err = s.arc.Archive(c, m.Aid); err != nil {
log.Error("s.arc.Archive(%d) error(%v)", m.Aid, err)
return
}
if a == nil {
log.Warn("archive(%d) is not exist", m.Aid)
return
}
if s.canDo(a.Mid) {
s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
}
var (
tx *xsql.Tx
round int8
state, _, _, _ = s.archiveState(c, a, nil, nil)
)
if archive.NormalState(state) { // NOTE: add archive must audit maybe code mode.
state = archive.StateForbidWait
}
if state == a.State {
log.Warn("archive(%d) add_archive newState(%d)==oldState(%d)", a.Aid, state, a.State)
return
}
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) error(%v)", a.Aid, err)
return
}
log.Info("archive(%d) begin add_archive transcation a_state(%d)", a.Aid, a.State)
// archive
if _, err = s.arc.TxUpState(tx, a.Aid, state); err != nil {
tx.Rollback()
log.Error("s.arc.TxUpState(%d, %d) error(%v)", a.Aid, state, err)
return
}
a.State = state
log.Info("archive(%d) add_archive upState(%d)", a.Aid, a.State)
if round, err = s.tranRound(c, tx, a); err != nil {
tx.Rollback()
log.Error("s.tranRound error(%v)", err)
return
}
a.Round = round
log.Info("archive(%d) add_archive upRound(%d)", a.Aid, a.Round)
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d) error(%v)", a.Aid, err)
return
}
log.Info("archive(%d) end add_archive transcation a_state(%d)", a.Aid, a.State)
return
}
func (s *Service) modifyArchive(c context.Context, m *message.Videoup) (err error) {
var a *archive.Archive
if a, err = s.arc.Archive(c, m.Aid); err != nil {
log.Error("s.arc.Archive(%d) error(%v)", m.Aid, err)
return
}
// start archive
if a.NotAllowUp() {
log.Warn("archive(%d) modify_archive state(%d) not allow update", a.Aid, a.State)
return
}
if s.canDo(a.Mid) {
s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
}
// TODO comment when online
if !m.EditArchive && !m.EditVideo {
// to sync pubbed arc if not edited or just edit order.
if archive.NormalState(a.State) {
s.sendAuditMsg(c, message.RouteForceSync, a.Aid)
log.Info("modifyArchive aid(%d) not modified or changed order. Do sync.", a.Aid)
}
return
}
var (
tx *xsql.Tx
state, _, _, _ = s.archiveState(c, a, nil, nil)
round int8
)
if archive.NormalState(state) || state == archive.StateForbidUserDelay {
if state != archive.StateForbidFixed {
state = archive.StateForbidWait
}
}
if state == a.State {
return
}
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) error(%v)", a.Aid, err)
return
}
log.Info("archive(%d) begin modify_archive transcation a_state(%d)", a.Aid, a.State)
// archive
if _, err = s.arc.TxUpState(tx, a.Aid, state); err != nil {
tx.Rollback()
log.Error("s.arc.TxUpState(%d, %d) error(%v)", a.Aid, state, err)
return
}
log.Info("archive(%d) modify_archive a.State(%d) upState(%d)", a.Aid, a.State, state)
a.State = state
if round, err = s.tranRound(c, tx, a); err != nil {
tx.Rollback()
return
}
a.Round = round
log.Info("archive(%d) modify_archive upRound(%d)", a.Aid, a.Round)
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d) error(%v)", a.Aid, err)
return
}
log.Info("archive(%d) end modify_archive transcation a_state(%d)", a.Aid, a.State)
if a.State == archive.StateForbidFixed {
s.addClickToRedis(c, a.Aid)
}
return
}
func (s *Service) deleteArchive(c context.Context, m *message.Videoup) (err error) {
var a *archive.Archive
if a, err = s.arc.Archive(c, m.Aid); err != nil {
log.Error("s.arc.Archive(%d) error(%v)", m.Aid, err)
return
}
log.Info("archive delete_archive aid(%d)", m.Aid)
s.syncBVC(c, a)
return
}
func (s *Service) deleteVideo(c context.Context, m *message.Videoup) (err error) {
var (
v *archive.Video
tx *xsql.Tx
sum int64
)
if v, err = s.arc.NewVideo(c, m.Filename); err != nil || v == nil {
return
}
log.Info("archive delete_video filename(%s) cid(%d) begin", m.Filename, v.Cid)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran error(%v)", err)
return
}
if sum, err = s.arc.NewSumDuration(c, v.Aid); err != nil {
tx.Rollback()
log.Error("s.arc.SumDuration(%d) filename(%s) error(%v)", v.Aid, v.Filename, err)
return
}
if _, err = s.arc.TxUpArcDuration(tx, v.Aid, sum); err != nil {
tx.Rollback()
log.Error("s.arc.TxUpArcDuration(%d, %d) filename(%s) error(%v)", v.Aid, sum, v.Filename, err)
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error(%v)", err)
return
}
log.Info("archive(%d) filename(%s) upArcDuration(%d)", v.Aid, v.Filename, sum)
log.Info("archive delete_video filename(%s) cid(%d) end", m.Filename, v.Cid)
return
}

View File

@@ -0,0 +1,88 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
"go-common/library/log"
"strconv"
"time"
)
// sendBblog 发送粉丝动态databus
func (s *Service) sendBblog(a *archive.Result) {
var (
err error
c = context.TODO()
noPush = int64(a.AttrVal(archive.AttrNoPushBplus))
//默认不展示
show = int64(0)
staffs []*archive.Staff
dataPoi, dataVote, dataVoteFix []byte
)
if noPush == 0 && a.IsNormal() {
show = int64(1)
}
if noPush == 1 {
show = int64(2)
}
msg := &message.BlogCardMsg{
Card: &archive.BlogCard{
Type: 8,
Rid: a.Aid,
OwnerID: a.Mid,
Show: show,
Ts: time.Now().Unix(),
Dynamic: a.Dynamic,
},
}
//lbs
if dataPoi, err = s.arc.POI(context.TODO(), a.Aid); err != nil {
log.Error("aid(%s) s.videoupPub.SendBblog(%v) POI error(%v)", a.Aid, msg, err)
}
//vote
if dataVote, err = s.arc.Vote(context.TODO(), a.Aid); err != nil {
log.Error("aid(%s) s.videoupPub.SendBblog(%v) Vote error(%v)", a.Aid, msg, err)
}
if dataPoi != nil || dataVote != nil {
if dataVote != nil {
var old *archive.VoteOld
if err = json.Unmarshal(dataVote, &old); err != nil {
log.Error("aid(%s) s.videoupPub.SendBblog(%+v) Vote old Unmarshal error(%v)", a.Aid, string(dataVote), err)
}
if dataVoteFix, err = json.Marshal(&archive.Vote{VoteID: old.VoteID, VoteTitle: old.VoteTitle}); err != nil {
log.Error("aid(%s) s.videoupPub.SendBblog(%+v) Vote new Marshal error(%v)", a.Aid, old, err)
}
}
var ext []byte
if ext, err = json.Marshal(&archive.Ext{LBS: string(dataPoi), Vote: string(dataVoteFix)}); err != nil {
log.Error("aid(%s) s.videoupPub.SendBblog(%+v) Unmarshal error(%v)", a.Aid, msg, err)
}
msg.Card.Ext = string(ext)
}
//staffs
if a.AttrVal(archive.AttrBitSTAFF) == archive.AttrYes {
if staffs, err = s.arc.Staffs(c, a.Aid); err != nil {
log.Error("aid(%s) s.arc.Staffs (%+v) error(%v)", a.Aid, msg, err)
}
var staffBox []*archive.StaffItem
if staffs != nil && len(staffs) > 0 {
for _, v := range staffs {
item := &archive.StaffItem{Type: 1, UID: v.StaffMID}
staffBox = append(staffBox, item)
}
}
if staffBox != nil && len(staffBox) > 0 {
msg.Card.Staffs = staffBox
log.Info("aid(%d) SendBblog(%+v) staffs(%+v)", a.Aid, msg, msg.Card.Staffs)
}
}
log.Info("aid(%d) start to send SendBblog(%+v) poi(%v) vote(%v) to databus", a.Aid, msg, string(dataPoi), string(dataVoteFix))
k := strconv.FormatInt(a.Aid, 10)
if err = s.blogPub.Send(c, k, msg); err != nil {
s.syncRetry(c, a.Aid, a.Mid, redis.ActionForSendBblog, "", "")
log.Error("aid(%s) %s s.videoupPub.SendBblog(%v) error(%v)", k, msg, err)
}
}

View File

@@ -0,0 +1,132 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
)
func (s *Service) delayproc() {
defer s.wg.Done()
for {
if s.closed {
return
}
s.pubDelay()
time.Sleep(1 * time.Minute)
}
}
func (s *Service) pubDelay() {
var (
c = context.TODO()
delays []*archive.Delay
err error
now = time.Now()
)
if delays, err = s.arc.NowDelays(c, now); err != nil {
log.Error("s.arc.NowDelays err(%v)", err)
return
}
if len(delays) == 0 {
log.Info("%s nothing need delay pub", now.Format("2006-01-02 15:04:05"))
return
}
var delayIds = make([]int64, 0, len(delays))
for _, v := range delays {
log.Info("archive(%d) type(%d) begin delay pub", v.Aid, v.Type)
var (
a *archive.Archive
tx *sql.Tx
)
delayIds = append(delayIds, v.ID)
if a, err = s.arc.Archive(c, v.Aid); err != nil || a == nil {
log.Error("s.arc.Archive(%d) error(%v)", v.Aid, err)
continue
}
//定时发布稿件 到临界点必须 state=-40
if v.Type == archive.DelayTypeForUser && a.State != archive.StateForbidUserDelay {
log.Info("archive(%d) type(%d) state(%d) delay failed", v.Aid, v.Type, a.State)
now := now.Unix()
s.msg.Send(c, "1_7_9", "您的稿件定时发布失败", fmt.Sprintf(`您的稿件《%s》av%d未能成功发布。原因未能及时通过审核 您可以在稿件编辑页面重新编辑定时发布的时间,为此带来的麻烦,深感抱歉。#{点击进入编辑>>}{"http://member.bilibili.com/v/video/submit.html?type=edit&aid=%d"}`,
a.Title, a.Aid, a.Aid), a.Mid, now)
s.msg.Send(c, "113_1_1", "您的视频定时发布失败", fmt.Sprintf(`您的视频《%s》av%d定时发布失败。原因未能及时通过审核。您可在稿件编辑页重新编辑定时发布时间。为此带来的麻烦深感抱歉。#{点击进行编辑>>}{"https://member.bilibili.com/v/video/submit.html?type=edit&aid=%d"}`,
a.Title, a.Aid, a.Aid), a.Mid, now)
continue
}
if v.Type == archive.DelayTypeForUser && a.State == archive.StateForbidUserDelay {
a.State = archive.StateOpen
var addit *archive.Addit
if addit, err = s.arc.Addit(c, a.Aid); err != nil && addit != nil && addit.MissionID > 0 {
a.State = archive.StateOpen
} else if !s.isWhite(a.Mid) && !s.isBlack(a.Mid) {
if pfl, _ := s.profile(c, a.Mid); pfl != nil && pfl.Follower < int64(s.fansCache) && s.isRoundType(a.TypeID) {
a.State = archive.StateOrange // NOTE: auto open must
}
}
} else {
log.Info("archive(%d) type(%d) state(%d) delay failed", v.Aid, v.Type, v.State)
continue
}
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran error(%v)", err)
continue
}
firstPass := false
if firstPass, err = s.txUpArcState(c, tx, a.Aid, a.State); err != nil {
tx.Rollback()
continue
}
log.Info("archive(%d) type(%d) delay upState(%d)", v.Aid, v.Type, a.State)
if firstPass {
a.PTime = xtime.Time(now.Unix())
if _, err = s.arc.TxUpPTime(tx, a.Aid, now); err != nil {
tx.Rollback()
continue
}
log.Info("archive(%d) type(%d) delay upPTime(%d)", v.Aid, v.Type, a.PTime)
}
if archive.NormalState(a.State) {
var round int8
if round, err = s.tranRound(c, tx, a); err != nil {
tx.Rollback()
continue
}
a.Round = round
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
continue
}
} else {
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
}
if err = tx.Commit(); err != nil {
log.Error("tx.commit error(%v)", err)
continue
}
if archive.NormalState(a.State) {
s.syncBVC(c, a)
s.changeMission(c, a, 0)
now := now.Unix()
s.msg.Send(c, "1_7_10", "您的稿件已经发布", fmt.Sprintf(`您的稿件《%s》av%d已经发布成功#{点击查看>>}{"http://www.bilibili.com/video/av%d/"} `,
a.Title, a.Aid, a.Aid), a.Mid, now)
s.msg.Send(c, "113_1_1", "您的视频定时发布成功", fmt.Sprintf(`您的视频《%s》av%d已经发布成功#{点击查看>>}{"http://www.bilibili.com/video/av%d/"} `,
a.Title, a.Aid, a.Aid), a.Mid, now)
s.sendAuditMsg(c, message.RouteDelayOpen, a.Aid)
if is, _ := s.IsUpperFirstPass(c, a.Mid, a.Aid); is {
go s.sendNewUpperMsg(c, a.Mid, a.Aid)
}
}
log.Info("archive(%d) type(%d) end delay pub", v.Aid, v.Type)
}
s.arc.DelDelayByIds(c, delayIds)
}

View File

@@ -0,0 +1,727 @@
package service
import (
"context"
"fmt"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
accApi "go-common/app/service/main/account/api"
"go-common/library/conf/env"
"go-common/library/database/sql"
"go-common/library/log"
xtime "go-common/library/time"
"strings"
)
func (s *Service) archiveVideo(c context.Context, filename string) (v *archive.Video, a *archive.Archive, err error) {
if v, err = s.arc.NewVideo(c, filename); err != nil {
log.Error("s.arc.Video(%s) error(%v)", filename, err)
return
}
if v == nil {
log.Error("s.arc.Video(%s) video is nil", filename)
err = fmt.Errorf("video(%s) is not exists", filename)
return
}
if a, err = s.arc.Archive(c, v.Aid); err != nil {
log.Error("s.arc.Archive(%d) filename(%s) error(%v)", v.Aid, filename, err)
return
}
if a == nil {
log.Error("s.arc.Archive(%s) archive(%d) is nil", filename, v.Aid)
err = fmt.Errorf("archive(%d) filename(%s) is not exists", v.Aid, filename)
}
return
}
func (s *Service) archiveVideoByAid(c context.Context, filename string, aid int64) (v *archive.Video, a *archive.Archive, err error) {
if v, err = s.arc.NewVideoByAid(c, filename, aid); err != nil {
log.Error("s.arc.Video(%s) error(%v)", filename, err)
return
}
if v == nil {
log.Error("s.arc.Video(%s) video is nil", filename)
err = fmt.Errorf("video(%s) is not exists", filename)
return
}
if a, err = s.arc.Archive(c, aid); err != nil {
log.Error("s.arc.Archive(%d) filename(%s) error(%v)", aid, filename, err)
return
}
if a == nil {
log.Error("s.arc.Archive(%s) archive(%d) is nil", filename, aid)
err = fmt.Errorf("archive(%d) filename(%s) is not exists", aid, filename)
}
return
}
func (s *Service) isPorder(a *archive.Archive) bool {
if a == nil {
return false
}
return a.AttrVal(archive.AttrBitIsPorder) == archive.AttrYes
}
func (s *Service) isUGCPay(a *archive.Archive) bool {
if a == nil {
return false
}
return a.AttrVal(archive.AttrBitUGCPay) == archive.AttrYes
}
func (s *Service) isStaff(a *archive.Archive) bool {
if a == nil {
return false
}
return a.AttrVal(archive.AttrBitSTAFF) == archive.AttrYes
}
func (s *Service) canDo(mid int64) bool {
return !s.c.Debug || (s.c.DebugMid == mid && mid > 0)
}
func (s *Service) archiveState(c context.Context, a *archive.Archive, v *archive.Video, ad *archive.AuditParam) (state int8, access int16, attr archive.Attr, forbidAttr *archive.ForbidAttr) {
// videos
var (
vs []*archive.Video
err error
)
if vs, err = s.arc.NewVideos(c, a.Aid); err != nil {
log.Error("s.arc.Videos(%d) error(%v)", a.Aid, err)
return
}
if len(vs) == 0 {
state = archive.StateForbidWait
log.Warn("archive(%d) have no videos", a.Aid)
return
}
var (
newState int8
newAccess int16
)
forbidAttr, _ = s.arc.Forbid(c, a.Aid)
//聚合状态和属性
for _, tv := range vs {
if v != nil && tv.Filename == v.Filename {
tv = v // NOTE: v maybe change by tran begin, so use v.
}
if tv.Status == archive.VideoStatusDelete {
continue
}
if tv.Status == archive.VideoStatusLock {
newState = archive.StateForbidLock
break
}
if tv.Status == archive.VideoStatusRecicle || newState == archive.StateForbidRecicle {
newState = archive.StateForbidRecicle
continue
}
if tv.Status == archive.VideoStatusXcodeFail || newState == archive.StateForbidXcodeFail {
newState = archive.StateForbidXcodeFail
continue
}
if tv.Status == archive.VideoStatusSubmit || newState == archive.StateForbidSubmit {
newState = archive.StateForbidSubmit
continue
}
if tv.Status == archive.VideoStatusWait || newState == archive.StateForbidSubmit {
newState = archive.StateForbidSubmit
continue
}
if tv.XcodeState <= archive.VideoXcodeHDFinish || newState == archive.StateForbidWait {
newState = archive.StateForbidSubmit
continue
}
if tv.XcodeState <= archive.VideoDispatchRunning || newState == archive.StateForbidSubmit {
newState = archive.StateForbidSubmit
continue
}
if tv.Status == archive.VideoStatusAccess || newAccess == archive.AccessMember {
newState = archive.StateOpen
newAccess = archive.AccessMember
} else if tv.Status == archive.VideoStatusOpen {
newState = archive.StateOpen
}
// attr
if tv.AttrVal(archive.AttrBitNoRank) == archive.AttrYes || a.AttrVal(archive.AttrBitNoRank) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitNoRank)
forbidAttr.SetAttr(archive.ForbidRank, archive.AttrYes, archive.ForbidRankMain)
}
if tv.AttrVal(archive.AttrBitNoDynamic) == archive.AttrYes || a.AttrVal(archive.AttrBitNoDynamic) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitNoDynamic)
forbidAttr.SetAttr(archive.ForbidDynamic, archive.AttrYes, archive.ForbidDynamicMain)
}
if tv.AttrVal(archive.AttrBitOverseaLock) == archive.AttrYes || a.AttrVal(archive.AttrBitOverseaLock) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitOverseaLock)
forbidAttr.SetAttr(archive.ForbidShow, archive.AttrYes, archive.ForbidShowOversea)
}
if tv.AttrVal(archive.AttrBitNoRecommend) == archive.AttrYes || a.AttrVal(archive.AttrBitNoRecommend) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitNoRecommend)
forbidAttr.SetAttr(archive.ForbidRecommend, archive.AttrYes, archive.ForbidRecommendMain)
}
if tv.AttrVal(archive.AttrBitNoSearch) == archive.AttrYes || a.AttrVal(archive.AttrBitNoSearch) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitNoSearch)
// NOTE: not search forbit
}
if tv.AttrVal(archive.AttrNoPushBplus) == archive.AttrYes || a.AttrVal(archive.AttrNoPushBplus) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrNoPushBplus)
}
if tv.AttrVal(archive.AttrBitParentMode) == archive.AttrYes || a.AttrVal(archive.AttrBitParentMode) == archive.AttrYes {
attr.Set(archive.AttrYes, archive.AttrBitParentMode)
}
}
if newState == archive.StateOpen {
if s.hadPassed(c, a.Aid) {
newState = archive.StateForbidFixed
if ad != nil && ad.IsAudit {
newState = archive.StateOpen
}
} else if s.isAuditType(a.TypeID) { // 多P或指定分区
newState = archive.StateForbidWait
} else if addit, _ := s.arc.Addit(c, a.Aid); addit != nil && (addit.OrderID > 0 || addit.UpFrom == archive.UpFromPGC || addit.UpFrom == archive.UpFromPGCSecret || addit.MissionID > 0) { // NOTE: order || up_from pgc || mission
newState = archive.StateForbidWait
} else if hisCnt, _ := s.arc.HistoryCount(c, a.Aid); hisCnt > 1 { // modified before dispatch finish
newState = archive.StateForbidWait
} else if delay, err := s.arc.Delay(c, a.Aid); err == nil && delay != nil {
if delay.DTime.Before(time.Now()) {
newState = archive.StateForbidWait
} else if s.isPorder(a) && !s.isAuditType(a.TypeID) { //私单+定时+非特殊分区->newState = -1
newState = archive.StateForbidWait
} else if delay.Type == archive.DelayTypeForUser {
newState = archive.StateForbidUserDelay
}
} else if s.isPorder(a) {
newState = archive.StateForbidWait
} else if s.isUGCPay(a) {
//付费稿件在付费待审完成后job才可以自动开放
newState = archive.StateForbidWait
}
if newState == archive.StateOpen && !s.isWhite(a.Mid) && !s.isBlack(a.Mid) {
if pfl, _ := s.profile(c, a.Mid); pfl != nil && pfl.Follower < int64(s.fansCache) && s.isRoundType(a.TypeID) && !s.isMission(c, a.Aid) {
newState = archive.StateOrange // NOTE: auto open must
}
}
}
state = newState
access = newAccess
return
}
func (s *Service) archiveRound(c context.Context, a *archive.Archive) (round int8) {
if archive.NormalState(a.State) {
//定时发布 或者 自动过审逻辑
isAuditType := s.isAuditType(a.TypeID)
if addit, _ := s.arc.Addit(c, a.Aid); addit != nil && (addit.OrderID > 0 || addit.UpFrom == archive.UpFromPGC || addit.UpFrom == archive.UpFromCoopera ||
addit.MissionID > 0 || (s.isPorder(a) && isAuditType)) {
round = archive.RoundEnd // NOTE: maybe -40 -> 0 商单稿件||pgc稿件|| 合作方嵌套稿件 || 活动||私单稿件 进round = 99
} else if addit != nil && addit.UpFrom == archive.UpFromPGCSecret {
round = archive.RoundTriggerClick // Note: 机密PGC通过后进机密回查
} else if (a.Round == archive.RoundAuditThird || a.Round == archive.RoundAuditSecond) && isAuditType {
round = archive.RoundEnd // NOTE: 特殊分区二三审进round = 99
} else if s.isWhite(a.Mid) || s.isBlack(a.Mid) {
round = archive.RoundReviewSecond
} else if pfl, _ := s.profile(c, a.Mid); pfl != nil && pfl.Follower >= int64(s.fansCache) {
round = archive.RoundReviewSecond
} else if pfl != nil && pfl.Follower < int64(s.fansCache) && s.isRoundType(a.TypeID) {
round = archive.RoundReviewFirst // NOTE: if audit type, state must not open!!! so cannot execute here...
} else {
round = archive.RoundReviewFirstWaitTrigger
if pfl == nil {
log.Info("archive(%d) card(%d) is nil", a.Aid, a.Mid)
} else {
log.Info("archive(%d) card(%d) fans(%d) little than config(%d)", a.Aid, a.Mid, pfl.Follower, s.fansCache)
}
}
} else if a.State == archive.StateForbidWait {
if addit, _ := s.arc.Addit(c, a.Aid); addit != nil && (addit.OrderID > 0 || addit.UpFrom == archive.UpFromPGC || addit.UpFrom == archive.UpFromPGCSecret || addit.UpFrom == archive.UpFromCoopera || addit.MissionID > 0) {
//二审待审 商单、pgc,活动
round = archive.RoundAuditSecond
} else if s.isAuditType(a.TypeID) {
//指定分区待审逻辑
// 如果修改未过审 history >1 and not passed (用户修改未过审)走二审
hadPassed := s.hadPassed(c, a.Aid)
hasEdit, _ := s.arc.HistoryCount(c, a.Aid)
if !hadPassed && hasEdit > 1 {
round = archive.RoundAuditSecond
} else if count, _ := s.arc.NewVideoCount(c, a.Aid); count == 1 {
//如果 新增投稿 多p 进二审 单P 进三审
round = archive.RoundAuditThird
} else {
round = archive.RoundAuditSecond
}
} else {
hasEdit, _ := s.arc.HistoryCount(c, a.Aid)
if s.isPorder(a) && hasEdit <= 1 {
round = archive.RoundReviewFlow
} else if s.isUGCPay(a) {
//付费待审 s -1 r 24
round = archive.RoundAuditUGCPayFlow
} else {
round = archive.RoundAuditSecond
}
}
} else if a.State == archive.StateForbidFixed {
round = archive.RoundAuditSecond
} else if a.State == archive.StateForbidUserDelay {
round = a.Round
} else if a.State == archive.StateForbidLater {
round = a.Round
} else {
round = archive.RoundBegin
// NOTE: user delete?? admin later???
}
return
}
func (s *Service) tranRound(c context.Context, tx *sql.Tx, a *archive.Archive) (round int8, err error) {
round = s.archiveRound(c, a)
if _, err = s.arc.TxUpRound(tx, a.Aid, round); err != nil {
log.Error("s.arc.TxUpRound(%d, %d) error(%v)", a.Aid, round, err)
return
}
return
}
func (s *Service) tranArchiveOper(tx *sql.Tx, a *archive.Archive) (err error) {
if _, err = s.arc.TxArchiveOper(tx, a.Aid, a.TypeID, a.State, a.Round, a.Attribute, archive.FirstRoundID, ""); err != nil {
log.Error("s.arc.TxArchiveOper error(%v)", err)
}
return
}
func (s *Service) tranVideo(c context.Context, tx *sql.Tx, a *archive.Archive, v *archive.Video) (err error) {
//up xcode_state
if _, err = s.arc.TxUpXcodeState(tx, v.Filename, v.XcodeState); err != nil {
log.Error("s.arc.TxUpXcodeState(%s, %d) error(%v)", v.Filename, v.XcodeState, err)
return
}
if _, err = s.arc.TxUpVideoXState(tx, v.Filename, v.XcodeState); err != nil {
log.Error("s.arc.TxUpVideoXState(%s, %d) error(%v)", v.Filename, v.XcodeState, err)
return
}
log.Info("archive(%d) filename(%s) upXcodeState(%d)", a.Aid, v.Filename, v.XcodeState)
var stCh bool // NOTE: status changed
// check xcode state
if v.XcodeState == archive.VideoXcodeSDFail || v.XcodeState == archive.VideoXcodeHDFail {
if _, err = s.arc.TxUpFailCode(tx, v.Filename, v.FailCode); err != nil {
log.Error("s.arc.TxUpFailCode(%s, %d, %d) error(%v)", v.Filename, v.FailCode, err)
return
}
if _, err = s.arc.TxUpVideoFailCode(tx, v.Filename, v.FailCode); err != nil {
log.Error("s.arc.TxUpVideoFailCode(%s, %d, %d) error(%v)", v.Filename, v.FailCode, err)
return
}
log.Info("archive(%d) filename(%s) upFailCode(%d)", a.Aid, v.Filename, v.FailCode)
stCh = true
} else if v.XcodeState == archive.VideoXcodeSDFinish {
if _, err = s.arc.TxUpPlayurl(tx, v.Filename, v.Playurl); err != nil {
log.Error("s.arc.TxUpPlayurl(%s, %s) error(%v)", v.Filename, v.Playurl, err)
return
}
if _, err = s.arc.TxUpVideoPlayurl(tx, v.Filename, v.Playurl); err != nil {
log.Error("s.arc.TxUpVideoPlayurl(%s, %s) error(%v)", v.Filename, v.Playurl, err)
return
}
log.Info("archive(%d) filename(%s) upPlayurl(%s)", a.Aid, v.Filename, v.Playurl)
if _, err = s.arc.TxUpVideoDuration(tx, v.Filename, v.Duration); err != nil {
log.Error("s.arc.TxUpVideoDuration(%s, %d) error(%v)", v.Filename, v.Duration, err)
return
}
if _, err = s.arc.TxUpVDuration(tx, v.Filename, v.Duration); err != nil {
log.Error("s.arc.TxUpVDuration(%s, %d) error(%v)", v.Filename, v.Duration, err)
return
}
log.Info("archive(%d) filename(%s) upVdoDuration(%d)", a.Aid, v.Filename, v.Duration)
stCh = true
} else if v.XcodeState == archive.VideoXcodeHDFinish {
if _, err = s.arc.TxUpResolutions(tx, v.Filename, v.Resolutions); err != nil {
log.Error("s.arc.TxUpResolutions(%s, %s) error(%v)", v.Filename, v.Resolutions, err)
return
}
if _, err = s.arc.TxUpVideoResolutionsAndDimensions(tx, v.Filename, v.Resolutions, v.Dimensions); err != nil {
log.Error("s.arc.TxUpVideoResolutions(%s,%s, %s) error(%v)", v.Filename, v.Resolutions, v.Dimensions, err)
return
}
log.Info("archive(%d) filename(%s) upResolution(%s)", a.Aid, v.Filename, v.Resolutions)
if _, err = s.arc.TxUpVideoDuration(tx, v.Filename, v.Duration); err != nil {
log.Error("s.arc.TxUpVideoDuration(%s, %d) error(%v)", v.Filename, v.Duration, err)
return
}
if _, err = s.arc.TxUpVDuration(tx, v.Filename, v.Duration); err != nil {
log.Error("s.arc.TxUpVDuration(%s, %d) error(%v)", v.Filename, v.Duration, err)
return
}
log.Info("archive(%d) filename(%s) upVdoDuration(%d)", a.Aid, v.Filename, v.Duration)
if _, err = s.arc.TxUpFilesize(tx, v.Filename, v.Filesize); err != nil {
log.Error("s.arc.TxUpFilesize(%s, %d) error(%v)", v.Filename, v.Filesize, err)
return
}
if _, err = s.arc.TxUpVideoFilesize(tx, v.Filename, v.Filesize); err != nil {
log.Error("s.arc.TxUpVideoFilesize(%s, %d) error(%v)", v.Filename, v.Filesize, err)
return
}
log.Info("archive(%d) filename(%s) upFilesize(%d)", a.Aid, v.Filename, v.Filesize)
}
//else if v.XcodeState == archive.VideoDispatchRunning || v.XcodeState == archive.VideoDispatchFinish {
// // TODO ???
//}
if !stCh {
return
}
if _, err = s.arc.TxUpStatus(tx, v.Filename, v.Status); err != nil {
log.Error("s.arc.TxUpStatus(%s, %d) error(%v) or rows==0", v.Filename, v.Status, err)
return
}
if v.Status == archive.VideoStatusDelete {
if _, err = s.arc.TxUpRelationStatus(tx, v.Cid, archive.StateForbidUpDelete); err != nil {
log.Error("s.arc.TxUpRelationStatus(%d, %d) error(%v) or rows==0", v.Cid, archive.StateOpen, err)
return
}
} else {
if _, err = s.arc.TxUpVideoStatus(tx, v.Filename, v.Status); err != nil {
log.Error("s.arc.TxUpVideoStatus(%s, %d) error(%v) or rows==0", v.Filename, v.Status, err)
return
}
}
// NOTE: reset relation back to active for data consistent. -100 still -100.
if v.Cid > 0 && v.Status != archive.VideoStatusDelete {
if _, err = s.arc.TxUpRelationStatus(tx, v.Cid, archive.StateOpen); err != nil {
log.Error("s.arc.TxUpRelationStatus(%d, %d) error(%v) or rows==0", v.Cid, archive.StateOpen, err)
return
}
}
log.Info("archive(%d) filename(%s) upStatus(%d)", a.Aid, v.Filename, v.Status)
var reason string
if v.Status == archive.VideoStatusXcodeFail {
if v.XcodeState == archive.VideoXcodeSDFail {
reason = "转码失败:" + archive.XcodeFailMsgs[v.FailCode]
} else if v.XcodeState == archive.VideoXcodeHDFail {
reason = "转码失败:" + archive.XcodeFailMsgs[v.FailCode]
}
} else if v.Status == archive.VideoStatusOpen {
reason = "生产组稿件一转成功"
} else if v.Status == archive.VideoStatusWait {
reason = "一转成功"
}
if _, err = s.arc.TxAddAudit(tx, v.ID, a.Aid, reason); err != nil {
log.Error("s.arc.TxAddAudit(%d, %d) filename(%s) reason(%s) error(%v)", v.ID, a.Aid, v.Filename, reason, err)
return
}
log.Info("archive(%d) filename(%s) addAudit reason(%s)", a.Aid, v.Filename, reason)
return
}
func (s *Service) tranArchive(c context.Context, tx *sql.Tx, a *archive.Archive, v *archive.Video, ad *archive.AuditParam) (change bool, err error) {
// start archive
if a.NotAllowUp() {
log.Warn("archive(%d) filename(%s) state(%d) not allow update", a.Aid, v.Filename, a.State)
return
}
var (
state, access, attr, forbidAttr = s.archiveState(c, a, v, ad)
now = time.Now()
)
if state == a.State {
log.Warn("archive(%d) filename(%s) newState(%d)==oldState(%d)", a.Aid, v.Filename, state, a.State)
} else {
change = true
firstPass := false
// archive
if firstPass, err = s.txUpArcState(c, tx, a.Aid, state); err != nil {
log.Error("s.txUpArcState(%d, %d) filename(%s) error(%v)", a.Aid, state, v.Filename, err)
return
}
a.State = state
log.Info("archive(%d) filename(%s) upState(%d)", a.Aid, v.Filename, a.State)
if firstPass {
if _, err = s.arc.TxUpPTime(tx, a.Aid, now); err != nil {
log.Error("s.arc.TxUpPTime(%d, %d) error(%v)", a.Aid, now.Unix(), err)
return
}
a.PTime = xtime.Time(now.Unix())
log.Info("archive(%d) filename(%s) upPTime(%d)", a.Aid, v.Filename, a.PTime)
}
}
if a.Access != access {
if _, err = s.arc.TxUpAccess(tx, a.Aid, access); err != nil {
log.Error("s.arc.TxUpAccess(%d, %d) filename(%s) error(%v)", a.Aid, access, v.Filename, err)
return
}
a.Access = access
log.Info("archive(%d) filename(%s) upAccess(%d)", a.Aid, v.Filename, a.Access)
}
if err = s.tranSumDuration(c, tx, a); err != nil {
log.Info("s.tranSumDuration error(%v)", err)
return
}
if a.Attribute != (a.Attribute | int32(attr)) {
if _, err = s.arc.TxUpAttr(tx, a.Aid, attr); err != nil {
log.Error("s.arc.TxUpAttr(%d, %d) filename(%s) error(%v)", a.Aid, attr, v.Filename, err)
return
}
a.WithAttr(attr)
log.Info("archive(%d) filename(%s) upAttribute(%d)", a.Aid, v.Filename, a.Attribute)
}
if _, err = s.arc.TxUpForbid(tx, forbidAttr); err != nil {
log.Error("s.arc.TxUpForbid(%+v) error(%v)", forbidAttr, err)
return
}
log.Info("archive(%d) filename(%s) forbidAttr(%+v)", a.Aid, v.Filename, forbidAttr)
return
}
func (s *Service) tranSumDuration(c context.Context, tx *sql.Tx, a *archive.Archive) (err error) {
var sum int64
if sum, err = s.arc.NewSumDuration(c, a.Aid); err != nil {
log.Error("s.arc.SumDuration(%d) error(%v)", a.Aid, err)
err = nil
} else if sum > 0 && a.Duration != sum {
if _, err = s.arc.TxUpArcDuration(tx, a.Aid, sum); err != nil {
log.Error("s.arc.TxUpArcDuration(%d, %d) error(%v)", a.Aid, sum, err)
return
}
a.Duration = sum
log.Info("archive(%d) upArcDuration(%d)", a.Aid, a.Duration)
}
return
}
func (s *Service) tranArcCover(c context.Context, tx *sql.Tx, a *archive.Archive, v *archive.Video) (err error) {
if a.Cover != "" {
return
}
// NOTE: first round need view archive cover, delete when three covers select for user
var cvs []string
//从ai处获取封面,若失败/没有封面信息,则直接返回
if cvs, err = s.arc.AICover(c, v.Filename); err != nil || len(cvs) == 0 {
log.Error("a.arc.AICover(aid(%d) filename(%s)) got covers from AI error(%v), cvs(%v) ", a.Aid, v.Filename, err, cvs)
err = nil
return
}
log.Info("s.arc.AICover(aid(%d), filename(%s)) got covers from AI: cvs(%v)", a.Aid, v.Filename, cvs)
a.Cover = cvs[0] //ai cover只取第一个元素
if _, err = s.arc.TxUpCover(tx, a.Aid, strings.Replace(a.Cover, "https:", "", -1)); err != nil {
log.Error("s.arc.TxUpCover(%d, %s) filename(%s) error(%v)", a.Aid, v.Filename, a.Cover, err)
return
}
log.Info("archive(%d) filename(%s) upCover(%s)", a.Aid, v.Filename, a.Cover)
return
}
func (s *Service) hadPassed(c context.Context, aid int64) (had bool) {
id, err := s.arc.GetFirstPassByAID(c, aid)
if err != nil {
log.Error("hadPassed s.arc.GetFirstPassByAID error(%v) aid(%d)", err, aid)
return
}
had = id > 0
return
}
func (s *Service) profile(c context.Context, mid int64) (p *accApi.ProfileStatReply, err error) {
if p, err = s.accRPC.ProfileWithStat3(c, &accApi.MidReq{Mid: mid}); err != nil {
p = nil
log.Error("s.accRPC.ProfileWithStat3(%d) error(%v)", mid, err)
}
return
}
func (s *Service) changeMission(c context.Context, a *archive.Archive, missionID int64) (err error) {
if missionID > 0 {
s.activity.UpVideo(c, a, missionID)
return
}
var addit *archive.Addit
if addit, err = s.arc.Addit(c, a.Aid); err != nil {
log.Error("s.arc.Addit(%d) error(%v)", a.Aid, err)
return
}
if addit == nil {
return
}
if addit.MissionID > 0 {
s.activity.AddVideo(c, a, addit.MissionID)
}
return
}
func (s *Service) unBindMission(c context.Context, a *archive.Archive, missionID int64) (err error) {
if missionID == 0 {
var addit *archive.Addit
if addit, err = s.arc.Addit(c, a.Aid); err != nil {
log.Error("s.arc.Addit(%d) error(%v)", a.Aid, err)
return
}
if addit == nil || addit.MissionID == 0 {
return
}
missionID = addit.MissionID
}
s.activity.UpVideo(c, a, missionID)
return
}
func (s *Service) cidsByAid(c context.Context, aid int64) (cids []int64, err error) {
var (
vs []*archive.Video
)
if vs, err = s.arc.NewVideos(c, aid); err != nil {
log.Error("archive(%d) cidsByAid s.arc.Videos error(%v)", aid, err)
return
}
for _, v := range vs {
cids = append(cids, v.Cid)
}
return
}
func (s *Service) syncBVC(c context.Context, a *archive.Archive) (err error) {
if env.DeployEnv == env.DeployEnvFat1 || env.DeployEnv == env.DeployEnvDev {
log.Info("archive(%d) syncBVC stop for dev/fat1 env", a.Aid)
return
}
var cids []int64
if cids, err = s.cidsByAid(c, a.Aid); err != nil {
log.Error("archive(%d) second_round cidsByAid error(%v)", a.Aid, err)
s.syncRetry(c, a.Aid, a.Mid, redis.ActionForBvcCapable, "", "")
return
}
var retryCids, okCids, noCids []int64
for _, cid := range cids {
//pgc付费 单独播放通道
//ugc 付费 也是单独播放通道
//ugc 通道普通视频播放控制
if a.AttrVal(archive.AttrBitBadgepay) == archive.AttrYes || a.AttrVal(archive.AttrBitIsBangumi) == archive.AttrYes || a.AttrVal(archive.AttrBitUGCPay) == archive.AttrYes {
noCids = append(noCids, cid)
continue
}
var count int
// playable videos num
if count, err = s.arc.NewVideoCountCapable(c, cid); err != nil {
log.Error("archive(%d) syncBVC checkCids cid(%d) s.arc.VideoCountCapable error(%v)", a.Aid, cid, err)
retryCids = append(retryCids, cid)
continue
}
if count == 0 {
noCids = append(noCids, cid)
} else if count == 1 {
if !a.IsNormal() {
noCids = append(noCids, cid)
} else {
okCids = append(okCids, cid)
}
} else {
if a.IsForbid() {
aids, _ := s.arc.ValidAidByCid(c, cid)
fcnt := 0
for _, aid := range aids {
var arc *archive.Archive
if arc, err = s.arc.Archive(c, aid); err != nil {
log.Error("syncBVC get archive (%d) cid(%d) error(%v)", aid, cid, err)
break
}
if arc != nil && arc.State >= 0 {
fcnt++
break
}
}
log.Info("syncBVC ValidAidByCid cid(%d) aids(%v) fcnt(%d)", cid, aids, fcnt)
// num of cids used in other open state archive
if err != nil {
log.Error("checkCids cid(%d) error(%v)", cid, err)
retryCids = append(retryCids, cid)
continue
} else if fcnt == 0 {
noCids = append(noCids, cid)
continue
}
// NOTE: when fcnt>0, means cid has normal archive.
}
okCids = append(okCids, cid)
}
}
var flagRetry = false
if len(retryCids) != 0 {
log.Warn("syncBVC aid(%d) cids(%v) need retry again", a.Aid, retryCids)
flagRetry = true
}
if len(okCids) != 0 {
if err = s.bvc.VideoCapable(c, a.Aid, okCids, message.CanPlay); err != nil {
log.Error("syncBVC aid(%d) cids(%v) s.bvc.VideoCapable error(%v)", a.Aid, okCids, err)
flagRetry = true
}
}
if len(noCids) != 0 {
if err = s.bvc.VideoCapable(c, a.Aid, noCids, message.CanNotPlay); err != nil {
log.Error("syncBVC aid(%d) cids(%v) s.bvc.VideoCapable error(%v)", a.Aid, noCids, err)
flagRetry = true
}
}
if flagRetry {
s.syncRetry(c, a.Aid, a.Mid, redis.ActionForBvcCapable, "", "")
}
return
}
//txAddFirstPass 添加第一次过审记录
func (s *Service) txAddFirstPass(c context.Context, tx *sql.Tx, aid int64, state int8) (firstPass bool, err error) {
if !archive.NormalState(state) || s.hadPassed(c, aid) {
return
}
if err = s.arc.AddFirstPass(tx, aid); err != nil {
log.Error("txAddFirstPass error(%v) aid(%d)", err, aid)
return
}
firstPass = true
return
}
//txUpArcState 更新稿件的state并联动添加第一次过审记录
func (s *Service) txUpArcState(c context.Context, tx *sql.Tx, aid int64, state int8) (firstPass bool, err error) {
if _, err = s.arc.TxUpState(tx, aid, state); err != nil {
log.Error("txUpArcState s.arc.TxUpState error(%v) aid(%d) state(%d)", err, aid, state)
return
}
if firstPass, err = s.txAddFirstPass(c, tx, aid, state); err != nil {
log.Error("txUpArcState s.txAddFirstPass error(%v) aid(%d) state(%d)", err, aid, state)
return
}
return
}
// IsUpperFirstPass 是否UP主第一次过审稿件
func (s *Service) IsUpperFirstPass(c context.Context, mid, aid int64) (is bool, err error) {
is = true
sMap, err := s.arc.UpperArcStateMap(c, mid)
if err != nil {
log.Error("s.arc.UpperArcStateMap(%d,%d) error(%v)", mid, aid, err)
return
}
delete(sMap, aid) //剔除当前稿件
var aids []int64
for k, v := range sMap {
aids = append(aids, k)
if archive.NormalState(v) {
//如果有其它过审的稿件那么当前UP主肯定不是第一次过审
is = false
break
}
}
if is {
//查询first_pass表
var count int
count, err = s.arc.FirstPassCount(c, aids)
if err != nil {
log.Error("s.arc.FirstPassCount(%v) error(%v)", aids, err)
return
}
is = count == 0
}
return
}

View File

@@ -0,0 +1,42 @@
package service
import (
"context"
"flag"
"testing"
"go-common/app/job/main/videoup/conf"
. "github.com/smartystreets/goconvey/convey"
"path/filepath"
)
var (
s *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/videoup-job-test.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
}
func TestArchiveVideo(t *testing.T) {
Convey("test archive video", t, func() {
v, a, err := s.archiveVideo(context.Background(), "j171212at3l9zsodcv33n5b7y2ihz0m0")
So(err, ShouldBeNil)
So(a, ShouldNotBeNil)
So(v, ShouldNotBeNil)
t.Logf("resp: %v", a)
})
}
func TestService_IsUpperFirstPass(t *testing.T) {
Convey("", t, func() {
is, err := s.IsUpperFirstPass(context.Background(), 17515232, 10101454)
So(err, ShouldBeNil)
So(is, ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,36 @@
package service
import (
"context"
"time"
"go-common/library/log"
)
func (s *Service) delArcEditHistory(limit int64) (delRows int64, err error) {
var (
c = context.TODO()
mtime = time.Now().Add(-2 * 30 * 24 * time.Hour)
before = time.Date(mtime.Year(), mtime.Month(), mtime.Day(), mtime.Hour(), 0, 0, 0, mtime.Location())
)
if delRows, err = s.arc.DelArcEditHistoryBefore(c, before, limit); err != nil {
log.Error("s.arc.TxDelArcEditHistoryBefore(%s) error(%v)", mtime.Format("2006-01-02 15:04:05"), err)
return
}
log.Info("delArchiveHistory before mtime(%s) rows(%d)", before.Format("2006-01-02 15:04:05"), delRows)
return
}
func (s *Service) delArcVideoEditHistory(limit int64) (delRows int64, err error) {
var (
c = context.TODO()
mtime = time.Now().Add(-2 * 30 * 24 * time.Hour)
before = time.Date(mtime.Year(), mtime.Month(), mtime.Day(), mtime.Hour(), 0, 0, 0, mtime.Location())
)
if delRows, err = s.arc.DelArcVideoEditHistoryBefore(c, before, limit); err != nil {
log.Error("s.arc.TxDelArcVideoEditHistoryBefore(%s) error(%v)", mtime.Format("2006-01-02 15:04:05"), err)
return
}
log.Info("delArcVideoEditHistory before mtime(%s) rows(%d)", before.Format("2006-01-02 15:04:05"), delRows)
return
}

View File

@@ -0,0 +1,46 @@
package service
import (
"encoding/json"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/library/log"
)
const (
_archive = "archive"
)
// arcResultConsumer consume archive result databus message
func (s *Service) arcResultConsumer() {
defer s.wg.Done()
var (
msgs = s.arcResultSub.Messages()
err error
)
for {
msg, ok := <-msgs
if !ok {
log.Error("s.arcResultSub.Messages closed")
return
}
msg.Commit()
s.arcResultMo++
m := &message.ArcResult{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
continue
}
newArc := &archive.Result{}
if err = json.Unmarshal(m.New, newArc); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", m.New, err)
continue
}
log.Info("arcResultConsumer Topic(%s) partition(%d) offset(%d) commit start", msg.Topic, msg.Partition, msg.Offset)
if m.Table == _archive {
log.Info("arcResultConsumer aid(%d) SendBblog msg(%v)", newArc.Aid, newArc)
s.sendBblog(newArc)
}
}
}

View File

@@ -0,0 +1,154 @@
package service
import (
"context"
"encoding/json"
"strings"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
"go-common/library/log"
)
func (s *Service) syncRetry(c context.Context, aid, mid int64, action, route, content string) (err error) {
retry := &redis.RetryJSON{}
retry.Action = action
retry.Data.Aid = aid
retry.Data.Route = route
retry.Data.Mid = mid
retry.Data.Content = content
if action == redis.ActionForVideocovers && (content == "" || strings.Contains(content, "/bfs/archive")) {
return
}
s.redis.PushFail(c, retry)
return
}
func (s *Service) retryproc() {
defer s.wg.Done()
for {
if s.closed {
return
}
var (
c = context.TODO()
bs []byte
err error
retry = &redis.RetryJSON{}
)
bs, err = s.redis.PopFail(c)
if err != nil || bs == nil {
time.Sleep(5 * time.Second)
continue
}
msg := &redis.Retry{}
if err = json.Unmarshal(bs, msg); err != nil {
log.Error("json.Unretry syncmarshal(%s) error(%v)", bs, err)
continue
}
log.Info("retry %s %s", msg.Action, bs)
if err = json.Unmarshal(bs, retry); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg, err)
continue
}
s.promRetry.Incr(msg.Action)
switch msg.Action {
case redis.ActionForBvcCapable:
var a *archive.Archive
if a, err = s.arc.Archive(c, retry.Data.Aid); err != nil {
log.Error("retry bvcCapable archive(%d) error(%v)", retry.Data.Aid, err)
continue
}
log.Info("retry aid(%d) syncBVC bvcCapable", a.Aid)
s.syncBVC(c, a)
case redis.ActionForSendOpenMsg:
s.sendAuditMsg(c, retry.Data.Route, retry.Data.Aid)
case redis.ActionForSendBblog:
var a *archive.Archive
dynamic := ""
if a, err = s.arc.Archive(c, retry.Data.Aid); err != nil {
log.Error("retry sendBblog archive(%d) error(%v)", retry.Data.Aid, err)
s.syncRetry(c, retry.Data.Aid, retry.Data.Mid, redis.ActionForSendBblog, "", "")
continue
}
if add, _ := s.arc.Addit(c, a.Aid); add != nil {
dynamic = add.Dynamic
}
s.sendBblog(&archive.Result{Aid: a.Aid, Mid: a.Mid, Dynamic: dynamic})
case redis.ActionForVideoshot:
imgs := strings.Split(retry.Data.Content, ",")
s.videoshotAdd(retry.Data.Aid, retry.Data.Route, imgs)
case redis.ActionForVideocovers:
if retry.Data.Mid > 20 {
continue
}
var a *archive.Archive
if a, err = s.arc.Archive(c, retry.Data.Aid); err != nil {
s.syncRetry(c, retry.Data.Aid, retry.Data.Mid+1, redis.ActionForVideocovers, retry.Data.Content, retry.Data.Content)
continue
}
log.Info("retryproc videocoverCopy aid(%d) old cover is(%s) retry count is(%d)", retry.Data.Aid, a.Cover, retry.Data.Mid)
if strings.Index(a.Cover, "//") == 0 {
a.Cover = "http:" + a.Cover
}
if strings.Index(a.Cover, "/bfs") == 0 && !strings.Contains(a.Cover, "bfs/archive") {
a.Cover = "http://i0.hdslb.com" + a.Cover
}
if a == nil || a.Cover == "" || strings.Contains(a.Cover, "bfs/archive") || (!strings.HasPrefix(a.Cover, "http://") && !strings.HasPrefix(a.Cover, "https://")) {
log.Error("retryproc videocoverCopy aid(%d) cover is(%s)", retry.Data.Aid, a.Cover)
continue
}
s.videocoverCopy(retry.Data.Aid, retry.Data.Mid, a)
case redis.ActionForPostFirstRound:
pmsg := &message.Videoup{}
if err = json.Unmarshal([]byte(retry.Data.Content), pmsg); err != nil {
log.Error("retryproc postFirstRound json.Unmarshal(%s) error(%v)", retry.Data.Content, err)
continue
}
s.sendPostFirstRound(c, retry.Data.Route, pmsg.Aid, pmsg.Filename, pmsg.AdminChange)
default:
log.Warn("retryproc unknown action(%s) message(%+v)", msg.Action, retry)
}
time.Sleep(10 * time.Millisecond)
}
}
//QueueProc .
func (s *Service) QueueProc() {
defer s.wg.Done()
for {
if s.closed {
return
}
var (
c = context.TODO()
bs []byte
err error
)
bs, err = s.redis.PopQueue(c, message.RouteVideoshotpv)
if err != nil || bs == nil {
time.Sleep(5 * time.Second)
continue
}
m := &message.BvcVideo{}
if err = json.Unmarshal(bs, m); err != nil {
log.Error("QueueProc json.Unmarshal(%v) error(%v)", string(bs), err)
continue
}
log.Info("queue proc pop %+v", m)
switch m.Route {
case message.RouteVideoshotpv:
err = s.videoshotPv(c, m)
default:
log.Warn("QueueProc unknown route(%s) message(%s)", m.Route, m.Route)
}
if err != nil {
log.Error("QueueProc error(%+v)", err)
}
time.Sleep(10 * time.Millisecond)
}
}

View File

@@ -0,0 +1,43 @@
package service
import (
"context"
"time"
"go-common/library/log"
)
func (s *Service) roundproc() {
defer s.wg.Done()
for {
if s.closed {
return
}
s.roundToEnd()
time.Sleep(5 * time.Minute)
}
}
func (s *Service) roundToEnd() {
var (
err error
rows int64
now = time.Now()
minTime = s.delayRoundMinTime
maxTime time.Time
c = context.TODO()
)
if s.roundDelayCache == 0 {
log.Error("roundEnd conf is 0")
return
}
if s.delayRoundMinTime.IsZero() {
minTime = now.Add(-time.Duration(s.roundDelayCache)*24*time.Hour - time.Hour)
}
maxTime = now.Add(-time.Duration(s.roundDelayCache) * 24 * time.Hour)
if rows, err = s.arc.UpDelayRound(c, minTime, maxTime); err != nil {
return
}
s.delayRoundMinTime = maxTime
log.Info("round auto change to end(99),startTime(%v),endTime(%v),affected(%d)", minTime, maxTime, rows)
}

View File

@@ -0,0 +1,195 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
"go-common/library/log"
)
func (s *Service) sendChangeMsg(c context.Context, a *archive.Archive, v *archive.Video, m *message.Videoup) (err error) {
const (
_changeTypeID = "1_7_13"
_changeCopyright = "1_7_14"
_changeTitle = "1_7_15"
_changeCover = "1_7_16"
)
var (
code string
title string
msg string
)
title = "稿件信息变动通知"
now := time.Now().Unix()
if m.ChangeTitle {
code = _changeTitle
msg = fmt.Sprintf(`您的稿件《%s》 #{av%d}{"http://www.bilibili.com/video/av%d/"} 标题内容已被管理员修改,相关规则请查阅投稿页和帮助中心。`, a.Title, a.Aid, a.Aid)
s.msg.Send(c, code, title, msg, a.Mid, now)
}
if m.ChangeTypeID {
code = _changeTypeID
msg = fmt.Sprintf(`您的稿件《%s》#{av%d}{"http://www.bilibili.com/video/av%d/"} 不符合分区分类规则,已被管理员移动至%s区相关规则请查阅投稿页和帮助中心`, a.Title, a.Aid, a.Aid, s.TypeMap[a.TypeID])
s.msg.Send(c, code, title, msg, a.Mid, now)
}
if m.ChangeCopyright {
code = _changeCopyright
msg = fmt.Sprintf(`您的稿件《%s》#{av%d}{"http://www.bilibili.com/video/av%d/"} 投稿类型已被管理员修改,相关规则请查阅投稿页和帮助中心。`, a.Title, a.Aid, a.Aid)
s.msg.Send(c, code, title, msg, a.Mid, now)
}
if m.ChangeCover {
code = _changeCover
msg = fmt.Sprintf(`您的稿件《%s》#{av%d}{"http://www.bilibili.com/video/av%d/"} 封面已被管理员修改,相关规则请查阅投稿页和帮助中心。`, a.Title, a.Aid, a.Aid)
s.msg.Send(c, code, title, msg, a.Mid, now)
}
return
}
// sendMissionMsg
func (s *Service) sendMissionMsg(c context.Context, a *archive.Archive) (err error) {
var (
code = "1_7_21"
title = "【您的稿件已通过审核】"
msg = `您的《%s》%d已经通过审核但由于不符合本次征稿活动的规则故该稿件无法参与本次活动的评选。 #{点击查看>>}{"http://www.bilibili.com/video/av%d/"} 如果您有疑问请联系help@bilibili.com。更多活动信息请关注哔哩哔哩活动。`
now = time.Now().Unix()
)
msg = fmt.Sprintf(msg, a.Title, a.Aid, a.Aid)
if err = s.msg.Send(c, code, title, msg, a.Mid, now); err != nil {
log.Error("s.msg.Send(%s,%s,%s,%d,%d) error(%v)", code, title, msg, a.Mid, now, err)
return
}
return
}
// sendNewUpperMsg
func (s *Service) sendNewUpperMsg(c context.Context, mid, aid int64) (err error) {
var (
code = "1_7_22"
title = "【 %s有位神秘人访问了你的作品】"
msg = `终于看到你的投稿啦|ω・欢迎加入UP主大家庭与我们分享你的热爱。我是你的贴身小秘创作君请收下我悄悄为你准备的 #{入门福利}{"http://member.bilibili.com/studio/annyroal/newcomer-letter?aid=%d"},一定要亲自打开噢(/ω\)想了解更多UP主资讯欢迎关注 #{@哔哩哔哩创作中心}{"https://space.bilibili.com/37090048/?from=message"} `
now = time.Now().Unix()
)
upper, err := s.profile(c, mid)
if err != nil {
log.Error("s.profile(%d) error(%v)", mid, err)
return
}
title = fmt.Sprintf(title, upper.Profile.Name)
msg = fmt.Sprintf(msg, aid)
if err = s.msg.Send(c, code, title, msg, mid, now); err != nil {
log.Error("s.msg.Send(%s,%s,%s,%d,%d) error(%v)", code, title, msg, mid, now, err)
return
}
return
}
func (s *Service) sendMsg(c context.Context, a *archive.Archive, v *archive.Video) (err error) {
const (
_codePass = "1_7_1"
_codeRecycle = "1_7_3"
_codeLock = "1_7_5"
_codeXcodeFail = "1_7_7"
)
var (
code string
title string
reason string
msg string
title2 string
msg2 string
)
switch a.State {
case archive.StateOpen, archive.StateForbidUserDelay, archive.StateOrange:
code = _codePass
title = "您的稿件已通过审核"
title2 = "您的视频已通过审核"
msg = fmt.Sprintf(`您的稿件《%s》av%d已经通过审核#{点击查看>>}{"http://www.bilibili.com/video/av%d/"}`, a.Title, a.Aid, a.Aid)
msg2 = fmt.Sprintf(`您的视频《%s》av%d已经通过审核#{点击查看>>}{"http://www.bilibili.com/video/av%d/"}`, a.Title, a.Aid, a.Aid)
case archive.StateForbidRecicle:
code = _codeRecycle
title = "您的稿件被退回"
title2 = "您的视频被退回"
if v != nil {
reason, _ = s.arc.Reason(c, v.ID)
} else {
reason = a.Reason
}
msg = fmt.Sprintf(`您的稿件《%s》av%d未能通过审核。原因%s 您可以编辑稿件重新投稿,或者对审核结果进行申诉。`, a.Title, a.Aid, reason)
msg2 = fmt.Sprintf(`您的视频《%s》av%d未能通过审核。原因%s。您可以编辑稿件重新投稿或者对审核结果进行申诉。#{点击进行编辑>>}{"https://member.bilibili.com/v/video/submit.html?type=edit&aid=%d"}`, a.Title, a.Aid, reason, a.Aid)
case archive.StateForbidLock:
code = _codeLock
title = "您的视频被退回且锁定"
title2 = title
if v != nil {
reason, _ = s.arc.Reason(c, v.ID)
} else {
reason = a.Reason
}
msg = fmt.Sprintf("您的稿件《%s》av%d未能通过审核且被锁定锁定稿件无法被编辑。原因%s。", a.Title, a.Aid, reason)
msg2 = fmt.Sprintf("您的视频《%s》av%d未能通过审核且被锁定锁定稿件无法被编辑。原因%s。", a.Title, a.Aid, reason)
case archive.StateForbidXcodeFail:
if v == nil {
log.Warn("(%d:%s)二转失败(-16)", a.Aid, title)
return
}
code = _codeXcodeFail
title = "您的视频未能成功转码"
title2 = title
msg = fmt.Sprintf(`您的稿件《%s》av%d未能成功转码。原因%s 请检查视频文件是否可以正常播放后再重新上传视频后再进行投稿,#{点击进入编辑>>}{"http://member.bilibili.com/v/video/submit.html?type=edit&aid=%d"}`,
a.Title, a.Aid, archive.XcodeFailMsgs[v.FailCode], a.Aid)
msg2 = fmt.Sprintf(`您的视频《%s》av%d未能成功转码。原因%s。请检查视频文件是否可以正常播放后再重新上传视频进行投稿。#{点击进行编辑>>}{"https://member.bilibili.com/v/video/submit.html?type=edit&aid=%d"}`,
a.Title, a.Aid, archive.XcodeFailMsgs[v.FailCode], a.Aid)
default:
return
}
now := time.Now().Unix()
s.msg.Send(c, code, title, msg, a.Mid, now)
s.msg.Send(c, "113_1_1", title2, msg2, a.Mid, now)
return
}
// sendAuditMsg send message when delay archive open publish or archive auto open or first round forbid
func (s *Service) sendAuditMsg(c context.Context, route string, aid int64) {
var (
msg = &message.Videoup{
Route: route,
Aid: aid,
Timestamp: time.Now().Unix(),
}
)
k := strconv.FormatInt(aid, 10)
log.Info("s.sendAuditMsg() key(%s) msg(%v)", k, msg)
if err := s.videoupPub.Send(c, k, msg); err != nil {
log.Error("s.sendAuditMsg() key(%s) msg(%v) error (%v)", k, msg, err)
s.syncRetry(c, aid, 0, redis.ActionForSendOpenMsg, msg.Route, "")
}
}
// sendPostFirstRound send message when first round after async status
func (s *Service) sendPostFirstRound(c context.Context, route string, aid int64, filename string, adminChange bool) {
var (
msg = &message.Videoup{
Route: route,
Aid: aid,
Filename: filename,
AdminChange: adminChange,
Timestamp: time.Now().Unix(),
}
bs []byte
)
k := strconv.FormatInt(aid, 10)
log.Info("sendPostFirstRound key(%s) msg(%+v)", k, msg)
if err := s.videoupPub.Send(c, k, msg); err != nil {
log.Error("sendPostFirstRound s.videoupPub.Send key(%s) msg(%+v) error (%v)", k, msg, err)
if bs, err = json.Marshal(msg); err != nil {
log.Error("sendPostFirstRound json.Marshal error(%v)", err)
return
}
s.syncRetry(c, aid, 0, redis.ActionForPostFirstRound, msg.Route, string(bs))
}
}

View File

@@ -0,0 +1,357 @@
package service
import (
"context"
"math"
"sync"
"time"
"go-common/app/job/main/videoup/conf"
"go-common/app/job/main/videoup/dao/activity"
"go-common/app/job/main/videoup/dao/archive"
"go-common/app/job/main/videoup/dao/bvc"
"go-common/app/job/main/videoup/dao/manager"
"go-common/app/job/main/videoup/dao/message"
"go-common/app/job/main/videoup/dao/monitor"
"go-common/app/job/main/videoup/dao/redis"
mngmdl "go-common/app/job/main/videoup/model/manager"
accApi "go-common/app/service/main/account/api"
"go-common/library/conf/env"
"go-common/library/log"
"go-common/library/queue/databus"
"go-common/library/stat/prom"
"github.com/pkg/errors"
)
// Service is service.
type Service struct {
c *conf.Config
// wait group
wg sync.WaitGroup
// acc rpc
accRPC accApi.AccountClient
// dao
arc *archive.Dao
mng *manager.Dao
msg *message.Dao
redis *redis.Dao
monitor *monitor.Dao
activity *activity.Dao
bvc *bvc.Dao
// databus sub
bvc2VuSub *databus.Databus
videoupSub *databus.Databus
arcResultSub *databus.Databus
videoshotSub2 *databus.Databus
// videoupSub 幂等判断
videoupSubIdempotent map[int32]int64
statSub *databus.Databus
// databus pub
videoupPub *databus.Databus
blogPub *databus.Databus
// cache: type, upper
sfTpsCache map[int16]int16
TypeMap map[int16]string
adtTpsCache map[int16]struct{}
thrTpsCache map[int16]int
thrMin, thrMax int
upperCache map[int8]map[int64]struct{}
fansCache int
roundTpsCache map[int16]struct{}
roundDelayCache int64
delayRoundMinTime time.Time
specialUp map[int64]struct{}
// monitor
bvc2VuMo int64
bvc2VuDelayMo int64
videoupMo int64
arcResultMo int64
statMo int64
//prom moni
promDatabus *prom.Prom
promRetry *prom.Prom
//统计差值
promVideoS *prom.Prom
promVideoE *prom.Prom
promPanic *prom.Prom
// closed
closed bool
}
// New is videoup service implementation.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
// dao
arc: archive.New(c),
mng: manager.New(c),
msg: message.New(c),
redis: redis.New(c),
monitor: monitor.New(c),
activity: activity.New(c),
bvc: bvc.New(c),
bvc2VuSub: databus.New(c.Bvc2VuSub),
videoupSubIdempotent: make(map[int32]int64),
videoupSub: databus.New(c.VideoupSub),
arcResultSub: databus.New(c.ArcResultSub),
statSub: databus.New(c.StatSub),
// databus pub
videoupPub: databus.New(c.VideoupPub),
blogPub: databus.New(c.BlogPub),
promDatabus: prom.BusinessInfoCount,
promVideoS: prom.CacheHit,
promVideoE: prom.CacheMiss,
promPanic: prom.CacheMiss,
promRetry: prom.BusinessErrCount,
}
var err error
if s.accRPC, err = accApi.NewClient(c.AccRPC); err != nil {
panic(err)
}
s.specialUp = make(map[int64]struct{}, len(c.SpecialUp))
for _, mid := range c.SpecialUp {
s.specialUp[mid] = struct{}{}
}
// load cache
s.loadType()
s.loadUpper()
s.loadConf()
s.wg.Add(1)
go s.bvc2VuConsumer()
s.wg.Add(1)
go s.videoupConsumer()
s.wg.Add(1)
go s.statConsumer()
s.wg.Add(1)
go s.arcResultConsumer()
if env.DeployEnv == env.DeployEnvProd {
s.videoshotSub2 = databus.New(c.VideoshotSub2)
s.wg.Add(1)
go s.videoshotSHConsumer()
}
s.wg.Add(1)
go s.retryproc()
s.wg.Add(1)
go s.QueueProc()
s.wg.Add(1)
go s.delayproc()
s.wg.Add(1)
go s.roundproc()
go s.cacheproc()
go s.monitorConsume()
go s.edithistoryproc()
return s
}
// Ping ping service.
func (s *Service) Ping(c context.Context) (err error) {
return s.arc.Ping(c)
}
//Rescue runtime panic rescue
func (s *Service) Rescue(data interface{}) {
r := recover()
if r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught: %+v and data is %+v", r, data)
s.promPanic.Incr("panic")
}
}
func (s *Service) edithistoryproc() {
for {
time.Sleep(nextDay(5))
for {
rows, _ := s.delArcEditHistory(100)
time.Sleep(1 * time.Second)
if rows == 0 {
break
}
}
for {
rows, _ := s.delArcVideoEditHistory(100)
time.Sleep(1 * time.Second)
if rows == 0 {
break
}
}
}
}
// Until next day x hours
func nextDay(hour int) time.Duration {
n := time.Now().Add(24 * time.Hour)
d := time.Date(n.Year(), n.Month(), n.Day(), hour, 0, 0, 0, n.Location())
return time.Until(d)
}
// Close consumer close.
func (s *Service) Close() {
s.bvc2VuSub.Close()
s.videoupSub.Close()
s.arcResultSub.Close()
s.statSub.Close()
if env.DeployEnv == env.DeployEnvProd {
s.videoshotSub2.Close()
}
s.closed = true
s.wg.Wait()
s.redis.Close()
}
func (s *Service) isMission(c context.Context, aid int64) bool {
if addit, _ := s.arc.Addit(c, aid); addit != nil && addit.MissionID > 0 {
return true
}
return false
}
func (s *Service) isWhite(mid int64) bool {
if ups, ok := s.upperCache[mngmdl.UpperTypeWhite]; ok {
_, isWhite := ups[mid]
return isWhite
}
return false
}
func (s *Service) isBlack(mid int64) bool {
if ups, ok := s.upperCache[mngmdl.UpperTypeBlack]; ok {
_, isBlack := ups[mid]
return isBlack
}
return false
}
func (s *Service) isAuditType(tpID int16) bool {
_, isAt := s.adtTpsCache[tpID]
return isAt
}
func (s *Service) loadType() {
tpMap, err := s.arc.TypeMapping(context.TODO())
if err != nil {
log.Error("s.arc.TypeMapping error(%v)", err)
return
}
s.sfTpsCache = tpMap
log.Info("s.sfTpsCache Data is (%+v)", s.sfTpsCache)
tpNaming, err := s.arc.TypeNaming(context.TODO())
if err != nil {
log.Error("s.arc.TypeNaming error(%v)", err)
return
}
s.TypeMap = tpNaming
log.Info("s.TypeMap Data is (%+v)", s.TypeMap)
// audit types
adt, err := s.arc.AuditTypesConf(context.TODO())
if err != nil {
log.Error("s.arc.AuditTypesConf error(%v)", err)
return
}
s.adtTpsCache = adt
log.Info("s.adtTpsCache Data is (%+v)", s.adtTpsCache)
// threshold
thr, err := s.arc.ThresholdConf(context.TODO())
if err != nil {
log.Error("s.arc.ThresholdConf error(%v)", err)
return
}
s.thrTpsCache = thr
log.Info("s.thrTpsCache Data is (%+v)", s.thrTpsCache)
var min, max = math.MaxInt32, 0
for _, t := range thr {
if min > t {
min = t
}
if max < t {
max = t
}
}
s.thrMin = min
s.thrMax = max
}
func (s *Service) loadUpper() {
var (
c = context.TODO()
)
upm, err := s.mng.Uppers(c)
if err != nil {
log.Error("s.mng.Uppers error(%v)", err)
return
}
s.upperCache = upm
}
func (s *Service) isRoundType(tpID int16) bool {
_, in := s.roundTpsCache[tpID]
return in
}
func (s *Service) loadConf() {
var (
fans int64
days int64
err error
roundTypes map[int16]struct{}
)
if fans, err = s.arc.FansConf(context.TODO()); err != nil {
log.Error("s.arc.FansConf error(%v)", err)
return
}
s.fansCache = int(fans)
if roundTypes, err = s.arc.RoundTypeConf(context.TODO()); err != nil {
log.Error("s.arc.RoundTypeConf error(%v)", err)
return
}
s.roundTpsCache = roundTypes
if days, err = s.arc.RoundEndConf(context.TODO()); err != nil {
log.Error("s.arc.RoundEndConf")
return
}
s.roundDelayCache = days
}
func (s *Service) cacheproc() {
for {
time.Sleep(1 * time.Minute)
s.loadType()
s.loadUpper()
s.loadConf()
}
}
func (s *Service) monitorConsume() {
if env.DeployEnv != env.DeployEnvProd {
return
}
var bvc2Vu, videoup, arcResult, stat, bvc2VuDelay int64
for {
time.Sleep(1 * time.Minute)
if s.bvc2VuMo-bvc2Vu == 0 {
s.monitor.Send(context.TODO(), "video-job bvc2Video did not consume within a minute")
}
if s.videoupMo-videoup == 0 {
s.monitor.Send(context.TODO(), "video-job videoup did not consume within a minute")
}
if s.arcResultMo-arcResult == 0 {
s.monitor.Send(context.TODO(), "video-job arcResult did not consume within a minute")
}
if s.statMo-stat == 0 {
s.monitor.Send(context.TODO(), "video-job stat did not consume within a minute")
}
if s.bvc2VuDelayMo-bvc2VuDelay > 0 {
s.monitor.Send(context.TODO(), "video-job bvc2videoup consume delayed.")
}
bvc2Vu = s.bvc2VuMo
videoup = s.videoupMo
arcResult = s.arcResultMo
stat = s.statMo
bvc2VuDelay = s.bvc2VuDelayMo
}
}

View File

@@ -0,0 +1,19 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Ping(t *testing.T) {
var err error
var c = context.Background()
Convey("test archive video", t, func() {
if err = s.Ping(c); err != nil {
t.Fail()
}
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,259 @@
package service
import (
"bytes"
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/redis"
"go-common/library/ecode"
"go-common/library/log"
)
const (
videoshotPath = "http://bfs.bilibili.co/bfs/"
videoshotBucket = "videoshot"
videoshotBfsKey = "624480b94ec1e0eb"
videoshotBfsSecret = "303c6cfa85cf2d550fa5050d123cd2"
archiveBucket = "archive"
archiveBfsKey = "8d4e593ba7555502"
archiveBfsSecret = "0bdbd4c7caeeddf587c3c4daec0475"
)
var (
videoshotHTTPClient = &http.Client{Timeout: time.Second * 5}
)
// videoshotSHConsumer is videoshot consumer in shanghai 云立方.
func (s *Service) videoshotSHConsumer() {
defer s.wg.Done()
var (
msgs = s.videoshotSub2.Messages()
)
for {
msg, ok := <-msgs
if !ok {
log.Error("s.videoshotSub2.Message closed")
return
}
msg.Commit()
var res struct {
Cid int64 `json:"cid"`
Image []string `json:"image"`
Bin string `json:"bin"`
}
if err := json.Unmarshal([]byte(msg.Value), &res); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
continue
}
log.Info("videoshotSHConsumer videoshotMessage in shanghai yunlifang key(%s) cid(%d) count(%d) partition(%d) offset(%d) commit", msg.Key, res.Cid, len(res.Image), msg.Partition, msg.Offset)
s.videoshotAdd(res.Cid, res.Bin, res.Image)
}
}
func (s *Service) videocoverCopy(aid, count int64, a *archive.Archive) {
if a == nil {
log.Warn("archive(%d) is not exist", aid)
return
}
cover := a.Cover
_, err := url.ParseRequestURI(cover)
if err != nil {
log.Error("videocoverCopy aid(%s) cover(%s) parse error(%v)", aid, cover, err)
return
}
count = count + 1
_, bs, err := s.videoshotDown(cover)
if err != nil {
log.Error("videocoverCopy aid(%d) cover(%s) videoshotDown err(%v)", aid, cover, err)
if err == ecode.NothingFound {
return
}
s.syncRetry(context.TODO(), aid, count, redis.ActionForVideocovers, cover, cover)
return
}
if cover, err = s.videocoverUp("", bs); err != nil {
log.Error("videocoverCopy aid(%d) cover(%s) videocoverUp err(%v)", aid, cover, err)
s.syncRetry(context.TODO(), aid, count, redis.ActionForVideocovers, a.Cover, a.Cover)
return
}
u, err := url.Parse(cover)
if err != nil {
log.Error("videocoverCopy aid(%d) url(%s) Parse(%s) error", aid, cover, err)
return
}
if _, err := s.arc.UpCover(context.TODO(), aid, u.Path); err != nil {
s.syncRetry(context.TODO(), aid, count, redis.ActionForVideocovers, cover, cover)
return
}
log.Info("videocoverCopy aid(%d) new cover(%s) sub(%s) success", aid, cover, u.Path)
}
func (s *Service) videoshotAdd(cid int64, bin string, imgs []string) {
_, err := url.ParseRequestURI(bin)
if err != nil {
log.Error("videoshotAdd cid(%s) add bin(%s) parse error(%v)", cid, bin, err)
return
}
fn, bs, err := s.videoshotDown(bin)
if err != nil {
if err == ecode.NothingFound {
return
}
s.syncRetry(context.TODO(), cid, 0, redis.ActionForVideoshot, bin, strings.Join(imgs, ","))
return
}
if _, err := s.videoshotUp(fn, bs); err != nil {
s.syncRetry(context.TODO(), cid, 0, redis.ActionForVideoshot, bin, strings.Join(imgs, ","))
return
}
for _, img := range imgs {
if len(img) == 0 {
continue
}
fn, bs, err := s.videoshotDown(img)
if err != nil {
if err == ecode.NothingFound {
return
}
s.syncRetry(context.TODO(), cid, 0, redis.ActionForVideoshot, bin, strings.Join(imgs, ","))
return
}
if _, err := s.videoshotUp(fn, bs); err != nil {
s.syncRetry(context.TODO(), cid, 0, redis.ActionForVideoshot, bin, strings.Join(imgs, ","))
return
}
}
if _, err := s.arc.AddVideoShot(context.TODO(), cid, len(imgs)); err != nil {
s.syncRetry(context.TODO(), cid, 0, "", bin, strings.Join(imgs, ","))
return
}
log.Info("videoshotAdd cid(%d) success", cid)
}
func (s *Service) videoshotDown(uri string) (filename string, bs []byte, err error) {
fileURL, err := url.ParseRequestURI(uri)
if err != nil {
log.Error("videoshotDown(%s) url parse error(%v)", uri, err)
return
}
filename = filepath.Base(fileURL.Path)
resp, err := videoshotHTTPClient.Get(uri)
if err != nil {
log.Error("videoshotDown download from url(%s) error(%v)", uri, err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("videoshowDown(%s) status error(%d)", uri, resp.StatusCode)
log.Error("%v", err)
if resp.StatusCode == http.StatusNotFound {
err = ecode.NothingFound
}
return
}
if bs, err = ioutil.ReadAll(resp.Body); err != nil {
log.Error("videoshowDown(%s) read body error(%v)", uri, err)
}
return
}
func (s *Service) videoshotUp(filename string, bs []byte) (url string, err error) {
var (
uri = fmt.Sprintf("%s%s/%s", videoshotPath, videoshotBucket, filename)
req *http.Request
resp *http.Response
code int
)
if req, err = http.NewRequest(http.MethodPut, uri, bytes.NewReader(bs)); err != nil {
return
}
var sign = func() string {
expire := time.Now().Unix()
content := fmt.Sprintf("%s\n%s\n%s\n%d\n", http.MethodPut, videoshotBucket, filename, expire)
mac := hmac.New(sha1.New, []byte(videoshotBfsSecret))
mac.Write([]byte(content))
return fmt.Sprintf("%s:%s:%d", videoshotBfsKey, base64.StdEncoding.EncodeToString(mac.Sum(nil)), expire)
}
req.Header.Set("Content-Type", "image/jpeg")
req.Header.Set("Authorization", sign())
if resp, err = videoshotHTTPClient.Do(req); err != nil {
log.Error("videoshotUp client.Do(%s) error(%v)", filename, err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("videoshotUp client.Do(%s) status: %d", filename, resp.StatusCode)
log.Error("%v", err)
return
}
if code, err = strconv.Atoi(resp.Header.Get("code")); err != nil {
err = fmt.Errorf("videoshotUp conv(%s) code to int error(%v)", filename, err)
log.Error("%v", err)
return
}
if code == http.StatusOK {
url = resp.Header.Get("location")
} else {
err = fmt.Errorf("videoshotUp client.Do(%s) code: %d", filename, code)
log.Error("%v", err)
}
return
}
func (s *Service) videocoverUp(filename string, bs []byte) (url string, err error) {
var (
uri = fmt.Sprintf("%s%s/%s", videoshotPath, archiveBucket, filename)
req *http.Request
resp *http.Response
code int
)
if req, err = http.NewRequest(http.MethodPut, uri, bytes.NewReader(bs)); err != nil {
return
}
var sign = func() string {
expire := time.Now().Unix()
content := fmt.Sprintf("%s\n%s\n%s\n%d\n", http.MethodPut, archiveBucket, filename, expire)
mac := hmac.New(sha1.New, []byte(archiveBfsSecret))
mac.Write([]byte(content))
return fmt.Sprintf("%s:%s:%d", archiveBfsKey, base64.StdEncoding.EncodeToString(mac.Sum(nil)), expire)
}
req.Header.Set("Content-Type", "image/jpeg")
req.Header.Set("Authorization", sign())
if resp, err = videoshotHTTPClient.Do(req); err != nil {
log.Error("videocoverUp client.Do(%s) error(%v)", filename, err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("videocoverUp client.Do(%s) status: %d", filename, resp.StatusCode)
log.Error("%v", err)
return
}
if code, err = strconv.Atoi(resp.Header.Get("code")); err != nil {
err = fmt.Errorf("videocoverUp conv(%s) code to int error(%v)", filename, err)
log.Error("%v", err)
return
}
if code == http.StatusOK {
url = resp.Header.Get("location")
} else {
err = fmt.Errorf("videocoverUp client.Do(%s) code: %d", filename, code)
log.Error("%v", err)
}
return
}

View File

@@ -0,0 +1,95 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/library/database/sql"
"go-common/library/log"
)
// statConsumer is stat message consumer.
func (s *Service) statConsumer() {
defer s.wg.Done()
const magicClick = 3000
var (
msgs = s.statSub.Messages()
err error
c = context.TODO()
)
for {
msg, ok := <-msgs
if !ok {
log.Error("s.statSub.Messages closed")
return
}
msg.Commit()
s.statMo++
m := &message.StatMsg{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("stat %s json.Unmarshal(%s) error(%v)", msg, msg.Value, err)
continue
}
if m.Type != "archive" || m.ID < 8050956 || m.Count < s.thrMin || m.Count > s.thrMax+magicClick {
continue
}
s.checkClick(c, m)
log.Info("statConsumer key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
}
}
func (s *Service) checkClick(c context.Context, m *message.StatMsg) (err error) {
var a *archive.Archive
if a, err = s.arc.Archive(c, m.ID); err != nil || a == nil {
log.Error("s.arc.Archive(%d) error(%v) or a==nil", m.ID, err)
return
}
if a.Round != archive.RoundReviewFirstWaitTrigger {
log.Warn("archive(%d) round(%d) not 31 wait trigger", a.Aid, a.Round)
return
}
var (
thr = s.thrTpsCache[s.sfTpsCache[a.TypeID]]
click, _ = s.redis.ArcClick(c, a.Aid)
)
if m.Count-click < thr || thr == 0 {
log.Warn("archive(%d) typeThr(%d) nowClick(%d) beforeClick(%d) round(%d)", a.Aid, thr, m.Count, click, a.Round)
return
}
var tx *sql.Tx
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran(%d) error(%v)", m.ID, err)
return
}
var round = archive.RoundTriggerClick
if _, err = s.arc.TxUpRound(tx, a.Aid, round); err != nil {
tx.Rollback()
log.Error("s.arc.TxUpRound(%d, %d) error(%d)", a.Aid, round, err)
return
}
a.Round = round
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit error(%v)", err)
return
}
log.Info("archive(%d) typeThr(%d) nowClick(%d) upRound success", a.Aid, thr, m.Count)
return
}
func (s *Service) addClickToRedis(c context.Context, aid int64) (err error) {
click, err := s.arc.Stat(c, aid)
if err != nil {
log.Error("s.arc.Stat(%d) error(%v)", aid, err)
return
}
if err = s.redis.AddArcClick(c, aid, click); err != nil {
log.Error("s.redis.AddArcClick(%d) error(%v)", click, err)
}
return
}

View File

@@ -0,0 +1,467 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"go-common/app/job/main/videoup/model/archive"
"go-common/app/job/main/videoup/model/message"
"go-common/app/job/main/videoup/model/redis"
xsql "go-common/library/database/sql"
"go-common/library/log"
)
// bvc2VuConsumer is bvc 2 videoup message consumer.
func (s *Service) bvc2VuConsumer() {
defer s.wg.Done()
var (
msgs = s.bvc2VuSub.Messages()
err error
c = context.TODO()
)
for {
func() {
msg, ok := <-msgs
if !ok {
log.Error("s,bvc2VuSub.Message closed")
return
}
defer s.Rescue(string(msg.Value))
msg.Commit()
s.bvc2VuMo++
m := &message.BvcVideo{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%v) error(%v)", string(msg.Value), err)
return
}
if time.Now().Unix()-m.Timestamp > s.c.BvcConsumeTimeout {
log.Info("bvcMessage consume delayed! key(%s) value(%s) partition(%d) offset(%d) route(%s) commit start", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
s.bvc2VuDelayMo++
}
log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) route(%s) commit start", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
s.promDatabus.Incr(m.Route)
switch m.Route {
case message.RouteUploadInfo:
s.promVideoS.Incr("xcode_sd")
s.uploadInfo(c, m)
case message.RouteXcodeSDFail:
err = s.xcodeSDFail(c, m)
case message.RouteXcodeSdFinish:
s.promVideoE.Incr("xcode_sd")
s.promVideoS.Incr("first_round")
err = s.xcodeSDFinish(c, m)
case message.RouteXcodeHDFail:
log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) route(%s) before enter func", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
err = s.xcodeHDFail(c, m)
case message.RouteXcodeHDFinish:
s.promVideoE.Incr("xcode_hd")
s.promVideoS.Incr("dispatch")
err = s.xcodeHDFinish(c, m)
case message.RouteDispatchRunning:
err = s.dispatchRunning(c, m)
case message.RouteDispatchFinish:
s.promVideoE.Incr("dispatch")
err = s.dispatchFinish(c, m)
case message.RouteVideoshotpv:
err = s.videoshotPv(c, m)
default:
log.Warn("bvc2VuConsumer unknown route(%s) message(%s)", m.Route, msg.Value)
}
if err == nil {
log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
} else {
log.Error("bvcMessage key(%s) value(%s) partition(%d) offset(%d) no commit error(%v)", msg.Key, msg.Value, msg.Partition, msg.Offset, err)
}
}()
}
}
func (s *Service) videoshotPv(c context.Context, m *message.BvcVideo) (err error) {
var count = len(m.ImgURLs)
if count == 0 {
return
}
var v *archive.Video
if v, err = s.arc.NewVideo(c, m.Filename); err != nil {
return
}
if v == nil {
log.Warn("filename(%s) videoshotpv video not exist", m.Filename)
return
}
s.arc.AddVideoShot(c, v.Cid, count)
// double write...
//s.videoshotAdd(v.Cid, m.BinURL, m.ImgURLs)
return
}
func (s *Service) uploadInfo(c context.Context, m *message.BvcVideo) (err error) {
s.redis.AddFilename(c, m.Filename)
log.Info("filename(%s) upload success", m.Filename)
return
}
func (s *Service) xcodeSDFail(c context.Context, m *message.BvcVideo) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
if v.XcodeState >= archive.VideoXcodeSDFail {
// NOTE: xcodeFail=1, xcodeState must uploadInfo=0
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
v.Status = archive.VideoStatusXcodeFail
v.XcodeState = archive.VideoXcodeSDFail
v.FailCode = archive.XcodeFailCodes[m.FailInfo]
// begin transcation
var (
tx *xsql.Tx
change bool
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin sd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) sd_fail tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if change, err = s.tranArchive(c, tx, a, v, nil); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) sd_fail tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end sd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if change {
s.sendMsg(c, a, v)
}
return
}
func (s *Service) xcodeSDFinish(c context.Context, m *message.BvcVideo) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
// if v.Playurl == m.PlayURL && v.Duration == m.Duration { // NOTE: check playurl&duration or xcode_state???
if v.XcodeState >= archive.VideoXcodeSDFinish {
// NOTE: sdFinish=2, xcideState must uploadInfo=0||xcodesdfail=1
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
// if video already deleted, no dispatch no update
if v.Status == archive.VideoStatusDelete {
log.Info("xcodeSDFinish archive(%d) video(%s) video already deleted", a.Aid, m.Filename)
v.Status = archive.VideoStatusDelete
} else {
v.Status = archive.VideoStatusWait // NOTE: default -1
}
// if archive already deleted, video state should be?
if a.State == archive.StateForbidUpDelete {
log.Info("xcodeSDFinish archive(%d) video(%s) archive already deleted", a.Aid, m.Filename)
v.Status = archive.VideoStatusDelete
}
v.XcodeState = archive.VideoXcodeSDFinish
v.Playurl = m.PlayURL
v.Duration = m.Duration
// begin transcation
var (
tx *xsql.Tx
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin sd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
if v.Status != archive.VideoStatusDelete {
log.Info("archive(%d) filename(%s) sd_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if _, err = s.tranArchive(c, tx, a, v, nil); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
}
if err = s.tranArcCover(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranArcCover(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) sd_finish tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if _, err = s.arc.TranVideoOper(c, tx, a.Aid, v.ID, v.Status, v.Attribute); err != nil {
tx.Rollback()
log.Error("s.arc.TranVideoOper(%d, %d, %d) error(%v)", a.Aid, v.ID, v.Status, err)
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
if s.canDo(a.Mid) {
s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
}
log.Info("archive(%d) filename(%s) end sd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
return
}
func (s *Service) xcodeHDFail(c context.Context, m *message.BvcVideo) (err error) {
log.Info("xcode hd fail filename (%s)", m.Filename)
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
if v.XcodeState >= archive.VideoXcodeHDFail {
// NOTE: hdfail=3, xcodeState must uploadInfo=0||xcodesdfail=1||sdfinish=2
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
v.Status = archive.VideoStatusXcodeFail
v.XcodeState = archive.VideoXcodeHDFail
v.FailCode = archive.XcodeFailCodes[m.FailInfo]
// begin transcation
var (
tx *xsql.Tx
change bool
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin hd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) hd_fail tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if change, err = s.tranArchive(c, tx, a, v, nil); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) hd_fail tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end hd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if change {
s.sendMsg(c, a, v)
}
return
}
func (s *Service) xcodeHDFinish(c context.Context, m *message.BvcVideo) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
if v.XcodeState >= archive.VideoXcodeHDFinish {
// NOTE: hdFinish=3, xcodeState must uploadInfo=0||xcodesdfail=1||sdfinish=2||hdfail=3
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
// make sure filename not exist in redis, otherwise videoup can not submit!!!
s.redis.DelFilename(c, m.Filename)
log.Info("filename(%s) del_filename from redis success", m.Filename)
// start deal hd finish
v.XcodeState = archive.VideoXcodeHDFinish
v.Resolutions = m.Resolutions
v.Filesize = m.Filesize
v.Duration = m.Duration
v.Dimensions = fmt.Sprintf("%d,%d,%d", m.Width, m.Height, m.Rotate)
// begin transcation
var (
tx *xsql.Tx
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin hd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) hd_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
// only hd5???
var attr int32
if strings.Contains(m.Resolutions, "hdflv2") || strings.Contains(m.Resolutions, "112") {
attr = archive.AttrYes
} else {
attr = archive.AttrNo
}
if _, err = s.arc.TxUpAttrBit(tx, a.Aid, attr, archive.AttrBitHasHD5); err != nil {
tx.Rollback()
log.Error("s.arc.TxUpAttrBit(%d, %d, hd5) error(%v)", a.Aid, attr, err)
return
}
log.Info("archive(%d) filename(%s) hd_finish attrBitHD5 fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end hd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
return
}
func (s *Service) dispatchRunning(c context.Context, m *message.BvcVideo) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
if v.XcodeState >= archive.VideoDispatchRunning {
// NOTE: dispathRun=4, xcodeState must uploadInfo=0||xcodefail=1||sdfinish=2||xcodehdfail=3||hdFinish=4
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
v.XcodeState = archive.VideoDispatchRunning
// begin transcation
var (
tx *xsql.Tx
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin dispatch_run transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) dispatch_run tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if _, err = s.tranArchive(c, tx, a, v, nil); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) dispatch_run tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end dispatch_run transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
return
}
func (s *Service) dispatchFinish(c context.Context, m *message.BvcVideo) (err error) {
var (
v *archive.Video
a *archive.Archive
)
if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
return
}
if v.XcodeState >= archive.VideoDispatchFinish {
// NOTE: dispathFinish=5, xcodeState must uploadInfo=0||xcodefail=1||sdfinish=2||hdFinish=3||dispathRun=4
log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
return // NOTE: is or not return???
}
v.XcodeState = archive.VideoDispatchFinish
// begin transcation
var (
tx *xsql.Tx
sChange, rChange bool
)
if tx, err = s.arc.BeginTran(c); err != nil {
log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
return
}
log.Info("archive(%d) filename(%s) begin dispatch_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if err = s.tranVideo(c, tx, a, v); err != nil {
tx.Rollback()
log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) dispatch_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if sChange, err = s.tranArchive(c, tx, a, v, nil); err != nil {
tx.Rollback()
log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) dispatch_finish tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
var round int8
if round, err = s.tranRound(c, tx, a); err != nil {
tx.Rollback()
return
}
rChange = round != a.Round
log.Info("archive(%d) filename(%s) dispatch_finish tranRound fininsh old_round(%d) new_round(%d)", a.Aid, v.Filename, a.Round, round)
a.Round = round
if sChange || rChange {
if err = s.tranArchiveOper(tx, a); err != nil {
tx.Rollback()
return
}
}
log.Info("archive(%d) filename(%s) dispatch_finish round_opr fininsh round(%d)", a.Aid, v.Filename, a.Round)
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
return
}
log.Info("archive(%d) filename(%s) end dispatch_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
if sChange {
//稿件二审in/out量监控 每个aid 只统计一次 1,自动过审2,手动审核需分开处理多P也只统计一次 start,end
var had bool
if archive.NormalState(a.State) {
//monitor second_round 自动开放 in/out diff
s.promVideoS.Incr("second_round")
s.promVideoE.Incr("second_round")
//auto open
s.syncBVC(c, a)
s.sendAuditMsg(c, message.RouteAutoOpen, a.Aid)
if is, _ := s.IsUpperFirstPass(c, a.Mid, a.Aid); is {
go s.sendNewUpperMsg(c, a.Mid, a.Aid)
}
} else if had, _ = s.redis.SetMonitorCache(c, a.Aid); had {
s.promVideoS.Incr("second_round")
}
s.sendMsg(c, a, v)
if a.State == archive.StateForbidFixed {
s.addClickToRedis(c, a.Aid)
}
}
return
}