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,94 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"archive_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/videoup/conf:go_default_library",
"//app/interface/main/videoup/model/archive:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"allow.go",
"app.go",
"check.go",
"check_staff.go",
"client.go",
"creator.go",
"deal.go",
"filter.go",
"geetest.go",
"infoc.go",
"log.go",
"pay.go",
"pre.go",
"service.go",
"subtitle.go",
"web.go",
],
importpath = "go-common/app/interface/main/videoup/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/creative/model/tag:go_default_library",
"//app/interface/main/videoup/conf:go_default_library",
"//app/interface/main/videoup/dao/account:go_default_library",
"//app/interface/main/videoup/dao/archive:go_default_library",
"//app/interface/main/videoup/dao/bfs:go_default_library",
"//app/interface/main/videoup/dao/creative:go_default_library",
"//app/interface/main/videoup/dao/dynamic:go_default_library",
"//app/interface/main/videoup/dao/elec:go_default_library",
"//app/interface/main/videoup/dao/filter:go_default_library",
"//app/interface/main/videoup/dao/geetest:go_default_library",
"//app/interface/main/videoup/dao/mission:go_default_library",
"//app/interface/main/videoup/dao/order:go_default_library",
"//app/interface/main/videoup/dao/pay:go_default_library",
"//app/interface/main/videoup/dao/subtitle:go_default_library",
"//app/interface/main/videoup/dao/tag:go_default_library",
"//app/interface/main/videoup/model/archive:go_default_library",
"//app/interface/main/videoup/model/geetest:go_default_library",
"//app/interface/main/videoup/model/mission:go_default_library",
"//app/interface/main/videoup/model/porder:go_default_library",
"//app/service/main/account/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/metadata:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/microcosm-cc/bluemonday: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,111 @@
package service
import (
"context"
"strings"
"time"
"unicode/utf8"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/log"
xtime "go-common/library/time"
)
// forbidTopTypesForAll fn 175=>ASMR
func (s *Service) forbidTopTypesForAll(tid int16) bool {
return tid == 175
}
func (s *Service) allowOrderUps(mid int64) (ok bool) {
if mid <= 0 {
ok = false
return
}
_, ok = s.orderUps[mid]
return
}
func (s *Service) allowType(typeid int16) (ok bool) {
_, ok = s.typeCache[typeid]
if s.forbidTopTypesForAll(typeid) {
ok = false
}
return
}
func (s *Service) allowCopyright(cp int8) (ok bool) {
ok = archive.InCopyrights(cp)
return
}
func (s *Service) allowSource(cp int8, source string) (ok bool) {
ok = cp == archive.CopyrightOriginal || (cp == archive.CopyrightCopy && len(strings.TrimSpace(source)) > 0)
return
}
func (s *Service) allowTag(tag string) (ok bool) {
if len(tag) == 0 {
return
}
for _, reg := range _emptyUnicodeReg {
if reg.MatchString(tag) {
return
}
}
tags := strings.Split(tag, ",")
if len(tags) > 12 {
return
}
for _, t := range tags {
if utf8.RuneCountInString(t) > 30 {
return
}
}
ok = true
return
}
func (s *Service) allowDelayTime(dtime xtime.Time) (ok bool) {
if dtime == 0 {
ok = true
return
}
const (
min = int64(4 * time.Hour / time.Second)
max = int64(15 * 24 * time.Hour / time.Second)
)
diff := int64(dtime) - time.Now().Unix()
ok = min < diff && diff < max
return
}
func (s *Service) allowHalfMin(c context.Context, mid int64) (ok bool) {
// 活动等其他业务方运营需要,接触半分钟的限速
if _, white := s.exemptHalfMinUps[mid]; white {
return true
}
log.Info("halfMin start | mid(%d).", mid)
exist, _, _ := s.acc.HalfMin(c, mid)
log.Info("halfMin from cache | mid(%d) exist(%v).", mid, exist)
//先判断缓存,ok取反,如果存在则不允许,继续等冷却时间;如果缓存不存在,则默认继续添加冷却时间窗口
if ok = !exist; ok {
log.Info("halfMin not exist | mid(%d)", mid)
s.acc.AddHalfMin(c, mid)
log.Info("halfMin add cache | mid(%d).", mid)
}
log.Info("halfMin end | mid(%d).", mid)
return
}
func (s *Service) allowRepeat(c context.Context, mid int64, title string) (ok bool) {
log.Info("allowRepeat check start | mid(%d) title(%s).", mid, title)
exist, _ := s.acc.SubmitCache(c, mid, title)
log.Info("allowRepeat from cache | mid(%d) title(%s) exist(%d).", mid, title, exist)
if ok = exist == 0; ok {
log.Info("allowRepeat not exist | mid(%d) title(%s)", mid, title)
s.acc.AddSubmitCache(c, mid, title)
log.Info("allowRepeat add cache | mid(%d) title(%s).", mid, title)
}
log.Info("allowRepeat check end | mid(%d) title(%s).", mid, title)
return
}

View File

