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,25 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/tv/service/app:all-srcs",
"//app/interface/main/tv/service/audit:all-srcs",
"//app/interface/main/tv/service/favorite:all-srcs",
"//app/interface/main/tv/service/goblin:all-srcs",
"//app/interface/main/tv/service/history:all-srcs",
"//app/interface/main/tv/service/pgc:all-srcs",
"//app/interface/main/tv/service/search:all-srcs",
"//app/interface/main/tv/service/thirdp:all-srcs",
"//app/interface/main/tv/service/tvvip:all-srcs",
"//app/interface/main/tv/service/view:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,77 @@
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"homepage_test.go",
"module_test.go",
"pgc_cards_test.go",
"recommend_test.go",
"region_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"auth.go",
"follow.go",
"homePage.go",
"intervention.go",
"module.go",
"pages.go",
"pgc_cards.go",
"recommend.go",
"region.go",
"service.go",
"tool.go",
"ugc_types.go",
"zone.go",
"zone_index.go",
],
importpath = "go-common/app/interface/main/tv/service/app",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/app:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/audit:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/search:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/search:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr: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,51 @@
package service
import (
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// SnMsg returns the season auth msg
func (s *Service) SnMsg(sid int64) (ok bool, msg string, err error) {
var season *model.SnAuth
if season, err = s.cmsDao.SnAuth(ctx, sid); err != nil {
log.Error("SnMsg LoadSeason Sid %d, Err %v", sid, err)
return
}
ok, msg = s.cmsDao.SnErrMsg(season)
return
}
// EpMsg returns the ep and its season auth msg
func (s *Service) EpMsg(epid, build int64) (ok bool, msg string, err error) {
var (
ep *model.EpAuth
season *model.SnAuth
cfg = s.conf.Cfg.VipMark.LoadepMsg
epMeta *model.EpCMS
)
if ep, err = s.cmsDao.EpAuth(ctx, epid); err != nil {
log.Error("EpMsg LoadEP epid %d, Err %v", epid, err)
return
}
if ok, msg = s.cmsDao.EpErrMsg(ep); !ok { // if ep is already not ok, just return, no need to check its season
return
}
if season, err = s.cmsDao.SnAuth(ctx, ep.SeasonID); err != nil { // ep ok, check season
log.Error("SnMsg LoadSeason Sid %d, Err %v", ep.SeasonID, err)
return
}
if ok, msg = s.cmsDao.SnErrMsg(season); !ok {
return
}
if build < cfg.Build { // old version logic, remind upgrade
if epMeta, err = s.cmsDao.LoadEpCMS(ctx, epid); err != nil {
log.Error("EpMsg LoadEpCMS epid %d, Err %v", epid, err)
return
}
if !epMeta.IsFree() { // if old version checks paid ep, remind the user by upgrade message
return false, cfg.Msg, nil
}
}
return
}

View File

@@ -0,0 +1,85 @@
package service
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// FollowData gets the follow data from pgc api
func (s *Service) FollowData(ctx context.Context, accessKey string) (res []*model.Follow) {
var (
err error
result []*model.Follow
)
result, err = s.dao.FollowData(ctx, s.TVAppInfo, accessKey)
if err != nil {
log.Error("[LoadHP] Can't Pick PGC Follow Data, Err: %v", err)
}
res = s.followInterv(result)
if res == nil {
log.Error("Follow Data is Nil!")
}
return
}
// Intervention
func (s *Service) followInterv(result []*model.Follow) (newRes []*model.Follow) {
for _, v := range result {
var (
sid = int64(atoi(v.SeasonID))
epid int64
err error
snCache *model.SeasonCMS
epCache *model.EpCMS
)
// filter not passed data
season, err := s.cmsDao.SnAuth(ctx, sid)
if err != nil {
log.Error("followInterv LoadSeason[%d] ERROR [%v]", sid, err)
continue
}
if season == nil {
log.Info("followInterv LoadSeason[%d] Can't Found", sid)
continue
}
if !(season.IsDeleted == 0 && season.Check == 1 && season.Valid == 1) {
log.Info("SEASON[%d] is not authorized to play, DETAILS: %v", sid, season)
continue
}
// season intervention
snCache, err = s.cmsDao.GetSnCMSCache(ctx, sid)
if err != nil {
log.Error("[cardInterv] ErrorCache GetSnCMSCache SeasonID(%d) (%v)", sid, err)
} else if snCache != nil {
if snCache.Title != "" {
v.Title = snCache.Title
}
if snCache.Cover != "" {
v.Cover = snCache.Cover
}
if snCache.NeedVip() {
v.CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
}
// ep intervention
if v.NewEP == nil {
continue
}
epid = int64(atoi(v.NewEP.EpisodeID))
epCache, err = s.cmsDao.GetEpCMSCache(ctx, epid)
if err != nil {
log.Error("[cardInterv] ErrorCache GetEpCMSCache EPID(%d) (%v)", epid, err)
} else if epCache != nil {
if epCache.Title != "" {
v.NewEP.IndexTitle = epCache.Title
}
if epCache.Cover != "" {
v.NewEP.Cover = epCache.Cover
}
}
newRes = append(newRes, v)
}
return
}

View File

@@ -0,0 +1,225 @@
package service
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_homepageID = 0
_jpType = 1
recomCategory = 1
)
// load homepage data
func (s *Service) loadHome(ctx context.Context) (err error) {
if len(s.ZoneData) == 0 { // pick area's data
log.Error("[loadHP] Can't Pick Zone Top Data!!")
return
}
homepage := model.Homepage{}
if homepage.Recom, err = s.HomeRecom(ctx); err != nil {
log.Error("[loadHP] Can't Load Home Recom Data !")
return
} // recom part
s.buildHeaderSids(homepage.Recom)
homepage.Lists, homepage.Latest = s.HomeList() // list part
s.HomeData = &homepage
return
}
// HomeFollow picks the homepage with follow data
func (s *Service) HomeFollow(ctx context.Context, req *model.ReqHomeFollow) (data *model.Homepage, err error) {
if s.HomeData == nil {
log.Error("HomeFollow Data is Nil ")
err = ecode.ServiceUnavailable
return
}
data = &(*s.HomeData)
if req.AccessKey != "" {
data.Follow = s.FollowData(ctx, req.AccessKey)
}
var newRecom []*model.Card // old version, filter UGC data
for _, v := range data.Recom {
if !v.IsUGC() {
newRecom = append(newRecom, v)
}
}
data.Recom = newRecom
return
}
func (s *Service) buildHeaderSids(Recom []*model.Card) {
if len(Recom) == 0 {
log.Error("Homepage Recom is Empty!")
return
}
newMap := make(map[int]int)
for _, v := range Recom {
newMap[v.SeasonID] = 1
}
if len(newMap) > 0 {
s.HeaderSids = newMap
}
}
// pgcIndexs picks and treats PGC Index Data
func (s *Service) pgcIndexs(ctx context.Context) (indexCards []*model.Card) {
var moduleOrder = s.conf.Cfg.ZonesInfo.ZonesName
pgcData, err := s.dao.HeaderData(ctx, s.TVAppInfo) // pick data from PGC API
if err != nil {
log.Error("[loadHP] Can't Pick PGC/AI Data, Err: %v", err)
return
}
for _, v := range moduleOrder { // arrange data according to hard-code order
if value, ok := pgcData[v]; ok {
for _, card := range value {
if card.NewEP != nil {
card.Type = _typePGC // define card type
indexCards = append(indexCards, card)
}
}
} else {
log.Error("PGC Data Missing %s data", v)
}
}
if err := s.cardIntervSn(indexCards); err != nil { // replace cover & title by CMS data
log.Error("[cardIntervSn] ERROR [%v]", err)
}
return
}
// HomeRecom for the Homepage header, merge the rank data and the intervention data and gets the final header data
func (s *Service) HomeRecom(ctx context.Context) (homeRecom []*model.Card, err error) {
var (
hsize = s.ZonesInfo[_homepageID].Top
intervReq = &model.ReqZoneInterv{
RankType: _homepageID,
Category: recomCategory,
Limit: hsize,
}
interv []*model.Card
)
indexData := s.pgcIndexs(ctx) // 1. Treat PGC data
resp, err := s.dao.ZoneIntervs(ctx, intervReq) // 2. Treat Interventions
if err != nil {
log.Error("[LoadPages] Can't Pick Intervention Data, Err: %v", err)
return
}
if interv, err = s.intervToCards(ctx, resp); err != nil {
log.Error("[LoadPages] Can't Combine Intervention Data, Err: %v", err)
return
}
homeRecom = mergeSlice(interv, indexData)
homeRecom = duplicate(homeRecom) // remove duplicated data
homeRecom = cutSlice(homeRecom, hsize)
return
}
// hideIndexShow for the configured zones to hide the index show, we modify their list
func (s *Service) hideIndexShow(list map[string][]*model.Card) {
if len(s.conf.Homepage.HideIndexShow) == 0 {
return
}
for _, zone := range s.conf.Homepage.HideIndexShow {
if _, ok := list[zone]; !ok {
continue
} else {
for k, v := range list[zone] { // hide index show without the influence to zone page
var newCard = *v
newCard.NewEP = &model.NewEP{
ID: v.NewEP.ID,
Index: v.NewEP.Index,
Cover: v.NewEP.Cover,
IndexShow: "",
}
list[zone][k] = &newCard
}
log.Info("Hide Index Show for Zone %s", zone)
}
}
}
// HomeList gets the five zones' list for the homepage
func (s *Service) HomeList() (list map[string][]*model.Card, latest []*model.Card) {
list = make(map[string][]*model.Card)
conf := s.ZonesInfo[_homepageID]
var (
idList = map[int][]*model.Card{} // the zone list, key is ID
listSize = conf.Bottom // the homepage list size
)
if len(s.ZoneData) == 0 {
log.Error("ZoneData is Empty!")
return
}
// remove the items alraedy in the headers
for k, v := range s.ZoneData {
// jp resources fill the latest part
if k == _jpType {
latest, list[s.ZonesInfo[k].Name] = s.HomeJP(v)
continue
}
idList[k] = []*model.Card{}
for _, vcard := range v {
if len(idList[k]) >= listSize { // we only pick 10 data for the lists
break
}
if _, ok := s.HeaderSids[vcard.SeasonID]; !ok {
idList[k] = append(idList[k], vcard)
}
}
list[s.ZonesInfo[k].Name] = idList[k]
}
// hide index show for configured list
s.hideIndexShow(list)
return
}
// HomeJP picks the JP resources to fill the latest part and the list part
func (s *Service) HomeJP(zoneData []*model.Card) (latest []*model.Card, list []*model.Card) {
var (
Intervs []*model.Card // homepage middle interventions
err error
middleSize = s.ZonesInfo[_homepageID].Middle
listSize = s.ZonesInfo[_homepageID].Bottom
middleM = s.ZonesInfo[_homepageID].MiddleM
)
// get homepage latest's intervention
if middleM != 0 {
if Intervs, err = s.modPGCIntervs(ctx, middleM, middleSize); err != nil {
return
}
} else {
if Intervs, err = s.getIntervs(context.TODO(), _homepageID, _latest, middleSize); err != nil {
return
}
}
// remove duplicated
allCards := mergeSlice(Intervs, zoneData)
allCards = duplicate(allCards)
latestSource := []*model.Card{}
// pick enough data for middle and list by checking duplication with the header data
for _, vcard := range allCards {
if len(latestSource) >= middleSize+listSize { // we pick enough data for the middle and the list of jp
break
}
if _, ok := s.HeaderSids[vcard.SeasonID]; !ok { // remove duplicated with the header data
latestSource = append(latestSource, vcard)
}
}
// cut the latest part and add them into headerSids for duplication check
latest = cutSlice(latestSource, middleSize)
for _, v := range latest {
s.HeaderSids[v.SeasonID] = 1
}
// cut JP list part
if len(latest) < middleSize {
list = []*model.Card{}
return
}
list = cutSlice(latestSource[middleSize:], listSize)
return
}

View File

@@ -0,0 +1,31 @@
package service
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_TreatIntervs(t *testing.T) {
Convey("Treat Interventions, result's length between 0 and 5", t, WithService(func(s *Service) {
res, err := s.HomeRecom(ctx)
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
So(len(res), ShouldBeLessThanOrEqualTo, s.ZonesInfo[_homepageID].Top)
}))
}
func TestService_FollowData(t *testing.T) {
Convey("Follow Data API is ok", t, WithService(func(s *Service) {
res := s.FollowData(ctx, "00f04e06e1caf7f8cbe17580d3fa3e62")
So(res, ShouldNotBeNil)
}))
}
func TestService_HomeList(t *testing.T) {
Convey("Follow Data API is ok", t, WithService(func(s *Service) {
res, latest := s.HomeList()
So(len(res), ShouldNotEqual, 0)
So(len(latest), ShouldNotEqual, 0)
}))
}

View File

@@ -0,0 +1,151 @@
package service
import (
"time"
"context"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/search"
"go-common/library/log"
)
// cardIntervSn, makes season intervention effective for cards
func (s *Service) cardIntervSn(cards []*model.Card) (err error) {
var (
snMetas map[int64]*model.SeasonCMS
epMetas map[int64]*model.EpCMS
sids []int64
newestEPIDs []int64
)
for _, card := range cards {
if card.NewEP != nil {
sids = append(sids, int64(card.SeasonID))
newestEPIDs = append(newestEPIDs, card.NewEP.ID)
}
}
if snMetas, err = s.cmsDao.LoadSnsCMSMap(ctx, sids); err != nil {
log.Error("[cardIntervSn] loadSnCMSMap Sids: %v, Err %v", sids, err)
return
}
if epMetas, err = s.cmsDao.LoadEpsCMS(ctx, newestEPIDs); err != nil {
log.Error("[cardIntervSn] loadEpCMS epids: %v, Err %v", newestEPIDs, err)
return
}
for _, card := range cards {
// season intervention
sid := int64(card.SeasonID)
if snCache, ok := snMetas[sid]; !ok {
log.Error("LoadSnsCMS Miss Info for Sid: %d", sid)
continue
} else { // intervention
if snCache.Title != "" {
card.Title = snCache.Title
}
if snCache.Cover != "" {
card.Cover = snCache.Cover
}
if snCache.NeedVip() { // card add vip corner mark
card.CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
}
// ep intervention
epid := card.NewEP.ID
if epCache, ok := epMetas[epid]; !ok {
log.Error("LoadSnsCMS Miss Info for Sid: %d", epid)
continue
} else { // intervention
if epCache.Cover != "" {
card.NewEP.Cover = epCache.Cover
}
}
}
return
}
// load index show proc
func (s *Service) indexShowproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.IndexShowReload))
s.indexShow()
}
}
func (s *Service) indexShow() (err error) {
defer elapsed("indexShow")()
var (
indexShows map[int64]string
sids []int64
)
if sids, err = s.dao.PassedSns(ctx); err != nil {
log.Error("[loadIndexShow] AllIntervs Error %v", err)
return
}
if indexShows, err = s.PgcCards(sids); err != nil {
log.Error("[loadIndexShow] AllIntervs Error %v", err)
return
}
log.Info("Reload Types For Index_Show, Origin:%d, Length: %d", len(sids), len(indexShows))
if len(indexShows) > 0 {
s.PGCIndexShow = indexShows
}
return
}
func (s *Service) filterIntervs(ctx context.Context) (err error) {
defer elapsed("filterIntervs")()
var (
sids, aids, rmSids, rmAids []int64
pgcAuth map[int64]*model.SnAuth
ugcAuth map[int64]*model.ArcCMS
)
if sids, aids, err = s.dao.AllIntervs(ctx); err != nil {
log.Error("[filterIntervs] AllIntervs Error %v", err)
return
}
if pgcAuth, err = s.cmsDao.LoadSnsAuthMap(ctx, sids); err != nil {
log.Error("[filterIntervs] LoadSnsAuthMap Error %v, Sids %v", err, sids)
return
}
if ugcAuth, err = s.cmsDao.LoadArcsMediaMap(ctx, aids); err != nil {
log.Error("[filterIntervs] LoadArcsMediaMap Error %v, Aids %v", err, aids)
return
}
for sid, pAuth := range pgcAuth {
if !pAuth.CanPlay() {
rmSids = append(rmSids, sid)
}
}
for aid, arcAuth := range ugcAuth {
if !arcAuth.CanPlay() {
rmAids = append(rmAids, aid)
}
}
if len(rmSids) > 0 || len(rmAids) > 0 {
if err = s.dao.RmInterv(ctx, rmAids, rmSids); err != nil {
log.Error("RmInterv Aids %v, Sids %v, Err %v", aids, sids, err)
return
}
log.Warn("[filterIntervs] Ori Sids %d, Aids %d, To Remove Sids %v, Aids %v", len(sids), len(aids), rmSids, rmAids)
}
return
}
func (s *Service) ppIdxIntev(ctx context.Context) (err error) {
var (
newIdx *search.IdxIntervSave
)
if newIdx, err = s.dao.IdxIntervs(ctx); err != nil {
log.Error("prepareIdxIntervs Err %v")
return
}
s.IdxIntervs = newIdx
log.Info("ppIdxInterv refresh, PGC %d, UGC %d", len(newIdx.Pgc), len(newIdx.Ugc))
return
}
func (s *Service) ppIdxIntervproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.EsIntervReload))
s.ppIdxIntev(context.Background())
}
}

View File

