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

时间:2023-03-08 17:32:52
利用java实现抽奖转盘(着重安全控制)

本文是针对jquery 实现抽奖转盘作者的一个补充(主要用java去实现转盘结果生成及存储,解决jquery 做法 非法用户采用模拟器实现改变转盘值的风险性),针对jQuery的具体实现,请看案例:http://www.cnblogs.com/mofish/archive/2013/01/24/2875516.html              本文就不一一细说了,那么现在就直入正题。

由于公司产品推广,最近要求实现一个邀请用户注册即可抽奖的转盘,页面展示如下:

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

java 实现方式如下:

构造实体类

WchatLotteryDomain.java

 package com.cy.dcts.domain.activity;

 import java.io.Serializable;

 /**
* 微信用户分享中奖基础数据类
* @author yanst 2016/4/23 9:36
*/
public class WchatLotteryDomain implements Serializable{
private static final long serialVersionUID = -1595371216905016135L; private Integer id; //中奖金额
private String prize; //中奖率
private Integer v; public WchatLotteryDomain(Integer id, String prize, Integer v){
this.id = id;
this.prize = prize;
this.v = v;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getPrize() {
return prize;
} public void setPrize(String prize) {
this.prize = prize;
} public Integer getV() {
return v;
} public void setV(Integer v) {
this.v = v;
}
}

抽奖算法实现类:  

1.初始数据集合:initDrawList  。

  2.generateAward方法实现根据概率随机生成中奖对象WchatLotteryDomain 

BigWheelDrawUtil.java

 package com.cy.dcts.common.util;

 import com.alibaba.fastjson.JSON;
import com.cy.dcts.domain.activity.WchatLotteryDomain; import java.util.ArrayList;
import java.util.List; /**
*
* wchat大转盘抽奖活动
*
* @author yanst 2016/4/23 9:23
*/
public class BigWheelDrawUtil { /**
* 给转盘的每个角度赋初始值
* @return
*/
private final static List<WchatLotteryDomain> initDrawList = new ArrayList<WchatLotteryDomain>() {{
add(new WchatLotteryDomain(1, "200", 1));
add(new WchatLotteryDomain(2, "100", 3));
add(new WchatLotteryDomain(3, "50", 30));
add(new WchatLotteryDomain(4, "30", 30));
add(new WchatLotteryDomain(5, "20", 26));
add(new WchatLotteryDomain(6, "10", 10));
}}; /**
* 生成奖项
* @return
*/
public static WchatLotteryDomain generateAward() {
List<WchatLotteryDomain> initData = initDrawList;
long result = randomnum(1, 100);
int line = 0;
int temp = 0;
WchatLotteryDomain returnobj = null;
int index = 0;
for (int i = 0; i < initDrawList.size(); i++) {
WchatLotteryDomain obj2 = initDrawList.get(i);
int c = obj2.getV();
temp = temp + c;
line = 100 - temp;
if (c != 0) {
if (result > line && result <= (line + c)) {
returnobj = obj2;
break;
}
}
}
return returnobj;
} // 获取2个值之间的随机数
private static long randomnum(int smin, int smax){
int range = smax - smin;
double rand = Math.random();
return (smin + Math.round(rand * range));
} public static void main(String[] args) {
System.out.println(JSON.toJSONString(generateAward()));
} }

controller 层 实现 显示抽奖的结果给页面,页面启动转盘,把对应的中间角度显示给用户看,同时把中间金额保存到系统中。

1.调用util类返回中奖项

 //生成中奖金额对象
WchatLotteryDomain wchatLotteryDomain = BigWheelDrawUtil.generateAward();

2.修改抽奖次数

//修改抽奖次数
Integer result = appShareService.markLuckDraw(id);

3.把中奖信息持久化

//写入中奖信息
writeXinRecord(mobile, wchatLotteryDomain);

4.把当前中奖信息及剩余中奖次数返回

//代码略,参考ActivityAction.java  107、108行

ActivityAction.java

