Files
2019-04-22 18:49:16 +08:00

407 lines
9.3 KiB
Go

package daily
import (
"context"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/app-show/conf"
arcdao "go-common/app/interface/main/app-show/dao/archive"
carddao "go-common/app/interface/main/app-show/dao/card"
tagdao "go-common/app/interface/main/app-show/dao/tag"
"go-common/app/interface/main/app-show/model"
"go-common/app/interface/main/app-show/model/card"
"go-common/app/interface/main/app-show/model/daily"
"go-common/app/service/main/archive/api"
"go-common/library/log"
)
const (
_initDailyKey = "daily_key_%d_%d"
_initColumnKey = "column_key_%d_%d"
_initColumnListKey = "columnlist_key_%d_%d"
)
var (
_emptyDaily = []*daily.Show{}
_emptyList = []*daily.Item{}
)
type Service struct {
c *conf.Config
cdao *carddao.Dao
arc *arcdao.Dao
tag *tagdao.Dao
// tick
tick time.Duration
// columnsCache
columnsCache map[string]*card.Column
// card
cardCache map[string][]*daily.Show
columnCache map[string]*daily.Show
columnListCache map[string][]*daily.Item
cardListCache map[string][]*card.ColumnList
}
// New new a daily service.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
cdao: carddao.New(c),
arc: arcdao.New(c),
tag: tagdao.New(c),
// tick
tick: time.Duration(c.Tick),
// columnsCache
columnsCache: map[string]*card.Column{},
// card
cardCache: map[string][]*daily.Show{},
columnCache: map[string]*daily.Show{},
columnListCache: map[string][]*daily.Item{},
cardListCache: map[string][]*card.ColumnList{},
}
now := time.Now()
s.loadColumnListCache(now)
s.loadColumnsCache()
s.loadNperCache(now)
go s.cacheproc()
return
}
// Daily
func (s *Service) Daily(c context.Context, plat int8, build, dailyID, pn, ps int) (res []*daily.Show) {
if pn > 0 {
pn = pn - 1
}
start := pn * ps
end := start + ps
key := fmt.Sprintf(_initColumnKey, plat, dailyID)
if column, ok := s.columnsCache[key]; ok {
if model.InvalidBuild(build, column.Build, column.Condition) {
res = _emptyDaily
return
}
cardKey := fmt.Sprintf(_initDailyKey, plat, dailyID)
if cards, ok := s.cardCache[cardKey]; ok {
for _, sw := range cards {
if model.InvalidBuild(build, sw.Build, sw.Condition) {
continue
}
res = append(res, sw)
}
resLen := len(res)
if resLen > end {
res = res[start:end]
} else if resLen > start {
res = res[start:]
} else {
res = _emptyDaily
}
}
}
if len(res) == 0 {
res = _emptyDaily
}
return
}
// ColumnList
func (s *Service) ColumnList(plat int8, build, columnID int) (res *daily.ColumnList) {
var (
column []*daily.ColumnList
)
key := fmt.Sprintf(_initColumnListKey, plat, columnID)
if columns, ok := s.cardListCache[key]; ok {
for _, c := range columns {
if model.InvalidBuild(build, c.Build, c.Condition) {
continue
}
tmp := &daily.ColumnList{
Cid: c.Cid,
Name: c.Name,
Ceid: c.Ceid,
Cname: c.Cname,
}
column = append(column, tmp)
}
if len(column) > 0 {
res = &daily.ColumnList{
Ceid: column[0].Ceid,
Name: column[0].Cname,
Children: column,
}
}
}
return
}
// Category
func (s *Service) Category(plat int8, build, categoryID, columnID, pn, ps int) (res *daily.Show) {
var (
key string
)
if pn > 0 {
pn = pn - 1
}
start := pn * ps
end := start + ps
if columnID > 0 {
key = fmt.Sprintf(_initDailyKey, plat, columnID)
} else {
listKey := fmt.Sprintf(_initColumnListKey, plat, categoryID)
if columns, ok := s.cardListCache[listKey]; ok {
for _, c := range columns {
if model.InvalidBuild(build, c.Build, c.Condition) {
continue
}
key = fmt.Sprintf(_initDailyKey, plat, c.Cid)
break
}
}
}
if columns, ok := s.columnCache[key]; ok {
res = columns
if pn*ps > 400 {
res.Body = _emptyList
return
}
if res.Body, ok = s.columnListCache[key]; ok {
resLen := len(res.Body)
if resLen > end {
res.Body = res.Body[start:end]
} else if resLen > start {
res.Body = res.Body[start:]
} else {
res.Body = _emptyList
}
}
if len(res.Body) == 0 {
res.Body = _emptyList
}
}
return
}
// loadColumnsCache load all columns cache
func (s *Service) loadColumnsCache() {
res, err := s.cdao.Columns(context.TODO())
if err != nil {
log.Error("s.cdao.Columns error(%v)", err)
return
}
tmp := map[string]*card.Column{}
for plat, columns := range res {
for _, column := range columns {
key := fmt.Sprintf(_initColumnKey, plat, column.ID)
tmp[key] = column
}
}
s.columnsCache = tmp
}
// loadColumnListCache
func (s *Service) loadColumnListCache(now time.Time) {
var (
tmp = map[string][]*card.ColumnList{}
)
platColumns, err := s.cdao.ColumnPlatList(context.TODO(), now)
if err != nil {
log.Error("s.cdao.ColumnPlatList error(%v)", err)
return
}
for plat, columns := range platColumns {
for _, column := range columns {
key := fmt.Sprintf(_initColumnListKey, plat, column.Ceid)
tmp[key] = append(tmp[key], column)
}
}
s.cardListCache = tmp
}
// loadNperCache
func (s *Service) loadNperCache(now time.Time) {
hdm, err := s.cdao.ColumnNpers(context.TODO(), now)
if err != nil {
log.Error("s.cdao.ColumnNpers error(%v)", err)
return
}
itm, aids, err := s.cdao.NperContents(context.TODO(), now)
if err != nil {
log.Error("s.cdao.NperContents error(%v)", err)
return
}
tmp, tmpColumns, tmpList := s.mergeCard(context.TODO(), hdm, itm, aids, now)
s.cardCache = tmp
s.columnCache = tmpColumns
s.columnListCache = tmpList
}
// cacheproc load all cache.
func (s *Service) cacheproc() {
for {
time.Sleep(s.tick)
now := time.Now()
s.loadColumnListCache(now)
s.loadColumnsCache()
s.loadNperCache(now)
}
}
// mergeCard
func (s *Service) mergeCard(c context.Context, hdm map[int8][]*card.ColumnNper, itm map[int][]*card.Content, itmaids map[int][]int64, now time.Time) (res map[string][]*daily.Show, columns map[string]*daily.Show, columnList map[string][]*daily.Item) {
var (
dailyMAX = 31
)
res = map[string][]*daily.Show{}
columnList = map[string][]*daily.Item{}
columns = map[string]*daily.Show{}
for plat, hds := range hdm {
for _, hd := range hds {
var (
ok bool
column *card.Column
)
columnskey := fmt.Sprintf(_initColumnKey, plat, hd.ColumnID)
if column, ok = s.columnsCache[columnskey]; !ok {
continue
}
switch column.Type {
case model.GotoDaily:
if dailykey := fmt.Sprintf(_initDailyKey, plat, hd.ColumnID); len(res[dailykey]) > dailyMAX {
continue
}
}
var (
sis []*daily.Item
)
its, ok := itm[hd.ID]
if !ok {
its = []*card.Content{}
}
switch column.Tpl {
case 1, 2:
var tmpItem = map[int64]*daily.Item{}
if aids, ok := itmaids[hd.ID]; ok {
tmpItem = s.fromCardAids(context.TODO(), aids)
}
for _, ci := range its {
si := s.fillCardItem(ci, tmpItem)
if si.Title == "" {
continue
}
if ci.TagID > 0 {
si.TagName, si.TagID = s.fromTagIDByName(c, ci.TagID, now)
}
sis = append(sis, si)
}
}
if len(sis) == 0 {
continue
}
sw := &daily.Show{}
sw.Head = &daily.Head{
ColumnID: hd.ID,
Build: hd.Build,
Condition: hd.Condition,
Plat: hd.Plat,
Desc: hd.Desc,
Type: column.Type,
}
if hd.Cover != "" {
sw.Cover = hd.Cover
}
var key string
switch sw.Head.Type {
case model.GotoDaily:
key = fmt.Sprintf(_initDailyKey, plat, hd.ColumnID)
sw.Head.Title = hd.Name
if len(res[key]) == 0 {
sw.Head.Date = now.Unix()
} else {
sw.Head.Date = int64(hd.NperTime)
}
sw.Body = sis
res[key] = append(res[key], sw)
case model.GotoColumn:
key = fmt.Sprintf(_initDailyKey, plat, hd.ID)
sw.Head.Title = hd.Name
sw.Head.Goto = hd.Goto
sw.Head.Param = hd.Param
sw.Head.URI = hd.URI
columnList[key] = sis
columns[key] = sw
}
}
}
return
}
// fillCardItem
func (s *Service) fillCardItem(csi *card.Content, tsi map[int64]*daily.Item) (si *daily.Item) {
si = &daily.Item{}
switch csi.Type {
case model.CardGotoAv:
si.Goto = model.GotoAv
si.Param = csi.Value
}
si.URI = model.FillURI(si.Goto, si.Param, nil)
if si.Goto == model.GotoAv {
aid, err := strconv.ParseInt(si.Param, 10, 64)
if err != nil {
log.Error("strconv.ParseInt(%s) error(%v)", si.Param, err)
} else {
if it, ok := tsi[aid]; ok {
si = it
if csi.Title != "" {
si.Title = csi.Title
}
} else {
si = &daily.Item{}
}
}
}
return
}
// fromCardAids get Aids.
func (s *Service) fromCardAids(ctx context.Context, aids []int64) (data map[int64]*daily.Item) {
var (
arc *api.Arc
ok bool
)
as, err := s.arc.ArchivesPB(ctx, aids)
if err != nil {
log.Error("s.arc.ArchivesPB(%v) error(%v)", aids, err)
return
}
if len(as) == 0 {
log.Warn("s.arc.ArchivesPB(%v) length is 0", aids)
return
}
data = map[int64]*daily.Item{}
for _, aid := range aids {
if arc, ok = as[aid]; ok {
if !arc.IsNormal() {
continue
}
i := &daily.Item{}
i.FromArchivePB(arc)
data[aid] = i
}
}
return
}
// fromTagIDByName from tag_id by tag_name
func (s *Service) fromTagIDByName(ctx context.Context, tagID int, now time.Time) (tagName string, tagIDInt int64) {
tag, err := s.tag.TagInfo(ctx, 0, tagID, now)
if err != nil {
log.Error("s.tag.TagInfo(%d) error(%v)", tagID, err)
return
}
tagName = tag.Name
tagIDInt = tag.Tid
return
}