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,91 @@
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",
"audit_result_test.go",
"intervs_test.go",
"playurl_test.go",
"region_test.go",
"service_test.go",
"sync_pgc_test.go",
"uplayurl_test.go",
"upper_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/admin/main/tv/conf:go_default_library",
"//app/admin/main/tv/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"arcType.go",
"arc_audit.go",
"archive.go",
"audit_result.go",
"full.go",
"intervs.go",
"label.go",
"mango.go",
"modules.go",
"order.go",
"others.go",
"panel.go",
"region.go",
"sear_inter.go",
"service.go",
"sync_pgc.go",
"uplayurl.go",
"upper.go",
"user.go",
"video.go",
"watermark.go",
],
importpath = "go-common/app/admin/main/tv/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/tv/conf:go_default_library",
"//app/admin/main/tv/dao:go_default_library",
"//app/admin/main/tv/model:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/account/model:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/time:go_default_library",
"//library/xstr:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/siddontang/go-mysql/mysql: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,75 @@
package service
import (
"fmt"
arcmdl "go-common/app/service/main/archive/api"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// loadTypes is used for gettting archive data from rpc
func (s *Service) loadTypes() (err error) {
var (
res map[int32]*arcmdl.Tp
resRel = make(map[int32][]int32)
typeReply *arcmdl.TypesReply
)
if typeReply, err = s.arcClient.Types(ctx, &arcmdl.NoArgRequest{}); err != nil {
log.Error("arcRPC loadType Error %v", err)
return
}
res = typeReply.Types
if len(res) == 0 {
log.Error("arcRPC loadType Empty")
return
}
for _, tInfo := range res {
if _, ok := resRel[tInfo.Pid]; !ok {
resRel[tInfo.Pid] = []int32{tInfo.ID}
continue
}
resRel[tInfo.Pid] = append(resRel[tInfo.Pid], tInfo.ID)
}
s.ArcTypes = res
s.arcPTids = resRel
return
}
// arcPName is used for get arc first partition with typeID(second partition)
func (s *Service) arcPName(cID int32) (name string, pid int32, err error) {
var (
c, p *arcmdl.Tp
ok bool
code = ecode.RequestErr.Code()
)
if c, ok = s.ArcTypes[cID]; !ok {
err = errors.Wrap(ecode.Int(code), fmt.Sprintf("can't find type for ID: %d ", cID))
return
}
if p, ok = s.ArcTypes[c.Pid]; !ok {
err = errors.Wrap(ecode.Int(code), fmt.Sprintf("can't find type for ID: %d, parent id: %d", cID, c.Pid))
return
}
return p.Name, c.Pid, nil
}
//Contains is used for check string in array
func (s *Service) Contains(tid int32) (contain bool) {
var (
name string
err error
)
if name, _, err = s.arcPName(tid); err != nil {
log.Warn("s.CheckArc.arcPName Tid %d, error(%v)", tid, err)
return
}
for _, v := range s.c.Cfg.PGCTypes {
if v == name {
return true
}
}
return
}

View File

@@ -0,0 +1,107 @@
package service
import (
"go-common/app/admin/main/tv/model"
arcmdl "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
func arcNormal(state int32) bool {
if state >= 0 || state == -6 { // archive can play
return true
}
return false
}
//AddArcs is used for adding archive
func (s *Service) AddArcs(aids []int64) (res *model.AddResp, err error) {
var (
valid bool
arc *model.SimpleArc
errFmt = "AddArcs %d, Error %v"
)
res = &model.AddResp{
Succ: []int64{},
Invalids: []int64{},
Exist: []int64{},
}
for _, v := range aids {
if valid, err = s.CheckArc(v); err != nil {
log.Error(errFmt, v, err)
return
}
// not valid aids
if !valid {
res.Invalids = append(res.Invalids, v)
continue
}
if arc, err = s.ExistArc(v); err != nil {
log.Error(errFmt, v, err)
return
}
// in our DB, already exist aids
if arc != nil {
res.Exist = append(res.Exist, v)
continue
}
if err = s.dao.NeedImport(v); err != nil {
log.Error(errFmt, v, err)
return
}
// added succesfully aids
res.Succ = append(res.Succ, v)
}
return
}
// CheckArc checks whether the archive is able to play and existing in Archive DB
func (s *Service) CheckArc(aid int64) (ok bool, err error) {
var (
argAid2 = &arcmdl.ArcRequest{Aid: aid}
arcReply *arcmdl.ArcReply
)
if arcReply, err = s.arcClient.Arc(ctx, argAid2); err != nil {
if ecode.NothingFound.Equal(err) { // archive not found at all
err = nil
return
}
log.Error("s.arcRPC.Archive3(%v) error(%v)", argAid2, err)
return
}
arc := arcReply.Arc
if s.Contains(arc.TypeID) { // filter pgc types
ok = false
return
}
if arc.Copyright != 1 {
ok = false
return
}
if arc.Rights.UGCPay == archive.AttrYes {
ok = false
return
}
if arcNormal(arc.State) {
ok = true
}
return
}
// ExistArc checks whether the archive is already in our TV DB, which means no need to import again
func (s *Service) ExistArc(aid int64) (res *model.SimpleArc, err error) {
var arc = model.SimpleArc{}
if err = s.DB.Where("aid = ?", aid).Where("deleted = ?", 0).First(&arc).Error; err != nil {
if err == gorm.ErrRecordNotFound {
err = nil
res = nil
return
}
log.Error("ExistArc DB Error %v", err)
return
}
return &arc, nil
}

View File

@@ -0,0 +1,260 @@
package service
import (
"fmt"
"context"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
const (
_arcOnline = 1
_arcOffline = 2
)
// typeBubbleSort sort type
func typeBubbleSort(pTypes []model.UgcType) (pSortTypes []model.UgcType) {
flag := true
for i := 0; i < len(pTypes)-1; i++ {
flag = true
for j := 0; j < len(pTypes)-i-1; j++ {
if pTypes[j].ID > pTypes[j+1].ID {
pTypes[j], pTypes[j+1] = pTypes[j+1], pTypes[j]
flag = false
}
}
if flag {
break
}
}
pSortTypes = pTypes
return
}
func (s *Service) existArcTps(passed bool) (existTypes map[int32]int, err error) {
var (
arcs []*model.Archive
db = s.DB.Where("deleted = ?", 0)
)
if passed {
db = db.Where("result = ?", 1)
}
existTypes = make(map[int32]int)
if err = db.Select("DISTINCT(typeid)").Find(&arcs).Error; err != nil {
log.Error("DistinctType Error %v", err)
return
}
for _, v := range arcs {
existTypes[v.TypeID] = 1
}
return
}
//arcTp return archive type list
func (s *Service) arcTp(passed bool) (pTypes []model.UgcType, err error) {
var (
cTypeList = make(map[int32][]model.UgcCType)
oriPTypes []model.UgcType
existTypes map[int32]int
)
typeList := s.ArcTypes
if existTypes, err = s.existArcTps(passed); err != nil {
return
}
//make parent and child node sperate
for _, v := range typeList {
if v.Pid == 0 {
oriPTypes = append(oriPTypes, model.UgcType{
ID: v.ID,
Name: v.Name,
})
} else {
cType := model.UgcCType{
Pid: v.Pid,
ID: v.ID,
Name: v.Name,
}
if _, ok := existTypes[v.ID]; ok {
cTypeList[v.Pid] = append(cTypeList[v.Pid], cType)
}
}
}
for _, v := range oriPTypes {
if cValue, ok := cTypeList[v.ID]; ok {
v.Children = cValue
pTypes = append(pTypes, v)
}
}
pTypes = typeBubbleSort(pTypes)
return
}
func (s *Service) loadTps() {
var (
data = &model.AvailTps{}
err error
)
if data.AllTps, err = s.arcTp(false); err != nil {
log.Error("loadTps Passed Err %v", err)
return
}
if data.PassedTps, err = s.arcTp(true); err != nil {
log.Error("loadTps All Err %v", err)
return
}
if len(data.AllTps) > 0 || len(data.PassedTps) > 0 {
s.avaiTps = data
}
}
// GetTps get cms used types data
func (s *Service) GetTps(c context.Context, passed bool) (data []model.UgcType, err error) {
if s.avaiTps == nil {
err = ecode.ServiceUnavailable
return
}
if passed {
data = s.avaiTps.PassedTps
} else {
data = s.avaiTps.AllTps
}
return
}
//GetArchivePid get archive pid with child id
func (s *Service) GetArchivePid(id int32) (pid int32) {
if value, ok := s.ArcTypes[id]; ok {
pid = value.Pid
return
}
return 0
}
func (s *Service) midTreat(param *model.ArcListParam) (mids []int64) {
if param.Mid != 0 {
return []int64{param.Mid}
}
if param.UpName != "" {
var data []*model.Upper
if err := s.DB.Where("ori_name LIKE ?", "%"+param.UpName+"%").Where("deleted = 0").Find(&data).Error; err != nil {
log.Error("ArchiveList MidTreat UpName %s, Err %v", param.UpName, err)
return
}
if len(data) > 0 {
for _, v := range data {
mids = append(mids, v.MID)
}
}
}
return
}
// ArchiveList is used for getting archive list
func (s *Service) ArchiveList(c *bm.Context, param *model.ArcListParam) (pager *model.ArcPager, err error) {
var (
archives []*model.ArcDB
reqES = new(model.ReqArcES)
data *model.EsUgcResult
aids []int64
mids []int64
upsInfo map[int64]string
)
reqES.FromArcListParam(param, s.typeidsTreat(param.Typeid, param.Pid))
reqES.Mids = s.midTreat(param)
pager = new(model.ArcPager)
if data, err = s.dao.ArcES(c, reqES); err != nil {
log.Error("ArchiveList Req %v, Err %v", param, err)
return
}
pager.Page = data.Page
if len(data.Result) == 0 {
return
}
for _, v := range data.Result {
aids = append(aids, v.AID)
mids = append(mids, v.MID)
}
if err = s.DB.Order("mtime " + reqES.MtimeSort()).Where(fmt.Sprintf("aid IN (%s)", xstr.JoinInts(aids))).Find(&archives).Error; err != nil {
log.Error("s.ArchiveList Find archives error(%v)", err)
return
}
if upsInfo, err = s.pickUps(mids); err != nil {
return
}
for _, v := range archives {
item := v.ToList(s.GetArchivePid(v.TypeID))
if name, ok := upsInfo[v.MID]; ok {
item.UpName = name
}
pager.Items = append(pager.Items, item)
}
return
}
func (s *Service) pickUps(mids []int64) (res map[int64]string, err error) {
if len(mids) == 0 {
return
}
var resSlice []*model.CmsUpper
res = make(map[int64]string, len(mids))
if err = s.DB.Where(fmt.Sprintf("mid IN (%s)", xstr.JoinInts(mids))).Where("deleted = 0").Find(&resSlice).Error; err != nil {
log.Error("pickUps Mids %v, Err %v", mids, err)
return
}
for _, v := range resSlice {
res[v.MID] = v.OriName
}
return
}
// ArcAction is used for online ugc archive
func (s *Service) ArcAction(ids []int64, action int) (err error) {
var (
w = map[string]interface{}{"deleted": 0, "result": 1}
tx = s.DB.Model(&model.Archive{}).Begin()
actValid int
)
if action == _arcOnline {
actValid = 1
} else if action == _arcOffline {
actValid = 0
} else {
return ecode.TvDangbeiWrongType
}
for _, v := range ids {
arch := model.Archive{}
if errDB := tx.Where(w).Where("id=?", v).First(&arch).Error; errDB != nil {
err = fmt.Errorf("找不到id为%v的数据", v)
log.Error("s.ArcAction First error(%v)", err)
tx.Rollback()
return
}
if errDB := tx.Where("id=?", v).
Update("valid", actValid).Error; errDB != nil {
err = errDB
log.Error("s.ArcAction Update error(%v)", err)
tx.Rollback()
return
}
}
tx.Commit()
return
}
// ArcUpdate is used for update ugc archive
func (s *Service) ArcUpdate(id int64, cover string, content string, title string) (err error) {
up := map[string]interface{}{
"cover": cover,
"content": content,
"title": title,
}
if err = s.DB.Model(&model.Archive{}).Where("id=?", id).Update(up).Error; err != nil {
log.Error("s.ArcUpdate Update error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,35 @@
package service
import (
"testing"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_CheckArc(t *testing.T) {
Convey("TestService_CheckArc Test", t, WithService(func(s *Service) {
res, err := s.CheckArc(10099763)
So(res, ShouldBeTrue)
So(err, ShouldBeNil)
}))
}
func TestService_ExistArc(t *testing.T) {
Convey("TestService_ExistArc Test", t, WithService(func(s *Service) {
res, err := s.ExistArc(12009430)
So(err, ShouldBeNil)
fmt.Println(res)
}))
}
func TestService_AddArcs(t *testing.T) {
Convey("TestService_ArchiveAdd Test", t, WithService(func(s *Service) {
res, err := s.AddArcs([]int64{
10099763, 10099764,
})
So(err, ShouldBeNil)
fmt.Println(res)
}))
}

View File

@@ -0,0 +1,247 @@
package service
import (
"net/url"
"context"
"fmt"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
"go-common/library/time"
"go-common/library/xstr"
)
// order const
const (
newOrder = 1
)
// EpResult gives the result of ep audit
func (s *Service) EpResult(req url.Values, page int, order int) (pager *model.EPResultPager, err error) {
var (
count int64
size = s.c.Cfg.AuditRSize
items []*model.EPResDB
db = s.DB.Model(&model.Content{}).
Where("tv_content.is_deleted=?", 0).
Joins("LEFT JOIN tv_ep_season ON tv_content.season_id=tv_ep_season.id").
Select("tv_content.*,tv_ep_season.title as stitle,tv_ep_season.category")
)
// order treatment
if order == newOrder {
db = db.Order("tv_content.inject_time DESC")
} else {
db = db.Order("tv_content.inject_time ASC")
}
// category treatment
if category := req.Get("category"); category != "" {
db = db.Where("tv_ep_season.category=?", category)
}
// audit status treatment
if state := req.Get("state"); state != "" {
switch state {
case "1": // passed
db = db.Where("tv_content.`state` = ?", 3)
case "2": // reject
db = db.Where("tv_content.`state` = ?", 4)
default: // waiting result
db = db.Where("tv_content.`state` NOT IN (3,4)")
}
}
// season_id treatment
if sid := req.Get("season_id"); sid != "" {
db = db.Where("tv_content.season_id=?", sid)
}
// epid treatment
if epid := req.Get("epid"); epid != "" {
db = db.Where("tv_content.epid=?", epid)
}
if err = db.Count(&count).Error; err != nil {
log.Error("Count Err %v", err)
return
}
pager = &model.EPResultPager{
Page: &model.Page{
Num: page,
Size: size,
Total: int(count),
},
}
if err = db.Offset((page - 1) * size).Limit(size).Find(&items).Error; err != nil {
return
}
// use time in string format to replace the time in number format
for _, v := range items {
pager.Items = append(pager.Items, v.ToItem())
}
return
}
//TimeFormat is used for format time
func (s *Service) TimeFormat(time time.Time) (format string) {
if time < 0 {
return ""
}
return time.Time().Format("2006-01-02 15:04:05")
}
// SeasonResult gives the result of ep audit
func (s *Service) SeasonResult(req url.Values, page int, order int) (pager *model.SeasonResultPager, err error) {
var (
count int64
size = s.c.Cfg.AuditRSize
dbTerms []*model.SeasonResDB
db = s.DB.Model(&model.TVEpSeason{}).Where("is_deleted=?", 0)
)
// order treatment
if order == newOrder {
db = db.Order("inject_time DESC")
} else {
db = db.Order("inject_time ASC")
}
// category treatment
if category := req.Get("category"); category != "" {
db = db.Where("tv_ep_season.category=?", category)
}
// audit status treatment
if state := req.Get("check"); state != "" {
switch state {
case "1": // passed
db = db.Where("tv_ep_season.`check` = ?", 1)
case "2": // reject
db = db.Where("tv_ep_season.`check` = ?", 0)
default: // waiting result
db = db.Where("tv_ep_season.`check` NOT IN (0,1)")
}
}
// season_id treatment
if sid := req.Get("season_id"); sid != "" {
db = db.Where("id=?", sid)
}
// title treatment
if title := req.Get("title"); title != "" {
db = db.Where("title LIKE ?", "%"+title+"%")
}
if err = db.Count(&count).Error; err != nil {
log.Error("db Count Err %v", err)
return
}
pager = &model.SeasonResultPager{
Page: &model.Page{
Num: page,
Size: size,
Total: int(count),
},
}
if err = db.Offset((page - 1) * size).Limit(size).Find(&dbTerms).Error; err != nil {
return
}
for _, v := range dbTerms {
pager.Items = append(pager.Items, v.ToItem())
}
return
}
func (s *Service) typeidsTreat(secondCat int32, firstCat int32) (typeids []int32) {
if secondCat != 0 { // typeid logic
typeids = []int32{secondCat}
} else if firstCat != 0 {
if secondCats, ok := s.arcPTids[firstCat]; ok && len(secondCats) > 0 {
typeids = secondCats
}
}
return
}
// ArcResult picks archive result data
func (s *Service) ArcResult(c context.Context, req *model.ReqArcCons) (data *model.ArcResPager, err error) {
if data, err = s.arcByES(req, s.typeidsTreat(req.SecondCat, req.FirstCat)); err != nil {
log.Error("arcByEs Err %v", err)
return
}
return
}
func (s *Service) arcByES(req *model.ReqArcCons, typeids []int32) (data *model.ArcResPager, err error) {
var (
esRes *model.EsUgcResult
aids []int64
arcs []*model.Archive
arcsMap = make(map[int64]*model.ArcRes)
reqES = new(model.ReqArcES)
)
reqES.FromAuditConsult(req, typeids)
if esRes, err = s.dao.ArcES(ctx, reqES); err != nil {
log.Error("UgcConsult Err %v", err)
return
}
data = &model.ArcResPager{
Page: esRes.Page,
}
if len(esRes.Result) == 0 {
return
}
for _, v := range esRes.Result {
aids = append(aids, v.AID)
}
if err = s.DB.Where(fmt.Sprintf("aid IN (%s)", xstr.JoinInts(aids))).Find(&arcs).Error; err != nil {
log.Error("arcByES DB Aids %v Err %v", aids, err)
return
}
if len(arcs) == 0 {
return
}
for _, v := range arcs {
arcsMap[v.AID] = v.ConsultRes(s.ArcTypes)
}
for _, v := range aids {
if arc, ok := arcsMap[v]; ok {
data.Items = append(data.Items, arc)
}
}
return
}
// VideoResult picks video audit consult result
func (s *Service) VideoResult(c context.Context, req *model.ReqVideoCons) (data *model.VideoResPager, err error) {
var (
videos []*model.Video
)
data = &model.VideoResPager{
Page: &model.Page{
Num: req.Pn,
Size: _pagesize,
},
}
db := s.DB.Model(&model.Video{}).Where("aid = ?", req.AVID).Where("deleted = 0")
if req.Status != "" {
db = db.Where("result = ?", req.Status)
}
if req.Title != "" {
db = db.Where("eptitle LIKE ?", "%"+req.Title+"%")
}
if req.CID != 0 {
db = db.Where("cid = ?", req.CID)
}
if err = db.Count(&data.Page.Total).Error; err != nil {
log.Error("VideoResult Count Err %v", err)
return
}
if req.Order != 1 {
db = db.Order("index_order ASC")
} else {
db = db.Order("index_order DESC")
}
if err = db.Offset((req.Pn - 1) * _pagesize).Limit(_pagesize).Find(&videos).Error; err != nil {
log.Error("arcByDB Err %v", err)
}
if len(videos) == 0 {
return
}
for _, v := range videos {
data.Items = append(data.Items, v.ConsultRes())
}
return
}

View File

@@ -0,0 +1,26 @@
package service
import (
"net/url"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_EpResult(t *testing.T) {
Convey("EPResult Test", t, WithService(func(s *Service) {
var req = url.Values{}
pager, err := s.EpResult(req, 1, 1)
So(err, ShouldBeNil)
So(len(pager.Items), ShouldBeGreaterThan, 0)
}))
}
func TestService_SeasonResult(t *testing.T) {
Convey("SeasonResult Test", t, WithService(func(s *Service) {
var req = url.Values{}
pager, err := s.SeasonResult(req, 1, 1)
So(err, ShouldBeNil)
So(len(pager.Items), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,59 @@
package service
import (
"context"
"database/sql"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
)
// FullImport .
func (s *Service) FullImport(c context.Context, build int) (result []*model.APKInfo, err error) {
result, err = s.dao.FullImport(c, build)
return
}
func (s *Service) loadSnsproc() {
for {
time.Sleep(time.Duration(s.c.Cfg.LoadSnFre))
s.loadSns(context.Background())
}
}
// loadSns loads all not deleted season Info
func (s *Service) loadSns(c context.Context) (err error) {
var (
rows *sql.Rows
sns = make(map[int64]*model.TVEpSeason)
sidCats = make(map[int][]int64)
)
if rows, err = s.DB.Model(&model.TVEpSeason{}).Where("is_deleted = 0").Select("id, title, category, state, valid, `check`").Rows(); err != nil {
log.Error("rows Err %v", err)
return
}
defer rows.Close()
for rows.Next() {
cont := &model.TVEpSeason{}
if err = rows.Scan(&cont.ID, &cont.Title, &cont.Category, &cont.State, &cont.Valid, &cont.Check); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
sns[cont.ID] = cont
if dataSet, ok := sidCats[cont.Category]; !ok {
sidCats[cont.Category] = []int64{cont.ID}
} else {
sidCats[cont.Category] = append(dataSet, cont.ID)
}
}
if err = rows.Err(); err != nil {
log.Error("rows.Err %v", err)
return
}
if len(sns) > 0 {
s.snsInfo = sns
s.snsCats = sidCats
}
return
}

View File

@@ -0,0 +1,199 @@
package service
import (
"go-common/app/admin/main/tv/model"
"go-common/library/database/sql"
"go-common/library/log"
)
// 0=not found, 1=pgc, 2=cms, 3=license
const (
ErrNotFound = 0
_TypeDefault = 0
_TypePGC = 1
_TypeUGC = 2
)
func (s *Service) getSeason(sid int64) (res *model.TVEpSeason, err error) {
sn := model.TVEpSeason{}
if err = s.DB.Where("id = ?", sid).First(&sn).Error; err != nil {
if err == sql.ErrNoRows {
err = nil
return
}
log.Error("GetSeason error(%v)\n", err)
return
}
return &sn, nil
}
// RemoveInvalids removes invalid interventions
func (s *Service) RemoveInvalids(invalids []*model.RankError) (err error) {
tx := s.DB.Begin()
for _, v := range invalids {
if err = tx.Model(&model.Rank{}).Where("id=?", v.ID).Update(map[string]int{"is_deleted": 1}).Error; err != nil {
log.Error("tvSrv.RemoveInvalids error(%v)", err)
tx.Rollback()
return
}
}
tx.Commit()
log.Info("Remove Invalid Interventions: %d", len(invalids))
return
}
// Intervs pick the intervention and combine the season data
func (s *Service) Intervs(req *model.IntervListReq) (res *model.RankList, err error) {
var (
intervs []*model.Rank
items []*model.SimpleRank
invalids []*model.RankError
db = req.BuildDB(s.DB).Order("position asc")
)
if err = db.Find(&intervs).Error; err != nil {
log.Error("[Intervs] DB query fail(%v)", err)
return
}
items, invalids = s.intervsValid(intervs)
err = s.RemoveInvalids(invalids)
res = &model.RankList{
List: items,
}
return
}
func (s *Service) intervsValid(intervs []*model.Rank) (items []*model.SimpleRank, invalids []*model.RankError) {
// check Its Season Status, pick invalid ones
for _, v := range intervs {
switch v.ContType {
case _TypePGC, _TypeDefault:
isValid, sn := s.snValid(v.ContID)
if !isValid {
invalids = append(invalids, v.BeError())
continue
}
items = append(items, v.BeSimpleSn(sn, s.pgcCatToName))
case _TypeUGC:
isValid, arc := s.arcValid(v.ContID)
if !isValid {
invalids = append(invalids, v.BeError())
continue
}
items = append(items, v.BeSimpleArc(arc, s.arcPName))
default:
log.Error("[Intervs] Rank Error Cont_Type %d, RankID:%d", v.ContType, v.ID)
continue
}
}
return
}
// snValid Distinguish whether the Season is existing and valid
func (s *Service) snValid(sid int64) (res bool, season *model.TVEpSeason) {
var err error
if season, err = s.getSeason(sid); err != nil || season == nil {
return
}
res = errTyping(int(season.Check), int(season.Valid), int(season.IsDeleted))
return
}
// arcValid Distinguish whether the archive is existing and valid
func (s *Service) arcValid(aid int64) (res bool, arc *model.SimpleArc) {
var err error
if arc, err = s.ExistArc(aid); err != nil || arc == nil {
return
}
res = errTyping(arc.Result, arc.Valid, arc.Deleted)
return
}
func errTyping(check, valid, isDeleted int) (res bool) {
if check == 1 && valid == 1 && isDeleted == 0 {
return true
}
return false
}
// RefreshIntervs is used to delete the previous interventions
func (s *Service) RefreshIntervs(req *model.IntervPubReq) (invalid *model.RankError, err error) {
var (
tx = s.DB.Begin()
txDel = req.BuildDB(tx)
position = 1
title string
)
if err = txDel.Delete(&model.Rank{}).Error; err != nil { // delete old intervs
log.Error("Del Previsou Intervs error(%v)\n", err)
tx.Rollback()
return
}
for _, v := range req.Items {
if invalid, title = s.checkInterv(req, v); invalid != nil {
tx.Rollback()
return
}
if err = tx.Create(v.BeComplete(req, title, position)).Error; err != nil { // create new ones
log.Error("Create New Intervs %v ,Error(%v)\n", v, err)
tx.Rollback()
return
}
position = position + 1
}
tx.Commit()
log.Info("RefreshIntervs Success")
return
}
// checkInterv checks whether the to-publish intervention is valid
func (s *Service) checkInterv(req *model.IntervPubReq, v *model.SimpleRank) (invalid *model.RankError, title string) {
var (
isValid bool
sn *model.TVEpSeason
arc *model.SimpleArc
rankErr = &model.RankError{
ID: int(v.ID),
SeasonID: int(v.ContID),
}
)
if req.IsIdx() {
if int(req.Category) != v.ContType+model.RankIdxBase { // if ugc, we can't accept pgc data
isValid = false
return rankErr, ""
}
}
switch v.ContType {
case _TypePGC, _TypeDefault:
isValid, sn = s.snValid(v.ContID)
if isValid && req.IsIdx() { // if index, check it's the pgc category's season
isValid = (sn.Category == int(req.Rank))
}
case _TypeUGC:
isValid, arc = s.arcValid(v.ContID)
if isValid && req.IsIdx() { // if index, check it's the first level type's archive
pid := s.GetArchivePid(arc.TypeID)
isValid = pid == int32(req.Rank)
}
default:
log.Error("[Intervs] Rank Error Cont_Type %d, RankID:%d", v.ContType, v.ID)
return rankErr, ""
}
if !isValid {
log.Error("snValid (%d) Not passed", v.ContID)
return rankErr, ""
}
if v.ContType == _TypeUGC && arc != nil {
title = arc.Title
}
if (v.ContType == _TypePGC || v.ContType == _TypeDefault) && sn != nil {
title = sn.Title
}
return
}
func (s *Service) pgcCatToName(cat int) (res string) {
if res, ok := s.pgcCatName[cat]; ok {
return res
}
return ""
}

View File

@@ -0,0 +1,59 @@
package service
import (
"fmt"
"testing"
"go-common/app/admin/main/tv/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_SeasonValidation(t *testing.T) {
Convey("Season Validation Test", t, WithService(func(s *Service) {
var season model.TVEpSeason
if err := s.DB.Model(&model.TVEpSeason{}).Where("`check`=?", 1).
Where("valid=?", 1).Where("is_deleted=?", 0).First(&season).Error; err != nil {
fmt.Printf("Error:(%v)", err)
return
}
fmt.Printf("Target ID is: %d", season.ID)
res, sModel := s.snValid(season.ID)
So(res, ShouldBeTrue)
So(sModel.ID == season.ID, ShouldBeTrue)
}))
}
func TestService_Intervs(t *testing.T) {
Convey("Get Intervention List", t, WithService(func(s *Service) {
res, err := s.Intervs(&model.IntervListReq{
Rank: 0,
Category: 1,
})
So(err, ShouldBeNil)
fmt.Println(res)
}))
}
func TestService_RemoveInvalids(t *testing.T) {
Convey("Remove Invalid Test", t, WithService(func(s *Service) {
var (
rank model.Rank
err error
invalids []*model.RankError
)
if err = s.DB.Where("is_deleted=?", 0).First(&rank).Error; err != nil {
fmt.Println(err)
return
}
invalids = append(invalids, &model.RankError{
ID: int(rank.ID),
SeasonID: int(rank.ContID),
})
err = s.RemoveInvalids(invalids)
So(err, ShouldBeNil)
// recover
err = s.DB.Model(rank).Where("id=?", rank.ID).Update(map[string]int{"is_deleted": 0}).Error
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,447 @@
package service
import (
"context"
"database/sql"
"fmt"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
"github.com/jinzhu/gorm"
)
// ugcLabels refreshes ugc labels
func (s *Service) ugcLabels() (err error) {
var firstTps = s.firstTps()
if err = s.ugcTpLabel(firstTps); err != nil {
log.Error("ugcTpLabel Err %v", err)
return
}
if err = s.ugcPubLabel(firstTps); err != nil {
log.Error("ugcPubLabel Tps %v, Err %v", firstTps, err)
}
return
}
func (s *Service) ugcPubLabel(firstTps []int32) (err error) {
var (
exist bool
cfg = s.c.Cfg.RefLabel
)
for _, v := range firstTps { // check and create pub_time label
pubtCore := &model.LabelCore{
CatType: model.UgcLabel,
Category: v,
Param: model.ParamUgctime,
Value: cfg.AllValue,
Valid: 1,
ParamName: cfg.UgcTime,
Name: cfg.AllName,
}
if exist, err = s.labelExist(pubtCore); err != nil {
return
}
if !exist {
if err = s.DB.Create(&model.LabelDB{LabelCore: *pubtCore}).Error; err != nil {
log.Error("ugcLabels Pubtime All, Create Err %v", err)
return
}
}
time.Sleep(20 * time.Millisecond)
}
return
}
func (s *Service) firstTps() (tps []int32) {
for _, v := range s.ArcTypes {
if v.Pid == 0 {
tps = append(tps, v.ID)
}
}
return
}
func (s *Service) ugcTpLabel(firstTps []int32) (err error) {
var (
exist bool
cfg = s.c.Cfg.RefLabel
)
for _, v := range firstTps {
extCore := &model.LabelCore{
CatType: model.UgcLabel,
Param: model.ParamTypeid,
Valid: 1,
Category: v,
Value: cfg.AllValue,
ParamName: cfg.UgcType,
Name: cfg.AllName,
}
if exist, err = s.labelExist(extCore); err != nil {
return
}
if !exist {
if err = s.DB.Create(&model.LabelDB{LabelCore: *extCore}).Error; err != nil {
log.Error("ugcLabels ArcTypeID %d, Create ALL Label Err %v", v, err)
return
}
}
}
for _, v := range s.ArcTypes {
if v.Pid == 0 { // if first level type, we check the "ALL" label
continue
}
extCore := &model.LabelCore{
CatType: model.UgcLabel,
Param: model.ParamTypeid,
Valid: 1,
Category: v.Pid,
Value: fmt.Sprintf("%d", v.ID),
}
if exist, err = s.labelExist(extCore); err != nil {
return
}
if !exist {
label := model.LabelDB{}
label.FromArcTp(v, s.c.Cfg.RefLabel.UgcType)
if err = s.DB.Create(&label).Error; err != nil {
log.Error("ugcLabels ArcTypeID %d, Create Err %v", v.ID, err)
return
}
time.Sleep(20 * time.Millisecond)
}
}
return
}
// labelsExist distinguishes whether the ids are existing
func (s *Service) labelsExist(ids []int64) (err error) {
var (
labels []*model.LabelDB
idmap = make(map[int64]int)
)
if err = s.DB.Where(fmt.Sprintf("id IN (%s)", xstr.JoinInts(ids))).Where("deleted = 0").Find(&labels).Error; err != nil {
log.Error("labelsExist Ids %v, Err %v", ids, err)
return
}
if len(labels) >= len(ids) {
return
}
for _, v := range labels {
idmap[v.ID] = 1
}
for _, v := range ids {
if _, ok := idmap[v]; !ok {
log.Warn("labelsExist ids %v, not exist %d", ids, v)
return ecode.RequestErr
}
}
return
}
func (s *Service) labelExist(req *model.LabelCore) (exist bool, err error) {
var (
label = model.LabelDB{}
db = s.DB.Model(label).Where("deleted = 0")
)
if req.ID != 0 {
db = db.Where("id = ?", req.ID)
}
if req.Category != 0 {
db = db.Where("category = ?", req.Category)
}
if req.CatType != 0 {
db = db.Where("cat_type = ?", req.CatType)
}
if req.Param != "" {
db = db.Where("param = ?", req.Param)
}
if req.Value != "" {
db = db.Where("value = ?", req.Value)
}
if req.Name != "" {
db = db.Where("name = ?", req.Name)
}
if err = db.First(&label).Error; err != nil {
if err == gorm.ErrRecordNotFound {
err = nil
return
}
log.Error("labelExist V %v, Err %v", req, err)
return
}
if label.ID > 0 {
exist = true
}
return
}
func (s *Service) pgcLabels() (err error) {
var (
result *model.PgcCond
exist bool
)
for _, cat := range s.c.Cfg.SupportCat.PGCTypes {
if result, err = s.dao.PgcCond(context.Background(), cat); err != nil {
log.Error("PgcCond Cat %d, Err %v", cat, err)
return
}
for _, cond := range result.Filter {
if len(cond.Value) == 0 {
continue
}
for _, v := range cond.Value {
if exist, err = s.labelExist(&model.LabelCore{
CatType: model.PgcLabel,
Category: cat,
Param: cond.ID,
Value: v.ID,
}); err != nil {
return
}
if exist {
continue
}
label := model.LabelDB{}
label.FromPgcCond(v, cond, cat)
if err = s.DB.Create(&label).Error; err != nil {
log.Error("pgcLabels Param %s, Cond %v Create Err %v", cond.ID, v, err)
return
}
time.Sleep(20 * time.Millisecond)
}
}
}
return
}
// AddUgcTm adds time label for ugc
func (s *Service) AddUgcTm(tm *model.UgcTime) (err error) {
var (
exist bool
timeV = tm.TimeV()
)
if exist, err = s.labelExist(&model.LabelCore{
Category: tm.Category,
Param: model.ParamUgctime,
Name: tm.Name,
}); err != nil {
return
}
if exist {
err = ecode.TvLabelExist
return
}
label := model.LabelDB{}
label.FromUgcTime(tm, s.c.Cfg.RefLabel.UgcTime)
if err = s.DB.Create(&label).Error; err != nil {
log.Error("ugcTimeLabel Time %s, Create Err %v", timeV, err)
}
return
}
// EditUgcTm edits a time label by name
func (s *Service) EditUgcTm(tm *model.EditUgcTime) (err error) {
var exist bool
if exist, err = s.labelExist(&model.LabelCore{
ID: tm.ID,
}); err != nil {
return
}
if !exist {
err = ecode.NothingFound
return
}
if err = s.DB.Model(&model.LabelDB{}).Where("id = ?", tm.ID).Update(map[string]string{
"name": tm.Name,
"value": tm.TimeV(),
}).Error; err != nil {
log.Error("ugcTimeLabel LabelID %d, Update Err %v", tm.ID, err)
}
return
}
// ActLabels act on labels
func (s *Service) ActLabels(ids []int64, act int) (err error) {
if err = s.labelsExist(ids); err != nil {
return
}
if err = s.DB.Model(&model.LabelDB{}).Where(fmt.Sprintf("id IN (%s)", xstr.JoinInts(ids))).Update(map[string]int{
"valid": act,
}).Error; err != nil {
log.Error("ActLabels LabelID %s, Update Err %v", ids, err)
}
return
}
// DelLabels deletes labels
func (s *Service) DelLabels(ids []int64) (err error) {
var exist bool
for _, v := range ids {
if exist, err = s.labelExist(&model.LabelCore{
ID: v,
CatType: model.UgcLabel,
Param: model.ParamUgctime,
}); err != nil {
return
}
if !exist {
log.Warn("DelLabels IDs %v, ID not exist %d", ids, v)
return ecode.RequestErr
}
}
if err = s.DB.Exec(fmt.Sprintf("UPDATE tv_label SET deleted = 1 WHERE id IN (%s)", xstr.JoinInts(ids))).Error; err != nil {
log.Error("DelLabels LabelID %s, Update Err %v", ids, err)
}
return
}
// DynamicLabels picks the defined pgc label types
func (s *Service) labelTypes() (tps []*model.TpLabel, err error) {
var rows *sql.Rows
// select category, param , param_name from tv_label where deleted = 0 and cat_type = 1 group by category,param
if rows, err = s.DB.Model(&model.LabelDB{}).Where("deleted = 0").
Where("cat_type = ?", model.PgcLabel).Select("category, param, param_name").Group("category,param").Rows(); err != nil {
log.Error("labelTypes rows Err %v", err)
return
}
defer rows.Close()
for rows.Next() {
var cont = &model.TpLabel{}
if err = rows.Scan(&cont.Category, &cont.Param, &cont.ParamName); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
tps = append(tps, cont)
}
if err = rows.Err(); err != nil {
log.Error("labelTypes rows Err %v", err)
}
return
}
func (s *Service) loadLabel() (err error) {
var (
newTps = make(map[int][]*model.TpLabel)
labels []*model.TpLabel
)
if labels, err = s.labelTypes(); err != nil {
log.Error("labelTypes err %v", err)
time.Sleep(time.Duration(10 * time.Second))
return
}
for _, v := range labels {
if lbs, ok := newTps[v.Category]; ok {
newTps[v.Category] = append(lbs, v)
} else {
newTps[v.Category] = append([]*model.TpLabel{}, v)
}
}
if len(newTps) > 0 {
s.labelTps = newTps
}
return
}
// LabelTp returns the category's label types
func (s *Service) LabelTp(category int) (lbs []*model.TpLabel, err error) {
var ok bool
if lbs, ok = s.labelTps[category]; !ok {
err = ecode.RequestErr
}
return
}
// PickLabels picks ugc labels
func (s *Service) PickLabels(req *model.ReqLabel, catType int) (data []*model.LabelList, err error) {
var (
db = s.DB.Model(&model.LabelDB{}).
Where("param = ?", req.Param).
Where("category = ?", req.Category).
Where("deleted = 0").
Where("cat_type = ?", catType)
labels []*model.LabelDB
)
if req.Title != "" {
db = db.Where("name LIKE ?", "%"+req.Title+"%")
}
if req.ID != 0 {
db = db.Where("id = ?", req.ID)
}
if err = db.Order("position ASC").Find(&labels).Error; err != nil {
log.Error("PickLabels Req %v, Err %v", req, err)
}
for _, v := range labels {
data = append(data, v.ToList())
}
return
}
// EditLabel edits a pgc label
func (s *Service) EditLabel(id int64, name string) (err error) {
var exist bool
if exist, err = s.labelExist(&model.LabelCore{
ID: id,
}); err != nil {
return
}
if !exist {
err = ecode.NothingFound
return
}
if err = s.DB.Model(&model.LabelDB{}).Where("id = ?", id).Update(map[string]string{
"name": name,
}).Error; err != nil {
log.Error("EditLabel LabelID %d, Update Err %v", id, err)
}
return
}
// PubLabel publish label's order
func (s *Service) PubLabel(ids []int64) (err error) {
if len(ids) == 0 {
return
}
var (
labels []*model.LabelDB
position = 1
tx = s.DB.Begin()
labelMap = make(map[int64]*model.LabelDB, len(ids))
)
if err = s.DB.Where(fmt.Sprintf("id IN (%s)", xstr.JoinInts(ids))).Find(&labels).Error; err != nil {
log.Error("PubLabel Ids %v, Err %v", ids, err)
return
}
if len(labels) == 0 {
err = ecode.NothingFound
return
}
for _, v := range labels {
labelMap[v.ID] = v
}
for _, id := range ids {
lbl, ok := labelMap[id]
if !ok {
err = ecode.RequestErr
log.Warn("PubLabel Id %d Not found", id)
return
}
if !lbl.SameType(labels[0]) {
log.Error("PubLabel Id %d FirstLabel ID %d, Not same type", lbl.ID, labels[0].ID)
err = ecode.RequestErr
tx.Rollback()
return
}
if err = tx.Model(&model.LabelDB{}).Where("id = ?", lbl.ID).Update(map[string]int{"position": position}).Error; err != nil {
log.Error("PubLabel ID %d, Err %v", lbl.ID, err)
tx.Rollback()
return
}
position = position + 1
}
tx.Commit()
return
}

View File

@@ -0,0 +1,205 @@
package service
import (
"fmt"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/pkg/errors"
)
// recomExist checks whether the recom exist or not
func (s *Service) recomExist(id int64) (exist bool) {
var recom model.MangoRecom
if err := s.DB.Where("id = ?", id).Where("deleted = 0").Find(&recom).Error; err != nil {
log.Error("[recomExist] ID %d, Err %v", id, err)
return
}
if recom.ID != 0 {
return true
}
return
}
func (s *Service) resExist(rid int64, rtype int) (exist bool) {
var recom model.MangoRecom
if err := s.DB.Where("rid = ?", rid).Where("rtype = ?", rtype).Where("deleted = 0").Find(&recom).Error; err != nil {
log.Error("[resExist] rid %d, Err %v", rid, err)
return
}
if recom.ID != 0 {
return true
}
return
}
// MangoList picks the mango recom list data
func (s *Service) MangoList(c *bm.Context) (data *model.MangoListResp, err error) {
var (
recoms []*model.MangoRecom
msg = s.c.Cfg.MangoErr
invalids []string
mre *model.MRecomMC
)
data = &model.MangoListResp{List: make([]*model.MangoRecom, 0)}
if err = s.DB.Where("deleted = ?", 0).Order("`rorder` ASC").Find(&recoms).Error; err != nil {
log.Error("[MangoList] DB query fail(%v)", err)
return
}
for _, v := range recoms { // check whether the archive or the season is still valid, otherwise we delete it and remind the user
if v.Rtype == _TypePGC {
if ok, _ := s.snValid(v.RID); ok {
data.List = append(data.List, v)
} else {
invalids = append(invalids, fmt.Sprintf("p%d", v.RID))
s.MangoDel(c, v.ID)
}
} else if v.Rtype == _TypeUGC {
if ok, _ := s.arcValid(v.RID); ok {
data.List = append(data.List, v)
} else {
invalids = append(invalids, fmt.Sprintf("u%d", v.RID))
s.MangoDel(c, v.ID)
}
} else {
log.Error("MangoList ID %d, Rid %d, Type %d, TypeError", v.ID, v.RID, v.Rtype)
invalids = append(invalids, fmt.Sprintf("%d", v.RID))
}
}
if len(invalids) > 0 {
data.Message = msg + joinStr(invalids)
}
if mre, err = s.dao.GetMRecom(c); err != nil {
log.Error("MangoList GetMRecom Err %v", err)
err = nil
return
}
data.Pubtime = mre.Pubtime.Time().Format("2006-01-02 15:04:05")
return
}
// joinStr joins strings
func joinStr(src []string) (res string) {
for k, v := range src {
if k == len(src)-1 {
res = res + v
} else {
res = res + v + ","
}
}
return
}
// MangoAdd adds the mango recom data
func (s *Service) MangoAdd(c *bm.Context, rtype int, rids []int64) (data *model.MangoAdd, err error) {
data = &model.MangoAdd{
Succ: make([]int64, 0),
Invalids: make([]int64, 0),
}
var (
succRecoms []*model.MangoRecom
newRids []int64
)
for _, v := range rids { // 检查是否存在
if ok := s.resExist(v, rtype); ok {
data.Invalids = append(data.Invalids, v)
continue
}
newRids = append(newRids, v)
}
if rtype == _TypePGC { // 检查对应的pgc和ugc是否存在并有效
for _, v := range newRids {
if ok, sn := s.snValid(v); !ok {
data.Invalids = append(data.Invalids, v)
} else {
succRecoms = append(succRecoms, sn.ToMango())
}
}
} else if rtype == _TypeUGC {
for _, v := range newRids {
if ok, arc := s.arcValid(v); !ok {
data.Invalids = append(data.Invalids, v)
} else {
var pid int32
if _, pid, err = s.arcPName(arc.TypeID); err != nil || pid == 0 {
log.Warn("MangoAdd Aid %d, TypeID %d, Err %v", v, arc.TypeID, err)
data.Invalids = append(data.Invalids, v)
continue
}
succRecoms = append(succRecoms, arc.ToMango(int(pid)))
}
}
} else {
err = ecode.TvDangbeiWrongType
return
}
if len(succRecoms) > 0 { // 选取最大顺序,在之后递增
tx := s.DB.Begin()
maxOrder := s.dao.MaxOrder(c)
for _, v := range succRecoms {
maxOrder = maxOrder + 1
v.Rorder = maxOrder
if err = tx.Create(v).Error; err != nil {
log.Error("MangoAdd Create Rid %d, Recom %v, Err %v", v.RID, v, err)
tx.Rollback()
return
}
}
tx.Commit() // add succ
for _, v := range succRecoms {
data.Succ = append(data.Succ, v.RID)
}
}
return
}
// MangoDel deletes the mango resource
func (s *Service) MangoDel(c *bm.Context, id int64) (err error) {
if !s.recomExist(id) {
return ecode.NothingFound
}
return s.dao.DelMRecom(c, id)
}
// MangoEdit edits the mango resource
func (s *Service) MangoEdit(c *bm.Context, req *model.ReqMangoEdit) (err error) {
if !s.recomExist(req.ID) {
return ecode.NothingFound
}
if err = s.DB.Model(&model.MangoRecom{}).Where("id = ?", req.ID).Update(map[string]interface{}{
"title": req.Title,
"cover": req.Cover,
"content": req.Content,
"staff": req.Staff,
"jid": req.JID,
"playcount": req.Playcount,
}).Error; err != nil {
log.Error("MangoDel ID %d, Mango %v, Err %v", req.ID, req, err)
}
return
}
// MangoPub publish the latest order of ids
func (s *Service) MangoPub(c *bm.Context, ids []int64) (err error) {
var order = 0
for _, v := range ids {
if !s.recomExist(v) {
return errors.Wrap(ecode.Int(404), fmt.Sprintf("ID: %d", v))
}
}
tx := s.DB.Begin()
for _, v := range ids {
order = order + 1
if err = tx.Model(&model.MangoRecom{}).Where("id = ?", v).Update(map[string]int{"rorder": order}).Error; err != nil {
log.Error("MangoPub ID %d, Err %v", v, err)
tx.Rollback()
return
}
}
tx.Commit()
err = s.dao.MangoRecom(c, ids)
return
}

View File

@@ -0,0 +1,295 @@
package service
import (
"fmt"
"strconv"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/jinzhu/gorm"
)
//ModulesAdd is used for add modules
func (s *Service) ModulesAdd(v *model.Modules) (err error) {
var (
order uint8
mod *model.Modules
)
if mod, err = s.isModulesExists(v.PageID, v.Title); err != nil {
return
}
if mod != nil {
err = fmt.Errorf("当前模块下,标题已存在")
return
}
if order, err = s.getOrder(v.PageID); err != nil {
return
}
//在已存在顺序上加一
v.Order = order + 1
if err = s.DB.Model(&model.Modules{}).Create(v).Error; err != nil {
return
}
return
}
//isModulesExists is use for checking is module exists
func (s *Service) isModulesExists(pageID string, title string) (v *model.Modules, err error) {
v = &model.Modules{}
w := map[string]interface{}{
"deleted": model.ModulesNotDelete,
"page_id": pageID,
"title": title,
}
if err = s.DB.Where(w).First(v).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return
}
return
}
//getOrder is used for getting existed model order
func (s *Service) getOrder(pageID string) (order uint8, err error) {
var v model.Modules
if err = s.DB.Where("deleted = 0").Where("page_id = ?", pageID).Order("`order` DESC").First(&v).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return 0, nil
}
log.Error("getOrder Err %v", err)
return
}
order = v.Order
return
}
//ModulesList is used for get module list
func (s *Service) ModulesList(pageID string) (v []*model.Modules, err error) {
selectStr := []string{
"id",
"title",
"page_id",
"source",
"type",
"flexible",
"icon",
"capacity",
"more",
"`order`",
"moretype",
"morepage",
"valid",
"src_type",
}
w := map[string]interface{}{
"deleted": model.ModulesNotDelete,
"page_id": pageID,
}
if err = s.DB.Where(w).Select(selectStr).Order("`order` ASC").Find(&v).Error; err != nil {
return
}
for i := range v {
attr := v[i]
pid, _ := strconv.Atoi(attr.PageID)
switch pid {
case model.PageMain:
attr.PageID = "主页"
case model.PageJP:
attr.PageID = "番剧"
case model.PageMovie:
attr.PageID = "电影"
case model.PageDocumentary:
attr.PageID = "纪录片"
case model.PageCN:
attr.PageID = "国创"
case model.PageSoapopera:
attr.PageID = "电视剧"
}
t, _ := strconv.Atoi(attr.Type)
switch t {
case model.TypeSevenFocus:
attr.Type = "首页七格焦点图"
case model.TypeFiveFocus:
attr.Type = "5格焦点"
case model.TypeSixFocus:
attr.Type = "6格焦点"
case model.TypeVertListFirst:
attr.Type = "竖图1列表"
case model.TypeVertListSecond:
attr.Type = "竖图2列表"
case model.TypeHorizList:
attr.Type = "横图列表"
case model.TypeZhuiFan:
attr.Type = "追番模块"
}
}
return
}
//ModulesEditGet is used for get module with module id
func (s *Service) ModulesEditGet(id uint64) (v *model.Modules, err error) {
selectStr := []string{
"id",
"title",
"page_id",
"type",
"source",
"flexible",
"icon",
"capacity",
"more",
"`order`",
"moretype",
"morepage",
"src_type",
}
w := map[string]interface{}{
"id": id,
"deleted": model.ModulesNotDelete,
}
v = &model.Modules{}
if err = s.DB.Where(w).Select(selectStr).First(v).Error; err != nil {
return
}
return
}
//ModulesEditPost is used for update module value
func (s *Service) ModulesEditPost(id uint64, v *model.Modules) (err error) {
var (
mod = &model.Modules{}
)
if mod, err = s.isModulesExists(v.PageID, v.Title); err != nil {
return
}
if mod != nil && mod.ID != id {
err = fmt.Errorf("当前模块下,标题已存在")
return
}
return s.DB.Model(&model.Modules{}).Where("id = ?", id).Update(v).Error
}
//GetModPub is used for get publish status from MC
func (s *Service) GetModPub(c *bm.Context, pageID string) (p model.ModPub, err error) {
return s.dao.GetModPub(c, pageID)
}
//ModulesPublish is used for publish module or deleted modules
func (s *Service) ModulesPublish(c *bm.Context, pageID string, state uint8, ids []int, deletedIds []int) (err error) {
if len(ids) > 30 {
err = fmt.Errorf("模块发布不能超过30个")
return
}
tx := s.DB.Begin()
for k, v := range ids {
up := map[string]interface{}{
"order": k + 1,
"valid": model.ModulesValid,
}
where := map[string]interface{}{
"id": v,
"page_id": pageID,
}
if err = tx.Model(&model.Modules{}).Where(where).Update(up).Error; err != nil {
tx.Rollback()
return
}
}
if len(deletedIds) > 0 {
deletedUp := map[string]interface{}{
"deleted": model.ModulesDelete,
}
if err = s.DB.Model(&model.Modules{}).Where("id in (?)", deletedIds).Where("page_id=?", pageID).
Update(deletedUp).Error; err != nil {
tx.Rollback()
return
}
}
if err = s.SetPublish(c, pageID, state); err != nil {
tx.Rollback()
return
}
tx.Commit()
return
}
//SetPublish is used for set publish status
func (s *Service) SetPublish(c *bm.Context, pageID string, state uint8) (err error) {
nowTime := time.Now()
t := nowTime.Format("2006-01-02 15:04:05")
p := model.ModPub{
Time: t,
State: state,
}
return s.dao.SetModPub(c, pageID, p)
}
// TypeSupport distinguish whether the source is supported or not
func (s *Service) TypeSupport(srcType int, source int) bool {
if srcType == _TypePGC {
_, ok := s.supCatMap.PgcMap[int32(source)]
return ok
}
if srcType == _TypeUGC {
_, ok := s.supCatMap.UgcMap[int32(source)]
return ok
}
return false
}
// loadCats, reload pgc & ugc support cats
func (s *Service) loadCats() {
var (
pgcTypes = s.c.Cfg.SupportCat.PGCTypes
ugcTypes = s.c.Cfg.SupportCat.UGCTypes
newCats = []*model.ParentCat{}
newCatsMap = &model.SupCats{
UgcMap: make(map[int32]int),
PgcMap: make(map[int32]int),
}
)
// load supporting pgc types
if len(pgcTypes) > 0 {
for _, v := range pgcTypes {
newCats = append(newCats, &model.ParentCat{
ID: v,
Name: s.pgcCatToName(int(v)),
Type: _TypePGC,
})
newCatsMap.PgcMap[v] = 1
}
}
// load support ugc first level types and their children
if len(ugcTypes) > 0 {
for _, v := range ugcTypes {
newCatsMap.UgcMap[v] = 1
var ugcCat = &model.ParentCat{
ID: v,
Type: _TypeUGC,
}
if tp, ok := s.ArcTypes[v]; ok {
ugcCat.Name = tp.Name
}
for _, types := range s.ArcTypes { // gather children
if types.Pid == v {
ugcCat.Children = append(ugcCat.Children, &model.CommonCat{
ID: types.ID,
Name: types.Name,
PID: types.Pid,
Type: _TypeUGC,
})
newCatsMap.UgcMap[types.ID] = 1
}
}
newCats = append(newCats, ugcCat)
}
}
if len(newCats) > 0 {
s.SupCats = newCats
s.supCatMap = newCatsMap
}
}

View File

@@ -0,0 +1,45 @@
package service
import (
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
)
const (
_orderTable = "tv_pay_order"
)
//OrderList user tv-vip order list
func (s *Service) OrderList(mid, pn, ps, paymentStime, paymentEtime int64, status int8, orderNo string) (data *model.OrderPageHelper, err error) {
var (
db = s.dao.DB.Table(_orderTable)
orders []*model.TvPayOrderResp
)
data = &model.OrderPageHelper{}
if mid != 0 {
db = db.Where("mid = ?", mid)
}
if orderNo != "" {
db = db.Where("order_no = ?", orderNo)
}
if status != 0 {
db = db.Where("status = ?", status)
}
if paymentStime != 0 {
db = db.Where("payment_time > ?", time.Unix(paymentStime, 0))
}
if paymentEtime != 0 {
db = db.Where("payment_time < ?", time.Unix(paymentEtime, 0))
}
db.Count(&data.Total)
if err := db.Offset((pn - 1) * ps).Limit(ps).Find(&orders).Error; err != nil {
log.Error("OrderList %v, Err %v", orders, err)
}
data.Items = orders
return
}

View File

@@ -0,0 +1,177 @@
package service
import (
"context"
"database/sql"
"fmt"
"net/url"
"strconv"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
"github.com/jinzhu/gorm"
)
// Playurl new playurl function, get url from API
func (s *Service) Playurl(cid int) (playurl string, err error) {
if playurl, err = s.dao.Playurl(ctx, cid); err != nil {
log.Error("Playurl API Error(%d) (%v)", cid, err)
return
}
if playurl, err = s.hostChange(playurl); err != nil {
log.Error("hostChange Error(%s)-(%v)", playurl, err)
return
}
log.Info("NewPlayURL cid = %d, playurl = %s", cid, playurl)
return
}
// hostChange can change the url from playurl api to tvshenhe's host
func (s *Service) hostChange(playurl string) (replacedURL string, err error) {
u, err := url.Parse(playurl)
if err != nil {
log.Error("hostChange ParseURL error (%v)", err)
return
}
log.Info("[hostChange] for URL: %s, Original Host: %s, Now we change it to: %s", playurl, u.Host, s.c.Cfg.Playpath)
u.Host = s.c.Cfg.Playpath // replace the host
u.RawQuery = "" // remove useless query
replacedURL = u.String()
return
}
// Upload can upload a file object: store the info in Redis, and transfer the file to Bfs
func (s *Service) Upload(c context.Context, fileName string, fileType string, timing int64, body []byte) (location string, err error) {
if location, err = s.dao.Upload(c, fileName, fileType, timing, body); err != nil {
log.Error("s.upload.Upload() error(%v)", err)
}
return
}
// unshelveReqT treats the unshelve request to db ( for update ) and dbSel (for select )
func (s *Service) unshelveReqT(req *model.ReqUnshelve) (db, dbSel *gorm.DB, err error) {
if length := len(req.IDs); length == 0 || length > s.c.Cfg.AuditConsult.UnshelveNb {
err = ecode.RequestErr
return
}
switch req.Type {
case 1: // sid
db = s.DB.Model(&model.TVEpSeason{}).Where("is_deleted = 0").
Where(fmt.Sprintf("id IN (%s)", xstr.JoinInts(req.IDs)))
dbSel = db.Select("id")
case 2: // epid
db = s.DB.Model(&model.Content{}).Where("is_deleted = 0").
Where(fmt.Sprintf("epid IN (%s)", xstr.JoinInts(req.IDs)))
dbSel = db.Select("epid")
case 3: // aid
db = s.DB.Model(&model.Archive{}).Where("deleted = 0").
Where(fmt.Sprintf("aid IN (%s)", xstr.JoinInts(req.IDs)))
dbSel = db.Select("aid")
case 4: // cid
db = s.DB.Model(&model.Video{}).Where("deleted = 0").
Where(fmt.Sprintf("cid IN (%s)", xstr.JoinInts(req.IDs)))
dbSel = db.Select("cid")
default:
err = ecode.RequestErr
}
return
}
// Unshelve is to soft delete the media data
func (s *Service) Unshelve(c context.Context, req *model.ReqUnshelve, username string) (resp *model.RespUnshelve, err error) {
var (
rows *sql.Rows
existMap = make(map[int64]int, len(req.IDs))
db, dbSelect *gorm.DB
updField = make(map[string]int, 1)
)
log.Warn("Unshelve Req Type %d, IDs %v, Username %s", req.Type, req.IDs, username) // record user's action
resp = &model.RespUnshelve{
SuccIDs: make([]int64, 0),
FailIDs: make([]int64, 0),
}
if db, dbSelect, err = s.unshelveReqT(req); err != nil {
log.Error("unshelve ReqT Err %v", err)
return
}
if rows, err = dbSelect.Rows(); err != nil {
log.Error("db rows Ids %v, Err %v", req.IDs, err)
return
}
defer rows.Close()
for rows.Next() { // pick existing ids
var sid int64
if err = rows.Scan(&sid); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
resp.SuccIDs = append(resp.SuccIDs, sid)
existMap[sid] = 1
}
if err = rows.Err(); err != nil {
log.Error("rows.Err %v", err)
return
}
for _, v := range req.IDs { // treat to have the non-existing ids
if _, ok := existMap[v]; !ok {
resp.FailIDs = append(resp.FailIDs, v)
}
}
if len(resp.SuccIDs) == 0 { // there isn't any to update ids
return
}
switch req.Type {
case 1, 2: // sid, epid
updField["is_deleted"] = 1
case 3, 4: // aid, cid
updField["deleted"] = 1
}
if err = db.Update(updField).Error; err != nil {
log.Error("update Ids %v, err %v", req.IDs, err)
}
return
}
// ChlSplash gets channel's splash data
func (s *Service) ChlSplash(c context.Context, req *model.ReqChannel) (res *model.ChannelPager, err error) {
var (
db = s.DB.Model(&model.Channel{}).Where("deleted!=?", _isDeleted)
items []*model.ChannelFmt
count int64
)
if req.Desc != "" {
db = db.Where("`desc` LIKE ?", "%"+req.Desc+"%")
}
if req.Title != "" {
db = db.Where("title = ?", req.Title)
}
db.Count(&count)
if req.Order == model.OrderDesc {
db = db.Order("mtime DESC")
} else {
db = db.Order("mtime ASC")
}
if err = db.Offset((req.Page - 1) * _pagesize).Limit(_pagesize).Find(&items).Error; err != nil {
log.Error("chlList Error (%v)", err)
return
}
for _, v := range items {
v.MtimeFormat = s.TimeFormat(v.Mtime)
v.Mtime = 0
}
res = &model.ChannelPager{
TotalCount: count,
Pn: req.Page,
Ps: _pagesize,
Items: items,
}
return
}
func atoi(str string) (res int) {
res, _ = strconv.Atoi(str)
return
}

View File

@@ -0,0 +1,412 @@
package service
import (
"bytes"
"context"
"encoding/json"
"net/http"
"strconv"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
const (
_configTableName = "tv_price_config"
_valid = 0
_invalid = 1
_noPid = 0
_orderField = "ctime"
_vip = 10
_online = 0
_delete = 2
)
// PanelInfo select panel info by id
func (s *Service) PanelInfo(id int64) (panelInfo *model.TvPriceConfigResp, err error) {
if panelInfo, err = s.dao.GetById(id); err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
log.Error("PanelInfo (%v) error(%v)", panelInfo, err)
return
}
if panelInfo != nil {
panelInfo.OriginPrice = panelInfo.Price
hasDiscount, price, discountInfos := s.hasDiscount(nil, panelInfo.ID)
if hasDiscount {
panelInfo.Price = price
}
panelInfo.Items = discountInfos
}
return
}
// PanelStatus change panel status by id
func (s *Service) PanelStatus(id, status int64) (err error) {
var (
flag bool
panelInfo *model.TvPriceConfigResp
)
if panelInfo, err = s.dao.GetById(id); err != nil {
log.Error("PanelInfo (%v) error(%v)", panelInfo, err)
}
if status == _online && panelInfo.SuitType == _vip {
if flag, err = s.hasOnlineUpgradeVipProduct(); err != nil {
log.Error("GetValidUpgradeVipProduct Err %v", err)
return
}
if flag {
err = ecode.TVVipSuitTypeConflict
return err
}
}
if panelInfo.PID == 0 && status == _delete {
if err = s.dao.DB.Table(_configTableName).Where("pid = ?", id).Update("status", _delete).Error; err != nil {
log.Error("PanelStatus discount (%v) error(%v)", id, err)
}
}
if err = s.dao.PanelStatus(id, status); err != nil {
log.Error("PanelStatus (%v) error(%v)", id, err)
}
return
}
// SavePanel add or update panel info
func (s *Service) SavePanel(c context.Context, panel *model.TvPriceConfig) (err error) {
opType := s.c.YSTParam.Update
//start tx
tx := s.DB.Begin()
if panel.PID != 0 {
_, _, discounts := s.hasDiscount(nil, panel.PID)
if flag := checkDisCountTime(discounts, panel); !flag {
err = ecode.TvPriceTimeConflict
return err
}
s.copyParentExtraField(panel)
}
if panel.ID != 0 && panel.PID == 0 {
if err = tx.Table(_configTableName).Where("pid = ?", panel.ID).
Update(map[string]interface{}{
"suit_type": panel.SuitType,
"sub_type": panel.SubType,
"selected": panel.Selected,
"superscript": panel.Superscript,
"month": panel.Month,
}).Error; err != nil {
log.Error("Update discount failed while update panel, Err %v", err)
tx.Rollback()
return
}
_, _, discounts := s.hasDiscount(tx, panel.ID)
for _, discount := range discounts {
if err = s.syncPanels(c, opType, &discount); err != nil {
err = ecode.TvVipProdSyncErr
tx.Rollback()
return err
}
}
}
if panel.ID == 0 {
opType = s.c.YSTParam.Insert
if panel.PID == 0 {
panel.Status = _invalid
} else {
s.copyParentExtraField(panel)
}
if flag := s.dao.ExistProduct(panel.ProductID); flag {
err = ecode.TvVipProductExit
return err
}
}
if err = tx.Save(panel).Error; err != nil {
log.Error("SavePanel %s, Err %v", panel, err)
return err
}
if err = s.syncPanels(c, opType, panel); err != nil {
err = ecode.TvVipProdSyncErr
tx.Rollback()
return err
}
tx.Commit()
return
}
// PanelList get panle list
func (s *Service) PanelList(platform, month, subType, suitType int64) (panels []*model.TvPriceConfigListResp, err error) {
var (
db = s.dao.DB.Model(&model.TvPriceConfigListResp{}).Where("pid = ? and status in (?, ?)", _noPid, _valid, _invalid)
)
if platform != 0 {
db = db.Where("platform = ?", platform)
}
if month != 0 {
db = db.Where("month = ?", month)
}
if subType != -1 {
db = db.Where("sub_type = ?", subType)
}
if suitType != -1 {
db = db.Where("suit_type = ?", suitType)
}
if err = db.Order("suit_type, sub_type desc, month desc").Find(&panels).Error; err != nil {
log.Error("OrderList %v, Err %v", panels, err)
return panels, err
}
for _, panel := range panels {
hasDiscount, price, _ := s.hasDiscount(nil, panel.ID)
panel.OriginPrice = panel.Price
if hasDiscount {
panel.Price = price
}
}
return
}
// HasDiscount Judge whether there is a discount
func (s *Service) hasDiscount(tx *gorm.DB, id int64) (hasDiscount bool, price int64, panels []model.TvPriceConfig) {
if tx == nil {
tx = s.dao.DB
}
if err := tx.Table(_configTableName).Where("pid = ? and status = ?", id, _valid).Find(&panels).Error; err != nil {
log.Error("HasDiscount %v, Err %v", id, err)
return
}
nowTime := time.Now().Unix()
for _, panel := range panels {
if int64(panel.Stime) < nowTime && nowTime < int64(panel.Etime) {
hasDiscount = true
price = panel.Price
return
}
}
return
}
// checkDisCountTime check discount time conflict
func checkDisCountTime(discounts []model.TvPriceConfig, panel *model.TvPriceConfig) (flag bool) {
var (
startTime = panel.Stime
endTime = panel.Etime
)
for _, discount := range discounts {
// do not compare with self
if panel.ID != discount.ID {
if discount.Stime < startTime && startTime < discount.Etime {
return false
}
if discount.Stime < endTime && endTime < discount.Etime {
return false
}
if discount.Stime > startTime && endTime > discount.Etime {
return false
}
}
}
return true
}
// checkRemotePanel check YST panel
func (s *Service) checkRemotePanel(c context.Context) {
var (
panels []*model.TvPriceConfigListResp
)
if err := s.dao.DB.Table(_configTableName).Order(_orderField).Find(&panels).Error; err != nil {
log.Error("CheckRemotePanel Err %v", err)
return
}
res, _ := s.getRemotePanels(c)
remotePanels := res.Product
panelMap := make(map[string]*model.TvPriceConfigListResp, len(panels))
remotePaneMap := make(map[string]model.Product, len(remotePanels))
for i := 0; i < len(panels); i++ {
panelMap[panels[i].ProductID] = panels[i]
}
for i := 0; i < len(remotePanels); i++ {
remotePaneMap[remotePanels[i].ID] = remotePanels[i]
}
for i := 0; i < len(panels); i++ {
rp, exists := remotePaneMap[panels[i].ProductID]
if exists {
s.compareFiled(rp, panels[i])
} else {
log.Error("Our panel not exists in YST, panel id is (%v)", panels[i].ProductID)
}
}
for i := 0; i < len(remotePanels); i++ {
p, exists := panelMap[remotePanels[i].ID]
if exists {
s.compareFiled(remotePanels[i], p)
} else {
log.Error("YST panel not exists in our db, panel id is (%v)", remotePanels[i].ID)
}
}
}
// getRemotePanels get YST panel
func (s *Service) getRemotePanels(c context.Context) (res *model.RemotePanel, err error) {
var (
req *http.Request
)
res = &model.RemotePanel{}
params := map[string]string{
"vod_type": s.c.YSTParam.QueryPanelType,
"source": s.c.YSTParam.Source,
}
reqBody, _ := json.Marshal(params)
getRemotePanelUrl := s.c.URLConf.GetRemotePanelUrl
if req, err = http.NewRequest(http.MethodPost, getRemotePanelUrl, bytes.NewReader(reqBody)); err != nil {
log.Error("MerakNotify NewRequest Err %v, Url %v", err, getRemotePanelUrl)
return
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
if err = s.client.Do(c, req, &res); err != nil {
log.Error("MergeUpInfo http req failed ,err:%v", err)
return
}
return
}
// compareFiled compare our panel field with YST
func (s *Service) compareFiled(remotePanel model.Product, panel *model.TvPriceConfigListResp) {
rpid := remotePanel.ID
prodiuctId := panel.ProductID
if rpid != prodiuctId {
log.Error("PanelInfo id different, Remote panel prodiuctId is (%v), Our panel prodiuctId is (%v)", rpid, prodiuctId)
}
if remotePanel.Price != panel.Price {
log.Error("PanelInfo price different, Remote panel prodiuctId is (%v), Our panel prodiuctId is (%v)", rpid, prodiuctId)
}
if remotePanel.Contract != strconv.Itoa(int(panel.SubType)) {
log.Error("PanelInfo subType different, Remote panel prodiuctId is (%v), Our panel prodiuctId is (%v)", rpid, prodiuctId)
}
if remotePanel.SuitType != panel.SuitType {
log.Error("PanelInfo suitType different, Remote panel id is (%v), Our panel id is (%v)", rpid, prodiuctId)
}
if remotePanel.ProductDuration != strconv.FormatInt(panel.Month*31, 10) {
log.Error("PanelInfo months different, Remote panel prodiuctId is (%v), Our panel prodiuctId is (%v)", rpid, prodiuctId)
}
if remotePanel.Title != panel.ProductName {
log.Error("PanelInfo productName different, Remote panel prodiuctId is (%v), Our panel id prodiuctId (%v)", rpid, prodiuctId)
}
if panel.PID != 0 {
parentPanel, err := s.dao.GetById(panel.PID)
if err != nil {
log.Error("PanelInfo (%v) error(%v)", parentPanel, err)
}
if remotePanel.ComboPkgID != parentPanel.ProductID {
log.Error("PanelInfo pid different, Remote panel prodiuctId is (%v), Our panel prodiuctId is (%v)", rpid, prodiuctId)
}
}
}
// syncPanels send our panel to YST
func (s *Service) syncPanels(c context.Context, opType string, panel *model.TvPriceConfig) (err error) {
var (
req *http.Request
res struct {
Result string `json:"result"`
Message string `json:"message"`
}
params struct {
OpType string `json:"optype"`
Source string `json:"source"`
Product model.Product `json:"product"`
}
)
params.OpType = opType
params.Source = s.c.YSTParam.Source
params.Product.ID = panel.ProductID
params.Product.VodType = s.c.YSTParam.InsertPanelType
params.Product.Title = panel.ProductName
params.Product.ProductDuration = strconv.FormatInt(31*panel.Month, 10)
params.Product.Description = panel.Remark
params.Product.Contract = strconv.Itoa(int(panel.SubType))
if panel.PID != 0 {
parentPanel, _ := s.dao.GetById(panel.PID)
if parentPanel != nil {
params.Product.ComboPkgID = parentPanel.ProductID
params.Product.ComboDes = parentPanel.Remark
} else {
log.Error("Prarent PanelInfo not found, pid =(%v)", panel.PID)
err = ecode.NothingFound
return err
}
}
params.Product.Price = panel.Price
params.Product.SuitType = panel.SuitType
reqBody, _ := json.Marshal(params)
SyncPanelUrl := s.c.URLConf.SyncPanelUrl
if req, err = http.NewRequest(http.MethodPost, SyncPanelUrl, bytes.NewReader(reqBody)); err != nil {
log.Error("MerakNotify NewRequest Err %v, Url %v", err, SyncPanelUrl)
return err
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
if err = s.client.Do(c, req, &res); err != nil {
log.Error("MergeUpInfo http req failed ,err:%v", err)
return err
}
if res.Result != "SUCCESS" {
err = ecode.TvVipProdSyncErr
log.Info("Sync panel To YST Fail,err:%v", res.Message)
return err
}
return
}
func (s *Service) hasOnlineUpgradeVipProduct() (flag bool, err error) {
var (
panels []*model.TvPriceConfig
)
if err = s.dao.DB.Table(_configTableName).Where("suit_type = ? and status = ? and pid = ?", _vip, _valid, _noPid).Find(&panels).Error; err != nil {
log.Error("GetValidUpgradeVipProduct Err %v", err)
return
}
flag = !(len(panels) == 0)
return
}
func (s *Service) copyParentExtraField(panel *model.TvPriceConfig) {
parentPanel, _ := s.dao.GetById(panel.PID)
if parentPanel != nil {
panel.SuitType = parentPanel.SuitType
panel.SubType = parentPanel.SubType
panel.Selected = parentPanel.Selected
panel.Superscript = parentPanel.Superscript
panel.Month = parentPanel.Month
}
}

View File

@@ -0,0 +1,16 @@
package service
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_Playurl(t *testing.T) {
Convey("PlayURL test", t, WithService(func(s *Service) {
url, err := s.Playurl(2476470)
fmt.Println(url)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,83 @@
package service
import (
"context"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
)
// RegList region list .
func (s *Service) RegList(ctx context.Context, arg *model.Param) (res []*model.RegList, err error) {
var reg []*model.RegDB
res = make([]*model.RegList, 0)
if reg, err = s.dao.RegList(ctx, arg); err != nil {
log.Error("s.dao.RegList error(%v)", err)
return
}
for _, v := range reg {
res = append(res, v.ToList())
}
return
}
// AddReg add region .
func (s *Service) AddReg(ctx context.Context, title, itype, itid, rank string) (err error) {
if err = s.dao.AddReg(ctx, title, itype, itid, rank); err != nil {
log.Error("s.dao.AddReg error(%v)", err)
}
return
}
// EditReg edit region .
func (s *Service) EditReg(ctx context.Context, pid, title, itype, itid string) (err error) {
if err = s.dao.EditReg(ctx, pid, title, itype, itid); err != nil {
log.Error("s.dao.EditReg error(%v)", err)
}
return
}
// UpState publish or not .
func (s *Service) UpState(ctx context.Context, pids []int, state string) (err error) {
if err = s.dao.UpState(ctx, pids, state); err != nil {
log.Error("s.dao.UpState error(%v)", err)
}
return
}
// RegSort .
func (s *Service) RegSort(ctx context.Context, ids []int) (err error) {
order := 0
for _, v := range ids {
if !s.isExist(v) {
log.Error("id is not exit! id(%d) error(%v)", v, err)
return
}
}
tx := s.DB.Begin()
for _, v := range ids {
order += 1
if err = tx.Model(&model.RegDB{}).Where("id=?", v).Update(map[string]int{"rank": order}).Error; err != nil {
log.Error("RegSort Update error(%v)", err)
tx.Rollback()
return
}
}
tx.Commit()
return
}
func (s *Service) isExist(id int) (f bool) {
var (
err error
a = &model.RegDB{}
)
if err = s.DB.Where("id=?", id).Where("deleted=0").Find(a).Error; err != nil {
log.Error("isExist s.DB.Where error(%s)")
return
}
if a.ID != 0 {
return true
}
return false
}

View File

@@ -0,0 +1,71 @@
package service
import (
"context"
"testing"
"go-common/app/admin/main/tv/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_RegList(t *testing.T) {
Convey("region list", t, WithService(func(s *Service) {
var (
err error
param = &model.Param{}
res []*model.RegList
c = context.Background()
)
res, err = s.RegList(c, param)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}
func TestService_AddReg(t *testing.T) {
Convey("add region", t, WithService(func(s *Service) {
var (
err error
c = context.Background()
)
err = s.AddReg(c, "0", "0", "0", "1")
So(err, ShouldBeNil)
}))
}
func TestService_EditReg(t *testing.T) {
Convey("edit region", t, WithService(func(s *Service) {
var (
err error
c = context.Background()
)
err = s.EditReg(c, "0", "0", "0", "0")
So(err, ShouldBeNil)
}))
}
func TestService_UpState(t *testing.T) {
Convey("update state", t, WithService(func(s *Service) {
var (
err error
c = context.Background()
pids = []int{1}
state = "0"
)
err = s.UpState(c, pids, state)
So(err, ShouldBeNil)
}))
}
func TestService_RegSort(t *testing.T) {
Convey("update region sort", t, WithService(func(s *Service) {
var (
err error
c = context.Background()
ids = []int{1}
)
err = s.RegSort(c, ids)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,114 @@
package service
import (
"go-common/app/admin/main/tv/model"
bm "go-common/library/net/http/blademaster"
)
const (
_isDeleted = 1
)
//SetSearInterRank set search intervene rank
func (s *Service) SetSearInterRank(c *bm.Context, rank []*model.OutSearchInter) (err error) {
err = s.dao.SetSearchInterv(c, rank)
return
}
//GetSearInterRank get search intervene rank
func (s *Service) GetSearInterRank(c *bm.Context) (rank []*model.OutSearchInter, err error) {
rank, err = s.dao.GetSearchInterv(c)
return
}
//GetSearInterList get search intervene list
func (s *Service) GetSearInterList(c *bm.Context, pn, ps int) (items []*model.SearInter, total int, err error) {
//rank, err = s.dao.GetSearchInterv(c)
//return
start := (pn - 1) * ps
db := s.DB.Where("deleted!=?", _isDeleted).Order("rank ASC")
if err = db.Model(&model.SearInter{}).Offset(start).Limit(ps).Find(&items).Error; err != nil {
return
}
s.DB.Model(&model.SearInter{}).Where("deleted!=?", _isDeleted).Count(&total)
return
}
//GetSearInterCount get search intervene count
func (s *Service) GetSearInterCount(c *bm.Context) (total int, err error) {
if err = s.DB.Model(&model.SearInter{}).Where("deleted!=?", _isDeleted).Count(&total).Error; err != nil {
return
}
return
}
//AddSearInter add search intervene
func (s *Service) AddSearInter(c *bm.Context, si *model.SearInter) (err error) {
if err = s.DB.Create(si).Error; err != nil {
return
}
return
}
//UpdateSearInter update search intervene
func (s *Service) UpdateSearInter(c *bm.Context, id int64, searchword string) (err error) {
if err = s.DB.Model(&model.SearInter{}).Where("id=?", id).Update("searchword", searchword).Error; err != nil {
return
}
return
}
//DelSearInter delete search intervene
func (s *Service) DelSearInter(c *bm.Context, id int64) (err error) {
if err = s.DB.Model(&model.SearInter{}).Where("id=?", id).Update("deleted", 1).Error; err != nil {
return
}
return
}
//RankSearInter set search intervene new rank
func (s *Service) RankSearInter(c *bm.Context, idsArr []string) (err error) {
tx := s.DB.Begin()
for k, v := range idsArr {
newRank := k + 1
id := v
if errDB := s.DB.Model(&model.SearInter{}).Where("id=?", id).Update("rank", newRank).Error; errDB != nil {
err = errDB
tx.Rollback()
return
}
}
tx.Commit()
return
}
//GetSearInterPublish get search intervene publish status
func (s *Service) GetSearInterPublish(c *bm.Context) (items []*model.SearInter, err error) {
limit := s.c.Cfg.SearInterMax
db := s.DB.Where("deleted!=?", _isDeleted).Order("rank ASC")
if err = db.Model(&model.SearInter{}).Limit(limit).Find(&items).Error; err != nil {
return
}
return
}
//GetMaxRank get search intervene max rank
func (s *Service) GetMaxRank(c *bm.Context) (items model.SearInter, err error) {
db := s.DB.Where("deleted!=?", _isDeleted).Order("rank DESC")
if err = db.Model(&model.SearInter{}).Limit(1).Find(&items).Error; err != nil {
return
}
return
}
//SetPublishState set publish status
func (s *Service) SetPublishState(c *bm.Context, state *model.PublishStatus) (err error) {
err = s.dao.SetPublishCache(c, state)
return
}
//GetPublishState get publish status
func (s *Service) GetPublishState(c *bm.Context) (state *model.PublishStatus, err error) {
state, err = s.dao.GetPublishCache(c)
return
}

View File

@@ -0,0 +1,117 @@
package service
import (
"context"
"go-common/library/log"
"time"
"go-common/app/admin/main/tv/conf"
"go-common/app/admin/main/tv/dao"
"go-common/app/admin/main/tv/model"
acccli "go-common/app/service/main/account/api"
arccli "go-common/app/service/main/archive/api"
httpx "go-common/library/net/http/blademaster"
"github.com/jinzhu/gorm"
)
var ctx = context.Background()
// Service biz service def.
type Service struct {
c *conf.Config
dao *dao.Dao
DB, DBShow *gorm.DB
accClient acccli.AccountClient
arcClient arccli.ArchiveClient
SupCats []*model.ParentCat
supCatMap *model.SupCats
IntervLimit int
arcPTids map[int32][]int32 // archive parent type ids
ArcTypes map[int32]*arccli.Tp
avaiTps *model.AvailTps
snsInfo map[int64]*model.TVEpSeason
snsCats map[int][]int64
abnCids []*model.AbnorCids // abnormal cids
pgcCatName map[int]string // pgc category name
labelTps map[int][]*model.TpLabel
client *httpx.Client
}
// New new a Service and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
IntervLimit: c.Cfg.IntervLimit,
SupCats: make([]*model.ParentCat, 0),
ArcTypes: make(map[int32]*arccli.Tp),
arcPTids: make(map[int32][]int32),
avaiTps: &model.AvailTps{},
snsInfo: make(map[int64]*model.TVEpSeason),
snsCats: make(map[int][]int64),
pgcCatName: make(map[int]string),
labelTps: make(map[int][]*model.TpLabel),
client: httpx.NewClient(conf.Conf.HTTPClient),
}
s.DB = s.dao.DB
s.DBShow = s.dao.DBShow
var err error
if s.accClient, err = acccli.NewClient(c.AccClient); err != nil {
panic(err)
}
if s.arcClient, err = arccli.NewClient(c.ArcClient); err != nil {
panic(err)
}
for k, v := range c.Cfg.PgcNames {
s.pgcCatName[atoi(k)] = v
}
s.loadData()
go s.loadDataproc()
s.loadSns(context.Background()) // load season info
go s.loadSnsproc()
s.loadAbnCids() // load abnormal cids
go s.loadAbnCidsproc()
go s.refLabelproc() // refresh ugc + pgc labels
go s.checkPanel()
return s
}
func (s *Service) loadDataproc() {
for {
time.Sleep(time.Duration(s.c.Cfg.SupportCat.ReloadFre))
s.loadData()
}
}
func (s *Service) refLabelproc() {
for {
s.ugcLabels()
s.pgcLabels()
time.Sleep(time.Duration(s.c.Cfg.RefLabel.Fre))
}
}
func (s *Service) checkPanel() {
for {
time.Sleep(time.Duration(3600) * time.Second)
log.Info("check panel info start!")
s.checkRemotePanel(ctx)
log.Info("check panel info end!")
}
}
func (s *Service) loadData() {
s.loadTypes() // load ugc types
s.loadTps() // load passed tps and all tps for cms type list
s.loadCats() // load support categorys ( pgc & ugc)
s.loadLabel() // load pgc label types
}
// Wait wait all closed.
func (s *Service) Wait() {
}
// Close close all dao.
func (s *Service) Close() {
}

View File

@@ -0,0 +1,59 @@
package service
import (
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/admin/main/tv/conf"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/tv-admin-test.toml")
flag.Set("conf", dir)
conf.Init()
srv = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
Reset(func() {})
f(srv)
}
}
func Test_existArcTypes(t *testing.T) {
Convey("existArcTs", t, WithService(func(s *Service) {
exist, err := s.existArcTps(true)
fmt.Println(exist)
fmt.Println(err)
So(err, ShouldBeNil)
exist, err = s.existArcTps(false)
fmt.Println(exist)
fmt.Println(err)
So(err, ShouldBeNil)
}))
}
func Test_Wait(t *testing.T) {
Convey("wait all closed", t, WithService(func(s *Service) {
s.Wait()
}))
}
func TestService_ResExist(t *testing.T) {
Convey("res exist", t, WithService(func(s *Service) {
fmt.Println(s.resExist(255, 1))
fmt.Println(s.recomExist(2))
}))
}

View File

@@ -0,0 +1,147 @@
package service
import (
"database/sql"
"go-common/app/admin/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/jinzhu/gorm"
)
const (
_seasonPassed = 1
_seasonToAudit = 2
_epPassed = 3
_epToAudit = 1
_seasonDefault = 4
)
// SeasonCheck manages the season's check status
func (s *Service) SeasonCheck(sn *model.TVEpSeason) (err error) {
var status int
if sn.Check != _seasonPassed {
status = _seasonToAudit
} else {
return
}
if err = s.DB.Model(&model.TVEpSeason{}).Where("id = ?", sn.ID).Update(map[string]int{"check": status}).Error; err != nil {
log.Error("tvSrv.SeasonCheck error(%v)", err)
}
return
}
// EpCheck manages the ep's check status
func (s *Service) EpCheck(ep *model.Content) (err error) {
var status int
if ep.State != _epPassed {
status = _epToAudit
} else {
return // if passed, no need to update the status
}
if err = s.DB.Model(&model.Content{}).Where("id = ?", ep.ID).Update(map[string]int{"state": status}).Error; err != nil {
log.Error("tvSrv.EpCheck error(%v)", err)
}
return
}
// EpDel controls the ep's deletion status, 0 or 1
func (s *Service) EpDel(epid int64, action int) (err error) {
if err = s.DB.Model(&model.TVEpContent{}).Where("id=?", epid).Update(map[string]int{"is_deleted": action}).Error; err != nil {
log.Error("tvSrv.EpDel error(%v)\n", err)
return
}
if err = s.DB.Model(&model.Content{}).Where("epid=?", epid).Update(map[string]int{"is_deleted": action}).Error; err != nil {
log.Error("tvSrv.EpDel error(%v)\n", err)
}
return
}
// SeasonRemove removes the season and its eps
func (s *Service) SeasonRemove(season *model.TVEpSeason) (err error) {
var rows *sql.Rows
// remove season
if err = s.DB.Model(&model.TVEpSeason{}).Where("id = ?", season.ID).Update(map[string]int{"is_deleted": 1}).Error; err != nil {
log.Error("tvSrv.removeSeason error(%v)", err)
return
}
// manage season's check status
if err = s.SeasonCheck(season); err != nil {
return
}
// manage season's eps check status and deletion status
if rows, err = s.DB.Model(&model.Content{}).Where("season_id = ?", season.ID).Select("id, epid, state").Rows(); err != nil {
log.Error("tvSrv.removeSeason error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
cont := &model.Content{}
if err = rows.Scan(&cont.ID, &cont.EPID, &cont.State); err != nil {
log.Error("rows.Scan error(%v)", err)
continue
}
if err = s.EpDel(int64(cont.EPID), 1); err != nil {
continue
}
if err = s.EpCheck(cont); err != nil {
continue
}
}
return
}
// SnUpdate receives the season update/create info from PGC side and save them in DB
func (s *Service) SnUpdate(c *bm.Context, req *model.TVEpSeason) (err error) {
var (
exist = model.TVEpSeason{}
updateMap = make(map[string]interface{})
reqForm = c.Request.PostForm
)
if err = s.DB.Where("id=?", req.ID).First(&exist).Error; err != nil && err != gorm.ErrRecordNotFound {
log.Error("SnUpdate Sid %d Err %v", req.ID, err)
return
}
if exist.ID <= 0 { // if data not exist, it's brand new data
req.Check = _seasonDefault
if err = s.DB.Create(req).Error; err != nil {
log.Error("tvSrv.createSeason error(%v)", err)
}
return
}
if exist.IsDeleted == 1 { // exist but was deleted
if err = s.DB.Model(&model.TVEpSeason{}).Where("id = ?", req.ID).Update(map[string]int{"is_deleted": 0}).Error; err != nil {
log.Error("tvSrv.removeSeason error(%v)", err)
return
}
}
if updateMap = exist.Updated(reqForm); len(updateMap) == 0 {
log.Warn("SnUpdate Sid %d No change", req.ID)
return
}
if err = s.DB.Model(&model.TVEpSeason{}).Where("id = ?", req.ID).Update(updateMap).Error; err != nil {
log.Error("SnUpdate Sid %d, Update Err %v", req.ID, err)
return
}
log.Info("SnUpdate Sid %d, Update Fields %v Succ", req.ID, updateMap)
return s.SeasonCheck(&exist)
}
// EpAct acts on ep item
func (s *Service) EpAct(c *bm.Context, cid int64, act int) (err error) {
var (
item = &model.Content{}
db = s.DB.Model(&model.Content{})
)
if err = db.Where("state=?", _epPassed).Where("is_deleted=?", 0).First(item, cid).Error; err != nil {
log.Error("query fail(%v)\n", err)
return ecode.RequestErr
}
if err = db.Where("id=?", cid).Update(map[string]int{"valid": act}).Error; err != nil {
log.Error("online error(%v)\n", err)
return ecode.RequestErr
}
return
}

View File

@@ -0,0 +1,89 @@
package service
import (
"go-common/app/admin/main/tv/model"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_EpCheck(t *testing.T) {
Convey("TestService_EpCheck test", t, WithService(func(s *Service) {
var cont = model.Content{}
// already passed to 7
err := s.EpCheck(&model.Content{
ID: 106396,
State: 3,
})
So(err, ShouldBeNil)
s.DB.Where("id=?", 106396).First(&cont)
So(cont.State == 7, ShouldBeTrue)
// reject to re_audit
err = s.EpCheck(&model.Content{
ID: 106396,
State: 4,
})
So(err, ShouldBeNil)
s.DB.Where("id=?", 106396).First(&cont)
So(cont.State == 1, ShouldBeTrue)
}))
}
func TestService_SeasonCheck(t *testing.T) {
Convey("TestService_SeasonCheck test", t, WithService(func(s *Service) {
var season = model.TVEpSeason{}
// already passed to 7
err := s.SeasonCheck(&model.TVEpSeason{
ID: 296,
Check: 1})
So(err, ShouldBeNil)
s.DB.Where("id=?", 296).First(&season)
So(season.Check == 7, ShouldBeTrue)
// reject to re_audit
err = s.SeasonCheck(&model.TVEpSeason{
ID: 296,
Check: 0})
So(err, ShouldBeNil)
s.DB.Where("id=?", 296).First(&season)
So(season.Check == 2, ShouldBeTrue)
}))
}
func TestService_EpDel(t *testing.T) {
Convey("TestService_EpDel test", t, WithService(func(s *Service) {
var (
cont = model.Content{}
ep = model.TVEpContent{}
epid = 5822
)
// delete
err := s.EpDel(int64(epid), 1)
s.DB.Where("epid=?", epid).First(&cont)
s.DB.Where("id=?", epid).First(&ep)
So(err, ShouldBeNil)
So(cont.IsDeleted == 1, ShouldBeTrue)
So(ep.IsDeleted == 1, ShouldBeTrue)
// recover
err = s.EpDel(30185, 0)
s.DB.Where("epid=?", 30185).First(&cont)
s.DB.Where("id=?", 30185).First(&ep)
So(err, ShouldBeNil)
So(cont.IsDeleted == 1, ShouldBeTrue)
So(ep.IsDeleted == 1, ShouldBeTrue)
}))
}
func TestService_SeasonRemove(t *testing.T) {
Convey("TestService_SeasonRemove test", t, WithService(func(s *Service) {
var (
season = model.TVEpSeason{}
seasonID = 20950
)
err := s.SeasonRemove(&model.TVEpSeason{
ID: int64(seasonID),
Check: 1,
})
s.DB.Where("id=?", seasonID).First(&season)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,28 @@
package service
import (
"fmt"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
)
// UPlayurl get ugc play url
func (s *Service) UPlayurl(aid int) (playurl string, err error) {
w := map[string]interface{}{
"deleted": 0,
"result": 1,
"cid": aid,
}
video := model.Video{}
if err = s.dao.DB.Model(&model.Video{}).Where(w).First(&video).Error; err != nil {
err = fmt.Errorf("找不到aid为%d的数据", aid)
return
}
if playurl, err = s.dao.UPlayurl(ctx, aid); err != nil {
log.Error("UPlayurl API Error(%d) (%v)", aid, err)
return
}
log.Info("UPlayurl aid = %d, playurl = %s", aid, playurl)
return
}

View File

@@ -0,0 +1,16 @@
package service
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_UPlayurl(t *testing.T) {
Convey("PlayURL test", t, WithService(func(s *Service) {
url, err := s.UPlayurl(120094301)
fmt.Println(url)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,261 @@
package service
import (
"context"
"go-common/app/admin/main/tv/model"
accmdl "go-common/app/service/main/account/api"
account "go-common/app/service/main/account/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
const (
_removeArcs = 2
_deleted = 1
_toinit = 1
_requestedInit = 2
_onlineAct = "1"
_offlineAct = "0"
_validOnline = 1
_validOffline = 0
)
// AddMids CheckMids checks the mids, whether all the uppers exist
func (s *Service) AddMids(mids []int64) (res *model.AddResp, err error) {
var (
accsReply *accmdl.InfosReply
accsInfo map[int64]*account.Info
midExist bool
)
// init the response
res = &model.AddResp{
Succ: []int64{},
Exist: []int64{},
Invalids: []int64{},
}
// rpc get all the mids' info
if accsReply, err = s.accClient.Infos3(ctx, &accmdl.MidsReq{
Mids: mids,
}); err != nil {
log.Error("CheckMids Mids: %v, Error: %v", mids, err)
return
}
accsInfo = accsReply.Infos
for _, v := range mids {
// if invalid account
if _, ok := accsInfo[v]; !ok {
res.Invalids = append(res.Invalids, v)
continue
}
if midExist, err = s.existMid(v); err != nil {
log.Error("AddMid %d, Error %v", v, err)
return
}
// if the account is existing in our DB
if midExist {
res.Exist = append(res.Exist, v)
continue
}
// add the upper
if err = s.dao.UpAdd(v); err != nil {
log.Error("AddMid %d, Error %v", v, err)
return
}
res.Succ = append(res.Succ, v)
}
return
}
// existMid checks whether the mid is existing in our DB
func (s *Service) existMid(mid int64) (exist bool, err error) {
if err = s.DB.Where("deleted = 0").Where("mid = ?", mid).First(&model.Upper{}).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return false, nil
}
log.Error("existMid %d, Error %v", mid, err)
return
}
return true, nil
}
// ImportMids is for updating the data to tell the tv-job to import the uppers' all videos
func (s *Service) ImportMids(mids []int64) (res *model.ImportResp, err error) {
var midExist bool
res = &model.ImportResp{
NotExist: []int64{},
Succ: []int64{},
}
for _, v := range mids {
if midExist, err = s.existMid(v); err != nil {
log.Error("ImportMids %d, Error %v", v, err)
return
}
if !midExist {
res.NotExist = append(res.NotExist, v)
continue
}
if err = s.DB.Model(&model.Upper{}).Where("mid = ?", v).Update(map[string]int{"toinit": _toinit, "state": _requestedInit}).Error; err != nil {
log.Error("ImportMids %d, Error %v", v, err)
return
}
res.Succ = append(res.Succ, v)
}
return
}
// DelMid is for updating remove one upper from the list
func (s *Service) DelMid(mid int64) (err error) {
var exist bool
if exist, err = s.existMid(mid); err != nil {
return
}
if !exist {
return ecode.TvUpperNotInList
}
if err = s.DB.Model(&model.Upper{}).Where("mid = ?", mid).Update(map[string]int{"deleted": _deleted, "toinit": _removeArcs}).Error; err != nil {
log.Error("DelMid %d, Error %v", mid, err)
}
return
}
// UpList shows the upper list
func (s *Service) UpList(order int, page int, name string, id int) (pager *model.UpperPager, err error) {
var (
source []*model.Upper
mids []int64
match map[int64]*account.Info
info *account.Info
ok bool
namesReply, infosReply *accmdl.InfosReply
nameRes map[int64]*account.Info
ids []int64
)
pager = &model.UpperPager{}
// id treatmnet
if id != 0 {
ids = append(ids, int64(id))
}
// name treatment
if name != "" {
if namesReply, err = s.accClient.InfosByName3(ctx, &accmdl.NamesReq{
Names: []string{name},
}); err != nil {
log.Error("accRPC InfosByName3 %s, Err %v", name, err)
return
}
nameRes = namesReply.Infos
for k := range nameRes {
ids = append(ids, k)
}
}
if source, pager.Page, err = s.dao.UpList(order, page, ids); err != nil {
return
}
// pick upper's name from AccRPC
for _, v := range source {
mids = append(mids, v.MID)
}
if infosReply, err = s.accClient.Infos3(ctx, &accmdl.MidsReq{
Mids: mids,
}); err != nil {
log.Error("accRPC Infos3, %v, Error %v", mids, err)
return
}
match = infosReply.Infos
for _, v := range source { // arrange the data to output
if info, ok = match[v.MID]; !ok {
log.Error("Mid %v, AccRPC info is nil", v.MID)
continue
}
pager.Items = append(pager.Items, &model.UpperR{
MID: v.MID,
State: v.State,
Name: info.Name,
Ctime: s.TimeFormat(v.Ctime),
Mtime: s.TimeFormat(v.Mtime),
})
}
return
}
// CmsList is the upper cms list service function
func (s *Service) CmsList(ctx context.Context, req *model.ReqUpCms) (pager *model.CmsUpperPager, err error) {
ups, page, errList := s.dao.UpCmsList(req)
if errList != nil {
return nil, errList
}
for _, v := range ups {
v.MtimeStr = v.Mtime.Time().Format("2006-01-02 15:04:05")
}
pager = &model.CmsUpperPager{
Items: ups,
Page: page,
}
return
}
// CmsAudit updates the mids' valid status
func (s *Service) CmsAudit(ctx context.Context, mids []int64, action string) (resp *model.RespUpAudit, err error) {
var (
okMids map[int64]*model.UpMC
validAct int
)
resp = &model.RespUpAudit{
Succ: mids,
}
if okMids, err = s.dao.VerifyIds(mids); err != nil {
return
}
if len(okMids) != len(mids) {
succ := []int64{}
for _, v := range mids {
if _, ok := okMids[v]; !ok {
resp.Invalid = append(resp.Invalid, v)
} else {
succ = append(succ, v)
}
}
resp.Succ = succ
}
if action == _onlineAct {
validAct = _validOnline
} else if action == _offlineAct {
validAct = _validOffline
}
if err = s.dao.AuditIds(resp.Succ, validAct); err != nil {
return
}
for _, v := range resp.Succ {
s.dao.DelCache(ctx, v) // delete cache from MC because their status has been updated
}
return
}
// CmsEdit updates the upper's info in both DB and cache
func (s *Service) CmsEdit(ctx context.Context, req *model.ReqUpEdit) (err error) {
var (
okMids map[int64]*model.UpMC
upInfo *model.UpMC
ok bool
)
if okMids, err = s.dao.VerifyIds([]int64{req.MID}); err != nil {
return
}
if len(okMids) == 0 {
return ecode.TvUpperNotInList
}
if upInfo, ok = okMids[req.MID]; !ok {
log.Error("okMids %v, Mid %d, not found", okMids, req.MID)
return ecode.NothingFound
}
if err = s.dao.SetUpper(req); err != nil { // update upper cms info in DB
return
}
upInfo.CMSFace = req.Face
upInfo.CMSName = req.Name
err = s.dao.SetUpMetaCache(ctx, upInfo) // update Cache
return
}

View File

@@ -0,0 +1,44 @@
package service
import (
"testing"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_AddMids(t *testing.T) {
Convey("TestService_CheckMids Test", t, WithService(func(s *Service) {
res, err := s.AddMids([]int64{
1, 2, 3, 777777777777, 999,
})
So(err, ShouldBeNil)
fmt.Println(res)
}))
}
func TestService_ImportMids(t *testing.T) {
Convey("TestService_ImportMids Test", t, WithService(func(s *Service) {
res, err := s.ImportMids([]int64{
1, 2, 3, 777777777777, 999,
})
So(err, ShouldBeNil)
fmt.Println(res)
}))
}
func TestService_DelMid(t *testing.T) {
Convey("TestService_DelMid Test", t, WithService(func(s *Service) {
err := s.DelMid(2)
So(err, ShouldBeNil)
}))
}
func TestService_UpList(t *testing.T) {
Convey("TestService_UpList Test", t, WithService(func(s *Service) {
res, err := s.UpList(2, 1, "", 0)
So(err, ShouldBeNil)
So(len(res.Items), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,23 @@
package service
import (
"context"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
//UserInfo select user info by mid
func (s *Service) UserInfo(c context.Context, mid int64) (userInfo *model.TvUserInfoResp, err error) {
if userInfo, err = s.dao.GetByMId(c, mid); err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
log.Error("UserInfo (%v) error(%v)", userInfo, err)
return
}
return
}

View File

@@ -0,0 +1,209 @@
package service
import (
"bytes"
"database/sql"
"encoding/csv"
"fmt"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/siddontang/go-mysql/mysql"
)
//VideoList is used for getting PGC video from DB
func (s *Service) VideoList(c *bm.Context, param *model.VideoListParam) (pager *model.VideoListPager, err error) {
var (
order string
total int
videos []*model.VideoListQuery
)
selectStr := []string{
"ugc_video.id",
"ugc_video.aid",
"ugc_video.cid",
"ugc_video.eptitle",
"ugc_video.valid",
"ugc_video.mtime",
"ugc_video.index_order",
"ugc_archive.title",
"ugc_archive.typeid",
}
//只筛选出未删除 且审核过的视频
w := map[string]interface{}{"deleted": 0, "result": 1}
db := s.DB.Model(&model.VideoListQuery{}).Where(w)
db = db.Select(selectStr).
Where("ugc_archive.deleted = ?", 0).
Where("ugc_archive.result = ?", 1).
Joins("LEFT JOIN ugc_archive ON ugc_archive.aid = ugc_video.aid")
if param.CID != "" {
db = db.Where("ugc_video.aid = ?", param.CID)
}
if param.VID != "" {
db = db.Where("`cid` = ?", param.VID)
}
if param.Typeid > 0 {
db = db.Where("typeid = ?", param.Typeid)
}
if param.Pid > 0 {
db = db.Where("typeid in (?)", s.arcPTids[param.Pid])
}
if param.Valid != "" {
db = db.Where("ugc_video.valid = ?", param.Valid)
}
if param.Order == 2 {
order = "ugc_video.mtime DESC"
} else {
order = "ugc_video.mtime ASC"
}
if err = db.Order(order).Offset((param.Pn - 1) * param.Ps).Limit(param.Ps).Find(&videos).Error; err != nil {
return
}
if err = db.Count(&total).Error; err != nil {
return
}
//get parent id
for k, v := range videos {
videos[k].PTypeID = s.GetArchivePid(v.TypeID)
}
pager = &model.VideoListPager{
Items: videos,
Page: &model.Page{
Num: param.Pn,
Size: param.Ps,
Total: int(total),
},
}
return
}
//VideoOnline is used for online PGC video
func (s *Service) VideoOnline(ids []int64) (err error) {
w := map[string]interface{}{"deleted": 0, "result": 1}
tx := s.DB.Begin()
for _, v := range ids {
video := model.Video{}
if errDB := tx.Model(&model.Video{}).Where(w).Where("id=?", v).First(&video).Error; errDB != nil {
err = fmt.Errorf("找不到id为%v的数据", v)
tx.Rollback()
return
}
if errDB := tx.Model(&model.Video{}).Where("id=?", v).
Update("valid", 1).Error; errDB != nil {
err = errDB
tx.Rollback()
return
}
}
tx.Commit()
return
}
//VideoHidden is used for hidden UGC video
func (s *Service) VideoHidden(ids []int64) (err error) {
w := map[string]interface{}{"deleted": 0, "result": 1}
tx := s.DB.Begin()
for _, v := range ids {
video := model.Video{}
if errDB := tx.Model(&model.Video{}).Where(w).Where("id=?", v).First(&video).Error; errDB != nil {
err = fmt.Errorf("找不到id为%v的数据", v)
tx.Rollback()
return
}
if errDB := tx.Model(&model.Video{}).Where("id=?", v).
Update("valid", 0).Error; errDB != nil {
err = errDB
tx.Rollback()
return
}
}
tx.Commit()
return
}
//VideoUpdate is used for hidden UGC video
func (s *Service) VideoUpdate(id int, title string) (err error) {
w := map[string]interface{}{"id": id}
if err = s.DB.Model(&model.Video{}).Where(w).Update("eptitle", title).Error; err != nil {
return
}
return
}
func (s *Service) getArc(aid int64) (res *model.Archive, err error) {
var data = model.Archive{}
if err = s.DB.Where("aid = ?", aid).Where("deleted =0").First(&data).Error; err != nil {
log.Error("getArc Aid %d, Err %v", aid, err)
return
}
res = &data
return
}
func (s *Service) loadAbnCids() {
var (
rows *sql.Rows
err error
abnCids []*model.AbnorCids
cfg = s.c.Cfg.Abnormal
)
// select cid from ugc_video where cid > 12780000 and deleted =0 and mark = 1 and submit = 1 and ctime < '2018-10-17 18:00:00' and transcoded = 0
if rows, err = s.DB.Model(&model.Video{}).Where("cid > ?", cfg.CriticalCid).
Where("deleted = 0").Where("mark = 1").Where("submit = 1").
Where(fmt.Sprintf("ctime < DATE_SUB(NOW(), INTERVAL %d HOUR)", cfg.AbnormHours)).
Where("transcoded = 0").Select("cid,aid,ctime,eptitle").Rows(); err != nil {
log.Error("loadAbnCids Err %v", err)
return
}
defer rows.Close()
for rows.Next() {
var (
video = &model.AbnorVideo{}
arc *model.Archive
)
if err = rows.Scan(&video.CID, &video.AID, &video.CTime, &video.VideoTitle); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
if arc, err = s.getArc(video.AID); err != nil {
log.Error("getArc Aid %d, Err %v", video.AID, err)
continue
}
abnCids = append(abnCids, video.ToCids(arc))
}
if err = rows.Err(); err != nil {
log.Error("loadAbnCids rows Err %v", err)
return
}
s.abnCids = abnCids
log.Info("loadAbnCids Update Memory, Length %d", len(abnCids))
}
func (s *Service) loadAbnCidsproc() {
for {
time.Sleep(time.Duration(s.c.Cfg.Abnormal.ReloadFre))
s.loadAbnCids()
}
}
// AbnDebug returns the memory abnormal cids
func (s *Service) AbnDebug() (data []*model.AbnorCids) {
return s.abnCids
}
// AbnormExport exports the abnormal cids in CSV format
func (s *Service) AbnormExport() (data *bytes.Buffer, fileName string) {
var cfg = s.c.Cfg.Abnormal
data = &bytes.Buffer{}
csvWriter := csv.NewWriter(data)
fileName = fmt.Sprintf("attachment;filename=\"Abnormal_%dh_%s.csv\"", cfg.AbnormHours, time.Now().Format(mysql.TimeFormat))
csvWriter.Write(cfg.ExportTitles)
for _, v := range s.abnCids {
csvWriter.Write(v.Export())
}
csvWriter.Flush()
return
}

View File

@@ -0,0 +1,368 @@
package service
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"go-common/app/admin/main/tv/model"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
xtime "go-common/library/time"
"go-common/library/ecode"
"go-common/library/xstr"
"github.com/jinzhu/gorm"
)
const _pagesize = 20
//WaterMarkist water mark list
func (s *Service) WaterMarkist(c *bm.Context, param *model.WaterMarkListParam) (pager *model.WaterMarkListPager, err error) {
var (
order string
total int
markList []*model.WaterMarkList
)
selectStr := []string{
"tv_content.id",
"tv_content.epid",
"tv_content.season_id",
"tv_content.title AS content_title",
"tv_content.mark_time",
"tv_ep_season.category",
"tv_ep_season.title AS season_title",
}
db := s.DB.Model(&model.WaterMarkList{})
db = db.Select(selectStr).
Where("tv_content.is_deleted = ?", 0).
Where("tv_content.mark = ?", model.WatermarkWhite).
Joins("LEFT JOIN tv_ep_season ON tv_ep_season.id = tv_content.season_id")
if param.Category != "" {
db = db.Where("tv_ep_season.category = ?", param.Category)
}
if param.EpID != "" {
db = db.Where("tv_content.epid = ?", param.EpID)
}
if param.SeasonID != "" {
db = db.Where("tv_content.season_id = ?", param.SeasonID)
}
if param.Order == model.OrderDesc {
order = "tv_content.mtime DESC"
} else {
order = "tv_content.mtime ASC"
}
if err = db.Order(order).Offset((param.Pn - 1) * param.Ps).Limit(param.Ps).Find(&markList).Error; err != nil {
return
}
for i := range markList {
attr := markList[i]
attr.Category = s.pgcCatToName(atoi(attr.Category))
}
if err = db.Count(&total).Error; err != nil {
return
}
pager = &model.WaterMarkListPager{
Items: markList,
Page: &model.Page{
Num: param.Pn,
Size: param.Ps,
Total: int(total),
},
}
return
}
//AddEpID add water mark by ep id
func (s *Service) AddEpID(c *bm.Context, ids []int64) (res *model.AddEpIDResp, err error) {
var (
notExist bool
)
res = &model.AddEpIDResp{
Succ: []int64{},
NotExist: []int64{},
Invalids: []int64{},
}
for _, v := range ids {
var mark model.WaterMarkOne
if notExist, err = s.valueWithEpID(v, &mark); err != nil {
return
}
if notExist {
res.NotExist = append(res.NotExist, v)
continue
}
if mark.Mark == model.WatermarkWhite {
res.Invalids = append(res.Invalids, v)
continue
}
if err = s.updateWithEpID(v); err != nil {
return
}
res.Succ = append(res.Succ, v)
}
return
}
//valueWithEpID get value with epid
func (s *Service) valueWithEpID(id int64, m *model.WaterMarkOne) (exist bool, err error) {
if err = s.DB.Where("is_deleted = 0").Where("epid = ?", id).First(m).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return true, nil
}
return
}
return false, nil
}
//updateWithEpID update with epid
func (s *Service) updateWithEpID(id int64) (err error) {
up := map[string]interface{}{
"mark": model.WatermarkWhite,
"mark_time": time.Now().Format("2006-01-02 15:04:05"),
}
if err = s.DB.Model(&model.WaterMarkOne{}).Where("epid=?", id).Update(up).Error; err != nil {
return
}
return
}
//AddSeasonID add water mark with season id
func (s *Service) AddSeasonID(c *bm.Context, ids []int64) (res *model.AddEpIDResp, err error) {
var (
notExist bool
)
res = &model.AddEpIDResp{
Succ: []int64{},
NotExist: []int64{},
Invalids: []int64{},
}
for _, v := range ids {
var mark model.WaterMarkOne
if notExist, err = s.valueWithSeasonID(v, &mark); err != nil {
return
}
if notExist {
res.NotExist = append(res.NotExist, v)
continue
}
if err = s.updateWithSeasonID(v); err != nil {
return
}
res.Succ = append(res.Succ, v)
}
return
}
//valueWithSeasonID get value with season id
func (s *Service) valueWithSeasonID(id int64, m *model.WaterMarkOne) (exist bool, err error) {
w := map[string]interface{}{
"is_deleted": 0,
"season_id": id,
//"mark": model.WatermarkDefault,
}
if err = s.DB.Where(w).First(m).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return true, nil
}
return
}
return false, nil
}
//updateWithSeasonID update with seasonID
func (s *Service) updateWithSeasonID(id int64) (err error) {
up := map[string]interface{}{
"mark": model.WatermarkWhite,
"mark_time": time.Now().Format("2006-01-02 15:04:05"),
}
if err = s.DB.Model(&model.WaterMarkOne{}).Where("season_id=?", id).
Update(up).Error; err != nil {
return
}
return
}
//DeleteWatermark delete watermark
func (s *Service) DeleteWatermark(ids []int64) (err error) {
if len(ids) > 50 {
err = fmt.Errorf("更新数量最多为50条")
return
}
up := map[string]interface{}{
"mark": model.WatermarkDefault,
}
if err = s.DB.Model(&model.WaterMarkOne{}).Where("id in (?)", ids).
Update(up).Error; err != nil {
return
}
return
}
// titleMatch picks the title match seasons
func (s *Service) titleMatch(title string) (match []int64) {
if len(s.snsInfo) == 0 {
return
}
for _, v := range s.snsInfo {
if strings.Contains(v.Title, title) {
match = append(match, v.ID)
}
}
return
}
// TransList picks the transcode list
func (s *Service) TransList(ctx context.Context, req *model.TransReq) (data *model.TransPager, err error) {
var (
db *gorm.DB
cntEp int
cntSn = new(model.SnCount)
)
data = &model.TransPager{
Page: &model.Page{
Num: req.Pn, Size: _pagesize, Total: 0,
},
Items: make([]*model.TransReply, 0),
}
if db, err = s.transReqT(req); err != nil {
return
}
if err = db.Count(&cntEp).Error; err != nil { //
log.Error("countEp Err %v", err)
return
}
if cntEp == 0 { // if no result ,just return
data.CountSn = 0
return
}
if (req.Pn-1)*_pagesize >= cntEp { // if page is much bigger than existing pages, just return error
err = ecode.TvDangbeiPageNotExist
return
}
if err = db.Table("tv_content").Select("COUNT(DISTINCT(season_id)) AS count").First(cntSn).Error; err != nil { // count season
log.Error("countSn Err %v", err)
return
}
db = postReqT(req, db) // order by & limit
if data.Items, err = s.transDB(db); err != nil {
log.Error("transDB Err %v", err)
return
}
data.Page = &model.Page{
Num: req.Pn,
Size: _pagesize,
Total: cntEp,
}
data.CountSn = cntSn.Count
return
}
func (s *Service) transDB(db *gorm.DB) (items []*model.TransReply, err error) {
var rows *sql.Rows
rows, err = db.Select("epid, title, transcoded, apply_time, mark_time, season_id").Rows()
if err != nil {
log.Error("rows Err %v", err)
return
}
for rows.Next() {
var (
cont = &model.TransReply{}
aTime, mTime xtime.Time
)
if err = rows.Scan(&cont.EpID, &cont.Etitle, &cont.Transcoded, &aTime, &mTime, &cont.SeasonID); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
if mTime > xtime.Time(0) { // resolve negative value issue
cont.MarkTime = mTime.Time().Format("2006-01-02 15:04:05")
}
cont.ApplyTime = aTime.Time().Format("2006-01-02 15:04:05")
if sn, ok := s.snsInfo[cont.SeasonID]; ok {
cont.Stitle = sn.Title
cont.Category = s.pgcCatToName(sn.Category)
}
items = append(items, cont)
}
if err = rows.Err(); err != nil {
log.Error("rows Err %v", err)
return
}
return
}
func (s *Service) transReqT(req *model.TransReq) (db *gorm.DB, err error) {
var (
inSids, catSids []int64
ok bool
)
db = s.DB.Model(model.Content{}).Where("apply_time != '0000-00-00 00:00:00'").Where("is_deleted = 0")
if req.Status != "" { // status
switch req.Status {
case "0":
db = db.Where("transcoded = 0")
case "1":
db = db.Where("transcoded = 1")
case "2":
db = db.Where("transcoded = 2")
}
}
if req.EpID != 0 {
db = db.Where("epid = ?", req.EpID)
}
if req.SeasonID != 0 {
db = db.Where("season_id = ?", req.SeasonID)
}
if req.Title != "" {
if inSids = s.titleMatch(req.Title); len(inSids) == 0 {
log.Warn("titleMatch %s, Empty", req.Title)
err = ecode.NothingFound
return
}
}
if req.Category != 0 {
if catSids, ok = s.snsCats[req.Category]; !ok || len(catSids) == 0 {
log.Warn("snsCats %d, Empty", req.Category)
err = ecode.NothingFound
return
}
if len(inSids) > 0 { // title match have sids
if inSids = intersect(catSids, inSids); len(inSids) == 0 {
err = ecode.NothingFound
return
}
} else {
inSids = catSids
}
}
if len(inSids) > 0 {
db = db.Where(fmt.Sprintf("season_id IN (%s)", xstr.JoinInts(inSids)))
}
return
}
func postReqT(req *model.TransReq, db *gorm.DB) (newDb *gorm.DB) {
if req.Order == 2 { // order
newDb = db.Order("apply_time ASC")
} else {
newDb = db.Order("apply_time DESC")
}
newDb = newDb.Offset((req.Pn - 1) * _pagesize).Limit(_pagesize)
return
}
func intersect(big, small []int64) (out []int64) {
var amap = make(map[int64]int, len(big))
for _, v := range big {
amap[v] = 1
}
for _, v := range small {
if _, ok := amap[v]; ok {
out = append(out, v)
}
}
return
}