@@ -0,0 +1,432 @@
package service
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_homeFocus = 1
_fiveFocus = 2
_sixFocus = 3
_verticalOneList = 4
_verticalTwoList = 5
_horizontalList = 6
_followMod = 7
)
func (s *Service) loadMods(ctx context.Context) (err error) {
var (
pageMods []*model.Module
newMap = make(map[int][]*model.Module)
zoneCfg = s.conf.Cfg.ZonesInfo
pages = append([]int{}, zoneCfg.PGCZonesID...)
)
for _, v := range zoneCfg.UGCZonesID {
pages = append(pages, int(v))
}
// load home & other module pages
if newMap[_homepageID], err = s.ModHome(); err != nil {
return
}
for _, v := range s.RegionInfo {
pages = append(pages, v.PageID)
}
pages = remRep(pages)
log.Info("loadModsRegion Len Pages %d, RegionInfo %d", len(pages), len(s.RegionInfo))
for _, v := range pages {
if pageMods, err = s.pageData(ctx, v); err != nil {
log.Error("LoadModPage Data PID %d, Err %v", v, err)
return
}
newMap[v] = pageMods
}
if len(newMap) > 0 {
s.ModPages = newMap
}
return
}
// ModHome load modularized homepage
func (s *Service) ModHome() (homepage []*model.Module, err error) {
var (
home = []*model.Module{}
pid = _homepageID
pageMods []*model.Module
)
if pageMods, err = s.pageData(ctx, pid); err != nil {
log.Error("LoadModPage Data PID %d, Err %v", pid, err)
return
}
// pick old logic homepage recom
if len(s.HomeData.Recom) != 0 {
home = append(home, &model.Module{
Type: _homeFocus,
PageID: pid,
Data: cardTransform(s.HomeData.Recom),
})
}
homepage = mergeSliceM(home, pageMods) // add home mods data into homepage
return
}
// PageFollow serves the http level, it picks the follow location and fill in with follow data and then output
func (s *Service) PageFollow(c context.Context, req *model.ReqPageFollow) (res []*model.Module, err error) {
var (
ok bool
resMods []*model.Module
cfgBuild = s.conf.Cfg.PGCFilterBuild
)
if resMods, ok = s.ModPages[req.PageID]; !ok {
err = ecode.ServiceUnavailable
log.Error("ModPage %d, Err %v", req.PageID, err)
return
}
// not logged in AND no need to filter ugc
if req.AccessKey == "" && req.Build > cfgBuild {
res = resMods
return
}
//decorate follow data + build filter logic
for _, v := range resMods {
modV := *v
if v.Type == _followMod && req.AccessKey != "" {
modV.Data = followToMod(s.FollowData(c, req.AccessKey))
res = append(res, &modV)
continue
}
if req.Build <= cfgBuild { // if old version, only output pgc data
if v.IsUGC() {
continue
}
if len(v.Data) > 0 {
var newData = []*model.ModCard{}
for _, tp := range v.Data {
if tp.IsUGC() {
continue
}
newData = append(newData, tp)
}
log.Info("ModID %d, Data Length %d, After UGC Filter Length %d", v.ID, len(v.Data), len(newData))
modV.Data = newData
}
}
res = append(res, &modV)
}
return
}
// dupRecom removes the recom sids from the ugc or pgc data
func dupRecom(recomSids map[int]int, CardList map[int][]*model.Card) {
if len(recomSids) > 0 { // remove recom data from PGC Data
for srcType, srcList := range CardList {
var filterS []*model.Card
for _, source := range srcList {
if _, ok := recomSids[source.SeasonID]; !ok {
filterS = append(filterS, source)
}
}
CardList[srcType] = filterS
}
}
}
// modMapping is used to map new zone idx to old zone idx pages
func (s *Service) modMapping(mod *model.Module) {
var (
tp *arcwar.Tp
cfg = s.conf.Cfg.ZonesInfo
pid int32
err error
)
mod.MoreTreat() // simple mapping with the page ID
if !mod.OnHomepage() || (mod.OnHomepage() && !mod.JumpNewIdx()) { // if not homepage or it's homepage but it jumps to old idx and zone page
return
}
if !mod.IsUGC() { // pgc jump to the source's old index page
mod.MorePage = mod.Source
return
}
if tp, err = s.arcDao.TypeInfo(int32(mod.Source)); err != nil { // if source can't be found, jump to the default Idx ID
mod.MorePage = cfg.OldIdxJump
return
}
if tp.Pid == 0 { // as it's UGC, let's find it's first level
pid = tp.ID
} else {
pid = tp.Pid
}
if oldIdxCat, ok := cfg.OldIdxMapping[fmt.Sprintf("%d", pid)]; ok { // if we can mapping, we map to the old index page
mod.MorePage = oldIdxCat
} else { // otherwise we jump to the default index page
mod.MorePage = cfg.OldIdxJump
}
}
// modData loads the data of a module
func (s *Service) pageData(ctx context.Context, pageID int) (res []*model.Module, err error) {
var (
mods []*model.Module
respMod *model.RespModInterv
recomSids = make(map[int]int)
recomAids = make(map[int]int)
PGCListM = copyCardList(s.PGCOrigins)
UGCListM = copyCardList(s.UGCOrigins)
)
if mods, err = s.dao.ModPage(ctx, pageID); err != nil {
log.Error("ModPage ID %d, Error %v", pageID, err)
return
}
for _, v := range mods {
s.modMapping(v) // treat the new index page mapping logic
}
// if there is no mod, no need to continue the logic
if len(mods) == 0 {
log.Error("ModPage ID %d, Modules Empty", pageID)
return
}
// remove the page's intervention and the home recom from the source data
for _, mod := range mods { // get all the mods' intervention
if respMod, err = s.dao.ModIntervs(ctx, mod.ID, mod.Capacity); err != nil || respMod == nil {
log.Error("modPGCIntervs ModID %d, Capacity %d, Err %v", mod.ID, mod.Capacity, err)
continue
}
if len(respMod.SIDs) > 0 {
for _, sid := range respMod.SIDs {
recomSids[int(sid)] = 1
}
}
if len(respMod.AIDs) > 0 {
for _, avid := range respMod.AIDs {
recomAids[int(avid)] = 1
}
}
}
if pageID == _homepageID { // if it's the homepage, we need also to filter the home recom
for _, focus := range s.HomeData.Recom {
if focus.IsUGC() {
recomAids[focus.SeasonID] = 1
} else {
recomSids[focus.SeasonID] = 1
}
}
}
dupRecom(recomSids, PGCListM) // filter pgc
dupRecom(recomAids, UGCListM) // filter ugc
// build each module's data
for _, mod := range mods {
switch mod.Type {
case _sixFocus, _fiveFocus, _verticalOneList, _verticalTwoList, _horizontalList:
if err = s.modData(ctx, &model.ReqModData{
Mod: mod,
PGCListM: PGCListM,
UGCListM: UGCListM,
}); err != nil {
log.Error("Load PageID %d, Mod %v, Data error %v", pageID, mod, err)
return
}
res = append(res, mod)
case _followMod:
res = append(res, mod)
default: // ignore invalid module
log.Error("Invalid ModID %d, Type %d", mod.ID, mod.Type)
}
}
return
}
// focusData loads fiveFocus or sixFocus's data
func (s *Service) modData(ctx context.Context, req *model.ReqModData) (err error) {
mod := req.Mod
var (
intervs []*model.Card
capacity int
pid = mod.PageID
backupCards []*model.Card
ok bool
)
// capacity logic
if mod.Type == _fiveFocus {
capacity = 5
} else if mod.Type == _sixFocus {
capacity = 6
} else { // lists
capacity = mod.Capacity
}
// get interventions
if intervs, err = s.modIntervs(ctx, mod.ID, capacity); err != nil {
return
}
mod.Data = cardTransform(intervs)
// pick up backup data
if mod.IsUGC() {
if backupCards, ok = req.UGCListM[mod.Source]; !ok {
log.Error("UGCListM Page %d Source %d is Empty!", pid, mod.Source)
}
} else {
if backupCards, ok = req.PGCListM[mod.Source]; !ok {
log.Error("PGCListM Page %d Source %d is Empty!", pid, mod.Source)
return
}
}
// merge intervention with pgc source data
allCards := mergeSlice(intervs, backupCards)
allCards = duplicate(allCards)
mod.Data = cardTransform(cutSlice(allCards, capacity))
// remove used cards from the source data
backupCards = allCards[len(mod.Data):]
if mod.IsUGC() {
req.UGCListM[mod.Source] = backupCards
usedCards := allCards[0:len(mod.Data)]
s.parentDup(usedCards, req)
} else {
req.PGCListM[mod.Source] = backupCards
}
return
}
// parentDup is dedicated for second level ugc types, pick the used cards
// and find its father list, and remove the used card from the father list also
func (s *Service) parentDup(usedCards []*model.Card, req *model.ReqModData) {
if len(usedCards) == 0 { // if none of cards has been used
return
}
var (
secondTid = int32(req.Mod.Source)
err error
tinfo *arcwar.Tp
children []*arcwar.Tp
)
if tinfo, err = s.arcDao.TypeInfo(secondTid); err != nil {
log.Error("parentDup TypeInfo Tid %d, Err %v", secondTid, err)
return
}
usedCardMap := make(map[int]int) // usedCards build map
for _, v := range usedCards {
usedCardMap[v.SeasonID] = 1
}
if tinfo.Pid == 0 { // if it's first level
if children, err = s.arcDao.TypeChildren(tinfo.ID); err != nil {
log.Error("parentDup Pid %d Cant found children", tinfo.ID)
return
}
for _, child := range children {
childList, ex := req.UGCListM[int(child.ID)]
if !ex {
log.Warn("parentDup Pid %d ChildID %d is not in UGClistM", tinfo.ID, child.ID)
continue
}
req.UGCListM[int(child.ID)] = dupList(childList, usedCardMap)
}
return
}
parentSrc, exist := req.UGCListM[int(tinfo.Pid)] // if it's second level type
if !exist || len(parentSrc) == 0 { // if parent list doesn't exist, just return
return
}
req.UGCListM[int(tinfo.Pid)] = dupList(parentSrc, usedCardMap)
}
// dupList travels the list, remove the targetIDs cards
func dupList(list []*model.Card, targetIDs map[int]int) (newlist []*model.Card) {
for _, vv := range list {
if _, dup := targetIDs[vv.SeasonID]; !dup {
newlist = append(newlist, vv)
}
}
return
}
// copy the pgc data for each page, to guarantee they are all independent for each page
func copyCardList(source map[int][]*model.Card) (copied map[int][]*model.Card) {
copied = make(map[int][]*model.Card)
for k, v := range source {
copied[k] = []*model.Card{}
for _, vcard := range v {
copied[k] = append(copied[k], &(*vcard))
}
}
return
}
// modPGCIntervs gets the interventions of a module, only treat PGC data
func (s *Service) modPGCIntervs(c context.Context, modID int, nbLimit int) (modCards []*model.Card, err error) {
var (
respInterv *model.RespModInterv
)
if respInterv, err = s.dao.ModIntervs(c, modID, nbLimit); err != nil || respInterv == nil {
log.Error("[modPGCIntervs] ModID: %d, Limit %d, Can't Pick Intervention Data, Err: %v", modID, nbLimit, err)
return
}
if len(respInterv.Ranks) == 0 {
log.Warn("[LoadPages] Mod %d, NbLimit %d, Intervention Empty", modID, nbLimit)
return // empty result
}
modCards, _ = s.transformCards(respInterv.SIDs)
return
}
// modIntervs gets the interventions of a module, treat both PGC & UGC Data
func (s *Service) modIntervs(c context.Context, modID int, nbLimit int) (modCards []*model.Card, err error) {
var (
resp *model.RespModInterv
)
if resp, err = s.dao.ModIntervs(c, modID, nbLimit); err != nil || resp == nil {
log.Error("[modIntervs] ModID: %d, Limit %d, Can't Pick Intervention Data, Err: %v", modID, nbLimit, err)
return
}
if len(resp.Ranks) == 0 {
log.Warn("[LoadPages] Mod %d, NbLimit %d, Intervention Empty", modID, nbLimit)
return // empty result
}
return s.intervToCards(c, resp)
}
func (s *Service) intervToCards(ctx context.Context, resp *model.RespModInterv) (modCards []*model.Card, err error) {
if len(resp.Ranks) == 0 {
return
}
pgcCards, pgcCardsMap := s.transformCards(resp.SIDs) // transform PGC
if len(pgcCards) == len(resp.Ranks) { // if all the ranks are pgc type
return pgcCards, err
}
var ugcCardsMap map[int64]*model.ArcCMS
if ugcCardsMap, err = s.cmsDao.LoadArcsMediaMap(ctx, resp.AIDs); err != nil { // transform UGC
log.Error("[modIntervs] Can't Pick MediaCache Data, Aids: %v, Err: %v", resp.AIDs, err)
return
}
for _, v := range resp.Ranks {
if v.IsUGC() {
if arc, ok := ugcCardsMap[v.ContID]; ok {
modCards = append(modCards, arc.ToCard())
} else {
log.Warn("modIntervs, ContID:%d, ContType:%d, Not found", v.ContID, v.ContType)
}
} else {
if sn, ok := pgcCardsMap[int(v.ContID)]; ok {
modCards = append(modCards, sn)
} else {
log.Warn("modIntervs, ContID:%d, ContType:%d, Not found", v.ContID, v.ContType)
}
}
}
return
}
// remRep delete repeat .
func remRep(slc []int) (result []int) {
tempMap := map[int]byte{}
for _, e := range slc {
l := len(tempMap)
tempMap[e] = 0
if len(tempMap) != l {
result = append(result, e)
}
}
return result
}

View File

@@ -0,0 +1,36 @@
package service
import (
"testing"
"encoding/json"
"fmt"
"go-common/app/interface/main/tv/model"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_HomeRecom(t *testing.T) {
Convey("TestService_HomeRecom", t, WithService(func(s *Service) {
homepage, err := s.ModHome()
So(homepage, ShouldNotBeNil)
So(err, ShouldBeNil)
}))
}
func TestService_PageFollow(t *testing.T) {
Convey("TestService_PageFollow", t, WithService(func(s *Service) {
res, err := s.PageFollow(ctx, &model.ReqPageFollow{
AccessKey: "",
PageID: 1,
Build: 1011,
})
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
for _, v := range res {
data, _ := json.Marshal(v)
fmt.Println(string(data))
}
}))
}

View File

@@ -0,0 +1,51 @@
package service
import (
"context"
"time"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// loadPagesproc loads the mod&zone&home pages
func (s *Service) loadPagesproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.PageReload))
s.loadPages()
}
}
func (s *Service) loadPages() {
defer elapsed("loadPages")() // record page loading time
pCtx := context.TODO()
// prepare pgc & ugc data
g, errCtx := errgroup.WithContext(pCtx)
g.Go(func() (err error) {
return s.filterIntervs(errCtx)
})
g.Go(func() (err error) {
return s.prepareUGCData(errCtx)
})
g.Go(func() (err error) {
s.preparePGCData(errCtx)
return nil
})
if err := g.Wait(); err != nil {
log.Error("loadPages PrepareData Err %v", err)
return
}
// load page data
if err := s.zonesData(pCtx); err != nil {
log.Error("loadPages, zonesData Err %v", err)
return
} // zonepage depends on PGCData refresh
if err := s.loadHome(pCtx); err != nil {
log.Error("loadPages, homeData Err %v", err)
return
} // homepage depends on the zone data from Zone's data
if err := s.loadMods(pCtx); err != nil {
log.Error("loadPages, Module Data Err %v", err)
return
} // modpage depends on home's recom and zone's pgc data
}

View File

@@ -0,0 +1,43 @@
package service
import (
"go-common/app/interface/main/tv/model"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_pgcPiece = 50
)
// PgcCards treat the slice of int, and call the PGC api
func (s *Service) PgcCards(ids []int64) (res map[int64]string, err error) {
var (
length = len(ids)
nbBatch = length / _pgcPiece // number of batch to run
resCards map[string]*model.SeasonCard
)
if length%_pgcPiece != 0 {
nbBatch = nbBatch + 1
}
res = make(map[int64]string)
for i := 0; i < nbBatch; i++ {
begin := i * _pgcPiece
end := (i + 1) * _pgcPiece
if end > length {
end = length
}
batchIDs := ids[begin:end]
batchStr := xstr.JoinInts(batchIDs)
if resCards, err = s.dao.PgcCards(ctx, batchStr); err != nil {
log.Error("PGCCards IDs: %s, Err: %v", batchStr, err)
return
}
for k, v := range resCards {
if sid := int64(atoi(k)); sid > 0 && v.NewEP != nil && v.NewEP.IndexShow != "" {
res[sid] = v.NewEP.IndexShow
}
}
}
return
}

View File

