简介这是一份面向微信小程序开发者与云开发初学者的完整答题类项目实战资源适用于在线考试、知识竞赛、培训测评等轻量级互动场景。资源基于微信原生小程序框架与腾讯云开发TCB构建实现前后端分离的免服务器部署方案显著降低运维门槛与开发复杂度。压缩包共75个文件含10个JS逻辑文件处理答题流程与云函数调用、10个WXSS样式文件含colorui等UI组件、6个WXML页面结构、35个PNG/JPG图片资源含界面截图、后台管理图、二维码及水印素材以及JSON配置、MD说明文档等整体大小为6.06MB目录结构清晰pages下分设index、details、rank、test等核心功能模块。已有222人学习下载读者可直接获取可运行的小程序源码、完整的云数据库设计思路、云函数业务逻辑如身份校验、答案判分、成绩统计、配套界面截图与README使用指南快速掌握微信小程序云开发的全栈实践路径。1. 微信答题小程序一个不用搭后端、不买服务器、30分钟就能上线的云开发实战项目你有没有遇到过这种场景导师临时布置一个课堂互动小工具要求明天上课前能扫码答题或者社区活动需要快速收集居民知识掌握情况但没时间走采购流程申请服务器、配环境、写接口这个基于云开发的微信答题小程序就是为这类“今天提需求、明天要上线”的真实场景而生的。它完全绕过传统 Web 开发中 Nginx Node.js/Java MySQL 的部署链路所有数据存储、函数执行、文件上传都由微信云开发原生承载——数据库用云数据库JSON 文档型逻辑用云函数Node.js 运行时静态资源走云存储。新手照着文档跑通首测只需 30 分钟熟手拿来改题库结构、加排行榜、接企业微信通知一天内就能交付定制版。它不是教学 Demo而是某高校教务组在期中教学检查中实际部署、支撑 2000 学生并发作答的真实轻量级系统底座。2. 架构与技术栈为什么选云开发不是“省事”而是“少踩坑”微信原生小程序 云开发的组合并非为了标新立异而是针对答题类应用高频出现的几个“血泪痛点”做了精准规避题库频繁变更→ 云数据库支持 JSON 灵活字段增删一道多选题无需改表结构答题结果需实时统计→ 云函数可直接调用聚合管道aggregate算出各题正确率比前端拉全量再计算快 5 倍以上学生交卷后要立刻看到分数→ 云函数触发器监听answerRecords集合新增自动更新用户总分并推送模板消息全程毫秒级响应运维零成本→ 不用管服务器崩没崩、MySQL 连接池满没满、SSL 证书过期没过期。整个架构只有三层前端层微信小程序原生 WXML/WXSS/JS通过wx.cloud.database()和wx.cloud.callFunction()直连云资源云服务层微信官方提供的三件套——云数据库带权限控制的 JSON 文档库、云函数Node.js 16.x 运行时冷启动约 300ms、云存储支持 CDN 加速的文件托管管理侧全部通过微信开发者工具内置的「云开发控制台」完成无命令行、无 SSH、无 Docker。提示这不是“阉割版后端”而是微信把 BaaSBackend as a Service做进了开发工具链。你写的云函数和本地 Node.js 项目一样能require第三方包如lodash、dayjs也能用async/await写异步逻辑唯一区别是process.env里多了WX_CONTEXT含 openid、unionid 等用户上下文。2.1 目录结构5 个核心文件撑起整个项目项目根目录极简没有node_modules、没有package.json云函数依赖在控制台单独安装只有开发者真正要维护的代码├── app.js # 小程序全局逻辑登录态管理、云初始化 ├── app.json # 页面路由、窗口样式等基础配置 ├── project.config.json # 微信开发者工具工程配置含云开发环境 ID ├── cloudfunctions/ # 云函数目录每个子目录即一个独立函数 │ ├── createQuiz/ # 创建试卷函数管理员调用 │ │ ├── index.js # 主逻辑 │ │ └── config.json # 函数配置内存 256MB、超时 5s │ ├── submitAnswer/ # 提交答案函数学生调用 │ │ └── index.js │ └── getRanking/ # 获取排行榜函数带聚合统计 │ └── index.js ├── miniprogram/ # 小程序前端源码 │ ├── pages/ │ │ ├── index/ # 首页试卷列表 │ │ ├── quiz/ # 答题页倒计时、单题渲染 │ │ └── result/ # 结果页得分、错题解析 │ └── utils/ │ └── db.js # 封装云数据库操作统一处理权限错误关键点在于云函数必须放在cloudfunctions/目录下且每个函数子目录名即为函数名。微信开发者工具会自动识别并上传——你改了submitAnswer/index.js右键该目录选择「上传并部署」线上函数就实时更新了不用手动npm install或docker build。2.2 核心模块拆解一个答题请求从进入到返回的完整链路以学生点击「提交」按钮为例看数据如何穿越前后端步骤 1前端封装提交动作miniprogram/pages/quiz/quiz.js// 提交答案前先校验必填项 const answers this.data.answers; if (!answers.length) { wx.showToast({ title: 请至少答一题, icon: none }); return; } // 调用云函数传入试卷ID、用户openid、答案数组 wx.cloud.callFunction({ name: submitAnswer, data: { quizId: this.data.quizId, answers: answers.map((a, i) ({ questionIndex: i, selectedOptions: a.selected || [] })) }, success: res { // res.result 包含 { score: 85, correctCount: 17, wrongList: [2,5] } wx.navigateTo({ url: /pages/result/result?score${res.result.score}quizId${this.data.quizId} }); }, fail: err { console.error(提交失败, err); wx.showToast({ title: 网络异常请重试, icon: none }); } });说明wx.cloud.callFunction是云开发最常用 API它自动携带当前用户的openid无需前端传后端云函数可通过event.userInfo.openId拿到。data字段会序列化为 JSON 传给云函数大小限制 1MB足够应付百题试卷。步骤 2云函数处理逻辑cloudfunctions/submitAnswer/index.jsconst cloud require(wx-server-sdk); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前环境 const db cloud.database(); const _ db.command; exports.main async (event, context) { const { userInfo, OPENID } event; // 自动注入用户身份 const { quizId, answers } event.data; try { // 1. 查询试卷详情含标准答案 const quizRes await db.collection(quizzes).doc(quizId).get(); if (!quizRes.data) throw new Error(试卷不存在); const quiz quizRes.data; const correctAnswers quiz.questions.map(q q.correctOption); // 2. 计算得分逐题比对 let score 0; const wrongList []; answers.forEach((ans, idx) { const isCorrect _.isEqual(ans.selectedOptions.sort(), correctAnswers[idx].sort()); if (isCorrect) score quiz.questionScore || 1; else wrongList.push(idx 1); // 错题序号从1开始 }); // 3. 写入答题记录带索引加速查询 await db.collection(answerRecords).add({ data: { quizId, openId: OPENID, answers, score, wrongList, createTime: db.serverDate(), nickname: userInfo.nickName || 匿名用户 } }); // 4. 更新用户总分原子操作避免并发覆盖 await db.collection(users).doc(OPENID).set({ data: { totalScore: _.inc(score), lastAnswerTime: db.serverDate() }, merge: true }); return { score, correctCount: answers.length - wrongList.length, wrongList }; } catch (err) { console.error(submitAnswer error:, err); throw err; } };参数说明cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })让函数自动匹配当前小程序绑定的云环境避免硬编码环境 IDdb.serverDate()用服务端时间戳防止用户手机时间被篡改_.inc(score)原子自增操作解决多人同时答题导致总分计算错误的问题merge: true只更新指定字段不覆盖用户其他信息如头像、地区。这个函数看似简单但已覆盖答题场景 90% 的核心诉求防作弊服务端校验、防并发原子操作、防时间篡改服务端时间、可追溯完整记录存档。3. 本地运行与真机调试三步跑通拒绝“本地能跑、线上报错”云开发最大的认知偏差是以为“本地开发工具里能跑线上没问题”。实际上云函数在本地模拟器里运行的是 Node.js 本地环境而线上是微信隔离的沙箱环境——权限、网络、时区、甚至Date.now()行为都有差异。以下三步是某开发者在部署后第 2 小时紧急回滚时总结出的黄金流程3.1 第一步用微信开发者工具「云开发」标签页初始化环境打开微信开发者工具 → 顶部菜单「工具」→ 「云开发」点击「开通云开发」→ 选择「按量计费」免费额度够学生项目用一年在弹出的「环境配置」中记下生成的环境 ID形如exam-abc123并复制到项目根目录project.config.json的cloudfunctionRoot同级字段{ description: 项目配置文件, setting: { urlCheck: true, es6: true, enhance: true, postcss: true, preloadBackgroundData: false, minified: true, newFeature: true, coverView: true, nodeModules: true, autoAudits: false }, cloud: true, cloudfunctionRoot: ./cloudfunctions/, cloudfunctionTemplateRoot: ./cloudfunctionTemplate/, cloudBase: { envId: exam-abc123, // ← 必须填这里 region: ap-guangzhou } }注意envId必须和你在「云开发控制台」看到的完全一致大小写敏感。填错会导致所有wx.cloud.*调用返回Error: env not found。3.2 第二步云函数本地调试必须用「云函数本地调试」模式不能直接node index.js微信开发者工具提供了专用调试入口在cloudfunctions/submitAnswer/目录上右键 → 「云函数本地调试」工具会自动启动本地 Node.js 服务并在控制台输出Listening on http://localhost:8080此时前端调用wx.cloud.callFunction({ name: submitAnswer })会自动转发到本地服务而非线上环境你可以在index.js里打console.log断点调试甚至require(debug)查看 MongoDB 查询语句。提示本地调试时event.userInfo是模拟数据默认nickName: 微信用户若需测试真实昵称可在调试面板手动修改eventJSON。3.3 第三步真机预览前务必检查「云开发权限」开关很多新手卡在「真机扫码白屏」原因 90% 是这个在微信开发者工具右上角「详情」→ 「本地设置」→ 勾选「启用云开发」更关键的是在「项目设置」→ 「云开发」→ 确保「云开发环境」已选择你开通的环境 ID不是「未选择」或「默认环境」最后在真机微信中打开「发现」→ 「小程序」→ 搜索「微信开发者工具」→ 进入后点击「预览」扫码即可。如果仍报错Error: permission denied大概率是云数据库集合权限没开——进「云开发控制台」→ 「数据库」→ 点击answerRecords集合 → 「权限设置」→ 将「创建」和「查找」设为「所有用户可访问」答题类应用合理无需登录鉴权。4. 常见问题排查那些让你凌晨两点还在翻日志的玄学错误云开发的报错信息往往很“温柔”比如Error: system error但背后可能是具体到某个字符的致命问题。以下是某开发者在 3 个项目中踩出的 5 条高频坑按「现象 → 原因 → 解决」整理亲测有效4.1 现象云函数上传后调用返回Error: function not found原因函数目录名和wx.cloud.callFunction({ name: xxx })中的name不一致或目录名含大写字母/中文/空格云函数名只支持小写字母、数字、下划线解决检查cloudfunctions/下目录名是否全小写且符合正则^[a-z][a-z0-9_]{0,29}$例如submitAnswer应改为submit_answer。4.2 现象db.collection(quizzes).where(...).get()返回空数组但控制台能看到数据原因云数据库集合设置了「仅创建者可读」权限而查询时未传openId或用户未登录解决进「云开发控制台」→ 「数据库」→ 点击集合 → 「权限设置」→ 将「查找」权限改为「所有用户可访问」或前端调用前确保wx.login()成功获取openId。4.3 现象云函数里console.log(hello)在本地调试有输出但线上日志里看不到原因线上日志默认只保留最近 3 天且console.log输出超过 1MB 会被截断更常见的是函数执行超时默认 5s日志根本来不及刷出解决在云函数开头加console.time(total)结尾加console.timeEnd(total)查看耗时若超时检查是否有同步阻塞操作如fs.readFileSync或未加await的 Promise。4.4 现象提交答案后answerRecords集合里openId字段为空原因前端调用wx.cloud.callFunction时用户尚未完成wx.login()导致event.userInfo.openId为undefined解决在app.js的onLaunch中强制静默登录wx.login({ success: res { if (res.code) { // 后续可传 code 到云函数做 session_key 解密 console.log(login success:, res.code); } } });4.5 现象排行榜页面getRanking函数返回Error: aggregate not supported原因聚合查询aggregate仅在云数据库「高级版」环境支持而免费版默认是「基础版」解决进「云开发控制台」→ 「数据库」→ 右上角「升级」→ 选择「高级版」学生认证用户可免费升升级后需重新部署云函数因运行时版本变化。5. 二次开发实战从“能用”到“好用”的 3 个改造技巧当你跑通基础答题流程后下一步往往是“老板说要加个功能”。这里不讲虚的直接给三个已在多个真实项目中落地的改造方案附可抄代码、可验证步骤。5.1 改造一为每道题增加“解析”字段点击错题显示详解原始题库结构quizzes集合只存question、options、correctOption学生交卷后只能看到对错无法理解为什么选 C 不对。加解析字段需两步Step 1扩展题库 JSON 结构在云开发控制台 → 「数据库」→quizzes集合 → 新建一条文档手动添加explanation字段{ question: HTTP 状态码 404 表示, options: [服务器内部错误, 请求超时, 未找到资源, 禁止访问], correctOption: [2], explanation: 404 是客户端错误状态码表示服务器无法根据请求的 URL 找到对应资源常见于网页链接失效。 }Step 2前端结果页动态渲染解析在miniprogram/pages/result/result.js的onLoad中补充查询解析逻辑// 根据 quizId 拉取整套题库含解析 const quizRes await db.collection(quizzes).doc(this.data.quizId).get(); const quiz quizRes.data; const questions quiz.questions || []; // 渲染时对每道错题显示解析 this.setData({ questions: questions.map((q, i) ({ ...q, isWrong: this.data.wrongList.includes(i 1), explanation: q.explanation || })) });然后在 WXML 中加条件渲染view wx:for{{questions}} wx:keyindex text{{index 1}}. {{item.question}}/text view wx:if{{item.isWrong item.explanation}} text classexplanation【解析】{{item.explanation}}/text /view /view效果学生点开结果页错题下方自动展开灰色小字解析无需额外跳转。5.2 改造二用云函数定时任务生成周度答题报告免人工学校教务组每周一要汇总各班级答题数据原始做法是导出 Excel 手动算。用云开发的「定时触发器」可全自动Step 1创建定时云函数weeklyReport在cloudfunctions/下新建目录weeklyReportindex.js内容const cloud require(wx-server-sdk); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); const db cloud.database(); const _ db.command; exports.main async (event, context) { const now new Date(); const weekAgo new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // 统计过去7天各试卷的参与人数、平均分 const report await db.collection(answerRecords) .aggregate() .match({ createTime: _.gte(weekAgo) }) .group({ _id: $quizId, count: _.sum(1), avgScore: _.avg($score) }) .end(); // 写入 reportLogs 集合归档 await db.collection(reportLogs).add({ data: { period: ${formatDate(weekAgo)} 至 ${formatDate(now)}, reports: report.list, createTime: db.serverDate() } }); return { status: success, count: report.list.length }; }; function formatDate(d) { return ${d.getFullYear()}-${(d.getMonth() 1).toString().padStart(2, 0)}-${d.getDate().toString().padStart(2, 0)}; }Step 2在控制台设置定时触发进「云开发控制台」→ 「云函数」→ 找到weeklyReport→ 「触发器」→ 「添加触发器」→ 类型选「定时触发器」→ Cron 表达式填0 0 9 * * 1每周一上午 9 点执行。验证方法在控制台手动「立即执行」一次然后查reportLogs集合是否新增文档。后续教务老师只需每周一上午进控制台看最新报告。5.3 改造三接入微信模板消息交卷后自动推送成绩学生交卷后除了页面跳转还能微信收到一条服务通知“您的《网络安全基础》测试得分为 92 分排名第 3”。实现只需两步Step 1在小程序管理后台配置模板消息登录 微信公众平台 → 「功能」→ 「订阅消息」→ 「添加模板」→ 搜索关键词“考试”或“成绩”选用系统模板如AT0002获取模板 ID。Step 2在submitAnswer函数末尾追加推送逻辑// 提交成功后立即推送模板消息 await cloud.openapi.subscribeMessage.send({ touser: OPENID, templateId: your_template_id_here, // 替换为真实模板 ID data: { thing1: { value: 《${quiz.title}》测试 }, number2: { value: res.result.score.toString() }, phrase3: { value: 已提交 }, time4: { value: new Date().toLocaleString(zh-CN) } } });注意cloud.openapi.subscribeMessage.send需要wx-server-sdk版本 ≥ 2.10.0若报错send is not a function请在云函数目录执行npm install --save wx-server-sdklatest并重新上传。从那以后我每次接到“加个通知”需求都强制走一遍「模板消息配置 → 云函数调用 → 真机扫码测试」闭环哪怕只是发个“提交成功”也确保用户真的收到了。因为对终端用户来说页面跳转是“我以为好了”而微信弹窗才是“我知道好了”。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?