379 lines
12 KiB
Go
379 lines
12 KiB
Go
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,
|
|
})
|
|
}
|