关于前端:小程序云开发实现一个投票应用

7次阅读

共计 6568 个字符,预计需要花费 17 分钟才能阅读完成。

因为最近想实际一下小程序的云开发能力,于是设计开发了一个简略的投票利用,欢送感兴趣的一起学习交换。

代码仓库 https://github.com/luosijie/m…

因为小程序【集体开发者】不凋谢【投票】类目,所以就不能在线预览了,我放几张利用的截图

数据库设计

总共用到了 3 个汇合

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 个云函数

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 比较简单

这里就不贴代码了

感兴趣的欢送返回 https://github.com/luosijie/m… 理解

谢谢浏览!

正文完
 0