@@ -0,0 +1,223 @@
package service
import (
"bytes"
"context"
"net"
"strings"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
)
// AppEdit edit archive by appclient.
func (s *Service) AppEdit(c context.Context, ap *archive.ArcParam, mid int64) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
var (
a = &archive.Archive{}
vs = []*archive.Video{}
)
if a, vs, err = s.arc.View(c, ap.Aid, ip); err != nil {
log.Error("s.arc.View err(%v) | aid(%d) ip(%s)", err, ap.Aid, ip)
return
}
if a == nil {
log.Error("s.arc.View(%d) not found", mid)
err = ecode.ArchiveNotExist
return
}
// pre check
if err = s.preEdit(c, mid, a, vs, ap, ip, ap.UpFrom); err != nil {
return
}
// edit
if err = s.arc.Edit(c, ap, ip); err != nil {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, ap.Aid, mid, ip)
return nil
})
g.Wait()
return
}
// AppUpCover main app upload cover.
func (s *Service) AppUpCover(c context.Context, fileType string, body []byte, mid int64) (url string, err error) {
if len(body) == 0 {
err = ecode.FileNotExists
log.Error("AppEcode FileNotExists mid(%d) error(%v)", mid, err)
return
}
if len(body) > s.c.Bfs.MaxFileSize {
err = ecode.FileTooLarge
log.Error("AppEcode FileTooLarge mid(%d) error(%v)", mid, err)
return
}
url, err = s.bfs.Upload(c, fileType, bytes.NewReader(body))
if err != nil {
log.Error("AppEcode s.bfs.Upload error(%v)", err)
}
return
}
func (s *Service) freshAppMissionByFirstTag(ap *archive.ArcParam) (res *archive.ArcParam) {
if ap.MissionID == 0 {
firstTag := strings.Split(ap.Tag, ",")[0]
if missionID, ok := s.missTagsCache[firstTag]; ok {
ap.MissionID = missionID
}
}
res = ap
return
}
// AppAdd add archive by main app.
func (s *Service) AppAdd(c context.Context, mid int64, ap *archive.ArcParam, ar *archive.AppRequest) (aid int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
defer func() {
if err != nil && err != ecode.VideoupCanotRepeat {
s.acc.DelSubmitCache(c, ap.Mid, ap.Title)
}
}()
ap = s.freshAppMissionByFirstTag(ap)
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
// pre check
if err = s.preAdd(c, mid, ap, ip, ap.UpFrom); err != nil {
return
}
if ap.PoiObj != nil {
log.Warn("poi_object is not nil, mid(%d),upfrom(%d),poi_object(%+v)", mid, ap.UpFrom, ap.PoiObj)
}
if aid, err = s.arc.Add(c, ap, ip); err != nil || aid == 0 {
return
}
ap.Aid = aid
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealOrder(ctx, mid, aid, ap.OrderID, ip)
return nil
})
g.Go(func() error {
s.dealWaterMark(ctx, mid, ap.Watermark, ip)
return nil
})
g.Go(func() error {
s.freshFavs(ctx, mid, ap, ip)
return nil
})
g.Go(func() error {
s.dealElec(ctx, 1, aid, mid, ip)
return nil
})
g.Go(func() error {
s.uploadVideoEditInfo(ctx, ap, aid, mid, ip)
return nil
})
g.Go(func() error {
s.lotteryBind(ctx, ap.LotteryID, aid, mid, ip)
return nil
})
g.Go(func() error {
s.addFollowing(ctx, mid, ap.FollowMids, ap.UpFrom, ip)
return nil
})
g.Go(func() error {
s.VideoInfoc(ctx, ap, ar)
return nil
})
g.Wait()
return
}
// AppEditFull fn
func (s *Service) AppEditFull(c context.Context, ap *archive.ArcParam, mid, buildNum int64, ar *archive.AppRequest) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
platform := ar.Platform
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
var (
a = &archive.Archive{}
vs = []*archive.Video{}
)
if a, vs, err = s.arc.View(c, ap.Aid, ip); err != nil {
log.Error("s.arc.View err(%v) | aid(%d) ip(%s)", err, ap.Aid, ip)
return
}
if a == nil {
log.Error("s.arc.View(%d) not found", mid)
err = ecode.ArchiveNotExist
return
}
if nvsCnt := s.checkVideosMaxLimitForEdit(vs, ap.Videos); nvsCnt > s.c.MaxAddVsCnt {
log.Error("checkVideosMaxLimitForEdit, vsCnt(%d), limit(%d), nvsCnt(%d)", len(vs), s.c.MaxAddVsCnt, nvsCnt)
err = ecode.VideoupVideosMaxLimit
return
}
ap = s.protectFeatureForApp(ap, a, buildNum, platform)
// pre check
if err = s.preEdit(c, mid, a, vs, ap, ip, ap.UpFrom); err != nil {
return
}
// edit
if err = s.arc.Edit(c, ap, ip); err != nil {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, ap.Aid, mid, ip)
return nil
})
g.Go(func() error {
s.uploadVideoEditInfo(ctx, ap, ap.Aid, mid, ip)
return nil
})
g.Go(func() error {
s.addFollowing(ctx, mid, ap.FollowMids, ap.UpFrom, ip)
return nil
})
g.Go(func() error {
s.VideoInfoc(ctx, ap, ar)
return nil
})
g.Wait()
return
}
// protectFeatureForApp fn
// feature list: porder,order,desc_format_id
func (s *Service) protectFeatureForApp(origin *archive.ArcParam, a *archive.Archive, buildNum int64, platform string) (res *archive.ArcParam) {
res = origin
res.Porder = a.Porder
res.OrderID = a.OrderID
res.DescFormatID = a.DescFormatID
// android except 5.26 5260000
if buildNum < 5260000 && platform == "android" {
res.Dynamic = a.Dynamic
res.MissionID = a.MissionID
// ios include 5.25.1 6680
} else if buildNum <= 6680 && platform == "ios" {
res.Dynamic = a.Dynamic
res.MissionID = a.MissionID
}
return
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"encoding/json"
"testing"
"go-common/app/interface/main/videoup/model/archive"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Ping(t *testing.T) {
var (
c = context.Background()
err error
)
Convey("Ping", t, WithService(func(s *Service) {
err = s.Ping(c)
So(err, ShouldBeNil)
}))
}
func Test_Close(t *testing.T) {
Convey("Close", t, WithService(func(s *Service) {
s.Close()
}))
}
func Test_WebAdd(t *testing.T) {
var (
c = context.Background()
err error
MID = int64(27515256)
body = `{"copyright":1,"cover":"","title":"test","tid":130,"tag":"音乐选集","no_reprint":1,"upos":0,"lang":"zh-CN","mission_id":0,"porder":{},"desc":"123","dynamic":"123","videos":[{"desc":"","filename":"g180126072jadys8fuaz74u18hkxwvnf","title":""}]}
`
aid int64
)
var ap = &archive.ArcParam{}
if err = json.Unmarshal([]byte(body), ap); err != nil {
return
}
Convey("webAdd", t, WithService(func(s *Service) {
aid, err = s.WebAdd(c, MID, ap, true)
So(err, ShouldBeNil)
So(aid, ShouldNotBeNil)
}))
}
func Test_WebEdit(t *testing.T) {
var (
c = context.Background()
err error
MID = int64(27515256)
body = `{"copyright":1,"cover":"","title":"test","tid":130,"tag":"音乐选集","no_reprint":1,"upos":0,"lang":"zh-CN","mission_id":0,"porder":{},"desc":"123","dynamic":"123","videos":[{"desc":"","filename":"g180126072jadys8fuaz74u18hkxwvnf","title":""}]}
`
)
var ap = &archive.ArcParam{}
if err = json.Unmarshal([]byte(body), ap); err != nil {
return
}
Convey("webEdit", t, WithService(func(s *Service) {
err = s.WebEdit(c, ap, MID)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,460 @@
package service
import (
"context"
"go-common/app/interface/main/creative/model/tag"
"go-common/app/interface/main/videoup/model/archive"
"go-common/app/interface/main/videoup/model/mission"
"go-common/app/interface/main/videoup/model/porder"
accapi "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
"go-common/library/xstr"
"net/url"
"regexp"
"strings"
"time"
"unicode/utf8"
)
var (
_emptyUnicodeRegForTitle = []*regexp.Regexp{
regexp.MustCompile(`[\x{202e}]+`), // right-to-left override
regexp.MustCompile(`[\x{200b}]+`), // zeroWithChar
regexp.MustCompile(`[\x{1f6ab}]+`), // no_entry_sign
regexp.MustCompile(`[\n]+`), // newline
regexp.MustCompile(`[\r]+`), // newline
}
_emptyUnicodeReg = []*regexp.Regexp{
regexp.MustCompile(`[\x{202e}]+`), // right-to-left override
regexp.MustCompile(`[\x{200b}]+`), // zeroWithChar
regexp.MustCompile(`[\x{1f6ab}]+`), // no_entry_sign
}
_nocharReg = []*regexp.Regexp{
regexp.MustCompile(`[\p{Hangul}]+`), // kr
regexp.MustCompile(`[\p{Tibetan}]+`), // tibe
regexp.MustCompile(`[\p{Arabic}]+`), // arabic
}
_filenameReg = regexp.MustCompile(`^[A-Z0-9a-z]+$`) // only letter digital.
_staffNameReg = regexp.MustCompile("^[\u4e00-\u9fa5a-zA-Z0-9]+$") //职能:数字、字母、中文
)
func (s *Service) checkMission(c context.Context, ap *archive.ArcParam) (err error) {
if ap.MissionID <= 0 {
log.Warn("MissionID(%d) error", ap.MissionID)
ap.MissionID = 0
return
}
missionID := ap.MissionID
tid := ap.TypeID
m, ok := s.missCache[missionID]
if !ok || m.ID == 0 {
err = ecode.VideoupMissionErr
return
}
if m.ETime.Before(time.Now()) {
log.Error("VideoupMissionEtimeInvalid err, tid(%d)|etime(%+v)", tid, m.ETime)
err = ecode.VideoupMissionEtimeInvalid
return
}
var missionTys map[int]*mission.Mission
if missionTys, err = s.miss.MissionOnlineByTid(c, tid); err != nil {
log.Error("MissionOnlineByTid err, s.tid(%+v)|err(%+v)", tid, err)
err = nil
return
}
// case: 这是活动全下架,还想参加活动
if len(missionTys) == 0 {
log.Error("missionTys already empty, tid(%d)|missionTys(%+v)", tid, missionTys)
err = ecode.VideoupMissionNoMatch
return
}
// 包含对分区无限制的活动
if _, ok := missionTys[missionID]; !ok {
log.Error("VideoupMissionNoMatch err, tid(%d)|missionID(%d)", tid, missionID)
err = ecode.VideoupMissionNoMatch
return
}
if ap.Copyright == archive.CopyrightCopy {
log.Error("VideoupCopyForbidJoinMission err, copyright(%d)|missionID(%d)", ap.Copyright, ap.MissionID)
err = ecode.VideoupCopyForbidJoinMission
return
}
return
}
func (s *Service) checkMissionTag(srcTag string, missionID int) (dstTag string, err error) {
var ctags = strings.Split(srcTag, ",")
dstTag = srcTag
// 交叉对比剔除掉当前活动已经使用的tag内容
if len(s.missTagsCache) != 0 {
var tags = make([]string, 0, len(ctags))
for _, t := range ctags {
if _, ok := s.missTagsCache[t]; !ok {
tags = append(tags, t)
}
}
dstTag = strings.Join(tags, ",")
}
// 校验
// 两种情况报错提示用户: 1. 未参加活动,只提交一个tag且是活动tag, 2. 参加活动只提交了一个tag且是其他活动的活动tag
if dstTag == "" && missionID == 0 {
log.Error("forbidMissionTagWithoutJoinMission srcTag(%s), MissionID(%d),s.missCache(%+v),s.missTagsCache(%+v)", srcTag, missionID, s.missCache, s.missTagsCache)
err = ecode.VideoupTagForbidNotJoinMission
return
}
// 未参加活动就直接返回校验后的tag内容
m, ok := s.missCache[missionID]
if !ok {
return
}
// 如果参加了当前有效的活动就会把对应的活动第一个tag拼接在头部, 对于活动id和tag不匹配的会做校验
var singleMissionTag string
if m.Tags != "" {
singleMissionTag = strings.Split(m.Tags, ",")[0]
} else {
singleMissionTag = m.Name
}
if len(dstTag) > 0 {
dstTag = singleMissionTag + "," + dstTag
} else {
dstTag = singleMissionTag
}
return
}
func (s *Service) checkVideo(ap *archive.ArcParam) (err error) {
vds := make([]*archive.VideoParam, 0)
fnMap := make(map[string]int)
var ok bool
for i, v := range ap.Videos {
if v == nil {
continue
}
if v.Title, ok = s.checkTitle(v.Title); !ok {
newErr := ecode.VideoupVideoTitleErr
err = ecode.Errorf(newErr, newErr.Message(), i+1)
log.Error("ap.Videos checkTitle err(%+v)|Title(%s)", err, v.Title)
return
}
if v.Desc, ok = s.checkDesc(v.Desc); !ok {
newErr := ecode.VideoupVideoDescErr
err = ecode.Errorf(newErr, newErr.Message(), i+1)
log.Error("ap.Videos checkDesc err(%+v)|Desc(%s)", err, v.Desc)
return
}
if ok = _filenameReg.MatchString(v.Filename); !ok {
newErr := ecode.VideoupVideoFilenameErr
err = ecode.Errorf(newErr, newErr.Message(), i+1)
log.Error("ap.Videos _filenameReg err(%+v)|filename(%s)", err, v.Filename)
return
}
if v.Cid == 0 && v.Filename == "" { // NOTE: cid>0 means code mode
newErr := ecode.VideoupVideoFilenameErr
err = ecode.Errorf(newErr, newErr.Message(), i+1)
log.Error("ap.Videos err(%+v)|Filename(%s)|Cid(%d)", err, v.Filename, v.Cid)
return
}
if _, ok := fnMap[v.Filename]; ok {
err = ecode.VideoupFilenameCanotRepeat
log.Error("ecode.VideoupFilenameCanotRepeat err(%+v)|Filename(%s)|index(%d)", err, v.Filename, i)
return
}
vds = append(vds, v)
fnMap[v.Filename] = 1
}
ap.Videos = vds
return
}
func (s *Service) checkCover(cover string) (cv string, ok bool) {
if cover == "" {
ok = true
return
}
uri, err := url.Parse(cover)
if err != nil {
return
}
if strings.Contains(uri.Host, "hdslb.com") {
cv = uri.Path
ok = true
return
} else if strings.Contains(uri.Host, "acgvideo.com") {
cv = cover
ok = true
return
}
return
}
func (s *Service) checkDynamicLen233(dynamic string) (dyn string, ok bool) {
dyn = strings.TrimSpace(dynamic)
var _emptyDynUnicodeReg = []*regexp.Regexp{
regexp.MustCompile(`[\x{FFFC}]+`), // obj
}
for _, reg := range _emptyDynUnicodeReg {
dyn = reg.ReplaceAllString(dyn, "")
}
if utf8.RuneCountInString(dyn) > 233 {
return
}
ok = true
return
}
func (s *Service) checkTitle(title string) (ct string, ok bool) {
ct = strings.TrimSpace(title)
if utf8.RuneCountInString(ct) > 80 {
return
}
for _, reg := range _nocharReg {
if reg.MatchString(ct) {
return
}
}
for _, reg := range _emptyUnicodeRegForTitle {
ct = reg.ReplaceAllString(ct, "")
}
ok = true
return
}
func (s *Service) checkDesc(desc string) (cd string, ok bool) {
cd = strings.TrimSpace(desc)
for _, reg := range _emptyUnicodeReg {
cd = reg.ReplaceAllString(cd, "")
}
if utf8.RuneCountInString(cd) > 2000 {
return
}
ok = true
return
}
func (s *Service) checkAccount(c context.Context, mid int64, ip string) (p *accapi.Profile, err error) {
if p, err = s.acc.Profile(c, mid, ip); err != nil {
return
}
if p.Silence == 1 {
err = ecode.UserDisabled
} else if p.Level < 1 {
err = ecode.UserLevelLow
}
if _, ok := s.exemptZeroLevelAndAnswerUps[mid]; ok && err == ecode.UserLevelLow {
log.Info("s.exemptZeroLevelAndAnswerUps, (%s),(%d),(%+v)", ip, mid, err)
err = nil
}
return
}
func (s *Service) checkOrderID(c context.Context, mid, orderID int64, ip string) (err error) {
orderIDs, err := s.order.ExecuteOrders(c, mid, ip)
if err != nil {
log.Error("s.order.ExecuteOrders mid(%d) ip(%s) error(%v)", mid, err)
err = ecode.VideoupOrderAPIErr
return
}
if _, ok := orderIDs[orderID]; !ok {
err = ecode.VideoupOrderIDNotAllow
}
return
}
func (s *Service) checkIdentify(c context.Context, mid int64, ip string) (err error) {
if _, ok := s.exemptIDCheckUps[mid]; ok {
log.Info("s.exemptIDCheckUps, (%s),(%d),(%+v)", ip, mid, err)
return
}
// fault-tolerant for service interruption
if err = s.acc.IdentifyInfo(c, ip, mid); err != nil {
if err != ecode.UserCheckNoPhone && err != ecode.UserCheckInvalidPhone {
log.Warn("s.accIdentifyInfo, account service maybe in interruption,(%s),(%d),(%+v)", ip, mid, err)
return nil
}
log.Error("s.accIdentifyInfo, (%s),(%d),(%+v)", ip, mid, err)
return
}
return
}
// checkPorderForAdd
func (s *Service) checkPorderForAdd(c context.Context, ap *archive.ArcParam, mid int64) (err error) {
// 防止脏数据, 强制计算和校验Porder的数据
ap.Porder.FlowID = 1
log.Info("ap.Porder (%+v)", ap.Porder)
// showType check
if len(ap.Porder.ShowType) > 0 {
var showTypes []int64
if showTypes, err = xstr.SplitInts(ap.Porder.ShowType); err != nil {
log.Error("SplitInts ShowType err, (%s),(%+v)", ap.Porder.ShowType, err)
return
}
//广告的展现形式太多或者太少
if len(showTypes) == 0 {
err = ecode.VideoupAdShowTypeErr
log.Error("check showTypes (%+v)|err(%+v)", ap.Porder, err)
return
}
for _, showType := range showTypes {
if showType > 0 {
if _, ok := s.PorderCfgs[showType]; !ok {
err = ecode.VideoupAdShowTypeErr
log.Error("VideoupAdShowTypeErr Porder(%+v)|err(%+v)", ap.Porder.ShowType, err)
return
}
}
}
}
// Official check
if ap.Porder.Official == 1 {
if _, ok := porder.OfficialIndustryMaps[ap.Porder.IndustryID]; !ok {
err = ecode.VideoupAdOfficialIndustryIDErr
log.Error("VideoupAdOfficialIndustryIDErr Porder(%+v)|err(%+v)", ap.Porder, err)
return
}
if ap.Porder.BrandID < 0 {
err = ecode.VideoupAdBrandIDErr
log.Error("VideoupAdBrandIDErr Porder(%+v)|err(%+v)", ap.Porder, err)
return
}
// logic map to OfficialIndustryMaps waiting for add other official industry
if _, ok := s.PorderGames[ap.Porder.BrandID]; !ok {
err = ecode.VideoupAdBrandIDErr
log.Error("VideoupAdBrandIDErr Porder(%+v)|err(%+v)", ap.Porder, err)
return
}
}
// Industry check
if ap.Porder.IndustryID > 0 {
if _, ok := s.PorderCfgs[ap.Porder.IndustryID]; !ok {
err = ecode.VideoupAdIndustryIDErr
log.Error("VideoupAdIndustryIDErr Porder(%+v)|err(%+v)", ap.Porder, err)
return
}
}
return
}
// checkDescForLength fn
func (s *Service) checkDescForLength(desc string, descFormatID int, typeID int16, copyright int8) (err error) {
if descFormatID == 0 {
if utf8.RuneCountInString(desc) > 250 {
err = ecode.VideoupFmDesLenOverLimit
log.Error("ecode.VideoupFmDesLenOverLimit, desc(%s),formatID(%d)", desc, descFormatID)
return
}
return
}
if utf8.RuneCountInString(desc) > 2000 {
err = ecode.VideoupFmDesLenOverLimit
log.Error("ecode.VideoupFmDesLenOverLimit, desc(%s),formatID(%d)", desc, descFormatID)
return
}
return
}
// checkVideos, 1: check len(video) == ? 0 , 2: check typeID in (145,146,147,83) and with multi Videos
func (s *Service) checkVideos(c context.Context, ap *archive.ArcParam) (err error) {
if len(ap.Videos) == 0 {
log.Error("checkVideos vds length 0")
err = ecode.VideoupZeroVideos
return
}
if len(ap.Videos) > 1 && ap.ForbidMultiVideoType() {
log.Error("checkVideos vds ForbidMultiVideoType, len(%d), type(%d) ", len(ap.Videos), ap.TypeID)
err = ecode.VideoupForbidMultiVideoForTypes
return
}
return
}
// tagsCheck fn
func (s *Service) tagsCheck(c context.Context, mid int64, tagName, ip string) (err error) {
var t *tag.Tag
tags := strings.Split(tagName, ",")
for i, tagStr := range tags {
if t, err = s.tag.TagCheck(c, mid, tagStr); err != nil {
log.Error("s.tag.TagCheck(%d, %+v, %s) error(%+v)", mid, t, ip, err)
err = nil
return
}
if t != nil && (t.State == tag.TagStateDel || t.State == tag.TagStateHide || t.Type == tag.OfficailActiveTag) {
newErr := ecode.VideoupTagForbid
err = ecode.Errorf(newErr, newErr.Message(), i+1)
log.Error("s.tag.VideoupTagForbid (%d, %+v, %s) error(%+v)", mid, t, ip, err)
return
}
}
return
}
// checkVideosMaxLimitForEdit fn
func (s *Service) checkVideosMaxLimitForEdit(vs []*archive.Video, pvideos []*archive.VideoParam) (addCnt int) {
fnMaps := make(map[string]string)
for _, v := range vs {
fnMaps[v.Filename] = v.Filename
}
for _, v := range pvideos {
if _, exist := fnMaps[v.Filename]; len(v.Filename) > 0 && !exist {
addCnt++
}
}
return
}
// checkPay fn
func (s *Service) checkAddPay(c context.Context, ap *archive.ArcParam, ip string) (err error) {
if err = s.checkPayProtocol(c, ap.Pay, ap.Mid); err != nil {
log.Error("s.checkAddPayProtocol (ap %+v) error(%+v)", ap, err)
return
}
if err = s.checkPayLimit(c, ap); err != nil {
log.Error("s.checkAddPayLimit (ap %+v) error(%+v)", ap, err)
return
}
if err = s.checkPayWithOrder(c, ap.Porder, ap.Pay, ap.OrderID, ap.Mid); err != nil {
log.Error("s.checkAddPayWithOrder (ap %+v) error(%+v)", ap, err)
return
}
return
}
// checkEditPay fn
// 关于RefuseUpdate
// 1.当ctime==ptime的时候也就是尚未稿件一审都是可以update付费模块的
// 2.开放过的,打回的稿件可以在任何时间点进行付费修改
// 3.开放过的非打回的稿件想要修改只能等60天时间过了之后(时间锁是为了保护普通用户的收看权利),否则自己申诉付费审核人员,要求强制打回
func (s *Service) checkEditPay(c context.Context, ap *archive.ArcParam, a *archive.Archive, ip string) (err error) {
_, registed, _ := s.pay.Ass(c, a.Aid, ip)
// 只要注册过付费信息都允许自主修改付费模块
if registed {
if err = s.checkPayProtocol(c, ap.Pay, ap.Mid); err != nil {
log.Error("s.checkAddPayProtocol (ap %+v) error(%+v)", ap, err)
return
}
if err = s.checkPayLimit(c, ap); err != nil {
log.Error("s.checkAddPayLimit (ap %+v) error(%+v)", ap, err)
return
}
if err = s.checkPayWithOrder(c, ap.Porder, ap.Pay, ap.OrderID, ap.Mid); err != nil {
log.Error("s.checkAddPayWithOrder (ap %+v) error(%+v)", ap, err)
return
}
//如果其他端都不传付费信息,那么就用现在有的来进行最后覆盖
if ap.Pay == nil {
ap.UgcPay = a.UgcPay
return
}
updateDeadLine := xtime.Time(a.PTime.Time().AddDate(0, 0, s.c.UgcPayAllowEditDays).Unix())
if ap.Pay != nil &&
a.CTime != a.PTime &&
a.State != archive.StateForbidRecicle &&
xtime.Time(time.Now().Unix()) < updateDeadLine {
log.Warn("checkEditPay ap.Pay.RefuseUpdate updateDeadLine (%+v)|(%+v)|(%+v)", a.Aid, a.CTime, a.PTime)
ap.Pay.RefuseUpdate = true
}
}
return
}

View File

@@ -0,0 +1,419 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/videoup/model/archive"
accapi "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
"strings"
"sync"
"unicode/utf8"
)
// checkAddStaff 新增稿件:检查联合投稿参数
func (s *Service) checkAddStaff(c context.Context, ap *archive.ArcParam, mid int64, ip string) (err error) {
var (
_logs []string
_logs2 []string
)
defer func() {
logStr := strings.Join(_logs, "\n")
if err != nil {
log.Error("s.checkAddStaff ap: %+v \nmid: %d \nlogs\n:%s", ap, mid, logStr)
} else {
log.Info("s.checkAddStaff ap: %+v \nmid: %d \nlogs\n%s", ap, mid, logStr)
}
}()
ap.HandleStaff = false
//新增稿件时如果没填写Staffs则不是联合投稿跳过检查
if len(ap.Staffs) == 0 {
ap.Staffs = make([]*archive.Staff, 0)
_logs = append(_logs, "INFO:非联合投稿,忽略")
return
}
//如果不是自制稿件不允许传Staffs
if ap.Copyright != archive.CopyrightOriginal && len(ap.Staffs) != 0 {
_logs = append(_logs, "ERR非自制稿件不允许传Staffs。")
err = ecode.VideoupStaffCopyright
return
}
//非全量的情况下检查当前UP主是在白名单中
_logs = append(_logs, fmt.Sprintf("INFO灰度开关 %v。分区配置 %v 注意这里打出的数据可能不是最新的有可能后面重新Load了", s.staffGary, s.staffTypeCache))
if err = s.checkStaffGray(ap.TypeID, mid); err != nil {
_logs = append(_logs, fmt.Sprintf("INFOUP主(%d)不在灰度名单中。", mid))
return
}
_logs2, err = s.checkStaffData(c, ap.TypeID, ap.Staffs, []*archive.Staff{}, ip)
_logs = append(_logs, _logs2...)
if err != nil {
return
}
//检查拉黑情况
_logs2, _, err = s.checkStaffRelation(c, mid, ap.Staffs, ip)
_logs = append(_logs, _logs2...)
if err != nil {
return
}
ap.HandleStaff = true
_logs = append(_logs, "INFO:通过")
return
}
// checkEditStaff 稿件编辑:检查联合投稿相关参数
func (s *Service) checkEditStaff(c context.Context, ap *archive.ArcParam, mid int64, a *archive.Archive, ip string) (err error) {
var (
cStaffs []*archive.Staff //当前稿件的联合投稿人
diffStaffs []*archive.Staff //变更的联合投稿人
_logs []string
_logs2 []string
)
defer func() {
logStr := strings.Join(_logs, "\n")
if err != nil {
log.Error("s.checkEditStaff ap: %+v \nmid: %d \narchive:%+v \nlogs:\n%v", ap, mid, a, logStr)
} else {
log.Info("s.checkEditStaff ap: %+v \nmid: %d \narchive:%+v \nlogs\n%v", ap, mid, a, logStr)
}
}()
ap.HandleStaff = false
//如果UP主不在白名单中不修改Staff数据
_logs = append(_logs, fmt.Sprintf("INFO灰度开关 %v分区配置 %v 注意这里打出的数据可能不是最新的有可能后面重新Load了", s.staffGary, s.staffTypeCache))
if err = s.checkStaffGray(ap.TypeID, mid); err != nil {
_logs = append(_logs, fmt.Sprintf("INFO:UP主(%d)不在灰度中", mid))
//如果UP主不在白名单中则将staff置空不修改staff
ap.Staffs = []*archive.Staff{}
ap.HandleStaff = false
err = nil
return
}
//获取当前稿件的Staff信息
if cStaffs, err = s.arc.ApplyStaffs(c, a.Aid, ip); err != nil {
_logs = append(_logs, fmt.Sprintf("ERR:获取线上Staffs失败err:%v", err))
return
}
if len(ap.Staffs) == 0 { //删除Staffs
_logs2, _, err = s.checkStaffRelation(c, mid, cStaffs, ip)
_logs = append(_logs, _logs2...)
if err != nil {
return
}
_logs = append(_logs, "INFO:UP主删除所有Staff")
ap.HandleStaff = true
return
}
//preEdit()的时候已经加上了以下"移区"、"换类型"的判断逻辑,这里冗余一下
//联合投稿不允许自制稿件改成非自制
if a.Copyright == archive.CopyrightOriginal && ap.Copyright != archive.CopyrightOriginal && a.AttrVal(archive.AttrBitStaff) == archive.AttrYes {
_logs = append(_logs, "ERROR:联合投稿不允许自制稿件改成非自制")
err = ecode.VideoupStaffChangeCopyright
return
}
_logs2, err = s.checkStaffData(c, ap.TypeID, ap.Staffs, cStaffs, ip)
_logs = append(_logs, _logs2...)
if err != nil {
return
}
//只验证修改过的Staff信息
changes, _logs2 := s.getStaffChanges(cStaffs, ap.Staffs)
_logs = append(_logs, _logs2...)
_logs = append(_logs, fmt.Sprintf("INFO:当前Staffs%+v 提交Staffs%+v 修改的Staffs%+v", cStaffs, ap.Staffs, changes))
for _, v := range changes {
for _, m := range v {
diffStaffs = append(diffStaffs, m)
}
}
_logs2, _, err = s.checkStaffRelation(c, mid, diffStaffs, ip)
_logs = append(_logs, _logs2...)
if err != nil {
return
}
ap.HandleStaff = true
_logs = append(_logs, "INFO:通过")
return
}
// checkStaffGray
func (s *Service) checkStaffGray(typeid int16, mid int64) (err error) {
if s.staffGary {
if _, ok := s.staffUps[mid]; !ok {
log.Error("当前Up主(%d)不在联合投稿白名单中。", mid)
err = ecode.VideoupStaffAuth
return
}
var ok1, ok2 bool
_, ok1 = s.staffTypeCache[typeid]
_, ok2 = s.staffTypeCache[0]
if ok1 && s.staffTypeCache[typeid].MaxStaff <= 0 {
log.Error("当前分区(%d)在联合投稿黑名单中。", typeid)
err = ecode.VideoupStaffTypeNotExists
return
}
if !ok1 && !ok2 {
log.Error("当前分区(%d)不在联合投稿白名单中。", typeid)
err = ecode.VideoupStaffTypeNotExists
return
}
}
return
}
// checkStaffData 检查联合投稿人的数据格式全量验证不管UP主有没有编辑都会走这个验证
func (s *Service) checkStaffData(c context.Context, typeid int16, staffs, cStaffs []*archive.Staff, ip string) (_logs []string, err error) {
var (
titles []string
cards map[int64]*accapi.Card
staffMids []int64
staffMap, cStaffMap map[int64]string
maxStaff int
)
staffMap = make(map[int64]string)
cStaffMap = make(map[int64]string)
if len(cStaffs) != 0 {
for _, v := range cStaffs {
cStaffMap[v.Mid] = v.Title
}
}
//检查分区的配置
if conf, ok := s.staffTypeCache[typeid]; ok {
maxStaff = conf.MaxStaff
} else if conf, ok := s.staffTypeCache[0]; ok {
maxStaff = conf.MaxStaff
} else {
_logs = append(_logs, fmt.Sprintf("ERR分区%d不在配置里。配置%v", typeid, s.staffTypeCache))
err = ecode.VideoupStaffTypeNotExists
return
}
if maxStaff == 0 {
_logs = append(_logs, fmt.Sprintf("ERR分区%d是黑名单。配置%v", typeid, s.staffTypeCache))
err = ecode.VideoupStaffTypeNotExists
return
}
//检查Staff数量
if len(staffs) > maxStaff {
_logs = append(_logs, fmt.Sprintf("ERRStaff数量超限。最多%d传递%d", maxStaff, len(staffs)))
err = ecode.VideoupStaffCountLimit
return
}
//检查Staff职能、Mid
for i, v := range staffs {
staffs[i].Title = strings.TrimSpace(v.Title)
v.Title = staffs[i].Title
if v.Mid == 0 {
_logs = append(_logs, "ERRStaff Mid为0。")
err = ecode.VideoupStaffMidInvalid
return
}
tl := utf8.RuneCountInString(v.Title)
if tl < 2 {
_logs = append(_logs, fmt.Sprintf("ERR职能(%v)长度不合法,长度:%d。", v.Title, tl))
err = ecode.VideoupStaffTitleShort
return
}
if tl > 4 {
_logs = append(_logs, fmt.Sprintf("ERR职能(%v)长度不合法,长度:%d。", v.Title, tl))
err = ecode.VideoupStaffTitleLength
return
}
if !_staffNameReg.MatchString(v.Title) {
_logs = append(_logs, fmt.Sprintf("ERR职能(%v)字符不合法。", v.Title))
err = ecode.VideoupStaffTitleChar
return
}
//不校验未修改的职能
if cTitle, ok := cStaffMap[v.Mid]; !ok || cTitle != v.Title {
titles = append(titles, v.Title)
}
staffMap[v.Mid] = v.Title
staffMids = append(staffMids, v.Mid)
}
if len(staffMap) != len(staffs) {
_logs = append(_logs, fmt.Sprintf("ERRStaff存在重复。传递%v去重后%v", staffs, staffMap))
err = ecode.VideoupStaffMidRepeat
return
}
//职能名称敏感词
_, hit, err := s.filter.VideoMultiFilter(c, titles, ip)
if err != nil {
_logs = append(_logs, fmt.Sprintf("ERR职能敏感词接口失败。error%v", err))
return
}
if len(hit) > 0 {
_logs = append(_logs, fmt.Sprintf("ERR职能存在敏感词。敏感词%v", hit))
err = ecode.VideoupStaffTitleFilter
return
}
//Staff Mid合法性检查
if cards, err = s.acc.Cards(c, staffMids, ip); err != nil {
_logs = append(_logs, fmt.Sprintf("ERRStaff账号信息获取失败。error%v", err))
return
}
for _, v := range staffMids {
if _, ok := cards[v]; !ok {
_logs = append(_logs, fmt.Sprintf("ERRStaff Mid(%d)不存在。", v))
err = ecode.VideoupStaffMidInvalid
return
}
}
return
}
// checkStaffRelation 检查联合投稿人拉黑关系
func (s *Service) checkStaffRelation(c context.Context, mid int64, staffs []*archive.Staff, ip string) (_logs []string, blocked []*archive.Staff, err error) {
var (
mids []int64
rels map[int64]int //relations
pass = true
staffMap map[int64]*archive.Staff
cards map[int64]*accapi.Card
)
blocked = make([]*archive.Staff, 0)
staffMap = make(map[int64]*archive.Staff)
for _, v := range staffs {
mids = append(mids, v.Mid)
staffMap[v.Mid] = v
}
if rels, err = s.FRelations(c, mid, mids, ip); err != nil {
_logs = append(_logs, fmt.Sprintf("ERR获取拉黑信息失败error:%v", err))
return
}
for k, v := range rels {
if v >= 128 {
_logs = append(_logs, fmt.Sprintf("ERRStaff(%d)在黑名单中", k))
pass = false
blocked = append(blocked, staffMap[k])
continue
}
}
cards, err = s.acc.Cards(c, mids, ip)
if !pass {
if err != nil {
_logs = append(_logs, fmt.Sprintf("ERR账号(%v)信息获取失败 error:%v", mids, err))
err = ecode.Errorf(ecode.VideoupStaffBlocked, ecode.VideoupStaffBlocked.Message(), "")
return
}
var bNames []string
for _, v := range blocked {
if _, ok := cards[v.Mid]; ok {
bNames = append(bNames, cards[v.Mid].Name)
} else {
_logs = append(_logs, fmt.Sprintf("ERR账号(%d)信息获取失败 error:%v", v.Mid, err))
}
}
err = ecode.Errorf(ecode.VideoupStaffBlocked, ecode.VideoupStaffBlocked.Message(), strings.Join(bNames, "、"))
}
for _, staff := range staffs {
if _, ok := cards[staff.Mid]; !ok {
_logs = append(_logs, fmt.Sprintf("ERR账号(%d)信息获取失败 error:%v", staff.Mid, err))
err = ecode.Errorf(ecode.CreativeAccServiceErr, "参与者(%d)信息获取失败", staff.Mid)
return
}
if cards[staff.Mid].Silence == 1 {
_logs = append(_logs, fmt.Sprintf("ERRStaff Mid(%d)被封禁。", staff.Mid))
err = ecode.Errorf(ecode.VideoupStaffUpSilence, ecode.VideoupStaffUpSilence.Message(), cards[staff.Mid].Name)
return
}
}
return
}
// getStaffChanges 获取联合投稿人的变更
func (s *Service) getStaffChanges(oS, nS []*archive.Staff) (changes map[string]map[int64]*archive.Staff, _logs []string) {
var (
allS = make([]*archive.Staff, 0)
oMap = make(map[int64]*archive.Staff)
nMap = make(map[int64]*archive.Staff)
)
str := ""
for _, v := range oS {
str += fmt.Sprintf("%d %s;", v.Mid, v.Title)
oMap[v.Mid] = v
}
_logs = append(_logs, " 原Staffs"+str)
str = ""
for _, v := range nS {
str += fmt.Sprintf("%d %s;", v.Mid, v.Title)
nMap[v.Mid] = v
}
_logs = append(_logs, " 提交Staffs"+str)
changes = make(map[string]map[int64]*archive.Staff)
changes["add"] = make(map[int64]*archive.Staff)
changes["edit"] = make(map[int64]*archive.Staff)
changes["del"] = make(map[int64]*archive.Staff)
allS = append(allS, oS...)
allS = append(allS, nS...)
for _, v := range allS {
if _, ok := oMap[v.Mid]; !ok {
changes["add"][v.Mid] = v
} else if _, ok := nMap[v.Mid]; !ok {
changes["del"][v.Mid] = v
} else if oMap[v.Mid].Title != nMap[v.Mid].Title {
changes["edit"][v.Mid] = v
}
}
return
}
// FRelations 获取用户与mid的关系Relations的反向
func (s *Service) FRelations(c context.Context, mid int64, fids []int64, ip string) (res map[int64]int, err error) {
var (
g, ctx = errgroup.WithContext(c)
sm sync.RWMutex
)
res = make(map[int64]int)
for _, v := range fids {
g.Go(func() error {
var r map[int64]int
if r, err = s.acc.Relations(ctx, v, []int64{mid}, ip); err != nil {
return err
}
sm.Lock()
res[v] = r[mid]
sm.Unlock()
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("s.FRelations(%d,%v) error(%v)", mid, fids, err)
}
return
}
// checkStaffMoveType 联合投稿不允许移区和转载类型有申请的Staff时都不允许移区
func (s *Service) checkStaffMoveType(c context.Context, ap *archive.ArcParam, a *archive.Archive, ip string) (err error) {
var (
_logs []string
)
defer func() {
if err != nil {
log.Error("s.checkStaffMoveType ap: %+v, archive:%+v logs(%v)", ap, a, _logs)
} else {
log.Info("s.checkStaffMoveType ap: %+v, archive:%+v logs(%v)", ap, a, _logs)
}
}()
//如果没发生移区和修改转载类型,则直接通过
if ap.TypeID == a.TypeID && a.Copyright == ap.Copyright {
_logs = append(_logs, "INFO:没有修改分区和转载类型")
return
}
var (
cStaffs []*archive.Staff //当前稿件的联合投稿人
)
if cStaffs, err = s.arc.ApplyStaffs(c, a.Aid, ip); err != nil {
_logs = append(_logs, fmt.Sprintf("ERR:获取Staff失败。error:%v", err))
log.Error("checkStaffMoveType() 获取线上Staffs失败err:%v", err)
return
}
if len(cStaffs) != 0 {
_logs = append(_logs, fmt.Sprintf("ERR: 不允许操作。当前Staffs(%v)", cStaffs))
err = ecode.VideoupStaffChangeTypeCopyright
return
}
_logs = append(_logs, "INFO:通过")
return
}

View File

@@ -0,0 +1,123 @@
package service
import (
"bytes"
"context"
"net"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
)
// ClientAdd add archive by client.
func (s *Service) ClientAdd(c context.Context, mid int64, ap *archive.ArcParam) (aid int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
defer func() {
if err != nil && err != ecode.VideoupCanotRepeat {
s.acc.DelSubmitCache(c, ap.Mid, ap.Title)
}
}()
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
if err = s.tagsCheck(c, mid, ap.Tag, ip); err != nil {
log.Error("s.tagsCheck mid(%d) ap(%+v) error(%v)", mid, ap.Tag, err)
return
}
// pre check
if err = s.preAdd(c, mid, ap, ip, archive.UpFromWindows); err != nil {
return
}
// add
if aid, err = s.arc.Add(c, ap, ip); err != nil || aid == 0 {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealOrder(ctx, mid, aid, ap.OrderID, ip)
return nil
})
g.Go(func() error {
s.freshFavs(ctx, mid, ap, ip)
return nil
})
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, aid, mid, ip)
return nil
})
g.Wait()
return
}
// ClientEdit edit archive by client.
func (s *Service) ClientEdit(c context.Context, ap *archive.ArcParam, mid int64) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
if err = s.tagsCheck(c, mid, ap.Tag, ip); err != nil {
log.Error("s.tagsCheck mid(%d) ap(%+v) error(%v)", mid, ap.Tag, err)
return
}
var (
a = &archive.Archive{}
vs = []*archive.Video{}
)
if a, vs, err = s.arc.View(c, ap.Aid, ip); err != nil {
log.Error("s.arc.View err(%v) | aid(%d) ip(%s)", err, ap.Aid, ip)
return
}
if a == nil {
log.Error("s.arc.View(%d) not found", mid)
err = ecode.ArchiveNotExist
return
}
if nvsCnt := s.checkVideosMaxLimitForEdit(vs, ap.Videos); nvsCnt > s.c.MaxAddVsCnt {
log.Error("checkVideosMaxLimitForEdit, vsCnt(%d), limit(%d), nvsCnt(%d)", len(vs), s.c.MaxAddVsCnt, nvsCnt)
err = ecode.VideoupVideosMaxLimit
return
}
// pre check
if err = s.preEdit(c, mid, a, vs, ap, ip, archive.UpFromWindows); err != nil {
return
}
// edit
if err = s.arc.Edit(c, ap, ip); err != nil {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, ap.Aid, mid, ip)
return nil
})
g.Wait()
return
}
// ClientUpCover client upload cover.
func (s *Service) ClientUpCover(c context.Context, fileType string, body []byte, mid int64) (url string, err error) {
if len(body) == 0 {
err = ecode.FileNotExists
return
}
if len(body) > s.c.Bfs.MaxFileSize {
err = ecode.FileTooLarge
return
}
url, err = s.bfs.Upload(c, fileType, bytes.NewReader(body))
if err != nil {
log.Error("s.bfs.Upload error(%v)", err)
}
return
}