@@ -0,0 +1,25 @@
package service
import (
"testing"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_PgcSeasonCards(t *testing.T) {
Convey("PgcCards", t, WithService(func(s *Service) {
ids := []int64{}
for i := int64(1); i < 7000; i++ {
ids = append(ids, i)
}
res, err := s.PgcCards(ids)
So(err, ShouldBeNil)
fmt.Println(len(res))
for k, v := range res {
fmt.Println("Key: ", k)
fmt.Println("Value: ", v)
}
}))
}

View File

@@ -0,0 +1,63 @@
package service
import (
"go-common/app/interface/main/tv/model"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_retry = 3
)
// RecomFilter gets the recommend data from PGC API and filter the not passed seasons
func (s *Service) RecomFilter(sid string, stype string) (res []*model.Recom, err error) {
var (
sids []int64
result []*model.Recom
cmsRes map[int64]*model.SeasonCMS
)
log.Info("[RecomFilter] Sid: %s, Stype: %s", sid, stype)
for i := 0; i < _retry; i++ {
if result, err = s.dao.RecomData(ctx, s.TVAppInfo, sid, stype); err == nil {
break
}
}
if err != nil {
log.Error("[RecomFilter] Can't Pick PGC Recom Data, Err: %v", err)
return
}
if len(result) == 0 {
log.Error("[RecomFilter] No need to filter for Sid: %s. Length = 0", sid)
return
}
for _, v := range result {
season, err2 := s.cmsDao.SnAuth(ctx, v.SeasonID)
if err != nil {
log.Error("[RecomFilter] LoadSeason[%d] ERROR [%v]", sid, err2)
continue
}
if season == nil {
log.Info("[RecomFilter] LoadSeason[%d] Can't Found", v.SeasonID)
continue
}
if !season.CanPlay() {
log.Info("[RecomFilter] SEASON[%d] is not authorized to play, DETAILS: %v", season.ID, season)
continue
}
res = append(res, v)
sids = append(sids, v.SeasonID)
}
// add vip corner mark
if cmsRes, err = s.cmsDao.LoadSnsCMSMap(ctx, sids); err != nil {
log.Error("[recomm.RecomFilter] sids(%s) error(%v)", xstr.JoinInts(sids), err)
return
}
for idx, v := range res {
if r, ok := cmsRes[v.SeasonID]; ok && r.NeedVip() {
res[idx].CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
}
log.Info("[RecomFilter] PGC Data: %d, Filtered Data: %d", len(result), len(res))
return
}

View File

@@ -0,0 +1,16 @@
package service
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_RecomFilter(t *testing.T) {
Convey("RecomFilter test", t, WithService(func(s *Service) {
res, err := s.RecomFilter("23874", "3")
So(err, ShouldBeNil)
fmt.Println(res)
}))
}

View File

@@ -0,0 +1,40 @@
package service
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// Regions .
func (s *Service) Regions(ctx context.Context) (res []*model.Region, err error) {
res = s.RegionInfo
return
}
func (s *Service) loadRegionproc() {
for {
time.Sleep(time.Duration(s.conf.Region.StopSpan))
s.loadRegion()
}
}
func (s *Service) loadRegion() {
var (
m int64
err error
res []*model.Region
)
if res, err = s.dao.Regions(ctx); err != nil {
log.Error("s.dao.Regions error(%v)", err)
}
if len(res) != 0 && err == nil {
s.RegionInfo = res
}
if m, err = s.dao.FindLastMtime(ctx); err != nil {
log.Error("s.dao.FindLastMtime error(%v)", err)
}
s.MaxTime = m
}

View File

@@ -0,0 +1,16 @@
package service
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_Regions(t *testing.T) {
Convey("test service regions", t, WithService(func(s *Service) {
res, err := s.Regions(context.Background())
So(err, ShouldBeNil)
So(len(res), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,88 @@
package service
import (
"context"
"go-common/app/interface/main/tv/conf"
appDao "go-common/app/interface/main/tv/dao/app"
arcDao "go-common/app/interface/main/tv/dao/archive"
auditDao "go-common/app/interface/main/tv/dao/audit"
cmsDao "go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/search"
"go-common/app/interface/main/tv/model"
seaMdl "go-common/app/interface/main/tv/model/search"
)
var ctx = context.Background()
// Service .
type Service struct {
// dao
dao *appDao.Dao
cmsDao *cmsDao.Dao
auditDao *auditDao.Dao
searchDao *search.Dao
arcDao *arcDao.Dao
// cfg
conf *conf.Config
TVAppInfo *conf.TVApp // tv app basic info
// memory
HomeData *model.Homepage // homepage data
ZoneData map[int][]*model.Card // zone list data for homepage
RankData map[int]map[string][]*model.Card // zone pages data
HeaderSids map[int]int // use to remove duplicated ones
ZoneSids map[int]map[int]int // same use as HeaderSids
ZonesInfo map[int]*conf.PageCfg // zones information data
PGCOrigins map[int][]*model.Card // pgc zone list data
UGCOrigins map[int][]*model.Card // pgc types data
PGCIndexShow map[int64]string // pgc index show data
ModPages map[int][]*model.Module // module pages
IdxIntervs *seaMdl.IdxIntervSave // index intervention storage
RegionInfo []*model.Region // region all
MaxTime int64
styleLabel map[int][]*model.ParamStyle // style label
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
// dao
dao: appDao.New(c),
cmsDao: cmsDao.New(c),
auditDao: auditDao.New(c),
searchDao: search.New(c),
arcDao: arcDao.New(c),
// config
conf: c,
TVAppInfo: c.TVApp,
// memory data
ZoneData: make(map[int][]*model.Card),
RankData: make(map[int]map[string][]*model.Card),
ZonesInfo: make(map[int]*conf.PageCfg),
PGCOrigins: make(map[int][]*model.Card),
ZoneSids: make(map[int]map[int]int),
ModPages: make(map[int][]*model.Module),
PGCIndexShow: make(map[int64]string),
HeaderSids: make(map[int]int),
IdxIntervs: &seaMdl.IdxIntervSave{
Pgc: make(map[int][]int64),
Ugc: make(map[int][]int64),
},
RegionInfo: make([]*model.Region, 0),
styleLabel: make(map[int][]*model.ParamStyle),
}
// transform string map to int map
for k, v := range c.Newzone {
srv.ZonesInfo[atoi(k)] = v
}
// not blocking data loading
srv.indexShow() // pgc index show data
go srv.indexShowproc()
srv.loadRegion() // load all dynamic regions
go srv.loadRegionproc()
srv.loadPages() // load pages
go srv.loadPagesproc()
srv.ppIdxIntev(ctx) // load es index interventions
go srv.ppIdxIntervproc()
return srv
}

View File

@@ -0,0 +1,45 @@
package service
import (
"encoding/json"
"flag"
"fmt"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.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 TestService_SearchTypes(t *testing.T) {
Convey("TestService_SearchTypes", t, WithService(func(s *Service) {
cont, err := s.SearchTypes()
So(err, ShouldBeNil)
So(len(cont), ShouldBeGreaterThan, 0)
for _, v := range cont {
data, _ := json.Marshal(v)
fmt.Println(string(data))
}
}))
}

View File

@@ -0,0 +1,199 @@
package service
import (
"strconv"
"time"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// mergeSlice merges two slices and return a new slice
func mergeSlice(s1 []*model.Card, s2 []*model.Card) []*model.Card {
slice := make([]*model.Card, len(s1)+len(s2))
copy(slice, s1)
copy(slice[len(s1):], s2)
return slice
}
// mergeSliceM merges two slices and return a new slice
func mergeSliceM(s1 []*model.Module, s2 []*model.Module) []*model.Module {
slice := make([]*model.Module, len(s1)+len(s2))
copy(slice, s1)
copy(slice[len(s1):], s2)
return slice
}
// transform string to int
func atoi(number string) (result int) {
result, _ = strconv.Atoi(number)
return result
}
// dupliInt is used to merge the base and the interv, then remove duplicated numbers
func dupliInt(base []int64, interv []int64) (merged []int64) {
var dupMap = make(map[int64]int)
for _, v := range append(interv, base...) {
length := len(dupMap)
dupMap[v] = 1
if len(dupMap) != length {
merged = append(merged, v)
}
}
return
}
// removeInt is used to remove interv data from base, and return the rest base data
func removeInt(base []int64, interv []int64) (restBase []int64) {
var intervMap = make(map[int64]int)
for _, v := range interv {
intervMap[v] = 1
}
for _, v := range base {
if _, ok := intervMap[v]; !ok {
restBase = append(restBase, v)
}
}
return
}
// applyInterv is used for ES index page intervention
func applyInterv(base []int64, interv []int64, pn int) (res []int64) {
if len(interv) == 0 {
return base
}
if pn == 1 { // first page, put the intervs in the beginning
return dupliInt(base, interv)
}
return removeInt(base, interv) // other pages, remove the intervs from the ES result
}
// remove duplicated Cards
func duplicate(a []*model.Card) (ret []*model.Card) {
resUGC := make(map[int]int)
resPGC := make(map[int]int)
for _, v := range a {
if v.IsUGC() {
if _, ok := resUGC[v.SeasonID]; ok {
log.Warn("[UGC] v.SeasonID %d is duplicated", v.SeasonID)
continue
}
resUGC[v.SeasonID] = 1
ret = append(ret, v)
} else {
if _, ok := resPGC[v.SeasonID]; ok {
log.Warn("v.SeasonID %d is duplicated", v.SeasonID)
continue
}
resPGC[v.SeasonID] = 1
ret = append(ret, v)
}
}
return
}
// cut the slice with the limit
func cutSlice(source []*model.Card, lengthLimit int) (res []*model.Card) {
if len(source) <= lengthLimit {
return source
}
return source[0:lengthLimit]
}
// transform card data to mod_card data
func cardTransform(source []*model.Card) (target []*model.ModCard) {
for _, v := range source {
target = append(target, &model.ModCard{
Card: *v,
})
}
return
}
// followToMod transforms follow structure data to module structure data
func followToMod(source []*model.Follow) (target []*model.ModCard) {
for _, v := range source {
target = append(target, &model.ModCard{
Card: model.Card{
SeasonID: atoi(v.SeasonID),
Title: v.Title,
Cover: v.Cover,
NewEP: &model.NewEP{
ID: int64(atoi(v.NewEP.EpisodeID)),
Index: v.NewEP.Index,
IndexShow: v.NewEP.IndexTitle,
Cover: v.NewEP.Cover,
},
CornerMark: v.CornerMark,
},
LastEPIndex: v.UserSeason.LastEPIndex,
NewestEPIndex: v.NewestEPIndex,
TotalCount: v.TotalCount,
IsFinish: v.IsFinish,
})
}
return
}
// elapsed record the function's execution time
func elapsed(funcName string) func() {
start := time.Now()
return func() {
log.Info("[Elapsed] %s took %v\n", funcName, time.Since(start))
}
}
// transformCards rewrite, use season & ep's cache to build the cards, instead of pick them from DB
func (s *Service) transformCards(sids []int64) (target []*model.Card, targetMap map[int]*model.Card) {
var (
seasons []*model.SeasonCMS
eps map[int64]*model.EpCMS
newestEPIDs []int64
err error
)
targetMap = make(map[int]*model.Card)
if seasons, newestEPIDs, err = s.cmsDao.LoadSnsCMS(ctx, sids); err != nil {
log.Error("transformCards - LoadSnsCMS - Sids %v, Err %v", sids, err)
return
}
if eps, err = s.cmsDao.LoadEpsCMS(ctx, newestEPIDs); err != nil {
log.Error("transformCard - LoadEpsCMS - Epids %v, Err %v", newestEPIDs, err)
return
}
for _, val := range seasons {
newcard := &model.Card{
SeasonID: int(val.SeasonID),
Title: val.Title,
Cover: val.Cover,
Type: _typePGC,
NewEP: &model.NewEP{
ID: val.NewestEPID,
},
}
if val.NeedVip() { // card add vip corner mark
newcard.CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
if val.NewestEPID == 0 {
log.Error("transformCard - NewestEPID of SeasonID %v is Empty", val.SeasonID)
} else {
// epMeta info
if epval, ok := eps[val.NewestEPID]; ok {
newcard.NewEP.Index = epval.Title
newcard.NewEP.Cover = epval.Cover
} else {
log.Error("transformCard - EpMetas of Epid is Empty", val.NewestEPID)
}
// epIndex show
if len(s.PGCIndexShow) > 0 { // maybe first launch it's empty, wait 2 minutes it will be ready
if indexShow, ok := s.PGCIndexShow[int64(val.SeasonID)]; ok && len(indexShow) > 0 {
newcard.NewEP.IndexShow = indexShow
} else {
log.Error("transformCard - Missing Index_show For Sid:%v", val.SeasonID)
}
}
}
target = append(target, newcard)
targetMap[newcard.SeasonID] = newcard
}
return
}

View File

@@ -0,0 +1,71 @@
package service
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// prepareUGCData loads ugc data
func (s *Service) prepareUGCData(c context.Context) (err error) {
var (
tids []int32
originData = make(map[int][]*model.Card)
)
if tids, err = s.arcDao.TargetTypes(); err != nil {
log.Error("[PrepareUGCData] TargetTypes Err %v", err)
return
}
for _, v := range tids {
if tidData, errUGC := s.ugcData(c, v); errUGC != nil {
log.Error("[PrepareUGCData] Tid %d, Err %v", v, errUGC)
continue
} else {
originData[int(v)] = tidData
}
}
if len(originData) > 0 {
s.UGCOrigins = originData
}
return
}
// ugcData gets the origin ugc data and intervene with TV CMS data
func (s *Service) ugcData(c context.Context, tid int32) (data []*model.Card, err error) {
var (
arcMetas []*model.ArcCMS
origin []*model.AIData
aids []int64
)
if origin, err = s.dao.UgcAIData(c, int16(tid)); err != nil {
log.Error("[ugcData] Can't Pick AI Data, Tid: %d, Err: %v", tid, err)
return
}
for _, v := range origin {
aids = append(aids, int64(v.AID))
}
if arcMetas, err = s.cmsDao.LoadArcsMedia(c, aids); err != nil {
log.Error("[ugcData] Can't Pick MediaCache Data, Tid: %d, Err: %v", tid, err)
return
}
for _, v := range arcMetas {
data = append(data, v.ToCard())
}
return
}
// SearchTypes return the ugc types
func (s *Service) SearchTypes() (res []*model.ArcType, err error) {
var typeMap map[int32]*model.ArcType
if typeMap, err = s.arcDao.FirstTypes(); err != nil {
log.Error("SearchTypes ArcDao FirstTypes Err %v", err)
return
}
for _, v := range s.conf.Cfg.ZonesInfo.UgcTypes {
if tp, ok := typeMap[v]; ok {
res = append(res, tp)
}
}
return
}

View File

@@ -0,0 +1,175 @@
package service
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
const (
_rank = 1
_list = 2
_latest = 3 // added No.3 type of module
_typePGC = 1
)
// preparePGCData loads
func (s *Service) preparePGCData(c context.Context) {
var (
zones = s.conf.Cfg.ZonesInfo.PGCZonesID
originData []*model.Card
err error
)
for _, v := range zones {
if originData, err = s.pgcData(c, v); err != nil {
log.Error("LoadPGCList Data for Zone %d, Err %v", v, err)
continue
}
if len(originData) == 0 {
log.Error("LoadPGCList Espically Data for Zone %d, Empty", v)
continue
}
s.PGCOrigins[v] = originData
}
}
// load all the zone's data and save them in memroy
func (s *Service) zonesData(c context.Context) (err error) {
zones := s.conf.Cfg.ZonesInfo.PGCZonesID
for _, v := range zones {
if err = s.loadZone(c, v); err != nil {
log.Error("[ZoneData] Error (%v)", err)
return
}
}
return
}
// load zone data
func (s *Service) loadZone(c context.Context, sType int) (err error) {
var (
top, middle, bottom []*model.Card
conf = s.ZonesInfo[sType]
zoneData = []*model.Card{}
pgcList = copyCardList(s.PGCOrigins)
)
s.ZoneSids[sType] = make(map[int]int) //re-init the zone header sids for removing duplication
// re-init the rank data's map
s.RankData[sType] = make(map[string][]*model.Card)
// top module logic
if conf.Top != 0 {
reqTop := &model.ReqZone{
SType: sType,
IntervType: _rank,
LengthLimit: conf.Top,
IntervM: conf.TopM,
PGCListM: pgcList,
}
if top, err = s.zoneLogic(c, reqTop); err != nil {
log.Error("ZoneTop %d Error %v", sType, err)
return
}
zoneData = mergeSlice(zoneData, top)
s.RankData[sType]["rank"] = top
}
// middle module logic
if conf.Middle != 0 {
reqMiddle := &model.ReqZone{
SType: sType,
IntervType: _latest,
LengthLimit: conf.Middle,
IntervM: conf.MiddleM,
PGCListM: pgcList,
}
if middle, err = s.zoneLogic(c, reqMiddle); err != nil {
log.Error("ZoneMiddle %d Error %v", sType, err)
return
}
zoneData = mergeSlice(zoneData, middle)
s.RankData[sType]["latest"] = middle
}
// bottom module logic
if conf.Bottom != 0 {
reqBottom := &model.ReqZone{
SType: sType,
IntervType: _list,
LengthLimit: conf.Bottom,
IntervM: 0,
PGCListM: pgcList,
}
if bottom, err = s.zoneLogic(c, reqBottom); err != nil {
log.Error("ZoneBottom %d Error %v", sType, err)
}
zoneData = mergeSlice(zoneData, bottom)
s.RankData[sType]["list"] = bottom
}
s.ZoneData[sType] = zoneData
return
}
// pgcData gets the origin pgc data and intervene with TV CMS data
func (s *Service) pgcData(c context.Context, seasonType int) (intervened []*model.Card, err error) {
intervened, err = s.dao.ChannelData(c, seasonType, s.TVAppInfo)
if err != nil {
log.Error("[LoadPGCList] Can't Pick PGC/AI Data, Zone %d, Err: %v", seasonType, err)
return
}
if err2 := s.cardIntervSn(intervened); err2 != nil {
log.Error("[cardIntervSn] ERROR [%v]", err2)
}
return
}
// getIntervs gets the specified type of intervention ( top, middle or botton ) with the number limit and transform the intervention to cards
func (s *Service) getIntervs(c context.Context, sType int, intervType int, nbLimit int) (resCards []*model.Card, err error) {
var resp *model.RespModInterv
if resp, err = s.dao.ZoneIntervs(c, &model.ReqZoneInterv{ // for home & zone old logic, we only pick PGC data
RankType: sType,
Category: intervType,
Limit: nbLimit,
}); err != nil {
log.Error("[loadZone] Can't Pick Intervention Data, Err: %v", err)
return
}
if len(resp.SIDs) == 0 {
log.Warn("[LoadPages] Zone %d, Category %d, Intervention Empty", sType, intervType)
return // empty result
}
resCards, _ = s.transformCards(resp.SIDs)
return
}
// zoneTop returns the zop module data
func (s *Service) zoneLogic(c context.Context, req *model.ReqZone) (modData []*model.Card, err error) {
var (
Intervs []*model.Card
pgcData = s.PGCOrigins[req.SType]
lenLmt = req.LengthLimit
sType = req.SType
intervM = req.IntervM
)
if intervM != 0 {
if Intervs, err = s.modPGCIntervs(c, intervM, lenLmt); err != nil {
return
}
} else {
if Intervs, err = s.getIntervs(c, sType, req.IntervType, lenLmt); err != nil {
return
}
}
// merge top intervention and pgc data, then remove duplicated
allCards := mergeSlice(Intervs, pgcData)
allCards = duplicate(allCards)
for _, v := range allCards {
if len(modData) >= lenLmt {
break
}
if _, ok := s.ZoneSids[sType][v.SeasonID]; !ok {
modData = append(modData, v)
s.ZoneSids[sType][v.SeasonID] = 1
}
}
// remove the used pgc cards
req.PGCListM[sType] = allCards[len(modData):]
return
}

View File

@@ -0,0 +1,208 @@
package service
import (
"context"
"math"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/search"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/log"
)
// LoadZoneIdx loads zone index page data
func (s *Service) LoadZoneIdx(page int, category int) (idxSns []*model.IdxSeason, pager *model.IdxPager, err error) {
pagesize := s.conf.Cfg.ZonePs
var (
sids []int64
count int
start = (page - 1) * pagesize
end = page*pagesize - 1
seasons []*model.SeasonCMS
arcs []*model.ArcCMS
)
idxSns = make([]*model.IdxSeason, 0)
// pick up the page sids
if sids, count, err = s.dao.ZrevrangeList(ctx, category, start, end); err != nil {
log.Error("LoadZoneIdx - ZrevrangeList - Category %d Start %d End %d, Error %v", category, start, end, err)
return
}
pager = &model.IdxPager{
CurrentPage: page,
TotalItems: count,
TotalPages: int(math.Ceil(float64(count) / float64(pagesize))),
PageSize: pagesize,
}
if len(sids) == 0 {
return
}
if s.catIsUGC(category) {
if arcs, err = s.cmsDao.LoadArcsMedia(ctx, sids); err != nil {
log.Error("PickDBeiPage - UGC - LoadArcsMedia - Sids %v, Error %v", sids, err)
return
}
for _, v := range arcs {
idxSns = append(idxSns, v.ToIdxSn())
}
} else {
if seasons, _, err = s.cmsDao.LoadSnsCMS(ctx, sids); err != nil {
log.Error("LoadZoneIdx - LoadSnCMS - Category %d Start %d End %d, Error %v", category, start, end, err)
return
}
for _, v := range seasons {
idxSn := v.IdxSn()
if idxShow, ok := s.PGCIndexShow[v.SeasonID]; ok { // use pgc index_show to replace the DB upinfo
idxSn.Upinfo = idxShow
}
idxSns = append(idxSns, idxSn)
}
}
log.Info("Combine Info for %d Sids, Page %d, Category %d", len(idxSns), page, category)
return
}
func (s *Service) catIsUGC(category int) bool {
for _, v := range s.conf.Cfg.ZonesInfo.UGCZonesID {
if category == int(v) {
return true
}
}
return false
}
// esInterv treats the es index intervention
func (s *Service) esInterv(ctx context.Context, req *search.ReqIdxInterv) (resIDs []int64, err error) {
var intervIDs []int64
if s.IdxIntervs != nil {
if req.IsPGC {
if sids, ok := s.IdxIntervs.Pgc[req.Category]; ok {
intervIDs = sids
}
} else {
if aids, ok := s.IdxIntervs.Ugc[req.Category]; ok {
intervIDs = aids
}
}
}
resIDs = applyInterv(req.EsIDs, intervIDs, req.Pn)
return
}
// EsPgcIdx returns the elastic search index page result
func (s *Service) EsPgcIdx(ctx context.Context, req *search.ReqPgcIdx) (res *search.EsPager, err error) {
var (
data *search.EsPgcResult
esSids, authSids []int64
target []*model.Card
idxCards []*search.EsCard
authMap map[int64]*model.SnAuth
)
if data, err = s.searchDao.PgcIdx(ctx, req); err != nil {
log.Error("EsPgcIdx Req %v, Err %v", req, err)
return
}
for _, v := range data.Result {
esSids = append(esSids, v.SeasonID)
}
if authMap, err = s.cmsDao.LoadSnsAuthMap(ctx, esSids); err != nil {
log.Error("EsPgcIdx Sids %v, LoadSnsAuthMap Err %v", esSids, err)
return
}
for _, v := range esSids {
if snAuth, ok := authMap[v]; ok {
if snAuth.CanPlay() {
authSids = append(authSids, v)
}
}
}
log.Info("EsPgcIdx EsSids Len %d, Detail %v. AuthSids Len %d, %v", len(esSids), esSids, len(authSids), authSids)
if req.IsDefault() { // if it's default condition, we apply the interventions
reqIdx := &search.ReqIdxInterv{}
reqIdx.FromPGC(authSids, req)
if authSids, err = s.esInterv(ctx, reqIdx); err != nil {
return
}
}
target, _ = s.transformCards(authSids)
for _, v := range target {
card := &search.EsCard{}
card.FromPgc(v)
idxCards = append(idxCards, card)
}
res = &search.EsPager{
Page: data.Page,
Result: idxCards,
Title: req.Title(),
}
return
}
// EsUgcIdx def.
func (s *Service) EsUgcIdx(ctx context.Context, req *search.ReqUgcIdx) (res *search.EsPager, err error) {
var (
srvReq = &search.SrvUgcIdx{
ReqEsPn: req.ReqEsPn,
}
childTps []*arcwar.Tp
esRes *search.EsUgcResult
aids []int64
ugcCardsMap map[int64]*model.ArcCMS
tp *arcwar.Tp
)
if req.PubTime != "" && req.PubTime != search.AllLabel { // pubtime treatment
if srvReq.PubTime, err = req.TimeStr(); err != nil {
log.Error("EsUgcIdx TimeJson Err %v", err)
return
}
}
if req.SecondTID > 0 { // typeid treatment
srvReq.TIDs = []int32{req.SecondTID}
} else {
if childTps, err = s.arcDao.TypeChildren(req.ParentTID); err != nil {
log.Error("EsUgcIdx TypeChildren parentTid %d, Err %v", req.ParentTID, err)
return
}
for _, v := range childTps {
srvReq.TIDs = append(srvReq.TIDs, v.ID)
}
}
if esRes, err = s.searchDao.UgcIdx(ctx, srvReq); err != nil { // pick es result
log.Error("EsUgcIdx Req %v, Err %v", req, err)
return
}
if tp, err = s.arcDao.TypeInfo(req.ParentTID); err != nil {
log.Error("EsUgcIdx ParentTID %d, Err %v", req.ParentTID, err)
return
}
res = &search.EsPager{
Page: esRes.Page,
Title: tp.Name,
}
if len(esRes.Result) == 0 {
return
}
for _, v := range esRes.Result {
aids = append(aids, v.AID)
}
if req.IsDefault() { // if it's default condition, we apply the interventions
reqIdx := &search.ReqIdxInterv{}
reqIdx.FromUGC(aids, req)
if aids, err = s.esInterv(ctx, reqIdx); err != nil {
return
}
}
if ugcCardsMap, err = s.cmsDao.LoadArcsMediaMap(ctx, aids); err != nil { // transform UGC
log.Error("[EsUgcIdx] Can't Pick MediaCache Data, Aids: %v, Err: %v", aids, err)
return
}
for _, v := range aids { // if canPlay, add it into the final result
if arcCMS, ok := ugcCardsMap[v]; ok {
if arcCMS.CanPlay() {
card := &search.EsCard{}
card.FromUgc(arcCMS.ToCard())
res.Result = append(res.Result, card)
}
}
}
return
}

View File

@@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//app/interface/main/tv/conf:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"audit.go",
"service.go",
"transcode.go",
],
importpath = "go-common/app/interface/main/tv/service/audit",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/audit:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log: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,70 @@
package audit
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
)
// HandleAudits treats the slice of IDList
func (s *Service) HandleAudits(ctx context.Context, vals []*model.IDList) (err error) {
var tx *sql.Tx
if tx, err = s.auditDao.BeginTran(ctx); err != nil {
log.Error("audit HandleAudits BeginTran Err %v", err)
return
}
for _, v := range vals {
if err = s.handleAudit(ctx, v, tx); err != nil {
log.Error("HandleAudits audit (%v), err %v", v, err)
tx.Rollback()
return
}
}
tx.Commit()
return
}
// handleAudit checks the prefix to dispatch the task to ugc/pgc season/ep
func (s *Service) handleAudit(ctx context.Context, val *model.IDList, tx *sql.Tx) (err error) {
var (
op = new(model.AuditOp)
)
if err = op.FromIDList(val); err != nil {
log.Error("audit handle Type error %v", val)
return
}
switch op.ContentType {
case model.UgcArc:
if arcCMS, err := s.cmsDao.LoadArcMeta(ctx, op.KID); err == nil && arcCMS != nil && arcCMS.NotDeleted() {
return auditCore(ctx, op, tx, s.auditDao.UpdateArc)
}
case model.UgcVideo:
if videoCMS, err := s.cmsDao.LoadVideoMeta(ctx, op.KID); err == nil && videoCMS != nil && videoCMS.NotDeleted() {
return auditCore(ctx, op, tx, s.auditDao.UpdateVideo)
}
case model.PgcSn:
if snCMS, err := s.cmsDao.SnAuth(ctx, op.KID); err == nil && snCMS != nil && snCMS.NotDeleted() {
return auditCore(ctx, op, tx, s.auditDao.UpdateSea)
}
case model.PgcEp:
if epCMS, err := s.cmsDao.EpAuth(ctx, op.KID); err == nil && epCMS != nil && epCMS.NotDeleted() {
return auditCore(ctx, op, tx, s.auditDao.UpdateCont)
}
default:
log.Error("audit handle Content Type Error %s", op.ToMsg())
return ecode.TvDangbeiWrongType
}
return ecode.NothingFound
}
type doAudit func(ctx context.Context, v *model.AuditOp, tx *sql.Tx) (err error)
func auditCore(c context.Context, v *model.AuditOp, tx *sql.Tx, updateFunc doAudit) (err error) {
if err = updateFunc(c, v, tx); err != nil {
log.Error("%s fail(%v)", v.ToMsg(), err)
}
return
}

View File

@@ -0,0 +1,24 @@
package audit
import (
"go-common/app/interface/main/tv/conf"
auditDao "go-common/app/interface/main/tv/dao/audit"
"go-common/app/interface/main/tv/dao/cms"
)
// Service .
type Service struct {
conf *conf.Config
auditDao *auditDao.Dao
cmsDao *cms.Dao
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
auditDao: auditDao.New(c),
cmsDao: cms.New(c),
}
return srv
}

View File

@@ -0,0 +1,21 @@
package audit
import (
"flag"
"path/filepath"
"time"
"go-common/app/interface/main/tv/conf"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.toml")
flag.Set("conf", dir)
conf.Init()
srv = New(conf.Conf)
time.Sleep(time.Second)
}

View File

@@ -0,0 +1,75 @@
package audit
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
)
const (
_typeUGC = "ugc"
_typePGC = "pgc"
)
type cidExistFunc = func(context.Context, int64) ([]int64, error)
type cidTransFunc = func(context.Context, []int64, int64) error
type reqTrans struct {
CID int64
Action int64
CheckFunc cidExistFunc
TransFunc cidTransFunc
}
// Transcode update the video/ep's transcoded status
func (s *Service) Transcode(req *model.ReqTransode) (err error) {
var ctx = context.TODO()
if req.ContType == _typePGC {
err = s.transPGC(ctx, req.CID, req.Action)
} else if req.ContType == _typeUGC {
err = s.transUGC(ctx, req.CID, req.Action)
} else {
err = ecode.TvDangbeiWrongType
}
return
}
func commonTrans(ctx context.Context, req reqTrans) (err error) {
var ids []int64
if ids, err = req.CheckFunc(ctx, req.CID); err != nil {
return
}
if len(ids) == 0 {
return ecode.NothingFound
}
err = req.TransFunc(ctx, ids, req.Action)
return
}
func (s *Service) transPGC(ctx context.Context, cid int64, action int64) (err error) {
return commonTrans(ctx, reqTrans{
CID: cid,
Action: action,
CheckFunc: s.auditDao.PgcCID,
TransFunc: s.auditDao.PgcTranscode,
})
}
func (s *Service) transUGC(ctx context.Context, cid int64, action int64) (err error) {
return commonTrans(ctx, reqTrans{
CID: cid,
Action: action,
CheckFunc: s.auditDao.UgcCID,
TransFunc: s.auditDao.UgcTranscode,
})
}
// ApplyPGC saves the pgc transcode apply time
func (s *Service) ApplyPGC(ctx context.Context, req *model.ReqApply) (err error) {
return commonTrans(ctx, reqTrans{
CID: req.CID,
Action: req.ApplyTime,
CheckFunc: s.auditDao.PgcCID,
TransFunc: s.auditDao.ApplyPGC,
})
}

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["service.go"],
importpath = "go-common/app/interface/main/tv/service/favorite",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/favorite:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/favorite/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log: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,89 @@
package favorite
import (
"context"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/dao/archive"
"go-common/app/interface/main/tv/dao/favorite"
"go-common/app/interface/main/tv/model"
arcwar "go-common/app/service/main/archive/api"
favmdl "go-common/app/service/main/favorite/model"
"go-common/library/ecode"
"go-common/library/log"
)
// Service .
type Service struct {
conf *conf.Config
dao *favorite.Dao
arcDao *archive.Dao
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
dao: favorite.New(c),
arcDao: archive.New(c),
}
return srv
}
const (
_ActAdd = 1
_ActDel = 2
)
// Favorites picks one page of the member's favorites
func (s *Service) Favorites(ctx context.Context, req *model.ReqFav) (resM *model.FavMList, err error) {
var (
res *favmdl.Favorites
arcs map[int64]*arcwar.Arc
aids []int64
pageNum int
)
resM = &model.FavMList{}
resM.Page.Size = s.conf.Cfg.FavPs
if res, err = s.dao.FavoriteV3(ctx, req.MID, req.Pn); err != nil { // pick favorite original data
log.Error("FavoriteV3 Mid %d, Pn %d, GetFav Err %v", req.MID, req.Pn, err)
return
}
if len(res.List) == 0 {
return
}
resM.Page = res.Page
// temp logic because client misuses the count as the number of pages
if resM.Page.Count%resM.Page.Size == 0 {
pageNum = resM.Page.Count / resM.Page.Size
} else {
pageNum = resM.Page.Count/resM.Page.Size + 1
}
resM.Page.Count = pageNum
// temp logic
for _, v := range res.List { // combine aids and get the archive info
aids = append(aids, v.Oid)
}
if arcs, err = s.arcDao.Archives(ctx, aids); err != nil {
log.Error("FavoriteV3 Mid %d, Pn %d, GetArc Err #%v", req.MID, req.Pn, err)
return
}
for _, v := range res.List { // arrange the final result
if arc, ok := arcs[v.Oid]; ok {
resM.List = append(resM.List, arc)
} else {
log.Warn("FavoriteV3 Mid %d, Pn %d, Miss Arc Info %d", req.MID, req.Pn, v.Oid)
}
}
return
}
// FavAct is favorite action, add or delete
func (s *Service) FavAct(ctx context.Context, req *model.ReqFavAct) (err error) {
if req.Action == _ActAdd {
return s.dao.FavAdd(ctx, req.MID, req.AID)
} else if req.Action == _ActDel {
return s.dao.FavDel(ctx, req.MID, req.AID)
}
return ecode.RequestErr
}

View File

@@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"hotword.go",
"label.go",
"playurl.go",
"service.go",
"splash.go",
"upgrade.go",
],
importpath = "go-common/app/interface/main/tv/service/goblin",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/account:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/goblin:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/goblin:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/tv/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log: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"],
)
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,24 @@
package goblin
import (
"time"
"go-common/library/log"
)
// reload hotword data from MC
func (s *Service) loadHotword() {
var err error
if s.Hotword, err = s.dao.Hotword(ctx); err != nil {
log.Error("loadHotword Error %v, List %v", err, s.Hotword)
return
}
}
// load hotword data regularly
func (s *Service) loadHotwordproc() {
for {
time.Sleep(time.Duration(s.conf.Search.HotwordFre))
s.loadHotword()
}
}

View File

@@ -0,0 +1,138 @@
package goblin
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/goblin"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_pgcLabel = 1
_ugcLabel = 2
)
func (s *Service) labelsproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.IndexLabel.Fre))
log.Info("Reload Label Data!")
s.prepareLabels()
}
}
// prepareLabels refreshes memory labels
func (s *Service) prepareLabels() {
pgcLbs, errpgc := s.catLabels(_pgcLabel)
if errpgc != nil {
log.Error("loadLabels PGC Err %v", errpgc)
return
}
ugcLbs, errugc := s.catLabels(_ugcLabel)
if errugc != nil {
log.Error("loadLabels PGC Err %v", errugc)
return
}
if len(pgcLbs) > 0 && len(ugcLbs) > 0 {
s.labels = &goblin.IndexLabels{
PGC: pgcLbs,
UGC: ugcLbs,
}
}
}
// catLabels picks ugc/pgc all categories labels
func (s *Service) catLabels(catType int) (result map[int][]*goblin.TypeLabels, err error) {
var (
cats []int
typeMap map[int32]*model.ArcType
)
result = make(map[int][]*goblin.TypeLabels)
if catType == _pgcLabel {
cats = s.conf.Cfg.ZonesInfo.PGCZonesID
} else if catType == _ugcLabel {
if typeMap, err = s.arcDao.FirstTypes(); err != nil {
log.Error("loadLabels ArcDao FirstTypes Err %v", err)
return
}
for k := range typeMap {
cats = append(cats, int(k))
}
} else {
err = ecode.TvDangbeiWrongType
return
}
if len(cats) == 0 {
err = ecode.TvDangbeiPageNotExist
return
}
for _, category := range cats {
if result[category], err = s.loadLabels(catType, category); err != nil {
log.Error("loadLabels Err %v", err)
return
}
}
return
}
// getTypeLabel builds an typeLabels object from a slice of labels
func getTypeLabel(in []*goblin.Label) *goblin.TypeLabels {
typeLbs := &goblin.TypeLabels{}
typeLbs.FromLabels(in)
return typeLbs
}
// loadLabels
func (s *Service) loadLabels(catType, category int) (result []*goblin.TypeLabels, err error) {
var (
ctx = context.Background()
labelMap = make(map[string][]*goblin.Label)
labels []*goblin.Label
showOrder []string
cfg = s.conf.Cfg.IndexLabel
)
if labels, err = s.dao.Label(ctx, category, catType); err != nil {
log.Error("loadLabels Dao Label Cat %d, %d, Err %v", category, catType, err)
return
}
for _, v := range labels { // gather labels by their param
v.TransYear(cfg)
labelMap[v.Param] = append(labelMap[v.Param], v)
}
if catType == _pgcLabel {
showOrder = cfg.PGCOrder
} else {
showOrder = cfg.UGCOrder
}
for _, v := range showOrder {
if line, ok := labelMap[v]; ok {
result = append(result, getTypeLabel(line))
delete(labelMap, v)
}
}
if len(labelMap) > 0 {
for _, v := range labelMap {
result = append(result, getTypeLabel(v))
}
}
return
}
// Labels picks label
func (s *Service) Labels(c context.Context, catType, category int) (result []*goblin.TypeLabels, err error) {
var (
indexLbs map[int][]*goblin.TypeLabels
ok bool
)
if catType == _pgcLabel {
indexLbs = s.labels.PGC
} else {
indexLbs = s.labels.UGC
}
if result, ok = indexLbs[category]; !ok {
err = ecode.NothingFound
}
return
}

