363 lines
8.9 KiB
Go
363 lines
8.9 KiB
Go
package region
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"go-common/app/interface/main/app-show/model"
|
|
"go-common/app/interface/main/app-show/model/region"
|
|
locmdl "go-common/app/service/main/location/model"
|
|
"go-common/library/ecode"
|
|
"go-common/library/log"
|
|
"go-common/library/net/metadata"
|
|
|
|
farm "github.com/dgryski/go-farm"
|
|
)
|
|
|
|
const (
|
|
_initRegionKey = "region_key_%d_%v"
|
|
_initlanguage = "hans"
|
|
_initVersion = "region_version"
|
|
_regionAll = int8(0)
|
|
_isRegion = int8(1)
|
|
_isRank = int8(2)
|
|
_initRegionlimit = "%d_%v"
|
|
_regionRepeat = "r_%d_%d"
|
|
)
|
|
|
|
var (
|
|
_emptyRegions = []*region.Region{}
|
|
_isBangumi = map[int]struct{}{
|
|
13: struct{}{},
|
|
177: struct{}{},
|
|
23: struct{}{},
|
|
11: struct{}{},
|
|
}
|
|
_isBangumiIndex = map[int]struct{}{
|
|
13: struct{}{},
|
|
23: struct{}{},
|
|
11: struct{}{},
|
|
167: struct{}{},
|
|
}
|
|
_regionlimit = map[int8]map[string]map[int]string{
|
|
model.PlatIPhone: map[string]map[int]string{
|
|
"65542_bilibili://cliparea": map[int]string{
|
|
5960: "gt",
|
|
6570: "lt",
|
|
},
|
|
"65541_bilibili://category/65541": map[int]string{
|
|
5960: "gt",
|
|
6570: "lt",
|
|
},
|
|
"65544_bilibili://albumarea": map[int]string{
|
|
6090: "gt",
|
|
6570: "lt",
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
// Regions get regions.
|
|
func (s *Service) Regions(c context.Context, plat int8, build int, ver, mobiApp, device, language string) (rs []*region.Region, version string, err error) {
|
|
ip := metadata.String(c, metadata.RemoteIP)
|
|
rs, version, err = s.getCache(c, plat, build, ver, ip, mobiApp, device, language, "", false)
|
|
return
|
|
}
|
|
|
|
// Regions get regions.
|
|
func (s *Service) RegionsList(c context.Context, plat int8, build int, ver, mobiApp, device, language, entrance string) (rs []*region.Region, version string, err error) {
|
|
ip := metadata.String(c, metadata.RemoteIP)
|
|
rs, version, err = s.getCache(c, plat, build, ver, ip, mobiApp, device, language, entrance, true)
|
|
return
|
|
}
|
|
|
|
// getCache get region from cache.
|
|
func (s *Service) getCache(c context.Context, plat int8, build int, ver, ip, mobiApp, device, language, entrance string, isNew bool) (res []*region.Region, version string, err error) {
|
|
if language == "" {
|
|
language = _initlanguage
|
|
}
|
|
var (
|
|
rs = s.cache[fmt.Sprintf(_initRegionKey, plat, language)]
|
|
child = map[int][]*region.Region{}
|
|
entranceShow = _isRegion
|
|
ridlimit = _regionlimit[plat]
|
|
ridtmp = map[string]struct{}{}
|
|
pids []string
|
|
auths map[string]*locmdl.Auth
|
|
)
|
|
switch entrance {
|
|
case "region":
|
|
entranceShow = _isRegion
|
|
case "rank":
|
|
entranceShow = _isRank
|
|
}
|
|
for _, rtmp := range rs {
|
|
if rtmp.Area != "" {
|
|
pids = append(pids, rtmp.Area)
|
|
}
|
|
}
|
|
if len(pids) > 0 {
|
|
auths, _ = s.loc.AuthPIDs(c, strings.Join(pids, ","), ip)
|
|
}
|
|
Retry:
|
|
for _, rtmp := range rs {
|
|
r := ®ion.Region{}
|
|
*r = *rtmp
|
|
if _, isgbm := _isBangumi[r.Rid]; isgbm {
|
|
r.IsBangumi = 1
|
|
}
|
|
if isNew {
|
|
if r.Entrance != _regionAll && entranceShow != r.Entrance {
|
|
continue
|
|
}
|
|
} else {
|
|
switch r.Rid {
|
|
case 65545, 65542, 65541, 65543, 65544, 65546:
|
|
if mobiApp == "android" {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if r.Rid != 165 || ((mobiApp != "iphone" || device != "pad") || build <= 3590) {
|
|
if model.InvalidBuild(build, r.Build, r.Condition) {
|
|
continue
|
|
}
|
|
}
|
|
key := fmt.Sprintf(_initRegionlimit, r.Rid, r.URI)
|
|
if rlimit, ok := ridlimit[key]; ok {
|
|
for blimit, climit := range rlimit {
|
|
if model.InvalidBuild(build, blimit, climit) {
|
|
continue Retry
|
|
}
|
|
}
|
|
}
|
|
if r.Rid == 65541 && (plat == model.PlatIPhone && build == 7040) {
|
|
continue
|
|
}
|
|
if r.Rid == 65543 && ((plat == model.PlatIPhone && (build == 7070 || build == 7040 || build == 7030)) ||
|
|
(plat == model.PlatAndroid && (build == 591182 || build == 591181 || build == 591178 || build == 591177))) {
|
|
continue
|
|
}
|
|
if auth, ok := auths[r.Area]; ok && auth.Play == locmdl.Forbidden {
|
|
log.Warn("s.invalid area(%v) ip(%v) error(%v)", r.Area, ip, err)
|
|
continue
|
|
}
|
|
if isAudit := s.auditRegion(mobiApp, plat, build, r.Rid); isAudit {
|
|
continue
|
|
}
|
|
rkey := fmt.Sprintf(_regionRepeat, r.Rid, r.Reid)
|
|
if _, ok := ridtmp[rkey]; !ok {
|
|
ridtmp[rkey] = struct{}{}
|
|
} else {
|
|
continue
|
|
}
|
|
if r.Reid != 0 {
|
|
cl, ok := child[r.Reid]
|
|
if !ok {
|
|
cl = []*region.Region{}
|
|
}
|
|
cl = append(cl, r)
|
|
child[r.Reid] = cl
|
|
} else {
|
|
res = append(res, r)
|
|
}
|
|
}
|
|
if len(res) == 0 {
|
|
res = _emptyRegions
|
|
} else {
|
|
for _, r := range res {
|
|
r.Children = child[r.Rid]
|
|
}
|
|
}
|
|
if version = s.hash(res); version == ver {
|
|
err = ecode.NotModified
|
|
res = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
// NewRegionList get region from cache.
|
|
func (s *Service) NewRegionList(c context.Context, plat int8, build int, ver, mobiApp, device, language string) (res []*region.Region, version string, err error) {
|
|
ip := metadata.String(c, metadata.RemoteIP)
|
|
var (
|
|
hantlanguage = "hant"
|
|
)
|
|
if ok := model.IsOverseas(plat); ok && language != _initlanguage && language != hantlanguage {
|
|
language = hantlanguage
|
|
} else if language == "" {
|
|
language = _initlanguage
|
|
}
|
|
var (
|
|
rs = s.cachelist[fmt.Sprintf(_initRegionKey, plat, language)]
|
|
child = map[int][]*region.Region{}
|
|
ridtmp = map[string]struct{}{}
|
|
pids []string
|
|
auths map[string]*locmdl.Auth
|
|
)
|
|
for _, rtmp := range rs {
|
|
if rtmp.Area != "" {
|
|
pids = append(pids, rtmp.Area)
|
|
}
|
|
}
|
|
if len(pids) > 0 {
|
|
auths, _ = s.loc.AuthPIDs(c, strings.Join(pids, ","), ip)
|
|
}
|
|
LOOP:
|
|
for _, rtmp := range rs {
|
|
r := ®ion.Region{}
|
|
*r = *rtmp
|
|
if _, isgbm := _isBangumiIndex[r.Rid]; isgbm {
|
|
r.IsBangumi = 1
|
|
}
|
|
var tmpl, limitshow bool
|
|
if limit, ok := s.limitCache[r.ID]; ok {
|
|
for i, l := range limit {
|
|
if i+1 <= len(limit)-1 {
|
|
if ((l.Condition == "gt" && limit[i+1].Condition == "lt") && (l.Build < limit[i+1].Build)) ||
|
|
((l.Condition == "lt" && limit[i+1].Condition == "gt") && (l.Build > limit[i+1].Build)) {
|
|
if (l.Condition == "gt" && limit[i+1].Condition == "lt") &&
|
|
(build > l.Build && build < limit[i+1].Build) {
|
|
break
|
|
} else if (l.Condition == "lt" && limit[i+1].Condition == "gt") &&
|
|
(build < l.Build && build > limit[i+1].Build) {
|
|
break
|
|
} else {
|
|
tmpl = true
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if tmpl {
|
|
if i == len(limit)-1 {
|
|
limitshow = true
|
|
// continue LOOP
|
|
break
|
|
}
|
|
tmpl = false
|
|
continue
|
|
}
|
|
if model.InvalidBuild(build, l.Build, l.Condition) {
|
|
limitshow = true
|
|
continue
|
|
// continue LOOP
|
|
} else {
|
|
limitshow = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if limitshow {
|
|
continue LOOP
|
|
}
|
|
if auth, ok := auths[r.Area]; ok && auth.Play == locmdl.Forbidden {
|
|
log.Warn("s.invalid area(%v) ip(%v) error(%v)", r.Area, ip, err)
|
|
continue
|
|
}
|
|
if isAudit := s.auditRegion(mobiApp, plat, build, r.Rid); isAudit {
|
|
continue
|
|
}
|
|
if config, ok := s.configCache[r.ID]; ok {
|
|
r.Config = config
|
|
}
|
|
key := fmt.Sprintf(_regionRepeat, r.Rid, r.Reid)
|
|
if _, ok := ridtmp[key]; !ok {
|
|
ridtmp[key] = struct{}{}
|
|
} else {
|
|
continue
|
|
}
|
|
if r.Reid != 0 {
|
|
cl, ok := child[r.Reid]
|
|
if !ok {
|
|
cl = []*region.Region{}
|
|
}
|
|
cl = append(cl, r)
|
|
child[r.Reid] = cl
|
|
} else {
|
|
res = append(res, r)
|
|
}
|
|
}
|
|
if len(res) == 0 {
|
|
res = _emptyRegions
|
|
} else {
|
|
for _, r := range res {
|
|
r.Children = child[r.Rid]
|
|
}
|
|
}
|
|
if version = s.hash(res); version == ver {
|
|
err = ecode.NotModified
|
|
res = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Service) hash(v []*region.Region) 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)
|
|
}
|
|
|
|
// loadRegion regions cache.
|
|
func (s *Service) loadRegion() {
|
|
res, err := s.dao.All(context.TODO())
|
|
if err != nil {
|
|
log.Error("s.dao.All error(%v)", err)
|
|
return
|
|
}
|
|
tmp := map[string][]*region.Region{}
|
|
for _, v := range res {
|
|
key := fmt.Sprintf(_initRegionKey, v.Plat, v.Language)
|
|
tmp[key] = append(tmp[key], v)
|
|
}
|
|
if len(tmp) > 0 {
|
|
s.cache = tmp
|
|
}
|
|
log.Info("region cacheproc success")
|
|
}
|
|
|
|
func (s *Service) loadRegionlist() {
|
|
res, err := s.dao.AllList(context.TODO())
|
|
if err != nil {
|
|
log.Error("s.dao.All error(%v)", err)
|
|
return
|
|
}
|
|
tmp := map[string][]*region.Region{}
|
|
tmpRegion := map[string]map[int]*region.Region{}
|
|
for _, v := range res {
|
|
key := fmt.Sprintf(_initRegionKey, v.Plat, v.Language)
|
|
tmp[key] = append(tmp[key], v)
|
|
// region list map
|
|
if r, ok := tmpRegion[key]; ok {
|
|
r[v.Rid] = v
|
|
} else {
|
|
tmpRegion[key] = map[int]*region.Region{
|
|
v.Rid: v,
|
|
}
|
|
}
|
|
}
|
|
if len(tmp) > 0 && len(tmpRegion) > 0 {
|
|
s.cachelist = tmp
|
|
s.regionListCache = tmpRegion
|
|
}
|
|
log.Info("region list cacheproc success")
|
|
limit, err := s.dao.Limit(context.TODO())
|
|
if err != nil {
|
|
log.Error("s.dao.limit error(%v)", err)
|
|
return
|
|
}
|
|
s.limitCache = limit
|
|
log.Info("region limit cacheproc success")
|
|
config, err := s.dao.Config(context.TODO())
|
|
if err != nil {
|
|
log.Error("s.dao.Config error(%v)", err)
|
|
return
|
|
}
|
|
s.configCache = config
|
|
log.Info("region config cacheproc success")
|
|
}
|