View File

@@ -0,0 +1,191 @@
package service
import (
"bytes"
"context"
"go-common/library/net/metadata"
"hash/crc32"
"net"
"strconv"
"strings"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// CreatorEdit edit archive by creator.
func (s *Service) CreatorEdit(c context.Context, mid int64, cp *archive.CreatorParam) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, cp, err)
return
}
var (
a = &archive.Archive{}
vs = make([]*archive.Video, 0)
)
if a, vs, err = s.arc.View(c, cp.Aid, ip); err != nil {
log.Error("s.arc.View err(%v) | aid(%d) ip(%s)", err, cp.Aid, ip)
return
}
if a == nil || vs == nil {
log.Error("s.arc.View(%d) not found", mid)
err = ecode.ArchiveNotExist
return
}
ap := &archive.ArcParam{
Aid: cp.Aid,
Tag: cp.Tag,
Title: cp.Title,
Desc: cp.Desc,
OpenElec: cp.OpenElec,
// ------ diff values ----- //
Mid: a.Mid,
Author: a.Author,
TypeID: a.TypeID,
Cover: coverURL(a.Cover),
Copyright: a.Copyright,
NoReprint: a.NoReprint,
OrderID: a.OrderID,
Source: a.Source,
Attribute: a.Attribute,
UpFrom: archive.UpFromCreator,
DTime: a.DTime,
DescFormatID: a.DescFormatID,
Dynamic: a.Dynamic,
IPv6: net.ParseIP(ip),
MissionID: int(a.MissionID),
}
for _, vp := range vs {
ap.Videos = append(ap.Videos, &archive.VideoParam{
Title: vp.Title,
Desc: vp.Desc,
Filename: vp.Filename,
})
}
if only := onlyChangeTagArc(cp, a); only {
ap.Tag = s.removeDupTag(ap.Tag)
if !s.allowTag(ap.Tag) {
log.Error("s.allowTag mid(%d) ap.Tag(%s) tag name or number too large or Empty", mid, ap.Tag)
err = ecode.VideoupTagErr
return
}
if err = s.tagsCheck(c, mid, ap.Tag, ip); err != nil {
log.Error("s.tagsCheck mid(%d) ap(%+v) error(%v)", mid, ap.Tag, err)
return
}
if a.Tag != ap.Tag {
s.arc.TagUp(c, ap.Aid, ap.Tag, ip)
}
s.dealTag(c, mid, ap.Aid, a.Tag, ap.Tag, ip, ap.TypeID)
} else {
if err = s.preEdit(c, mid, a, vs, ap, ip, archive.UpFromCreator); err != nil {
log.Error("s.preCreatorEdit mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
if err = s.arc.Edit(c, ap, ip); err != nil {
return
}
}
s.dealElec(c, ap.OpenElec, ap.Aid, mid, ip)
return
}
// 判断是否只在这种特殊的情况下,开放浏览/待审的稿件只修改了Tag信息
func onlyChangeTagArc(cp *archive.CreatorParam, a *archive.Archive) (only bool) {
st := a.State == archive.StateForbidSubmit ||
a.State == archive.StateForbidUserDelay ||
a.State == archive.StateOpen ||
a.State == archive.StateOrange ||
a.State == archive.StateForbidWait
ch := a.Title == cp.Title &&
a.Desc == cp.Desc &&
a.Tag != cp.Tag
if st && ch {
only = true
}
return
}
// CreatorAdd add archive by creator.
func (s *Service) CreatorAdd(c context.Context, mid int64, ap *archive.ArcParam) (aid int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
defer func() {
if err != nil && err != ecode.VideoupCanotRepeat {
s.acc.DelSubmitCache(c, ap.Mid, ap.Title)
}
}()
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
// pre check
if err = s.preAdd(c, mid, ap, ip, archive.UpFromCreator); err != nil {
return
}
// add
if aid, err = s.arc.Add(c, ap, ip); err != nil || aid == 0 {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealOrder(ctx, mid, aid, ap.OrderID, ip)
return nil
})
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, ap.Aid, mid, ip)
return nil
})
g.Wait()
return
}
// CreatorUpCover creator upload cover.
func (s *Service) CreatorUpCover(c context.Context, fileType string, body []byte, mid int64) (url string, err error) {
if len(body) == 0 {
err = ecode.FileNotExists
return
}
if len(body) > s.c.Bfs.MaxFileSize {
err = ecode.FileTooLarge
return
}
url, err = s.bfs.Upload(c, fileType, bytes.NewReader(body))
if err != nil {
log.Error("s.bfs.Upload error(%v)", err)
}
return
}
// coverURL convert cover url to full url.
func coverURL(uri string) (cover string) {
if uri == "" {
//cover = "http://static.hdslb.com/images/transparent.gif"
return
}
cover = uri
if strings.Index(uri, "http://") == 0 {
return
}
if len(uri) >= 10 && uri[:10] == "/templets/" {
return
}
if strings.HasPrefix(uri, "group1") {
cover = "http://i0.hdslb.com/" + uri
return
}
if pos := strings.Index(uri, "/uploads/"); pos != -1 && (pos == 0 || pos == 3) {
cover = uri[pos+8:]
}
cover = strings.Replace(cover, "{IMG}", "", -1)
cover = "http://i" + strconv.FormatInt(int64(crc32.ChecksumIEEE([]byte(cover)))%3, 10) + ".hdslb.com" + cover
return
}