View File

@@ -0,0 +1,62 @@
package goblin
import (
"context"
"fmt"
"go-common/app/interface/main/tv/model"
arcwar "go-common/app/service/main/archive/api"
tvapi "go-common/app/service/main/tv/api"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_tvVipOk = 1
)
// UgcPlayurl returns the result of ugc play url
func (s *Service) UgcPlayurl(c context.Context, p *model.PlayURLReq, mid int64) (result map[string]interface{}, err error) {
var (
arc *arcwar.Arc
firstRes map[string]interface{}
firstResp *model.PlayURLResp
tvRes *tvapi.UserInfoReply
)
result = make(map[string]interface{})
if firstRes, firstResp, err = s.dao.UgcPlayurl(c, p); err != nil {
return
}
if _, ok := s.VipQns[p.Qn]; !ok { // if it doesn't request vip quality, let it go
return firstRes, nil
}
if mid != 0 {
if tvRes, err = s.tvCilent.UserInfo(c, &tvapi.UserInfoReq{Mid: mid}); err != nil && !ecode.EqualError(ecode.NothingFound, err) {
log.Error("[playurl.UgcPlayurl] mid(%d) error(%s)", mid, err)
return
}
if tvRes != nil && tvRes.Status == _tvVipOk {
return firstRes, nil // if it's tv vip, let it go !
}
if arc, err = s.arcDao.Archive3(c, p.Avid); err != nil || arc == nil { // try author himself
log.Warn("s.arcDao.Archive3 failed can not view Aid %d, Mid %", p.Avid, mid)
return
}
if arc.Author.Mid == mid {
return firstRes, nil // if it's upper himself, let it go
}
}
// downgrade logic
for _, qn := range firstResp.AcceptQuality {
qnStr := fmt.Sprintf("%d", qn)
if _, ok := s.VipQns[qnStr]; ok { // if vip
continue
}
p.Qn = qnStr
result, _, err = s.dao.UgcPlayurl(c, p)
return
}
err = ecode.NothingFound // it doesn't have any other quality to allow downgrade
log.Error("Allow Quality %v, Err %v", firstResp.AcceptQuality, err)
return
}

