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

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

时间: 2024-10-22 03:39:47

利用java实现抽奖转盘(着重安全控制)的相关文章

利用canvas实现抽奖转盘

之前做过的项目中,有需要抽奖转盘功能的.项目已经完工一段时间了,也没出现什么严重的bug,所以现在拎出来分享给大家. 功能需求 转盘要美观,转动效果流畅. 转盘上需要显示奖品图片,并且奖品是后台读取的照片和名字. 转动动画完成后要有相应提示. 获取的奖品具体算法在数据库里操作,前端只提供最后的效果展示. 知识要点 引用了一个jq插件:awardRotate,用来实现更智能化的转动(插件下载:http://www.jqcool.net/jquery-jqueryrotate.html). 使用ca

利用canvas实现抽奖转盘---转载别人的

功能需求 转盘要美观,转动效果流畅. 转盘上需要显示奖品图片,并且奖品是后台读取的照片和名字. 转动动画完成后要有相应提示. 获取的奖品具体算法在数据库里操作,前端只提供最后的效果展示. 知识要点 引用了一个jq插件:awardRotate,用来实现更智能化的转动(插件下载:http://www.jqcool.net/jquery-jqueryrotate.html). 使用canvas标签和对应的html5 api 进行操作.(canvas中文手册可以查看http://javascript.r

Android实现抽奖转盘

慕课网视频 今天学习了以下抽奖转盘的实现 首先学习了以下 SurfaceView 的一般使用方法 下面的代码是 写 SurfaceView 的一个模板 package com.negro.myluckypan; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.SurfaceHolder; import andr

css 如何“画”一个抽奖转盘

主要描述的是如何运用 css 绘制一个抽奖转盘,并运用原生 js 实现转盘抽奖效果. 先来张效果图: 布局 一般来说,转盘一般有四个部分组成:外层闪烁的灯.内层旋转的圆盘.圆盘上的中奖结果.指针. 所以html的结构如下: <div class="turntable-wrap"> <div class="light" id="turntable_light"></div> <div class="

利用java开发一个双击执行的小程序

之前我们利用java写了很多东西,但是好像都没有什么实际意义. 因为有意义桌面小程序怎么都得有个界面,可是界面又不太好搞.或者 了解到这一层的人就少之又少了. 呀,是不是还得开辟一些版面来介绍awt和 swing... 算了 先把这个 双击执行的小程序 贡献出来. 这次 在分享一下源代码[以前还没有上传过源代码,布置怎么个搞法] 要求是: 输入一个 后缀名,然后输入所在目录,然后 点击查找,比如我们可以 输入F:\,然后查找 F盘下面的所有后缀名为比如.pdf 举例: 主要是 看了很多 资源,然

利用java日期类生成数据仓库维度表

利用java日期类生成数据仓库维度表 Date类: 最基础的日期时间类,返回一个相对日期的毫秒数.精确到毫秒,但不支持日期的国际化和分时区显示.Date 类从Java 开发包(JDK)1.0 就开始进化,当时它只包含了几个取得或者设置一个日期数据的各个部分的方法, 比如说月, 日, 和年. 这些方法现在遭到了批评并且已经被转移到了Calendar类里去了,这种改进旨在更好的处理日期数据的国际化格式. Calender类: 相对于Date更加强大的时间类,是抽象类,提供了常规的日期修改功能和国际化

利用Java针对MySql封装的jdbc框架类 JdbcUtils 完整实现(包含增删改查、JavaBean反射原理,附源码)

最近看老罗的视频,跟着完成了利用Java操作MySql数据库的一个框架类JdbcUtils.java,完成对数据库的增删改查.其中查询这块,包括普通的查询和利用反射完成的查询,主要包括以下几个函数接口: 1.public Connection getConnection()   获得数据库的连接 2.public boolean updateByPreparedStatement(String sql, List<Object>params)throws SQLException  更新数据库

利用Java反射实现JavaBean对象相同属性复制并初始化目标对象为空的属性的BeanUtils

有时遇到将数据传输对象转换成JSON串会将属性值为空的属性去掉,利用Java反射实现JavaBean对象数据传输对象的相同属性复制并初始化数据传输对象属性为空的属性,然后转换成JSON串 package com.banksteel.util; import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.ut

利用Java提供的Observer接口和Observable类实现观察者模式

对于观察者模式,其实Java已经为我们提供了已有的接口和类.对于订阅者(Subscribe,观察者)Java为我们提供了一个接口,JDK源码如下: 1 package java.util; 2 3 public interface Observer { 4 void update(Observable o, Object arg); 5 } 和我们上一篇实现的观察者一样,仅提供一个update方法用于接收通知者的通知做出相应改变. 我们再来看看Java为我们提供了一个怎样的通知者(Publish