Files
web_huakui_server/web/service/srv_recharge/callback.go
T
rootandClaude Opus 4.7 e7179da072 feat: 同步 huangguo2-server 的 11 个非基线提交
按意图重做在 huakui 代码底:
- 删除数据埋点逻辑:移除 pub/dataReport、reportser、srv_report、
  eventmod、上报定时任务及注册/登录/点赞/收藏/评论/解锁/订单处的
  上报调用和 reportSdkConfig 配置下发
- app 认证改为按设备ID识别用户:去掉 token 校验、Register/Login/
  ResetPassword/GuestLogin 接口,新增 ResolveDeviceUser 和设备缓存
- 广告改为按 AppId 从媒资中心拉取:pub/storage 新增 QueryAdvitise,
  UpdateAdvitiseData 替代 UpdateAdvitiseDataFromJt
- 后台登录取消谷歌二次验证,密码正确即登录
- 短剧/专题 slug 改为 10 位大小写字母+数字混合
- 本地 app.yaml/web.yaml 保留 HKDJNET_* 模板(不提交生产凭据)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-16 12:59:14 +08:00

223 lines
7.2 KiB
Go

package srv_recharge
import (
"context"
"fmt"
"hkdj-web-server/pub/common/log"
"hkdj-web-server/pub/common/srvdef"
"hkdj-web-server/pub/models/cointransmod"
"hkdj-web-server/pub/models/rechargepackagemod"
"hkdj-web-server/pub/models/usermod"
"hkdj-web-server/pub/models/vipordermod"
"hkdj-web-server/pub/payment"
"hkdj-web-server/pub/pubdef"
"time"
"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
oid := param.Oid
switch order.OrderType {
case vipordermod.OrderTypeCoin:
go handleCoinOrderSuccess(orderSnap, oid, realAmount)
default:
go handleVipOrderSuccess(orderSnap, oid, realAmount)
}
return nil
}
func handleVipOrderSuccess(order vipordermod.VipOrder, oid string, realAmount int64) {
ctx := context.Background()
_, _ = oid, realAmount
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, oid string, realAmount int64) {
ctx := context.Background()
_, _ = oid, realAmount
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()},
},
)
}