View File

@@ -0,0 +1,55 @@
package goblin
import (
"context"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/dao/account"
"go-common/app/interface/main/tv/dao/archive"
gobDao "go-common/app/interface/main/tv/dao/goblin"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/goblin"
tvapi "go-common/app/service/main/tv/api"
)
// Service .
type Service struct {
conf *conf.Config
dao *gobDao.Dao
accDao *account.Dao
arcDao *archive.Dao
ChlSplash map[string]string // channel's splash data
Hotword []*model.Hotword // search hotword data
VipQns map[string]int // playurl qualities for vips
labels *goblin.IndexLabels
tvCilent tvapi.TVServiceClient
}
var ctx = context.TODO()
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
dao: gobDao.New(c),
ChlSplash: make(map[string]string),
VipQns: make(map[string]int),
accDao: account.New(c),
arcDao: archive.New(c),
labels: &goblin.IndexLabels{},
}
var err error
if srv.tvCilent, err = tvapi.NewClient(c.TvVipClient); err != nil {
panic(err)
}
for _, v := range c.Cfg.VipQns {
srv.VipQns[v] = 1
}
go srv.loadSph() // splash
go srv.loadHotword() // hotword
go srv.loadSphproc() // splash proc
go srv.loadHotwordproc() // hotword proc
srv.prepareLabels() // prepare index labels
go srv.labelsproc()
return srv
}

View File

@@ -0,0 +1,43 @@
package goblin
import (
"flag"
"fmt"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.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 TestService_Labels(t *testing.T) {
Convey("TestService_Labels", t, WithService(func(s *Service) {
results, err := s.Labels(ctx, 1, 2)
So(err, ShouldBeNil)
So(results, ShouldNotBeNil)
for _, v := range results {
fmt.Println(v.ParamName)
}
}))
}

View File

@@ -0,0 +1,53 @@
package goblin
import (
"time"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
)
func (s *Service) loadSphproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.PageReload))
log.Info("Reload Splash Data!")
s.loadSph()
}
}
func (s *Service) loadSph() {
var (
err error
chls []*model.Channel
chlSplash = make(map[string]string)
)
// pick channel's splash data
if chls, err = s.dao.ChlInfo(ctx); err != nil {
log.Error("LoadSph Error (%v)", err)
return
}
if len(chls) == 0 {
log.Error("loadSph Channel Data is Empty!")
return
}
// travel the channels to make the map
for _, v := range chls {
chlSplash[v.Title] = v.Splash
}
s.ChlSplash = chlSplash
log.Info("Reload %d Channel Data", len(chlSplash))
}
// PickSph picks the splash data from memory map
func (s *Service) PickSph(channel string) (sph string, err error) {
var ok bool
if len(s.ChlSplash) == 0 {
log.Error("Channel Data is Nil")
return "", ecode.ServiceUnavailable
}
if sph, ok = s.ChlSplash[channel]; !ok {
sph = s.conf.Cfg.DefaultSplash
}
return sph, nil
}

View File

@@ -0,0 +1,14 @@
package goblin
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
)
// VerUpdate .
func (s *Service) VerUpdate(c context.Context, ver *model.VerUpdate) (result *model.HTTPData, errCode ecode.Codes, err error) {
result, errCode, err = s.dao.VerUpdate(c, ver)
return
}

View File

@@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"combine.go",
"history.go",
"service.go",
],
importpath = "go-common/app/interface/main/tv/service/history",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/history/model:go_default_library",
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/history:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/history:go_default_library",
"//library/log:go_default_library",
"//library/sync/errgroup: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"],
)
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,147 @@
package history
import (
"context"
hismdl "go-common/app/interface/main/history/model"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/history"
"go-common/library/log"
)
func (s *Service) pgcHisRes(ctx context.Context, res []*hismdl.Resource) (resMap map[int64]*history.HisRes, err error) {
var (
snMetas map[int64]*model.SeasonCMS
epMetas map[int64]*model.EpCMS
pickSids []int64
pickEpids []int64
)
resMap = make(map[int64]*history.HisRes)
for _, v := range res {
pickSids = append(pickSids, v.Sid)
pickEpids = append(pickEpids, v.Epid)
}
if snMetas, err = s.cmsDao.LoadSnsCMSMap(ctx, pickSids); err != nil {
log.Error("LoadSnsCMS Sids %v, Err %v", pickSids, err)
return
}
if epMetas, err = s.cmsDao.LoadEpsCMS(ctx, pickEpids); err != nil {
log.Warn("LoadEpsCMS Epids %v, Err %v", pickEpids, err)
err = nil
}
for _, v := range res {
his := hisTrans(v)
his.Type = _typePGC
his.Page = nil
// season info
snMeta, okS := snMetas[v.Sid]
if !okS {
log.Error("pgcHisRes Missing Info Sid %d", v.Sid)
continue
}
his.Title = snMeta.Title
his.Cover = snMeta.Cover
if snMeta.NeedVip() { // add vip corner mark
his.CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
// ep info
epMeta, okE := epMetas[v.Epid]
if !okE {
log.Warn("pgcHisRes Missing Info Epid %d", v.Epid)
} else {
his.EPMeta = &history.HisEP{
EPID: epMeta.EPID,
Cover: epMeta.Cover,
Title: epMeta.Subtitle,
LongTitle: epMeta.Title,
}
}
resMap[v.Sid] = his
}
return
}
func (s *Service) ugcHisRes(ctx context.Context, res []*hismdl.Resource) (resMap map[int64]*history.HisRes, err error) {
var (
arcMetas map[int64]*model.ArcCMS
videoMetas map[int64]*model.VideoCMS
pickAids []int64
pickCids []int64
)
resMap = make(map[int64]*history.HisRes)
for _, v := range res {
pickAids = append(pickAids, v.Oid)
pickCids = append(pickCids, v.Cid)
}
if arcMetas, err = s.cmsDao.LoadArcsMediaMap(ctx, pickAids); err != nil {
log.Error("LoadArcsMediaMap Sids %v, Err %v", pickAids, err)
return
}
if videoMetas, err = s.cmsDao.LoadVideosMeta(ctx, pickCids); err != nil {
log.Warn("LoadVideosMeta Epids %v, Err %v", pickCids, err)
err = nil
}
for _, v := range res {
his := hisTrans(v)
his.Type = _typeUGC
his.Page = nil
// season info
arcMeta, okS := arcMetas[v.Oid]
if !okS {
log.Error("ugcHisRes Missing Info Aid %d", v.Oid)
continue
}
his.Title = arcMeta.Title
his.Cover = arcMeta.Cover
// ep info
video, okE := videoMetas[v.Cid]
if !okE {
log.Warn("ugcHisRes Missing Info Cid %d", v.Cid)
} else {
his.Page = &history.HisPage{
CID: video.CID,
Part: video.Title,
Page: video.IndexOrder,
}
}
resMap[v.Oid] = his
}
return
}
func hisTrans(res *hismdl.Resource) *history.HisRes {
return &history.HisRes{
Mid: res.Mid,
Oid: res.Oid,
Sid: res.Sid,
Epid: res.Epid,
Cid: res.Cid,
Business: res.Business,
DT: res.DT,
Pro: res.Pro,
Unix: res.Unix,
Type: _typePGC,
}
}
func (s *Service) getDuration(ctx context.Context, res []*hismdl.Resource) (durs map[int64]int64) {
var (
aids []int64
)
durs = make(map[int64]int64)
for _, v := range res {
aids = append(aids, v.Oid)
}
resMeta := s.arcDao.LoadViews(ctx, aids)
for _, v := range res {
if view, ok := resMeta[v.Oid]; ok && len(view.Pages) > 0 {
for _, vp := range view.Pages {
if v.Cid == vp.Cid {
durs[v.Oid] = vp.Duration
break
}
}
}
}
return
}

View File

@@ -0,0 +1,152 @@
package history
import (
"context"
hismodel "go-common/app/interface/main/history/model"
"go-common/app/interface/main/tv/model/history"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
const (
_videoPGC = "pgc"
_videoUGC = "archive"
_typePGC = 1
_typeUGC = 2
)
// pick history from cursor and cache, then compare to tell whether we could use cache or not
func (s *Service) cacheHis(c context.Context, mid int64) (resp *history.RespCacheHis, err error) {
var (
cfg = s.conf.Cfg.HisCfg
hismc *history.HisMC
)
resp = &history.RespCacheHis{
UseCache: true,
}
if resp.Res, err = s.dao.Cursor(c, mid, 0, cfg.Pagesize, 0, cfg.Businesses); err != nil {
log.Error("history dao.Cursor Mid %d, Err %v", mid, err)
return
}
if len(resp.Res) == 0 {
log.Info("Mid %d, No history", mid)
return
}
if hismc, err = s.dao.HisCache(c, mid); err != nil {
log.Error("history dao.HisCache Mid %d, Err %v", mid, err)
return
}
if hismc != nil { // if the first item in cache and from cursor is the same, return with cache
if resp.Res[0].Unix == hismc.LastViewAt {
resp.Filtered = hismc.Res
return
}
}
resp.UseCache = false
return
}
func (s *Service) combineHis(c context.Context, req *history.ReqCombineHis) (filtered []*history.HisRes) {
var (
durs = make(map[int64]int64)
pgcRes, ugcRes []*hismodel.Resource
pgcMap, ugcMap map[int64]*history.HisRes
)
g, _ := errgroup.WithContext(c)
for _, v := range req.OriRes { // combine pgc & ugc data
if v.Business == _videoPGC { // combine pgc history data
if _, ok := req.OkSids[v.Sid]; !ok {
continue
}
pgcRes = append(pgcRes, v)
} else if v.Business == _videoUGC { // combine ugc history data
if _, ok := req.OkAids[v.Oid]; !ok {
continue
}
ugcRes = append(ugcRes, v)
} else {
continue
}
}
okRes := mergeRes(pgcRes, ugcRes)
g.Go(func() (err error) { // get pgc info
pgcMap, err = s.pgcHisRes(context.Background(), pgcRes)
return
})
g.Go(func() (err error) { // get ugc info
ugcMap, err = s.ugcHisRes(context.Background(), ugcRes)
return
})
g.Go(func() (err error) { // get duration info
durs = s.getDuration(context.Background(), okRes)
return nil
})
if err := g.Wait(); err != nil { // wait history combine media info
log.Error("getHistory For Mid %d, Err %v", req.Mid, err)
}
for _, v := range okRes {
var resrc *history.HisRes
if v.Business == _videoPGC {
if res, ok := pgcMap[v.Sid]; ok {
resrc = res
}
} else if v.Business == _videoUGC {
if res, ok := ugcMap[v.Oid]; ok {
resrc = res
}
}
if resrc == nil {
log.Error("okRes Business %s, CID %d, %d, Empty", v.Business, v.Sid, v.Oid)
continue
}
if dur, ok := durs[v.Oid]; ok { // duration
resrc.PageDuration = dur
}
filtered = append(filtered, resrc)
}
return
}
// GetHistory picks history from rpc and combine the media data from Cache & DB
func (s *Service) GetHistory(c context.Context, mid int64) (filtered []*history.HisRes, err error) {
var respCache *history.RespCacheHis
if respCache, err = s.cacheHis(c, mid); err != nil {
return
}
if respCache.UseCache {
return respCache.Filtered, nil
}
okSids, okAids := s.filterIDs(c, mid, respCache.Res)
filtered = s.combineHis(c, &history.ReqCombineHis{
Mid: mid,
OkAids: okAids,
OkSids: okSids,
OriRes: respCache.Res,
})
s.dao.SaveHisCache(c, filtered)
log.Info("Mid %d, OriLen %d, Filtered %d", mid, len(respCache.Res), len(filtered))
return
}
// filterIDs picks the original history resource, arrange them into pgc and ugc and then filter by DAO
func (s *Service) filterIDs(ctx context.Context, mid int64, res []*hismodel.Resource) (okSids, okAids map[int64]int) {
var ugcAIDs, pgcSIDs []int64
for _, v := range res { // we pick only pgc & archive from History rpc
if v.Business == _videoPGC {
pgcSIDs = append(pgcSIDs, v.Sid)
} else if v.Business == _videoUGC {
ugcAIDs = append(ugcAIDs, v.Oid)
}
}
okSids, okAids = s.cmsDao.MixedFilter(ctx, pgcSIDs, ugcAIDs) // we filter the okSids and okAids
log.Info("Mid %d, okSids %v, okAids %v", mid, okSids, okAids)
return
}
// mergeRes merges two slices and return a new slice
func mergeRes(s1 []*hismodel.Resource, s2 []*hismodel.Resource) []*hismodel.Resource {
slice := make([]*hismodel.Resource, len(s1)+len(s2))
copy(slice, s1)
copy(slice[len(s1):], s2)
return slice
}

View File

@@ -0,0 +1,27 @@
package history
import (
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/dao/archive"
"go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/history"
)
// Service .
type Service struct {
conf *conf.Config
dao *history.Dao
cmsDao *cms.Dao
arcDao *archive.Dao
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
dao: history.New(c),
cmsDao: cms.New(c),
arcDao: archive.New(c),
}
return srv
}

View File

@@ -0,0 +1,45 @@
package history
import (
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/tv/conf"
"context"
"encoding/json"
"fmt"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.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 TestService_GetHistory(t *testing.T) {
Convey("TestService_GetHistory", t, WithService(func(s *Service) {
cont, err := s.GetHistory(context.Background(), int64(27515401))
So(err, ShouldBeNil)
data, _ := json.Marshal(cont)
fmt.Println(string(data))
}))
}

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"media.go",
"service.go",
],
importpath = "go-common/app/interface/main/tv/service/pgc",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/app:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/pgc:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log: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,198 @@
package pgc
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
"go-common/library/ecode"
"go-common/library/log"
)
// snAuth picks the season's auth status and validate it
func (s *Service) snAuth(sid int64) (msg string, err error) {
var season *model.SnAuth
if season, err = s.cmsDao.SnAuth(ctx, sid); err != nil {
log.Error("snVerify Sid %d, Err %v", sid, err)
err = ecode.NothingFound
return
}
if !season.CanPlay() {
err = ecode.CopyrightLimit
_, msg = s.cmsDao.SnErrMsg(season) // season auth failure msg
}
return
}
func (s *Service) snDecor(core *model.SnDetailCore) {
if snCMS, err := s.cmsDao.LoadSnCMS(ctx, core.SeasonID); err == nil {
core.CmsInterv(snCMS)
} else {
log.Warn("snDecor LoadSnCMS %d, err %v", core.SeasonID, err)
}
core.StyleLabel = s.styleLabel[core.SeasonID]
}
// epAuthDecor checks epids status, hide auditing episodes
func (s *Service) epAuthDecor(epids []int64) (res map[int64]*model.EpDecor, msg string, err error) {
var (
epsMetaMap map[int64]*model.EpCMS
epsAuthMap map[int64]*model.EpAuth
)
res = make(map[int64]*model.EpDecor, len(epids))
if epsAuthMap, err = s.cmsDao.LoadEpsAuthMap(ctx, epids); err != nil {
log.Warn("FullIntervs LoadEpsAuthMap epids %v, err %v", epids, err)
return
}
if epsMetaMap, err = s.cmsDao.LoadEpsCMS(ctx, epids); err != nil {
log.Warn("FullIntervs LoadEpsCMS epids %v, err %v", epids, err)
return
}
for _, v := range epids {
if epAuth, ok := epsAuthMap[v]; ok {
if epAuth.CanPlay() { // we hide not passed episodes
decor := &model.EpDecor{
Watermark: epAuth.Whitelist(),
}
if epMeta, okMeta := epsMetaMap[v]; okMeta { // ep intervention
decor.EpCMS = epMeta
}
res[v] = decor
}
}
}
if len(res) == 0 { // return err
err = ecode.CopyrightLimit
msg = s.conf.Cfg.AuthMsg.PGCOffline
log.Warn("Epids %v, After filter empty", epids)
}
return
}
// SnDetail validates the season is authorized to play and involve the intervention
func (s *Service) SnDetail(c context.Context, param *model.MediaParam) (detail *model.SeasonDetail, msg string, err error) {
var (
sid = param.SeasonID
epids []int64
decors map[int64]*model.EpDecor
cfg = s.conf.Cfg.VipMark
)
if msg, err = s.snAuth(sid); err != nil {
return
}
if detail, err = s.dao.Media(ctx, param); err != nil { // pgc media api
log.Error("DAO MediaDetail Sid %d, Error (%v)", sid, err)
return
}
// filter auditing eps, and do ep intervention and watermark logic
for _, v := range detail.Episodes {
if cfg.V1HideChargeable { // before vip version goes online, we still hide the chargeable episodes
if v.EpisodeStatus != cfg.EpFree {
continue
}
}
epids = append(epids, v.EPID)
}
if decors, msg, err = s.epAuthDecor(epids); err != nil {
return
}
offAuditing := make([]*model.Episode, 0, len(decors))
for _, v := range detail.Episodes {
if decor, ok := decors[v.EPID]; ok {
if decor.EpCMS != nil {
v.CmsInterv(decor.EpCMS)
}
v.WaterMark = decor.Watermark
offAuditing = append(offAuditing, v)
}
}
detail.Episodes = offAuditing
s.snDecor(&detail.SnDetailCore)
return
}
// SnDetailV2 validates the season is authorized to play and involve the intervention
func (s *Service) SnDetailV2(c context.Context, param *model.MediaParam) (detail *model.SnDetailV2, msg string, err error) {
var (
sid = param.SeasonID
epids []int64
decors map[int64]*model.EpDecor
cmarkCfg = s.conf.Cfg.VipMark
)
if msg, err = s.snAuth(sid); err != nil {
return
}
if detail, err = s.dao.MediaV2(ctx, param); err != nil || detail == nil { // pgc media api v2
log.Error("DAO MediaDetail Sid %d, Error (%v)", sid, err)
return
}
detail.TypeTrans()
if len(detail.Section) > 0 { // pgc media api v2 logic, prevues are in the sections, we need to pick them up and re-insert into the episodes list
for _, v := range detail.Section {
detail.Episodes = append(detail.Episodes, v.Episodes...)
}
}
for _, v := range detail.Episodes {
epids = append(epids, v.ID)
}
if decors, msg, err = s.epAuthDecor(epids); err != nil {
return
}
offAuditing := make([]*model.EpisodeV2, 0, len(decors))
for _, v := range detail.Episodes {
if decor, ok := decors[v.ID]; ok {
if decor.EpCMS != nil {
v.CmsInterv(decor.EpCMS)
}
v.WaterMark = decor.Watermark
if v.Status != cmarkCfg.EpFree { // if ep is not free, put the corner mark
v.CornerMark = &(*cmarkCfg.EP)
}
offAuditing = append(offAuditing, v)
}
}
detail.Episodes = offAuditing
s.snDecor(&detail.SnDetailCore)
return
}
// EpControl validates the ep is authorized to play and involve the intervention
func (s *Service) EpControl(c context.Context, epid int64) (sid int64, msg string, err error) {
var ep *model.EpAuth
if ep, err = s.cmsDao.EpAuth(c, epid); err != nil {
log.Error("LoadEP Epid %d Error(%v)", epid, err)
err = ecode.NothingFound
return
}
if !ep.CanPlay() {
err = ecode.CopyrightLimit
_, msg = s.cmsDao.EpErrMsg(ep) // ep auth failure msg
return
}
sid = ep.SeasonID
return
}
func (s *Service) upStyleCache() {
for {
res, err := s.dao.GetLabelCache(context.Background())
if err != nil {
log.Error("s.dao.GetLabelCache upStyleCache error(%s)", err)
time.Sleep(5 * time.Second)
continue
}
if len(res) > 0 {
s.styleLabel = res
}
time.Sleep(time.Duration(s.conf.Style.LabelSpan))
}
}
func (s *Service) styleCache() {
res, err := s.dao.GetLabelCache(context.Background())
if err != nil {
log.Error("s.dao.GetLabelCache error(%v)", err)
panic(err)
}
s.styleLabel = res
}

