基於小程序·雲開發構建高考查分小程序丨實戰

2019高考報名人數達到了 1031 萬的新高,做爲一名三年前參考高考的準程序猿,趕在高考前,加班加點從零開始作了一款高考查分小程序,算是一名老學長送給學弟學妹們的高考禮。上線僅 1 個月,用戶數就突破了 1k,關於小程序的介紹就很少說了,能夠去搜【歷年高考分數線查詢】體驗,今天主要談談技術原理和實現細節。git

圖片描述

數據來源

小程序後臺共收錄近 30w 條數據,包含 2008-2017 年全部重點高校的各個批次的文理分科錄取分數線以及 2008-2018 全部採用新課標一卷、新課標二卷、新課標三卷以及部分自主命題省份的從提早批到高職專科批的錄取分數線,勉強稱得上內容翔實。github

圖片描述

全部數據均採集自各大院校和各高考相關網站,因爲數據量巨大,爲提升速度,使用了 concurrent.futures (須要 Python3.5+) 模塊裏的 ThreadPoolExecutor 來構建線程池來併發執行多任務。sql

數據庫採用的是 PgSQL,一款號稱世界上最強大的開源數據庫產品,全部數據均存在新建的 gaokao 數據庫中,其下有兩個表,university(院校的錄取分)和 province(省份的批次線)數據庫

university 表說明json

字段 解釋
name 院校名稱
stu_loc 生源地
stu_wl 文理科
pc 錄取批次
year 年份
score 錄取平均分

province 表說明小程序

字段 解釋
year 年份
stu_loc 考生所在地
stu_wl 文理科
pc 批次
control 本批次最低控制線

30w 的數據量,多個站點,併發爬取,數據衝突是不可避免地,在執行插入以前,首先過濾掉殘缺不全的數據,好比在插入 university 表時某條數據缺乏 pc 字段,那麼這條記錄就應該被捨棄,最嚴重的是數據重複,我採用的解決辦法是:先查詢待插入的數據是否已經存在, university 表的主碼是(name,stu,stu_wl,pc,year),由於現實約束一個院校只能在一個年份在一個類別一個批次只能有一個錄取平均分,若是不存在,才執行最後的插入,並 commit 提交事務。後端

後臺搭建

在 30w 條數據拿到後,我打算後臺採用 Flask+PgSQL 的模式實現,甚至在後臺在阿里雲服務器部署好,小程序端在開發者工具聯調經過以後,小程序上線遇到到一個大麻煩,由於小程序要求線上運行不能經過 ip 地址訪問後臺,必須經過備案的域名訪問,域名購買一個倒不麻煩,只是域名備案比較耗時間,須要一週多時間,而當時距離高考也就不到 5 天,在手足無措之時,無心間看到小程序雲開發,關於小程序雲開發,官網的介紹是:微信小程序

開發者可使用雲開發開發微信小程序、小遊戲,無需搭建服務器,便可使用雲端能力。

雲開發爲開發者提供完整的原生雲端支持和微信服務支持,弱化後端和運維概念,無需搭建服務器,使用平臺提供的 API 進行核心業務開發,便可實現快速上線和迭代,同時這一能力,同開發者已經使用的雲服務相互兼容,並不互斥。數組

也就是說,只要把數據導入小程序自帶的後臺,就能經過小程序平臺的 API 訪問到這些數據,之前瞭解過第三方的 LeanCloud雲 和 Bomb 雲,沒想到小程序如今集成了這些功能,不得不佩服一下騰訊。promise

也就是,接下來的後臺的工做是主要是導入數據,查詢小程序後臺可知,後臺支持導入 json 或者 csv 格式的數據。因而我就寫了個腳本,把數據從本地數據庫導出到 json 文件中:

import psycopg2
import json

# 鏈接 pgsql 數據庫,爲保證隱私,密碼已隱藏
conn = psycopg2.connect(database="gaokao", user="postgres", password="*******", host="127.0.0.1", port="5432")
cur = conn.cursor()

cur.execute('select stu_loc,year,stu_wl,pc,control from province')
result = []
query_res = cur.fetchall()
for i in query_res:
    item = {}
    item['stu_loc'] = i[0]
    item['year'] = i[1]
    item['wl'] = i[2]
    item['pc'] = i[3]
    item['score'] = i[4]
    result.append(item)
