You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
8.9 KiB
362 lines
8.9 KiB
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") |
|
}
|
|
|