View File

@@ -0,0 +1,36 @@
package pgc
import (
"context"
"go-common/app/interface/main/tv/conf"
appDao "go-common/app/interface/main/tv/dao/app"
"go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/pgc"
"go-common/app/interface/main/tv/model"
)
var ctx = context.Background()
// Service .
type Service struct {
appDao *appDao.Dao
cmsDao *cms.Dao
dao *pgc.Dao
conf *conf.Config
styleLabel map[int64][]*model.ParamStyle // style label
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
appDao: appDao.New(c),
cmsDao: cms.New(c),
dao: pgc.New(c),
styleLabel: make(map[int64][]*model.ParamStyle),
}
srv.styleCache()
go srv.upStyleCache() // style label cache
return srv
}

View File

@@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"search.go",
"service.go",
"wild.go",
],
importpath = "go-common/app/interface/main/tv/service/search",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/search:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/search:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr: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"],
)
go_test(
name = "go_default_test",
srcs = [
"service_test.go",
"wild_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model/search:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,112 @@
package search
import (
"context"
"strconv"
"go-common/app/interface/main/tv/model"
searchMdl "go-common/app/interface/main/tv/model/search"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_searchAll = "all_tv"
_searchPGC = "tv_pgc"
_searchUGC = "tv_ugc"
_typePGC = "pgc"
)
// SearchSug returns the result of search sug
func (s *Service) SearchSug(ctx context.Context, req *searchMdl.ReqSug) (result searchMdl.SugResponse, err error) {
if result, err = s.dao.SearchSug(ctx, req); err != nil {
return
}
build, _ := strconv.Atoi(req.Build)
if build != 0 && build <= s.conf.Search.SugPGCBuild && len(result.Result.Tag) > 0 {
var filtered = []*searchMdl.STag{}
for _, v := range result.Result.Tag {
if v.Type == _typePGC {
filtered = append(filtered, v)
}
}
result.Result.Tag = filtered
}
return
}
func (s *Service) batchToCommonPgc(ctx context.Context, input []*searchMdl.PgcResult) (output []*searchMdl.CommonResult) {
var (
err error
cids []int64
cmsRes map[int64]*model.SeasonCMS
)
for _, v := range input {
output = append(output, v.ToCommon())
cids = append(cids, int64(v.ID))
}
if cmsRes, err = s.cmsDao.LoadSnsCMSMap(ctx, cids); err != nil {
log.Error("[search.cornerMark] cids(%s) error(%v)", xstr.JoinInts(cids), err)
return
}
for idx, v := range output {
if r, ok := cmsRes[int64(v.ID)]; ok && r.NeedVip() {
output[idx].CornerMark = &(*s.conf.Cfg.SnVipCorner)
}
}
return
}
func batchToCommonUgc(input []*searchMdl.UgcResult) (output []*searchMdl.CommonResult) {
for _, v := range input {
output = append(output, v.ToCommon())
}
return
}
// SearchRes distinguishes the search type and pick the result
func (s *Service) SearchRes(ctx context.Context, req *searchMdl.ReqSearch) (data *searchMdl.RespForClient, err error) {
var resCommon *searchMdl.ResultResponse
data = &searchMdl.RespForClient{
SearchType: req.SearchType,
}
switch req.SearchType {
case _searchAll:
var resAll searchMdl.RespAll
if resAll, resCommon, err = s.dao.SearchAll(ctx, req); err != nil {
return
}
if resAll.PageInfo != nil {
data.PageInfo = resAll.PageInfo
}
if resAll.Result != nil {
data.ResultAll = &searchMdl.AllForClient{
Pgc: s.batchToCommonPgc(ctx, resAll.Result.Pgc),
Ugc: batchToCommonUgc(resAll.Result.Ugc),
}
}
case _searchPGC:
var resPgc searchMdl.RespPgc
if resPgc, resCommon, err = s.dao.SearchPgc(ctx, req); err != nil {
return
}
data.PGC = s.batchToCommonPgc(ctx, resPgc.Result)
case _searchUGC:
if req.Category == 0 { // in case of ugc, must have category
err = ecode.RequestErr
return
}
var resUgc searchMdl.RespUgc
if resUgc, resCommon, err = s.dao.SearchUgc(ctx, req); err != nil {
return
}
data.UGC = batchToCommonUgc(resUgc.Result)
default:
data = nil
err = ecode.TvDangbeiWrongType
return
}
data.ResultResponse = resCommon
return
}

View File

@@ -0,0 +1,27 @@
package search
import (
"go-common/app/interface/main/tv/conf"
arcdao "go-common/app/interface/main/tv/dao/archive"
cmsDao "go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/search"
)
// Service .
type Service struct {
conf *conf.Config
dao *search.Dao
arcDao *arcdao.Dao
cmsDao *cmsDao.Dao
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
dao: search.New(c),
arcDao: arcdao.New(c),
cmsDao: cmsDao.New(c),
}
return srv
}

View File

@@ -0,0 +1,30 @@
package search
import (
"flag"
"path/filepath"
"time"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.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)
}
}

View File

