splash.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. package splash
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "math"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "go-common/app/interface/main/app-resource/conf"
  11. addao "go-common/app/interface/main/app-resource/dao/ad"
  12. locdao "go-common/app/interface/main/app-resource/dao/location"
  13. spdao "go-common/app/interface/main/app-resource/dao/splash"
  14. "go-common/app/interface/main/app-resource/model"
  15. "go-common/app/interface/main/app-resource/model/splash"
  16. locmdl "go-common/app/service/main/location/model"
  17. "go-common/library/ecode"
  18. "go-common/library/log"
  19. "go-common/library/net/metadata"
  20. "go-common/library/sync/errgroup"
  21. farm "github.com/dgryski/go-farm"
  22. )
  23. const (
  24. _birthType = 2
  25. _vipType = 4
  26. _defaultType = 0
  27. _initVersion = "splash_version"
  28. _initSplashKey = "splash_key_%d_%d_%d"
  29. )
  30. var (
  31. _emptySplashs = []*splash.Splash{}
  32. )
  33. // Service is splash service.
  34. type Service struct {
  35. dao *spdao.Dao
  36. ad *addao.Dao
  37. loc *locdao.Dao
  38. // tick
  39. tick time.Duration
  40. // splash duration
  41. splashTick string
  42. // screen
  43. andScreen map[int8]map[float64][2]int
  44. iosScreen map[int8]map[float64][2]int
  45. // cache
  46. cache map[string][]*splash.Splash
  47. defaultCache map[string][]*splash.Splash
  48. birthCache map[string][]*splash.Splash
  49. vipCache map[string][]*splash.Splash
  50. // splash random
  51. splashRandomIds map[int8]map[int64]struct{}
  52. }
  53. // New new a splash service.
  54. func New(c *conf.Config) *Service {
  55. s := &Service{
  56. dao: spdao.New(c),
  57. ad: addao.New(c),
  58. loc: locdao.New(c),
  59. // tick
  60. tick: time.Duration(c.Tick),
  61. // splash duration
  62. splashTick: c.Duration.Splash,
  63. // screen
  64. andScreen: map[int8]map[float64][2]int{},
  65. iosScreen: map[int8]map[float64][2]int{},
  66. // splash cache
  67. cache: map[string][]*splash.Splash{},
  68. defaultCache: map[string][]*splash.Splash{},
  69. birthCache: map[string][]*splash.Splash{},
  70. vipCache: map[string][]*splash.Splash{},
  71. // splash random
  72. splashRandomIds: map[int8]map[int64]struct{}{},
  73. }
  74. s.load()
  75. s.loadBirth()
  76. s.loadSplashRandomIds(c)
  77. go s.loadproc()
  78. return s
  79. }
  80. // Display dispaly data.
  81. 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) {
  82. // get from cache
  83. res, version, err = s.getCache(c, plat, w, h, build, channel, ver, now)
  84. return
  85. }
  86. func (s *Service) Birthday(c context.Context, plat int8, w, h int, birth string) (res *splash.Splash, err error) {
  87. // get from cache
  88. res, err = s.getBirthCache(c, plat, w, h, birth)
  89. return
  90. }
  91. // AdList ad splash list
  92. 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) {
  93. var (
  94. list []*splash.List
  95. show []*splash.Show
  96. config *splash.CmConfig
  97. )
  98. if ok := model.IsOverseas(plat); ok {
  99. err = ecode.NotModified
  100. return
  101. }
  102. g, ctx := errgroup.WithContext(c)
  103. g.Go(func() error {
  104. var e error
  105. if list, config, e = s.ad.SplashList(ctx, mobiApp, device, buvid, birth, adExtra, height, width, build, mid); e != nil {
  106. log.Error("cm s.ad.SplashList error(%v)", e)
  107. return e
  108. }
  109. return nil
  110. })
  111. g.Go(func() error {
  112. var e error
  113. if show, e = s.ad.SplashShow(ctx, mobiApp, device, buvid, birth, adExtra, height, width, build, mid); e != nil {
  114. log.Error("cm s.ad.SplashShow error(%v)", e)
  115. return e
  116. }
  117. return nil
  118. })
  119. if err = g.Wait(); err != nil {
  120. log.Error("cm splash errgroup.WithContext error(%v)", err)
  121. return
  122. }
  123. res = &splash.CmSplash{
  124. CmConfig: config,
  125. List: list,
  126. Show: show,
  127. }
  128. return
  129. }
  130. // getCache cache display data.
  131. 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) {
  132. var (
  133. ip = metadata.String(c, metadata.RemoteIP)
  134. screen map[int8]map[float64][2]int
  135. )
  136. if model.IsIOS(plat) {
  137. screen = s.iosScreen
  138. } else if model.IsAndroid(plat) {
  139. screen = s.andScreen
  140. }
  141. // TODO fate go start
  142. var (
  143. fgId int64
  144. oldIdStr string
  145. fgids = s.splashRandomIds[plat]
  146. pids []string
  147. auths map[string]*locmdl.Auth
  148. )
  149. vers := strings.Split(ver, strconv.Itoa(now.Year()))
  150. if len(vers) > 1 {
  151. ver = vers[0]
  152. oldIdStr = vers[1]
  153. }
  154. for id, _ := range fgids {
  155. fgId = id
  156. idStr := strconv.FormatInt(fgId, 10)
  157. if oldIdStr != idStr {
  158. break
  159. }
  160. }
  161. for tSplash, tScreen := range screen {
  162. var ss []*splash.Splash
  163. if ss = s.cache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
  164. wh := s.similarScreen(plat, w, h, tScreen)
  165. width := wh[0]
  166. height := wh[1]
  167. ss = s.cache[fmt.Sprintf(_initSplashKey, plat, width, height)]
  168. }
  169. for _, splash := range ss {
  170. if splash.Type != tSplash {
  171. continue
  172. }
  173. if splash.Area != "" {
  174. pids = append(pids, splash.Area)
  175. }
  176. }
  177. }
  178. if len(pids) > 0 {
  179. auths, _ = s.loc.AuthPIDs(c, strings.Join(pids, ","), ip)
  180. }
  181. // TODO fate go end
  182. for tSplash, tScreen := range screen {
  183. var (
  184. ss []*splash.Splash
  185. // advance time
  186. advance, _ = time.ParseDuration(s.splashTick)
  187. )
  188. if ss = s.cache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
  189. wh := s.similarScreen(plat, w, h, tScreen)
  190. width := wh[0]
  191. height := wh[1]
  192. ss = s.cache[fmt.Sprintf(_initSplashKey, plat, width, height)]
  193. }
  194. for _, splash := range ss {
  195. if splash.Type != tSplash {
  196. continue
  197. }
  198. // gt splash start time
  199. if splash.NoPreview == 1 {
  200. if h1 := now.Add(advance); int64(splash.Start) > h1.Unix() {
  201. continue
  202. }
  203. }
  204. // TODO fate go start
  205. if fgids != nil && splash.ID != fgId {
  206. if _, ok := fgids[splash.ID]; ok {
  207. continue
  208. }
  209. }
  210. // TODO fate go end
  211. if model.InvalidBuild(build, splash.Build, splash.Condition) {
  212. continue
  213. }
  214. if splash.Area != "" {
  215. if auth, ok := auths[splash.Area]; ok && auth.Play == locmdl.Forbidden {
  216. log.Warn("s.invalid area(%v) ip(%v) error(%v)", splash.Area, ip, err)
  217. continue
  218. }
  219. }
  220. res = append(res, splash)
  221. }
  222. }
  223. if vSplash := s.getVipCache(plat, w, h, screen, now); vSplash != nil {
  224. res = append(res, vSplash)
  225. }
  226. if dSplash := s.getDefaultCache(plat, w, h, screen, now); dSplash != nil {
  227. res = append(res, dSplash)
  228. }
  229. if len(res) == 0 {
  230. res = _emptySplashs
  231. }
  232. if version = s.hash(res); version == ver {
  233. err = ecode.NotModified
  234. res = nil
  235. }
  236. version = version + strconv.Itoa(now.Year()) + strconv.FormatInt(fgId, 10)
  237. return
  238. }
  239. // getBirthCache get birthday splash.
  240. func (s *Service) getBirthCache(c context.Context, plat int8, w, h int, birth string) (res *splash.Splash, err error) {
  241. var (
  242. screen map[int8]map[float64][2]int
  243. wh [2]int
  244. )
  245. if model.IsIOS(plat) {
  246. screen = s.iosScreen
  247. } else if model.IsAndroid(plat) {
  248. screen = s.andScreen
  249. }
  250. if v, ok := screen[_birthType]; !ok {
  251. return
  252. } else {
  253. wh = s.similarScreen(plat, w, h, v)
  254. w = wh[0]
  255. h = wh[1]
  256. }
  257. sps := s.birthCache[fmt.Sprintf(_initSplashKey, plat, w, h)]
  258. for _, sp := range sps {
  259. if sp.BirthStartMonth == "12" && sp.BirthEndMonth == "01" {
  260. if (sp.BirthStart <= birth && "1231" >= birth) || ("0101" <= birth && sp.BirthEnd >= birth) {
  261. res = sp
  262. return
  263. }
  264. }
  265. if sp.BirthStart <= birth && sp.BirthEnd >= birth {
  266. res = sp
  267. return
  268. }
  269. }
  270. err = ecode.NothingFound
  271. return
  272. }
  273. // getVipCache
  274. func (s *Service) getVipCache(plat int8, w, h int, screen map[int8]map[float64][2]int, now time.Time) (res *splash.Splash) {
  275. var (
  276. ss []*splash.Splash
  277. )
  278. if v, ok := screen[_vipType]; !ok {
  279. return
  280. } else if ss = s.vipCache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
  281. wh := s.similarScreen(plat, w, h, v)
  282. width := wh[0]
  283. height := wh[1]
  284. ss = s.vipCache[fmt.Sprintf(_initSplashKey, plat, width, height)]
  285. }
  286. if len(ss) == 0 {
  287. return
  288. }
  289. res = ss[(now.Day() % len(ss))]
  290. return
  291. }
  292. // getDefaultCache
  293. func (s *Service) getDefaultCache(plat int8, w, h int, screen map[int8]map[float64][2]int, now time.Time) (res *splash.Splash) {
  294. var (
  295. ss []*splash.Splash
  296. )
  297. if v, ok := screen[_defaultType]; !ok {
  298. return
  299. } else if ss = s.defaultCache[fmt.Sprintf(_initSplashKey, plat, w, h)]; len(ss) == 0 {
  300. wh := s.similarScreen(plat, w, h, v)
  301. width := wh[0]
  302. height := wh[1]
  303. ss = s.defaultCache[fmt.Sprintf(_initSplashKey, plat, width, height)]
  304. }
  305. if len(ss) == 0 {
  306. return
  307. }
  308. res = ss[(now.Day() % len(ss))]
  309. return
  310. }
  311. func (s *Service) hash(v []*splash.Splash) string {
  312. bs, err := json.Marshal(v)
  313. if err != nil {
  314. log.Error("json.Marshal error(%v)", err)
  315. return _initVersion
  316. }
  317. return strconv.FormatUint(farm.Hash64(bs), 10)
  318. }
  319. // cacheproc load splash into cache.
  320. func (s *Service) load() {
  321. res, err := s.dao.ActiveAll(context.TODO())
  322. if err != nil {
  323. log.Error("s.dao.GetActiveAll() error(%v)", err)
  324. return
  325. }
  326. var (
  327. tmp []*splash.Splash
  328. tmpdefault []*splash.Splash
  329. )
  330. for _, r := range res {
  331. if r.Type == _defaultType {
  332. tmpdefault = append(tmpdefault, r)
  333. } else {
  334. tmp = append(tmp, r)
  335. }
  336. }
  337. s.cache = s.dealCache(tmp)
  338. log.Info("splash cacheproc success")
  339. s.defaultCache = s.dealCache(tmpdefault)
  340. log.Info("splash default cacheproc tmpdefault")
  341. resVip, err := s.dao.ActiveVip(context.TODO())
  342. if err != nil {
  343. log.Error("s.dao.ActiveVip() error(%v)", err)
  344. return
  345. }
  346. s.vipCache = s.dealCache(resVip)
  347. log.Info("splash Vip cacheproc success")
  348. }
  349. // loadBirth load birthday splash.
  350. func (s *Service) loadBirth() {
  351. res, err := s.dao.ActiveBirth(context.TODO())
  352. if err != nil {
  353. log.Error("s.dao.ActiveBirthday() error(%v)", err)
  354. return
  355. }
  356. s.birthCache = s.dealCache(res)
  357. log.Info("splash Birthday cacheproc success")
  358. }
  359. // dealCache
  360. func (s *Service) dealCache(sps []*splash.Splash) (res map[string][]*splash.Splash) {
  361. res = map[string][]*splash.Splash{}
  362. tmpand := map[int8]map[float64][2]int{}
  363. tmpios := map[int8]map[float64][2]int{}
  364. for plat, v := range s.andScreen {
  365. for r, value := range v {
  366. if _, ok := tmpand[plat]; ok {
  367. tmpand[plat][r] = value
  368. } else {
  369. tmpand[plat] = map[float64][2]int{
  370. r: value,
  371. }
  372. }
  373. }
  374. }
  375. for plat, v := range s.iosScreen {
  376. for r, value := range v {
  377. if _, ok := tmpios[plat]; ok {
  378. tmpios[plat][r] = value
  379. } else {
  380. tmpios[plat] = map[float64][2]int{
  381. r: value,
  382. }
  383. }
  384. }
  385. }
  386. for _, v := range sps {
  387. v.URI = model.FillURI(v.Goto, v.Param, nil)
  388. key := fmt.Sprintf(_initSplashKey, v.Plat, v.Width, v.Height)
  389. res[key] = append(res[key], v)
  390. // generate screen
  391. if model.IsAndroid(v.Plat) {
  392. if _, ok := tmpand[v.Type]; ok {
  393. tmpand[v.Type][splash.Ratio(v.Width, v.Height)] = [2]int{v.Width, v.Height}
  394. } else {
  395. tmpand[v.Type] = map[float64][2]int{
  396. splash.Ratio(v.Width, v.Height): [2]int{v.Width, v.Height},
  397. }
  398. }
  399. } else if model.IsIOS(v.Plat) {
  400. if _, ok := tmpios[v.Type]; ok {
  401. tmpios[v.Type][splash.Ratio(v.Width, v.Height)] = [2]int{v.Width, v.Height}
  402. } else {
  403. tmpios[v.Type] = map[float64][2]int{
  404. splash.Ratio(v.Width, v.Height): [2]int{v.Width, v.Height},
  405. }
  406. }
  407. }
  408. }
  409. s.andScreen = tmpand
  410. s.iosScreen = tmpios
  411. return
  412. }
  413. func (s *Service) loadSplashRandomIds(c *conf.Config) {
  414. splashIds := map[int8]map[int64]struct{}{}
  415. for k, v := range c.Splash.Random {
  416. key := model.Plat(k, "")
  417. splashIds[key] = map[int64]struct{}{}
  418. for _, idStr := range v {
  419. idInt, _ := strconv.ParseInt(idStr, 10, 64)
  420. splashIds[key][idInt] = struct{}{}
  421. }
  422. }
  423. s.splashRandomIds = splashIds
  424. log.Info("splash Random cache success")
  425. }
  426. // loadproc load process.
  427. func (s *Service) loadproc() {
  428. for {
  429. time.Sleep(s.tick)
  430. s.load()
  431. s.loadBirth()
  432. }
  433. }
  434. // similarScreen android screnn size
  435. func (s *Service) similarScreen(plat int8, w, h int, screen map[float64][2]int) (wh [2]int) {
  436. if model.IsIOS(plat) {
  437. switch {
  438. case w == 750:
  439. h = 1334
  440. case w == 640 && h > 960:
  441. h = 1136
  442. case w == 640:
  443. h = 960
  444. case w == 2732:
  445. h = 2048
  446. case w == 2048:
  447. h = 1536
  448. case w == 1024:
  449. h = 768
  450. case w == 1242:
  451. h = 2208
  452. case w == 1496 || w == 1536:
  453. w = 2048
  454. h = 1536
  455. case w == 748 || w == 768:
  456. w = 1024
  457. h = 768
  458. }
  459. }
  460. min := float64(1<<64 - 1)
  461. for r, s := range screen {
  462. if s[0] == w && s[1] == h {
  463. wh = s
  464. return
  465. }
  466. abs := math.Abs(splash.Ratio(w, h) - r)
  467. if abs < min {
  468. min = abs
  469. wh = s
  470. }
  471. }
  472. return
  473. }
  474. // Close dao
  475. func (s *Service) Close() {
  476. s.dao.Close()
  477. }