共计 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… 理解
谢谢浏览!
正文完