View File

@@ -0,0 +1,115 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
)
func (s *Service) dealElec(c context.Context, openElec int8, aid, mid int64, ip string) (err error) {
show, err := s.elec.ArcShow(c, mid, aid, ip)
if err != nil {
log.Error("s.elec.ArcShow(%d, %d, %d, %d) error(%v)", mid, aid, openElec, show, err)
return
}
if show != (openElec == 1) {
s.elec.ArcUpdate(c, mid, aid, openElec, ip)
}
return
}
func (s *Service) dealOrder(c context.Context, mid, aid, orderID int64, ip string) (err error) {
if orderID == 0 {
return
}
if err = s.order.BindOrder(c, mid, aid, orderID, ip); err != nil {
log.Error("s.order.ExecuteOrder mid(%d) aid(%d) orderId(%d) error(%v)", mid, aid, orderID, err)
err = ecode.VideoupOrderAPIErr
}
return
}
func (s *Service) dealTag(c context.Context, mid, aid int64, srcTag, descTag, ip string, typeID int16) (err error) {
if srcTag != descTag {
typeName := ""
if tp, ok := s.typeCache[typeID]; ok && tp != nil {
typeName = tp.Name
if tp, ok = s.typeCache[tp.PID]; ok && tp != nil {
typeName = fmt.Sprintf("%s,%s", typeName, tp.Name)
}
}
if err = s.tag.UpBind(c, mid, aid, descTag, typeName, ip); err != nil {
log.Error("s.tag.UpBind(%d, %d, %s, %s,%s) error(%d)", mid, aid, srcTag, descTag, typeName, err)
return
}
}
return
}
func (s *Service) dealWaterMark(c context.Context, mid int64, wm *archive.Watermark, ip string) (err error) {
if wm != nil {
if err = s.creative.SetWatermark(c, mid, wm.State, wm.Ty, wm.Pos, ip); err != nil {
log.Error("s.creative.SetWatermark(%d,%+v,%+v) error(%d)", mid, wm, err)
return
}
}
return
}
func (s *Service) freshFavs(c context.Context, mid int64, ap *archive.ArcParam, ip string) (err error) {
if err = s.arc.FreshFavTypes(c, mid, int(ap.TypeID)); err != nil {
log.Error("s.arc.FreshFavTypes(%d,%+v,%+v) error(%d)", mid, ap, err)
return
}
return
}
func (s *Service) uploadVideoEditInfo(c context.Context, ap *archive.ArcParam, aid, mid int64, ip string) (err error) {
ap.EmptyVideoEditInfo()
editors := make([]*archive.Editor, 0)
for _, v := range ap.Videos {
if v.Editor != nil && v.Cid > 0 {
v.Editor.UpFrom = ap.UpFrom
v.Editor.CID = v.Cid
editors = append(editors, v.Editor)
}
}
if len(editors) > 0 {
if err = s.creative.UploadMaterial(c, editors, aid, mid, ip); err != nil {
log.Error("s.creative.UploadMaterial (%+v,%d,%d,%s) error(%+v)", editors, aid, mid, ip, err)
return
}
}
return
}
func (s *Service) lotteryBind(c context.Context, lotteryID, aid, mid int64, ip string) (err error) {
ck, _ := s.dynamic.UserCheck(c, mid, ip)
if lotteryID > 0 && (ck == 1) {
if err = s.dynamic.LotteryBind(c, lotteryID, aid, mid, ip); err != nil {
log.Error("s.dynamic.LotteryBind (%+v,%d,%d,%s) error(%d)", lotteryID, aid, mid, ip, err)
return
}
}
return
}
func (s *Service) addFollowing(c context.Context, mid int64, fids []int64, upfrom int8, ip string) (err error) {
if len(fids) > 0 {
var src int
if upfrom == archive.UpFromAPPAndroid {
src = 173
} else if upfrom == archive.UpFromAPPiOS || upfrom == archive.UpFromIpad {
src = 183
} else {
src = 173
}
for _, fid := range fids {
s.acc.AddFollowing(context.Background(), mid, fid, src, ip)
}
}
return
}