# indent=2 控制 json 格式的縮進
# ensure_ascii 控制中文的正常顯示
with open("province.json", 'w', encoding="utf-8") as f:
    f.write(json.dumps(result, indent=2, ensure_ascii=False))

這裏還有有個坑須要說明一下,小程序後臺要求的 json 格式和咱們日常意義上的 json 格式還有點區別,首先,json 的全部內容不能被 [ 和 ] 包括起來,並且每一個被 {} 所包括得數據項之間不能有逗號。

圖片描述

選用 notepad++ 打開原來的 json 文件,使用替換功能就能解決,把 [ 和 ] 替換成空格,把 },替換成 } 便可。

修改以後,在小程序後臺經過導入該 json 文件,後臺搭建就基本完成了。

小程序端編寫

關於小程序端的編寫,我主要談談兩點經驗,第一是頁面的編寫,好比下面這個界面。

圖片描述

最開始想實現這樣的效果,徹底沒有思路,最後在從自定義模態彈窗那獲得了思路,一開始地區院校這個下拉框對應的佈局是隱藏的,在 wxml 文件中經過 hidden=true 控制,一點擊 地區/院校 下拉框,就把 hidden 置爲 false,若是開始有其餘下拉框對應的佈局的 hidden 屬性是 false 的話,同時要把這些佈局的 hidden 屬性置爲 true 來隱藏其餘佈局,固然,這裏的 true or false 須要在 js 裏經過 setData() 動態修改,把修改後的數據從數據層渲染到視圖層。

第二是關於小程序雲開發的原生 Bug,查詢後臺時一次只能最多查詢到 20 條數據,要實現一次獲得全部匹配的結果,須要解決兩個問題,第一個問題很天然而然就能想到,第一次查到 20 條數據後,第二次跳過前 20 條再取 20 條,第三次跳過前 40 條再取 20 條,以此類推;還有一個更爲致命的問題,查詢後臺的 API 獲取結果的回調函數的 異步 的,也就是說,爲了保證得到完整數據,第二次查詢須要寫在第一次查詢的回調裏,第三次查詢須要寫在第二次查詢的回調裏,並且你還不能顯式地知道要查詢多少次,須要寫多少層這樣的嵌套,以及煩人的同名變量覆蓋問題,這就是所謂的 異步地獄。爲了解決這個問題,須要咱們編寫代碼把這個異步方法轉成同步的,具體作法是:

先在所要添加功能的js頁面中導入 runtime.js 文件,同時把runtime.js文件放入相應文件夾

const regeneratorRuntime = require("../runtime");

runtime.js 下載地址:https://github.com/inspurer/CampusPunchcard/blob/master/runtime.js

同時模仿下例代碼完成業務邏輯:

// 查詢可能較慢,最好加入加載動畫​
wx.showLoading({
          title: '加載中',
        })
        const countResult = await db.collection('province').where({
          stu_loc: name,
          pc: pici,

        }).count()
        const total = countResult.total
        //計算需分幾回取
        const batchTimes = Math.ceil(total / MAX_LIMIT)
        // 承載全部讀操做的 promise 的數組
        //初次循環獲取雲端數據庫的分次數的promise數組
        for (let i = 0; i < batchTimes; i++) {
          const promise = await db.collection('province').where({
            stu_loc: name,
            pc: pici,
          }).skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
          //二次循環根據​獲取的promise數組的數據長度獲取所有數據push到newResult數組中
          for (let j = 0; j < promise.data.length; j++) {
            var item = {};
            item.code = i * MAX_LIMIT + j;
            item.name = promise.data[j].stu_loc;
            item.year = promise.data[j].year;
            item.wl = promise.data[j].wl;
            item.pc = promise.data[j].pc;
            item.score = promise.data[j].score;
            console.table(promise.data)
            newResult.push(item)
          }
        }
        if (newResult.length != 0) {
          that.setData({
            hasdataFlag: true,
            resultData: newResult
          })
        } else {
          that.setData({
            hasdataFlag: false,
            resultData: newResult
          })
        }
        // 隱藏加載動畫
        wx.hideLoading()

以上就是我本次開發的一些心得體會,歡迎批評指正。

源碼連接

https://github.com/TencentClo...

若是你有關於使用雲開發CloudBase相關的技術故事/技術實戰經驗想要跟你們分享,歡迎留言聯繫咱們哦~比心!

圖片描述

相關文章
相關標籤/搜索