本文是针对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:2314 */15 public class BigWheelDrawUtil {16 17 18 /**19 * 给转盘的每个角度赋初始值20 * @return21 */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 * @return34 */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='/images/loading.gif' data-original="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='/images/loading.gif' data-original="js/jquery.min.js" type="text/javascript" charset="utf-8"></script> 61 <script src='/images/loading.gif' data-original="js/pageResponse.min.js" type="text/javascript" charset="utf-8"></script> 62 <script type="text/javascript" src='/images/loading.gif' data-original="js/Rotate.js"></script> 63 <script type="text/javascript" src='/images/loading.gif' data-original="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(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.
原标题:利用java实现抽奖转盘(着重安全控制)
关键词:JAVA