View File

@@ -0,0 +1,97 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
"github.com/microcosm-cc/bluemonday"
)
var (
fieldMap = map[string]string{
"title": "稿件标题",
"source": "转载来源",
"desc": "稿件简介",
"dynamic": "推荐语",
}
)
// WebSingleFilter fn.
func (s *Service) WebSingleFilter(c context.Context, content string) (dt *archive.FilterData, hit []string, err error) {
ip := metadata.String(c, metadata.RemoteIP)
if dt, hit, err = s.filter.VideoFilter(c, content, ip); err != nil {
log.Error("s.WebSingleFilter(%s) error(%v)", content, err)
return
}
if len(hit) > 0 {
log.Warn("s.WebSingleFilter(%s) hit(%v) ip(%s)", content, hit, ip)
}
return
}
// WebFilterArcParam fn.
func (s *Service) WebFilterArcParam(c context.Context, ap *archive.ArcParam, ip string) (code int, msg string, data map[string]interface{}) {
res := make(map[string]string)
data = make(map[string]interface{})
g := &errgroup.Group{}
ctx := context.TODO()
// title
g.Go(func() error {
_, hit, _ := s.filter.VideoFilter(ctx, ap.Title, ip)
if len(hit) > 0 {
field := "title"
fmtMsg := ecode.VideoupFieldFilterForbid.Message()
res[field] = fmt.Sprintf(fmtMsg, fieldMap[field])
}
return nil
})
// source
g.Go(func() error {
_, hit, _ := s.filter.VideoFilter(ctx, ap.Source, ip)
if len(hit) > 0 {
field := "source"
fmtMsg := ecode.VideoupFieldFilterForbid.Message()
res[field] = fmt.Sprintf(fmtMsg, fieldMap[field])
}
return nil
})
// desc
g.Go(func() error {
_, hit, _ := s.filter.VideoFilter(ctx, ap.Desc, ip)
if len(hit) > 0 {
field := "desc"
fmtMsg := ecode.VideoupFieldFilterForbid.Message()
res[field] = fmt.Sprintf(fmtMsg, fieldMap[field])
}
return nil
})
// dynamic
g.Go(func() error {
_, hit, _ := s.filter.VideoFilter(ctx, ap.Dynamic, ip)
if len(hit) > 0 {
field := "dynamic"
fmtMsg := ecode.VideoupFieldFilterForbid.Message()
res[field] = fmt.Sprintf(fmtMsg, fieldMap[field])
}
return nil
})
g.Wait()
if len(res) != 0 {
code = ecode.VideoupFieldFilterForbid.Code()
msg = "当前输入包含敏感信息,请修正"
data["info"] = res
log.Warn("WebFilterArcParam res(%+v),ap(%+v),ip(%s)", res, ap, ip)
}
return
}
// xss filter
func xssFilter(content string) string {
p := bluemonday.StrictPolicy()
return p.Sanitize(content)
}

View File

@@ -0,0 +1,44 @@
package service
import (
"context"
"crypto/md5"
"encoding/hex"
"go-common/app/interface/main/videoup/model/archive"
gmdl "go-common/app/interface/main/videoup/model/geetest"
"go-common/library/log"
)
// Validate fn
func (s *Service) Validate(c context.Context, geetest *archive.Geetest, upFormStr string, mid int64) (stat bool, err error) {
if geetest == nil {
log.Error("arc param geetest is nil, mid(%d)", mid)
return
}
var res *gmdl.ValidateRes
validate := geetest.Validate
seccode := geetest.Seccode
challenge := geetest.Challenge
if len(validate) != 32 {
log.Error("s.Validate(%s,%s,%s,%d) err(validate not eq 32byte)", challenge, validate, seccode, mid)
return
}
if geetest.Success != 1 {
slice := md5.Sum([]byte(challenge))
stat = hex.EncodeToString(slice[:]) == validate
return
}
slice := md5.Sum([]byte(s.c.Geetest.PrivateKEY + "geetest" + challenge))
if hex.EncodeToString(slice[:]) != validate {
log.Error("s.Validate(%s,%s,%s,%d) err(challenge not found)", challenge, validate, seccode, mid)
return
}
res, err = s.gt.Validate(c, challenge, seccode, upFormStr, s.c.Geetest.CaptchaID, mid)
if err != nil {
log.Error("s.Validate(%s,%s,%s,%d) err(gtServer validate failed.)", challenge, validate, seccode, mid)
return
}
slice = md5.Sum([]byte(seccode))
stat = hex.EncodeToString(slice[:]) == res.Seccode
return
}

View File

