精华内容
下载资源
问答
  • 抽奖活动页面设计
    千次阅读
    2017-07-13 11:09:37

    核心代码实现

    待续

    数据库设计

    待续

    高并发下的优化

    转载:http://blog.csdn.net/qq_16681169/article/details/53750704
    一. 项目思考

    由于项目发起了一个抽奖活动,发起活动之前给所有用户发短信提示他们购买了我们的产品有抽奖权益。然后用户上来进入抽奖页面点击爆增,过了一会儿页面就打不开了。后面查看了下各种日志,发现了瓶颈在数据库,由于读写冲突严重,导致响应变慢,有不少连接都超时了。后面看到监控和日志留下的数据,发现负责抽奖的微服务集群qps暴涨12倍,db的qps也涨了10倍。这很明显是一个高并发下如何摆脱数据库读写,I/O瓶颈的问题。
    整点开抢后瞬时巨量的请求同时涌入,即使我们Nginx端做过初步限流,整个业务逻辑校验阶段运作良好,但是系统的瓶颈就转移到其他环节:大量的读写请求,导致后面的请求全部排队等待,等前面一个update完成释放行锁后才能处理下一个请求,大量请求等待,占用了数据库的连接!一旦数据库同一时间片内的连接数被打满,就会导致这个时间片内其他后来的全部请求因拿不到连接而超时,导致访问此数据库的其他环节也出现问题!所以RT就会异常飙高!
    于是我们在思考着怎么优化这个高并发下的抽奖问题

    二. 优化思路

    听了经验丰富的师兄的经验,也借鉴了下网上的一些思路,能采用的有效措施主要是:降级,限流,缓存,消息队列。主要原则是:尽量不暴露db,把大部分请求在服务的系统上层处理了。

    三. 优化细节

    1. 抽奖详情页

    a. 线上开启缓存
    线上已写缓存逻辑,但是没有用switch开启。开启后可以减少数据库的并发IO压力,减少锁冲突。

    b. 关于本地缓存淘汰策略的细节处理
    缓存超过或等于限制大小全部清空。建议等于时不清空,而使用缓存淘汰算法:比如LRU,LFU,NRU等,这样不会出现缓存过大清空后,从数据库更新数据到缓存,缓存里数据依旧很大。导致缓存清空频率过高,反而降低系统的吞吐量。例如guava cache中的参数是

    //设置缓存容器的初始容量为10
    initialCapacity(10)
    //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
    maximumSize(100)

    1. 抽奖逻辑

    a.队列削峰
    用额外的单进程处理一个队列,下单请求放到队列里,一个个处理,就不会有qps的高并发问题了。场景中抽奖用户会在到点的时间涌入,DB瞬间就接受暴击压力,hold不住就会宕机,然后影响整个业务。队列的长度保持固定,对于如果请求排队在队伍中靠后,比如奖品100个的情况下,中奖率10%,队列里请求任务超过1000时,就直接将后续的抽奖请求返回不中奖。用tair记录排队数,如果奖品没发完,再请空tair,允许请求继续入队列。这样队列起到了降级和削峰的作用。

    b.将事务和行级悲观锁改成乐观锁
    原来的代码是通过悲观锁来控制超发的情况。(比如一共有100个商品,在最后一刻,我们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是99个,然后都通过了这一个余量判断,最终导致超发。)
    在原来的代码中用的是for update行锁,在高并发的情况下会很多这样的修改请求,每个请求都需要等待锁,某些线程可能永远都没有机会抢到这个锁,这种请求就会死在那里。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。
    可以采用乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号(Version)更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。

    c.对于与抽奖无直接关系的流程采用异步
    比如抽奖成功之后的发短信功能另起一个线程池专门处理。这样可以提高请求的处理速率,提高qps上升后的乘载能力。

    d.数据库的读写分离
    现在的数据库查询都是读的主库。将数据库的大量查询改为从库,减轻主库的读写压力。主服务器进行写操作时,不影响查询应用服务器的查询性能,降低阻塞,提高并发。

    e.同一时间片内,采用信号量机制
    确保进来的人数不会过多导致系统响应超时: 信号量的采用,能够使得抽奖高峰期内,同一时间片内不会进入过多的用户,从底层实现上规避了系统处理大数据量的风险。这个可以配合队列进行限流处理。

    f. 消息存储机制
    将数据请求先添加到信息队列中(比如Tair存储的数据结构中),然后再写工具启动定时任务从Tair中取出数据去入库,这样对于db的并发度大大降低到了定时任务的频率。但是问题可能会出在保持数据的一致性和完整性上。

    g.必要时候采用限流降级的测流
    当并发过多时为了保证系统整体可用性,抛弃一些请求。对于被限流的请求视为抽不到奖。

    3.额外考虑

    a.防止黑客刷奖
    防止黑客恶意攻击(比如cc攻击)导致qps过高,可以考虑策略在服务入口为相同uid的账户请求限制每秒钟的最高访问数。

    b. 中奖数据预热
    中奖只是少数,大部分人并不会中奖,所以可以在第一步便限制只有少数用户的请求能够打到真正抽奖逻辑上。是否可以考虑在抽奖之前先用随机算法生成一批中奖候选人。然后当用户请求过来时如果其中绝大多数请求都非中奖候选人,则直接返回抽奖失败,不走抽奖拿奖品的流程。少部分用户请求是中奖候选人,则进入队列,排在队列前面的获得奖品,发完为止,先到先得。
    举个例子:10万个用户抽奖,奖品100个,先随机选出中奖候选人500个。用户请求过来时,不走抽奖查库逻辑的用户过滤掉99500个,剩余的候选人的请求用队列处理,先到先得。这样可以把绝大多数的请求拦截在服务上游不用查库,但是缺点是不能保证奖品一定会被抽完(可能抽奖候选人只有不到100人参与抽奖)。

    四.设计架构图
    这里写图片描述

    JS前端实现


    核心函数是一个jQuery中旋转rotate插件
    支持Internet Explorer 6.0+ 、Firefox 2.0 、Safari 3 、Opera 9 、Google Chrome,高级浏览器下使用Transform,低版本ie使用VML实现。

    • rotate(angle)angle参数:[Number] – 默认为 0

    根据给定的角度旋转图片例如:

    $("#img").rotate45);
    或 $('#img').rotate({angle:45})
    • rotate(parameters)

    parameters参数:[Object] 包含旋转参数的对象。支持的属性:

    1.angle属性:[Number] – default 0 – 旋转的角度数,并且立即执行例如:

    $("#img").rotate({angle:45});

    2.bind属性:[Object] 对象,包含绑定到一个旋转对象的事件。事件内部的 (this) (this).rotate(…)。例如 (click on arrow):

    $("#img").rotate({
    bind: {
    click: function () {
    $(this).rotate({
    angle: 0,
    animateTo: 180
    })
    }
    }
    });

    3.animateTo属性:[Number] – default 0 – 从当前角度值动画旋转到给定的角度值 (或给定的角度参数)例如: 结合上面的例子,请参阅使用。

    4.duration属性:[Number] – 指定使用animateTo的动画执行持续时间例如 (click on arrow):

    $("#img").rotate({
    bind: {
    click: function () {
    $(this).rotate({
    duration: 6000,
    angle: 0,
    animateTo: 100
    })
    }
    }
    });

    5.step属性:[Function] – 每个动画步骤中执行的回调函数,当前角度值作为该函数的第一个参数

    6.callback属性:[Function] 动画完成时执行的回调函数例如 (click on arrow):

    $("#img").rotate({bind: {
    click: function () {
    $(this).rotate({
    angle: 0,
    animateTo: 180,
    callback: function () {
    alert(1)
    }
    })
    }
    }
    });

    getRotateAngle

    这个函数只是简单地返回旋转对象当前的角度。

    $("#img").rotate({
    angle: 45,
    bind: {
    click: function () {
    alert($(this).getRotateAngle());
    }
    }
    });

    stopRotate

    这个函数只是简单地停止正在进行的旋转动画。

    $("#img").rotate({
    bind: {
    click: function () {
    $("#img").rotate({
    angle: 0,
    animateTo: 180,
    duration: 6000
    });
    setTimeout(function () {
    $("#img").stopRotate();
    }, 1000);
    }
    }
    });
    更多相关内容
  • 抽奖活动架构设计

    2022-02-06 16:55:00
    实现一个大转盘活动,用户有三次抽奖机会,每次抽奖后给用户返回中奖结果。 中奖概率和奖品在活动前由产品经理给出 一等奖——中奖概率为 1 %,1000元红包 二等奖——中奖概率为 5 %,100元红包 三等奖——中奖...

    一 需求描述

    实现一个大转盘活动,用户有三次抽奖机会,每次抽奖后给用户返回中奖结果。

    中奖概率和奖品在活动前由产品经理给出

    一等奖——中奖概率为 1 %,1000 元红包

    二等奖——中奖概率为 5 %,100 元红包

    三等奖——中奖概率为 10 %,10 元红包

    其余为没有中奖

    用户通过具体页面可以看到中奖纪录。

    二 需求分析

    我们发现中奖概率的格式是有问题的,只有中奖概率和奖品,没有奖品数量。没有奖品数量就是对预算没有控制,如果中奖数量超过了预算就会造成损失,所以在需求中要补充奖品数量和活动总预算。

    直接实现该需求是比较简单的——对用户的抽奖次数做校验,然后在每次抽奖的时候利用随机数查看用户是否在中奖区间来确定抽奖结果。

    但是,作为架构师,要对需求有预判。对于产品的日常运营来说,活动是常规需求,每个月都有形形色色的活动。例如,大转盘抽奖、九宫格抽奖、开箱子抽奖等。这些抽奖活动有不同的前端表现、中奖概率和获取抽奖资格的途径,但底层抽奖逻辑是共性的,我们可以把共性抽象出来,把抽奖部分做成通用的系统,供上层逻辑调用。如果能实现该系统,那么有很多好处。

    提升效率:抽奖部分只需修改配置文件即可实现,实现需求时只需要重点关注表现层即可。

    提升稳定性:核心抽奖逻辑固定,经过严格测试和多个项目验证,每次活动都是复用代码,并不修改代码,减少了由于程序问题导致发送奖品出错而造成的经济损失。

    三 实现方案

    1 实现功能分析

    1)外部交互

    • 增加用户抽奖次数的功能

    • 获取抽奖纪录的功能

    • 执行抽奖操作,返回结果的功能

    2)内部逻辑

    • 修改用户抽奖次数的功能

    • 配置奖品库存和中奖概率等属性功能

    • 处理用户抽奖逻辑

    • 针对系统的稳定性和逻辑的严谨性的功能

    整体项目模块结构如下图

    2 对外交互设计

    由于系统是一个通用系统,会同时服务多个活动。对于每个活动,我们可以用活动 ID 来区分。活动 ID 是一个通用字段,所以在下面的设计中,默认都带有活动 ID。

    1)增加抽奖资格

    增加抽奖资格是针对一个具体用户的,只需要外部传递过来用户的 UID 即可。

    由于修改接口涉及用户数据,比较敏感,所以要有鉴权操作。只有该活动对应的逻辑服务才有权限给用户增加抽奖次数。另外,还要在内部做一些控制,监控增加的抽奖次数,不要超过配置文件中设置的增加抽奖资格的上限。

    针对请求,我们只需要返回增加的结果即可。如果成功,则返回增加的次数,如果失败,则返回失败原因。

    要区分失败的错误码,识别是系统内部的错误还是增加次数达到上限的业务错误。

    2)获取抽奖记录

    该功能只涉及读操作,拉取用户抽奖的历史记录。这里需要考虑分页查询,因为是通用抽奖系统,所以不同活动的抽奖次数不一定,有些活动的抽奖次数会很多。做成分页查询,前端可以根据展示需求按需拉取抽奖记录。

    3)执行抽奖操作

    此功能对外部调用的业务来说,交互比较简单,只需要传入 UID、给出抽奖结果即可。

    返回结果的状态码部分要设计得通用一些,能够区分不同的情况。

    成功;需要返回用户的中奖信息,中奖信息包括奖品名字,奖品数量等奖品属性。返回中奖信息的协议要满足可扩展性,后续增加新属性时能直接扩展。但有一个特殊情况,未中奖是失败,还是另一种成功呢?

    未中奖作为一种特殊的“奖品”,是一种不错的实现方式。因为没中奖也是一种正常逻辑。未中奖和中其他奖品采用相同的返回格式,能够实现逻辑通用。这种方案需要调用方针对未中奖进行特殊处理。

    失败:

    在失败的时候,要区分是系统失败还是业务失败。系统失败是后端系统的原因,后端考虑好柔性即可。由于这个业务的特点,还有很多业务侧相关的逻辑。例如:活动是有时效的,可能是活动过期了;用户没有抽奖资格却来抽奖;当前奖品库存已空。

    4)内部逻辑设计

    a 数据结构

    (1)奖品部分

    主要信息包括奖品名称、奖品总数量、已经抽取数量和中奖概率权重。

    (2)用户部分

    有两部分数据:用户抽奖属性数据和用户抽奖记录数据

    用户抽奖属性数据:主要包括用户已经抽奖的次数、用户一共有多少抽奖次数和用户抽奖相关数据。一个用户在一个活动中仅有一条数据,

    用户抽奖记录数据:用户抽奖记录的流水,用户在什么时候参与抽奖并获得什么奖品。通过流水能够展示用户中奖记录,而且为后续发奖提供依据。

    b 逻辑实现

    逻辑实现除了实现常规的增删改查,更多的是要考虑针对熊抽奖场景,如何保障系统的安全性,以及用户体验的柔性。

    风险控制

    • 要控制抽奖次数的上限

    • 奖品要有库存的概念

    • 奖品要有价值预算

    幂等

    在实现业务逻辑的时候,需要考虑很多细节。例如,分布式系统通过网络处理请求,有时会造成重复请求命令,或者读写冲突,要通过幂等的方式避免这些问题。

    (1)设定抽奖和减少抽奖次数的时机

    应该先扣减抽奖次数,再调用抽奖逻辑更新奖品状态。

    (2)中奖纪录要有订单ID,防止因为程序重试导致写多条中奖纪录

    可以设置中奖订单 ID 的内容为{UID}-{活动ID]-{第多少次抽奖中奖}

    (3)在发奖环节保证不多发奖品

    发奖状态字段有三个状态:未处理(默认)、发放中和已发送,三个状态依次改变,除此之外,不能随意调整状态:未处理转换成发放中,发放中转换成已发送。

    (4)在发奖环节增加审查逻辑

    5)柔性

    对于抽奖环节,可以针对抽奖失败进行柔性处理。

    如果在抽奖过程中后端系统出现内部错误,导致抽奖没有真实执行,则可以采取两种方案:一是给出明确提示,表明当前系统有问题,请用户间隔一段时间后再试(后端要有告警,及时修复问题);二是按照未中奖,或者默认的一种奖品进行发放,让用户体验正常。但也要有告警通知,因为此时系统已经处于有问题的状态。

    如果奖品库存不足,则有三种方案可供选择:按照未中奖处理;发放次一级奖品;发放默认奖品。

    四 整体架构设计

    1 整体架构如下图

    2 抽奖流程如下图

    3 发放奖品流程如下图

    五 案例总结

    1 审视需求

    架构师在接到需求后,不要进入惯性思维来思考如何实现需求,而是要思考需求是否合理,是否有漏洞和风险。例如,最初的需求中并没有考虑活动预算,直接按照表面意思来实现需求是有风险的。

    2 产品思维

    架构师要具有产品思维,能够对需求进行预判。对于一个表面看是活动类的需求,能够分析这个需求是否是长期需求,是否有必要实现通用的抽象的方案。如果答案是肯定的,那么接下来再对具体的方案进行抽象。

    3 积极思考

    实现一个需求时要想到抽象复用。

    展开全文
  • 网站抽奖活动页面设计及制作

    千次阅读 2014-05-21 14:02:33
    活动页面

    活动主页:查看地址


    首页banner:



    二级页面banner:



    侧边栏banner:


    展开全文
  • 使用 NodeJS Express MongoDB 上线环境部署 搭建H5抽奖活动页面
  • 主要为大家详细介绍了jQuery自定义数值抽奖活动的相关代码,具有一定的参考价值,感兴趣的朋友可以参考一下
  • Bootstrap公司年会抽奖活动代码基于Bootstrap3.3.4制作,响应式设计,自适应分辨率,兼容PC端和移动端,自定义主题,有添加抽奖名单功能。
  • 9、中奖号码显示在页面的下方,需要信息滚动显示中奖号码(号码中间加***),其中需要后台能够手动添加一些中奖信息,便于后台操作。 10、后台功能要求:记录姓名、QQ 和手机号的记录,能够导出 excel 表的功能。 ...
  • 摇一摇抽奖的JSP页面,JS渲染文件,还有数据库的抽奖设计,还有后端controller文件,拿来就能用,后台可管理抽奖奖品的,方便。
  • app H5活动抽奖活动(转盘)前端+后台设计

    万次阅读 多人点赞 2016-08-06 15:58:50
    界面流程用户从app 进入H5页面 接口连接:192.168.1.1:8181/youxi/getSession.jsp?userid=1020201 userid为app端传的参数 getSession.jsp代码 ...游戏结束进入抽奖页面 页面部分代码 js部分代码 <sc

    界面

    这里写图片描述

    流程

    用户从app 进入H5页面
    接口连接:192.168.1.1:8181/youxi/getSession.jsp?userid=1020201 userid为app端传的参数
    getSession.jsp代码
    这里写图片描述
    response.sendRedirect(“youxi/index.html”);//跳向游戏页面
    游戏结束进入抽奖页面
    页面部分代码
    这里写图片描述
    js部分代码

    
    <script type="text/javascript">
        //加载页面执行
        $(function(){
        //从session中获取userid  
        var userid = '<%=session.getAttribute("userid")%>';
        $.ajax({
                    //请求接口,该接口返回用户剩余抽奖次数 num
                    url:"http://api.booea.cn:8105/api/getLotteryNum.jsp?userid="+userid+"&type=getnum",
                    type:"get",
                    dataType:"json",
                    success:function(data){
                        var num = parseInt(data.num);
                        $("#num").text(num);
                        }
        });
    
        //点击抽奖按钮时执行此function
        var $plateBtn = $('#plateBtn');
        $plateBtn.click(function(){
            var userid = '<%=session.getAttribute("userid")%>';
            var numDom=document.getElementById("num");
            var numVal=parseInt(numDom.innerHTML);
            console.log(numVal);
            //剩余抽奖次数大于0时 ajax请求抽奖接口 返回剩余抽奖次数num和中奖结果result
            if(numVal>0){
                $.ajax({
                    url:"http://api.booea.cn:8105/api/lottery.jsp?userid="+userid+"&type=app",
                    type:"get",
                    dataType:"json",
                    success: function(data){
                        //剩余抽奖次数
                        var lotterynum = parseInt(data.num);
                        $("#num").text(lotterynum);
                        //中奖结果
                        var num = parseInt(data.result);
                        switch(num){
                    case 1: 
                        rotateFunc(5,90,'恭喜你中了 <em>酒店体验券X1</em>');
                        jumpFunc2();//跳转网页
                        break;
                    case 2: 
                        rotateFunc(2,65,'恭喜你中了 <em>金币10</em>');
                        break;
                    case 3: 
                        rotateFunc(7,282,'恭喜你中了 <em>金币10</em>');
                        break;
                    case 4: 
                        rotateFunc(4,247,'恭喜你中了 <em>金币10 </em>');
                        break;
                    case 5: 
                        rotateFunc(2,55,'恭喜你中了 <em>金币10</em>');
                        break;
                    case 6: 
                        rotateFunc(6,24,'恭喜你中了 <em>金币10</em>');
                        break;
                    case 7: 
                        rotateFunc(7,292,'恭喜你中了 <em>金币10</em>');
                        break;
                    case 8:
                        rotateFunc(1,170,'恭喜你中了 <em>七彩云南六日五夜游X1</em>');
                        jumpFunc1();
                        break;
                    default:
                        rotateFunc(4,247,'恭喜你中了 <em>金币10</em>');
    
                }
                    //numDom.innerHTML=numVal-1;
    
                        }
                    }); 
            }else{
                alert("好像没有抽奖机会了哦,分享试试!");  
            }
        });
    
        var $resultTxt = $('#resultTxt');
        var rotateFunc = function(awards,angle,text){  //awards:奖项,angle:奖项对应的角度
            $plateBtn.stopRotate();//转盘静止
            $plateBtn.rotate({
                angle: 0, //指针起始位置角度0
                duration: 5000, //旋转时间5秒
                animateTo: angle + 1440,  //旋转度数 (angle是图片上各奖项对应的角度,1440是让指针固定旋转4圈)
                callback: function(){ //结束时执行的方法
                    $resultTxt.html(text);
                    $result.show();
                }
            });
        };
        //倒计时跳转云南 case=8时 6秒后跳转网页到信息填写1.html
        var jumpFunc1 = function(){
            setTimeout(function(){
                window.location.href="http://api.booea.cn:8105/huodong/xinxitianxie1.html";
                },6000);
        };
        //倒计时跳转酒店
        var jumpFunc2 = function(){
            //setTimeout(方法,倒计时);
            setTimeout(function(){
                window.location.href="http://api.booea.cn:8105/huodong/xinxitianxie2.html";
                },6000);
        };
    
        var $resultBtn = $('#resultBtn');
        var $result = $('#result');
        $resultBtn.click(function(){
            $result.hide();
        });
    
     $(document).ready(function(){
         $(".guanbi").click(function(){
             $(".banner").fadeOut("slow");
         });
     });
    
    $(function(){
        var url = null;
        //$(document).on('click','.jihuir',function(){});
        //等价于$('.jihuir').on('click',function(){});
        //也等价于$('.jihuir').click(function(){});
        $(document).on('click','.jihuir',function(){
            var text = $(this).text();
            $('#loadingDiv').css('display','block');   
            $('#popup').slideDown();
        }); 
    
    });
     //点击弹出蒙版 
     $(document).ready(function(){
         $('#loadingDiv').click(function(){
             $(this).fadeOut("slow");
         });
     });
     //点击刷新重新加载页面 
     $(document).ready(function(){
         $('#choujiang .shuaxin').click(function(){
             window.location.href="http://api.booea.cn:8105/huodong/zhuanpanapp.html";
             alert("klabn");
         });
     });
    
    </script>

    页面加载时请求接口

    util util = new util();
            t_dao t_dao = new t_dao();
            t_dao.setDao("cloud-01");
            Rs rs = null;
            String json = "";
            String error = "";
            String userid = util.get("userid");
            String type = util.get("type");//接口标示字段
            if("".equals(userid) || userid == null){
                error += "userid为空";
            }
            if("".equals(type) || type == null){
                error += "type为空";  
            }
            if("".equals(error)){
                //获取剩余抽奖次数接口
                if("getnum".equals(type)){
                    int n = 0;
                    n = t_dao.getNum("t_appuser", "userid='"+userid+"'");
                    //若用户为空,向表中填写用户,默认抽奖次数和分享次数都为1
                    if(n == 0){
                        rs = t_dao.getRs("t_appuser");
                        rs.set("userid", userid);
                        rs.set("num", 1);
                        rs.set("share",1);
                        t_dao.save(rs);
                        json = "{\"userid\":\""+userid+"\",\"num\":\"1\",\"share\":\"1\"}";
                    } else {
                        //存在用户,查询次数
                        String num = t_dao.get("t_appuser","userid='"+userid+"'","num");
                        String share = t_dao.get("t_appuser","userid='"+userid+"'","share");
                        json = "{\"userid\":\""+userid+"\",\"num\":\""+num+"\",\"share\":\""+share+"\"}";
                    }
                }
                //分享接口
                if("addnum".equals(type)){
                    rs = t_dao.getRs("t_appuser","userid='"+userid+"'");
                    String num = rs.get("num");
                    String share = rs.get("share");
                    //若分享次数为1,抽次数奖加1分享次数变为0;若分享次数为0则不进行任何操作
                    if("1".equals(share)){
                        rs.set("num", Tools.isNumber(num)+1);
                        rs.set("share", 0);
                        t_dao.save(rs);
                        json = "{\"userid\":\""+userid+"\",\"num\":\""+(Tools.isNumber(num)+1)+"\",\"share\":\"0\"}";
                    }else{
                        json = "{\"userid\":\""+userid+"\",\"num\":\""+num+"\",\"share\":\"0\"}";
                    }
                }
            }
        util.out(json);
        util = null;
        t_dao = null;

    点击抽奖时请求接口

      //抽奖接口,随机生成中奖序号,返回中奖序号和剩余抽奖次数
            util util = new util();
            t_dao t_dao = new t_dao();
            t_dao.setDao("cloud-01");
            Rs rs = null;
            String json = "";
            String error = "";
            String result = "0";
            int lotteryNum = 0;//剩余抽奖次数
            String userid = util.get("userid");
            String type = util.get("type");//接口标识字段,判断是微信还是app
            if("".equals(userid) || userid == null){
                error += "userid为空";
            }
            if("".equals(type) || type == null){
                error += "type为空";
            }
            if("".equals(error)){
               //随机生成0~1000
              int num = (int) Math.floor(Math.random()*1000);
              //num为0,中奖序号为1,概率千分之一
              if(num == 0){        
                  result = ",1";
              }
              else if(num == 1000){        
                  result = "8";
              } 
              else {
    
                int[] data = {1, 2, 3, 4, 5, 6, 7};
                //floor方法为四舍五入
                int n = data[(int) Math.floor(Math.random()*data.length)];
                switch(n){
                    case 1: result = "2";
                        break;
                    case 2: result = "2";
                        break;
                    case 3: result = "3";
                        break;
                    case 4: result = "4";
                        break;
                    case 5: result = "5";
                        break;
                    case 6: result = "6";
                        break;
                    case 7: result = "7";
                        break;
    
                    default:result = "2";
                }
            }
              //app端的操作,微信端不进行任何操作
            if("app".equals(type)){
                rs = t_dao.getRs("t_appuser","userid='"+userid+"'");
                String n = rs.get("num");
                rs.set("num", Tools.isNumber(n)-1);
                lotteryNum = Tools.isNumber(n)-1;
                t_dao.save(rs);
                t_dao = null;
                //中奖序号为2~7时金币加10
                if(!("1".equals(result)||"8".equals(result))){
                    t_dao t_dao1 = new t_dao();
                    Rs rs1 = t_dao1.getRs("t_user","c_userid='"+userid+"'");
                    int jinbi = Tools.isNumber(rs1.get("c_jinbi"))+10;
                    rs1.set("c_jinbi", jinbi);
                    t_dao1.save(rs1);
                    t_dao1 = null;
                }
            }   
            }
    
        json = "{\"error\":\""+error+"\",\"result\":\""+result+"\",\"num\":\""+lotteryNum+"\"}";
        util.out(json);
        out.clear();  
        out = pageContext.pushBody();  
        util = null;

    说明

    1 中奖概率放在后台 相对来说比较安全
    2 请求接口客户端页面跳转 后台使用jsp写的因为没有用servlet action 所有的点击请求跳转都是点击跳转 跳转成功后加载页面时默认自动请求接口获取数据
    3 乱码问题 页面 后台 数据库 统一编码 同时注意浏览器和ide默认编码
    4 跨域请求问题 网上有很多解决办法 为了省事 我把后台jsp代码和网页代码放在一个服务器上了(本来不是一个服务器上的) 有时间研究一下如何跨域请求
    5 个人笔记 没有任何质量保证和技术参考价值

    好多人私信要源码,现在上传了两个demo。
    H5活动抽奖转盘
    这个UI比较漂亮,功能比较全

    展开全文
  • 抽奖系统设计方案

    2021-02-02 09:38:23
    软件结构特点软件架构风格和策略本系统主要是对数据库进行操作,所以对于本系统的架构风格和策略采用CURD的比较适合。CRUD 是创建(Create)、 读取(Read)、更新...设计模式在本系统中,普通用户可以订阅某些其他...
  • 这几年工作中做过不少营销活动,这里以抽奖活动为例,讨论一下如何设计出一个高可用、高并发的营销系统。 高可用、高并发架构的核心是分流和限流。系统架构时,应根据每一种营销活动的场景与特性,制定不同的分流、...
  • 活动抽奖组件设计

    2019-10-01 10:35:21
    略 公司内部资料以及安全红线 只讨论技术细节 不透漏相关交互和设计 二 交互以及视觉 整体视觉: 略 抽奖模块:大概是以一个花朵的花瓣为抽奖背景,旋转的时候不同的花瓣点亮,省略视觉稿,只讨论技术实现...
  • 最近做了个项目,其中有项目需求是要实现跑马灯抽奖效果,实现此功能主要用到js相关知识,废话不多说,感兴趣的朋友可以阅读下全文。 开始之前先来看上篇文章遗漏的两个问题和几个知识点,是自己重构的过程中需要...
  • HTML5响应式手机模板:h5手机抽奖游戏活动页面集合模板 HTML+CSS+JavaScript 手机模板wap 手机网站模板 企业手机网站模板 手机网站模板 手机模板 响应式手机网站 html5手机网站模板 企业手机网站模板 公司手机网站...
  • HTML5+CSS大作业——年会抽奖网页设计(1页) 常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 ...
  • HTML手机签到转盘抽奖页面模板基于Bootstrap3.3.2制作,响应式设计,包括签到、日历、转盘抽奖、中奖纪录等手机模板页面。
  • ​ 现在微信小程序开发的抽奖小程序的应用场景也很多,翻牌抽奖活动也是近期比较活跃的抽奖活动之一,那么我们怎么来做翻牌小游戏呢,我们可以在飞多多智慧小程序来实现。
  • php的主要工作是负责配置奖项及对应的中奖概率, 当前端页面点击翻动某个方块时会想后台PHP发送ajax请求 ,那么后台PHP根据配置的概率,通过概率算法给出中奖结果,同时将未中奖的奖项信息一并以JSON数据格式发送给...
  • web抽奖抽奖程序

    2017-09-10 17:25:58
    连接数据的的抽奖程序,用户可以自定义抽奖等级,中奖人数,抽过的不能再抽。web项目,连接MySQL数据库,前端页面使用jsp展示抽奖效果。
  • 最近有个转盘抽奖的需求,搜了一下现有的轮子,有的是用jQuery的动画函数实现的,有的是用canvas绘图然后再用高频率的setTimeout调用旋转方法,前者太老了没法简单移植到vue项目,后者感觉性能表现可能不会太好。...
  • 微信小程序首页设计,登录界面设计,仿京东界面,抽奖界面设计,微信小程序期末大作业,有登陆界面的购物商城界面
  • java抽奖接口后台管理源码(包含xml,controller,service.html),与我上传的资源后端接口,前端展示,后台管理一套
  • 抽奖平台一般都是网页设计的 理念的逻辑对于网页上的大量中奖什么贵重物品的选项一般后台设置的都是千万分之一的中奖率。所以我个人觉得娱乐一下就好了 没必要为了这个中大奖去“守株待兔”一样天天蹲在哪里怎么用...
  • 活动抽奖前端实现.zip

    2019-06-10 13:55:55
    公司正好在进行618大促,没来得及设计数据库和管理后台,只能用前端储存来实现一下抽奖功能,贴一下代码,希望大家多提建议。
  • 当然是要肝项目了,肝一个有互联网技术体系、有分布式架构运用、有DDD思想和设计模式实践的真实场景项目,才能让一个尚未接触此类项目或是长期陷入CURD的熟练工,打开视野,快速成长起来。 整整半年,小傅哥都在做...
  • 抽奖管理系统可以说是一个综合性的信息管理系统,这它包含了个人资料管理、抽奖报名管理、抽奖结果管理、留言管理等多种功能。该系统分为用户和管理员两种身份。个人中心模块主要用于用户修改个人资料,添加用户...
  • js抽奖活动简单实现

    千次阅读 2018-07-25 15:13:36
    html代码实现:页面中只有一个内容区块,其中包含一个文本和两个按钮。 (注意:无论案例大小,始终试着将结构和样式相分离。) &lt;div class="content"&gt; &lt;p class="text&...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,176
精华内容 2,870
关键字:

抽奖活动页面设计