@@ -0,0 +1,242 @@
package search
import (
"context"
mdlSearch "go-common/app/interface/main/tv/model/search"
v1 "go-common/app/service/main/archive/api"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
const (
_showHide = 0
season = "番剧"
upper = "用户"
movie = "影视"
_searchType = "all"
_mobiAPP = "app"
_bangumiType = 1
_biliUserType = 2
_filmType = 3
_mediaBangumiType = 7
_mediaFtType = 8
)
// UserSearch search user .
func (s *Service) UserSearch(ctx context.Context, arg *mdlSearch.UserSearch) (res []*mdlSearch.User, err error) {
if res, err = s.dao.UserSearch(ctx, arg); err != nil {
log.Error("s.dao.UserSearch error(%v)", err)
}
if len(res) == 0 {
res = make([]*mdlSearch.User, 0)
}
return
}
// SearchAll search all .
func (s *Service) SearchAll(ctx context.Context, arg *mdlSearch.UserSearch) (res *mdlSearch.ResultAll, err error) {
var (
user = &mdlSearch.Search{}
avm map[int64]*v1.Arc
avids []int64
items []*mdlSearch.Item
wildCfg = s.conf.Wild.WildSearch
)
arg.SeasonNum = wildCfg.SeasonNum
arg.MovieNum = wildCfg.MovieNum
arg.SearchType = _searchType
arg.MobiAPP = _mobiAPP
if user, err = s.dao.SearchAllWild(ctx, arg); err != nil {
log.Error(" s.dao.SearchAllWild error(%v)", err)
}
res = &mdlSearch.ResultAll{}
if user == nil {
return
}
res.Trackid = user.Trackid
res.Page = user.Page
res.Attribute = user.Attribute
nis := make([]*mdlSearch.NavInfo, 0, 4)
// season
if user.PageInfo.Bangumi != nil {
var nav = &mdlSearch.NavInfo{
Name: season,
Total: user.PageInfo.Bangumi.NumResult,
Pages: user.PageInfo.Bangumi.Pages,
Type: _bangumiType,
}
if user.PageInfo.Bangumi.NumResult > wildCfg.SeasonNum {
nav.Show = wildCfg.SeasonMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// media season
if user.PageInfo.MediaBangumi != nil {
var nav = &mdlSearch.NavInfo{
Name: season,
Total: user.PageInfo.MediaBangumi.NumResult,
Pages: user.PageInfo.MediaBangumi.Pages,
Type: _mediaBangumiType,
}
if user.PageInfo.MediaBangumi.NumResult > wildCfg.SeasonNum {
nav.Show = wildCfg.SeasonMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// upper
if user.PageInfo.BiliUser != nil {
var nav = &mdlSearch.NavInfo{
Name: upper,
Total: user.PageInfo.BiliUser.NumResult,
Pages: user.PageInfo.BiliUser.Pages,
Type: _biliUserType,
}
nis = append(nis, nav)
}
// movie
if user.PageInfo.Film != nil {
var nav = &mdlSearch.NavInfo{
Name: movie,
Total: user.PageInfo.Film.NumResult,
Pages: user.PageInfo.Film.Pages,
Type: _filmType,
}
if user.PageInfo.Movie != nil && user.PageInfo.Movie.NumResult > wildCfg.MovieNum {
nav.Show = wildCfg.MovieMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// media movie
if user.PageInfo.MediaFt != nil {
var nav = &mdlSearch.NavInfo{
Name: movie,
Total: user.PageInfo.MediaFt.NumResult,
Pages: user.PageInfo.MediaFt.Pages,
Type: _mediaFtType,
}
if user.PageInfo.MediaFt.NumResult > wildCfg.MovieNum {
nav.Show = wildCfg.MovieMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
res.NavInfo = nis
// archive
for _, v := range user.Result.Video {
avids = append(avids, v.ID)
}
for _, v := range user.Result.Movie {
if v.Type == "movie" {
avids = append(avids, v.Aid)
}
}
if arg.Page == 1 {
for _, v := range user.Result.User {
for _, vr := range v.Res {
avids = append(avids, vr.Aid)
}
}
for _, v := range user.Result.BiliUser {
for _, vr := range v.Res {
avids = append(avids, vr.Aid)
}
}
}
group := new(errgroup.Group)
if len(avids) != 0 {
group.Go(func() (err error) {
if avm, err = s.arcDao.Archives(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = group.Wait(); err != nil {
return
}
// item add data .
var promptBangumi, promptFt string
// season
bangumi := user.Result.Bangumi
items = make([]*mdlSearch.Item, 0, len(bangumi))
for _, v := range bangumi {
si := &mdlSearch.Item{}
si.FromSeason(v, mdlSearch.GotoBangumiWeb)
items = append(items, si)
}
if len(res.Items.Season) > 5 && arg.RID == 0 {
res.Items.Season = res.Items.Season[:5]
res.Items.Season = items
} else {
res.Items.Season = items
}
// movie
movie := user.Result.Movie
items = make([]*mdlSearch.Item, 0, len(movie))
for _, v := range movie {
si := &mdlSearch.Item{}
si.FromMovie(v, avm)
items = append(items, si)
}
res.Items.Movie = items
// season2
mb := user.Result.MediaBangumi
// movie2
mf := user.Result.MediaFt
items = make([]*mdlSearch.Item, 0, len(mb)+len(mf))
for _, v := range mb {
si := &mdlSearch.Item{}
si.FromMedia(v, promptBangumi, mdlSearch.GotoBangumi, nil)
items = append(items, si)
}
for _, v := range mf {
si := &mdlSearch.Item{}
si.FromMedia(v, promptFt, mdlSearch.GotoMovie, nil)
si.Goto = mdlSearch.GotoAv
items = append(items, si)
}
if len(res.Items.Season2) > 5 && arg.RID == 0 {
res.Items.Season2 = res.Items.Season2[:5]
res.Items.Season2 = items
} else {
res.Items.Season2 = items
}
items = make([]*mdlSearch.Item, 0, len(user.Result.Video))
for _, v := range user.Result.Video {
si := &mdlSearch.Item{}
si.FromVideo(v, avm[v.ID])
items = append(items, si)
}
res.Items.Archive = items
return
}
// PgcSearch search .
func (s *Service) PgcSearch(ctx context.Context, arg *mdlSearch.UserSearch) (res *mdlSearch.TypeSearch, err error) {
var (
wildCfg = s.conf.Wild.WildSearch
)
arg.SeasonNum = wildCfg.SeasonNum
arg.MovieNum = wildCfg.MovieNum
arg.SearchType = _searchType
arg.MobiAPP = _mobiAPP
if res, err = s.dao.PgcSearch(ctx, arg); err != nil {
log.Error("[wild.PgcSearch] s.dao.PgcSearch error(%v)", err)
return
}
if len(res.Items) <= 0 {
res.Items = make([]*mdlSearch.Item, 0)
}
return
}

View File

@@ -0,0 +1,55 @@
package search
import (
"context"
"testing"
mdlSearch "go-common/app/interface/main/tv/model/search"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_UserSearch(t *testing.T) {
Convey("test user search", t, WithService(func(s *Service) {
arg := &mdlSearch.UserSearch{
Keyword: "lex",
Build: "111",
SearchType: "all",
Page: 1,
Pagesize: 20,
}
res, err := s.UserSearch(context.Background(), arg)
So(err, ShouldBeNil)
So(len(res), ShouldNotBeEmpty)
}))
}
func TestService_SearchAll(t *testing.T) {
Convey("test search all", t, WithService(func(s *Service) {
arg := &mdlSearch.UserSearch{
Keyword: "工作细胞",
Build: "111",
SearchType: "bili_user",
Page: 1,
Pagesize: 20,
}
res, err := s.SearchAll(context.Background(), arg)
So(err, ShouldBeNil)
So(res, ShouldNotBeEmpty)
}))
}
func TestService_PgcSearch(t *testing.T) {
Convey("test pgc search", t, WithService(func(s *Service) {
arg := &mdlSearch.UserSearch{
Keyword: "工作细胞",
Build: "111",
SearchType: "all",
Page: 1,
Pagesize: 20,
}
res, err := s.PgcSearch(context.Background(), arg)
So(err, ShouldBeNil)
So(res, ShouldNotBeEmpty)
}))
}

View File

@@ -0,0 +1,63 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dangbei_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/thirdp:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dangbei.go",
"mango.go",
"mango_recom.go",
"service.go",
],
importpath = "go-common/app/interface/main/tv/service/thirdp",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/thirdp:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/thirdp:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/cache:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,105 @@
package thirdp
import (
"math"
"go-common/app/interface/main/tv/dao/thirdp"
"go-common/app/interface/main/tv/model"
tpMdl "go-common/app/interface/main/tv/model/thirdp"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
func (s *Service) buildPager(req *tpMdl.ReqDBeiPages) (pager *model.IdxPager, err error) {
var (
addCache bool
count int
)
// pick up the count from redis, otherwise pick it from DB
if count, err = s.dao.GetThirdpCnt(ctx, req.TypeC); err != nil {
if count, err = s.dao.ThirdpCnt(ctx, req.TypeC); err != nil {
return // if db count still error, fatal error
}
log.Error("PickDBeiPage - Can't Get Count, Pass by DB, Page %d", req.Page)
addCache = true
}
pager = &model.IdxPager{
CurrentPage: int(req.Page),
TotalItems: count,
TotalPages: int(math.Ceil(float64(count) / float64(req.Ps))),
PageSize: int(req.Ps),
}
if req.Page > int64(pager.TotalPages) {
err = ecode.TvDangbeiPageNotExist
return
}
// async Reset the DB data: Count & CurrentPage ID in MC for next time
if addCache {
cache.Save(func() {
s.dao.SetThirdpCnt(ctx, count, req.TypeC)
log.Info("PickDBeiPage Set Count %d Into Cache", count)
})
}
return
}
// PickDBeiPage picks the dangbei's page
func (s *Service) PickDBeiPage(page int64, typeC string) (data *tpMdl.DBeiPage, err error) {
var (
cPageID int64
sids []int64 // this page's season ids
sns []*model.SeasonCMS
arcs []*model.ArcCMS
dbeiSns []*tpMdl.DBeiSeason
pager *model.IdxPager
)
req := &tpMdl.ReqDBeiPages{
Ps: s.conf.Cfg.Dangbei.Pagesize,
TypeC: typeC,
Page: page,
}
if pager, err = s.buildPager(req); err != nil {
return
}
if req.LastID, err = s.dao.LoadPageID(ctx, req); err != nil {
log.Error("MangoPage getPageID LastPage %d Miss, Pass by offset", page-1)
return
}
if sids, cPageID, err = s.dao.DBeiPages(ctx, req); err != nil {
return
}
if len(sids) == 0 {
err = errors.Wrapf(ecode.NothingFound, "Type_C [%s], Page [%d], Offset Result Empty", typeC, page)
return
}
// load data from cache and transform data to Dangbei structure
if typeC == thirdp.DBeiPGC { // pgc - seasonCMS
if sns, _, err = s.cmsDao.LoadSnsCMS(ctx, sids); err != nil {
log.Error("PickDBeiPage - PGC - LoadSnsCMS - Sids %v, Error %v", sids, err)
return
}
for _, v := range sns { // transform the object to DbeiSeason
dbeiSns = append(dbeiSns, tpMdl.DBeiSn(v))
}
} else if typeC == thirdp.DBeiUGC { // ugc - arcCMS
if arcs, err = s.cmsDao.LoadArcsMedia(ctx, sids); err != nil {
log.Error("PickDBeiPage - UGC - LoadArcsMedia - Sids %v, Error %v", sids, err)
return
}
for _, v := range arcs {
first, second := s.arcDao.GetPTypeName(int32(v.TypeID))
dbeiSns = append(dbeiSns, tpMdl.DbeiArc(v, first, second))
}
}
// async Reset the DB data: CurrentPage ID in MC for next time
cache.Save(func() {
s.dao.SetPageID(ctx, &tpMdl.ReqPageID{Page: page, ID: cPageID, TypeC: typeC})
})
data = &tpMdl.DBeiPage{
List: dbeiSns,
Pager: pager,
}
return
}

View File

@@ -0,0 +1,41 @@
package thirdp
import (
"fmt"
"testing"
"encoding/json"
"go-common/app/interface/main/tv/dao/thirdp"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_PickDBeiPage(t *testing.T) {
Convey("TestService_PickDBeiPage", t, WithService(func(s *Service) {
data, err := s.PickDBeiPage(0, thirdp.DBeiUGC)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
fmt.Println(data)
}))
}
func TestService_MangoSns(t *testing.T) {
Convey("TestService_MangoSns", t, WithService(func(s *Service) {
data, err := s.MangoSns(ctx, 7)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
str, _ := json.Marshal(data)
fmt.Println(string(str))
}))
}
func TestService_MangoArcs(t *testing.T) {
Convey("TestService_MangoArcs", t, WithService(func(s *Service) {
data, err := s.MangoArcs(ctx, 3)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
str, _ := json.Marshal(data)
fmt.Println(string(str))
}))
}

View File

@@ -0,0 +1,262 @@
package thirdp
import (
"context"
"math"
"go-common/app/interface/main/tv/dao/thirdp"
"go-common/app/interface/main/tv/model"
tpMdl "go-common/app/interface/main/tv/model/thirdp"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// mangoPage picks the Mango's page
func (s *Service) mangoPage(page int64, typeC string) (pager *model.IdxPager, dataSet []*tpMdl.RespSid, err error) {
var (
cPageID int64
req = &tpMdl.ReqDBeiPages{
Page: page,
TypeC: typeC,
Ps: int64(s.conf.Cfg.Dangbei.MangoPS),
}
)
if pager, err = s.buildPager(req); err != nil {
return
}
if req.LastID, err = s.dao.LoadPageID(ctx, req); err != nil {
log.Error("MangoPage getPageID Page %d Miss, Pass by offset", page-1)
return
}
if dataSet, cPageID, err = s.dao.MangoPages(ctx, req); err != nil {
return
}
if len(dataSet) == 0 {
err = errors.Wrapf(ecode.NothingFound, "Type_C [%s], Page [%d], Offset Result Empty", typeC, page)
return
}
cache.Save(func() {
s.dao.SetPageID(ctx, &tpMdl.ReqPageID{Page: page, ID: cPageID, TypeC: typeC})
})
return
}
// MangoSns picks mango season pages
func (s *Service) MangoSns(ctx context.Context, page int64) (data *tpMdl.MangoSnPage, err error) {
var (
pager *model.IdxPager
dataSet []*tpMdl.RespSid
snMetas = map[int64]*model.SeasonCMS{}
snAuths = map[int64]*model.SnAuth{}
newestEpids []int64
epMetas = map[int64]*model.EpCMS{}
)
if pager, dataSet, err = s.mangoPage(page, thirdp.MangoPGC); err != nil {
return
}
data = &tpMdl.MangoSnPage{
Pager: pager,
}
sids := tpMdl.PickSids(dataSet)
if snMetas, err = s.cmsDao.LoadSnsCMSMap(ctx, sids); err != nil {
log.Error("MangoSns - PGC - LoadSnsCMS - Sids %v, Error %v", sids, err)
return
}
if snAuths, err = s.cmsDao.LoadSnsAuthMap(ctx, sids); err != nil {
log.Error("MangoSns - PGC - LoadSnsAuthMap - Sids %v, Error %v", sids, err)
return
}
for _, v := range snMetas { // pick newestEpids
if v.NewestEPID != 0 {
newestEpids = append(newestEpids, v.NewestEPID)
}
}
if len(newestEpids) > 0 { // pick eps cms meta info
if epMetas, err = s.cmsDao.LoadEpsCMS(ctx, newestEpids); err != nil {
log.Error("MangoSns - PGC - LoadEpsCMS - Epids %v, Error %v", newestEpids, err)
return
}
}
for _, v := range dataSet { // transform the object to DbeiSeason
var (
snMeta *model.SeasonCMS
snAuth *model.SnAuth
okMeta, okAuth bool
)
if snMeta, okMeta = snMetas[v.Sid]; okMeta {
if snAuth, okAuth = snAuths[v.Sid]; okAuth {
mangoSn := tpMdl.ToMangoSn(snMeta, v.Mtime, snAuth.CanPlay())
if newestEp := snMeta.NewestEPID; newestEp != 0 {
if epMeta, ok := epMetas[snMeta.NewestEPID]; ok {
mangoSn.EpCover = epMeta.Cover
}
}
data.List = append(data.List, mangoSn)
continue
}
}
log.Warn("MangoSns Sid %d Missing Info, Meta %v, Auth %v", v.Sid, okMeta, okAuth)
}
return
}
// MangoArcs picks mango archive pages
func (s *Service) MangoArcs(ctx context.Context, page int64) (data *tpMdl.MangoArcPage, err error) {
var (
pager *model.IdxPager
dataSet []*tpMdl.RespSid
arcMetas map[int64]*model.ArcCMS
)
if pager, dataSet, err = s.mangoPage(page, thirdp.MangoUGC); err != nil {
return
}
data = &tpMdl.MangoArcPage{
Pager: pager,
}
sids := tpMdl.PickSids(dataSet)
if arcMetas, err = s.cmsDao.LoadArcsMediaMap(ctx, sids); err != nil {
log.Error("MangoArcs - UGC - LoadArcsMediaMap - Sids %v, Error %v", sids, err)
return
}
for _, v := range dataSet { // transform the object to DbeiSeason
if arcMeta, ok := arcMetas[v.Sid]; ok {
cat1, cat2 := s.arcDao.GetPTypeName(int32(arcMeta.TypeID))
data.List = append(data.List, tpMdl.ToMangoArc(arcMeta, v.Mtime, cat1, cat2))
continue
}
log.Warn("MangoSns Aid %d Missing Info", v.Sid)
}
return
}
// MangoEps returns mango eps data
func (s *Service) MangoEps(ctx context.Context, sid int64, page int) (data *tpMdl.MangoEpPage, err error) {
var (
count int
pagesize = s.conf.Cfg.Dangbei.MangoPS
resp []*tpMdl.RespSid
epMetas map[int64]*model.EpCMS
epAuths map[int64]*model.EpAuth
)
if count, err = s.dao.LoadSnCnt(ctx, true, sid); err != nil {
log.Error("MangoEps LoadSnCnt Sid %d, Err %v", sid, err)
return
}
totalPages := int(math.Ceil(float64(count) / float64(pagesize)))
if page > totalPages {
return nil, ecode.TvDangbeiPageNotExist
}
data = &tpMdl.MangoEpPage{
SeasonID: sid,
Pager: &model.IdxPager{
CurrentPage: page,
TotalItems: count,
TotalPages: int(math.Ceil(float64(count) / float64(pagesize))),
PageSize: int(pagesize),
},
}
if resp, err = s.dao.MangoSnOffset(ctx, true, sid, page, pagesize); err != nil {
log.Error("MangoEps MangoSnOffset Sid %d, Err %v", sid, err)
return
}
epids := tpMdl.PickSids(resp)
if epMetas, err = s.cmsDao.LoadEpsCMS(ctx, epids); err != nil {
log.Error("MangoEps LoadEpsCMS Sid %d, Err %v", sid, err)
return
}
if epAuths, err = s.cmsDao.LoadEpsAuthMap(ctx, epids); err != nil {
log.Error("MangoEps LoadEpsAuthMap Sid %d, Err %v", sid, err)
return
}
for _, v := range resp {
var (
epMeta *model.EpCMS
epAuth *model.EpAuth
okMeta, okAuth bool
)
if epMeta, okMeta = epMetas[v.Sid]; okMeta {
if epAuth, okAuth = epAuths[v.Sid]; okAuth {
data.List = append(data.List, &tpMdl.MangoEP{
EpCMS: *epMeta,
SeasonID: sid,
Mtime: v.Mtime,
Autorised: epAuth.CanPlay(),
})
continue
}
}
log.Warn("MangoEps Sid %d, Epids %d Missing Info, Meta %v, Auth %v", sid, v.Sid, okMeta, okAuth)
}
return
}
// MangoVideos returns mango videos data
func (s *Service) MangoVideos(ctx context.Context, sid int64, page int) (data *tpMdl.MangoVideoPage, err error) {
var (
count int
pagesize = s.conf.Cfg.Dangbei.MangoPS
resp []*tpMdl.RespSid
videoMetas map[int64]*model.VideoCMS
vp *arcwar.ViewReply
vPages = make(map[int64]*arcwar.Page)
)
if count, err = s.dao.LoadSnCnt(ctx, false, sid); err != nil {
log.Error("MangoVideos LoadSnCnt Sid %d, Err %v", sid, err)
return
}
totalPages := int(math.Ceil(float64(count) / float64(pagesize)))
if page > totalPages {
return nil, ecode.TvDangbeiPageNotExist
}
data = &tpMdl.MangoVideoPage{
AVID: sid,
Pager: &model.IdxPager{
CurrentPage: page,
TotalItems: count,
TotalPages: int(math.Ceil(float64(count) / float64(pagesize))),
PageSize: int(pagesize),
},
}
if resp, err = s.dao.MangoSnOffset(ctx, false, sid, page, pagesize); err != nil {
log.Error("MangoVideos MangoSnOffset Sid %d, Err %v", sid, err)
return
}
epids := tpMdl.PickSids(resp)
if videoMetas, err = s.cmsDao.LoadVideosMeta(ctx, epids); err != nil {
log.Error("MangoVideos LoadEpsCMS Sid %d, Err %v", sid, err)
return
}
if vp, err = s.arcDao.GetView(ctx, sid); err != nil {
log.Error("MangoVideos ViewPage getView Aid:%d, Err:%v", sid, err)
return
}
for _, v := range vp.Pages {
vPages[v.Cid] = v
}
for _, v := range resp {
var (
vMeta *model.VideoCMS
view *arcwar.Page
okMeta, okView bool
)
if vMeta, okMeta = videoMetas[v.Sid]; okMeta {
if view, okView = vPages[v.Sid]; okView {
data.List = append(data.List, &tpMdl.MangoVideo{
CID: v.Sid,
Page: vMeta.IndexOrder,
Desc: view.Desc,
Title: vMeta.Title,
Duration: view.Duration,
Autorised: vMeta.CanPlay(),
Mtime: v.Mtime,
})
continue
}
}
log.Warn("MangoViews Sid %d, Epids %d Missing Info, Meta %v, View %v", sid, v.Sid, okMeta, okView)
}
return
}

View File

@@ -0,0 +1,90 @@
package thirdp
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
tpMdl "go-common/app/interface/main/tv/model/thirdp"
arcwar "go-common/app/service/main/archive/api"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_rtypePGC = 1
_rtypeUGC = 2
)
func (s *Service) mangoR() (err error) {
var (
ctx = context.Background()
rids []int64
recoms []*tpMdl.MangoRecom
params []*tpMdl.MangoParams
catInfo *arcwar.Tp
)
if rids, err = s.dao.MangoOrder(ctx); err != nil { // pick mango recoms' order
log.Error("mango MangoOrder Error %v", err)
return
}
if len(rids) == 0 {
log.Error("mango MangoOrder Empty")
return
}
if recoms, err = s.dao.MangoRecom(ctx, rids); err != nil { // pick mango recom data
log.Error("mango MangoRecom Rids [%v], Err %v", rids, err)
return
}
for _, recom := range recoms {
if recom.Rtype == _rtypePGC {
var sn *model.SeasonCMS
if sn, err = s.cmsDao.LoadSnCMS(context.Background(), recom.RID); err != nil {
return err
}
param := recom.ToParam()
param.Category = tpMdl.PgcCat(recom.Category)
param.Role = sn.Role
param.PlayTime = sn.Playtime.Time().Format("2006-01-02")
params = append(params, param)
} else if recom.Rtype == _rtypeUGC {
var arc *model.ArcCMS
if arc, err = s.cmsDao.LoadArcMeta(context.Background(), recom.RID); err != nil {
return err
}
param := recom.ToParam()
if catInfo, err = s.arcDao.TypeInfo(int32(recom.Category)); err != nil { // pick ugc category name
log.Warn("MangoRecom Recom RID %d, Cat %d", recom.RID, recom.Category)
} else {
param.Category = catInfo.Name
}
param.PlayTime = arc.Pubtime.Time().Format("2006-01-02")
params = append(params, param)
} else {
return ecode.TvDangbeiWrongType
}
}
if len(params) > 0 {
s.mangoRecom = params
}
return
}
func (s *Service) mangorproc() {
for {
time.Sleep(time.Duration(s.conf.Cfg.PageReload))
if err := s.mangoR(); err != nil {
log.Error("mango Error %v", err)
}
}
}
// MangoRecom returns the mango recom data
func (s *Service) MangoRecom() (data []*tpMdl.MangoParams) {
if len(s.mangoRecom) == 0 {
data = make([]*tpMdl.MangoParams, 0)
return
}
data = s.mangoRecom
return
}

View File

@@ -0,0 +1,45 @@
package thirdp
import (
"context"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/dao/archive"
cmsDao "go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/thirdp"
tpMdl "go-common/app/interface/main/tv/model/thirdp"
xcache "go-common/library/cache"
)
var (
ctx = context.Background()
cache *xcache.Cache
)
func init() {
cache = xcache.New(1, 1024)
}
// Service .
type Service struct {
dao *thirdp.Dao
cmsDao *cmsDao.Dao
arcDao *archive.Dao
conf *conf.Config
mangoRecom []*tpMdl.MangoParams // mango recom data
}
// New .
func New(c *conf.Config) *Service {
srv := &Service{
// dao
dao: thirdp.New(c),
cmsDao: cmsDao.New(c),
arcDao: archive.New(c),
// config
conf: c,
}
go srv.mangorproc() // load mango recom data
srv.mangoR()
return srv
}

View File

@@ -0,0 +1,30 @@
package thirdp
import (
"flag"
"path/filepath"
"time"
"go-common/app/interface/main/tv/conf"
. "github.com/smartystreets/goconvey/convey"
)
var (
srv *Service
)
func init() {
dir, _ := filepath.Abs("../../cmd/tv-interface.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)
}
}

View File

@@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"service.go",
"vip.go",
],
importpath = "go-common/app/interface/main/tv/service/tvvip",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/model/tvvip:go_default_library",
"//app/service/main/tv/api:go_default_library",
"//library/log: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,27 @@
package tvvip
import (
"go-common/app/interface/main/tv/conf"
"go-common/app/service/main/tv/api"
"go-common/library/log"
)
// Service .
type Service struct {
conf *conf.Config
tvVipClient api.TVServiceClient
}
// New .
func New(c *conf.Config) *Service {
tvVipClient, err := api.NewClient(c.TvVipClient)
if err != nil {
log.Error("client.Dial err(%v)", err)
panic(err)
}
srv := &Service{
conf: c,
tvVipClient: tvVipClient,
}
return srv
}

View File

@@ -0,0 +1,106 @@
package tvvip
import (
"context"
tvmdl "go-common/app/interface/main/tv/model/tvvip"
pb "go-common/app/service/main/tv/api"
)
const (
ystSystemError = "999"
)
// VipInfo implementation
func (s *Service) VipInfo(ctx context.Context, mid int64) (resp *pb.UserInfoReply, err error) {
return s.tvVipClient.UserInfo(ctx, &pb.UserInfoReq{Mid: mid})
}
func (s *Service) YstVipInfo(ctx context.Context, mid int64, sign string) (resp *pb.YstUserInfoReply, err error) {
return s.tvVipClient.YstUserInfo(ctx, &pb.YstUserInfoReq{Mid: mid, Sign: sign})
}
// ChangeHistory implementation
func (s *Service) ChangeHistory(ctx context.Context, id int32) (resp *pb.ChangeHistoryReply, err error) {
return s.tvVipClient.ChangeHistory(ctx, &pb.ChangeHistoryReq{Id: id})
}
// ChangeHistorys implementation
func (s *Service) ChangeHistorys(ctx context.Context, mid int64, from, to, pn, ps int32) (resp *pb.ChangeHistorysReply, err error) {
return s.tvVipClient.ChangeHistorys(ctx, &pb.ChangeHistorysReq{Mid: mid, From: from, To: to, Pn: pn, Ps: ps})
}
// PanelInfo implemention
func (s *Service) PanelInfo(ctx context.Context, mid int64) (resp *pb.PanelInfoReply, err error) {
resp, err = s.tvVipClient.PanelInfo(ctx, &pb.PanelInfoReq{Mid: mid})
return
}
// GuestPanelInfo implemention
func (s *Service) GuestPanelInfo(ctx context.Context) (resp *pb.GuestPanelInfoReply, err error) {
return s.tvVipClient.GuestPanelInfo(ctx, &pb.GuestPanelInfoReq{})
}
// CreateQr implemention
func (s *Service) CreateQr(ctx context.Context, req *tvmdl.CreateQrReq) (resp *pb.CreateQrReply, err error) {
pr := new(pb.CreateQrReq)
req.CopyIntoPbCreateOrReq(pr)
return s.tvVipClient.CreateQr(ctx, pr)
}
// CreateGuestQr implemention
func (s *Service) CreateGuestQr(ctx context.Context, req *tvmdl.CreateGuestQrReq) (resp *pb.CreateGuestQrReply, err error) {
pr := new(pb.CreateGuestQrReq)
req.CopyIntoPbCreateGuestQrReq(pr)
return s.tvVipClient.CreateGuestQr(ctx, pr)
}
// TokenInfo implemention
func (s *Service) TokenInfo(ctx context.Context, tokens []string) (resp *pb.TokenInfoReply, err error) {
req := &pb.TokenInfoReq{
Token: tokens,
}
return s.tvVipClient.TokenInfo(ctx, req)
}
// CreateOrder implementation
func (s *Service) CreateOrder(ctx context.Context, clientIp string, req *tvmdl.CreateOrderReq) (resp *pb.CreateOrderReply, err error) {
pr := new(pb.CreateOrderReq)
req.CopyIntoPbCreateOrderReq(pr)
return s.tvVipClient.CreateOrder(ctx, pr)
}
// CreateGuestOrder implementation
func (s *Service) CreateGuestOrder(ctx context.Context, mid int64, clientIp string, req *tvmdl.CreateGuestOrderReq) (resp *pb.CreateGuestOrderReply, err error) {
pr := new(pb.CreateGuestOrderReq)
pr.Mid = mid
req.CopyIntoPbCreateGuestOrderReq(pr)
return s.tvVipClient.CreateGuestOrder(ctx, pr)
}
// PayCallback implementation
func (s *Service) PayCallback(ctx context.Context, req *tvmdl.YstPayCallbackReq) (resp *pb.PayCallbackReply) {
var err error
pr := new(pb.PayCallbackReq)
req.CopyIntoPbPayCallbackReq(pr)
resp, err = s.tvVipClient.PayCallback(ctx, pr)
if err != nil {
resp = new(pb.PayCallbackReply)
resp.Result = ystSystemError
resp.Msg = err.Error()
}
return
}
// ContractCallback implementation
func (s *Service) WxContractCallback(ctx context.Context, req *tvmdl.WxContractCallbackReq) (resp *pb.WxContractCallbackReply) {
var err error
wc := new(pb.WxContractCallbackReq)
req.CopyIntoPbWxContractCallbackReq(wc)
resp, err = s.tvVipClient.WxContractCallback(ctx, wc)
if err != nil {
resp = new(pb.WxContractCallbackReply)
resp.Result = ystSystemError
resp.Msg = err.Error()
}
return
}

View File

@@ -0,0 +1,52 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"access.go",
"auth.go",
"contain.go",
"empty_arc.go",
"service.go",
"view.go",
],
importpath = "go-common/app/interface/main/tv/service/view",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/tv/conf:go_default_library",
"//app/interface/main/tv/dao/account:go_default_library",
"//app/interface/main/tv/dao/archive:go_default_library",
"//app/interface/main/tv/dao/cms:go_default_library",
"//app/interface/main/tv/dao/favorite:go_default_library",
"//app/interface/main/tv/dao/upper:go_default_library",
"//app/interface/main/tv/model:go_default_library",
"//app/interface/main/tv/model/upper:go_default_library",
"//app/interface/main/tv/model/view:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/stat/prom:go_default_library",
"//library/sync/errgroup: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,46 @@
package view
import (
"context"
"go-common/app/interface/main/tv/dao/account"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
)
// checkAceess check user Aceess
func (s *Service) checkAceess(c context.Context, mid, aid int64, state, access int, ak, ip string) (err error) {
if state >= 0 && access == 0 {
return
}
if state < 0 {
if state == archive.StateForbidFixed {
log.Warn("archive(%d) is fixed", aid)
} else if state == archive.StateForbidUpDelete {
log.Warn("archive(%d) is deleted", aid)
} else {
log.Warn("mid(%d) have not access view not pass archive(%d) ", mid, aid)
}
err = ecode.NothingFound
return
}
if mid == 0 {
log.Warn("not login can not view(%d) state(%d) access(%d) mid(%d)", aid, state, access, mid)
err = ecode.AccessDenied
s.prom.Incr("no_login_access")
return
}
card, err := s.accDao.Card3(c, mid)
if err != nil {
log.Warn("s.accDao.Info failed can not view(%d) state(%d) access(%d)", aid, state, access)
s.prom.Incr("err_login_access")
return
}
if access > 0 && int(card.Rank) < access && !account.IsVip(card) {
err = ecode.AccessDenied
log.Warn("mid(%d) rank(%d) vip(tp:%d,status:%d) have not access(%d) view archive(%d) ", mid, card.Rank, card.Vip.Type, card.Vip.Status, access, aid)
s.prom.Incr("login_access")
}
return
}

View File

@@ -0,0 +1,32 @@
package view
import (
"context"
"go-common/app/interface/main/tv/model"
"go-common/library/log"
)
// ArcMsg returns the arc auth msg
func (s *Service) ArcMsg(aid int64) (arc *model.ArcCMS, ok bool, msg string, err error) {
if arc, err = s.cmsDao.LoadArcMeta(context.TODO(), aid); err != nil {
log.Error("ArcMsg loadArcMeta aid %d, Err %v", aid, err)
return
}
ok, msg = s.cmsDao.UgcErrMsg(arc.Deleted, arc.Result, arc.Valid)
return
}
// VideoMsg returns the arc auth msg
func (s *Service) VideoMsg(ctx context.Context, cid int64) (ok bool, msg string, err error) {
var video *model.VideoCMS
if video, err = s.cmsDao.LoadVideoMeta(context.TODO(), cid); err != nil {
log.Error("VideoMsg LoadVideoMeta aid %d, Err %v", cid, err)
return
}
ok, msg = s.cmsDao.UgcErrMsg(video.Deleted, video.Result, video.Valid)
if ok { // if video is normal, we also need to check it's archive
_, ok, msg, err = s.ArcMsg(int64(video.AID))
}
return
}

View File

@@ -0,0 +1,145 @@
package view
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
"go-common/app/interface/main/tv/model/view"
arcwar "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
var (
_rate = map[int]int64{15: 464, 16: 464, 32: 1028, 48: 1328, 64: 2192, 74: 3192, 80: 3192, 112: 6192, 116: 6192, 66: 1820}
)
func initPage(v *arcwar.Page, isBangumi bool) (page *view.Page) {
page = &view.Page{}
metas := make([]*view.Meta, 0, 4)
for q, r := range _rate {
meta := &view.Meta{
Quality: q,
Size: int64(float64(r*v.Duration) * 1.1 / 8.0),
}
metas = append(metas, meta)
}
if isBangumi {
v.From = "bangumi"
}
page.Page = v
page.Metas = metas
return
}
func (s *Service) initPages(c context.Context, vs *view.Static, ap []*arcwar.Page) (err error) {
var (
cids []int64
pages = make([]*view.Page, 0, len(ap))
vsAuth map[int64]*model.VideoCMS
isBangumi = vs.AttrVal(archive.AttrBitIsBangumi) == archive.AttrYes
emptyArc = true
)
for _, v := range ap {
cids = append(cids, v.Cid)
}
if vsAuth, err = s.cmsDao.LoadVideosMeta(c, cids); err != nil {
log.Error("initPages LoadVideosMeta Cid %v, Err %v", cids, err)
return
}
for _, v := range ap {
if auth, ok := vsAuth[v.Cid]; ok { // auditing data can't show
if !auth.Auditing() {
pages = append(pages, initPage(v, isBangumi))
}
if auth.CanPlay() {
emptyArc = false
}
}
}
if emptyArc { // if the arc doesn't have any video that can play, we put its valid field to 0 in an asynchronous manner
log.Info("emptyArc add Aid %d, Cids %v", vs.Aid, cids)
s.emptyArcCh <- vs.Aid
}
if len(pages) == 0 {
err = ecode.TvAllDataAuditing
return
}
vs.Pages = pages
return
}
// initRelates init Relates
func (s *Service) initRelates(c context.Context, v *view.View, ip string, now time.Time) {
var (
rls []*view.Relate
err error
)
if rls, err = s.dealRcmdRelate(ctx, v.Aid, ip); err != nil {
log.Error("initRelates For Aid %d, Error %v", v.Aid, err)
return
}
if len(rls) == 0 {
s.prom.Incr("zero_relates")
return
}
v.Relates = rls
}
func (s *Service) dealRcmdRelate(c context.Context, aid int64, ip string) (rls []*view.Relate, err error) {
if rls, err = s.arcDao.RelatesCache(c, aid); err != nil { // mc error
return
}
if len(rls) != 0 {
s.pHit.Incr("relate_cache")
return
}
var (
aids []int64
as map[int64]*arcwar.Arc
arcMetas map[int64]*model.ArcCMS
)
s.pMiss.Incr("relate_cache")
if aids, err = s.arcDao.RelateAids(c, aid, ip); err != nil { // backsource
return
}
if len(aids) == 0 {
return
}
g, errCtx := errgroup.WithContext(c)
g.Go(func() (err error) {
as, err = s.arcDao.Archives(errCtx, aids)
return
})
g.Go(func() (err error) {
arcMetas, err = s.cmsDao.LoadArcsMediaMap(errCtx, aids)
return
})
if err = g.Wait(); err != nil {
log.Error("dealRcmdRelate For Aid %d, Err %v", aid, err)
return
}
for _, aid := range aids {
if a, ok := as[aid]; ok {
// auth, filter can't play ones
if arcCMS, okCMS := arcMetas[aid]; !okCMS {
log.Error("LoadArcsMediaMap Missing Aid %d Info", aid)
continue
} else if canplay, _ := s.cmsDao.UgcErrMsg(arcCMS.Deleted, arcCMS.Result, arcCMS.Valid); !canplay {
log.Warn("LoadArcsMediaMap Aid %d Can't play, Struct %v", aid, arcCMS)
continue
}
// can play, init them
r := &view.Relate{}
r.FromAv(a, "")
rls = append(rls, r)
}
}
if len(rls) != 0 {
s.arcDao.AddRelatesCache(aid, rls)
}
return
}

View File

@@ -0,0 +1,40 @@
package view
import (
"time"
"go-common/library/log"
)
func (s *Service) emptyArcproc() {
var (
ps = s.conf.Cfg.EmptyArc.UnshelvePS
emptyAids = make(map[int64]int, ps)
)
for {
aid, ok := <-s.emptyArcCh
if !ok {
log.Warn("[emptyArcproc] channel quit")
return
}
emptyAids[aid] = 1
if len(emptyAids) < ps { // not enough cid, stay waiting
time.Sleep(2 * time.Second)
continue
}
distinctAIDs := pickKeys(emptyAids)
emptyAids = make(map[int64]int, ps)
if err := s.cmsDao.UnshelveArcs(ctx, distinctAIDs); err != nil {
log.Error("emptyArc Aids %v, Err %v", distinctAIDs, err)
continue
}
log.Info("emptyArc Apply %d Aids: %v", len(distinctAIDs), distinctAIDs)
}
}
func pickKeys(q map[int64]int) (res []int64) {
for k := range q {
res = append(res, k)
}
return
}

View File

@@ -0,0 +1,49 @@
package view
import (
"context"
"go-common/app/interface/main/tv/conf"
"go-common/app/interface/main/tv/dao/account"
"go-common/app/interface/main/tv/dao/archive"
"go-common/app/interface/main/tv/dao/cms"
"go-common/app/interface/main/tv/dao/favorite"
"go-common/app/interface/main/tv/dao/upper"
"go-common/library/stat/prom"
)
// Service .
type Service struct {
conf *conf.Config
// dao
arcDao *archive.Dao
accDao *account.Dao
cmsDao *cms.Dao
upDao *upper.Dao
favDao *favorite.Dao
// prom
pHit *prom.Prom
pMiss *prom.Prom
prom *prom.Prom
emptyArcCh chan int64
}
var ctx = context.TODO()
// New .
func New(c *conf.Config) *Service {
srv := &Service{
conf: c,
arcDao: archive.New(c),
accDao: account.New(c),
cmsDao: cms.New(c),
upDao: upper.New(c),
favDao: favorite.New(c),
pHit: prom.CacheHit,
pMiss: prom.CacheMiss,
prom: prom.BusinessInfoCount,
emptyArcCh: make(chan int64, c.Cfg.EmptyArc.ChanSize),
}
go srv.emptyArcproc()
return srv
}

View File

@@ -0,0 +1,120 @@
package view
import (
"context"
"time"
"go-common/app/interface/main/tv/model"
upMdl "go-common/app/interface/main/tv/model/upper"
"go-common/app/interface/main/tv/model/view"
arcwar "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
)
// View all view data.
func (s *Service) View(c context.Context, mid, aid int64, ak, ip string, now time.Time) (v *view.View, isok bool, errMsg string, err error) {
var (
arcMeta *model.ArcCMS
faved bool
reginfo *arcwar.Tp
)
if arcMeta, isok, errMsg, err = s.ArcMsg(aid); err != nil { // arc auth msg, if not ok, we return the err msg
log.Info("View ArcMsg Aid:%d, Err:%v", aid, err)
return
} else if !isok {
return
}
if v, err = s.ViewPage(c, mid, aid, ak, ip, now); err != nil { // View page
if err == ecode.AccessDenied || err == ecode.NothingFound {
log.Warn("s.ViewPage() mid(%d) aid(%d) ak(%s) ip(%s) error(%v)",
mid, aid, ak, ip, err)
return
}
if err == ecode.TvAllDataAuditing { // the err is used to transport the message that all the data is being audited
isok, errMsg = s.cmsDao.AuditingMsg()
err = nil
return
}
log.Error("s.ViewPage() mid(%d) aid(%d) ak(%s) ip(%s) error(%v)",
mid, aid, ak, ip, err)
return
}
if reginfo, err = s.arcDao.TypeInfo(int32(arcMeta.TypeID)); err != nil {
log.Warn("s.arcDao.TypeInfo tid(%d) error(%v)", arcMeta.TypeID, err)
}
if reginfo != nil {
v.PID = reginfo.Pid
}
s.arcMetaInterv(v, arcMeta) // cms interv
s.initRelates(c, v, ip, now) // get relates
v.ReqUser = &view.ReqUser{}
if faved, err = s.favDao.InDefault(c, mid, aid); err != nil {
log.Warn("s.favDao InDefault Mid %d, Aid %d, Err %v", mid, aid, err)
}
if faved {
v.ReqUser.Favorite = 1
}
return
}
// arcMetaInterv replaces view's archive info by arc cms meta info
func (s *Service) arcMetaInterv(v *view.View, arcMeta *model.ArcCMS) {
if arcMeta == nil {
return
}
if arcMeta.Title != "" {
v.Static.Title = arcMeta.Title
}
if arcMeta.Cover != "" {
v.Static.Pic = arcMeta.Cover
}
if arcMeta.Content != "" {
v.Static.Desc = arcMeta.Content
}
// todo 分p的干预
}
// ViewPage view page data.
func (s *Service) ViewPage(c context.Context, mid, aid int64, ak, ip string, now time.Time) (v *view.View, err error) {
var (
vs *view.Static
vp *arcwar.ViewReply
upper *upMdl.Upper
)
if vp, err = s.arcDao.GetView(c, aid); err != nil {
log.Error("ViewPage getView Aid:%d, Err:%v", aid, err)
return
}
if upper, err = s.upDao.LoadUpMeta(c, vp.Arc.Author.Mid); err != nil || !upper.CanShow() { // if upper can't be found or upper is not valid, hide it
vp.Arc.Author.Face = ""
vp.Arc.Author.Name = ""
} else { // if upper is valid, use cms info to show
vp.Arc.Author.Face = upper.CMSFace
vp.Arc.Author.Name = upper.CMSName
}
vs = &view.Static{Arc: vp.Arc}
if err = s.initPages(c, vs, vp.Pages); err != nil {
log.Error("ViewPage initPages Aid %d, Err %v", aid, err)
return
}
v = &view.View{Static: vs}
if v.AttrVal(archive.AttrBitIsPGC) != archive.AttrYes {
// check access
if err = s.checkAceess(c, mid, v.Aid, int(v.State), int(v.Access), ak, ip); err != nil {
// archive is ForbitFixed and Transcoding and StateForbitDistributing need analysis history body .
if v.State != archive.StateForbidFixed {
return
}
err = nil
}
if v.Access > 0 {
v.Stat.View = 0
}
}
if mid != 0 {
v.History, _ = s.arcDao.Progress(ctx, v.Aid, mid)
}
return
}