小程序雲開發實現一個投票應用

由於最近想實踐一下小程序的雲開發能力,因而設計開發了一個簡單的投票應用,歡迎感興趣的一塊兒學習交流。

代碼倉庫 https://github.com/luosijie/m...前端

因爲小程序【我的開發者】不開放【投票】類目,因此就不能在線預覽了,我放幾張應用的截圖git

數據庫設計

總共用到了3個集合github

1. users (用戶集合)

基本上直接保存用用戶的userInfo數據數據庫

{
  "_id":"023ce9555ff068de0314b5521c313ee6",
  "OPENID":"oZK45EoiyFzv...7R64I",
  "nickName":"LSJ",
  "gender":1,
  "language":"zh_CN",
  "city":"Xiamen",
  "province":
  "Fujian",
  "country":"China",
  "avatarUrl":"https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTL9lhZHZdYsMx3mjhZZYbbE5OZhUqUefNtsibkhdrSTIdpdhzv34lYHXtafMjuoibJ8JwTj5VM76CkA/132"
}
2. votes (投票集合)
{
  "_id":"21ded5cb5ff5f0530407988a4e8f18a5", // 惟一id
  "creator":"o-ZK45EoiyFzvevQyQTSZUV7R64I", // 發起人
  "title":"阿斯頓大的as da", // 標題
  "desc":"阿斯頓阿斯頓", // 描述
  "startTime":"2021-1-7", // 開始日期
  "endTime":"2021-1-8", // 結束日期
  "state":"ing" // 狀態
}
3. options (選項集合)
{
  "_id":"be7fb3985ff5f05403068303431d580b", // 惟一id
  "vote_id":"21ded5cb5ff5f0530407988a4e8f18a5", // 選項對應的投票_id
  "title":"阿斯頓大的大的", // 標題
  "desc":"撒打算的灑大地上阿斯頓", // 描述
  "image":"http://tmp/2jVXjjLScAyNf0dffe2c5fc6479bee73fe954b64a3e7.png", // 配圖
  "users":["o-ZK45EoiyFzvevQyQTSZUV7R64I"] // 該選項的投票者
}

雲函數開發

總共寫了6個雲函數json