@@ -0,0 +1,79 @@
package service
import (
"context"
"encoding/json"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/log"
"go-common/library/net/metadata"
"strconv"
)
type infoc struct {
Aid string `json:"aid"`
Ext2 json.RawMessage `json:"ext2"`
Ext1 json.RawMessage `json:"ext1"`
Ext3 json.RawMessage `json:"ext3"`
Mid string `json:"mid"`
Cid string `json:"cid"`
Filename string `json:"filename"`
Upfrom string `json:"upfrom"`
PicCount string `json:"pic_count"`
VideoCount string `json:"video_count"`
Build string `json:"build"`
Platform string `json:"platform"`
Device string `json:"device"`
MobiApp string `json:"mobi_app"`
// none business fields
IP string `json:"ip"`
LogID string `json:"logid"`
Name string `json:"name"`
}
// VideoInfoc fn
func (s *Service) VideoInfoc(c context.Context, ap *archive.ArcParam, ar *archive.AppRequest) (err error) {
log.Warn("infocproc begin ap(%+v) ar(%+v)", ap, ar)
ip := metadata.String(c, metadata.RemoteIP)
name := "APP投稿分P的视频和图片的计数"
logID := "001729"
for _, v := range ap.Videos {
if v.Editor == nil || v.Cid == 0 {
continue
}
infoc := &infoc{
Name: name,
Mid: strconv.FormatInt(ap.Mid, 10),
Aid: strconv.FormatInt(ap.Aid, 10),
Cid: strconv.FormatInt(v.Cid, 10),
Filename: v.Filename,
Upfrom: strconv.Itoa(int(ap.UpFrom)),
PicCount: strconv.Itoa(int(v.Editor.PicCount)),
VideoCount: strconv.Itoa(int(v.Editor.VideoCount)),
MobiApp: ar.MobiApp,
Platform: ar.Platform,
Build: ar.Build,
Device: ar.Device,
IP: ip,
LogID: logID,
}
log.Warn("infocproc create infoc ap(%+v) ar(%+v) infoc(%+v)", ap, ar, infoc)
err = s.infoc.Info(
infoc.Aid,
"",
"",
"",
infoc.Mid,
infoc.Cid,
infoc.Filename,
infoc.Upfrom,
infoc.PicCount,
infoc.VideoCount,
infoc.Build,
infoc.Platform,
infoc.Device,
infoc.MobiApp,
)
log.Warn("infocproc end infoc ap(%+v) ar(%+v) infoc(%+v)|err(%+v)", ap, ar, infoc, err)
}
return
}

View File

@@ -0,0 +1,50 @@
package service
import (
"fmt"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/log"
"go-common/library/queue/databus/report"
"net"
"strconv"
"time"
)
// SendArchiveLog send to log service
func (s *Service) SendArchiveLog(aid int64, build, buvid, action, platform string, ap *archive.ArcParam, videoerr error) {
var (
LogType = archive.LogTypeSuccess
index = []interface{}{ap.TypeID, ap.UpFrom, ap.OrderID, ap.Title, ap.BizFrom, strconv.Itoa(ap.MissionID), fmt.Sprint(videoerr)}
)
if videoerr != nil {
LogType = archive.LogTypeFail
}
buildNum, _ := strconv.Atoi(build)
ip := net.IP(ap.IPv6[:]).String()
uInfo := &report.UserInfo{
Mid: ap.Mid,
Platform: platform,
Build: int64(buildNum),
Buvid: buvid,
Business: archive.ArchiveAddLogID,
Type: LogType,
Oid: aid,
Action: action,
Ctime: time.Now(),
Index: index,
IP: ip,
}
ap.Aid = aid
// es 最多存32k,我们日志就最多记100p吧
if len(ap.Videos) > 100 {
ap.Videos = ap.Videos[:100]
}
uInfo.Content = map[string]interface{}{
"content": ap,
"errmsg": videoerr,
}
report.User(uInfo)
log.Info("sendLog data(%+v)", uInfo)
}

View File

@@ -0,0 +1,124 @@
package service
import (
"context"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/queue/databus/report"
"time"
)
// dealAddPay fn
func (s *Service) dealAddPay(c context.Context, pay *archive.Pay, aid, mid int64, ip string) (err error) {
// step 1: call API
if err = s.pay.AssReg(c, mid, aid, pay.Price, ip); err != nil {
log.Error("s.pay.AssReg mid(%d)|aid(%d)|pay(%+v) error(%v)", mid, aid, pay, err)
return
}
// step 2: add protocol user log
var index = []interface{}{mid, pay.ProtocolID}
uInfo := &report.UserInfo{
Mid: mid,
Business: archive.UgcpayAddarcProtocol,
Type: 1,
Oid: aid,
Action: "add",
Ctime: time.Now(),
IP: ip,
Index: index,
}
uInfo.Content = map[string]interface{}{
"content": pay,
"protocol_id": pay.ProtocolID,
"mid": mid,
}
report.User(uInfo)
log.Warn("sendLog dealAddPay protocol info (%+v)", uInfo)
return
}
// dealAdjustPay fn
func (s *Service) dealAdjustPay(c context.Context, pay *archive.Pay, aid, mid int64, ip string) (err error) {
// step 1: call API
if err = s.pay.AssReg(c, mid, aid, pay.Price, ip); err != nil {
log.Error("s.pay.AssReg mid(%d)|aid(%d)|pay(%+v) error(%v)", mid, aid, pay, err)
return
}
// step 2: add protocol user log
var index = []interface{}{mid, pay.ProtocolID}
uInfo := &report.UserInfo{
Mid: mid,
Business: archive.UgcpayAddarcProtocol,
Type: 1,
Oid: aid,
Action: "edit",
Ctime: time.Now(),
IP: ip,
Index: index,
}
uInfo.Content = map[string]interface{}{
"content": pay,
"protocol_id": pay.ProtocolID,
"mid": mid,
}
report.User(uInfo)
log.Warn("sendLog dealAdjustPay protocol info (%+v)", uInfo)
return
}
// 参与UGC付费之前必须接受当前最新的投稿协议
func (s *Service) checkPayProtocol(c context.Context, pay *archive.Pay, mid int64) (err error) {
if pay != nil {
accept, _ := s.pay.UserAcceptProtocol(c, pay.ProtocolID, mid)
if accept {
return
}
if !accept && pay.ProtocolAccept == 0 {
log.Error("s.rejectUgcProtocolBefore (%+v),(%+v)", pay, err)
err = ecode.VideoupPayProtocolLimit
return
}
}
return
}
// 付费提交校验
func (s *Service) checkPayLimit(c context.Context, ap *archive.ArcParam) (err error) {
pay := ap.Pay
if pay != nil {
if _, ok := s.exemptUgcPayUps[ap.Mid]; !ok { // 用户灰度
log.Error("s.checkAddPayLimit VideoupPayUserNotAllow (%d),(%+v)", ap.Mid, err)
err = ecode.VideoupPayUserNotAllow
return
}
if pay.Open == 1 {
if ap.Copyright != archive.CopyrightOriginal { // 创作类型
log.Error("s.checkAddPayLimit VideoupPayCopyrightErr (%+v),(%+v)", ap, err)
err = ecode.VideoupPayCopyrightErr
return
}
if pay.Price > 1000 || pay.Price < 1 { //开启之后的定价必须合理有效
log.Error("s.checkAddPayLimit VideoupPayPriceErr (%+v),(%+v)", ap, err)
err = ecode.VideoupPayPriceErr
return
}
ap.UgcPay = 1
} else {
ap.UgcPay = 0
}
}
return
}
// 一起检测是否和商单以及私单冲突,只能三选一
func (s *Service) checkPayWithOrder(c context.Context, porder *archive.Porder, pay *archive.Pay, orderID, mid int64) (err error) {
joinPorder := porder != nil && porder.FlowID > 0
joinPay := pay != nil && pay.Open == 1
if joinPay && (joinPorder || orderID > 0) {
log.Error("s.checkAddPayWithOrder VideoupPayCommericalLimit (%d)|(%+v)|(%+v)|(%+v),(%+v)", mid, porder, pay, orderID, err)
err = ecode.VideoupPayCommericalLimit
return
}
return
}

View File

