前言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。既然上面都說要圈粉了,那就增長一個分享接口就行了,後面判斷這個分享接口被調用的次數就能間接獲得某個時間段的訪問量了。對了,每一個接口還按照頁面區分好了,因此你不用擔憂其餘頁面數據的干擾。
而後,而後小王終於能夠忘了曾一度被問卷調查所支配的恐怖和被囚禁於數據統計中的那份屈辱了。
全文完,點贊不謝!