文档章节

[分享] 来,做一个问卷调查(有抽奖!)

没错就是酱紫
 没错就是酱紫
发布于 2016/07/28 09:16
字数 2644
阅读 93
收藏 0
点赞 0
评论 0

前言

    “小王,明天公司在***举办一个xxx产品发布会,你今天准备2000份问卷调查。还有,我们这次还做一个抽奖活动,也记得弄一个抽奖箱和一些抽奖球哦。”

    ……

    活动结束了,小王想起早上捧着这2000张问卷和抽奖箱的情景,生平第一次对弘二头肌起了念想。回过神来看着桌子上回收回来的问卷,整整齐齐的像座小山一样好看,但领导依然不太满意,因为只回收了1000来张。可是1000多张的样本已经足够了呀,统计也很花时间的呀。小王本想反驳,但他什么也没说,只是下意识地摸了摸自己的背包,包里装着那丢失的900多张问卷。

    以上剧情根据真实故事改编,如有雷同,算你倒霉。

数字化大背景

    现在还有不少活动是用纸质问卷来做调查的,几千张纸是小钱,但后期统计这一堆数据可是费神费力的苦力活。以前设备落后,手机上做问卷体验太差。但现在是80岁大爷都会玩智能手机的年代,一个二维码也解决了入口问题,在线调查问卷的体验也就上来了。再加上现在办个活动什么的都是用微信宣传微信组织,配合一点抽奖活动,观众们还是愿意去回答的。既然已经具备了在线问卷的大环境,下面就让小茄带大家来做一个在线问卷调查吧。

需求

先来分析一下需求。

1、在线问卷调查的使用者都是市场运营的工作人员,他们对编程的了解很少,所以后台操作必须简单明了。

2、输入为问题信息,输出为回答统计信息,输出需要使用可视化图表呈现,必要时也提供元数据。

3、最好能带一点圈粉属性,扫一扫关注公众号然后才开始答题。硬生生让人关注公众号,许多人可能无动于衷,但增加了一个问卷和抽奖的梗,关注公众号就显得非常合理自然。

4、最好能带一点统计功能,统计一下到底多少人打开了页面,从而为后续改进提供数据分析支撑。

其中1、2是刚需,3、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’;  建议一定要拉着妹子的手,手把手地把这个好用的技能教给她。

更进一步

通过上面几步,一个简单好用的在线问卷就已经实现了。细心的你估计发现了,3、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。既然上面都说要圈粉了,那就增加一个分享接口就好了,后面判断这个分享接口被调用的次数就能间接得到某个时间段的访问量了。对了,每个接口还按照页面区分好了,所以你不用担心其他页面数据的干扰。

然后,然后小王终于可以忘了曾一度被问卷调查所支配的恐怖和被囚禁于数据统计中的那份屈辱了。

全文完,点赞不谢!

© 著作权归作者所有

共有 人打赏支持
没错就是酱紫
粉丝 6
博文 16
码字总数 33938
作品 0
东城
程序员
【云栖大会精华汇】2017杭州云栖大会主论坛、分论坛在内的100+视频分享

【2017杭州云栖大会视频资料集合】 2017杭州云栖大会主论坛、分论坛在内的100+视频分享 涵盖重磅产品发布、数据库专场、阿里云ET大脑生态专场、新零售专场、异构计算&高性能计算专场、开发者...

乔川 ⋅ 2017/11/02 ⋅ 0

中国开发者生存发展情况调查问卷

亲爱的朋友, 为了解移动开发者的生存状况和现实需求,客观呈现移动开发者对广告平台认知和使用情况,DCCI举办了此次“2015年中国移动开发者生存发展调查”活动。此次活动旨在为开发者和各大...

dcci ⋅ 2015/08/11 ⋅ 0

2015年中国移动开发者生存发展调查

亲爱的朋友, 为了解移动开发者的生存状况和现实需求,客观呈现移动开发者对广告平台认知和使用情况,DCCI举办了此次“2015年中国移动开发者生存发展调查”活动。此次活动旨在为开发者和各大...

dcci ⋅ 2015/08/12 ⋅ 0

程序员的时间都去了哪儿?

如果你认为软件工程师将所有时间都花在了构思巧妙算法和令人惊叹的图形例程,然后将这些转化为优雅紧凑的代码上,那你就错了。 据Electric Cloud的一份数据报告显示,开发者近 20% 的时间可能...

oschina ⋅ 2013/05/01 ⋅ 39

掘金浏览器插件用户有奖小调查~

一定要使用掘金浏览器插件后再填问卷哦~ 点击进入参加掘金浏览器插件有奖调研 什么?你还不知道掘金浏览器插件!!快来了解一下吧~ juejin.im/extension 掘金浏览器插件为设计师、程序员、...

