- 移除 买点 (user acquisition tracking) 子系统:dataReport SDK、eventmod 事件模型、reportdata 定时上报 job、reportser/srv_report 包装层,以及业务 侧的 ~20 处 Report* 调用点;去掉 proto.ConfigInfo 的 ReportSDKConfig 字段、DataReport yaml 配置块和 pubdef.TrackerAppId。 - 广告获取改走 versionUrl /api/stat/advitise/v2/get(appId=12),移除 adcenter(Jt) 调用;新增 POST /api/app/ads/click 异步上报; proto.AdvitiseData 别名 proxy.AdvitiseData,AList/BList 改名 AppInfoAList/AppInfoBList。 - web 后台登录取消谷歌验证码(srv_mwacc.AccLogin 去掉 code 参数),密码 正确即发放 token。 - app 端取消注册/登录/找回密码/游客登录接口及密码、token 相关代码;鉴权 中间件按 X-User-Agent 中的 devID 识别用户,首次访问自动建号,Redis 缓存 10 分钟;修改资料不再支持改密码。 - app m3u8 ticket 校验失败不再降级到 fallback 路径,仍按原 m3u8Name 返回。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
221 lines
7.1 KiB
Go
221 lines
7.1 KiB
Go
package srv_recharge
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/srvdef"
|
|
"videoweb/pub/models/cointransmod"
|
|
"videoweb/pub/models/rechargepackagemod"
|
|
"videoweb/pub/models/usermod"
|
|
"videoweb/pub/models/vipordermod"
|
|
"videoweb/pub/payment"
|
|
"videoweb/pub/pubdef"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
)
|
|
|
|
// BillCallbackParam 支付成功后的入账参数。签名校验由新支付中心回调完成。
|
|
type BillCallbackParam struct {
|
|
TradeNo string `json:"transNo" form:"transNo"`
|
|
Oid string `json:"oid" form:"oid"`
|
|
RealAmount int64 `json:"payMoney" form:"payMoney"`
|
|
PaymentAt time.Time `json:"paymentAt" form:"paymentAt"`
|
|
SuccessAt time.Time `json:"successAt" form:"successAt"`
|
|
}
|
|
|
|
// OnPayCenterCallback 统一支付回调入口
|
|
// 通过 vip_order 表查询订单,再按 OrderType 分支处理。
|
|
func OnPayCenterCallback(ctx *gin.Context, param *BillCallbackParam) *srvdef.SrvErr {
|
|
log.Info("srv_recharge:OnPayCenterCallback",
|
|
log.Any("tradeNo", param.TradeNo),
|
|
log.Any("oid", param.Oid),
|
|
log.Any("realAmount", param.RealAmount),
|
|
)
|
|
if param.TradeNo == "" {
|
|
return srvdef.New(srvdef.ErrParamError, "tradeNo 不能为空", "")
|
|
}
|
|
|
|
order, serr := vipordermod.FindOneVipOrder(ctx, bson.M{
|
|
vipordermod.FN.VipOrder.TradeNo: param.TradeNo,
|
|
})
|
|
if serr != nil {
|
|
log.Error("OnPayCenterCallback FindOneVipOrder failed",
|
|
log.Any("tradeNo", param.TradeNo), log.E(serr))
|
|
return serr
|
|
}
|
|
|
|
if order.Status == pubdef.VipOrderSuccess {
|
|
log.Info("OnPayCenterCallback already success", log.Any("tradeNo", param.TradeNo))
|
|
return nil
|
|
}
|
|
|
|
if order.Oid != "" && order.Oid != param.Oid {
|
|
return srvdef.New(srvdef.ErrParamError,
|
|
fmt.Sprintf("oid 不匹配: %s vs %s", order.Oid, param.Oid), "")
|
|
}
|
|
|
|
paymentAt := resolvePaymentAt(param)
|
|
|
|
if serr = vipordermod.UpdateOrderSuccess(ctx, param.TradeNo, param.Oid, param.RealAmount, paymentAt); serr != nil {
|
|
log.Error("OnPayCenterCallback UpdateOrderSuccess failed",
|
|
log.Any("tradeNo", param.TradeNo), log.E(serr))
|
|
return serr
|
|
}
|
|
|
|
orderSnap := *order
|
|
realAmount := param.RealAmount
|
|
|
|
switch order.OrderType {
|
|
case vipordermod.OrderTypeCoin:
|
|
go handleCoinOrderSuccess(orderSnap, realAmount)
|
|
default:
|
|
go handleVipOrderSuccess(orderSnap, realAmount)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleVipOrderSuccess(order vipordermod.VipOrder, realAmount int64) {
|
|
ctx := context.Background()
|
|
|
|
pkg, err := findVipPackage(ctx, order.CardId)
|
|
if err != nil {
|
|
log.Error("handleVipOrderSuccess find package failed",
|
|
log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err))
|
|
return
|
|
}
|
|
|
|
expire, err := payment.ActivateVip(ctx, order.UserId, pkg)
|
|
if err != nil {
|
|
log.Error("handleVipOrderSuccess ActivateVip failed, retrying",
|
|
log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err))
|
|
time.Sleep(3 * time.Second)
|
|
expire, err = payment.ActivateVip(ctx, order.UserId, pkg)
|
|
if err != nil {
|
|
log.Error("handleVipOrderSuccess ActivateVip retry failed",
|
|
log.Any("userId", order.UserId), log.Any("packageId", order.CardId), log.E(err))
|
|
}
|
|
}
|
|
log.Info("handleVipOrderSuccess done", log.Any("userId", order.UserId), log.Any("expire", expire))
|
|
|
|
if pkg.GiftCoin > 0 {
|
|
_ = payment.WriteCoinTransByUser(ctx, payment.WriteCoinTransOpt{
|
|
UserId: order.UserId,
|
|
FlowDir: cointransmod.FlowIn,
|
|
TranType: cointransmod.TranTypeVipGiftCoin,
|
|
Amount: pkg.GiftCoin,
|
|
OrderNo: order.TradeNo,
|
|
Remark: payment.CoinTransRemark(cointransmod.TranTypeVipGiftCoin, pkg.Name),
|
|
})
|
|
}
|
|
}
|
|
|
|
func handleCoinOrderSuccess(order vipordermod.VipOrder, realAmount int64) {
|
|
ctx := context.Background()
|
|
|
|
if err := addUserCoins(ctx, order.UserId, order.Coin); err != nil {
|
|
log.Error("handleCoinOrderSuccess addUserCoins failed, retrying",
|
|
log.Any("userId", order.UserId), log.Any("coin", order.Coin), log.E(err))
|
|
time.Sleep(3 * time.Second)
|
|
if err = addUserCoins(ctx, order.UserId, order.Coin); err != nil {
|
|
log.Error("handleCoinOrderSuccess addUserCoins retry failed",
|
|
log.Any("userId", order.UserId), log.Any("coin", order.Coin), log.E(err))
|
|
}
|
|
}
|
|
log.Info("handleCoinOrderSuccess done", log.Any("userId", order.UserId), log.Any("coin", order.Coin))
|
|
|
|
writeCoinOrderTrans(ctx, order)
|
|
}
|
|
|
|
func writeCoinOrderTrans(ctx context.Context, order vipordermod.VipOrder) {
|
|
baseCoin, giftCoin := resolveCoinOrderSplit(ctx, order)
|
|
balanceAfter, ok := coinOrderBalanceAfter(ctx, order.UserId)
|
|
if !ok || giftCoin <= 0 {
|
|
_ = payment.WriteCoinTransByUser(ctx, payment.WriteCoinTransOpt{
|
|
UserId: order.UserId,
|
|
FlowDir: cointransmod.FlowIn,
|
|
TranType: cointransmod.TranTypeCoinRecharge,
|
|
Amount: order.Coin,
|
|
OrderNo: order.TradeNo,
|
|
Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinRecharge, ""),
|
|
})
|
|
return
|
|
}
|
|
|
|
if baseCoin > 0 {
|
|
_ = payment.WriteCoinTrans(ctx, payment.WriteCoinTransOpt{
|
|
UserId: order.UserId,
|
|
FlowDir: cointransmod.FlowIn,
|
|
TranType: cointransmod.TranTypeCoinRecharge,
|
|
Amount: baseCoin,
|
|
OrderNo: order.TradeNo,
|
|
Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinRecharge, ""),
|
|
}, balanceAfter-giftCoin)
|
|
}
|
|
_ = payment.WriteCoinTrans(ctx, payment.WriteCoinTransOpt{
|
|
UserId: order.UserId,
|
|
FlowDir: cointransmod.FlowIn,
|
|
TranType: cointransmod.TranTypeCoinGift,
|
|
Amount: giftCoin,
|
|
OrderNo: order.TradeNo,
|
|
Remark: payment.CoinTransRemark(cointransmod.TranTypeCoinGift, ""),
|
|
}, balanceAfter)
|
|
}
|
|
|
|
func resolveCoinOrderSplit(ctx context.Context, order vipordermod.VipOrder) (int64, int64) {
|
|
if order.BaseCoin+order.GiftCoin == order.Coin && (order.BaseCoin > 0 || order.GiftCoin > 0) {
|
|
return order.BaseCoin, order.GiftCoin
|
|
}
|
|
pkg, _ := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{
|
|
rechargepackagemod.FN.RechargePackage.Id: order.CoinCfgId,
|
|
rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeCoin,
|
|
})
|
|
if pkg != nil && pkg.BaseCoin+pkg.GiftCoin == order.Coin {
|
|
return pkg.BaseCoin, pkg.GiftCoin
|
|
}
|
|
return order.Coin, 0
|
|
}
|
|
|
|
func coinOrderBalanceAfter(ctx context.Context, userId int64) (int64, bool) {
|
|
user, serr := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: userId})
|
|
if serr != nil || user == nil {
|
|
return 0, false
|
|
}
|
|
return user.Recharge, true
|
|
}
|
|
|
|
func resolvePaymentAt(param *BillCallbackParam) time.Time {
|
|
if !param.PaymentAt.IsZero() {
|
|
return param.PaymentAt
|
|
}
|
|
if !param.SuccessAt.IsZero() {
|
|
return param.SuccessAt
|
|
}
|
|
return time.Now()
|
|
}
|
|
|
|
func findVipPackage(ctx context.Context, packageId int64) (*rechargepackagemod.RechargePackage, *srvdef.SrvErr) {
|
|
pkg, serr := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{
|
|
rechargepackagemod.FN.RechargePackage.Id: packageId,
|
|
rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeVip,
|
|
})
|
|
if serr != nil || pkg == nil {
|
|
return nil, srvdef.New(srvdef.ErrNoData, "会员套餐不存在", "")
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
func addUserCoins(ctx context.Context, userId int64, coin int64) *srvdef.SrvErr {
|
|
return usermod.UpdateOneUser(ctx,
|
|
bson.M{usermod.FN.User.Id: userId},
|
|
bson.M{
|
|
"$inc": bson.M{usermod.FN.User.Recharge: coin},
|
|
"$set": bson.M{usermod.FN.User.UpdatedAt: time.Now()},
|
|
},
|
|
)
|
|
}
|
|
|