本文是针对jquery 实现抽奖转盘作者的一个补充(主要用java去实现转盘结果生成及存储,解决jquery 做法 非法用户采用模拟器实现改变转盘值的风险性),针对jQuery的具体实现,请看案例:http://www.cnblogs.com/mofish/archive/2013/01/24/2875516.html 本文就不一一细说了,那么现在就直入正题。
由于公司产品推广,最近要求实现一个邀请用户注册即可抽奖的转盘,页面展示如下:
java 实现方式如下:
构造实体类
WchatLotteryDomain.java
1 package com.cy.dcts.domain.activity; 2 3 import java.io.Serializable; 4 5 /** 6 * 微信用户分享中奖基础数据类 7 * @author yanst 2016/4/23 9:36 8 */ 9 public class WchatLotteryDomain implements Serializable{ 10 private static final long serialVersionUID = -1595371216905016135L; 11 12 private Integer id; 13 14 //中奖金额 15 private String prize; 16 17 //中奖率 18 private Integer v; 19 20 public WchatLotteryDomain(Integer id, String prize, Integer v){ 21 this.id = id; 22 this.prize = prize; 23 this.v = v; 24 } 25 26 public Integer getId() { 27 return id; 28 } 29 30 public void setId(Integer id) { 31 this.id = id; 32 } 33 34 public String getPrize() { 35 return prize; 36 } 37 38 public void setPrize(String prize) { 39 this.prize = prize; 40 } 41 42 public Integer getV() { 43 return v; 44 } 45 46 public void setV(Integer v) { 47 this.v = v; 48 } 49 }
抽奖算法实现类:
1.初始数据集合:initDrawList 。
2.generateAward方法实现根据概率随机生成中奖对象WchatLotteryDomain
BigWheelDrawUtil.java
1 package com.cy.dcts.common.util; 2 3 import com.alibaba.fastjson.JSON; 4 import com.cy.dcts.domain.activity.WchatLotteryDomain; 5 6 import java.util.ArrayList; 7 import java.util.List; 8 9 /** 10 * 11 * wchat大转盘抽奖活动 12 * 13 * @author yanst 2016/4/23 9:23 14 */ 15 public class BigWheelDrawUtil { 16 17 18 /** 19 * 给转盘的每个角度赋初始值 20 * @return 21 */ 22 private final static List<WchatLotteryDomain> initDrawList = new ArrayList<WchatLotteryDomain>() {{ 23 add(new WchatLotteryDomain(1, "200", 1)); 24 add(new WchatLotteryDomain(2, "100", 3)); 25 add(new WchatLotteryDomain(3, "50", 30)); 26 add(new WchatLotteryDomain(4, "30", 30)); 27 add(new WchatLotteryDomain(5, "20", 26)); 28 add(new WchatLotteryDomain(6, "10", 10)); 29 }}; 30 31 /** 32 * 生成奖项 33 * @return 34 */ 35 public static WchatLotteryDomain generateAward() { 36 List<WchatLotteryDomain> initData = initDrawList; 37 long result = randomnum(1, 100); 38 int line = 0; 39 int temp = 0; 40 WchatLotteryDomain returnobj = null; 41 int index = 0; 42 for (int i = 0; i < initDrawList.size(); i++) { 43 WchatLotteryDomain obj2 = initDrawList.get(i); 44 int c = obj2.getV(); 45 temp = temp + c; 46 line = 100 - temp; 47 if (c != 0) { 48 if (result > line && result <= (line + c)) { 49 returnobj = obj2; 50 break; 51 } 52 } 53 } 54 return returnobj; 55 } 56 57 // 获取2个值之间的随机数 58 private static long randomnum(int smin, int smax){ 59 int range = smax - smin; 60 double rand = Math.random(); 61 return (smin + Math.round(rand * range)); 62 } 63 64 65 public static void main(String[] args) { 66 System.out.println(JSON.toJSONString(generateAward())); 67 } 68 69 }
controller 层 实现 显示抽奖的结果给页面,页面启动转盘,把对应的中间角度显示给用户看,同时把中间金额保存到系统中。
1.调用util类返回中奖项
//生成中奖金额对象 WchatLotteryDomain wchatLotteryDomain = BigWheelDrawUtil.generateAward();
2.修改抽奖次数
//修改抽奖次数 Integer result = appShareService.markLuckDraw(id);
3.把中奖信息持久化
//写入中奖信息 writeXinRecord(mobile, wchatLotteryDomain);
4.把当前中奖信息及剩余中奖次数返回
//代码略,参考ActivityAction.java 107、108行
ActivityAction.java
1 /** 2 * 抽奖 3 * 4 * @param id id 5 * @param mobile 中奖号码 6 * @return 7 */ 8 @RequestMapping("wXinMarkLuckDraw.jspx") 9 @ResponseBody 10 public JSonRespone markLuckDraw(Long id, String mobile) { 11 //参数验证 12 if (id == null || id.longValue() == 0) { 13 return JSonRespone.makeHasContentJSonRespone("1", "您没有抽奖次数!"); 14 } 15 //参数验证 16 if (StringUtils.isEmpty(mobile)) { 17 return JSonRespone.makeHasContentJSonRespone("1", "中奖手机号码为空!"); 18 } 19 20 //生成中奖金额对象 21 WchatLotteryDomain wchatLotteryDomain = BigWheelDrawUtil.generateAward(); 22 if(wchatLotteryDomain == null){ 23 return JSonRespone.makeHasContentJSonRespone("3", "生成抽奖数据失败"); 24 } 25 try { 26 //修改抽奖次数 27 Integer result = appShareService.markLuckDraw(id); 28 if (result == null || result == 0) { 29 return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。"); 30 } 31 } catch (Exception e) { 32 logger.debug(e.getMessage()); 33 return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。"); 34 } 35 36 if(logger.isErrorEnabled()){ 37 logger.error("微信分享活动:手机号码为:{},中奖信息:{}", mobile, JSON.toJSONString(wchatLotteryDomain)); 38 } 39 40 //写入中间信息 41 return writeXinRecord(mobile, wchatLotteryDomain); 42 } 43 44 // 微信 用户分享 认证之后送话费活动 中奖记录存储路径 45 private static final String wXinFilePath = "/home/wxhb/lottery.txt"; 46 //"/home/wxhb/lottery.txt"; 47 //"D:/list.txt"; 48 49 50 /** 51 * 写入中奖金额 52 * @param mobile 53 * @param wchatLotteryDomain 54 * @return 55 */ 56 private JSonRespone writeXinRecord(String mobile,WchatLotteryDomain wchatLotteryDomain ) { 57 // 记录时间 58 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 59 Calendar calendar = Calendar.getInstance(); 60 String date = simpleDateFormat.format(calendar.getTime()); 61 // 记录文件是否存在 62 File file = new File(wXinFilePath); 63 if (!file.exists()) { 64 try { 65 file.createNewFile(); 66 } catch (IOException e) { 67 e.printStackTrace(); 68 } 69 } 70 // 临时记录存储 71 ArrayList<String> arrayList = new ArrayList<>(); 72 // 是否已经存在记录 73 Scanner in = null; 74 try { 75 in = new Scanner(file); 76 } catch (FileNotFoundException e) { 77 e.printStackTrace(); 78 } 79 // 读取记录放置临时数组 80 while (in.hasNextLine()) { 81 arrayList.add(in.nextLine()); 82 } 83 in.close(); 84 // 查询记录是否存在 85 if (arrayList.size() > 0) { 86 for (String str : arrayList) { 87 if (mobile.equals(str.split("-")[0])) { 88 return JSonRespone.makeHasContentJSonRespone("1", "成功", "记录已存在"); 89 } 90 } 91 } 92 // 写入记录 93 BufferedWriter out = null; 94 try { 95 out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true))); 96 out.write(mobile + " " + date + " " + wchatLotteryDomain.getPrize()); 97 out.newLine(); 98 out.close(); 99 } catch (IOException e) { 100 e.printStackTrace(); 101 return JSonRespone.makeHasContentJSonRespone("0", "失败", e.getMessage()); 102 } 103 104 Map<String, Object> resultMap = new HashMap<String, Object>(); 105 try { 106 //获取抽奖次数 107 resultMap.put("luckDrawCounts", appShareService.getLuckDrawCounts(mobile));//抽奖次数 108 resultMap.put("wchatLotteryDomain", wchatLotteryDomain); 109 } catch (Exception e) { 110 logger.debug(e.getMessage()); 111 } 112 return JSonRespone.makeHasContentJSonRespone("0", "成功", resultMap); 113 }
抽奖页面代码:
这里省略大转盘样式代码,详细参考:http://www.cnblogs.com/mofish/archive/2013/01/24/2875516.html
点击抽奖按钮 最先执行lottery.html 98行代码 ,页面入口已经告诉大家,剩余请大家往下看应该就明白了。
lottery.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>xxx</title> 6 <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport"/> 7 <link rel="stylesheet" type="text/css" href="css/app.css"/> 8 </head> 9 <body> 10 <div class="page"> 11 <div id="verify-section"> 12 <img src="img/1.png" width="750" height="654"> 13 <div class="field lottery"> 14 <h2>输入您的手机号码,查看您的可抽奖次数。</h2> 15 <p> 16 <input type="tel" id="mobile" class="mobile" placeholder="请输入你的手机号码" maxlength="11"/> 17 </p> 18 <p> 19 <input type="text" id="code" placeholder="验证码" maxlength="6"/> 20 <button id="btn-code" class="btn">获取验证码</button> 21 </p> 22 <h2 class="error">手机号码格式不正确</h2> 23 <p> 24 <button id="btn-verify" class="btn">提交</button> 25 </p> 26 </div> 27 </div> 28 <div id="lottery-section" class="field lottery"> 29 <h2>您的可抽奖次数为:_<span class="lucktime"></span>_次</h2> 30 <p> 31 <button id="btn-list" class="btn">查看好友认证的情况</button> 32 </p> 33 34 <p> 35 <label for="mobile-check">请核对您的充值号码:</label> 36 <input type="tel" id="mobile-check" placeholder="请输入要充值的手机号" maxlength="11"/> 37 <p id="submit-check" style="display: none;">充值号码格式不正确 </p> 38 </p> 39 40 <div class="ly-plate"> 41 <div class="m-rotate"></div> 42 <div class="m-pointer"></div> 43 </div> 44 <p class="lottery-msg"></p> 45 46 <h2 class="submit-msg" style="display: none;">话费将在1个工作日内充值,请注意查收。</h2> 47 <p class="share-more"> 48 <button id="btn-share-more" class="btn">话费还有好多,我要继续推荐</button> 49 </p> 50 </div> 51 <div id="overlay"> 52 <div class="verify-list"> 53 <a href="#" onclick="$('#overlay').hide();"></a> 54 <ul class="list"> 55 </ul> 56 </div> 57 </div> 58 </div> 59 </body> 60 <script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script> 61 <script src="js/pageResponse.min.js" type="text/javascript" charset="utf-8"></script> 62 <script type="text/javascript" src="js/Rotate.js"></script> 63 <script type="text/javascript" src="js/app.js" charset="utf-8"></script> 64 <script type="text/javascript"> 65 $(function () { 66 // $("#lottery-section").show(); 67 $("#btn-list").click(function () { 68 $("#overlay").show(); 69 }); 70 $("#btn-share-more").click(function(){ 71 window.location = "index.html"; 72 }); 73 pageResponse({ 74 selectors: 'div.page', 75 mode: 'auto', // auto || contain || cover ,默认模式为auto 76 width: '750', //输入页面的宽度,只支持输入数值,默认宽度为320px 77 height: '654' 78 }); 79 80 //启动转盘 81 var rotateFunc = function (angle, prize, luckDrawCounts) { //angle: 奖项对应的角度 prize:中奖金额 luckDrawCounts:抽奖次数 82 $('.m-rotate').stopRotate(); 83 $('.m-rotate').rotate({ 84 angle: 0, 85 duration: 5000, 86 animateTo: angle + 1440, //angle是图片上各奖项对应的角度,1440是我要让指针旋转4圈。所以最后的结束的角度就是这样子^^ 87 callback: function () { 88 //更改抽奖次数 89 $(".submit-msg").show(); 90 $(".lucktime").html(luckDrawCounts); 91 isLottery = false; 92 $(".lottery-msg").html('您抽中了' + prize + '元手机话费,恭喜您。').css("color", "#fff"); 93 } 94 }); 95 }; 96 97 98 $(".m-pointer").rotate({ 99 bind: { 100 click: function () { 101 $("#submit-check").hide(); 102 //判断充值号码 103 if (!verifyPhoneNumber($("#mobile-check").val())) { 104 $("#submit-check").css("color", "red").show(); 105 return false; 106 } 107 108 if (luckDrawCounts != 0 && isLottery == false) { 109 var ajaxTimeoutTest = $.ajax({ 110 url: "/activity/wXinMarkLuckDraw.jspx", 111 data: {"id": listIds[0], "mobile": $("#mobile-check").val()}, 112 type: "POST", 113 // timeout : 5000, //超时时间设置,单位毫秒 114 success: function (rs) { 115 if (rs.result == "0") { 116 //生成中奖数据 117 var data = rs.content.wchatLotteryDomain; 118 //抽奖剩余次数 119 var luckDrawCounts = rs.content.luckDrawCounts; 120 if (data.id == 1) { 121 rotateFunc(360, data.prize, luckDrawCounts); 122 } 123 if (data.id == 2) { 124 rotateFunc(300, data.prize, luckDrawCounts); 125 } 126 if (data.id == 3) { 127 rotateFunc(240, data.prize, luckDrawCounts); 128 } 129 if (data.id == 4) { 130 rotateFunc(180, data.prize, luckDrawCounts); 131 } 132 if (data.id == 5) { 133 rotateFunc(120, data.prize, luckDrawCounts); 134 } 135 if (data.id == 6) { 136 rotateFunc(60, data.prize, luckDrawCounts); 137 } 138 }else { 139 isLottery = false; 140 $(".lottery-msg").html(rs.errorMessage).css("color", "red"); 141 } 142 } 143 // ,complete : function(XMLHttpRequest,status){ //请求完成后最终执行参数 144 // if(status=='timeout'){ 145 // ajaxTimeoutTest.abort(); 146 // isLottery = false; 147 // $(".lottery-msg").html("当前抽奖人数过多请稍后重试!").css("color", "red"); 148 // } 149 // } 150 }); 151 }else{ 152 if (isLottery && luckDrawCounts > 0){ 153 $(".lottery-msg").html('请提交后再重新抽奖').css("color", "red"); 154 }else{ 155 $(".lottery-msg").html('对不起你的抽奖机会用完了').css("color", "red"); 156 } 157 } 158 } 159 } 160 }); 161 }); 162 </script> 163 </html>
为了体验性,我这里的所有请求都是采用Ajax请求。
Java实现抽奖转盘 示例到这里就结束了,比较简单大家一看应该就明白了。这里也想补充说明下:
1.在考虑安全的情况下,抽奖算法实现,最好写在后台,因为这样中奖金额直接在后台去持久化了,无需经过页面传输,页面只是做了单纯的展示,一些非法操作,是没有办法通过改变中奖金额,去刷我们的中奖金额。
举例:如某个用户抽奖中了200元话费,我这里接口入参只需要告诉我 id 和 mobile ,并没有传中奖金额,这里前端就没有办法非法改变中奖金额了。
2.考虑如果用户点击抽奖按钮,但此时由于比较卡(可能受网络限制,请求很慢等等原因)造成用户点击了但是已经离开当前页面了,此时用户应该算已经抽奖了。这时我每次请求都去检查了抽奖次数估,也不会出现重复提交问题。
//修改抽奖次数 Integer result = appShareService.markLuckDraw(id);
if (result == null || result == 0) {
return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。");
}
第一次写博客,请的不好请大家见谅。
有需要源码的朋友可以留言,后续有空我会把项目中的代码整理成单独demo.