@@ -0,0 +1,338 @@
package service
import (
"context"
"go-common/app/interface/main/videoup/model/archive"
accapi "go-common/app/service/main/account/api"
"go-common/library/ecode"
"go-common/library/log"
xtime "go-common/library/time"
"html"
"strings"
"time"
)
func (s *Service) preMust(c context.Context, mid int64, ap *archive.ArcParam, ip string, upFrom int8) (err error) {
// title xss filter
originTitleLen := len(ap.Title)
ap.Title = html.UnescapeString(xssFilter(ap.Title))
if len(ap.Title) != originTitleLen {
log.Warn("ap.Title inject by xss:mid(%d)|ip(%d)", mid, ip)
}
// check videos
if upFrom != archive.UpFromAPP {
if err = s.checkVideos(c, ap); err != nil {
log.Error("s.checkVideos mid(%d) ap.TypeID(%d) err(%+v)", mid, ap.TypeID, err)
return
}
}
// check archive
if !s.allowType(ap.TypeID) {
log.Error("s.allowType mid(%d) ap.TypeID(%d) typeid not exists", mid, ap.TypeID)
err = ecode.VideoupTypeidErr
return
}
if !s.allowCopyright(ap.Copyright) {
log.Error("s.allowCopyright mid(%d) ap.Copyright(%d) no legal copyright", mid, ap.Copyright)
err = ecode.VideoupCopyrightErr
return
}
ap.Tag = s.removeDupTag(ap.Tag)
if !s.allowTag(ap.Tag) {
log.Error("s.allowTag mid(%d) ap.Tag(%s) tag name or number too large or Empty", mid, ap.Tag)
err = ecode.VideoupTagErr
return
}
if err = s.checkVideo(ap); err != nil {
log.Error("s.checkVideo mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
var ok bool
if ap.Cover, ok = s.checkCover(ap.Cover); !ok {
log.Error("s.checkCover mid(%d) ap.Cover(%s) cover no legal", mid, ap.Cover)
err = ecode.VideoupCoverErr
return
}
if ap.Title, ok = s.checkTitle(ap.Title); !ok || ap.Title == "" {
log.Error("s.checkTitle mid(%d) ap.Title(%s) title contains legal char or is empty", err, mid, ap.Title)
err = ecode.VideoupTitleErr
return
}
if ap.Dynamic, ok = s.checkDynamicLen233(ap.Dynamic); !ok {
log.Error("s.checkDynamic err(%+v) mid(%d) ap.Dynamic(%s) contains length larger 233", err, mid, ap.Dynamic)
err = ecode.VideoupDynamicErr
return
}
if ap.Desc, ok = s.checkDesc(ap.Desc); !ok {
log.Error("s.checkDesc mid(%d) ap.Desc(%s) desc contains legal char or is empty", mid, ap.Desc)
err = ecode.VideoupDescErr
return
}
var p *accapi.Profile
if p, err = s.checkAccount(c, mid, ip); err != nil {
log.Error("s.checkAccount mid(%d) error(%v)", mid, err)
return
}
ap.Author = p.Name
if ap.Copyright == archive.CopyrightCopy {
ap.NoReprint = 0
}
// DisableVideoDesc except UpFromWindows, step 1 for all
if upFrom != archive.UpFromWindows {
for _, v := range ap.Videos {
v.Desc = ""
}
}
// 防止脏数据
if ap.Vote != nil && ap.Vote.VoteID == 0 {
ap.Vote = nil
}
return
}
func (s *Service) preOrder(c context.Context, ap *archive.ArcParam, a *archive.Archive, ip string) (err error) {
if ap.Porder != nil && ap.Porder.FlowID > 0 && ap.OrderID > 0 {
err = ecode.VideoupPvodForbidOrderAlready
return
}
if ap.OrderID < 0 {
err = ecode.VideoupOrderIDNotAllow
return
}
if ap.Aid == 0 && ap.OrderID == 0 { // NOTE: add no orderid
return
}
if ap.Aid > 0 && ap.OrderID == 0 && a.OrderID == 0 { // NOTE: edit always no orderid
return
}
if ap.Aid > 0 { // NOTE: edit had order id, not allow change
ap.OrderID = a.OrderID
ap.DTime = a.DTime
return
}
if !s.allowOrderUps(ap.Mid) {
log.Error("s.allowOrderUps mid(%d) error(%v)", ap.Mid, err)
err = ecode.VideoupUperIDNotAllow
return
}
if err = s.checkOrderID(c, ap.Mid, ap.OrderID, ip); err != nil {
return
}
var ptime xtime.Time
if ptime, err = s.order.PubTime(c, ap.Mid, ap.OrderID, ip); err != nil {
err = ecode.VideoupOrderAPIErr
return
}
if ap.Aid == 0 && int64(ptime) < time.Now().Add(2*time.Hour).Unix() {
err = ecode.VideoupLaunchTimeIllegal
return
}
ap.DTime = ptime
return
}
func (s *Service) preAdd(c context.Context, mid int64, ap *archive.ArcParam, ip string, upFrom int8) (err error) {
if ap.ForbidAddVideoType() {
err = ecode.VideoupTypeidErr
log.Error("ap.ForbidAddVideoType VideoupTypeidErr mid(%d),type(%d),err(%v) ", mid, ap.TypeID, err)
return
}
if ap.ForbidCopyrightAndTypes() {
err = ecode.VideoupCopyrightErr
log.Error("ap.ForbidCopyrightAndTypes VideoupCopyrightErr mid(%d),copyright(%d),type(%d),err(%v) ", mid, ap.Copyright, ap.TypeID, err)
return
}
if len(ap.Videos) > s.c.MaxAddVsCnt {
err = ecode.VideoupVideosMaxLimit
log.Error("ap.VideoupVideosMaxLimit current(%d), max(%d),err(%v) ", len(ap.Videos), s.c.MaxAddVsCnt, err)
return
}
if !s.allowSource(ap.Copyright, ap.Source) {
err = ecode.VideoupSourceErr
return
}
originDesc := ap.Desc
//join source and desc for CopyrightCopy with \n
if ap.Copyright == archive.CopyrightCopy && len(strings.TrimSpace(ap.Source)) > 0 {
ap.Desc = ap.Source + "\n" + ap.Desc
}
// App端允许在添加和编辑稿件的时候简介为空,但是需要区分是操作系统平台
ap.Desc = s.switchDesc(upFrom, ap.Desc)
// preMust method must be first
if err = s.preMust(c, mid, ap, ip, upFrom); err != nil {
log.Error("s.preMust mid(%d), err(%v) ", mid, err)
return
}
if !s.allowRepeat(c, mid, ap.Title) {
err = ecode.VideoupCanotRepeat
return
}
if !s.allowDelayTime(ap.DTime) {
err = ecode.VideoupDelayTimeErr
return
}
if err = s.checkMission(c, ap); err != nil {
log.Error("s.checkMission mid(%d) ap.MissionID(%d)|TypeID(%d) missionId not exists", mid, ap.MissionID, ap.TypeID)
return
}
if ap.Tag, err = s.checkMissionTag(ap.Tag, ap.MissionID); err != nil {
log.Error("s.checkMissionTag mid(%d) ap.tag(%s) ap.MissionID(%d) missionId not exists", mid, ap.Tag, ap.MissionID)
return
}
if err = s.checkDescForLength(originDesc, ap.DescFormatID, ap.TypeID, ap.Copyright); err != nil {
log.Error("s.checkDescForLength mid(%d) ap.Source(%s), apDesc(%s),ap.DescFormatID(%d) ap.Lang(%d) err(%v)", mid, ap.Source, originDesc, ap.DescFormatID, ap.Lang, err)
return
}
if err = s.preOrder(c, ap, nil, ip); err != nil {
log.Error("s.preOrder mid(%d) ap(%v), err(%v) ", mid, ap, err)
return
}
//checkPorderForAdd
if ap.Porder != nil && ap.Porder.IndustryID > 0 {
if err = s.checkPorderForAdd(c, ap, mid); err != nil {
log.Error("s.checkPorderForAdd mid(%d) ap(%v) |err(%+v)", mid, ap, err)
return
}
}
ap.NilPoiObj()
return
}
func (s *Service) switchDesc(upFrom int8, originDesc string) (resDesc string) {
resDesc = originDesc
if (upFrom == archive.UpFromAPP ||
upFrom == archive.UpFromAPPAndroid ||
upFrom == archive.UpFromIpad ||
upFrom == archive.UpFromAPPiOS) &&
len(originDesc) == 0 {
resDesc = "-"
}
return
}
func (s *Service) preEdit(c context.Context, mid int64, a *archive.Archive, vs []*archive.Video, ap *archive.ArcParam, ip string, upFrom int8) (err error) {
//检查联合投稿移区和修改转载类型
if err = s.checkStaffMoveType(c, ap, a, ip); err != nil {
return
}
if len(ap.Videos) > s.c.MaxAllVsCnt {
newErr := ecode.VideoupMaxAllVsCntLimit
err = ecode.Errorf(newErr, newErr.Message(), s.c.MaxAllVsCnt)
log.Error("MaxAllVsCnt err(%+v)|MaxAllVsCnt(%d)|mid(%d)|aid(%d)", err, s.c.MaxAllVsCnt, mid, a.Aid)
return
}
// App端允许在添加和编辑稿件的时候简介为空,但是需要区分是操作系统平台
if ap.ForbidCopyrightAndTypes() {
err = ecode.VideoupCopyrightErr
log.Error("ap.ForbidCopyrightAndTypes VideoupCopyrightErr mid(%d),copyright(%d),type(%d),err(%v) ", mid, ap.Copyright, ap.TypeID, err)
return
}
ap.Desc = s.switchDesc(upFrom, ap.Desc)
if err = s.preMust(c, mid, ap, ip, upFrom); err != nil {
log.Error("s.preMust mid(%d), err(%v) ", mid, err)
return
}
// DisableVideoDesc, except UpFromWindows step 2 for edit
if upFrom != archive.UpFromWindows {
ap.DisableVideoDesc(vs)
}
ap.TypeID, ap.Copyright, ap.Tag, ap.MissionID, ap.DescFormatID = s.protectFieldForEdit(ap, a)
//not in cache or not StateForbidRecicle
_, ok := s.missCache[ap.MissionID]
if (!ok && ap.MissionID > 0) || (a.State != archive.StateForbidRecicle) {
ap.MissionID = a.MissionID
}
if a.State == archive.StateForbidRecicle {
if err = s.checkMission(c, ap); err != nil {
log.Error("s.checkMission mid(%d) ap.MissionID(%d)|TypeID(%d) missionId not exists", mid, ap.MissionID, ap.TypeID)
return
}
}
if ap.Tag, err = s.checkMissionTag(ap.Tag, ap.MissionID); err != nil {
log.Error("s.checkMissionTag mid(%d) ap.tag(%s) ap.MissionID(%d) missionId not exists", mid, ap.Tag, ap.MissionID)
return
}
// mid check
if a.Mid != mid {
log.Error("mid(%d) is not author(%d)", mid, a.Mid)
err = ecode.ArchiveOwnerErr
return
}
// state check
if a.NotAllowUp() {
err = ecode.ArchiveBlocked
return
}
// web和新发粉版允许修改创作类型其他的都不允许
if upFrom != archive.UpFromWeb &&
upFrom != archive.UpFromAPPiOS &&
upFrom != archive.UpFromIpad &&
upFrom != archive.UpFromAPPAndroid {
ap.NoReprint = a.NoReprint
log.Info("upfrom forbid change np, np(%d)|upfrom(%+v)", a.NoReprint, upFrom)
}
// NoReprint check
if a.NoReprint == 0 && ap.NoReprint == 1 {
log.Error("notAllow set NoReprint = 1 after now's Noreprint is 0 mid(%d) ap.NoReprint(%d) a.NoReprint(%d)", mid, ap.NoReprint, a.NoReprint)
err = ecode.VideoupForbidNoreprint
return
}
// allowDelayTime check for archive which state nq -40
if a.State == archive.StateForbidUserDelay {
ap.DTime = a.DTime
} else if a.State != archive.StateOpen {
if a.DTime != ap.DTime && !s.allowDelayTime(ap.DTime) {
log.Error("s.allowDelayTime err(%+v) mid(%d) ap.Dtime(%d) must between 4h 15d", err, mid, ap.DTime)
err = ecode.VideoupDelayTimeErr
return
}
}
if err = s.preOrder(c, ap, a, ip); err != nil {
log.Error("s.preOrder mid(%d) ap(%v), err(%v) ", mid, ap, err)
return
}
// checkDescForLength
if err = s.checkDescForLength(ap.Desc, ap.DescFormatID, ap.TypeID, ap.Copyright); err != nil {
log.Error("s.checkDescForLength mid(%d) ap.Desc(%s) ap.DescFormatID(%d) err(%v)", mid, ap.Desc, ap.DescFormatID, err)
return
}
// 手动暴力禁止编辑的时候进行修改poi地理位置信息
ap.PoiObj = nil
// checkEditPay
if err = s.checkEditPay(c, ap, a, ip); err != nil {
log.Error("s.checkEditPay mid(%d) ap(%+v) a(%+v) err(%v)", mid, ap, a, err)
return
}
return
}
// protectFieldForEdit only StateForbidRecicle allow change typeID and Copyright
// 简介模板的ID暂时近期内不允许修改
func (s *Service) protectFieldForEdit(ap *archive.ArcParam, a *archive.Archive) (typeID int16, copyright int8, tag string, missionID, descFormatID int) {
if a.State == archive.StateForbidRecicle ||
a.State == archive.StateForbidSubmit ||
a.State == archive.StateForbidFixed ||
a.State == archive.StateOrange ||
a.State == archive.StateOpen {
return ap.TypeID, ap.Copyright, ap.Tag, ap.MissionID, ap.DescFormatID
}
return a.TypeID, a.Copyright, a.Tag, a.MissionID, a.DescFormatID
}
func (s *Service) removeDupTag(tagStr string) string {
result := []string{}
elements := strings.Split(tagStr, ",")
for i := 0; i < len(elements); i++ {
exists := false
for v := 0; v < i; v++ {
if elements[v] == elements[i] {
exists = true
break
}
}
if !exists {
result = append(result, elements[i])
}
}
return strings.Join(result, ",")
}

View File

@@ -0,0 +1,292 @@
package service
import (
"context"
"strings"
"time"
"go-common/app/interface/main/videoup/conf"
"go-common/app/interface/main/videoup/dao/account"
"go-common/app/interface/main/videoup/dao/archive"
"go-common/app/interface/main/videoup/dao/bfs"
"go-common/app/interface/main/videoup/dao/creative"
"go-common/app/interface/main/videoup/dao/dynamic"
"go-common/app/interface/main/videoup/dao/elec"
"go-common/app/interface/main/videoup/dao/filter"
"go-common/app/interface/main/videoup/dao/geetest"
"go-common/app/interface/main/videoup/dao/mission"
"go-common/app/interface/main/videoup/dao/order"
"go-common/app/interface/main/videoup/dao/pay"
"go-common/app/interface/main/videoup/dao/subtitle"
"go-common/app/interface/main/videoup/dao/tag"
arcmdl "go-common/app/interface/main/videoup/model/archive"
missmdl "go-common/app/interface/main/videoup/model/mission"
"go-common/app/interface/main/videoup/model/porder"
"go-common/library/log"
binfoc "go-common/library/log/infoc"
"sync"
)
// Service is archive and videos service.
type Service struct {
c *conf.Config
// dao
arc *archive.Dao
acc *account.Dao
bfs *bfs.Dao
miss *mission.Dao
elec *elec.Dao
order *order.Dao
tag *tag.Dao
creative *creative.Dao
filter *filter.Dao
gt *geetest.Dao
dynamic *dynamic.Dao
sub *subtitle.Dao
pay *pay.Dao
wg sync.WaitGroup
asyncCh chan func() error
// type cache
typeCache map[int16]*arcmdl.Type
orderUps map[int64]int64
staffUps map[int64]int64
missCache map[int]*missmdl.Mission
missTagsCache map[string]int
staffTypeCache map[int16]*arcmdl.StaffTypeConf
staffGary bool
descFmtsCache map[int16]map[int8][]*arcmdl.DescFormat
exemptIDCheckUps map[int64]int64
exemptZeroLevelAndAnswerUps map[int64]int64
exemptUgcPayUps map[int64]int64
exemptHalfMinUps map[int64]int64
PorderCfgs map[int64]*porder.Config
PorderGames map[int64]*porder.Game
infoc *binfoc.Infoc
}
// New new a service.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
arc: archive.New(c),
acc: account.New(c),
bfs: bfs.New(c),
miss: mission.New(c),
elec: elec.New(c),
order: order.New(c),
tag: tag.New(c),
creative: creative.New(c),
filter: filter.New(c),
gt: geetest.New(c),
dynamic: dynamic.New(c),
sub: subtitle.New(c),
pay: pay.New(c),
asyncCh: make(chan func() error, 100),
infoc: binfoc.New(c.AppEditorInfoc),
}
s.loadType()
s.loadMission()
s.loadOrderUps()
s.loadPorderCfgListAsMap()
s.loadPorderGameListAsMap()
s.loadExemptIDCheckUps()
s.loadDescFormatCache()
s.loadExemptZeroLevelAndAnswer()
s.loadExemptUgcPayUps()
s.loadStaffUps()
s.loadStaffConfig()
s.loadExemptHalfMinUps()
go s.loadproc()
go s.asyncproc()
return s
}
func (s *Service) asyncproc() {
s.wg.Add(1)
defer s.wg.Done()
for {
f, ok := <-s.asyncCh
if !ok {
return
}
retry(3, 1*time.Second, f)
}
}
func retry(attempts int, sleep time.Duration, callback func() error) (err error) {
for i := 0; ; i++ {
err = callback()
if err == nil {
return
}
if i >= (attempts - 1) {
break
}
time.Sleep(sleep)
log.Error("asyncproc retry retrying after error:(%+v)", err)
}
log.Error("asyncproc retry after %d attempts, last error: %s", attempts, err)
return nil
}
func (s *Service) loadType() {
tpm, err := s.arc.TypeMapping(context.TODO())
if err != nil {
log.Error("s.arc.TypeMapping error(%v)", err)
return
}
s.typeCache = tpm
}
// s.missTagsCache: missionName or first tag
func (s *Service) loadMission() {
mm, err := s.miss.Missions(context.TODO())
if err != nil {
log.Error("s.miss.Mission error(%v)", err)
return
}
s.missTagsCache = make(map[string]int)
for _, m := range mm {
if len(m.Tags) > 0 {
splitedTags := strings.Split(m.Tags, ",")
s.missTagsCache[splitedTags[0]] = m.ID
} else {
s.missTagsCache[m.Name] = m.ID
}
}
s.missCache = mm
}
func (s *Service) loadOrderUps() {
orderUps, err := s.order.Ups(context.TODO())
if err != nil {
return
}
s.orderUps = orderUps
}
// loadDescFormatCache
func (s *Service) loadDescFormatCache() {
fmts, err := s.arc.DescFormat(context.TODO())
if err != nil {
return
}
newFmts := make(map[int16]map[int8][]*arcmdl.DescFormat)
for _, d := range fmts {
if _, okTp := newFmts[d.TypeID]; !okTp {
newFmts[d.TypeID] = make(map[int8][]*arcmdl.DescFormat)
}
if _, okCp := newFmts[d.TypeID][d.Copyright]; !okCp {
newFmts[d.TypeID][d.Copyright] = []*arcmdl.DescFormat{}
}
newFmts[d.TypeID][d.Copyright] = append(newFmts[d.TypeID][d.Copyright], d)
}
log.Info("s.loadDescFormat: newFmts(%d), fmts(%d)", len(newFmts), len(fmts))
s.descFmtsCache = newFmts
}
func (s *Service) loadStaffUps() {
staffUps, err := s.arc.StaffUps(context.TODO())
if err != nil {
return
}
s.staffUps = staffUps
}
func (s *Service) loadproc() {
for {
time.Sleep(time.Duration(s.c.Tick))
s.loadType()
s.loadMission()
s.loadExemptIDCheckUps()
s.loadExemptZeroLevelAndAnswer()
s.loadExemptUgcPayUps()
s.loadExemptHalfMinUps()
s.loadDescFormatCache()
s.loadOrderUps()
s.loadPorderCfgListAsMap()
s.loadPorderGameListAsMap()
s.loadStaffUps()
s.loadStaffConfig()
}
}
func (s *Service) loadPorderCfgListAsMap() {
porderCfgList, err := s.arc.PorderCfgList(context.TODO())
if err != nil {
log.Error("s.arc.PorderCfgList error(%v)", err)
return
}
s.PorderCfgs = porderCfgList
}
func (s *Service) loadPorderGameListAsMap() {
list, err := s.arc.GameList(context.TODO())
if err != nil {
log.Error("GameList error(%v)", err)
return
}
if len(list) == 0 || list == nil {
log.Error("GameList empty error(%v)", err)
return
}
s.PorderGames = list
}
// Ping ping success.
func (s *Service) Ping(c context.Context) (err error) {
return s.arc.Ping(c)
}
// Close close resource.
func (s *Service) Close() {
s.acc.Close()
s.arc.Close()
s.infoc.Close()
close(s.asyncCh)
s.wg.Wait()
}
// loadExemptIDCheckUps
func (s *Service) loadExemptIDCheckUps() {
ups, err := s.arc.UpSpecial(context.TODO(), 8)
if err != nil {
return
}
s.exemptIDCheckUps = ups
}
// loadExemptIDCheckUps
func (s *Service) loadExemptZeroLevelAndAnswer() {
ups, err := s.arc.UpSpecial(context.TODO(), 12)
if err != nil {
return
}
s.exemptZeroLevelAndAnswerUps = ups
}
// loadExemptUgcPayUps
func (s *Service) loadExemptUgcPayUps() {
ups, err := s.arc.UpSpecial(context.TODO(), 17)
if err != nil {
return
}
s.exemptUgcPayUps = ups
}
// loadStaffConfig 加载联合投稿分区配置
func (s *Service) loadStaffConfig() {
var err error
if s.staffGary, s.staffTypeCache, err = s.arc.StaffTypeConfig(context.TODO()); err != nil {
s.staffGary = true //怕权限被放出,没有获取到 creative配置的时候默认开启了灰度策略
}
}
// loadExemptHalfMinUps
func (s *Service) loadExemptHalfMinUps() {
ups, err := s.arc.UpSpecial(context.TODO(), 25)
if err != nil {
return
}
s.exemptHalfMinUps = ups
}

