From 97c9588e5d7a92ed358628a1a4be62c565f0dae2 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 15 Sep 2026 22:11:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=92=8C=E5=B9=BF=E5=91=8A?= =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/adsctrl/ads.go | 1 + app/api/pingctrl/pingctrl.go | 36 ++-- app/api/pingctrl/version.go | 73 ++++++++ app/api/rechargectrl/yinse.go | 7 + app/api/recreationctrl/recreationctrl.go | 1 + app/appg/appg.go | 1 + app/service/adser/advertiseV2.go | 2 + app/service/adser/jtAdvertise.go | 222 +++++++++++++---------- app/service/rechargeser/new_pay.go | 16 +- app/service/rechargeser/rechargeser.go | 33 ++-- common/rchgutil/bill.go | 43 +++-- common/rchgutil/recharge.go | 91 ++++------ 12 files changed, 328 insertions(+), 198 deletions(-) create mode 100644 app/api/pingctrl/version.go diff --git a/app/api/adsctrl/ads.go b/app/api/adsctrl/ads.go index 10f5c17..78f6f05 100644 --- a/app/api/adsctrl/ads.go +++ b/app/api/adsctrl/ads.go @@ -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) } diff --git a/app/api/pingctrl/pingctrl.go b/app/api/pingctrl/pingctrl.go index 8288ae2..316d47c 100644 --- a/app/api/pingctrl/pingctrl.go +++ b/app/api/pingctrl/pingctrl.go @@ -11,6 +11,7 @@ import ( "91porn-server/common/constant" "91porn-server/common/services/message" "91porn-server/common/store" + v10 "91porn-server/common/v10" "91porn-server/models/cache/bannerjumpdata" "91porn-server/models/cache/sysconfdata" "91porn-server/models/v/bannerjumpmod" @@ -32,7 +33,6 @@ import ( "91porn-server/common" "91porn-server/common/log" "91porn-server/common/stderr" - "91porn-server/common/version" "91porn-server/models/commod" "91porn-server/models/v/sourcemod" "91porn-server/models/v/systemmod" @@ -248,40 +248,29 @@ 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)) @@ -324,6 +313,7 @@ func DomainList(ctx *gin.Context) { adsList = append(adsList, adsinfo) } } + log.Info("JtAdvertiseThreeServer", log.Any("ads", adsList)) sysInfo.Ads.AdsList = adsList }) common.Go(func() { diff --git a/app/api/pingctrl/version.go b/app/api/pingctrl/version.go new file mode 100644 index 0000000..8800447 --- /dev/null +++ b/app/api/pingctrl/version.go @@ -0,0 +1,73 @@ +package pingctrl + +import ( + "91porn-server/app/appg" + "91porn-server/common/httputil" + "91porn-server/common/log" + + "encoding/json" + "fmt" + "net/http" +) + +type PacketType int32 + +const ( + PacketTypeEnterprise PacketType = 1 // 企业签 + PacketTypeShop PacketType = 2 // 商店包 + PacketTypeTestFlight PacketType = 3 // TF包 +) + +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: 2, + 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 +} diff --git a/app/api/rechargectrl/yinse.go b/app/api/rechargectrl/yinse.go index 044bdd0..0ac3174 100644 --- a/app/api/rechargectrl/yinse.go +++ b/app/api/rechargectrl/yinse.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/http" + "time" "91porn-server/app/service/rechargeser" "91porn-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)) } diff --git a/app/api/recreationctrl/recreationctrl.go b/app/api/recreationctrl/recreationctrl.go index 6e26a18..52894fa 100644 --- a/app/api/recreationctrl/recreationctrl.go +++ b/app/api/recreationctrl/recreationctrl.go @@ -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) } diff --git a/app/appg/appg.go b/app/appg/appg.go index 7170c84..2b870cd 100644 --- a/app/appg/appg.go +++ b/app/appg/appg.go @@ -175,6 +175,7 @@ type GlobalConfig struct { DataCentreURL string `json:"dataCentreUrl"` // 数据中心 ApiUrls []string `json:"apiUrls"` // api域名 ProductUrl string `json:"productUrl"` // 产品中心 + VersionUrl string `json:"versionUrl"` // 版本/广告中心地址 //Active2023 string `json:"active2023"` // 2023新春抽奖地址 NavigateUrl string `json:"navigateUrl"` // 导航站地址 HlH5Url string `json:"hlH5Url"` // 黑料h5url diff --git a/app/service/adser/advertiseV2.go b/app/service/adser/advertiseV2.go index 409c27f..b41202f 100644 --- a/app/service/adser/advertiseV2.go +++ b/app/service/adser/advertiseV2.go @@ -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) { diff --git a/app/service/adser/jtAdvertise.go b/app/service/adser/jtAdvertise.go index e4882a6..95d7ef9 100644 --- a/app/service/adser/jtAdvertise.go +++ b/app/service/adser/jtAdvertise.go @@ -9,16 +9,17 @@ import ( "sort" "strconv" "strings" - "time" + "sync" "91porn-server/app/appg" "91porn-server/app/service/activityclient" "91porn-server/common" - "91porn-server/common/crypt" "91porn-server/common/httputil" "91porn-server/common/log" "91porn-server/models/v/usermod" "91porn-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 = 2 - 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) { diff --git a/app/service/rechargeser/new_pay.go b/app/service/rechargeser/new_pay.go index 1a66e26..79d61f0 100644 --- a/app/service/rechargeser/new_pay.go +++ b/app/service/rechargeser/new_pay.go @@ -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{ @@ -283,7 +295,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)) diff --git a/app/service/rechargeser/rechargeser.go b/app/service/rechargeser/rechargeser.go index c8dc122..f6b4611 100644 --- a/app/service/rechargeser/rechargeser.go +++ b/app/service/rechargeser/rechargeser.go @@ -97,17 +97,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]", @@ -123,8 +125,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]", @@ -507,6 +509,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 diff --git a/common/rchgutil/bill.go b/common/rchgutil/bill.go index 84220a6..1139f3e 100644 --- a/common/rchgutil/bill.go +++ b/common/rchgutil/bill.go @@ -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, - "91PORN", + 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 = "91PORN" + 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 } diff --git a/common/rchgutil/recharge.go b/common/rchgutil/recharge.go index 749fb57..8c59f6c 100644 --- a/common/rchgutil/recharge.go +++ b/common/rchgutil/recharge.go @@ -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,必填