1. addRecord 新增投票記錄
/**
 * 新增投票記錄
 * @param {String} title 標題
 * @param {String} desc 描述
 * @param {String} startTime 開始日期
 * @param {String} endTime 結束日期
 * @param {String} anonymous 匿名
 * @param {String} min 容許小投票數
 * @param {String} max 容許最大投票數
 * @param {String} type 投票類型:normal; pk
 * @returns {Object} 包含投票_id
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  const voteCollection = db.collection('votes')
  const data = {
    creator: wxContext.OPENID, // 發起人
    title: event.title,
    desc: event.desc,
    startTime: event.startTime,
    endTime: event.endTime,
    anonymous: event.anonymous,
    min: event.min,
    max: event.max,
    type: event.type,
    state: 'ing'
  }
  // 集合投票votes:新增記錄
  const res = await voteCollection.add({
    data
  })
  // 集合選項options: 新增記錄
  const options = event.options
  const optionCollection = db.collection('options')
  const optionPromise = options.map( ele => {
    const option = {
      vote_id: res._id,
      ...ele
    }
    return optionCollection.add({
      data: option
    })
  })
  let resOptions = await Promise.all(optionPromise)
  resOptions = resOptions.map(e =>  e._id)
  // 返回投票結果
  return {
    success: true,
    message: '新增投票成功',
    ...res
  }
}
2.getRecordDetail 獲取投票詳情
/**
 * 獲取投票詳情
 * @param {String} _id 投票_id
 * @return {Object} 投票數據
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  const _id = event._id
  const OPENID = cloud.getWXContext().OPENID
  // 查找集合中的投票數據
  const voteCollection = db.collection('votes')
  // 聚合聯表查詢
  const voteQuery = await voteCollection
  .aggregate()
  .match({ _id })
  .lookup({
    from: 'users',
    localField: 'creator',
    foreignField: 'OPENID',
    as: 'creator'
  })
  .end()
  let vote = {}
  if (voteQuery && voteQuery.list.length) {
    vote = voteQuery.list[0]
    vote.creator = vote.creator[0]
    // 判斷是否當前投票的發起人
    vote.isOwner = vote.creator.OPENID === OPENID

    // 查找集合中的選項數據
    const optionsCollection = db.collection('options')
    const optionsQuary = await optionsCollection
    .aggregate()
    .match({ vote_id: _id })
    .lookup({
      from: 'users',
      localField: 'users',
      foreignField: 'OPENID',
      as: 'users'
    })
    .end()
    vote.options = optionsQuary.list
    // 統計已經投票的人數
    let votedTotal = 0
    vote.options.forEach(e => {
      if (e.users && e.users.length) {
        votedTotal += e.users.length
      }
    })
    vote.votedTotal = votedTotal
    // 計算當前投票的狀態
    if (vote.state !== 'end') {
      // 未開始
      if (new Date().getTime() < new Date(vote.startTime).getTime()) {
        vote.state = 'pre'
      }
      // 已過時 = 已結束
      if (new Date().getTime() > new Date(vote.endTime).getTime()) {
        vote.state = 'end'
      }
    }
    return {
      success: true,
      data: vote
    }
  } else {
    return {
      success: false,
      message: '找不到投票信息'
    }
  }
}
3. vote 投票操做
/**
 * 投票操做
 * @param {String} voteId 投票_id
 * @param {String} optionId 選項_id
 * @return {Object} 投票結果
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  const _id = event.optionId
  const vote_id = event.voteId
  const OPENID = cloud.getWXContext().OPENID
  // 獲取當前投票數據對應的全部選項數據
  const options = db.collection('options')
  let voteOptions = await options.where({ vote_id }).get()
  voteOptions = voteOptions.data
  // 判斷用戶是否投過票
  let curOptionUsers = []
  for (let i = 0; i < voteOptions.length; i++) {
    // 找到選項中全部投過票的用戶
    const users = voteOptions[i].users
    if (users && users.length) {
      if (voteOptions[i]._id === _id) {
        curOptionUsers = users
      }
      if (users && users.length) {
        // OPENID重複-說明已經投過票->直接返回
        if (users.indexOf(OPENID) > -1) {
          return {
            success: false,
            message: '您已經投過票了'
          }
        }
      }
    }
  }
  // 沒有投票->將當前用戶OPENID插入到對應的字段
  curOptionUsers.push(OPENID)
  const res = await options.where({ _id }).update({
    data: {
      users: curOptionUsers
    }
  })
  return {
    success: true,
    data: res,
    message: '投票成功'
  }
}
4. getRecordPage 獲取個人投票記錄分頁
/**
 * 獲取個人投票記錄分頁
 * @param {Number} no 頁碼
 * @param {Number} size 頁數
 * @return {Object} 投票數據列表和總數
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  const size = event.size
  //獲取接口參數
  const no = event.no
  const OPENID = wxContext.OPENID
  const voteCollection = db.collection('votes')
  // 查找集合中的投票數據
  const votes = await voteCollection.aggregate()
  .match({
    creator: OPENID
  })
  .lookup({
    from: 'options',
    localField: '_id',
    foreignField: 'vote_id',
    as: 'options'
  })
  .sort({
    _id: -1
  })
  .skip((no - 1) * size)
  .limit(size)
  .end()
  // 計算總數
  const total = await voteCollection.count()
  let data = votes.list
  // 計算投票狀態
  if (data.length) {
    data = data.map(e => {
      if (e.state !== 'end') {
        // 未開始
        if (new Date().getTime() < new Date(e.startTime).getTime()) {
          e.state = 'pre'
        }
        // 已過時 = 已結束
        if (new Date().getTime() > new Date(e.endTime).getTime()) {
          e.state = 'end'
        }
      }
      // 統計已投票人數
      let votedTotal = 0
      const options = e.options
      options.forEach(o => {
        if (o.users && o.users.length) {
          votedTotal += o.users.length
        }
      })
      delete e.options
      return {
        ...e,
        votedTotal
      }
    })
  }
  return {
    total,
    data
  }
}
5. login 登陸註冊
/**
 * 登陸註冊
 * @param {String} OPENID 從cloud.getWXContext()中獲取
 * @return {Object} 用書數據
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()

exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  // 查找集合中的用戶數據
  const userCollection = db.collection('users')
  const users = await userCollection.where({ OPENID: wxContext.OPENID }).get()
  let user
  if (users && users.data.length) {
    // 用戶已經存在-直接賦值用戶數據
    user = users.data[0]
  } else {
    // 新用戶-向數據庫插入用戶數據
    user = {
      OPENID: wxContext.OPENID,
      ...event.userInfo
    }
    await userCollection.add({
      data: user
    })
  }
  // 返回用戶數據-前端用來緩存
  return {
    ...user
  }
}
6. checkImage 校驗圖片合法性
/**
 * 校驗圖片合法性
 * @param {*} event.fileID 微信雲存儲的圖片ID
 * @return {Number} 0:校驗失敗;1:校驗經過
 */
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
  const contentType = 'image/png'
  const fileID = event.fileID
  try {
    // 根據fileID下載圖片
    const file = await cloud.downloadFile({
      fileID
    })
    const value = file.fileContent
    // 調用 imgSecCheck 藉口,校驗不經過接口會拋錯
    // 必要參數 media { contentType, value }
    const result = await cloud.openapi.security.imgSecCheck({
      media: {
        contentType,
        value
      }
    })
    return 1
  } catch (err) {
    return 0
  }
}

前端開發

此次小程序端的開發

採用的是 滴滴前端團隊出品的 mpx 框架小程序

由於UI比較簡單

這裏就不貼代碼了api

感興趣的歡迎前往 https://github.com/luosijie/m... 瞭解緩存

謝謝閱讀!

相關文章
相關標籤/搜索