Initial commit
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelpackage"
|
||||
"stat/models/downloadCpc"
|
||||
downloadinfo "stat/models/downloadinfo"
|
||||
"stat/models/versionMod"
|
||||
"stat/service/cacheSer"
|
||||
"stat/service/crossSer"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type ChannelParams struct {
|
||||
PC string `json:"i" bson:"i"` //推广码
|
||||
DC string `json:"dc" bson:"dc"` //商区序列
|
||||
CH string `json:"ch" bson:"ch"` //渠道
|
||||
}
|
||||
|
||||
var (
|
||||
appSvrUrlDict = make(map[int32]string)
|
||||
appSvrIdxDict = make(map[int32]statg.ServerIndex)
|
||||
once sync.Once
|
||||
apkGenLock = utils.NewExpiredLock()
|
||||
landPageVisit = utils.NewExpiredLock()
|
||||
landPageClick = utils.NewExpiredLock()
|
||||
landPageVisitIpTimeout = time.Hour // 每个IP,1个小时内无论访问多少次,都算一次
|
||||
landPageClickIpTimeout = time.Hour // 每个IP,1个小时内无论访问多少次,都算一次
|
||||
)
|
||||
|
||||
func Init() {
|
||||
once.Do(func() {
|
||||
for _, endpoint := range statg.GlobalEndpoints {
|
||||
appSvrIdxDict[endpoint.AppId] = endpoint.OwnerIndex
|
||||
if url, ok := statg.Conf.InnerServers[endpoint.OwnerIndex]; ok {
|
||||
appSvrUrlDict[endpoint.AppId] = url
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// CrossButtonClick 跨服务器点击下载返回最新的下载链接
|
||||
func CrossButtonClick(ctx context.Context, userAgent string, peerIP string, req *proto.PageButtonClickReq) (resp *proto.PageButtonClickResp, err error) {
|
||||
req.Ip = peerIP
|
||||
srvIdx := appSvrIdxDict[req.AppId]
|
||||
if srvIdx == statg.ServerIndexNone {
|
||||
return nil, fmt.Errorf("appid %d not found", req.AppId)
|
||||
}
|
||||
if srvIdx == statg.Conf.ServerIndex {
|
||||
return PageButtonClick(ctx, peerIP, userAgent, req)
|
||||
}
|
||||
addr := appSvrUrlDict[req.AppId]
|
||||
if addr == "" {
|
||||
return nil, fmt.Errorf("appid %d server not found", req.AppId)
|
||||
}
|
||||
res := crossSer.CrossAppDownloadRes{}
|
||||
err = crossSer.InnerRequest(addr+"/api/stat/inner/appDownload", req, &res, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Data == nil {
|
||||
return nil, fmt.Errorf("data not found")
|
||||
}
|
||||
return res.Data, nil
|
||||
}
|
||||
|
||||
// PageButtonClick 落地页点击下载返回最新的下载链接
|
||||
func PageButtonClick(ctx context.Context, userAgent string, peerIP string, req *proto.PageButtonClickReq) (resp *proto.PageButtonClickResp, err error) {
|
||||
resp = &proto.PageButtonClickResp{}
|
||||
|
||||
var channelCode ChannelParams
|
||||
// TODO 存储记录
|
||||
_ = json.Unmarshal([]byte(req.Channel), &channelCode)
|
||||
|
||||
if req.Ip == "" {
|
||||
req.Ip = peerIP
|
||||
}
|
||||
channelCode.DC = utils.RepairDC(channelCode.DC)
|
||||
|
||||
if req.Terminal == "ios" && req.PackageType == versionMod.Light {
|
||||
var h5Req = &proto.H5ConfigReq{
|
||||
AppId: req.AppId,
|
||||
DC: channelCode.DC,
|
||||
PC: channelCode.PC,
|
||||
Domain: channelCode.PC,
|
||||
}
|
||||
resp.Terminal = req.Terminal
|
||||
resp.PackageType = req.PackageType
|
||||
resp.Status = true
|
||||
resp.LightData, _ = GetH5Config(ctx, h5Req, userAgent, req.Ip)
|
||||
return
|
||||
}
|
||||
|
||||
downloadInfo := &downloadinfo.DownloadInfoModel{
|
||||
AppId: req.AppId,
|
||||
IP: req.Ip,
|
||||
Domain: req.Domain,
|
||||
PC: channelCode.PC,
|
||||
DC: channelCode.DC,
|
||||
CH: channelCode.CH,
|
||||
SysType: strings.ToLower(strings.TrimSpace(req.Terminal)),
|
||||
PackageType: req.PackageType,
|
||||
PeerIP: peerIP,
|
||||
UserAgent: userAgent,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
//和APP统一
|
||||
if req.PackageType == versionMod.Shop {
|
||||
downloadInfo.SysType = "h5_ios"
|
||||
}
|
||||
|
||||
if landPageClick.TryLock(req.Ip, time.Now().Add(landPageClickIpTimeout)) {
|
||||
cacheSer.Writers.LandPageClickWriter.Add(downloadInfo)
|
||||
}
|
||||
if downloadInfo.IP == "" {
|
||||
downloadInfo.IP = peerIP
|
||||
}
|
||||
|
||||
var data *versionMod.VersionModel
|
||||
cacheKey := fmt.Sprintf("version_button#appId:%d#%s#%d", req.AppId, req.Terminal, req.PackageType)
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
if item, ok := cache.(*versionMod.VersionModel); ok {
|
||||
data = item
|
||||
}
|
||||
}
|
||||
if data == nil {
|
||||
data, err = versionMod.ButtonClick(ctx, req.AppId, req.Terminal, req.PackageType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, data, 2*time.Minute)
|
||||
}
|
||||
|
||||
resp.PackageType = data.PackageType
|
||||
resp.Status = data.Status
|
||||
resp.Terminal = data.Terminal
|
||||
resp.VersionNum = data.VersionNum
|
||||
resp.BackUrl = data.BackLink
|
||||
resp.Link = append(resp.Link, data.Link...)
|
||||
|
||||
// IOS的TF包管理
|
||||
if req.PackageType == versionMod.Tf {
|
||||
// 1. FT包管理
|
||||
|
||||
for _, tfInfo := range data.TfInfo {
|
||||
one := proto.TfInfo{
|
||||
AppName: tfInfo.AppName,
|
||||
AppIcon: tfInfo.AppIcon,
|
||||
URL: tfInfo.URL,
|
||||
}
|
||||
resp.Tf = append(resp.Tf, one)
|
||||
}
|
||||
|
||||
if len(resp.Tf) == 0 {
|
||||
resp.Tf = make([]proto.TfInfo, 0)
|
||||
for _, v := range data.Link {
|
||||
tmp := proto.TfInfo{
|
||||
URL: v,
|
||||
}
|
||||
if len(data.BackLink) > 0 {
|
||||
tmp.BackUrl = data.BackLink
|
||||
}
|
||||
resp.Tf = append(resp.Tf, tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if data.Terminal == "ios" {
|
||||
// 配置多个随机返回一个
|
||||
if len(data.Link) >= 1 {
|
||||
resp.Link = make([]string, 0)
|
||||
index := rand.Intn(len(data.Link))
|
||||
resp.Link = append(resp.Link, data.Link[index])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增批量打包逻辑
|
||||
if req.Terminal == "android" && len(data.Md5) > 0 {
|
||||
randTime := int64(60*60) + int64(rand.Intn(60*60))
|
||||
|
||||
requestPackageTime := time.Now().Add(-time.Second * time.Duration(randTime))
|
||||
// 下载链接
|
||||
projectString := ""
|
||||
beginPath := ""
|
||||
pathList := strings.Split(strings.Replace(data.Md5, ".apk", "", -1), "_")
|
||||
if len(pathList) == 3 {
|
||||
beginPath = strings.TrimSpace(pathList[len(pathList)-1])
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
endpoint := statg.GlobalAppIdEndpoints[req.AppId]
|
||||
if endpoint != nil {
|
||||
projectString = fmt.Sprintf("%s/out/%s/%s%s_", endpoint.NameEn, beginPath, endpoint.NameEn, requestPackageTime.Format("20060102")[2:])
|
||||
}
|
||||
if projectString != "" {
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if req.AppId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if req.AppId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if req.AppId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
|
||||
if len(packageUrlArray) > 0 {
|
||||
if len(channelCode.DC) > 0 { //&&
|
||||
pack, serr := channelpackage.FindOneChannelPackage(ctx, bson.M{
|
||||
channelpackage.FN.ChannelPackage.AppId: req.AppId,
|
||||
channelpackage.FN.ChannelPackage.Channel: channelCode.DC,
|
||||
channelpackage.FN.ChannelPackage.Version: data.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.FileName: data.Md5,
|
||||
})
|
||||
if serr == nil || serr.Code == stdLog.DataNotExist {
|
||||
if pack != nil && pack.Status == channelpackage.ChanApkStatusSuccess {
|
||||
pass, links := ApkCheckLinks(pack, packageUrlArray)
|
||||
if pass {
|
||||
resp.Link = links
|
||||
_ = pack.IncDownCount(ctx)
|
||||
return resp, nil
|
||||
} else {
|
||||
ApkReportErrorLinks(ctx, pack, links)
|
||||
}
|
||||
} else {
|
||||
CheckChannelApkAutoGen(ctx, req.AppId, channelCode.DC, data, pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 默认使用第一个
|
||||
passedLinks := make([]string, 0)
|
||||
for _, packageLink := range packageUrlArray {
|
||||
link := strings.TrimSpace(packageLink + projectString + data.Md5)
|
||||
checklink := link
|
||||
cerr := CheckPackageLink(ctx, checklink)
|
||||
if cerr != nil {
|
||||
// log.Error("AppVersion", log.Any("link", link), log.E(cerr))
|
||||
// if statg.Conf.RunMod == consts.RunModRelease && !slices.Contains([]int32{666, 998}, req.AppId) {
|
||||
// appDict, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
// text := fmt.Sprintf("【%s】Android 下载链接异常:\n链接:%s\n错误:%s\n", appDict[req.AppId], link, cerr.Error())
|
||||
// SendLinkErrorMessageToBot(text)
|
||||
// }
|
||||
} else {
|
||||
passedLinks = append(passedLinks, link)
|
||||
}
|
||||
}
|
||||
if len(passedLinks) > 0 {
|
||||
resp.Link = passedLinks
|
||||
}
|
||||
} else {
|
||||
link := strings.TrimSpace(packageUrlString + projectString + data.Md5)
|
||||
resp.Link = []string{link}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PageButtonList 落地页获取下载按钮
|
||||
func PageButtonList(ctx context.Context, isIos bool, peerIP string, req *proto.PageButtonListReq) (resp *proto.PageButtonListResp, err error) {
|
||||
resp = &proto.PageButtonListResp{
|
||||
IosTF: false,
|
||||
}
|
||||
var channelCode ChannelParams
|
||||
// TODO 存储记录
|
||||
_ = json.Unmarshal([]byte(req.Channel), &channelCode)
|
||||
|
||||
channelCode.DC = utils.RepairDC(channelCode.DC)
|
||||
|
||||
req.Ip = peerIP
|
||||
|
||||
if req.Ip != "" {
|
||||
logInfo := &downloadCpc.DownloadCpc{
|
||||
AppId: req.AppId,
|
||||
IP: req.Ip,
|
||||
PC: channelCode.PC,
|
||||
DC: channelCode.DC,
|
||||
CH: channelCode.CH,
|
||||
PeerIP: peerIP,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if landPageVisit.TryLock(req.Ip, time.Now().Add(landPageVisitIpTimeout)) {
|
||||
cacheSer.Writers.LandPageVisitWriter.Add(logInfo)
|
||||
}
|
||||
}
|
||||
cacheKey := fmt.Sprintf("version_button_list#appId:%d", req.AppId)
|
||||
var buttons versionMod.VersionList
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
if list, ok := cache.(versionMod.VersionList); ok {
|
||||
buttons = list
|
||||
}
|
||||
}
|
||||
if buttons == nil {
|
||||
buttons, err = versionMod.ButtonList(ctx, req.AppId)
|
||||
if err != nil {
|
||||
err = errors.New("服务器内部错误")
|
||||
return
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, buttons, 0)
|
||||
}
|
||||
|
||||
if len(buttons) < 1 {
|
||||
err = errors.New("请开启版本")
|
||||
return
|
||||
}
|
||||
|
||||
for _, value := range buttons {
|
||||
if isIos && value.Terminal == "android" {
|
||||
// 如果是ios设备请求,记录是android 跳过
|
||||
continue
|
||||
}
|
||||
if value.PackageType == versionMod.Enterprise && value.Terminal == "ios" {
|
||||
resp.IosDown = true
|
||||
}
|
||||
if value.PackageType == versionMod.Shop && value.Terminal == "ios" {
|
||||
resp.IosStore = true
|
||||
if len(value.Link) > 0 {
|
||||
resp.IosLink = value.Link[0]
|
||||
}
|
||||
}
|
||||
if value.PackageType == versionMod.Tf && value.Terminal == "ios" {
|
||||
resp.IosTF = true
|
||||
}
|
||||
if value.PackageType == versionMod.Light && value.Terminal == "ios" {
|
||||
resp.IosH5 = true
|
||||
}
|
||||
}
|
||||
// android下载按钮
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if req.AppId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if req.AppId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if req.AppId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
resp.AndroidButton = len(packageUrlArray)
|
||||
// 是否有渠道包
|
||||
var version *versionMod.VersionModel
|
||||
versionKey := fmt.Sprintf("version_button#appId:%d#%s#%d", req.AppId, "android", 0)
|
||||
if cache, exists := cacheSer.Cache.Get(versionKey); exists {
|
||||
if item, ok := cache.(*versionMod.VersionModel); ok {
|
||||
version = item
|
||||
}
|
||||
}
|
||||
if version == nil {
|
||||
version, err = versionMod.ButtonClick(ctx, req.AppId, "android", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, version, 2*time.Minute)
|
||||
}
|
||||
|
||||
pack, serr := channelpackage.FindOneChannelPackage(ctx, bson.M{
|
||||
channelpackage.FN.ChannelPackage.AppId: req.AppId,
|
||||
channelpackage.FN.ChannelPackage.Channel: channelCode.DC,
|
||||
channelpackage.FN.ChannelPackage.Version: version.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.FileName: version.Md5,
|
||||
})
|
||||
if serr != nil && serr.Code != stdLog.DataNotExist {
|
||||
return nil, errors.New(serr.Msg)
|
||||
}
|
||||
if pack != nil && pack.Status == channelpackage.ChanApkStatusSuccess {
|
||||
resp.HasChannelApk = true
|
||||
}
|
||||
|
||||
// ----------------End---------------
|
||||
|
||||
resp.Contact = proto.ContactInfo{
|
||||
Telegram: "https://t.me/madoutiktok",
|
||||
Paoto: "https://ptcc.in/madouyingshi",
|
||||
Madou: "http://madou101.com",
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user