  /**
* 抽奖
*
* @param id id
* @param mobile 中奖号码
* @return
*/
@RequestMapping("wXinMarkLuckDraw.jspx")
@ResponseBody
public JSonRespone markLuckDraw(Long id, String mobile) {
//参数验证
if (id == null || id.longValue() == 0) {
return JSonRespone.makeHasContentJSonRespone("1", "您没有抽奖次数!");
}
//参数验证
if (StringUtils.isEmpty(mobile)) {
return JSonRespone.makeHasContentJSonRespone("1", "中奖手机号码为空!");
} //生成中奖金额对象
WchatLotteryDomain wchatLotteryDomain = BigWheelDrawUtil.generateAward();
if(wchatLotteryDomain == null){
return JSonRespone.makeHasContentJSonRespone("3", "生成抽奖数据失败");
}
try {
//修改抽奖次数
Integer result = appShareService.markLuckDraw(id);
if (result == null || result == 0) {
return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。");
}
} catch (Exception e) {
logger.debug(e.getMessage());
return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。");
} if(logger.isErrorEnabled()){
logger.error("微信分享活动:手机号码为:{},中奖信息:{}", mobile, JSON.toJSONString(wchatLotteryDomain));
} //写入中间信息
return writeXinRecord(mobile, wchatLotteryDomain);
} // 微信 用户分享 认证之后送话费活动 中奖记录存储路径
private static final String wXinFilePath = "/home/wxhb/lottery.txt";
//"/home/wxhb/lottery.txt";
//"D:/list.txt"; /**
* 写入中奖金额
* @param mobile
* @param wchatLotteryDomain
* @return
*/
private JSonRespone writeXinRecord(String mobile,WchatLotteryDomain wchatLotteryDomain ) {
// 记录时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
String date = simpleDateFormat.format(calendar.getTime());
// 记录文件是否存在
File file = new File(wXinFilePath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 临时记录存储
ArrayList<String> arrayList = new ArrayList<>();
// 是否已经存在记录
Scanner in = null;
try {
in = new Scanner(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 读取记录放置临时数组
while (in.hasNextLine()) {
arrayList.add(in.nextLine());
}
in.close();
// 查询记录是否存在
if (arrayList.size() > 0) {
for (String str : arrayList) {
if (mobile.equals(str.split("-")[0])) {
return JSonRespone.makeHasContentJSonRespone("1", "成功", "记录已存在");
}
}
}
// 写入记录
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
out.write(mobile + " " + date + " " + wchatLotteryDomain.getPrize());
out.newLine();
out.close();
} catch (IOException e) {
e.printStackTrace();
return JSonRespone.makeHasContentJSonRespone("0", "失败", e.getMessage());
} Map<String, Object> resultMap = new HashMap<String, Object>();
try {
//获取抽奖次数
resultMap.put("luckDrawCounts", appShareService.getLuckDrawCounts(mobile));//抽奖次数
resultMap.put("wchatLotteryDomain", wchatLotteryDomain);
} catch (Exception e) {
logger.debug(e.getMessage());
}
return JSonRespone.makeHasContentJSonRespone("0", "成功", resultMap);
}

抽奖页面代码:

这里省略大转盘样式代码,详细参考:http://www.cnblogs.com/mofish/archive/2013/01/24/2875516.html

点击抽奖按钮 最先执行lottery.html 98行代码   ,页面入口已经告诉大家,剩余请大家往下看应该就明白了。

lottery.html

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>xxx</title>
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport"/>
<link rel="stylesheet" type="text/css" href="css/app.css"/>
</head>
<body>
<div class="page">
<div id="verify-section">
<img src="img/1.png" width="750" height="654">
<div class="field lottery">
<h2>输入您的手机号码,查看您的可抽奖次数。</h2>
<p>
<input type="tel" id="mobile" class="mobile" placeholder="请输入你的手机号码" maxlength="11"/>
</p>
<p>
<input type="text" id="code" placeholder="验证码" maxlength="6"/>
<button id="btn-code" class="btn">获取验证码</button>
</p>
<h2 class="error">手机号码格式不正确</h2>
<p>
<button id="btn-verify" class="btn">提交</button>
</p>
</div>
</div>
<div id="lottery-section" class="field lottery">
<h2>您的可抽奖次数为:_<span class="lucktime"></span>_次</h2>
<p>
<button id="btn-list" class="btn">查看好友认证的情况</button>
</p> <p>
<label for="mobile-check">请核对您的充值号码:</label>
<input type="tel" id="mobile-check" placeholder="请输入要充值的手机号" maxlength="11"/>
<p id="submit-check" style="display: none;">充值号码格式不正确 </p>
</p> <div class="ly-plate">
<div class="m-rotate"></div>
<div class="m-pointer"></div>
</div>
<p class="lottery-msg"></p> <h2 class="submit-msg" style="display: none;">话费将在1个工作日内充值,请注意查收。</h2>
<p class="share-more">
<button id="btn-share-more" class="btn">话费还有好多,我要继续推荐</button>
</p>
</div>
<div id="overlay">
<div class="verify-list">
<a href="#" onclick="$('#overlay').hide();"></a>
<ul class="list">
</ul>
</div>
</div>
</div>
</body>
<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pageResponse.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="js/Rotate.js"></script>
<script type="text/javascript" src="js/app.js" charset="utf-8"></script>
<script type="text/javascript">
$(function () {
// $("#lottery-section").show();
$("#btn-list").click(function () {
$("#overlay").show();
});
$("#btn-share-more").click(function(){
window.location = "index.html";
});
pageResponse({
selectors: 'div.page',
mode: 'auto', // auto || contain || cover ,默认模式为auto
width: '750', //输入页面的宽度,只支持输入数值,默认宽度为320px
height: '654'
}); //启动转盘
var rotateFunc = function (angle, prize, luckDrawCounts) { //angle: 奖项对应的角度 prize:中奖金额 luckDrawCounts:抽奖次数
$('.m-rotate').stopRotate();
$('.m-rotate').rotate({
angle: 0,
duration: 5000,
animateTo: angle + 1440, //angle是图片上各奖项对应的角度,1440是我要让指针旋转4圈。所以最后的结束的角度就是这样子^^
callback: function () {
//更改抽奖次数
$(".submit-msg").show();
$(".lucktime").html(luckDrawCounts);
isLottery = false;
$(".lottery-msg").html('您抽中了' + prize + '元手机话费,恭喜您。').css("color", "#fff");
}
});
}; $(".m-pointer").rotate({
bind: {
click: function () {
$("#submit-check").hide();
//判断充值号码
if (!verifyPhoneNumber($("#mobile-check").val())) {
$("#submit-check").css("color", "red").show();
return false;
} if (luckDrawCounts != 0 && isLottery == false) {
var ajaxTimeoutTest = $.ajax({
url: "/activity/wXinMarkLuckDraw.jspx",
data: {"id": listIds[0], "mobile": $("#mobile-check").val()},
type: "POST",
// timeout : 5000, //超时时间设置,单位毫秒
success: function (rs) {
if (rs.result == "0") {
//生成中奖数据
var data = rs.content.wchatLotteryDomain;
//抽奖剩余次数
var luckDrawCounts = rs.content.luckDrawCounts;
if (data.id == 1) {
rotateFunc(360, data.prize, luckDrawCounts);
}
if (data.id == 2) {
rotateFunc(300, data.prize, luckDrawCounts);
}
if (data.id == 3) {
rotateFunc(240, data.prize, luckDrawCounts);
}
if (data.id == 4) {
rotateFunc(180, data.prize, luckDrawCounts);
}
if (data.id == 5) {
rotateFunc(120, data.prize, luckDrawCounts);
}
if (data.id == 6) {
rotateFunc(60, data.prize, luckDrawCounts);
}
}else {
isLottery = false;
$(".lottery-msg").html(rs.errorMessage).css("color", "red");
}
}
// ,complete : function(XMLHttpRequest,status){ //请求完成后最终执行参数
// if(status=='timeout'){
// ajaxTimeoutTest.abort();
// isLottery = false;
// $(".lottery-msg").html("当前抽奖人数过多请稍后重试!").css("color", "red");
// }
// }
});
}else{
if (isLottery && luckDrawCounts > 0){
$(".lottery-msg").html('请提交后再重新抽奖').css("color", "red");
}else{
$(".lottery-msg").html('对不起你的抽奖机会用完了').css("color", "red");
}
}
}
}
});
});
</script>
</html>

为了体验性,我这里的所有请求都是采用Ajax请求。

Java实现抽奖转盘 示例到这里就结束了,比较简单大家一看应该就明白了。这里也想补充说明下:

1.在考虑安全的情况下,抽奖算法实现,最好写在后台,因为这样中奖金额直接在后台去持久化了,无需经过页面传输,页面只是做了单纯的展示,一些非法操作,是没有办法通过改变中奖金额,去刷我们的中奖金额。

举例:如某个用户抽奖中了200元话费,我这里接口入参只需要告诉我 id 和 mobile ,并没有传中奖金额,这里前端就没有办法非法改变中奖金额了。

2.考虑如果用户点击抽奖按钮,但此时由于比较卡(可能受网络限制,请求很慢等等原因)造成用户点击了但是已经离开当前页面了,此时用户应该算已经抽奖了。这时我每次请求都去检查了抽奖次数估,也不会出现重复提交问题。

//修改抽奖次数
Integer result = appShareService.markLuckDraw(id);

if (result == null || result == 0) {
     return JSonRespone.makeHasContentJSonRespone("2", "抽奖失败,请刷新重新验证。");
  }

第一次写博客,请的不好请大家见谅。

有需要源码的朋友可以留言,后续有空我会把项目中的代码整理成单独demo.