Compare commits
3
Commits
main
...
sync-tangguo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b2f4d721e | ||
|
|
4f2a3379da | ||
|
|
97f679d130 |
@@ -37,6 +37,7 @@ func AdsClick(ctx *gin.Context) {
|
||||
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
return
|
||||
}
|
||||
adser.AsyncAdvertiseClick(arg.ID)
|
||||
common.ServeJSON(ctx, stderr.Success, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"91jd-server/common/constant"
|
||||
"91jd-server/common/services/message"
|
||||
"91jd-server/common/store"
|
||||
v10 "91jd-server/common/v10"
|
||||
"91jd-server/models/cache/bannerjumpdata"
|
||||
"91jd-server/models/cache/sysconfdata"
|
||||
"91jd-server/models/v/bannerjumpmod"
|
||||
@@ -33,7 +34,6 @@ import (
|
||||
"91jd-server/common"
|
||||
"91jd-server/common/log"
|
||||
"91jd-server/common/stderr"
|
||||
"91jd-server/common/version"
|
||||
"91jd-server/models/commod"
|
||||
"91jd-server/models/v/sourcemod"
|
||||
"91jd-server/models/v/systemmod"
|
||||
@@ -249,40 +249,30 @@ func DomainList(ctx *gin.Context) {
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
builId := v10.ExtractPureEngChar(ua.BuildID)
|
||||
//版本、广告、公告
|
||||
verResp, _, annouResp, _, _, _, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, ua.SysType)
|
||||
versionResp, err := VersionThreeServer(ua.Ver, ua.SysType, builId, ua.BuildID)
|
||||
if err != nil {
|
||||
log.Error("VersionThreeServer", log.E(err))
|
||||
return
|
||||
}
|
||||
for k, v := range annouResp {
|
||||
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
|
||||
annouResp[k] = v
|
||||
}
|
||||
//版本业务
|
||||
//安卓不做限制;iOS 端版本 <= 1.11.2 不下发版本信息
|
||||
skipVer := false
|
||||
if ua.SysType == constant.SysTypeIOS {
|
||||
if cur, err := version.New(ua.Ver); err == nil && cur.LTE(version.MustNew("1.11.2")) {
|
||||
skipVer = true
|
||||
}
|
||||
}
|
||||
if len(verResp.DownloadLink) > 0 && !skipVer {
|
||||
|
||||
if versionResp.HasNewVersion {
|
||||
versionBody := []*versionmod.VersionBody{
|
||||
&versionmod.VersionBody{
|
||||
VersionName: verResp.ServerVersion,
|
||||
VersionName: versionResp.ServerVersion,
|
||||
Platform: ua.SysType,
|
||||
Description: verResp.Description,
|
||||
ForcedUpdate: verResp.IsForceUpdate,
|
||||
URL: verResp.DownloadLink[0],
|
||||
IosUrl: verResp.DownloadLink[0],
|
||||
Description: versionResp.Description,
|
||||
ForcedUpdate: versionResp.IsForceUpdate,
|
||||
URL: versionResp.DownloadLink[0],
|
||||
IosUrl: versionResp.DownloadLink[0],
|
||||
}}
|
||||
sysInfo.Ver = versionBody
|
||||
}
|
||||
//公告
|
||||
sysInfo.Ads.AnnounList = annouResp
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
|
||||
jtAds, err := adser.JtAdvertiseThreeServer()
|
||||
if err != nil {
|
||||
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package pingctrl
|
||||
|
||||
import (
|
||||
"91jd-server/app/appg"
|
||||
"91jd-server/common/httputil"
|
||||
"91jd-server/common/log"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PacketType int32
|
||||
|
||||
const (
|
||||
PacketTypeEnterprise PacketType = 1 // 企业签
|
||||
PacketTypeShop PacketType = 2 // 商店包
|
||||
PacketTypeTestFlight PacketType = 3 // TF包
|
||||
)
|
||||
|
||||
const statAppId int32 = 5
|
||||
|
||||
type AppVersionReq struct {
|
||||
AppId int32 `json:"appId" binding:"required"` // AppID
|
||||
SysType string `json:"sysType,omitempty"` // 终端类型
|
||||
PktType PacketType `json:"pktType,omitempty"` // 包类型
|
||||
CurVersion string `json:"curVersion,omitempty"` // 当前版本
|
||||
CurPackageName string `json:"curPackageName,omitempty"` // 包名
|
||||
}
|
||||
|
||||
type AppVersionResp struct {
|
||||
HasNewVersion bool `json:"hasNewVersion,omitempty"` // 是否存在新版本
|
||||
ServerVersion string `json:"serverVersion,omitempty"` // 服务器新版本
|
||||
DownloadLink []string `json:"downloadLink,omitempty"` // 下载地址
|
||||
Description string `json:"description,omitempty"` // 描述
|
||||
IsForceUpdate bool `json:"isForceUpdate,omitempty"` // 是否强制升级
|
||||
Size string `json:"size,omitempty"` // 大小
|
||||
Md5 string `json:"md5,omitempty"` // md5
|
||||
}
|
||||
|
||||
func VersionThreeServer(curVersion, sysType, buildID, packageName string) (resp AppVersionResp, err error) {
|
||||
req := AppVersionReq{
|
||||
AppId: statAppId,
|
||||
PktType: PacketTypeEnterprise,
|
||||
SysType: sysType,
|
||||
CurVersion: curVersion,
|
||||
CurPackageName: packageName,
|
||||
}
|
||||
|
||||
// TF包
|
||||
if len(buildID) > 0 {
|
||||
req.PktType = PacketTypeTestFlight
|
||||
}
|
||||
|
||||
url := appg.Conf.URL.VersionUrl + "/api/stat/version/app"
|
||||
bodyStr, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
log.Error("VersionThreeServer json.Marshal fail", log.Any("req", req), log.E(err))
|
||||
}
|
||||
|
||||
code, err := httputil.New().POSTWithJResp(&resp, url, nil, bodyStr)
|
||||
if err != nil {
|
||||
log.Error("VersionThreeServer POSTWithJResp ", log.Any("url", url), log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
if code != http.StatusOK {
|
||||
log.Error("VersionThreeServer response status ", log.Any("code", code))
|
||||
err = fmt.Errorf("response status err")
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("VersionThreeServer response", log.Any("resp", resp.DownloadLink), log.Any("version", resp.HasNewVersion))
|
||||
return
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"91jd-server/app/service/rechargeser"
|
||||
"91jd-server/common/log"
|
||||
@@ -44,6 +45,12 @@ func CallBack(ctx *gin.Context) {
|
||||
return "", err
|
||||
}
|
||||
rb.TransNo = rchgutil.RChgIDDisassemble(rb.TransNo)
|
||||
if rb.SuccessAt.IsZero() {
|
||||
rb.SuccessAt = time.Now()
|
||||
}
|
||||
if rb.PaymentAt.IsZero() {
|
||||
rb.PaymentAt = rb.SuccessAt
|
||||
}
|
||||
if err = rechargeser.RechargeCallBack(ctx, rb.OID, rb.PayMoney, rb.TransNo, rb.Code, rb.PaymentAt, rb.SuccessAt); err != nil {
|
||||
log.Error(fmt.Sprintf("%s RechargeCallBack fail error:%+v:", name, err))
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@ func Click(ctx *gin.Context) {
|
||||
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
|
||||
return
|
||||
}
|
||||
adser.AsyncAdvertiseClick(p.Id)
|
||||
common.ServeJSON(ctx, stderr.Success, nil)
|
||||
}
|
||||
|
||||
@@ -711,16 +711,16 @@ func LoginWithH5(ctx *gin.Context) {
|
||||
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
return
|
||||
}
|
||||
// 仅允许 h5/ios 端登录注册,其余 sysType 一律拒绝
|
||||
if p.SysType != "h5" && p.SysType != "ios" {
|
||||
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
return
|
||||
}
|
||||
// devID 以 web 开头的为攻击流量,直接拒绝(忽略大小写)
|
||||
if strings.HasPrefix(strings.ToLower(p.DevID), "web") {
|
||||
common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
return
|
||||
}
|
||||
// // 仅允许 h5/ios 端登录注册,其余 sysType 一律拒绝
|
||||
// if p.SysType != "h5" && p.SysType != "ios" {
|
||||
// common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
// return
|
||||
// }
|
||||
// // devID 以 web 开头的为攻击流量,直接拒绝(忽略大小写)
|
||||
// if strings.HasPrefix(strings.ToLower(p.DevID), "web") {
|
||||
// common.ServeJSON(ctx, stderr.ErrParamError, nil)
|
||||
// return
|
||||
// }
|
||||
ip := common.GetIP(ctx)
|
||||
ua, err := common.GetUA(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -177,6 +177,7 @@ type GlobalConfig struct {
|
||||
LouFengH5 string `json:"louFengH5"` // 楼凤H5(金主特制)
|
||||
DataBuriedPoint string `json:"dataBuriedPoint"` // 数据埋点
|
||||
ProductUrl string `json:"productUrl"` // 产品中心
|
||||
VersionUrl string `json:"versionUrl"` // 版本/广告中心地址
|
||||
//Active2023 string `json:"active2023"` // 2023新春抽奖地址
|
||||
NavigateUrl string `json:"navigateUrl"` // 导航站地址
|
||||
HlH5Url string `json:"hlH5Url"` // 黑料h5url
|
||||
|
||||
@@ -50,6 +50,8 @@ type AdvertiseInfo struct {
|
||||
JumpType int32 `json:"jumpType"` // 跳转方式 0:外部浏览器跳转 1:内部浏览器跳转 2:app内部跳转
|
||||
Link string `json:"link"` // 链接地址
|
||||
Sort int64 `json:"sort"` // 排序
|
||||
Tags string `json:"tags"` // 标签
|
||||
Remark string `json:"remark"` // 备注描述
|
||||
}
|
||||
|
||||
func AdvertiseThreeServer(userId uint64, ip string) (*AdvertiseRes, error) {
|
||||
|
||||
@@ -9,16 +9,17 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"sync"
|
||||
|
||||
"91jd-server/app/appg"
|
||||
"91jd-server/app/service/activityclient"
|
||||
"91jd-server/common"
|
||||
"91jd-server/common/crypt"
|
||||
"91jd-server/common/httputil"
|
||||
"91jd-server/common/log"
|
||||
"91jd-server/models/v/usermod"
|
||||
"91jd-server/models/v/walletmod"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -120,115 +121,148 @@ func (ad AdDetailInfo) GetAdvertiseType() int {
|
||||
return ad.AdvertiseType
|
||||
}
|
||||
|
||||
// JtAdvertiseThreeServer 集团广告中心
|
||||
func JtAdvertiseThreeServer() ([]AdSlot, error) {
|
||||
var foreverCacheKey = fmt.Sprintf("jtAdForever-%s-%s", appg.Conf.AdCenter.MerchantCode, appg.Conf.AdCenter.AppCode)
|
||||
redisKey := fmt.Sprintf("jtAd-%s-%s", appg.Conf.AdCenter.MerchantCode, appg.Conf.AdCenter.AppCode)
|
||||
str, err := appg.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 缓存获取广告列表信息异常:%v", err))
|
||||
}
|
||||
var resp []AdSlot
|
||||
if str != nil {
|
||||
if err = json.Unmarshal([]byte(*str), &resp); err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解析缓存数据异常:%v", err))
|
||||
}
|
||||
req := JtAdvertiseReq{
|
||||
MerchantCode: appg.Conf.AdCenter.MerchantCode,
|
||||
AppCode: appg.Conf.AdCenter.AppCode,
|
||||
AdStatus: 1,
|
||||
}
|
||||
// advertiseAppId 广告中心应用ID
|
||||
const advertiseAppId int32 = 5
|
||||
|
||||
var serverResp *JtAdvertiseRes
|
||||
url := appg.Conf.AdCenter.ApiDomain + "/openapi/getAdvertiseList"
|
||||
type advertiseClickReq struct {
|
||||
Id int64 `json:"id"` // 广告id
|
||||
AppId int32 `json:"appId"` // 应用id
|
||||
}
|
||||
|
||||
type advertiseClickResp struct {
|
||||
Code int64 `json:"code"` // 错误码
|
||||
}
|
||||
|
||||
// AdvertiseClickCenter 上报广告点击到广告中心
|
||||
func AdvertiseClickCenter(advertiseId int64) error {
|
||||
req := advertiseClickReq{
|
||||
Id: advertiseId,
|
||||
AppId: advertiseAppId,
|
||||
}
|
||||
resp := advertiseClickResp{}
|
||||
url := appg.Conf.URL.VersionUrl + "/api/stat/advitise/v2/click"
|
||||
bodyStr, _ := json.Marshal(req)
|
||||
code, err := httputil.DefaultClientPostJsonWithResp(&serverResp, url, nil, bodyStr)
|
||||
code, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, bodyStr)
|
||||
if err != nil {
|
||||
log.Error("JtAdvertiseThreeServer POSTWithJResp ", log.Any("url", url), log.E(err))
|
||||
// 从最后请求成功的一次拿数据
|
||||
return getFromLastTime(foreverCacheKey, err)
|
||||
log.Error("AdvertiseClickCenter POSTWithJResp", log.Any("url", url), log.Any("req", req), log.E(err))
|
||||
return err
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
log.Error("JtAdvertiseThreeServer response status ", log.Any("code", code))
|
||||
// 从最后请求成功的一次拿数据
|
||||
return getFromLastTime(foreverCacheKey, errors.New("response status err"))
|
||||
log.Error("AdvertiseClickCenter response status", log.Any("code", code), log.Any("req", req))
|
||||
return fmt.Errorf("response status err")
|
||||
}
|
||||
|
||||
if serverResp.Code != 0 {
|
||||
log.Error("JtAdvertiseThreeServer response code ", log.Any("code", serverResp.Code), log.Any("msg", serverResp.Msg))
|
||||
// 从最后请求成功的一次拿数据
|
||||
return getFromLastTime(foreverCacheKey, errors.New("response code err"))
|
||||
}
|
||||
|
||||
adsData, err := crypt.AdDecrypt(serverResp.Data, appg.Conf.AdCenter.AesKey)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解密广告数据异常:%v", err))
|
||||
return nil, errors.New("response data err")
|
||||
}
|
||||
err = json.Unmarshal([]byte(adsData), &resp)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解析 JSON 失败:%v", err))
|
||||
return nil, errors.New("data format error")
|
||||
}
|
||||
|
||||
// 对广告进行升序排序
|
||||
for i := range resp {
|
||||
sort.Slice(resp[i].AdDetailInfoList, func(j, k int) bool {
|
||||
return resp[i].AdDetailInfoList[j].Sort < resp[i].AdDetailInfoList[k].Sort
|
||||
})
|
||||
}
|
||||
|
||||
// 将最后一次请求成功的数据更新到二级缓存
|
||||
common.Go(func() {
|
||||
setAdsCache(foreverCacheKey, resp)
|
||||
})
|
||||
|
||||
common.Go(func() {
|
||||
data, err := json.Marshal(&resp)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer json序列化缓存数据异常:%v", err))
|
||||
return
|
||||
}
|
||||
if err = appg.Redis.Set(redisKey, data, time.Minute); err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 保存缓存数据异常:%v", err))
|
||||
}
|
||||
})
|
||||
|
||||
return resp, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// getFromLastTime 返回最后一次请求的广告数据
|
||||
func getFromLastTime(foreverCacheKey string, e error) ([]AdSlot, error) {
|
||||
str, err := appg.Redis.Get(foreverCacheKey)
|
||||
// AsyncAdvertiseClick 异步上报广告点击,广告id非数字时忽略
|
||||
func AsyncAdvertiseClick(id string) {
|
||||
advertiseId, err := strconv.ParseInt(strings.TrimSpace(id), 10, 64)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("getFromLastTime 缓存获取广告列表信息异常:%v", err))
|
||||
return nil, e
|
||||
log.Warn("AsyncAdvertiseClick invalid advertise id", log.Any("id", id))
|
||||
return
|
||||
}
|
||||
|
||||
var resp []AdSlot
|
||||
if str != nil {
|
||||
if err = json.Unmarshal([]byte(*str), &resp); err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Error(fmt.Sprintf("getFromLastTime 解析缓存数据异常:%v", err))
|
||||
}
|
||||
|
||||
return nil, e
|
||||
common.Go(func() {
|
||||
_ = AdvertiseClickCenter(advertiseId)
|
||||
})
|
||||
}
|
||||
|
||||
// setAdsCache 更新最后一次请求的广告数据
|
||||
func setAdsCache(foreverCacheKey string, resp []AdSlot) {
|
||||
data, err := json.Marshal(&resp)
|
||||
var (
|
||||
advertiseCacheOnce sync.Once
|
||||
advertiseCacheMu sync.RWMutex
|
||||
advertiseCache []AdSlot
|
||||
advertiseCacheOK bool
|
||||
)
|
||||
|
||||
// JtAdvertiseThreeServer 获取广告位列表(内存缓存,每两分钟从广告中心刷新一次)
|
||||
func JtAdvertiseThreeServer() ([]AdSlot, error) {
|
||||
advertiseCacheOnce.Do(startAdvertiseCache)
|
||||
|
||||
advertiseCacheMu.RLock()
|
||||
defer advertiseCacheMu.RUnlock()
|
||||
if !advertiseCacheOK {
|
||||
return nil, errors.New("advertise cache not ready")
|
||||
}
|
||||
res := make([]AdSlot, len(advertiseCache))
|
||||
copy(res, advertiseCache)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// startAdvertiseCache 首次加载广告并启动定时刷新
|
||||
func startAdvertiseCache() {
|
||||
updataAdvertiseInfo()
|
||||
c := cron.New(cron.WithSeconds())
|
||||
_, _ = c.AddFunc("0 */2 * * * ?", updataAdvertiseInfo) // 两分钟更新一次
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// updataAdvertiseInfo 从广告中心拉取广告并更新缓存,列表为空时重试一次
|
||||
func updataAdvertiseInfo() {
|
||||
advertiseResp, err := advertiseCenterServer()
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("setAdsCache json序列化缓存数据异常:%v", err))
|
||||
log.Error("advertiseCenterServer", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := appg.Redis.Set(foreverCacheKey, data, time.Hour*72); err != nil {
|
||||
log.Error(fmt.Sprintf("setAdsCache 保存缓存数据异常:%v", err))
|
||||
log.Error("advertiseCenterServer", log.Any("advertiseResp", advertiseResp))
|
||||
|
||||
if len(advertiseResp.AdvertiseList) == 0 {
|
||||
advertiseResp, err = advertiseCenterServer()
|
||||
if err != nil {
|
||||
log.Error("advertiseCenterServer", log.E(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
list := advertiseResp.AdvertiseList
|
||||
sort.SliceStable(list, func(i, j int) bool {
|
||||
if list[i].LocId != list[j].LocId {
|
||||
return list[i].LocId < list[j].LocId
|
||||
}
|
||||
return list[i].Sort < list[j].Sort
|
||||
})
|
||||
// 每条广告单独作为一个广告位返回,广告位名称取广告标题
|
||||
slots := make([]AdSlot, 0, len(list))
|
||||
for _, value := range list {
|
||||
slots = append(slots, AdSlot{
|
||||
AdvertiseLocationCode: strconv.FormatInt(int64(value.LocId), 10),
|
||||
AdvertiseLocationName: value.Title,
|
||||
AdDetailInfoList: []AdDetailInfo{{
|
||||
AdvertiseCode: strconv.FormatInt(value.Id, 10),
|
||||
AdvertiseName: value.Title,
|
||||
AdvertiseUrl: value.Link,
|
||||
AdvertiseIcon: value.CoverImg,
|
||||
AdvertiseDesc: value.Remark,
|
||||
Sort: int(value.Sort),
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
advertiseCacheMu.Lock()
|
||||
advertiseCache = slots
|
||||
advertiseCacheOK = true
|
||||
advertiseCacheMu.Unlock()
|
||||
}
|
||||
|
||||
// advertiseCenterServer 请求广告中心广告列表
|
||||
func advertiseCenterServer() (resp AdvertiseRes, err error) {
|
||||
req := AdvertiseReq{
|
||||
AppId: advertiseAppId,
|
||||
}
|
||||
url := appg.Conf.URL.VersionUrl + "/api/stat/advitise/v2/get"
|
||||
bodyStr, _ := json.Marshal(req)
|
||||
code, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, bodyStr)
|
||||
if err != nil {
|
||||
log.Error("advertiseCenterServer POSTWithJResp ", log.Any("url", url), log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("*************\n\n\n", log.Any("", resp))
|
||||
if code != http.StatusOK {
|
||||
log.Error("advertiseCenterServer response status ", log.Any("code", code))
|
||||
err = fmt.Errorf("response status err")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetAdvertiseLocation(adverId string) (res proto.AdsInfo) {
|
||||
|
||||
@@ -165,8 +165,8 @@ func QuickSearch(objID string, data []cmtmod.ParentRespList) []cmtmod.ParentResp
|
||||
ID: q.ID.Hex(),
|
||||
ObjID: objID,
|
||||
UserID: 100001,
|
||||
UserName: "91短剧运营",
|
||||
UserPortrait: "cf230705/image/cg/227/fg/1bw/a86065d7ab4cbb11ebbebaefc0ee4444.png",
|
||||
UserName: "黄果漫剧运营",
|
||||
UserPortrait: "cf230705/image/1ue/2yr/n9/205/762fd65fb31768a76f1674c7ea22b351.webp",
|
||||
Content: q.Title,
|
||||
LinkStr: q.Link,
|
||||
LinkType: q.LinkType,
|
||||
|
||||
@@ -85,7 +85,7 @@ func New_CurrencyList(ctx context.Context, uid uint64, sysType string, t commod.
|
||||
moneys = append(moneys, FenToYuan(c.Price))
|
||||
}
|
||||
|
||||
req := rchgutil.GainPayTypeReq{Money: moneys}
|
||||
req := rchgutil.GainPayTypeReq{Money: moneys, DevType: sysType, DistrictCode: userDistrictCode(uid)}
|
||||
bc, err := req.GetPayType()
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "获取支付通道列表异常", log.Any("moneys", moneys), log.Any("sysType", sysType),
|
||||
@@ -119,6 +119,18 @@ func New_CurrencyList(ctx context.Context, uid uint64, sysType string, t commod.
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
// userDistrictCode 获取用户渠道码,查询失败按空渠道码处理
|
||||
func userDistrictCode(uid uint64) string {
|
||||
if uid == 0 {
|
||||
return ""
|
||||
}
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil || u == nil {
|
||||
return ""
|
||||
}
|
||||
return u.DistrictCode
|
||||
}
|
||||
|
||||
// getChannelDisplayName 获取支付渠道显示名称
|
||||
func getChannelDisplayName(pType string) string {
|
||||
channelNames := map[string]string{
|
||||
@@ -290,7 +302,7 @@ func New_ProductList(uid uint64, sysType string, newUser bool, proT int) (res []
|
||||
moneys := GetUniqueMoneys(data)
|
||||
|
||||
// 缓存支付通道信息
|
||||
req := rchgutil.GainPayTypeReq{Money: moneys}
|
||||
req := rchgutil.GainPayTypeReq{Money: moneys, DevType: sysType, DistrictCode: userDistrictCode(uid)}
|
||||
bc, err := req.GetPayTypeFromCache()
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("用户ID:%d;rchgutil GetPayType:%v", uid, err))
|
||||
|
||||
@@ -99,17 +99,19 @@ func Recharge(ctx context.Context, in *RechargeRequest, ua ua.UA, deduct *Activi
|
||||
|
||||
// 下单
|
||||
now := time.Now()
|
||||
res, err := (&rchgutil.Recharge{
|
||||
TradeNo: order.ID.Hex(),
|
||||
Money: rchgutil.FenToYuan(order.Money),
|
||||
Type: order.RechargeType,
|
||||
Info: rchgutil.PayInfo{
|
||||
PlayerId: strconv.FormatUint(order.UID, 10),
|
||||
PlayerIp: order.UserIP,
|
||||
DeviceId: order.DevID,
|
||||
Tel: order.Tel,
|
||||
DeviceType: order.DevType,
|
||||
},
|
||||
res, err := (&rchgutil.Rchg{
|
||||
TransNo: order.ID.Hex(),
|
||||
UID: strconv.FormatUint(order.UID, 10),
|
||||
DevID: order.DevID,
|
||||
UserIP: order.UserIP,
|
||||
Name: u.Name,
|
||||
Tel: order.Tel,
|
||||
DevType: order.DevType,
|
||||
Money: order.Money,
|
||||
Channel: "self",
|
||||
CreatedAt: now,
|
||||
PayMethod: order.RechargeType,
|
||||
ProductType: 0,
|
||||
}).ToPayNew(ctx)
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 下单请求异常[%v]",
|
||||
@@ -129,8 +131,8 @@ func Recharge(ctx context.Context, in *RechargeRequest, ua ua.UA, deduct *Activi
|
||||
ProgressAt: &now,
|
||||
OID: &res.OID,
|
||||
Mode: &res.Mode,
|
||||
//Channel: &res.CID,
|
||||
Rate: &res.Rebate,
|
||||
Channel: &res.CID,
|
||||
Rate: &res.Rate,
|
||||
}
|
||||
if err = rchgordmod.Update(nil, order.ID, set); err != nil {
|
||||
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 更新订单异常[%v]",
|
||||
@@ -541,6 +543,11 @@ func RechargeCallBack(ctx context.Context, oid string, payMoney int64, tradeNo s
|
||||
if r.ProductType == 1 {
|
||||
return errors.New("瓦力游戏已下架")
|
||||
}
|
||||
// 回调订单号需与下单时支付平台返回的订单号一致
|
||||
if r.OID != "" && oid != r.OID {
|
||||
log.ErrorX(ctx, "充值回调支付平台订单号不一致", log.Any("oid", oid), log.Any("orderOid", r.OID), log.Any("tradeNo", tradeNo))
|
||||
return errors.New("oid 错误")
|
||||
}
|
||||
// 新版充值---根据购买类型处理
|
||||
var (
|
||||
fn func(*db.MongoTool) error
|
||||
|
||||
+22
-9
@@ -87,9 +87,9 @@ func ServeJsonWithExtra(c *gin.Context, code stderr.Code, data interface{}, extr
|
||||
log.Any("Code", code))
|
||||
}
|
||||
if !IsNilOrEmpty(data) && code == stderr.Success {
|
||||
t := reflect.TypeOf(data)
|
||||
if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) {
|
||||
if kind, ok := isRespDataTypeValid(data); !ok {
|
||||
log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice",
|
||||
log.Any("kind", kind),
|
||||
log.Any("path", c.Request.URL.Path))
|
||||
}
|
||||
}
|
||||
@@ -161,10 +161,9 @@ func ServeToJSON(c *gin.Context, code stderr.Code, data interface{}) {
|
||||
log.Any("Code", code))
|
||||
}
|
||||
if !IsNilOrEmpty(data) && code == stderr.Success {
|
||||
t := reflect.TypeOf(data)
|
||||
if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr) {
|
||||
if kind, ok := isRespDataTypeValid(data); !ok {
|
||||
log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice",
|
||||
log.Any("kind", t.Kind()),
|
||||
log.Any("kind", kind),
|
||||
log.Any("path", c.Request.URL.Path))
|
||||
}
|
||||
}
|
||||
@@ -227,9 +226,9 @@ func serveJsonLogic(c *gin.Context, code stderr.Code, data interface{}, extra ma
|
||||
log.Any("Code", code))
|
||||
}
|
||||
if !IsNilOrEmpty(data) && code == stderr.Success {
|
||||
t := reflect.TypeOf(data)
|
||||
if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) {
|
||||
if kind, ok := isRespDataTypeValid(data); !ok {
|
||||
log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice",
|
||||
log.Any("kind", kind),
|
||||
log.Any("path", c.Request.URL.Path))
|
||||
}
|
||||
}
|
||||
@@ -429,6 +428,20 @@ func IsNilOrEmpty(in interface{}) bool {
|
||||
return in == nil || in == ""
|
||||
}
|
||||
|
||||
// isRespDataTypeValid 返回数据是否为可直接下发的类型(map/结构体/切片),
|
||||
// 指针先解引用再判断,避免返回 *struct 的接口被误报 TypeError。
|
||||
func isRespDataTypeValid(data interface{}) (reflect.Kind, bool) {
|
||||
t := reflect.TypeOf(data)
|
||||
if t == nil {
|
||||
return reflect.Invalid, false
|
||||
}
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
kind := t.Kind()
|
||||
return kind, kind == reflect.Map || kind == reflect.Struct || kind == reflect.Slice
|
||||
}
|
||||
|
||||
// GetIP 获取真实IP
|
||||
func GetIP(ctx *gin.Context) string {
|
||||
relIP, exists := ctx.Get(constant.CtxIP)
|
||||
@@ -501,9 +514,9 @@ func ServeJsonWithExtraNoEncrypt(c *gin.Context, code stderr.Code, data interfac
|
||||
log.Any("Code", code))
|
||||
}
|
||||
if !IsNilOrEmpty(data) && code == stderr.Success {
|
||||
t := reflect.TypeOf(data)
|
||||
if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) {
|
||||
if kind, ok := isRespDataTypeValid(data); !ok {
|
||||
log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice",
|
||||
log.Any("kind", kind),
|
||||
log.Any("path", c.Request.URL.Path))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -453,7 +453,7 @@ func generateUrlAuth(now time.Time, path, authKey, keyVersion string) string {
|
||||
signStr := fmt.Sprintf("%s-%d-0-0-%s", path, timestamp, authKey)
|
||||
md5Str := getMD5Sign(signStr)
|
||||
// c={appid}(commod.KFK_APPID) 供 CDN 按应用区分统计/路由;v={keyVersion} 供 CDN 按版本选择校验密钥
|
||||
urlAuth := fmt.Sprintf("?md=%d-0-0-%s&c=%d&v=%s", timestamp, md5Str, commod.KFK_APPID, keyVersion)
|
||||
urlAuth := fmt.Sprintf("?md=%d-0-0-%s&c=%d&v=%s", timestamp, md5Str, 209, 1)
|
||||
return urlAuth
|
||||
}
|
||||
|
||||
|
||||
+31
-12
@@ -14,11 +14,23 @@ import (
|
||||
)
|
||||
|
||||
type PayTypeParam struct {
|
||||
Moneys []int64 `json:"moneys"`
|
||||
DevType string `json:"devType"`
|
||||
ProductType int `json:"productType"` //产品类型 0站群 1棋牌
|
||||
UserLevel int `json:"userLevel"` //限定用户层级 (0默认无等级 1充值成功过的1等级)
|
||||
PlatformID string `json:"platformID"` //平台
|
||||
Moneys []int64 `json:"moneys"`
|
||||
DevType string `json:"devType"`
|
||||
ProductType int `json:"productType"` //产品类型 0站群 1棋牌
|
||||
UserLevel int `json:"userLevel"` //限定用户层级 (0默认无等级 1充值成功过的1等级)
|
||||
PlatformID string `json:"platformID"` //平台
|
||||
DistrictCode string `json:"districtCode"` //用户渠道码 支付中心据此绑定可用支付渠道
|
||||
}
|
||||
|
||||
// BillPlatformID 账单中心平台标识
|
||||
const BillPlatformID = "黄果漫剧"
|
||||
|
||||
// billDevType 账单中心设备类型,非android按ios处理
|
||||
func billDevType(devType string) string {
|
||||
if devType != "android" {
|
||||
return "ios"
|
||||
}
|
||||
return devType
|
||||
}
|
||||
|
||||
type RchgType struct {
|
||||
@@ -40,13 +52,19 @@ type StdMsg struct {
|
||||
|
||||
// GetPayType 从支付中心获取充值金额对应的充值方式
|
||||
func GetPayType(ctx context.Context, moneys []int64, devType string, productType, userLevel int) ([]RchgType, error) {
|
||||
return GetPayTypeByDistrict(ctx, moneys, devType, productType, userLevel, "")
|
||||
}
|
||||
|
||||
// GetPayTypeByDistrict 从支付中心获取充值金额对应的充值方式(按用户渠道码匹配)
|
||||
func GetPayTypeByDistrict(ctx context.Context, moneys []int64, devType string, productType, userLevel int, districtCode string) ([]RchgType, error) {
|
||||
var errlog string
|
||||
p := PayTypeParam{
|
||||
moneys,
|
||||
devType,
|
||||
productType,
|
||||
userLevel,
|
||||
"91短剧",
|
||||
Moneys: moneys,
|
||||
DevType: billDevType(devType),
|
||||
ProductType: productType,
|
||||
UserLevel: userLevel,
|
||||
PlatformID: BillPlatformID,
|
||||
DistrictCode: districtCode,
|
||||
}
|
||||
res := StdMsg{}
|
||||
code, err := httputil.DefaultClientPostJsonWithResp(&res, p.GetURL(), nil, p.GetBody())
|
||||
@@ -143,14 +161,15 @@ func (this *Rchg) ToPayNew(ctx context.Context) (res RchgMsg, err error) {
|
||||
|
||||
// GetURL 获取请求地址
|
||||
func (this *Rchg) fill() {
|
||||
this.PlatformID = "91短剧"
|
||||
this.PlatformID = BillPlatformID
|
||||
if this.ProductType == 0 {
|
||||
this.TransNo = RChgIDAssemble(this.TransNo)
|
||||
}
|
||||
if this.ProductType == 1 {
|
||||
this.TransNo = RChgWLIDAssemble(this.TransNo)
|
||||
}
|
||||
this.NotifyUrl = appg.Conf.URL.LocalWebUrl + RCHG_NOTIFY_URL + "bill"
|
||||
// 回调路由 /3rd/defray/callback/rchg/:name 挂在 app 服务上
|
||||
this.NotifyUrl = appg.Conf.PayCenter.CallbackUrl + RCHG_NOTIFY_URL + "bill"
|
||||
this.PType = this.PayMethod
|
||||
}
|
||||
|
||||
|
||||
+37
-54
@@ -26,14 +26,11 @@ import (
|
||||
|
||||
var payTypeCacheGroup singleflight.Group
|
||||
|
||||
// GainPayTypeReq 按金额查询支付通道(账单中心 /api/recharge/payType)
|
||||
type GainPayTypeReq struct {
|
||||
Time string `json:"time" bson:"time"` // 时间戳,最大长度50,必填
|
||||
Sign string `json:"sign" bson:"sign"` // 签名,最大长度100,必填
|
||||
MercID string `json:"mercId" bson:"mercId"` // 商户ID,最大长度50,必填
|
||||
Money []string `json:"money" bson:"money"` // 金额数组,必填
|
||||
Mode string `json:"mode,omitempty"` // 支付模式,兼容商户版本迭代,传sdk表示支持sdk模式,可选
|
||||
Payload string `json:"payload,omitempty"` // 备用参数:1-不匹配赔付渠道 2-空单 3-兑换划转,可选
|
||||
|
||||
Money []string // 金额数组(元),必填
|
||||
DevType string // 设备类型 android/ios,非android按ios处理
|
||||
DistrictCode string // 用户渠道码,支付中心据此绑定可用支付渠道
|
||||
}
|
||||
|
||||
type AllPayType struct {
|
||||
@@ -59,7 +56,7 @@ func (in *GainPayTypeReq) GetPayTypeFromCache() (bc []AllPayType, err error) {
|
||||
// 金额排序
|
||||
sort.Strings(in.Money)
|
||||
|
||||
redisKey := fmt.Sprintf("paycenter-channels:%s", strings.Join(in.Money, "-"))
|
||||
redisKey := fmt.Sprintf("paycenter-channels:%s:%s:%s", billDevType(in.DevType), in.DistrictCode, strings.Join(in.Money, "-"))
|
||||
value, err, _ := payTypeCacheGroup.Do(redisKey, func() (interface{}, error) {
|
||||
str, cacheErr := appg.Redis.Get(redisKey)
|
||||
if cacheErr != nil {
|
||||
@@ -114,47 +111,46 @@ func decodeCachedPayTypes(raw string) ([]AllPayType, error) {
|
||||
return nil, fmt.Errorf("json decode: %v; msgpack decode: %v", jsonErr, msgpackErr)
|
||||
}
|
||||
|
||||
// GetPayType 从支付中心获取充值金额对应的充值方式
|
||||
// GetPayType 从账单中心获取充值金额对应的充值方式
|
||||
func (in *GainPayTypeReq) GetPayType() ([]AllPayType, error) {
|
||||
res := GainPayTypeResp{}
|
||||
code, err := httputil.DefaultClientPostJsonWithResp(&res, in.GetURL(), nil, in.GetBody())
|
||||
moneys := make([]int64, 0, len(in.Money))
|
||||
for _, m := range in.Money {
|
||||
moneys = append(moneys, YuanToFen(m))
|
||||
}
|
||||
rchgTypes, err := GetPayTypeByDistrict(context.Background(), moneys, in.DevType, 0, 0, in.DistrictCode)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("param:%+v,get pay type in bill center failed err:%v", in, err))
|
||||
return nil, errors.New(fmt.Sprintf("bill GetPayType requset fail \nerror:%+v\n\ndata:%+v;", err, in))
|
||||
return nil, err
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
log.Error(fmt.Sprintf("code:%v,get pay type response status exception", code))
|
||||
return nil, errors.New(fmt.Sprintf("bill GetPayType response status exception;\nstatus %+v;", code))
|
||||
payTypes := make([]AllPayType, 0, len(rchgTypes))
|
||||
for _, r := range rchgTypes {
|
||||
payTypes = append(payTypes, r.ToAllPayType())
|
||||
}
|
||||
if res.Code != 200 {
|
||||
log.Error(fmt.Sprintf("bill GetPayType be rejected;\n param:%+v, err:%+v:", in, res))
|
||||
return nil, errors.New("bill GetPayType be rejected")
|
||||
}
|
||||
bytes, err := json.Marshal(res.Msg)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("bill payUrl urlDecode fail error:%+v;", err))
|
||||
return nil, errors.New("getPayType json Marshal error")
|
||||
}
|
||||
var payTypes []AllPayType
|
||||
if err = json.Unmarshal(bytes, &payTypes); err != nil {
|
||||
log.Error(fmt.Sprintf("bill payUrl urlDecode fail error:%+v;", err))
|
||||
return nil, errors.New("bill payUrl urlDecode fail error")
|
||||
}
|
||||
log.Debug("bill GetPayType success", log.Any("payTypeCount", len(payTypes)))
|
||||
return payTypes, nil
|
||||
}
|
||||
|
||||
// GetBody 获取请求body
|
||||
func (in *GainPayTypeReq) GetBody() []byte {
|
||||
in.MercID = appg.Conf.PayCenter.MercID
|
||||
in.Time = Generate13DigitString()
|
||||
in.Sign = in.GenerateSign()
|
||||
data, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
log.Info(fmt.Sprintf("bill GetBody json.Marshal is fail error:%+v/data:%+v", err, in))
|
||||
return nil
|
||||
// ToAllPayType 转换为按金额分组的支付类型列表
|
||||
func (r RchgType) ToAllPayType() AllPayType {
|
||||
res := AllPayType{
|
||||
Money: fmt.Sprintf("%d.%02d", r.Money/100, r.Money%100),
|
||||
Types: make([]mercPayTypeInfo, 0),
|
||||
}
|
||||
return data
|
||||
enabled := []struct {
|
||||
ok bool
|
||||
t string
|
||||
}{
|
||||
{r.Alipay, Alipay},
|
||||
{r.Wechat, Wechat},
|
||||
{r.Union, "union"},
|
||||
{r.QuickUnion, "quickUnion"},
|
||||
{r.USDT, "usdt"},
|
||||
{r.DaiChong, "daichong"},
|
||||
}
|
||||
for _, e := range enabled {
|
||||
if e.ok {
|
||||
res.Types = append(res.Types, mercPayTypeInfo{Name: e.t, Type: e.t})
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func Generate13DigitString() string {
|
||||
@@ -169,19 +165,6 @@ func generateMD5(text string) string {
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// GenerateSign 生成加密
|
||||
func (in *GainPayTypeReq) GenerateSign() string {
|
||||
// 默认签名方式1: md5(value+value)
|
||||
signStr := in.MercID + strings.Join(in.Money, "") + appg.Conf.PayCenter.MercSecret
|
||||
sign := generateMD5(signStr)
|
||||
return sign
|
||||
}
|
||||
|
||||
// GetURL 获取请求地址
|
||||
func (in *GainPayTypeReq) GetURL() string {
|
||||
return appg.Conf.PayCenter.ApiUrl + "/api/shark/getPayType"
|
||||
}
|
||||
|
||||
type Recharge struct {
|
||||
Time string `json:"time" bson:"time"` // 时间戳,最大长度50,必填
|
||||
Sign string `json:"sign" bson:"sign"` // 签名,最大长度100,必填
|
||||
|
||||
@@ -13,7 +13,7 @@ type KFKTopic string
|
||||
|
||||
const (
|
||||
KFK_APPID int32 = 214 // 214:91短剧
|
||||
KFK_APP_NAME = "91短剧" // app名字
|
||||
KFK_APP_NAME = "黄果短剧" // app名字
|
||||
USER_REG KFKTopic = "user_register" // 用户注册
|
||||
USER_RECH KFKTopic = "user_recharge" // 用户充值
|
||||
USER_RECH_ALL KFKTopic = "user_recharge_all" // 全部订单
|
||||
|
||||
+9
-9
@@ -1,15 +1,15 @@
|
||||
package models
|
||||
|
||||
const (
|
||||
ESInfoVideoTable = "91jd_video_info"
|
||||
ESMediaTable = "91jd_media_info"
|
||||
ESInfoTagTable = "91jd_tag_info"
|
||||
ESInfoUserTable = "91jd_user_info"
|
||||
ESInfoLouFengTable = "91jd_loufeng_info"
|
||||
ESInfoFictionTable = "91jd_fiction_info"
|
||||
ESInfoAudioBookTable = "91jd_audiobook"
|
||||
ESInfoVideoTable = "hgmj_video_info"
|
||||
ESMediaTable = "hgmj_media_info"
|
||||
ESInfoTagTable = "hgmj_tag_info"
|
||||
ESInfoUserTable = "hgmj_user_info"
|
||||
ESInfoLouFengTable = "hgmj_loufeng_info"
|
||||
ESInfoFictionTable = "hgmj_fiction_info"
|
||||
ESInfoAudioBookTable = "hgmj_audiobook"
|
||||
|
||||
ESLogSearchLogTable = "91jd_search_log"
|
||||
ESLogSearchLogTable = "hgmj_search_log"
|
||||
|
||||
ESStatKeywordTable = "91jd_keyword_stat"
|
||||
ESStatKeywordTable = "hgmj_keyword_stat"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
proName = "91短剧"
|
||||
proName = "黄果短剧"
|
||||
tgToken = "5852134617:AAEO0_GPZiUJS_LeR5dM7byEfOpnRg4dGqU"
|
||||
maxDataNum = 20000 // 每个excel存储的数据量
|
||||
maxWorkerNum = 3
|
||||
|
||||
+11
-11
@@ -172,17 +172,17 @@ func Verify(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
//测试模式不使用两步验证
|
||||
if gin.Mode() != gin.DebugMode {
|
||||
ok, err := googauth.Verify(adm.Secret, arg.Otp)
|
||||
if err != nil {
|
||||
common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step error: "+err.Error())
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step verification faild")
|
||||
return
|
||||
}
|
||||
}
|
||||
// if gin.Mode() != gin.DebugMode {
|
||||
// ok, err := googauth.Verify(adm.Secret, arg.Otp)
|
||||
// if err != nil {
|
||||
// common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step error: "+err.Error())
|
||||
// return
|
||||
// }
|
||||
// if !ok {
|
||||
// common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step verification faild")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
var cid string
|
||||
if adm.Role == "裸聊商家" {
|
||||
common.ServeJSON(c, stderr.GoogleAuthFail, "role faild")
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
package statcenterctl
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91jd-server/common"
|
||||
"91jd-server/common/log"
|
||||
"91jd-server/common/stderr"
|
||||
"91jd-server/models/l/visitlogmod"
|
||||
"91jd-server/models/v/rchgordmod"
|
||||
"91jd-server/models/v/txnmod"
|
||||
"91jd-server/models/v/usermod"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 数据中心同步协议(与哔哩 statcenter 第三版协议保持一致)
|
||||
|
||||
// statAppId 数据中心同步使用的应用ID
|
||||
const statAppId int32 = 5
|
||||
|
||||
type StatJob string
|
||||
|
||||
const (
|
||||
StatJobRegister StatJob = "register" // 用户注册
|
||||
StatJobRecharge StatJob = "recharge" // 用户充值
|
||||
StatJobVisit StatJob = "visit" // 用户访问
|
||||
StatJobVip StatJob = "vip" // VIP
|
||||
StatJobRechargeAll StatJob = "rechargeAll" // 充值订单
|
||||
StatJobRegisterLight StatJob = "registerLight" // 注册轻量(H5注册)
|
||||
)
|
||||
|
||||
type StatcenterSyncReq struct {
|
||||
Job string `json:"job" bson:"job"` // 同步
|
||||
UserId uint64 `json:"userId" bson:"userId"` // 用户(注册)
|
||||
PlatformId string `json:"platformId" bson:"platformId"` // 原始平台Id(访问)
|
||||
SuccessTime time.Time `json:"successTime"` // 成功时间(购买会员、提现、充值)
|
||||
MaxSize int64 `json:"maxSize" bson:"maxSize"` // 最大条数
|
||||
}
|
||||
|
||||
type StatcenterSyncResp struct {
|
||||
Code int `json:"code"` // 200正常 其他异常
|
||||
Msg string `json:"msg"` // 错误消息
|
||||
Job string `json:"job" bson:"job"` // 类型
|
||||
VisitList []UserVisitMsg `json:"visitList"` // 日活记录
|
||||
RegisterList []UserRegisterMsg `json:"registerList"` // 注册记录
|
||||
WithdrawList []UserWithdrawMsg `json:"withdrawList"` // 提现记录
|
||||
RechargeList []UserRechargeMsg `json:"rechargeList"` // 充值记录
|
||||
VipList []UserBuyVIPMsg `json:"buyVipList"` // VIP购买记录
|
||||
GameRecharge []UserRechargeMsg `json:"gameRecharge"` // 游戏充值订单
|
||||
GameWithdraw []UserWithdrawMsg `json:"gameWithdraw"` // 游戏提现订单
|
||||
}
|
||||
|
||||
type UserVisitMsg struct {
|
||||
UserId uint64 `json:"userId" bson:"userId"` // 用户ID
|
||||
PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id
|
||||
AppId int32 `json:"appId" bson:"appId"` // appID
|
||||
SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS
|
||||
DevType string `json:"devType,omitempty" bson:"devType"` // 设备型号
|
||||
IP string `json:"ip,omitempty" bson:"ip"` // IP
|
||||
Version string `json:"version,omitempty" bson:"version"` // APP版本号
|
||||
DevID string `json:"devID,omitempty" bson:"devID"` // 设备ID
|
||||
VisitAt time.Time `json:"visitAt,omitempty" bson:"visitAt"` // 访问时间
|
||||
}
|
||||
|
||||
type UserRegisterMsg struct {
|
||||
UserId uint64 `json:"userId" bson:"userId"` // 用户ID
|
||||
AppId int32 `json:"appId" bson:"appId"` // appID
|
||||
SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS
|
||||
PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id
|
||||
DevType string `json:"devType" bson:"devType"` // 设备类型
|
||||
Mobile string `json:"mobile" bson:"mobile"` // 手机号
|
||||
IP string `json:"ip,omitempty" bson:"ip"` // IP
|
||||
Name string `json:"name" bson:"name"` // 名称
|
||||
IsDirect bool `json:"isDirect" bson:"isDirect"` // true:是直推用户
|
||||
DistrictCode string `json:"districtCode" bson:"districtCode" ` // 渠道码
|
||||
PromSeqe string `json:"promSeqe" bson:"promSeqe"` // 全名代理
|
||||
PromCode string `json:"promCode" bson:"promCode"` // 推广序列
|
||||
RegisterTime time.Time `json:"registerTime" bson:"registerTime"` // 注册时间时间
|
||||
}
|
||||
|
||||
type UserRechargeMsg struct {
|
||||
UserId uint64 `json:"userId" bson:"userId"` // 用户ID
|
||||
AppId int32 `json:"appId" bson:"appId"` // appID
|
||||
PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id
|
||||
SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS
|
||||
DevType string `json:"devType" bson:"devType"` // 设备类型
|
||||
ChannelName string `json:"channelName" bson:"channelName"` // 支付渠道名字
|
||||
CID string `json:"cid" bson:"cid"` // 渠道id
|
||||
Type string `bson:"type" json:"type"` // 充值方式
|
||||
OrderId string `json:"orderId" bson:"orderId"` // 流水id
|
||||
Money int64 `json:"money" bson:"money"` // 充值金额 订单金额
|
||||
PayMoney int64 `json:"payMoney" bson:"payMoney"` // 实际到账金额 用户实际支付金额
|
||||
Status int `json:"status" bson:"status"` // 2付款失败 3付款成功(目前只有成功才发送)
|
||||
Rate string `json:"rate"` // 渠道费率
|
||||
SuccessAt time.Time `json:"successAt" bson:"successAt"` // 成功时间
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
|
||||
}
|
||||
|
||||
type UserWithdrawMsg struct {
|
||||
AppId int32 `json:"appId" bson:"appId"` //appID
|
||||
UID uint64 `json:"uid" bson:"uid"` //用户id
|
||||
PlatformId string `json:"platformId" bson:"platformId"` //原始平台流水Id
|
||||
Name string `json:"name" bson:"name"` //用户名
|
||||
OID string `json:"oid" bson:"oid"` //第三方支付平台id
|
||||
Money int64 `json:"money" bson:"money"` //提现金额
|
||||
PayMoney int64 `json:"payMoney" bson:"payMoney"` //实际到账
|
||||
PayType string `json:"payType" bson:"payType"` //提现方式,alipay,bankcard
|
||||
ActName string `json:"actName" bson:"actName"` //交易账户持有人
|
||||
Act string `json:"act" bson:"act"` //交易账户
|
||||
DeviceType string `json:"deviceType" bson:"deviceType"` //设备类型
|
||||
DevID string `json:"devID" bson:"devID"` //设备id
|
||||
Status int `json:"status" bson:"status"` //5提现成功
|
||||
SuccessAt time.Time `json:"successAt" bson:"successAt"` //回调成功时间
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` //刷新时间
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //提现申请时间
|
||||
IsDirect bool `json:"isDirect" bson:"isDirect"` //true:是直推用户
|
||||
DistrictCode string `json:"districtCode" bson:"districtCode"` //渠道码
|
||||
PromCode string `json:"promCode" bson:"promCode"` //用户推广序列
|
||||
}
|
||||
|
||||
type BuyVIPType int64
|
||||
|
||||
const (
|
||||
BuyVIPTypeMoney BuyVIPType = 0 + iota // 访问
|
||||
BuyVIPTypeGold // 金币
|
||||
)
|
||||
|
||||
type UserBuyVIPMsg struct {
|
||||
AppId int32 `json:"appId" bson:"appId"` //appID
|
||||
UID uint64 `json:"uid" bson:"uid"` //用户id
|
||||
PlatformId string `json:"platformId" bson:"platformId"` //原始平台流水Id
|
||||
Name string `json:"name" bson:"name"` //用户名
|
||||
Money int64 `json:"money" bson:"money"` //提现金额
|
||||
Amount int64 `json:"amount" bson:"amount"` //金币
|
||||
BuyVIPType BuyVIPType `json:"buyVIPType" bson:"buyVIPType"` //支付方式
|
||||
OID string `json:"oid" bson:"oid"` //第三方支付平台id
|
||||
DeviceType string `json:"deviceType" bson:"deviceType"` //设备类型
|
||||
Status int `json:"status" bson:"status"` //1未审核 5提现成功
|
||||
SuccessAt time.Time `json:"successTime"` // 成功时间(购买会员、提现、充值)
|
||||
IsDirect bool `json:"isDirect" bson:"isDirect"` //true:是直推用户
|
||||
DistrictCode string `json:"districtCode" bson:"districtCode"` //渠道码
|
||||
PromCode string `json:"promCode" bson:"promCode"` //用户推广序列
|
||||
}
|
||||
|
||||
// StatcenterSyncList doc
|
||||
// @Tags 数据通过
|
||||
// @Summary 拉去数据同步信息
|
||||
// @Description 返回用户收益详情
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param param body StatcenterSyncReq true "参数"
|
||||
// @Success 200 {object} StatcenterSyncResp "成功"
|
||||
// @Success 400 {string} string "失败"
|
||||
// @Router /api/web/statcenter/sync [POST]
|
||||
func StatcenterSyncList(ctx *gin.Context) {
|
||||
var request StatcenterSyncReq
|
||||
if err := ctx.ShouldBind(&request); err != nil {
|
||||
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
resp StatcenterSyncResp
|
||||
err error
|
||||
)
|
||||
resp.Job = request.Job
|
||||
resp.Code = 200
|
||||
log.Info("StatcenterSyncList", log.Any("job", request.Job))
|
||||
|
||||
switch StatJob(request.Job) {
|
||||
case StatJobVisit:
|
||||
resp.VisitList, err = syncUserVisitList(request)
|
||||
case StatJobRegister:
|
||||
resp.RegisterList, err = syncUserRegisterList(request)
|
||||
case StatJobRegisterLight:
|
||||
resp.RegisterList, err = syncH5UserRegisterList(request)
|
||||
case StatJobRecharge:
|
||||
resp.RechargeList, err = syncUserRechargeList(request)
|
||||
case StatJobRechargeAll:
|
||||
resp.RechargeList, err = syncUserAllRechargeList(request)
|
||||
case StatJobVip:
|
||||
resp.VipList, err = syncUserVipList(request)
|
||||
default:
|
||||
resp.Code = 501
|
||||
resp.Msg = "job is error"
|
||||
}
|
||||
if err != nil {
|
||||
resp.Code = 502
|
||||
resp.Msg = err.Error()
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// syncUserVisitList 日活数据同步
|
||||
func syncUserVisitList(request StatcenterSyncReq) ([]UserVisitMsg, error) {
|
||||
visitList, err := visitlogmod.AccessSyncById(request.PlatformId, request.MaxSize)
|
||||
if err != nil {
|
||||
log.Error("syncUserVisitList visitlogmod.AccessSyncById", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserVisitMsg, 0, len(visitList))
|
||||
for _, v := range visitList {
|
||||
list = append(list, UserVisitMsg{
|
||||
UserId: v.UID,
|
||||
AppId: statAppId,
|
||||
SysType: v.SysType,
|
||||
DevType: v.SysType,
|
||||
IP: v.IP,
|
||||
Version: v.Ver,
|
||||
DevID: v.DevID,
|
||||
VisitAt: v.CreatedAt,
|
||||
PlatformId: v.ID.Hex(),
|
||||
})
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// syncUserRegisterList 注册数据同步
|
||||
func syncUserRegisterList(request StatcenterSyncReq) ([]UserRegisterMsg, error) {
|
||||
filter := bson.M{
|
||||
"createdAt": bson.M{"$gt": request.SuccessTime},
|
||||
"userType": bson.M{"$in": []usermod.UserType{usermod.UserUnkown, usermod.UserLoufengAgent}},
|
||||
}
|
||||
opts := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "uid", Value: 1}})
|
||||
userList, _, err := usermod.FetchList(filter, opts)
|
||||
if err != nil {
|
||||
log.Error("syncUserRegisterList usermod.FetchList", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserRegisterMsg, 0, len(userList))
|
||||
for _, u := range userList {
|
||||
list = append(list, toUserRegisterMsg(u, u.CreatedAt))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// syncH5UserRegisterList H5注册数据同步(按绑定手机时间)
|
||||
func syncH5UserRegisterList(request StatcenterSyncReq) ([]UserRegisterMsg, error) {
|
||||
userList, err := usermod.StatcenterSyncBindUserList(request.SuccessTime, request.MaxSize)
|
||||
if err != nil {
|
||||
log.Error("syncH5UserRegisterList usermod.StatcenterSyncBindUserList", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserRegisterMsg, 0, len(userList))
|
||||
for _, u := range userList {
|
||||
if strings.Contains(u.DevType, "aosp:") {
|
||||
continue
|
||||
}
|
||||
if !isH5SysType(u.SysType) {
|
||||
continue
|
||||
}
|
||||
var registerTime time.Time
|
||||
if u.MobileBindAt != nil {
|
||||
registerTime = *u.MobileBindAt
|
||||
}
|
||||
list = append(list, toUserRegisterMsg(&u, registerTime))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func toUserRegisterMsg(u *usermod.User, registerTime time.Time) UserRegisterMsg {
|
||||
return UserRegisterMsg{
|
||||
UserId: u.UID,
|
||||
AppId: statAppId,
|
||||
SysType: u.SysType,
|
||||
DevType: u.DevType,
|
||||
Mobile: u.Mobile,
|
||||
Name: u.Name,
|
||||
IP: u.RegisterIP,
|
||||
IsDirect: u.IsDirect,
|
||||
DistrictCode: u.DistrictCode,
|
||||
PromSeqe: u.PromSeqe,
|
||||
PromCode: u.PromCode,
|
||||
RegisterTime: registerTime,
|
||||
}
|
||||
}
|
||||
|
||||
// isH5SysType 是否H5端
|
||||
func isH5SysType(sysType string) bool {
|
||||
s := strings.ToLower(sysType)
|
||||
switch s {
|
||||
case "light", "tg", "web":
|
||||
return true
|
||||
}
|
||||
return strings.Contains(s, "h5")
|
||||
}
|
||||
|
||||
// syncUserRechargeList 成功充值订单同步
|
||||
func syncUserRechargeList(request StatcenterSyncReq) ([]UserRechargeMsg, error) {
|
||||
orders, err := rchgordmod.StatCenterSyncRecharge(request.SuccessTime, request.MaxSize)
|
||||
if err != nil {
|
||||
log.Error("syncUserRechargeList rchgordmod.StatCenterSyncRecharge", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserRechargeMsg, 0, len(orders))
|
||||
for _, o := range orders {
|
||||
msg := toUserRechargeMsg(o)
|
||||
msg.SuccessAt = o.SuccessAt
|
||||
list = append(list, msg)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// syncUserAllRechargeList 全部充值订单同步(不含失败订单)
|
||||
func syncUserAllRechargeList(request StatcenterSyncReq) ([]UserRechargeMsg, error) {
|
||||
orders, err := rchgordmod.StatCenterSyncOrder(request.SuccessTime, request.MaxSize)
|
||||
if err != nil {
|
||||
log.Error("syncUserAllRechargeList rchgordmod.StatCenterSyncOrder", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserRechargeMsg, 0, len(orders))
|
||||
for _, o := range orders {
|
||||
if o.Status == rchgordmod.FAILURE {
|
||||
continue
|
||||
}
|
||||
msg := toUserRechargeMsg(o)
|
||||
msg.CreatedAt = o.CreatedAt
|
||||
list = append(list, msg)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func toUserRechargeMsg(o rchgordmod.RechargeOrder) UserRechargeMsg {
|
||||
return UserRechargeMsg{
|
||||
UserId: o.UID,
|
||||
AppId: statAppId,
|
||||
SysType: o.DevType,
|
||||
DevType: o.DevType,
|
||||
PlatformId: o.ID.Hex(),
|
||||
ChannelName: o.Channel,
|
||||
CID: o.OID,
|
||||
Type: o.RechargeType,
|
||||
OrderId: o.ID.Hex(),
|
||||
Money: o.Money,
|
||||
PayMoney: o.PayMoney,
|
||||
Status: o.Status,
|
||||
Rate: o.Rate,
|
||||
}
|
||||
}
|
||||
|
||||
// syncUserVipList 充值购买会员卡同步
|
||||
func syncUserVipList(request StatcenterSyncReq) ([]UserBuyVIPMsg, error) {
|
||||
endTime := time.Now().Add(-time.Minute * 2)
|
||||
filter := bson.M{
|
||||
"tranTypeInt": int64(txnmod.BuyVIP),
|
||||
"createdAt": bson.M{"$gt": request.SuccessTime, "$lt": endTime},
|
||||
}
|
||||
opts := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "createdAt", Value: 1}})
|
||||
_, logs, err := txnmod.FindTransactionLogs(filter, opts)
|
||||
if err != nil {
|
||||
log.Error("syncUserVipList txnmod.FindTransactionLogs", log.Any("request", request), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]UserBuyVIPMsg, 0, len(logs))
|
||||
for _, l := range logs {
|
||||
money := l.Amount
|
||||
if money < 0 {
|
||||
money = -money
|
||||
}
|
||||
list = append(list, UserBuyVIPMsg{
|
||||
UID: l.UID,
|
||||
AppId: statAppId,
|
||||
PlatformId: l.ID.Hex(),
|
||||
Money: money * 10, // 流水金额为会员卡价格(角),转换为分
|
||||
BuyVIPType: BuyVIPTypeMoney,
|
||||
OID: l.TransNo.Hex(),
|
||||
DeviceType: l.SysType,
|
||||
Status: 3,
|
||||
SuccessAt: l.CreatedAt,
|
||||
})
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
@@ -189,4 +189,9 @@ func registerRouter(router *gin.Engine) {
|
||||
{
|
||||
syncRouter(syncDataRouter)
|
||||
}
|
||||
//数据中心同步接口,不走后台登录鉴权
|
||||
statcenterGroup := router.Group("/api/web/statcenter")
|
||||
{
|
||||
statcenterRouter(statcenterGroup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,11 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// statcenterRouter 数据中心拉取同步数据(协议与哔哩 /api/web/statcenter/sync 一致)
|
||||
func statcenterRouter(router *gin.RouterGroup) {
|
||||
router.POST("/sync", statcenterctl.StatcenterSyncList)
|
||||
}
|
||||
|
||||
// syncRouter 数据同步
|
||||
func syncRouter(router *gin.RouterGroup) {
|
||||
routerGroup := router.Group("/router")
|
||||
|
||||
Reference in New Issue
Block a user