前言html
“小王,明天公司在***举办一个xxx产品发布会,你今天准备2000份问卷调查。还有,咱们此次还作一个抽奖活动,也记得弄一个抽奖箱和一些抽奖球哦。”前端
……算法
活动结束了,小王想起早上捧着这2000张问卷和抽奖箱的情景,平生第一次对弘二头肌起了念想。回过神来看着桌子上回收回来的问卷,整整齐齐的像座小山同样好看,但领导依然不太满意,由于只回收了1000来张。但是1000多张的样本已经足够了呀,统计也很花时间的呀。小王本想反驳,但他什么也没说,只是下意识地摸了摸本身的背包,包里装着那丢失的900多张问卷。sql
以上剧情根据真实故事改编,若有雷同,算你倒霉。数据库
数字化大背景编程
如今还有很多活动是用纸质问卷来作调查的,几千张纸是小钱,但后期统计这一堆数据但是费神费力的苦力活。之前设备落后,手机上作问卷体验太差。但如今是80岁大爷都会玩智能手机的年代,一个二维码也解决了入口问题,在线调查问卷的体验也就上来了。再加上如今办个活动什么的都是用微信宣传微信组织,配合一点抽奖活动,观众们仍是愿意去回答的。既然已经具有了在线问卷的大环境,下面就让小茄带你们来作一个在线问卷调查吧。后端
需求api
先来分析一下需求。服务器
一、在线问卷调查的使用者都是市场运营的工做人员,他们对编程的了解不多,因此后台操做必须简单明了。微信
二、输入为问题信息,输出为回答统计信息,输出须要使用可视化图表呈现,必要时也提供元数据。
三、最好能带一点圈粉属性,扫一扫关注公众号而后才开始答题。硬生生让人关注公众号,许多人可能无动于衷,但增长了一个问卷和抽奖的梗,关注公众号就显得很是合理天然。
四、最好能带一点统计功能,统计一下到底多少人打开了页面,从而为后续改进提供数据分析支撑。
其中一、2是刚需,三、4是软需。
后端
简单分析能够发现,开发这个小应用最主要的工做是在后端开发部分,并且这个主要是以数据处理为主,显然采用面向数据库编程的方式来开发更为合适。
面向数据库开发第一步,先来定义数据库吧。先使用excel作出相应的表格,大概是这样的:
而后就是分表写数据库,将question、options、answer分红3个表,以questionID作索引关联3个表,另外用户信息和奖品信息也要用一个数据表来保存。原本这里想用MySQL for Excel来实现,这样市场的妹子们也能简单上手。不过想一想仍是导出一个sql脚本更好,毕竟这样就能够手把手教妹子怎么把问卷数据写到sql文件里面了。(/▽╲)
问卷数据的读写均可以用WeX5通用的查询接口来实现数据的读写,这里再也不赘述。
这里要本身写的是抽奖算法的实现,要点是保证中奖概率的均一性。可是,算法也不能太死板,主要看脸,哦不,主要看奖品大小。
若是有大奖,那么大奖单独出来全部人抽一次会比较好,这样能有效活跃起现场气氛。这种状况下能够设置一个抽奖期间,后台统计这个期间内的人数,而后在这我的数里面随机选中一个便可。若是都是些小奖品,那么确定就是先答题后抽奖,抽奖结果要立刻呈现。也就是每一个观众抽奖的时刻是不一样的,并且抽奖的人数也是未知的,这种状况下要保证先后抽奖的人都有相同的中奖概率,并且要把奖品发完的话,好像很难的样子。可是,既然是小奖品,按照先来先得发不完也没事的原则,每次都查询当前奖品池的奖品,若是还有奖品则用随机数判断是否中奖,不然就不中奖就完事了,简单粗暴。
因此说,一切以实际出发,把重心放到重要的事上,把吃奶的力用到吃奶上,才是王道。
贴个抽奖算法的简单实现:
1 public static JSONObject drawPrize(JSONObject params, ActionContext context) throws SQLException, NamingException { 2 // 获取参数 3 String batch = params.getString("batch"); 4 int index = params.getInteger("index"); 5 String weixinID = params.getString("weixinID"); 6 JSONObject result = new JSONObject(); 7 Connection conn = context.getConnection(DATASOURCE); 8 9 try { 10 conn.setAutoCommit(false); 11 try { 12 // 获取user 13 Statement stat = conn.createStatement(); 14 try { 15 ResultSet rsUser = stat.executeQuery("SELECT * FROM user WHERE fBatch = '" + batch + "' AND fWeixinID = '" + weixinID + "'"); 16 if (!rsUser.next()) { 17 // 未登记 18 result.put("code", -2); 19 } else if (!Utils.isEmptyString(rsUser.getString("fPrize" + index))) { 20 // 已中奖 21 result.put("code", -1); 22 result.put("prize", rsUser.getString("fPrize" + index)); 23 } else { 24 // 读取奖池 25 List<String> prizes = new ArrayList<String>(); 26 ResultSet rsPrize = stat.executeQuery("SELECT * FROM prize WHERE (fTotal - COALESCE(fCount, 0)) > 0 AND fBatch = '" + batch + "' AND fIndex = " + index); 27 while (rsPrize.next()) { 28 prizes.add(rsPrize.getString("fName")); 29 } 30 if (prizes.size() == 0) { 31 // 奖池空了 32 result.put("code", -3); 33 } else { 34 Random r = new Random(); 35 // 看运气 36 int luck = r.nextInt(10); 37 if (luck > 0) { 38 // 未中奖 39 result.put("code", 0); 40 } else { 41 // 抽奖 42 luck = r.nextInt(prizes.size()); 43 String prize = prizes.get(luck); 44 45 int k = stat.executeUpdate("UPDATE prize SET fCOUNT = COALESCE(fCount, 0) + 1 WHERE (fTotal - COALESCE(fCount, 0)) > 0 AND fBatch = '" + batch + "' AND fIndex = " 46 + index + " AND fName = '" + prize + "'"); 47 if (k == 0) { 48 // 未中奖 49 result.put("code", 0); 50 } else { 51 // 记录数据 52 stat.executeUpdate("UPDATE user SET fPrize" + index + " = '" + prize + "' WHERE fBatch = '" + batch + "' AND fWeixinID = '" + weixinID + "'"); 53 result.put("code", 1); 54 result.put("prize", prize); 55 } 56 } 57 } 58 } 59 } finally { 60 stat.close(); 61 } 62 conn.commit(); 63 } catch (SQLException e) { 64 conn.rollback(); 65 throw e; 66 } 67 } finally { 68 conn.close(); 69 } 70 71 return result; 72 }
前端
问卷部分:前端固然是一个单页应用了。由于问题形式差很少,因此能够作一个问题模板,将从后端获取到的依次问题数据渲染到页面。这里能够用WeX5的数据组件和模板绑定来实现。另外要考虑到的一个问题是问卷的原子性,就是说要么不回答,要么就要回答全部题目。因此问卷的提交是一次性的,不能作成每道题都提交的形式。由于数据量不大,因此能够一次请求把全部question、option都取回来,减小请求数。
抽奖部分:这里使用了摇一摇的形式来进行抽奖。原理很简单,就是判断加速度计在一个时间区间内的变化率大小,当变化率超过必定阈值时就说明当前手机受力突增,也就是正在“摇一摇”的状态。具体实现是监听’devicemotion’事件,代码以下:
1 // 摇一摇事件
2 if (window.DeviceMotionEvent) {
3 window.addEventListener('devicemotion', deviceMotionHandler, false);
4 } else {
5 alert('本设备不支持摇一摇');
6 }
7 function deviceMotionHandler(eventData) {
8 var acceleration = eventData.accelerationIncludingGravity;
9 var curTime = new Date().getTime();
10 if ((curTime - last_update) > 100) {
11 var diffTime = curTime - last_update;
12 last_update = curTime;
13 x = acceleration.x;
14 y = acceleration.y;
15 z = acceleration.z;
16 var speed = Math.abs(x + y + z - last_x - last_y - last_z) / diffTime * 10000;
17
18 if (speed > SHAKE_THRESHOLD) {
19 self.imgRockClick();
20 }
21 last_x = x;
22 last_y = y;
23 last_z = z;
24 }
25 }
输出部分:问卷数据采集完以后,可使用echart来展示统计数据。具体教程能够看看官方文档:http://docs.wex5.com/integrate-echarts/ ,可是不赞同使用单文件的形式,建议采用模块按需载入的方式。这里用到的无外乎是柱状或者饼状图,因此只加载基类和这两类js文件便可。
若是妹子要元数据怎么办?一行代码搞定:select * from answer into outfile ‘d:/answer.xls’; 建议必定要拉着妹子的手,手把手地把这个好用的技能教给她。
更进一步
经过上面几步,一个简单好用的在线问卷就已经实现了。细心的你估计发现了,三、4点需求还没实现呢。好吧,下面看看这两点怎么实现,没兴趣的同窗能够直接到文章末尾点赞了,谢谢配合。
首先是增长圈粉属性。
这个前提就是你要把应用部署在你的公众号服务器上。尚未服务器?Cloud X5 搞起吧,简明教程:http://docs.wex5.com/about-cloudx5/
圈粉主要是要把你的应用入口改为微信网页受权页面,也就是这个地址:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect,记得里面的大写字母参数要改为你本身的参数。回调 uri 记得要作URI转码。通常来讲咱们还要获取用户信息的,因此这里的SCOPE填入snsapi_userinfo。其余参数请参考微信开发者文档自行补充,这里就不赘述了:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842&token=&lang=zh_CN。若是对这一部分不太熟悉的话,能够看看小茄上一篇【30分钟作一个二维码名片应用】http://www.wex5.com/openway_qrcode/,里面有详细介绍如何使用WeX5进行微信公众号开发。
再来看看统计功能:在2016年7月4号以前,你都只能在网页中引用站长工具啦、百度统计啦、谷歌统计来进行数据统计。而如今你也可使用微信自家的统计功能了,这个是专门统计微信客户端的访问量的。传送门:https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1467639271&version=1&lang=zh_CN,直接在后台就能看。因为它统计的是使用了JSSDK的页面,因此这个页面也须要配置jssdk_config。既然上面都说要圈粉了,那就增长一个分享接口就行了,后面判断这个分享接口被调用的次数就能间接获得某个时间段的访问量了。对了,每一个接口还按照页面区分好了,因此你不用担忧其余页面数据的干扰。
而后,而后小王终于能够忘了曾一度被问卷调查所支配的恐怖和被囚禁于数据统计中的那份屈辱了。
全文完,点赞不谢!