123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- package splash
- import (
- "context"
- "encoding/json"
- "fmt"
- "math"
- "strconv"
- "strings"
- "time"
- "go-common/app/interface/main/app-resource/conf"
- addao "go-common/app/interface/main/app-resource/dao/ad"
- locdao "go-common/app/interface/main/app-resource/dao/location"
- spdao "go-common/app/interface/main/app-resource/dao/splash"
- "go-common/app/interface/main/app-resource/model"
- "go-common/app/interface/main/app-resource/model/splash"
- locmdl "go-common/app/service/main/location/model"
- "go-common/library/ecode"
- "go-common/library/log"
- "go-common/library/net/metadata"
- "go-common/library/sync/errgroup"
- farm "github.com/dgryski/go-farm"
- )
- const (
- _birthType = 2
- _vipType = 4
- _defaultType = 0
- _initVersion = "splash_version"
- _initSplashKey = "splash_key_%d_%d_%d"
- )
- var (
- _emptySplashs = []*splash.Splash{}
- )
- // Service is splash service.
- type Service struct {
- dao *spdao.Dao
- ad *addao.Dao
- loc *locdao.Dao
- // tick
- tick time.Duration
- // splash duration
- splashTick string
- // screen
- andScreen map[int8]map[float64][2]int
- iosScreen map[int8]map[float64][2]int
- // cache
- cache map[string][]*splash.Splash
- defaultCache map[string][]*splash.Splash
- birthCache map[string][]*splash.Splash
- vipCache map[string][]*splash.Splash
- // splash random
- splashRandomIds map[int8]map[int64]struct{}
- }
- // New new a splash service.
- func New(c *conf.Config) *Service {
- s := &Service{
- dao: spdao.New(c),
- ad: addao.New(c),
- loc: locdao.New(c),
- // tick
- tick: time.Duration(c.Tick),
- // splash duration
- splashTick: c.Duration.Splash,
- // screen
- andScreen: map[int8]map[float64][2]int{},
- iosScreen: map[int8]map[float64][2]int{},
- // splash cache
- cache: map[string][]*splash.Splash{},
- defaultCache: map[string][]*splash.Splash{},
- birthCache: map[string][]*splash.Splash{},
- vipCache: map[string][]*splash.Splash{},
- // splash random
- splashRandomIds: map[int8]map[int64]struct{}{},
- }
- s.load()
- s.loadBirth()
- s.loadSplashRandomIds(c)
- go s.loadproc()
- return s
- }
- // Display dispaly data.
- func (s *Service) Display(c context.Context, plat int8, w, h, build int, channel, ver string, now time.Time) (res []*splash.Splash, version string, err error) {
- // get from cache
- res, version, err = s.getCache(c, plat, w, h, build, channel, ver, now)
- return
- }
- func (s *Service) Birthday(c context.Context, plat int8, w, h int, birth string) (res *splash.Splash, err error) {
- // get from cache
- res, err = s.getBirthCache(c, plat, w, h, birth)
- return
- }
- // AdList ad splash list
- func (s *Service) AdList(c context.Context, plat int8, mobiApp, device, buvid, birth, adExtra string, height, width, build int, mid int64) (res *splash.CmSplash, err error) {
- var (
- list []*splash.List
- show []*splash.Show
- config *splash.CmConfig
- )
- if ok := model.IsOverseas(plat); ok {
- err = ecode.NotModified
- return
- }
- g, ctx := errgroup.WithContext(c)
- g.Go(func() error {
- var e error
- if list, config, e = s.ad.SplashList(ctx, mobiApp, device, buvid, birth, adExtra, height, width, build, mid); e != nil {
- log.Error("cm s.ad.SplashList error(%v)", e)
- return e
- }
- return nil
- })
- g.Go(func() error {
- var e error
- if show, e = s.ad.SplashShow(ctx, mobiApp, device, buvid, birth, adExtra, height, width, build, mid); e != nil {
- log.Error("cm s.ad.SplashShow error(%v)", e)
- return e
- }
- return nil
- })
- if err = g.Wait(); err != nil {
- log.Error("cm splash errgroup.WithContext error(%v)", err)
- return
- }
- res = &splash.CmSplash{
- CmConfig: config,
- List: list,
- Show: show,
- }
- return
- }
- // getCache cache display data.
- func (s *Service) getCache(c context.Context, plat int8, w, h, build int, channel, ver string, now time.Time) (res []*splash.Splash, version string, err error) {
- var (
- ip = metadata.String(c, metadata.RemoteIP)
- screen map[int8]map[float64][2]int
- )
- if model.IsIOS(plat) {
- screen = s.iosScreen
- } else if model.IsAndroid(plat) {
- screen = s.andScreen
- }
- // TODO fate go start
- var (
- fgId int64
- oldIdStr string
- fgids = s.splashRandomIds[plat]
- pids []string
- auths map[string]*locmdl.Auth
- )
- vers := strings.Split(ver, strconv.Itoa(now.Year()))
- if len(vers) > 1 {
- ver = vers[0]
- oldIdStr = vers[1]
- }
- for id, _ := range fgids {
- fgId = id
- idStr := strconv.FormatInt(fgId, 10)
- if oldIdStr != idStr {
- break
- }
- }
- for tSplash, tScreen := range screen {
- var ss []*splash.Splash
- if ss = s.cache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
- wh := s.similarScreen(plat, w, h, tScreen)
- width := wh[0]
- height := wh[1]
- ss = s.cache[fmt.Sprintf(_initSplashKey, plat, width, height)]
- }
- for _, splash := range ss {
- if splash.Type != tSplash {
- continue
- }
- if splash.Area != "" {
- pids = append(pids, splash.Area)
- }
- }
- }
- if len(pids) > 0 {
- auths, _ = s.loc.AuthPIDs(c, strings.Join(pids, ","), ip)
- }
- // TODO fate go end
- for tSplash, tScreen := range screen {
- var (
- ss []*splash.Splash
- // advance time
- advance, _ = time.ParseDuration(s.splashTick)
- )
- if ss = s.cache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
- wh := s.similarScreen(plat, w, h, tScreen)
- width := wh[0]
- height := wh[1]
- ss = s.cache[fmt.Sprintf(_initSplashKey, plat, width, height)]
- }
- for _, splash := range ss {
- if splash.Type != tSplash {
- continue
- }
- // gt splash start time
- if splash.NoPreview == 1 {
- if h1 := now.Add(advance); int64(splash.Start) > h1.Unix() {
- continue
- }
- }
- // TODO fate go start
- if fgids != nil && splash.ID != fgId {
- if _, ok := fgids[splash.ID]; ok {
- continue
- }
- }
- // TODO fate go end
- if model.InvalidBuild(build, splash.Build, splash.Condition) {
- continue
- }
- if splash.Area != "" {
- if auth, ok := auths[splash.Area]; ok && auth.Play == locmdl.Forbidden {
- log.Warn("s.invalid area(%v) ip(%v) error(%v)", splash.Area, ip, err)
- continue
- }
- }
- res = append(res, splash)
- }
- }
- if vSplash := s.getVipCache(plat, w, h, screen, now); vSplash != nil {
- res = append(res, vSplash)
- }
- if dSplash := s.getDefaultCache(plat, w, h, screen, now); dSplash != nil {
- res = append(res, dSplash)
- }
- if len(res) == 0 {
- res = _emptySplashs
- }
- if version = s.hash(res); version == ver {
- err = ecode.NotModified
- res = nil
- }
- version = version + strconv.Itoa(now.Year()) + strconv.FormatInt(fgId, 10)
- return
- }
- // getBirthCache get birthday splash.
- func (s *Service) getBirthCache(c context.Context, plat int8, w, h int, birth string) (res *splash.Splash, err error) {
- var (
- screen map[int8]map[float64][2]int
- wh [2]int
- )
- if model.IsIOS(plat) {
- screen = s.iosScreen
- } else if model.IsAndroid(plat) {
- screen = s.andScreen
- }
- if v, ok := screen[_birthType]; !ok {
- return
- } else {
- wh = s.similarScreen(plat, w, h, v)
- w = wh[0]
- h = wh[1]
- }
- sps := s.birthCache[fmt.Sprintf(_initSplashKey, plat, w, h)]
- for _, sp := range sps {
- if sp.BirthStartMonth == "12" && sp.BirthEndMonth == "01" {
- if (sp.BirthStart <= birth && "1231" >= birth) || ("0101" <= birth && sp.BirthEnd >= birth) {
- res = sp
- return
- }
- }
- if sp.BirthStart <= birth && sp.BirthEnd >= birth {
- res = sp
- return
- }
- }
- err = ecode.NothingFound
- return
- }
- // getVipCache
- func (s *Service) getVipCache(plat int8, w, h int, screen map[int8]map[float64][2]int, now time.Time) (res *splash.Splash) {
- var (
- ss []*splash.Splash
- )
- if v, ok := screen[_vipType]; !ok {
- return
- } else if ss = s.vipCache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
- wh := s.similarScreen(plat, w, h, v)
- width := wh[0]
- height := wh[1]
- ss = s.vipCache[fmt.Sprintf(_initSplashKey, plat, width, height)]
- }
- if len(ss) == 0 {
- return
- }
- res = ss[(now.Day() % len(ss))]
- return
- }
- // getDefaultCache
- func (s *Service) getDefaultCache(plat int8, w, h int, screen map[int8]map[float64][2]int, now time.Time) (res *splash.Splash) {
- var (
- ss []*splash.Splash
- )
- if v, ok := screen[_defaultType]; !ok {
- return
- } else if ss = s.defaultCache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
- wh := s.similarScreen(plat, w, h, v)
- width := wh[0]
- height := wh[1]
- ss = s.defaultCache[fmt.Sprintf(_initSplashKey, plat, width, height)]
- }
- if len(ss) == 0 {
- return
- }
- res = ss[(now.Day() % len(ss))]
- return
- }
- func (s *Service) hash(v []*splash.Splash) string {
- bs, err := json.Marshal(v)
- if err != nil {
- log.Error("json.Marshal error(%v)", err)
- return _initVersion
- }
- return strconv.FormatUint(farm.Hash64(bs), 10)
- }
- // cacheproc load splash into cache.
- func (s *Service) load() {
- res, err := s.dao.ActiveAll(context.TODO())
- if err != nil {
- log.Error("s.dao.GetActiveAll() error(%v)", err)
- return
- }
- var (
- tmp []*splash.Splash
- tmpdefault []*splash.Splash
- )
- for _, r := range res {
- if r.Type == _defaultType {
- tmpdefault = append(tmpdefault, r)
- } else {
- tmp = append(tmp, r)
- }
- }
- s.cache = s.dealCache(tmp)
- log.Info("splash cacheproc success")
- s.defaultCache = s.dealCache(tmpdefault)
- log.Info("splash default cacheproc tmpdefault")
- resVip, err := s.dao.ActiveVip(context.TODO())
- if err != nil {
- log.Error("s.dao.ActiveVip() error(%v)", err)
- return
- }
- s.vipCache = s.dealCache(resVip)
- log.Info("splash Vip cacheproc success")
- }
- // loadBirth load birthday splash.
- func (s *Service) loadBirth() {
- res, err := s.dao.ActiveBirth(context.TODO())
- if err != nil {
- log.Error("s.dao.ActiveBirthday() error(%v)", err)
- return
- }
- s.birthCache = s.dealCache(res)
- log.Info("splash Birthday cacheproc success")
- }
- // dealCache
- func (s *Service) dealCache(sps []*splash.Splash) (res map[string][]*splash.Splash) {
- res = map[string][]*splash.Splash{}
- tmpand := map[int8]map[float64][2]int{}
- tmpios := map[int8]map[float64][2]int{}
- for plat, v := range s.andScreen {
- for r, value := range v {
- if _, ok := tmpand[plat]; ok {
- tmpand[plat][r] = value
- } else {
- tmpand[plat] = map[float64][2]int{
- r: value,
- }
- }
- }
- }
- for plat, v := range s.iosScreen {
- for r, value := range v {
- if _, ok := tmpios[plat]; ok {
- tmpios[plat][r] = value
- } else {
- tmpios[plat] = map[float64][2]int{
- r: value,
- }
- }
- }
- }
- for _, v := range sps {
- v.URI = model.FillURI(v.Goto, v.Param, nil)
- key := fmt.Sprintf(_initSplashKey, v.Plat, v.Width, v.Height)
- res[key] = append(res[key], v)
- // generate screen
- if model.IsAndroid(v.Plat) {
- if _, ok := tmpand[v.Type]; ok {
- tmpand[v.Type][splash.Ratio(v.Width, v.Height)] = [2]int{v.Width, v.Height}
- } else {
- tmpand[v.Type] = map[float64][2]int{
- splash.Ratio(v.Width, v.Height): [2]int{v.Width, v.Height},
- }
- }
- } else if model.IsIOS(v.Plat) {
- if _, ok := tmpios[v.Type]; ok {
- tmpios[v.Type][splash.Ratio(v.Width, v.Height)] = [2]int{v.Width, v.Height}
- } else {
- tmpios[v.Type] = map[float64][2]int{
- splash.Ratio(v.Width, v.Height): [2]int{v.Width, v.Height},
- }
- }
- }
- }
- s.andScreen = tmpand
- s.iosScreen = tmpios
- return
- }
- func (s *Service) loadSplashRandomIds(c *conf.Config) {
- splashIds := map[int8]map[int64]struct{}{}
- for k, v := range c.Splash.Random {
- key := model.Plat(k, "")
- splashIds[key] = map[int64]struct{}{}
- for _, idStr := range v {
- idInt, _ := strconv.ParseInt(idStr, 10, 64)
- splashIds[key][idInt] = struct{}{}
- }
- }
- s.splashRandomIds = splashIds
- log.Info("splash Random cache success")
- }
- // loadproc load process.
- func (s *Service) loadproc() {
- for {
- time.Sleep(s.tick)
- s.load()
- s.loadBirth()
- }
- }
- // similarScreen android screnn size
- func (s *Service) similarScreen(plat int8, w, h int, screen map[float64][2]int) (wh [2]int) {
- if model.IsIOS(plat) {
- switch {
- case w == 750:
- h = 1334
- case w == 640 && h > 960:
- h = 1136
- case w == 640:
- h = 960
- case w == 2732:
- h = 2048
- case w == 2048:
- h = 1536
- case w == 1024:
- h = 768
- case w == 1242:
- h = 2208
- case w == 1496 || w == 1536:
- w = 2048
- h = 1536
- case w == 748 || w == 768:
- w = 1024
- h = 768
- }
- }
- min := float64(1<<64 - 1)
- for r, s := range screen {
- if s[0] == w && s[1] == h {
- wh = s
- return
- }
- abs := math.Abs(splash.Ratio(w, h) - r)
- if abs < min {
- min = abs
- wh = s
- }
- }
- return
- }
- // Close dao
- func (s *Service) Close() {
- s.dao.Close()
- }
|