245 lines
7.5 KiB
Go
245 lines
7.5 KiB
Go
package activitySer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/rand"
|
|
"strconv"
|
|
"time"
|
|
|
|
"stat/api/proto"
|
|
"stat/common/log"
|
|
"stat/common/mongos"
|
|
"stat/common/redis"
|
|
"stat/common/stdLog"
|
|
"stat/common/utils"
|
|
"stat/models/activitymod"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var ipDrawLimit = 20 // 同一IP每天抽奖限制
|
|
|
|
// H5活动详情
|
|
func GetActivityInfo(ctx context.Context, param proto.ActivityInfoReq) (stdLog.CodeType, *proto.ActivityInfoResp, error) {
|
|
now := time.Now()
|
|
res := &proto.ActivityInfoResp{}
|
|
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
|
|
|
data, err := activitymod.FindOneActivityInfo(ctx, filter)
|
|
if err != nil {
|
|
return stdLog.QueryDataError, res, err
|
|
}
|
|
if data == nil {
|
|
return stdLog.ActivityNoExistErr, res, fmt.Errorf("data does not exist")
|
|
}
|
|
if now.Before(data.StartTime) || now.After(data.EndTime) || !data.Enable {
|
|
return stdLog.ActivityNoExistErr, res, fmt.Errorf("activity is closed")
|
|
}
|
|
res = &proto.ActivityInfoResp{
|
|
Id: data.Id,
|
|
Name: data.Name,
|
|
Desc: data.Desc,
|
|
H5Domain: data.H5Domain,
|
|
GiftDesc: data.GiftDesc,
|
|
Enable: data.Enable,
|
|
StartTime: data.StartTime,
|
|
EndTime: data.EndTime,
|
|
Banner: data.Banner,
|
|
}
|
|
return stdLog.Success, res, nil
|
|
}
|
|
|
|
// H5页面奖品信息
|
|
func GetActivityGiftInfo(ctx context.Context, param proto.ActivityGiftInfoReq) (stdLog.CodeType, *proto.ActivityGiftInfoResp, error) {
|
|
data := &proto.ActivityGiftInfoResp{List: make([]*proto.H5GiftInfo, 0)}
|
|
filter := bson.M{activitymod.FN.LotteryGift.ActId: param.ActId}
|
|
count, err := activitymod.CountLotteryGifts(ctx, filter)
|
|
if err != nil {
|
|
return stdLog.QueryDataError, data, err
|
|
}
|
|
data.Count = count
|
|
|
|
opt := &options.FindOptions{}
|
|
opt.SetSort(bson.M{activitymod.FN.LotteryGift.Sort: 1})
|
|
if err := activitymod.FindManyLotteryGiftWithBind(ctx, &data.List, filter, opt); err != nil {
|
|
return stdLog.QueryDataError, data, err
|
|
}
|
|
return stdLog.Success, data, nil
|
|
}
|
|
|
|
// 抽奖
|
|
func Draw(ctx context.Context, param *proto.DrawReq) (stdLog.CodeType, *proto.DrawResp, error) {
|
|
res := &proto.DrawResp{}
|
|
now := time.Now()
|
|
|
|
// 活动是否存在
|
|
actFilter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
|
data, err := activitymod.FindOneActivityInfo(ctx, actFilter)
|
|
if err != nil {
|
|
return stdLog.QueryDataError, res, err
|
|
}
|
|
if data == nil {
|
|
return stdLog.ActivityNoExistErr, res, fmt.Errorf("data does not exist")
|
|
}
|
|
if now.Before(data.StartTime) || now.After(data.EndTime) || !data.Enable {
|
|
return stdLog.ActivityNoExistErr, res, fmt.Errorf("activity is closed")
|
|
}
|
|
|
|
// 抽奖码是否有效
|
|
lcFilter := bson.M{
|
|
activitymod.FN.LottertyCodeInfo.LotteryCode: param.LotteryCode,
|
|
activitymod.FN.LottertyCodeInfo.ActId: param.ActId.Hex(),
|
|
}
|
|
lcInfo, err := activitymod.FindOneLottertyCodeInfo(ctx, lcFilter)
|
|
if err != nil || lcInfo == nil {
|
|
return stdLog.LotteryCodeIsInvalid, res, fmt.Errorf("lottery code is invalid")
|
|
}
|
|
if lcInfo.IsUsed {
|
|
return stdLog.LotteryCodeIsUsed, res, fmt.Errorf("lottery code is used")
|
|
}
|
|
|
|
// 根据用户IP限制抽奖次数
|
|
if !CanUserDraw(param.Ip, param.ActId.Hex()) {
|
|
return stdLog.LotteryNumReachedTheLimit, res, fmt.Errorf("the number of lottery reached the limit")
|
|
}
|
|
|
|
// 抽奖
|
|
gFilter := bson.M{activitymod.FN.LotteryGift.ActId: param.ActId.Hex()}
|
|
opt := &options.FindOptions{}
|
|
opt.SetSort(bson.M{activitymod.FN.LotteryGift.Rate: -1})
|
|
giftList, err := activitymod.FindManyLotteryGift(ctx, gFilter, opt)
|
|
if err != nil {
|
|
return stdLog.QueryDataError, res, fmt.Errorf("lottery gift data err")
|
|
}
|
|
var giftRand int32
|
|
for _, v := range giftList {
|
|
giftRand += v.Rate
|
|
}
|
|
|
|
flag := true
|
|
for {
|
|
randNum := rand.Int31n(giftRand)
|
|
// fmt.Printf("\ngiftRand is %d at 1st\n", giftRand)
|
|
// fmt.Printf("\nrandNum is %d at 1st\n", randNum)
|
|
for _, w := range giftList {
|
|
// fmt.Printf("randNum is %d before minus\n", randNum)
|
|
// fmt.Printf("\ngiftRand is %d before minus\n", giftRand)
|
|
randNum -= w.Rate
|
|
giftRand -= w.Rate
|
|
// fmt.Printf("\nAfter minus randNum is %d\nw.Rate is %d\ngift info %+v\n", randNum, w.Rate, w)
|
|
// fmt.Printf("\nAfter minus giftRand is %d\n", giftRand)
|
|
if randNum <= 0 {
|
|
var notgaveNum int64 // total 每种奖品的总数量, givedNum 每种奖品未被抽中的数量
|
|
gFilter := bson.M{
|
|
activitymod.FN.LotteryGetGift.GiftId: w.Id.Hex(),
|
|
activitymod.FN.LotteryGetGift.ActId: w.ActId,
|
|
activitymod.FN.LotteryGetGift.LotteryCode: "",
|
|
}
|
|
info, _ := activitymod.FindManyLotteryGetGift(ctx, gFilter)
|
|
notgaveNum = int64(len(info))
|
|
if notgaveNum == 0 {
|
|
randNum = rand.Int31n(giftRand + 1)
|
|
continue
|
|
}
|
|
res.GiftId = w.Id.Hex()
|
|
res.GiftName = w.GiftName
|
|
res.GiftCode = info[0].GiftCode
|
|
flag = false
|
|
break
|
|
}
|
|
}
|
|
if !flag {
|
|
break
|
|
}
|
|
}
|
|
|
|
fn := func(sctx mongo.SessionContext) error {
|
|
// 更新lotterycodeinfo
|
|
lciFilter := bson.M{
|
|
activitymod.FN.LottertyCodeInfo.LotteryCode: param.LotteryCode,
|
|
activitymod.FN.LottertyCodeInfo.ActId: param.ActId.Hex(),
|
|
}
|
|
lciUpdate := bson.M{"$set": bson.M{
|
|
activitymod.FN.LottertyCodeInfo.UserIp: param.Ip,
|
|
activitymod.FN.LottertyCodeInfo.LotteryAt: now,
|
|
activitymod.FN.LottertyCodeInfo.UpdatedAt: now,
|
|
activitymod.FN.LottertyCodeInfo.IsUsed: true,
|
|
activitymod.FN.LottertyCodeInfo.GiftCode: res.GiftCode,
|
|
activitymod.FN.LottertyCodeInfo.Remark: "已使用",
|
|
}}
|
|
err := activitymod.UpdateOneLottertyCodeInfo(sctx, lciFilter, lciUpdate)
|
|
if err != nil {
|
|
log.Warn("Draw: UpdateOneLottertyCodeInfo err", log.Any("err", err))
|
|
return err
|
|
}
|
|
|
|
// 更新lotterygetgift
|
|
lggFilter := bson.M{
|
|
activitymod.FN.LotteryGetGift.GiftCode: res.GiftCode,
|
|
activitymod.FN.LotteryGetGift.ActId: param.ActId.Hex(),
|
|
}
|
|
lggUpdate := bson.M{"$set": bson.M{
|
|
activitymod.FN.LotteryGetGift.UpdatedAt: now,
|
|
activitymod.FN.LotteryGetGift.LotteryCode: param.LotteryCode,
|
|
activitymod.FN.LotteryGetGift.IsGaveOut: true,
|
|
activitymod.FN.LotteryGetGift.Remark: "兑换码已发放",
|
|
}}
|
|
err = activitymod.UpdateOneLotteryGetGift(sctx, lggFilter, lggUpdate)
|
|
if err != nil {
|
|
log.Error("Draw: UpdateOneLotteryGetGift err", log.Any("err", err))
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if err := mongos.Transaction(ctx, fn); err != nil {
|
|
return stdLog.Failure, &proto.DrawResp{}, err
|
|
}
|
|
return stdLog.Success, res, nil
|
|
}
|
|
|
|
// 验证用户Ip是否能抽奖
|
|
func CanUserDraw(ip, actId string) bool {
|
|
redisKey := fmt.Sprintf("actId:%s:userIp:%s", actId, ip)
|
|
numStr, err := redis.StatRds.Get(redisKey)
|
|
if err != nil {
|
|
log.Error("canUserDraw redis get err", log.E(err))
|
|
return false
|
|
}
|
|
var num = int64(0)
|
|
if numStr != nil {
|
|
num, err = strconv.ParseInt(*numStr, 10, 64)
|
|
if err != nil {
|
|
log.Error("canUserDraw str to int64 err", log.E(err))
|
|
return false
|
|
}
|
|
}
|
|
if num > int64(ipDrawLimit) {
|
|
log.Warn("canUserDraw user IP triggered the limit")
|
|
return false
|
|
}
|
|
val, err := redis.StatRds.Incr(redisKey)
|
|
if err != nil {
|
|
log.Error("canUserDraw redis get err", log.E(err))
|
|
return false
|
|
}
|
|
if val == 1 {
|
|
duration := redis.StatRds.TTL(redisKey)
|
|
if duration == time.Duration(time.Second*-1) {
|
|
expiredAt := utils.EndOfToday().UTC().Sub(time.Now().UTC())
|
|
// expiredAt = expiredAt - (time.Second * 36200)
|
|
// str := expiredAt.String()
|
|
// fmt.Printf("\nexpiredAt is %d\n", expiredAt)
|
|
// fmt.Printf("\nstr is %s\n", str)
|
|
redis.StatRds.Expire(redisKey, expiredAt)
|
|
}
|
|
}
|
|
if val > int64(ipDrawLimit) {
|
|
log.Warn("canUserDraw user IP triggered the limit")
|
|
return false
|
|
}
|
|
return true
|
|
}
|