View File

@@ -0,0 +1,99 @@
package service
import (
"flag"
"go-common/app/interface/main/videoup/conf"
"path/filepath"
"time"
"context"
. "github.com/smartystreets/goconvey/convey"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"testing"
"unicode/utf8"
)
var (
s *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/videoup.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
Reset(func() {})
f(s)
}
}
func Test_checkAddStaff(t *testing.T) {
var (
c = context.Background()
mid int64 = 222
sf1 = &archive.Staff{
Title: "a",
Mid: 123,
}
sf2 = &archive.Staff{
Title: "ab",
Mid: 124,
}
ap = &archive.ArcParam{
Copyright: archive.CopyrightCopy,
Staffs: []*archive.Staff{sf1, sf2},
}
err error
)
Convey("checkAddStaff", t, WithService(func(s *Service) {
err = s.checkAddStaff(c, ap, mid, "")
So(err, ShouldEqual, ecode.VideoupStaffCopyright)
}))
}
func Test_getStaffChanges(t *testing.T) {
var (
sf1 = &archive.Staff{
Title: "a",
Mid: 123,
}
sf2 = &archive.Staff{
Title: "ab",
Mid: 124,
}
sf3 = &archive.Staff{
Title: "abv",
Mid: 124,
}
os = []*archive.Staff{sf1, sf2}
ns = []*archive.Staff{sf1, sf3}
)
Convey("checkAddStaff", t, WithService(func(s *Service) {
changes, _ := s.getStaffChanges(os, ns)
So(changes, ShouldNotBeNil)
}))
}
func Test_CheckStaffReg(t *testing.T) {
var err error
v := &archive.Staff{
Title: "配音",
Mid: 123,
}
Convey("CheckStaffReg", t, WithService(func(s *Service) {
tl := utf8.RuneCountInString(v.Title)
if tl > 4 || tl == 0 {
err = ecode.VideoupStaffTitleLength
}
if !_staffNameReg.MatchString(v.Title) {
err = ecode.VideoupStaffTitleLength
}
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,17 @@
package service
import (
"context"
"go-common/app/interface/main/videoup/model/archive"
)
func (s *Service) dealSubtitle(c context.Context, sub *archive.Subtitle, aid, mid int64, ip string) (err error) {
if sub != nil {
var open bool
if sub.Open == 1 {
open = true
}
s.sub.Update(c, aid, open, sub.Lan)
}
return
}

View File

@@ -0,0 +1,214 @@
package service
import (
"bytes"
"context"
"net"
"go-common/app/interface/main/videoup/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
)
// WebAdd add archive by web.
func (s *Service) WebAdd(c context.Context, mid int64, ap *archive.ArcParam, validated bool) (aid int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
g := &errgroup.Group{}
ctx := context.TODO()
defer func() {
// VideoupCanotRepeat high level but basic
if err != nil && err != ecode.VideoupCanotRepeat && err != ecode.VideoupAddLimitHalfMin {
g.Go(func() error {
s.acc.DelSubmitCache(ctx, mid, ap.Title)
return nil
})
}
if err != nil && err != ecode.VideoupAddLimitHalfMin {
g.Go(func() error {
s.acc.DelHalfMin(ctx, mid)
return nil
})
}
}()
if !validated && !s.allowHalfMin(c, mid) {
log.Warn("VideoupAddLimitHalfMin mid(%d) ap(%+v) validated(%+v)", mid, ap, validated)
err = ecode.VideoupAddLimitHalfMin
return
}
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
if err = s.checkAddPay(c, ap, ip); err != nil {
log.Error("s.checkAddPay mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
// 检查联合投稿
if err = s.checkAddStaff(c, ap, mid, ip); err != nil {
return
}
// pre check
if err = s.preAdd(c, mid, ap, ip, archive.UpFromWeb); err != nil {
return
}
// add
if aid, err = s.arc.Add(c, ap, ip); err != nil || aid == 0 {
return
}
g.Go(func() error {
s.dealOrder(ctx, mid, aid, ap.OrderID, ip)
return nil
})
g.Go(func() error {
s.freshFavs(ctx, mid, ap, ip)
return nil
})
g.Go(func() error {
s.acc.AddHalfMin(ctx, mid)
return nil
})
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, aid, mid, ip)
return nil
})
g.Go(func() error {
s.dealSubtitle(ctx, ap.Subtitle, aid, mid, ip)
return nil
})
// same to edit go func, 当且仅当付费设置有且开启
g.Go(func() error {
if ap.Pay != nil && ap.Pay.Open == 1 && !ap.Pay.RefuseUpdate {
if err = s.dealAddPay(ctx, ap.Pay, aid, mid, ip); err != nil {
//异步重试队列
s.asyncCh <- func() error {
return s.dealAddPay(ctx, ap.Pay, aid, mid, ip)
}
}
err = nil
}
return nil
})
g.Wait()
return
}
// WebEdit edit archive by web.
func (s *Service) WebEdit(c context.Context, ap *archive.ArcParam, mid int64) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
var (
a = &archive.Archive{}
vs = []*archive.Video{}
)
if a, vs, err = s.arc.View(c, ap.Aid, ip); err != nil {
log.Error("s.arc.View err(%v) | aid(%d) ip(%s)", err, ap.Aid, ip)
return
}
if a == nil {
log.Error("s.arc.View(%d) not found", mid)
err = ecode.ArchiveNotExist
return
}
if nvsCnt := s.checkVideosMaxLimitForEdit(vs, ap.Videos); nvsCnt > s.c.MaxAddVsCnt {
log.Error("checkVideosMaxLimitForEdit, vsCnt(%d), limit(%d), nvsCnt(%d)", len(vs), s.c.MaxAddVsCnt, nvsCnt)
err = ecode.VideoupVideosMaxLimit
return
}
if err = s.checkEditStaff(c, ap, mid, a, ip); err != nil {
return
}
// pre check
if err = s.preEdit(c, mid, a, vs, ap, ip, archive.UpFromWeb); err != nil {
return
}
// edit
if err = s.arc.Edit(c, ap, ip); err != nil {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealElec(ctx, ap.OpenElec, ap.Aid, mid, ip)
return nil
})
g.Go(func() error {
s.dealSubtitle(ctx, ap.Subtitle, ap.Aid, mid, ip)
return nil
})
g.Go(func() error {
// web端60天之后无脑调价 && web端开放过在60天内不允许修改价格但是可以修改稿件其他信息
if ap.Pay != nil && ap.Pay.Open == 1 && !ap.Pay.RefuseUpdate {
if err = s.dealAdjustPay(ctx, ap.Pay, ap.Aid, mid, ip); err != nil {
//异步重试队列
s.asyncCh <- func() error {
return s.dealAdjustPay(ctx, ap.Pay, ap.Aid, mid, ip)
}
}
err = nil
}
return nil
})
g.Wait()
return
}
// WebUpCover client upload cover.
func (s *Service) WebUpCover(c context.Context, fileType string, body []byte, mid int64) (url string, err error) {
if len(body) == 0 {
err = ecode.FileNotExists
return
}
if len(body) > s.c.Bfs.MaxFileSize {
err = ecode.FileTooLarge
return
}
url, err = s.bfs.Upload(c, fileType, bytes.NewReader(body))
if err != nil {
log.Error("s.bfs.Upload error(%v)", err)
}
return
}
// WebCmAdd add archive by web client and from business order.
func (s *Service) WebCmAdd(c context.Context, mid int64, ap *archive.ArcParam) (aid int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
ap.IPv6 = net.ParseIP(ip)
defer func() {
if err != nil && err != ecode.VideoupCanotRepeat {
s.acc.DelSubmitCache(c, ap.Mid, ap.Title)
}
}()
if ap.TypeID != archive.AdvertisingTypeID {
err = ecode.VideoupTypeidErr
log.Error("ap.TypeID is not AdvertisingTypeID mid(%d),type(%d),err(%v) ", mid, ap.TypeID, err)
return
}
if err = s.checkIdentify(c, mid, ip); err != nil {
log.Error("s.CheckIdentify mid(%d) ap(%+v) error(%v)", mid, ap, err)
return
}
// pre check
if err = s.preAdd(c, mid, ap, ip, archive.UpFromCM); err != nil {
return
}
// add
if aid, err = s.arc.Add(c, ap, ip); err != nil || aid == 0 {
return
}
g := &errgroup.Group{}
ctx := context.TODO()
g.Go(func() error {
s.dealOrder(ctx, mid, aid, ap.OrderID, ip)
return nil
})
g.Wait()
return
}