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

代码仓库 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... 理解

谢谢浏览!