金正皓 ⋅ 2017/10/13 ⋅ 0

小伙伴们,C语言学习调查问卷来啦

亲爱的小伙伴们,大家好啊~~~ 为了给大家在我的微信公众号:C语言编程技术分享 http://weixin.qq.com/r/mkmguCDEan0VrWTb9xyI (二维码自动识别) 上提供更好的C语言编程学习的资料,希望您...

石家的鱼 ⋅ 2017/11/01 ⋅ 0

团队学习分享与学习分享的能力层级

我们团队是固定每周一次的团队学习分享,围绕工作相关的技术与心得,每个人都可以进行微分享。微分享的门槛低,只要有五分钟的内容,就可以进行分享,这样一来,能减轻分享人的负担。而且,每...

知猪侠_CD ⋅ 2017/12/22 ⋅ 0

使用 Node.js、Express、AngularJS 和 MongoDB 构建一个Web程序

使用 Node.js、Express、AngularJS 和 MongoDB 构建一个实时问卷调查应用程序 2014 年 3 月 20 日 04:53 | 抄本 Joe Lennon 高级移动应用程序开发人员 Joe Lennon 是来自爱尔兰科克市的软件开...

Bob_Zheng ⋅ 2015/12/19 ⋅ 0

神经网络简介(翻译)

原文链接:神经网络简介(翻译) 微信公众号:机器学习养成记 搜索添加微信公众号:chenchenwings 看起来很熟悉?这是我们的老朋友,逻辑回归。将这个模型解释为具有S型“激活函数”的线性感知...

小沁_3ca9 ⋅ 2017/12/11 ⋅ 0

关于动态生成表单 已经对动态生成表单数据的填写以后的存储问题

我最近看到腾讯的调查问卷 想实现腾讯调查问卷 那种东西,非常好奇 后台可以通过html表单元素生成表单,并配置生成表单模板块 这张模板是怎么保存的,什么思路? 生成表单以后 客户选择的表单...

votouch ⋅ 2017/02/14 ⋅ 1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

服务网关过滤器

过滤器作用 我们的微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了。但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都需要有一定的限制,系统并不会...

明理萝 ⋅ 6分钟前 ⋅ 1

【2018.06.21学习笔记】【linux高级知识 14.1-14.3】

14.1 NFS介绍 14.2 NFS服务端安装配置 14.3 NFS配置选项

lgsxp ⋅ 15分钟前 ⋅ 0

Day18 vim编辑模式、命令模式与练习

编辑模式 命令模式 :nohl 不高亮显示 :x与:wq类似,如果在更改文件之后操作,两者效果一样;如果打开文件,没有任何操作; :wq会更改mtime,但是:x不会。 练习题 扩展 vim的特殊用法 ht...

杉下 ⋅ 19分钟前 ⋅ 0

Enum、EnumMap、EnumSet

1、Enum 不带参数 public enum Car { AUDI { @Override public int getPrice() { return 25000; } }, MERCEDES { ......

职业搬砖20年 ⋅ 19分钟前 ⋅ 0

Java中的锁使用与实现

1.Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。 在Lock出现之前,java程序是靠synchronized关键字实现锁功能的,而Java SE5之后,...

ZH-JSON ⋅ 21分钟前 ⋅ 0

线程组和 ThreadLocal

前言 在上面文章中,我们从源码的角度上解析了一下线程池,并且从其 execute 方法开始把线程池中的相关执行流程过了一遍。那么接下来,我们来看一个新的关于线程的知识点:线程组。 线程组 ...

猴亮屏 ⋅ 22分钟前 ⋅ 0

相对路径和绝对路径

基本概念   文件路径就是文件在电脑中的位置,表示文件路径的方式有两种,相对路径和绝对路径。在网页设计中通过路径可以表示链接,插入图像、Flash、CSS文件的位置。   物理路径:物理路...

临江仙卜算子 ⋅ 26分钟前 ⋅ 0

消息队列属性及常见消息队列介绍

什么是消息队列? 消息队列是在消息的传输过程中保存消息的容器,用于接收消息并以文件的方式存储,一个队列的消息可以同时被多个消息消费者消费。分布式消息服务DMS则是分布式的队列系统,消...

中间件小哥 ⋅ 28分钟前 ⋅ 0

java程序员使用web3j进行以太坊开发详解

如何使用web3j为Java应用或Android App增加以太坊区块链支持,教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤器...

笔阁 ⋅ 28分钟前 ⋅ 0

vim编辑模式、vim命令模式

vim编辑模式 使用vim filename 进入的界面是一般模式,在这个模式下虽然我们能够查看,复制,剪切,粘贴,但是不能编辑新的内容,如何能直接写入东西呢?这就需要进入编辑模式了,从一般模式...

李超小牛子 ⋅ 31分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部