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 }