@@ -0,0 +1,573 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/proto"
|
||||
"91porn-server/app/service/activityclient"
|
||||
"91porn-server/app/service/customerser"
|
||||
"91porn-server/app/service/productser"
|
||||
"91porn-server/app/service/rechargeser"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/constant"
|
||||
"91porn-server/common/crypt"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/rchgutil"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/middleware/ua"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/advanceordermod"
|
||||
"91porn-server/models/v/currencymod"
|
||||
"91porn-server/models/v/productmod"
|
||||
"91porn-server/models/v/rchgamtmod"
|
||||
"91porn-server/models/v/sourcemod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// PayChannel 支付渠道(精简字段)
|
||||
type PayChannel struct {
|
||||
Type string `json:"type"`
|
||||
TypeName string `json:"typeName"`
|
||||
}
|
||||
|
||||
// ActivityCurrencyItem 活动服金币充值列表项
|
||||
type ActivityCurrencyItem struct {
|
||||
ID primitive.ObjectID `json:"id"`
|
||||
Coin int64 `json:"coin"`
|
||||
Money int64 `json:"money"`
|
||||
Name string `json:"typeName"`
|
||||
GiveCoin int64 `json:"giveCoin"`
|
||||
RechargeType []PayChannel `json:"rechargeType"`
|
||||
}
|
||||
|
||||
func toPayChannels(src []rchgamtmod.PayChannelRes) []PayChannel {
|
||||
channels := make([]PayChannel, 0, len(src))
|
||||
for _, ch := range src {
|
||||
channels = append(channels, PayChannel{
|
||||
Type: ch.Type,
|
||||
TypeName: ch.TypeName,
|
||||
})
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
// GetCurrencyList 获取金币充值列表
|
||||
func GetCurrencyList(ctx context.Context) ([]*ActivityCurrencyItem, stderr.Code) {
|
||||
currencys, err := currencymod.List(commod.Gold)
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "活动服-获取金币列表异常", log.E(err))
|
||||
return nil, stderr.ErrNetWorkBusy
|
||||
}
|
||||
|
||||
removeRepeat := make(map[int64]struct{})
|
||||
var moneys []string
|
||||
for _, cur := range currencys {
|
||||
if _, ok := removeRepeat[cur.Price]; ok {
|
||||
continue
|
||||
}
|
||||
removeRepeat[cur.Price] = struct{}{}
|
||||
moneys = append(moneys, rechargeser.FenToYuan(cur.Price))
|
||||
}
|
||||
|
||||
payReq := rchgutil.GainPayTypeReq{Money: moneys}
|
||||
bc, err := payReq.GetPayType()
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "活动服-获取支付通道列表异常", log.E(err))
|
||||
return nil, stderr.ErrNetWorkBusy
|
||||
}
|
||||
|
||||
data := make([]*ActivityCurrencyItem, 0, len(currencys))
|
||||
for _, v := range currencys {
|
||||
if v.Price < 100 {
|
||||
continue
|
||||
}
|
||||
rechargeType := rechargeser.GetPayChannelDetails_New(v.Price, bc, 0)
|
||||
if len(rechargeType) <= 0 {
|
||||
continue
|
||||
}
|
||||
data = append(data, &ActivityCurrencyItem{
|
||||
ID: v.ID,
|
||||
Coin: v.Coins,
|
||||
Money: v.Price,
|
||||
Name: v.Name,
|
||||
GiveCoin: v.GiveGold,
|
||||
RechargeType: toPayChannels(rechargeType),
|
||||
})
|
||||
}
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
// CardCategory 会员卡分类
|
||||
type CardCategory int
|
||||
|
||||
const (
|
||||
DefaultCard CardCategory = 0
|
||||
PreSaleCard CardCategory = 1
|
||||
NewbieCard CardCategory = 2
|
||||
)
|
||||
|
||||
// VipCardInfo 活动服会员卡信息
|
||||
type VipCardInfo struct {
|
||||
Id string `json:"id"`
|
||||
Category CardCategory `json:"category"`
|
||||
VipGrade string `json:"vipGrade"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
PreMoney int64 `json:"preMoney"`
|
||||
Money int64 `json:"money"`
|
||||
CanUseCoin bool `json:"canUseCoin"`
|
||||
DayCount int64 `json:"dayCount"`
|
||||
GiveCoin int64 `json:"giveCoin"`
|
||||
Sort int32 `json:"sort"`
|
||||
Image string `json:"image"`
|
||||
Rights []RightInfo `json:"rights"`
|
||||
RechargeType []PayChannel `json:"rechargeType"`
|
||||
ActivityStartAt time.Time `json:"activityStartAt"`
|
||||
ActivityEndAt time.Time `json:"activityEndAt"`
|
||||
CanUpgrade bool `json:"canUpgrade"`
|
||||
PreSaleStep int `json:"preSaleStep"` // 预售卡阶段 0-预付阶段 1-尾款阶段 2-尾款付款完成(结束)
|
||||
}
|
||||
|
||||
// RightInfo 权益信息
|
||||
type RightInfo struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
// VipDeduct 会员卡当前可用抵扣(vip/list 按抵扣后金额匹配支付通道用)
|
||||
type VipDeduct struct {
|
||||
ProductID string // 会员卡ID
|
||||
DeductAmount int64 // 券面额(分)
|
||||
}
|
||||
|
||||
// GetProductList 获取会员卡列表。
|
||||
// deducts 非空时,对携带抵扣的普通会员卡按"抵扣后有效金额"重新匹配支付通道(rechargeType),
|
||||
// money/preMoney 仍返回原价(避免与下单预览重复扣减)。
|
||||
func GetProductList(ctx context.Context, uid uint64, deducts []VipDeduct) ([]VipCardInfo, error) {
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("用户不存在: %d", uid)
|
||||
}
|
||||
|
||||
productList, err := rechargeser.New_ProductList(uid, u.SysType, true, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 抵扣券:按抵扣后有效金额预匹配支付通道(命中的卡覆盖 rechargeType)
|
||||
deductedChans := buildDeductedRechargeTypes(productList, deducts)
|
||||
|
||||
var cards []VipCardInfo
|
||||
for _, group := range productList {
|
||||
for _, item := range group.List {
|
||||
p := item.Product
|
||||
card := VipCardInfo{
|
||||
Id: p.ID.Hex(),
|
||||
VipGrade: fmt.Sprintf("%d", p.VipLevel),
|
||||
Name: p.Name,
|
||||
Desc: p.Desc,
|
||||
PreMoney: p.OriginalPrice * 10,
|
||||
Money: p.DiscountedPrice * 10,
|
||||
CanUseCoin: p.IsAmountPay,
|
||||
DayCount: int64(p.Duration),
|
||||
GiveCoin: p.GiveCoin,
|
||||
Sort: int32(p.Sort),
|
||||
Image: p.BGImg,
|
||||
CanUpgrade: p.IsUpgrade,
|
||||
}
|
||||
|
||||
switch p.ProductType {
|
||||
case commod.AdvanceCard:
|
||||
card.Category = PreSaleCard
|
||||
advOrder, _ := advanceordermod.IsExist(bson.M{"productID": p.ID, "uid": uid})
|
||||
// advOrder.Status 取值(advanceordermod):
|
||||
// 0 DEFAULT / 1 AdvanceProcessing(预付中) / 2 AdvanceSUCCESS(预付成功)
|
||||
// 3 BalanceProcessing(尾款中) / 4 BalanceSUCCESS(尾款成功)
|
||||
if advOrder == nil || advOrder.Status == advanceordermod.DEFAULT || advOrder.Status == advanceordermod.AdvanceProcessing {
|
||||
// 预付阶段:尚未下单 / 预付中
|
||||
card.PreSaleStep = 0
|
||||
card.ActivityStartAt = p.ActivityTime
|
||||
card.ActivityEndAt = p.EndTime
|
||||
card.Money = p.AdvanceAmount * 10
|
||||
} else if advOrder.Status == advanceordermod.AdvanceSUCCESS || advOrder.Status == advanceordermod.BalanceProcessing {
|
||||
// 尾款阶段:预付成功 / 尾款支付中
|
||||
card.PreSaleStep = 1
|
||||
card.ActivityStartAt = p.StartTime
|
||||
card.ActivityEndAt = p.EndTime
|
||||
card.Money = p.BalanceAmount * 10
|
||||
card.Image = p.NewBgImg
|
||||
} else if advOrder.Status == advanceordermod.BalanceSUCCESS {
|
||||
// 尾款付款完成(结束)
|
||||
card.PreSaleStep = 2
|
||||
}
|
||||
case commod.NEWUSERCard:
|
||||
card.Category = NewbieCard
|
||||
card.ActivityStartAt = u.CreatedAt
|
||||
card.ActivityEndAt = card.ActivityStartAt.Add(time.Duration(p.ShowCountdownTime) * time.Hour)
|
||||
if time.Now().After(card.ActivityEndAt) {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
card.Category = DefaultCard
|
||||
}
|
||||
|
||||
if card.Image == "" {
|
||||
card.Image = p.BGImg
|
||||
}
|
||||
|
||||
rights := make([]RightInfo, 0, len(p.NewPrivilege))
|
||||
for _, pr := range p.NewPrivilege {
|
||||
rights = append(rights, RightInfo{
|
||||
Name: pr.Name,
|
||||
Desc: pr.Desc,
|
||||
Image: pr.Image,
|
||||
})
|
||||
}
|
||||
card.Rights = rights
|
||||
card.RechargeType = toPayChannels(item.RechargeType)
|
||||
// 抵扣券:按抵扣后金额匹配的支付通道覆盖原价通道
|
||||
if chans, ok := deductedChans[p.ID.Hex()]; ok {
|
||||
card.RechargeType = chans
|
||||
}
|
||||
|
||||
cards = append(cards, card)
|
||||
}
|
||||
}
|
||||
return cards, nil
|
||||
}
|
||||
|
||||
// minDeductPayFen 抵扣后最低实付(分),与下单折价封顶保持一致
|
||||
const minDeductPayFen = 1
|
||||
|
||||
// buildDeductedRechargeTypes 依据 deducts 计算各会员卡"抵扣后有效金额"对应的支付通道。
|
||||
// 支付通道按支付金额匹配(如微信要求达标金额),券抵扣后实付变小可能改变可用通道,
|
||||
// 故对携带抵扣的普通会员卡(非预售卡)按 min(券面额, 原价-最低实付) 折后金额重新匹配 rechargeType。
|
||||
// 返回 productIdHex -> 支付通道;deducts 为空或无命中时返回 nil,调用方保留原价通道。
|
||||
func buildDeductedRechargeTypes(productList []proto.ProductList, deducts []VipDeduct) map[string][]PayChannel {
|
||||
deductMap := make(map[string]int64, len(deducts))
|
||||
for _, d := range deducts {
|
||||
if d.ProductID != "" && d.DeductAmount > 0 {
|
||||
deductMap[d.ProductID] = d.DeductAmount
|
||||
}
|
||||
}
|
||||
if len(deductMap) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type effEntry struct {
|
||||
hex string
|
||||
effFen int64
|
||||
}
|
||||
var entries []effEntry
|
||||
moneySet := make(map[string]struct{})
|
||||
var moneys []string
|
||||
for _, group := range productList {
|
||||
for _, item := range group.List {
|
||||
p := item.Product
|
||||
if p.ProductType == commod.AdvanceCard {
|
||||
continue // 预售卡分阶段付款,不参与抵扣
|
||||
}
|
||||
deduct, ok := deductMap[p.ID.Hex()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
originFen := p.DiscountedPrice * 10
|
||||
maxDeduct := originFen - minDeductPayFen
|
||||
if maxDeduct <= 0 {
|
||||
continue
|
||||
}
|
||||
if deduct > maxDeduct {
|
||||
deduct = maxDeduct
|
||||
}
|
||||
effFen := originFen - deduct
|
||||
entries = append(entries, effEntry{hex: p.ID.Hex(), effFen: effFen})
|
||||
y := rechargeser.FenToYuan(effFen)
|
||||
if _, exist := moneySet[y]; !exist {
|
||||
moneySet[y] = struct{}{}
|
||||
moneys = append(moneys, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bc, err := (&rchgutil.GainPayTypeReq{Money: moneys}).GetPayTypeFromCache()
|
||||
if err != nil {
|
||||
log.Warn("活动服-抵扣后支付通道查询失败", log.Any("moneys", moneys), log.E(err))
|
||||
}
|
||||
out := make(map[string][]PayChannel, len(entries))
|
||||
for _, e := range entries {
|
||||
out[e.hex] = toPayChannels(rechargeser.GetPayChannelDetails_New(e.effFen, bc, 0))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// UserBalance 用户余额信息
|
||||
type UserBalance struct {
|
||||
Gold int64 `json:"gold"` // 金币余额
|
||||
Integral int64 `json:"integral"` // 积分余额
|
||||
LotteryFreeTimes int64 `json:"lotteryFreeTimes"` // 抽奖免费次数
|
||||
VipLevel int `json:"vipLevel,omitempty"` // VIP 等级;用户未开通或已过期时不下发
|
||||
VipExpireDate time.Time `json:"vipExpireDate,omitempty"` // VIP 截止时间;用户未开通或已过期时不下发
|
||||
PromotionCode string `json:"promotionCode,omitempty"` // 邀请码
|
||||
PromoteURL string `json:"promoteURL,omitempty"` // 邀请链接(已拼好 PromotionField + 邀请码)
|
||||
}
|
||||
|
||||
// GetUserBalance 获取用户金币/积分余额及 VIP 信息(VIP 已过期时不下发 VIP 字段)
|
||||
func GetUserBalance(uid uint64) (*UserBalance, error) {
|
||||
w, err := walletmod.GetWallet(uid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询钱包异常: %w", err)
|
||||
}
|
||||
res := &UserBalance{}
|
||||
if w != nil {
|
||||
res.Gold = w.Amount
|
||||
res.Integral = w.Integral
|
||||
res.LotteryFreeTimes = w.LotteryTimes
|
||||
}
|
||||
// 用户信息独立取数,单点失败不影响余额下发
|
||||
u, uerr := usermod.FindUserByUID(uid)
|
||||
if uerr != nil {
|
||||
log.Warn("活动服-查询用户信息失败", log.Any("uid", uid), log.E(uerr))
|
||||
return res, nil
|
||||
}
|
||||
if u == nil {
|
||||
return res, nil
|
||||
}
|
||||
if u.IsVIP(time.Now()) {
|
||||
res.VipLevel = u.VipLevel
|
||||
res.VipExpireDate = u.VipExpireDate
|
||||
}
|
||||
// 邀请码 + 邀请链接(与 /api/app/mine/info 的 promoteURL 拼接方式一致)
|
||||
res.PromotionCode = u.PromCode
|
||||
if base := sourcemod.GetRandomPromotionURL(); base != "" && u.PromCode != "" {
|
||||
res.PromoteURL = common.BindUrl(base, constant.PromotionField+u.PromCode)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func convertBuyType(actBuyType int) commod.BuyType {
|
||||
switch actBuyType {
|
||||
case 1:
|
||||
return commod.BuyGold
|
||||
case 2:
|
||||
return commod.BuyProduct
|
||||
default:
|
||||
return commod.BuyType(actBuyType)
|
||||
}
|
||||
}
|
||||
|
||||
type RechargeAttribution struct {
|
||||
ActivityID string
|
||||
ExperimentID string
|
||||
ExperimentVariant string
|
||||
SessionID string
|
||||
}
|
||||
|
||||
// CreateRechargeOrder 创建充值订单。活动入口的 sourcePage 由服务端固定,
|
||||
// 避免调用方伪造其他来源;其余实验字段仍走统一下单校验。
|
||||
func CreateRechargeOrder(
|
||||
ctx context.Context,
|
||||
uid uint64,
|
||||
rechargeType, productID string,
|
||||
buyType int,
|
||||
ip string,
|
||||
attribution RechargeAttribution,
|
||||
couponID string,
|
||||
deductAmount int64,
|
||||
) (payUrl, mode string, err error) {
|
||||
pid, err := primitive.ObjectIDFromHex(productID)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("invalid productId: %w", err)
|
||||
}
|
||||
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return "", "", fmt.Errorf("用户不存在: %d", uid)
|
||||
}
|
||||
|
||||
realBuyType := convertBuyType(buyType)
|
||||
|
||||
in := &rechargeser.RechargeRequest{
|
||||
UID: uid,
|
||||
IP: ip,
|
||||
RechargeType: rechargeType,
|
||||
ProductID: pid,
|
||||
BuyType: realBuyType,
|
||||
SourcePage: rechargeser.OrderSourcePageH5Activity,
|
||||
SourceRef: attribution.ActivityID,
|
||||
ActivityID: attribution.ActivityID,
|
||||
ExperimentID: attribution.ExperimentID,
|
||||
ExperimentVariant: attribution.ExperimentVariant,
|
||||
SessionID: attribution.SessionID,
|
||||
}
|
||||
// 抵扣券作为活动服附加对象单独传入,不混入客户端下单请求体
|
||||
var deduct *rechargeser.ActivityDeduct
|
||||
if couponID != "" && deductAmount > 0 {
|
||||
deduct = &rechargeser.ActivityDeduct{CouponID: couponID, DeductAmount: deductAmount}
|
||||
}
|
||||
return rechargeser.Recharge(ctx, in, ua.UA{SysType: u.SysType}, deduct)
|
||||
}
|
||||
|
||||
// BuyCoinProduct 金币购买商品
|
||||
func BuyCoinProduct(ctx context.Context, uid uint64, productID string, sysType string) (stderr.Code, error) {
|
||||
pid, err := primitive.ObjectIDFromHex(productID)
|
||||
if err != nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("invalid productId: %w", err)
|
||||
}
|
||||
|
||||
p, err := productmod.FindProduct(pid, sysType)
|
||||
if err != nil || p == nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("商品不存在: %s", productID)
|
||||
}
|
||||
|
||||
if !p.IsAmountPay {
|
||||
return stderr.ErrParamError, fmt.Errorf("该商品不支持金币购买")
|
||||
}
|
||||
|
||||
code := productser.Buy(uid, p.ProductType, pid, primitive.NilObjectID, primitive.NilObjectID, 0, "", sysType, "", 0, false, ua.UA{SysType: sysType}, "", productser.VIPExperimentAttribution{})
|
||||
if code != stderr.Success {
|
||||
return code, fmt.Errorf("购买失败")
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
// GenerateSecretKey 生成 AES-256 密钥
|
||||
func GenerateSecretKey() (string, error) {
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(key), nil
|
||||
}
|
||||
|
||||
// DecryptSign AES-CBC 解密签名载荷
|
||||
func DecryptSign(secretKeyBase64 string, signBase64 string) (*activityclient.SignPayload, error) {
|
||||
secretKey, err := base64.StdEncoding.DecodeString(secretKeyBase64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode secretKey failed: %w", err)
|
||||
}
|
||||
signBytes, err := base64.URLEncoding.DecodeString(signBase64)
|
||||
if err != nil {
|
||||
signBytes, err = base64.StdEncoding.DecodeString(signBase64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode sign failed: %w", err)
|
||||
}
|
||||
}
|
||||
plaintext, err := crypt.CoreAesDecrypt(signBytes, string(secretKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypt sign failed: %w", err)
|
||||
}
|
||||
var payload activityclient.SignPayload
|
||||
if err := json.Unmarshal([]byte(plaintext), &payload); err != nil {
|
||||
return nil, fmt.Errorf("parse sign payload failed: %w", err)
|
||||
}
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
// HmacSHA256 用于回调签名
|
||||
func HmacSHA256(secretKeyBase64 string, data []byte) (string, error) {
|
||||
keyBytes, err := base64.StdEncoding.DecodeString(secretKeyBase64)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decode secretKey failed: %w", err)
|
||||
}
|
||||
h := hmac.New(sha256.New, keyBytes)
|
||||
h.Write(data)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// BuildSignedURL 生成带 sign 的活动服请求 URL
|
||||
func BuildSignedURL(userId, nickname, avatar string) (signedURL, sign string, err error) {
|
||||
conf := appg.Conf.ActivityServer
|
||||
baseURL := activityclient.GetActivityDomain()
|
||||
if baseURL == "" {
|
||||
return "", "", fmt.Errorf("活动服域名未获取,请检查配置")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
payload := &activityclient.SignPayload{
|
||||
AppId: conf.AppId,
|
||||
UserId: userId,
|
||||
Nickname: nickname,
|
||||
Avatar: avatar,
|
||||
Ts: now.Unix(),
|
||||
}
|
||||
|
||||
sign, err = activityclient.EncryptSign(conf.SecretKey, payload)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("encrypt sign failed: %w", err)
|
||||
}
|
||||
|
||||
signedURL = fmt.Sprintf("%s?appId=%s&sign=%s", baseURL, conf.AppId, sign)
|
||||
return signedURL, sign, nil
|
||||
}
|
||||
|
||||
// AppInfoResp 应用信息
|
||||
type AppInfoResp struct {
|
||||
CustomerServiceUrl string `json:"customerServiceUrl"`
|
||||
}
|
||||
|
||||
// GetAppInfo 获取应用信息
|
||||
func GetAppInfo(ctx context.Context, uid uint64) (*AppInfoResp, error) {
|
||||
resp, err := customerser.GetUrl(uid, ua.UA{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取客服地址失败: %w", err)
|
||||
}
|
||||
return &AppInfoResp{
|
||||
CustomerServiceUrl: resp.Data.Url,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuildLoginCurl 生成活动服登录接口的 curl 命令(调试用)
|
||||
func BuildLoginCurl(userId, nickname, avatar string) (string, error) {
|
||||
conf := appg.Conf.ActivityServer
|
||||
activityHost := activityclient.GetActivityDomain()
|
||||
if activityHost == "" {
|
||||
return "", fmt.Errorf("活动服域名未获取,请检查配置")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
payload := &activityclient.SignPayload{
|
||||
AppId: conf.AppId,
|
||||
UserId: userId,
|
||||
Nickname: nickname,
|
||||
Avatar: avatar,
|
||||
Ts: now.Unix(),
|
||||
}
|
||||
|
||||
sign, err := activityclient.EncryptSign(conf.SecretKey, payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("encrypt sign failed: %w", err)
|
||||
}
|
||||
|
||||
payloadJSON, _ := json.MarshalIndent(payload, "", " ")
|
||||
|
||||
result := fmt.Sprintf("AppId: %s\nSecretKey: %s\n\n", conf.AppId, conf.SecretKey)
|
||||
result += fmt.Sprintf("--- Generated Sign ---\nsign: %s\n\n", sign)
|
||||
result += fmt.Sprintf("payload: %s\n\n", string(payloadJSON))
|
||||
result += fmt.Sprintf("--- CURL ---\ncurl -X POST '%s/api/app/index/login' \\\n -H 'Content-Type: application/json' \\\n -d '{\"appId\":\"%s\",\"sign\":\"%s\"}'",
|
||||
activityHost, conf.AppId, sign)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
)
|
||||
|
||||
// DeductType 扣款类型
|
||||
type DeductType int
|
||||
|
||||
const (
|
||||
DeductGold DeductType = 1 // 金币
|
||||
DeductIntegral DeductType = 2 // 积分
|
||||
DeductLotteryTimes DeductType = 3 // 抽奖免费次数
|
||||
)
|
||||
|
||||
// DeductReq 扣款请求
|
||||
type DeductReq struct {
|
||||
UserId string `json:"userId" binding:"required"`
|
||||
DeductType DeductType `json:"deductType" binding:"required"`
|
||||
Amount int64 `json:"amount" binding:"required"`
|
||||
ActivityId string `json:"activityId"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// Deduct 活动扣款
|
||||
func Deduct(ctx context.Context, req *DeductReq) (stderr.Code, error) {
|
||||
uid, err := strconv.ParseUint(req.UserId, 10, 64)
|
||||
if err != nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("invalid userId: %s", req.UserId)
|
||||
}
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return stderr.Failure, fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("用户不存在: %s", req.UserId)
|
||||
}
|
||||
|
||||
if req.Amount <= 0 {
|
||||
return stderr.ErrParamError, fmt.Errorf("扣款数量必须大于0")
|
||||
}
|
||||
|
||||
w, err := walletmod.GetWallet(uid)
|
||||
if err != nil {
|
||||
return stderr.Failure, fmt.Errorf("查询钱包异常: %w", err)
|
||||
}
|
||||
if w == nil {
|
||||
w = &walletmod.Wallet{}
|
||||
}
|
||||
|
||||
desc := req.Remark
|
||||
|
||||
switch req.DeductType {
|
||||
case DeductGold:
|
||||
if w.Amount < req.Amount {
|
||||
return stderr.InsufficientGold, fmt.Errorf("金币余额不足,当前%d,需要%d", w.Amount, req.Amount)
|
||||
}
|
||||
return deductGold(ctx, uid, u, req, desc)
|
||||
case DeductIntegral:
|
||||
if w.Integral < req.Amount {
|
||||
return stderr.InsufficientPoint, fmt.Errorf("积分余额不足,当前%d,需要%d", w.Integral, req.Amount)
|
||||
}
|
||||
return deductIntegral(ctx, uid, u, req, desc)
|
||||
case DeductLotteryTimes:
|
||||
if w.LotteryTimes < req.Amount {
|
||||
return stderr.InsufficientLotteryFreeTimes, fmt.Errorf("抽奖免费次数不足,当前%d,需要%d", w.LotteryTimes, req.Amount)
|
||||
}
|
||||
return deductLotteryTimes(ctx, uid, u, req, desc)
|
||||
default:
|
||||
return stderr.ErrParamError, fmt.Errorf("不支持的扣款类型: %d", req.DeductType)
|
||||
}
|
||||
}
|
||||
|
||||
func deductGold(ctx context.Context, uid uint64, u *usermod.User, req *DeductReq, desc string) (stderr.Code, error) {
|
||||
amount := req.Amount
|
||||
|
||||
err := appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
w, err := walletmod.DebitAmount(t, amount, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动扣款-扣除金币", log.Any("uid", uid), log.Any("amount", amount), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: -amount,
|
||||
ActualAmount: float64(-amount),
|
||||
TranType: txnmod.ActivityDeductGold.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityDeductGold),
|
||||
Desc: fmt.Sprintf("%s-扣除%d金币", desc, amount),
|
||||
SysType: u.SysType,
|
||||
RealAmount: w.RealAmount(),
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return stderr.InsufficientGold, fmt.Errorf("金币余额不足")
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
func deductIntegral(ctx context.Context, uid uint64, u *usermod.User, req *DeductReq, desc string) (stderr.Code, error) {
|
||||
integral := req.Amount
|
||||
|
||||
err := appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
_, err := walletmod.DebitIntegral(t, integral, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动扣款-扣除积分", log.Any("uid", uid), log.Any("integral", integral), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Integral: -integral,
|
||||
TranType: txnmod.ActivityDeductIntegral.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityDeductIntegral),
|
||||
Desc: fmt.Sprintf("%s-扣除%d积分", desc, integral),
|
||||
SysType: u.SysType,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return stderr.InsufficientPoint, fmt.Errorf("积分余额不足")
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
func deductLotteryTimes(ctx context.Context, uid uint64, u *usermod.User, req *DeductReq, desc string) (stderr.Code, error) {
|
||||
times := req.Amount
|
||||
|
||||
err := appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
_, err := walletmod.DebitLotteryTimes(t, times, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动扣款-扣除抽奖免费次数", log.Any("uid", uid), log.Any("times", times), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: -times,
|
||||
TranType: txnmod.ActivityDeductLotteryTimes.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityDeductLotteryTimes),
|
||||
Desc: fmt.Sprintf("%s-扣除抽奖免费%d次", desc, times),
|
||||
SysType: u.SysType,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return stderr.InsufficientLotteryFreeTimes, fmt.Errorf("抽奖免费次数不足")
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/backpackmod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/videocoupon"
|
||||
"91porn-server/models/v/walletmod"
|
||||
)
|
||||
|
||||
// RewardType 奖励类型
|
||||
type RewardType int
|
||||
|
||||
const (
|
||||
RewardGold RewardType = 1 // 金币
|
||||
RewardVIP RewardType = 2 // 会员卡(VIP天数)
|
||||
RewardGoldBonusCoupon RewardType = 3 // 金币加赠券
|
||||
RewardGoldVideoCoupon RewardType = 4 // 金币观影券
|
||||
RewardAiChangeFaceFree RewardType = 5 // AI视频换脸免费次数
|
||||
RewardAiUndressFree RewardType = 6 // AI脱衣免费次数
|
||||
RewardIntegral RewardType = 7 // 积分
|
||||
RewardPhysical RewardType = 8 // 实物奖品
|
||||
)
|
||||
|
||||
// AddressInfo 收货地址信息
|
||||
type AddressInfo struct {
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// RewardReq 发奖请求
|
||||
type RewardReq struct {
|
||||
RequestId string `json:"requestId"`
|
||||
UserId string `json:"userId" binding:"required"`
|
||||
RewardType RewardType `json:"rewardType" binding:"required"`
|
||||
Amount int64 `json:"amount" binding:"required"`
|
||||
CouponValue int64 `json:"couponValue"`
|
||||
Duration int `json:"duration"`
|
||||
ActivityId string `json:"activityId"`
|
||||
RewardName string `json:"rewardName"`
|
||||
Remark string `json:"remark"`
|
||||
Address *AddressInfo `json:"address"`
|
||||
}
|
||||
|
||||
// RewardItem 批量发奖中的单个奖品
|
||||
type RewardItem struct {
|
||||
RequestId string `json:"requestId"`
|
||||
RewardType RewardType `json:"rewardType"`
|
||||
Amount int64 `json:"amount"`
|
||||
CouponValue int64 `json:"couponValue"`
|
||||
Duration int `json:"duration"`
|
||||
RewardName string `json:"rewardName"`
|
||||
Address *AddressInfo `json:"address"`
|
||||
}
|
||||
|
||||
// BatchRewardReq 批量发奖请求
|
||||
type BatchRewardReq struct {
|
||||
UserId string `json:"userId" binding:"required"`
|
||||
ActivityId string `json:"activityId"`
|
||||
Remark string `json:"remark"`
|
||||
Rewards []RewardItem `json:"rewards" binding:"required"`
|
||||
}
|
||||
|
||||
// GrantBatchReward 批量发放活动奖励
|
||||
func GrantBatchReward(ctx context.Context, req *BatchRewardReq) error {
|
||||
if len(req.Rewards) == 0 {
|
||||
return nil
|
||||
}
|
||||
uid, err := strconv.ParseUint(req.UserId, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid userId: %s", req.UserId)
|
||||
}
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return fmt.Errorf("用户不存在: %s", req.UserId)
|
||||
}
|
||||
return appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
for _, item := range req.Rewards {
|
||||
single := &RewardReq{
|
||||
RequestId: item.RequestId,
|
||||
UserId: req.UserId,
|
||||
RewardType: item.RewardType,
|
||||
Amount: item.Amount,
|
||||
CouponValue: item.CouponValue,
|
||||
Duration: item.Duration,
|
||||
ActivityId: req.ActivityId,
|
||||
RewardName: item.RewardName,
|
||||
Remark: req.Remark,
|
||||
Address: item.Address,
|
||||
}
|
||||
if err := grantRewardTx(ctx, t, uid, u, single); err != nil {
|
||||
log.ErrorX(ctx, "批量发奖-单项失败已跳过",
|
||||
log.Any("uid", req.UserId),
|
||||
log.Any("requestId", item.RequestId),
|
||||
log.Any("rewardType", item.RewardType),
|
||||
log.E(err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GrantReward 发放活动奖励(单个)
|
||||
func GrantReward(ctx context.Context, req *RewardReq) error {
|
||||
uid, err := strconv.ParseUint(req.UserId, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid userId: %s", req.UserId)
|
||||
}
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return fmt.Errorf("用户不存在: %s", req.UserId)
|
||||
}
|
||||
|
||||
return appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
||||
return grantRewardTx(ctx, t, uid, u, req)
|
||||
})
|
||||
}
|
||||
|
||||
func grantRewardTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq) error {
|
||||
desc := req.Remark
|
||||
|
||||
switch req.RewardType {
|
||||
case RewardGold:
|
||||
return grantGoldTx(ctx, t, uid, u, req, desc)
|
||||
case RewardVIP:
|
||||
return grantVIPTx(ctx, t, uid, u, req, desc)
|
||||
case RewardGoldBonusCoupon:
|
||||
return grantGoldBonusCouponTx(ctx, t, uid, u, req, desc)
|
||||
case RewardGoldVideoCoupon:
|
||||
return grantGoldVideoCouponTx(ctx, t, uid, u, req, desc)
|
||||
case RewardAiChangeFaceFree:
|
||||
return grantAiChangeFaceFreeTx(ctx, t, uid, u, req, desc)
|
||||
case RewardAiUndressFree:
|
||||
return grantAiUndressFreeTx(ctx, t, uid, u, req, desc)
|
||||
case RewardIntegral:
|
||||
return grantIntegralTx(ctx, t, uid, u, req, desc)
|
||||
case RewardPhysical:
|
||||
return grantPhysicalTx(ctx, t, uid, u, req, desc)
|
||||
default:
|
||||
return fmt.Errorf("不支持的奖励类型: %d", req.RewardType)
|
||||
}
|
||||
}
|
||||
|
||||
func grantGoldTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
amount := req.Amount
|
||||
creditPlan := walletmod.CreditPlan{Amount: &amount}
|
||||
|
||||
w, err := walletmod.Credit(t, creditPlan, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放金币", log.Any("uid", uid), log.Any("amount", amount), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: amount,
|
||||
ActualAmount: float64(amount),
|
||||
TranType: txnmod.ActivityRewardGold.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardGold),
|
||||
Desc: fmt.Sprintf("%s-发放%d金币", desc, amount),
|
||||
SysType: u.SysType,
|
||||
RealAmount: w.RealAmount(),
|
||||
UniqueOrder: req.RequestId,
|
||||
})
|
||||
}
|
||||
|
||||
func grantVIPTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
days := int(req.Amount)
|
||||
if days <= 0 {
|
||||
return errors.New("VIP天数必须大于0")
|
||||
}
|
||||
|
||||
var expire time.Time
|
||||
if u.VipExpireDate.After(time.Now()) {
|
||||
expire = u.VipExpireDate.AddDate(0, 0, days)
|
||||
} else {
|
||||
expire = time.Now().AddDate(0, 0, days)
|
||||
}
|
||||
|
||||
vipLevel := 1
|
||||
if u.VipLevel > vipLevel {
|
||||
vipLevel = u.VipLevel
|
||||
}
|
||||
|
||||
sel := usermod.UserSelector{
|
||||
VipExpireDate: &expire,
|
||||
VipLevel: &vipLevel,
|
||||
}
|
||||
if err := usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放VIP", log.Any("uid", uid), log.Any("days", days), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
TranType: txnmod.ActivityRewardVIP.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardVIP),
|
||||
Desc: fmt.Sprintf("%s-发放VIP%d天", desc, days),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
})
|
||||
}
|
||||
|
||||
func grantGoldBonusCouponTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
if req.CouponValue <= 0 {
|
||||
return errors.New("券额度(couponValue)必须大于0")
|
||||
}
|
||||
|
||||
if err := txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: req.Amount,
|
||||
TranType: txnmod.ActivityRewardGoldBonusCoupon.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardGoldBonusCoupon),
|
||||
Desc: fmt.Sprintf("%s-发放金币加赠券%d张", desc, req.Amount),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
expiredAt := now.AddDate(0, 0, 30)
|
||||
for i := int64(0); i < req.Amount; i++ {
|
||||
item := backpackmod.Backpack{
|
||||
UID: uid,
|
||||
GoodsType: backpackmod.GoldBonusCoupon,
|
||||
GoodsValue: req.CouponValue,
|
||||
GoodsOrigin: desc,
|
||||
Status: backpackmod.Unused,
|
||||
ExpiredTime: expiredAt,
|
||||
CreateTime: now,
|
||||
}
|
||||
if err := backpackmod.AddGoods(t, uid, item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放金币加赠券", log.Any("uid", uid), log.Any("couponValue", req.CouponValue), log.Any("amount", req.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
func grantGoldVideoCouponTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
if req.CouponValue <= 0 {
|
||||
return errors.New("券额度(couponValue)必须大于0")
|
||||
}
|
||||
|
||||
if err := txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: req.Amount,
|
||||
TranType: txnmod.ActivityRewardGoldVideoCoupon.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardGoldVideoCoupon),
|
||||
Desc: fmt.Sprintf("%s-发放金币观影券%d张", desc, req.Amount),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
coupons := make([]videocoupon.UserGoldVideoCoupon, 0, req.Amount)
|
||||
for i := int64(0); i < req.Amount; i++ {
|
||||
coupons = append(coupons, videocoupon.UserGoldVideoCoupon{
|
||||
UID: uid,
|
||||
Num: int(req.CouponValue),
|
||||
Used: false,
|
||||
Source: videocoupon.GoldVideoCouponSource(desc),
|
||||
})
|
||||
}
|
||||
if err := videocoupon.InsertManyTrans(t, coupons); err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放金币观影券", log.Any("uid", uid), log.Any("couponValue", req.CouponValue), log.Any("amount", req.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
func grantAiChangeFaceFreeTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
if err := txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: req.Amount,
|
||||
TranType: txnmod.ActivityRewardAiChangeFaceFree.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardAiChangeFaceFree),
|
||||
Desc: fmt.Sprintf("%s-发放AI换脸免费%d次", desc, req.Amount),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
permanent := time.Date(2099, 12, 31, 23, 59, 59, 0, time.Local)
|
||||
item := backpackmod.Backpack{
|
||||
UID: uid,
|
||||
GoodsType: backpackmod.AiChangeFaceDiscount,
|
||||
GoodsValue: req.Amount,
|
||||
GoodsOrigin: desc,
|
||||
Status: backpackmod.Unused,
|
||||
ExpiredTime: permanent,
|
||||
CreateTime: now,
|
||||
}
|
||||
if err := backpackmod.AddGoods(t, uid, item); err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放AI换脸免费次数", log.Any("uid", uid), log.Any("times", req.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
func grantIntegralTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
integral := req.Amount
|
||||
|
||||
_, err := walletmod.CreditIntegral(t, integral, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放积分", log.Any("uid", uid), log.Any("integral", integral), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: integral,
|
||||
TranType: txnmod.ActivityRewardIntegral.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardIntegral),
|
||||
Desc: fmt.Sprintf("%s-发放%d积分", desc, integral),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
})
|
||||
}
|
||||
|
||||
func grantAiUndressFreeTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
times := req.Amount
|
||||
creditPlan := walletmod.CreditPlan{AiUndressFreeTimes: ×}
|
||||
|
||||
_, err := walletmod.Credit(t, creditPlan, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.InfoX(ctx, "活动发放-发放AI脱衣免费次数", log.Any("uid", uid), log.Any("times", times), log.Any("activityId", req.ActivityId))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: times,
|
||||
TranType: txnmod.ActivityRewardAiUndressFree.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardAiUndressFree),
|
||||
Desc: fmt.Sprintf("%s-发放AI脱衣免费%d次", desc, times),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
})
|
||||
}
|
||||
|
||||
func grantPhysicalTx(ctx context.Context, t *db.MongoTool, uid uint64, u *usermod.User, req *RewardReq, desc string) error {
|
||||
addrDesc := ""
|
||||
if req.Address != nil {
|
||||
addrDesc = fmt.Sprintf("【 收件人:%s 手机:%s 地址:%s】", req.Address.Name, req.Address.Phone, req.Address.Address)
|
||||
}
|
||||
rewardName := req.RewardName
|
||||
if rewardName == "" {
|
||||
rewardName = "实物奖品"
|
||||
}
|
||||
|
||||
log.InfoX(ctx, "活动发放-发放实物奖品", log.Any("uid", uid), log.Any("rewardName", rewardName), log.Any("activityId", req.ActivityId), log.Any("address", addrDesc))
|
||||
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
||||
UID: uid,
|
||||
Amount: req.Amount,
|
||||
TranType: txnmod.ActivityRewardPhysical.Key(),
|
||||
TranTypeInt: int64(txnmod.ActivityRewardPhysical),
|
||||
Desc: fmt.Sprintf("%s-发放%s%s", desc, rewardName, addrDesc),
|
||||
SysType: u.SysType,
|
||||
UniqueOrder: req.RequestId,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user