代码改变世界

利用java实现抽奖转盘(着重安全控制)

2016-04-24 00:20  咸鱼也有翻身日  阅读(35459)  评论(38编辑  收藏  举报

本文是针对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.