需求:
花5豪币 点一次 每天点击上限次数 每人中奖上限
分享免费玩2次 分享只算一次成功 后面分享不增加次数
奖品表code id 类型 豪币 券 不中
概率可配置
每天凌晨清除0点击数
数据库表(完成)
奖品表:
ID
productId //存券ID定死
type //无奖品 豪币 卷
chance概率
createTime
createUser
//抽奖算法 (完成)
1、初始化奖池,插入抽奖奖品 sadd act:hash 奖品表ID:i
2、抽取奖品
3、获取判断
4、清空奖池
免费次数表://先查此表 有值且大于0先扣除并且不扣积分
ID
OPENID
TIMES
中奖记录表:
ID
OPENID
INBH
OUTHB
YEAR
MONTH
DAY
CREATETIME
接口:出入豪币接口、 查询剩余豪币数接口、优惠券接口.
属性配置文件
点击上限
花费豪币数
中奖上限
redis表:
用户击数//每天凌晨重置 //扣积分
redPacket:openid:click
每人中奖上限
redPacket:openid:limitWinHb
微信签名方法
@Path("/getSignature.do")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getSignatureMethod(@QueryParam("--") String xx){
String appId = RedPacketRainConfig.sub_appid;//不会变更
String code = appId + ";" + toUrl;//跳转链接即调用它的页面
String wxtk = "wxtk";
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyddMM");
String dateKey = format.format(date);
String key = wxtk + dateKey;
String enCode = CryptoUtil.desEncode(key, code);
HttpPost httpPost = null;
CloseableHttpResponse response = null;
CloseableHttpClient client = null;
JSONObject resultParams = null;
httpPost = new HttpPost("---");
StringEntity entityParams = new StringEntity(enCode, "utf-8");
httpPost.setEntity(entityParams);
httpPost.setHeader("Content-Type", "text/plain;charset=UTF-8");
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10 * 1000)
.setConnectionRequestTimeout(30 * 1000)
.build();
httpPost.setConfig(requestConfig);
client = HttpClients.createDefault();
try {
response = client.execute(httpPost);
if (response != null && response.getEntity() != null) {
String notifyURLParam = XmlUtils.parseStream(response.getEntity().getContent());
resultParams = JSON.parseObject(notifyURLParam);
resultParams.put("appid", appId);
}
} catch (IOException e) {
e.printStackTrace();
}
return resultParams.toJSONString();
}
代码实现:仅部分核心
@Path("/limit.do")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String isLimit(@QueryParam("openid") String openid) {
JSONObject object=new JSONObject();
if(openid==null||"".equals(openid)){
object.put("code","0");
object.put("msg","未获取openid,请联系管理员或刷新页面!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
try {
int freeTimes = 0;
ActRedpacketFreetimesModel actRedpackeFreetimesModel =actRedpacketFreetimesService.get(openid);
//不存在则说明未分享过
if(actRedpackeFreetimesModel!=null){
freeTimes=Integer.parseInt(actRedpackeFreetimesModel.getFreetimes());
}
if(JedisUtil.hget("redPacketTimes",openid)==null) {
JedisUtil.hset("redPacketTimes", openid, "0");
}
if(System.currentTimeMillis()>new SimpleDateFormat("yyyy-MM-dd").parse("2019-12-26").getTime()){
object.put("code","0");
object.put("msg","活动已到期不可请求!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
if(freeTimes>0) {
object.put("code","0");
object.put("msg","");
object.put("result","00");
return JsonUtil.toJsonStr(object);
}else {
int click = Integer.parseInt(JedisUtil.hget("redPacketTimes", openid));
//免费次数为0 并且点击数>=上限数 或者免费次数为空(未分享过) 并且点击数>=2
boolean clickIsLimit = (click >= RedPacketRainConfig.limit_click);
if ((freeTimes <= 0 && clickIsLimit)) {
object.put("code", "0");
object.put("msg", "已超出当日可玩次数!");
object.put("result", "01");
return JsonUtil.toJsonStr(object);
}
Float hb=0f;
hb = actRedpacketHBService.returnHb(openid);
if(hb<RedPacketRainConfig.cost_hb){
object.put("code","0");
object.put("msg","您的豪币不足!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
}
}catch (NotMemberException e){
object.put("code", "1");
object.put("msg", "非会员请先注册!");
object.put("result", "02");
return JsonUtil.toJsonStr(object);
}
catch (Exception e){
e.printStackTrace();
object.put("code", "1");
object.put("msg", "系统异常,请联系管理!");
object.put("result", "01");
return JsonUtil.toJsonStr(object);
}
object.put("code","0");
object.put("msg","");
object.put("result","00");
return JsonUtil.toJsonStr(object);
}
//红包雨核心算法接口
// 00 成功 01 失败提示! 02 服务器异常!
@Path("/code.do")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String redPacketCore(@QueryParam("openid") String openid) {
JSONObject object=new JSONObject();
if(openid==null||"".equals(openid)){
object.put("code","1");
object.put("msg","未获取openid,请联系管理员!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
try {
if(JedisUtil.hget("redPacketTimes",openid)==null) {
JedisUtil.hset("redPacketTimes", openid, "0");
}
if(System.currentTimeMillis()>new SimpleDateFormat("yyyy-MM-dd").parse("2019-12-26").getTime()){
object.put("code","0");
object.put("msg","活动已到期不可请求!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
} catch (ParseException e) {
e.printStackTrace();
}
String clientId= UUID.randomUUID().toString()+":"+openid;
long start =System.currentTimeMillis();
//开定时器每天晚上12点清空del redPacketTimes 公用的hash 存红包
//空为第一次玩 初始化值0
if(JedisUtil.hget("redPacketTimes",openid)==null) {
JedisUtil.hset("redPacketTimes", openid, "0");
}
if(JedisUtil.get("redPacketBigGiftTimes")==null) {//最大奖项当日上限制
JedisUtil.set("redPacketBigGiftTimes","2");
}
//获取免费次数。若为空也设置为0
int freeTimes = 0;
ActRedpacketFreetimesModel actRedpackeFreetimesModel =actRedpacketFreetimesService.get(openid);
//不存在则说明未分享过
if(actRedpackeFreetimesModel!=null){
freeTimes=Integer.parseInt(actRedpackeFreetimesModel.getFreetimes());
}
int click = Integer.parseInt(JedisUtil.hget("redPacketTimes",openid));
//免费次数为0 并且点击数>=上限数 或者免费次数为空(未分享过) 并且点击数>=2
boolean clickIsLimit=(click>= RedPacketRainConfig.limit_click);
if((freeTimes<=0&&clickIsLimit)){
object.put("code","0");
object.put("msg","已超出当日可玩次数!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
RLock redissonLock= redissonClient.getLock(clientId);
Boolean bs=false;
try {
bs=redissonLock.tryLock(3,10, TimeUnit.SECONDS);
logger.info("time:"+new Date()+"---openid:"+openid);
System.out.println("time:"+new Date()+"---openid"+openid);
//豪币余额接口
Float hb=actRedpacketHBService.returnHb(openid);
if(JedisUtil.get("isInit")==null) {
JedisUtil.set("isInit", "0");
}
if("0".equals(JedisUtil.get("isInit"))) {
System.out.println("初始化奖池");
JedisUtil.del("redPacketGift");
//初始化奖池
List<ActRedpacketGiftModel> giftList = actRedpacketGiftService.getAll();
//记录当前次数
int k = 0;
int stock = 0;
for (int i = 0; i < giftList.size(); i++) {
float chance = Float.parseFloat(giftList.get(i).getChance());
int time = (int) (chance * 10000);
//大奖,获取库存
if("2".equals(giftList.get(i).getWintype())){
//库存若为0则继续生成其他奖品
if(giftList.get(i).getStock()<=0){
continue;
}
}
//概率乘10000 加入redis奖池
for (int j = 0; j < time; j++) {
k++;
JedisUtil.sadd("redPacketGift", giftList.get(i).getId() + ":" + k);
}
}
JedisUtil.set("isInit","1");
}
//用户这次没有获取到锁,点击数小于10或免费次数大于0 可抽情况下,返回繁忙
//免费次数为0情况下
if(freeTimes<=0) {
//hb数小于需要消耗的豪币数
if(hb<RedPacketRainConfig.cost_hb){
object.put("code","0");
object.put("msg","您的豪币不足!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}
if (!bs && click < RedPacketRainConfig.limit_click) {
//没获取锁,点击数<10 不扣除豪币 不+次数 提示繁忙
object.put("code","0");
object.put("msg","服务繁忙,请稍后继续参与活动!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
} else if (!bs && click >= RedPacketRainConfig.limit_click) {
//没获取锁,点击数超限 扣除豪币
object.put("code","0");
object.put("msg","已超出当日可玩次数!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
//System.out.println("线程:{},获取锁失败,商品已卖完"+Thread.currentThread().getName());
} else {
actRedpacketHBService.updateHb(openid,"0",RedPacketRainConfig.cost_hb.toString());
System.out.println("扣除豪币:"+RedPacketRainConfig.cost_hb.toString());
click = Integer.parseInt(JedisUtil.hget("redPacketTimes", openid));
//-------------------------开始抽奖-------------------------------------
int realClick = click + 1;
JedisUtil.hset("redPacketTimes", openid, Integer.toString(realClick));
String wingift=JedisUtil.srandmember("redPacketGift");
//大奖限制逻辑
ActRedpacketWingiftLogModel actRedpacketWingiftLogModel=new ActRedpacketWingiftLogModel();
String[] strings=wingift.split(":");
System.out.println(strings[0]);
ActRedpacketGiftModel actRedpacketGiftModel=actRedpacketGiftService.get(strings[0]);
int x=bigRule(actRedpacketGiftModel,wingift,strings);
if(x==1){
//重新生成奖品
wingift=JedisUtil.srandmember("redPacketGift");
strings=wingift.split(":");
actRedpacketGiftModel=actRedpacketGiftService.get(strings[0]);
}
if("0".equals(actRedpacketGiftModel.getWintype())||"1".equals(actRedpacketGiftModel.getWintype())||"2".equals(actRedpacketGiftModel.getWintype())){
actRedpacketHBService.sendCpn(openid,actRedpacketGiftModel.getProductid());
}
System.out.println("线程:{" + Thread.currentThread().getName() + "},获取锁成功,锁状态:{" + bs + "},用户已点击数:{" + realClick + "}," + "奖池奖品信息:{"+wingift+ "}");
//大奖限制逻辑
actRedpacketWingiftLogModel.setCostBH(RedPacketRainConfig.cost_hb);
actRedpacketWingiftLogModel.setOpenId(openid);
actRedpacketWingiftLogModel.setPrizeName(actRedpacketGiftModel.getPrizename());
actRedpacketWingiftLogModel.setProductId(actRedpacketGiftModel.getProductid());
actRedpacketWingiftLogModel.setWinType(actRedpacketGiftModel.getWintype());
actRedpacketWingiftLogService.save(actRedpacketWingiftLogModel);
object.put("code","0");
object.put("msg","恭喜中奖");
object.put("result","00");
object.put("prizeName",actRedpacketGiftModel.getPrizename());
object.put("winType",actRedpacketGiftModel.getWintype());
//-------------------------抽奖结束----------------------------------
//处理业务代码需要的时间 取奖品表 抽奖 获取奖项 判断 若为豪币判断是否超限制 调用接口 清空奖池
}
}else{
//还有免费次数情况
if (!bs) {
object.put("code","0");
object.put("msg","服务繁忙,请稍后继续参与活动!");
object.put("result","01");
return JsonUtil.toJsonStr(object);
}else{//获取锁的其他情况 即可抽
String wingift=JedisUtil.srandmember("redPacketGift");
//大奖限制逻辑
ActRedpacketWingiftLogModel actRedpacketWingiftLogModel=new ActRedpacketWingiftLogModel();
String[] strings=wingift.split(":");
ActRedpacketGiftModel actRedpacketGiftModel=actRedpacketGiftService.get(strings[0]);
int x=bigRule(actRedpacketGiftModel,wingift,strings);
if(x==1){
//重新生成奖品
wingift=JedisUtil.srandmember("redPacketGift");
strings=wingift.split(":");
actRedpacketGiftModel=actRedpacketGiftService.get(strings[0]);
}
if("0".equals(actRedpacketGiftModel.getWintype())||"1".equals(actRedpacketGiftModel.getWintype())||"2".equals(actRedpacketGiftModel.getWintype())){
actRedpacketHBService.sendCpn(openid,actRedpacketGiftModel.getProductid());
}
//代码日志记录有误 花费0豪币
//actRedpacketWingiftLogModel.setCostBH(0f);
actRedpacketWingiftLogModel.setCostBH(RedPacketRainConfig.cost_hb);
actRedpacketWingiftLogModel.setOpenId(openid);
actRedpacketWingiftLogModel.setPrizeName(actRedpacketGiftModel.getPrizename());
actRedpacketWingiftLogModel.setProductId(actRedpacketGiftModel.getProductid());
actRedpacketWingiftLogModel.setWinType(actRedpacketGiftModel.getWintype());
actRedpacketWingiftLogService.save(actRedpacketWingiftLogModel);
System.out.println("线程:{" + Thread.currentThread().getName() + "},获取锁成功,锁状态:{" + bs + "}," + "奖池奖品信息:{"+wingift+ "}");
freeTimes=Integer.parseInt(actRedpackeFreetimesModel.getFreetimes());
int nowTimes = freeTimes - 1;
actRedpacketFreetimesService.updateTimes(Integer.toString(nowTimes),openid);
HashMap hashMap=new HashMap();
object.put("code","0");
object.put("msg","恭喜中奖");
object.put("result","00");
object.put("prizeName",actRedpacketGiftModel.getPrizename());
object.put("winType",actRedpacketGiftModel.getWintype());
//-------------------------抽奖结束----------------------------------
}
}
//非注册会员
}catch (NotMemberException e) {
e.printStackTrace();
object.put("msg","非注册会员");
object.put("code","1");
object.put("result","02");
logger.error("NotMemberException:"+e.getMessage());
//更新豪币失败
}catch (UpdateHBException e) {
e.printStackTrace();
object.put("msg","豪币扣除失败,请稍后重新参与活动");
object.put("code","1");
object.put("result","02");
logger.error("UpdateHBException:"+e.getMessage());
}catch (SendCpnException e) {
e.printStackTrace();
object.put("msg","发送优惠券异常");
object.put("code","1");
object.put("result","02");
logger.error("SendCpnException:"+e.getMessage());
}catch (Exception e) {
e.printStackTrace();
object.put("msg","服务器异常,请联系管理员!");
object.put("code","1");
object.put("result","02");
logger.error("Exception:"+e.getMessage());
} finally {
if(bs){
redissonLock.unlock();
}
}
long endTime =System.currentTimeMillis();
System.out.println("执行一次请求时间:"+(endTime-start)+"毫秒");
return JsonUtil.toJsonStr(object);
}
//分享获得免费次数接口
@Path("/ftimes.do")
@GET
@Produces(MediaType.APPLICATION_JSON)
public String shareFree(@QueryParam("openid") String openid){
JSONObject object=new JSONObject();
ActRedpacketFreetimesModel actRedpackeFreetimesModel =actRedpacketFreetimesService.get(openid);
//未分享过
if(actRedpackeFreetimesModel==null){
object.put("code","0");
object.put("msg","分享成功,获得2次游戏机会");
actRedpacketFreetimesService.insertTimes(IdUtil.getFullId(),openid,RedPacketRainConfig.free_times,new Date());
}else{
object.put("code","0");
object.put("msg","分享成功,已获得过2次机会");
}
return JsonUtil.toJsonStr(object);
}
//大奖规则 一天中奖上限2次
public int bigRule(ActRedpacketGiftModel actRedpacketGiftModel,String wingift,String[] strings){
int bigTimes=Integer.parseInt(JedisUtil.get("redPacketBigGiftTimes").toString());
if("2".equals(actRedpacketGiftModel.getWintype())){
//若上限次数小于等于0或者库存小于等于0,说明已达当日大奖中奖次数上限
int bigStock=actRedpacketGiftModel.getStock();
if(bigTimes<=0||bigStock<=0){
//若为2说明已经移除大奖重新初始化奖池 备用方案 非万分一概率情况
/*if(!"2".equals(JedisUtil.get("isInit"))) {
logger.info("移除大奖,初始化奖池");
JedisUtil.del("redPacketGift");
//初始化奖池
List<ActRedpacketGiftModel> giftList = actRedpacketGiftService.getAll();
//记录当前次数
int k = 0;
int stock = 0;
for (int i = 0; i < giftList.size(); i++) {
float chance = Float.parseFloat(giftList.get(i).getChance());
int time = (int) (chance * 10000);
//大奖,获取库存
if ("1".equals(giftList.get(i).getWintype())) {
//库存若为0则继续生成其他奖品
continue;
}
//概率乘10000 加入redis奖池
for (int j = 0; j < time; j++) {
k++;
JedisUtil.sadd("redPacketGift", giftList.get(i).getId() + ":" + k);
}
}
JedisUtil.set("isInit", "2");
}*/
//移除大奖
JedisUtil.srem("redPacketGift",wingift);
return 1;
}else{
//未大上限 扣除次数 扣除大奖库存
int realBigTimes=bigTimes-1;
JedisUtil.set("redPacketBigGiftTimes", Integer.toString(realBigTimes));
bigStock=bigStock-1;
actRedpacketGiftService.updateStock(strings[0],bigStock);
return 0;
}
}else{
return 0;
}
}