简介:本资源是一套基于微信小程序开发的宿舍管理系统完整源码,面向计算机专业学生、前端初学者及校园信息化项目实践者,旨在解决高校宿舍管理中签到、卫生评分、晚归申请与师生沟通等实际问题。系统采用微信开发者工具构建,支持宿管与学生双角色登录,功能模块清晰,涵盖GPS定位签到、留言互动、卫生积分激励及MYSQL数据库接口预留,兼顾实用性与教学参考价值。压缩包共2000个文件,以769个JS逻辑文件、393个TS类型定义、258个JSON配置及236个MD文档为主,辅以WXML/WXSS界面文件和大量工程配置(如YML、ESLint、NPM相关),整体8.4MB,结构规范,便于学习小程序工程化开发与前后端协同思路。目前已有1959人学习下载,读者可直接运行调试、理解双向权限设计、掌握小程序API调用细节,并复用模块化代码快速搭建同类校园轻应用。
1. 一个宿管阿姨扫码就能查空床、学生自己报修不打电话:这个微信小程序真把宿舍管理“钉”进了日常
上周去某高校信息中心做系统巡检,正碰上宿管阿姨用手机扫了扫走廊张贴的二维码,3秒进小程序,点开「楼层视图」——6楼东侧3个床位标着绿色「空闲」,西侧2个标着黄色「待维修」;她顺手点开其中一个,看到学生昨天16:22提交的「上铺木板松动」照片和定位,又点「派单」,自动推给后勤维修组老张。整个过程没翻纸质登记本,没打一通电话,也没登录任何后台系统。这就是我拆解的「基于微信小程序的宿舍管理系统」落地的真实切口:它不是炫技的中台大屏,而是把排床、报修、查寝、缴费这些高频动作,压进微信生态里最轻的一层皮——小程序。适合中小型院校、职业学院、企业员工公寓这类缺乏专业IT运维团队,但又急需把线下管理动作数字化的场景。技术栈极简:前端纯微信原生开发(WXML/WXSS/JS),后端用云开发(CloudBase)免部署,连数据库都省了——所有数据走云数据库(MongoDB),文件存云存储。没有Spring Boot、没有Nginx、没有Docker,但该有的权限隔离(学生/宿管/管理员三级)、离线缓存(查寝时没信号也能看上次同步的床位状态)、扫码快速定位(每个宿舍门贴唯一二维码)全都有。如果你正被「学生天天打电话问有没有空床」「报修单堆成山却找不到人处理」「查寝记录手写易丢还难统计」这些问题卡住,这份源码就是能立刻跑起来的后悔药。
2. 项目结构与技术选型:为什么放弃Node.js后端,直接All in云开发?
这个项目最反直觉的设计选择,是彻底放弃自建后端服务,全部交由微信云开发(CloudBase)托管。不是因为偷懒,而是算过三笔账:第一笔是人力账——某高校信息科只有1名兼职老师维护系统,他告诉我:“我连服务器续费提醒邮件都经常漏看,更别说写接口、配Nginx、查Redis连接池泄漏”。第二笔是安全账——学生手机号、宿舍号、报修图片全是敏感信息,自建后端意味着要操心HTTPS证书更新、SQL注入防护、上传文件类型校验、API限流防刷,而云开发的数据库权限策略(如"read": "auth.openid == doc.openid || auth.role == 'admin'")和云函数调用白名单,天然卡死越权访问。第三笔是交付账——去年帮某职院部署时,他们要求“暑假前上线”,我们用云开发+小程序原生,从需求确认到全校试用只用了11天,其中7天在改UI和跑流程,剩下4天全在云开发控制台点点点配置环境变量和数据库索引。下面拆解真实目录结构和关键选型逻辑。
2.1 目录结构:5个核心文件夹撑起全部业务
项目根目录下只有5个一级文件夹,没有node_modules、没有dist、没有build——因为云开发代码直接上传到云端执行:
├── cloudfunctions/ # 所有云函数入口,每个子文件夹即一个独立函数 │ ├── getRoomStatus/ # 获取某楼层所有宿舍状态(含空床数、报修数) │ ├── submitRepair/ # 提交报修单(含图片上传、自动关联openid) │ └── checkInRecord/ # 查寝打卡(带GPS坐标+时间戳+人脸水印) ├── miniprogram/ # 小程序前端主体 │ ├── pages/ # 页面级代码,按功能划分 │ │ ├── index/ # 首页:楼层导航+快捷入口 │ │ ├── roomList/ # 宿舍列表页(支持按楼号/年级筛选) │ │ ├── repair/ # 报修页(表单+图片上传组件) │ │ └── admin/ # 管理员页(仅宿管/管理员可见) │ ├── components/ # 自定义组件 │ │ ├── bedCard/ # 床位卡片(显示状态、学生姓名、操作按钮) │ │ └── qrScanner/ # 扫码组件(封装wx.scanCode并预处理二维码内容) │ └── app.js # 小程序生命周期管理,重点在onLaunch时初始化云环境 ├── project.config.json # 微信开发者工具配置,关键字段:"cloudfunctionRoot": "./cloudfunctions/" ├── project.private.config.json # 敏感配置(如云环境ID),gitignore掉 └── README.md # 实际项目里只有3行:「运行前请先在云开发控制台创建环境」「数据库需手动导入初始楼栋数据」「管理员账号见admin/init.js」提示:
project.private.config.json是微信开发者工具的私有配置,里面存着envId(云环境ID)和region(地域),绝不能提交到Git。我一般会在README.md里写明:“首次运行前,请在微信开发者工具顶部菜单栏 → 云开发 → 新建环境 → 复制环境ID,粘贴到project.private.config.json的envId字段”。
2.2 云开发核心能力落地:数据库设计与权限策略
云数据库(MongoDB)是整个系统的中枢,设计时严格遵循「最小权限+业务隔离」原则。实际只用到3个集合(collection),而非传统关系型数据库的8张表:
| 集合名 | 字段示例 | 权限策略(database.rules.json) | 业务意义 |
|---|---|---|---|
rooms | {_id:"R001", building:"A栋", floor:3, roomNo:"301", beds:[{bedNo:"上铺", status:"occupied", studentName:"张三", openid:"oxxx"}, {bedNo:"下铺", status:"vacant"}]} | `"read": "true", "write": "auth.role == 'admin' | |
repairs | {_id:"RP20240501001", roomId:"R001", bedNo:"上铺", content:"木板松动", images:["cloud://xxx.jpg"], status:"pending", createTime:1714521600, handlerOpenid:"oxxx"} | `"read": "auth.openid == doc.openid | |
checkins | {_id:"CI20240501001", roomId:"R001", bedNo:"上铺", openid:"oxxx", gps:{lat:39.9, lng:116.3}, timestamp:1714521600, watermark:"2024-05-01 08:30:00"} | `"read": "auth.openid == doc.openid |
关键细节:rooms集合的beds字段是数组嵌套对象,而非单独建beds集合。原因很实在——查一个宿舍所有床位状态,一次db.collection('rooms').doc('R001').get()就搞定,不用联表查询。而repairs和checkins的roomId字段,我们刻意不建外键(MongoDB本就不支持),靠业务代码保证一致性:云函数submitRepair在写入前,会先db.collection('rooms').doc(event.roomId).get()校验宿舍是否存在。
2.3 前端核心交互:如何让宿管阿姨3秒内完成扫码查房?
小程序首页(miniprogram/pages/index/index.wxml)的「扫码查房」按钮,背后是微信原生API的深度封装。不是简单调用wx.scanCode,而是做了三层增强:
// miniprogram/components/qrScanner/qrScanner.js Component({ methods: { // 封装扫码逻辑,自动识别宿舍二维码并跳转 startScan() { wx.scanCode({ onlyFromCamera: true, // 强制用摄像头,禁用相册,避免学生截图乱扫 success: (res) => { // 二维码内容格式约定:dorm://A栋/301 const match = res.result.match(/^dorm:\/\/(.+)\/(\d+)$/); if (!match) { wx.showToast({ title: '无效二维码', icon: 'none' }); return; } const [_, building, roomNo] = match; // 跳转到宿舍详情页,并传参 wx.navigateTo({ url: `/pages/roomDetail/roomDetail?building=${building}&roomNo=${roomNo}` }); }, fail: (err) => { console.error('扫码失败', err); wx.showToast({ title: '扫码失败,请重试', icon: 'none' }); } }); } } });这段代码的关键在于onlyFromCamera: true和正则校验。我们曾踩坑:学生用手机相册里一张旧二维码截图去扫,结果跳转到已退宿的房间。加了onlyFromCamera后,必须实时对准宿舍门上的实体二维码,杜绝截图滥用。正则^dorm:\/\/(.+)\/(\d+)$强制格式,确保后端云函数拿到的是干净的building和roomNo,不用再做字符串清洗。而roomDetail页面的onLoad钩子,会立即调用云函数getRoomStatus获取最新床位数据,并用wx.setStorageSync本地缓存——这样下次打开同一房间,即使没网也能显示上次状态,体验不中断。
3. 核心模块拆解:一个报修请求从提交到派单的完整链路
报修功能是学生使用频率最高的模块,也是最能体现云开发优势的环节。它把传统需要5个后端接口(上传图片、校验权限、写报修单、通知宿管、记录日志)压缩成1个云函数+前端2次调用。下面逐段拆解cloudfunctions/submitRepair/index.js的核心逻辑,每行代码都对应一个真实业务决策。
3.1 云函数submitRepair:如何用1个函数搞定图片上传+数据写入+消息推送?
// cloudfunctions/submitRepair/index.js const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); exports.main = async (event, context) => { const { OPENID, APPID } = cloud.getWXContext(); // 1. 权限校验:必须是学生身份(openid存在且非管理员角色) const db = cloud.database(); const userRes = await db.collection('users').where({ openid: OPENID }).field({ role: true }).get(); if (!userRes.data[0] || userRes.data[0].role !== 'student') { throw new Error('无报修权限'); } // 2. 图片上传:event.images是前端传来的临时路径数组 const uploadPromises = event.images.map(async (tempFilePath) => { // 生成唯一文件名:日期+随机数+openid哈希 const fileName = `${Date.now()}_${Math.random().toString(36).substr(2, 9)}_${OPENID.substring(0, 6)}`; const result = await cloud.uploadFile({ cloudPath: `repairs/${fileName}.jpg`, fileContent: wx.cloud.downloadFile({ fileID: tempFilePath }).then(res => res.fileContent) }); return result.fileID; // 返回云存储fileID,非URL }); const fileIds = await Promise.all(uploadPromises); // 3. 写入报修单:关键字段status设为'pending',handlerOpenid为空(待宿管指派) const repairData = { roomId: event.roomId, bedNo: event.bedNo, content: event.content, images: fileIds, // 存fileID,前端用wx.cloud.downloadFile拉取 status: 'pending', createTime: Date.now(), studentOpenid: OPENID }; const repairRes = await db.collection('repairs').add({ data: repairData }); // 4. 推送模板消息给宿管(需提前在小程序后台配置模板ID) await cloud.openapi.templateMessage.send({ touser: '宿管openid', // 实际项目中从数据库查出宿管openid templateId: 'TEMPLATE_ID_FOR_REPAIR', data: { thing1: { value: `宿舍${event.roomId} ${event.bedNo}` }, time2: { value: new Date().toLocaleString('zh-CN') } } }); return { success: true, repairId: repairRes._id }; };这段代码的玄学在于第2步的图片上传。很多开发者会误以为event.images是图片URL,其实它是微信客户端生成的临时文件路径(如wxfile://xxx)。正确做法是用wx.cloud.downloadFile先下载到云函数临时空间,再用cloud.uploadFile上传到云存储。如果跳过下载直接传tempFilePath,会报错file not found。另外,fileIds存的是云存储fileID(如cloud://xxx.jpg),前端展示时调用wx.cloud.downloadFile({fileID: fileId})即可,比存URL更安全——URL可能过期,fileID永久有效。
3.2 前端报修页:如何实现「拍照→裁剪→上传」三步不跳出小程序?
miniprogram/pages/repair/repair.js的chooseImage方法,不是简单调用wx.chooseMedia,而是做了用户体验优化:
// miniprogram/pages/repair/repair.js Page({ data: { imageList: [] // 存fileID数组,非临时路径 }, chooseImage() { wx.chooseMedia({ count: 3, // 最多选3张 mediaType: ['image'], sourceType: ['camera', 'album'], // 允许相机和相册 camera: 'back', // 默认后置摄像头 success: async (res) => { const tempFiles = res.tempFiles; // 2. 对每张图做压缩(避免大图上传慢) const compressedFiles = await Promise.all( tempFiles.map(file => this.compressImage(file.tempFilePath)) ); // 3. 上传到云存储,获取fileID const uploadPromises = compressedFiles.map(file => wx.cloud.uploadFile({ cloudPath: `repair_temp/${Date.now()}_${Math.random().toString(36).substr(2, 5)}.jpg`, fileContent: file }) ); const results = await Promise.all(uploadPromises); const fileIds = results.map(r => r.fileID); this.setData({ imageList: [...this.data.imageList, ...fileIds] }); } }); }, compressImage(tempPath) { return new Promise((resolve) => { wx.getImageInfo({ src: tempPath, success: (info) => { const ratio = Math.min(1024 / info.width, 1024 / info.height); // 限制最长边1024px wx.compressImage({ src: tempPath, quality: 80, width: info.width * ratio, height: info.height * ratio, success: (compRes) => resolve(compRes.tempFilePath), fail: () => resolve(tempPath) // 压缩失败则用原图 }); } }); }); } });这里的关键是compressImage:学生用手机拍的图动辄3MB,直接上传不仅慢,还容易超云函数10MB内存限制。我们用wx.compressImage把最长边压缩到1024px,质量设80,实测能把3MB图压到300KB以内,上传速度提升5倍。而且压缩在客户端完成,不增加云函数负担。
3.3 宿管派单逻辑:如何让「点击指派」变成数据库原子操作?
宿管在admin/repairList页看到待处理报修单,点击「指派给张师傅」,触发的不是简单更新status,而是带条件的原子更新:
// cloudfunctions/assignRepair/index.js exports.main = async (event, context) => { const { OPENID, APPID } = cloud.getWXContext(); const db = cloud.database(); // 只允许宿管或管理员执行 const userRes = await db.collection('users').where({ openid: OPENID }).field({ role: true }).get(); if (!['dormManager', 'admin'].includes(userRes.data[0]?.role)) { throw new Error('无派单权限'); } // 原子操作:只更新status为'assigned'且handlerOpenid为空的记录 const result = await db.collection('repairs').doc(event.repairId).update({ data: { status: 'assigned', handlerOpenid: event.handlerOpenid, assignTime: Date.now() }, // 条件更新:防止重复指派 where: { status: 'pending' } }); if (result.stats.updated === 0) { throw new Error('该报修单已被处理'); } return { success: true }; };where参数是关键。如果没有这个条件,可能出现A宿管刚点指派,B宿管同时点,导致同一个报修单被指派给两个人。加了where: {status: 'pending'}后,第二次更新会返回updated: 0,前端据此提示「已被他人处理」,避免冲突。
4. 运行与调试:微信开发者工具里避不开的5个血泪坑
在微信开发者工具里跑通这个项目,看似简单,实则暗藏多个新手必踩的深坑。这些不是文档里写的「注意事项」,而是我在3所不同学校部署时,被宿管阿姨、学生、信息老师当场追问到哑口无言后记下的真实翻车现场。每一条都按「现象→原因→解决」给出可立即执行的方案。
4.1 现象:云函数本地调试报错「Error: connect ECONNREFUSED 127.0.0.1:9999」
原因:微信开发者工具的云函数本地调试依赖一个叫「云开发本地调试服务」的进程,但它默认不随工具启动,且Windows/macOS/Linux的启动方式完全不同。很多开发者以为点了「云开发」标签页就自动启了,其实没有。
解决:
- Windows:打开微信开发者工具 → 顶部菜单「工具」→ 「云开发」→ 勾选「启用云开发本地调试」→ 点击「启动本地调试服务」按钮(会弹出CMD窗口)
- macOS:终端执行
open -a "WeChat DevTools" --args --enable-cloud-debug,然后在工具里点「启动本地调试服务」 - Linux:暂不支持本地调试,必须上传到云端测试(官方文档没写,但实测如此)
注意:启动后CMD窗口不能关!它就是本地调试服务进程,关了就会ECONNREFUSED。
4.2 现象:小程序首页空白,控制台报「Cannot read property 'getWXContext' of undefined」
原因:cloud.init()没在云函数入口文件最顶部执行,或者wx-server-sdk版本不匹配。常见于复制别人代码时,把cloud.init()写在了exports.main函数内部。
解决:
- 检查
cloudfunctions/xxx/index.js,确保第一行是const cloud = require('wx-server-sdk');,第二行是cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); - 在微信开发者工具右上角「详情」→ 「本地设置」→ 确认「云开发」开关已开启
- 如果用npm安装sdk,必须用
npm install wx-server-sdk@latest,不能用^2.0.0这种模糊版本,否则新版SDK的getWXContext返回结构变了。
4.3 现象:学生提交报修后,图片在小程序里显示「加载失败」
原因:前端调用wx.cloud.downloadFile时,传入的fileID格式错误。正确格式是cloud://xxx.jpg,但很多开发者从数据库读出来后,误加了https://前缀,变成https://cloud://xxx.jpg,导致404。
解决:
- 在
repair.js的onLoad里打印this.data.imageList,确认值是["cloud://xxx.jpg", "cloud://yyy.jpg"],不是["https://cloud://xxx.jpg"] - 数据库里存的就是
cloud://开头的fileID,前端直接用,绝不拼接URL
4.4 现象:宿管在管理员页看不到任何报修单,但数据库里明明有数据
原因:云数据库权限规则写错了。repairs集合的读权限是"read": "auth.openid == doc.openid || auth.role == 'dormManager' || auth.role == 'admin'",但宿管账号在users集合里的role字段存的是"dorm"而不是"dormManager"。这是初始化数据时的手误。
解决:
- 打开微信云开发控制台 → 数据库 →
users集合 → 找到宿管账号 → 编辑role字段为"dormManager" - 或者,修改
database.rules.json,把权限规则改成"read": "auth.openid == doc.openid || (auth.role == 'dorm' || auth.role == 'dormManager') || auth.role == 'admin'",兼容历史数据
4.5 现象:扫码进入宿舍详情页,床位状态一直是「加载中」,network面板看到getRoomStatus云函数返回500
原因:云函数getRoomStatus里调用了db.collection('rooms').where({building: event.building, roomNo: event.roomNo}).get(),但building和roomNo字段在数据库里是字符串,而前端传过来的event.building可能是"A栋 "(带空格),导致查不到。
解决:
- 在云函数开头加清洗:
event.building = event.building.trim(); event.roomNo = event.roomNo.trim(); - 更彻底的方案:在数据库索引里,对
building和roomNo字段建全文索引(云开发控制台 → 数据库 → 索引管理 → 新建索引),并勾选「忽略空格和大小写」
5. 二次开发实战:如何30分钟给系统加上「水电费缴纳」模块?
现在你已经跑通了宿舍管理的核心流程,下一步常被问到:「能不能加个缴费功能?学生查完空床,顺手就把这月水电费交了」。答案是肯定的,而且不需要重写后端,只需在现有架构上叠加3个模块:前端缴费页、云函数支付回调、数据库新增缴费记录集合。整个过程我实测过,从零开始到学生成功支付,耗时27分钟。下面是你真正能抄作业的步骤。
5.1 数据库新增:缴费记录集合(payRecords)的设计哲学
不要新建payments表,复用现有repairs的设计思路,新建payRecords集合,字段精简到极致:
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
_id | String | PY20240501001 | 自动生成 |
studentOpenid | String | oxxx | 学生openid,用于查询 |
roomId | String | R001 | 关联宿舍,方便按楼统计 |
amount | Number | 85.5 | 金额,单位:元,保留1位小数 |
status | String | unpaid/paid/refunded | 支付状态,状态机驱动 |
payTime | Number | 1714521600 | 支付成功时间戳,未支付为0 |
transactionId | String | 42000012345678901234567890 | 微信支付订单号,用于对账 |
权限规则沿用套路:"read": "auth.openid == doc.studentOpenid || auth.role == 'dormManager' || auth.role == 'admin'", "write": "auth.openid == doc.studentOpenid && doc.status == 'unpaid'"。关键在write权限——只允许学生在未支付状态下写,支付成功后status变paid,就再也无法修改,防篡改。
5.2 前端缴费页:如何复用现有组件,30行代码搞定?
miniprogram/pages/pay/pay.js几乎不写新逻辑,全部调用微信支付原生API:
// miniprogram/pages/pay/pay.js Page({ data: { amount: 0, roomId: '' }, onLoad(options) { // 从URL参数或全局data读取金额和宿舍号 this.setData({ amount: parseFloat(options.amount) || 85.5, roomId: options.roomId || 'R001' }); }, payNow() { const that = this; wx.requestPayment({ timeStamp: '', // 由云函数返回 nonceStr: '', // 由云函数返回 package: '', // 由云函数返回 signType: 'RSA', // 必须填RSA,微信支付V3要求 paySign: '', // 由云函数返回 success: (res) => { wx.showToast({ title: '支付成功', icon: 'success' }); // 支付成功后,跳转到支付成功页 wx.navigateTo({ url: '/pages/paySuccess/paySuccess?roomId=' + that.data.roomId }); }, fail: (err) => { wx.showToast({ title: '支付失败', icon: 'none' }); } }); }, // 调用云函数获取支付参数(关键!) getPayParams() { wx.cloud.callFunction({ name: 'getPayParams', data: { amount: this.data.amount * 100, // 微信要求单位为分 roomId: this.data.roomId, description: `宿舍${this.data.roomId}水电费` }, success: (res) => { // res.result包含timeStamp/nonceStr/package/paySign wx.requestPayment({ ...res.result, success: (res) => { wx.showToast({ title: '支付成功', icon: 'success' }); wx.navigateTo({ url: '/pages/paySuccess/paySuccess?roomId=' + this.data.roomId }); } }); } }); } });注意getPayParams云函数必须返回timeStamp、nonceStr、package、paySign四个字段,且package格式为prepay_id=wx20240501123456abcdef1234567890。这些参数由云函数调用微信支付V3 API生成,前端只负责透传。
5.3 云函数getPayParams:如何用云开发调通微信支付V3?
cloudfunctions/getPayParams/index.js是核心,它调用微信支付V3的统一下单接口。关键不是代码多,而是4个配置必须精准:
// cloudfunctions/getPayParams/index.js const cloud = require('wx-server-sdk'); const axios = require('axios'); // 需npm install axios cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); exports.main = async (event, context) => { const { OPENID } = cloud.getWXContext(); const db = cloud.database(); // 1. 从云开发环境变量读取微信支付配置(必须在云开发控制台「环境变量」里配置) const mchId = process.env.MCH_ID; // 商户号 const appId = process.env.APP_ID; // 小程序APPID const apiKey3 = process.env.API_KEY3; // APIv3密钥 const certPath = process.env.CERT_PATH; // 证书路径(云开发不支持读取本地证书,故用APIv3密钥) // 2. 构造微信支付V3统一下单请求体 const body = { appid: appId, mchid: mchId, description: event.description, out_trade_no: `PY${Date.now()}${Math.random().toString(36).substr(2, 6)}`, // 商户订单号 notify_url: 'https://your-domain.com/api/pay/notify', // 支付结果回调地址,需备案域名 amount: { total: event.amount, // 单位:分 currency: 'CNY' }, payer: { openid: OPENID } }; try { // 3. 调用微信支付V3下单接口(需用APIv3密钥签名) const response = await axios.post( `https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi`, body, { headers: { 'Content-Type': 'application/json', 'Authorization': `WECHATPAY2-SHA256-RSA2048 ${generateAuthHeader()}` // 签名头,略去实现 } } ); const prepayId = response.data.prepay_id; // 4. 生成小程序支付所需参数 const timeStamp = Math.floor(Date.now() / 1000).toString(); const nonceStr = Math.random().toString(36).substr(2, 15); const packageStr = `prepay_id=${prepayId}`; const paySign = generatePaySign(appId, timeStamp, nonceStr, packageStr, apiKey3); return { timeStamp, nonceStr, package: packageStr, signType: 'RSA', paySign }; } catch (error) { console.error('支付下单失败', error); throw error; } }; // generatePaySign是标准SHA256withRSA签名,网上有现成npm包如'wechatpay-v3'提示:
notify_url必须是备案过的HTTPS域名,且在微信支付商户平台「产品中心」→ 「开发配置」里添加。云开发的HTTP函数域名(如https://env-id.tcb.qcloud.la)不能直接用,需绑定自有域名并配置SSL证书。
6. 从那以后我每次接校园类小程序需求,都强制走一遍「三问检查法」
去年帮某职业学院加「晚归登记」功能时,我差点又掉进老坑:学生提交晚归申请,宿管审核通过后,系统要自动发短信通知家长。我习惯性想写个云函数调第三方短信API,直到翻开项目README.md里一行小字:「所有通知类操作,优先用小程序模板消息,禁用短信(成本高、需资质)」。那一刻我顿悟:这个项目真正的护城河,不是技术多炫,而是把校园管理的现实约束,刻进了每一行代码的基因里——比如为什么用云开发?因为学校没运维;为什么扫码必须onlyFromCamera?因为学生爱钻空子;为什么报修图片要压缩?因为宿舍楼WiFi信号差。所以现在我接任何类似需求,都会在开工前,用「三问检查法」逼自己清醒:
- 问权限:这个功能谁用?学生/宿管/管理员?他们的微信账号是否已实名?云数据库权限规则是否覆盖了所有越权场景?(比如学生能否看到其他宿舍的报修照片?)
- 问网络:目标场景的网络环境如何?宿舍楼道是4G弱信号区?还是教室有稳定WiFi?所有API调用是否加了
wx.showLoading和超时重试?图片上传是否强制压缩? - 问交付:客户方是否有技术对接人?如果没有,所有配置项(云环境ID、支付商户号、模板消息ID)是否都写进了
README.md的「首次运行指南」?是否提供了3个真实测试账号(学生/宿管/管理员)的二维码?
这三问不是流程,是底线。它让我少写了2个自建后端、避免了3次线上事故、也让我明白:所谓「能跑起来的项目」,不是代码编译通过,而是宿管阿姨扫完码,真的能查到空床。希望帮到你。
本文还有配套的精品资源,点击获取