尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序+云开发实战:校园资讯共享平台搭建与部署全记录

微信小程序+云开发实战:校园资讯共享平台搭建与部署全记录 最近帮学校社团做了一个校园资讯共享平台的微信小程序从源码搭建到部署上线前前后后踩了不少坑。这个项目很适合想入门微信小程序开发、或者打算做校内信息聚合类产品的朋友参考。今天这篇博文就从头到尾梳理一遍整体架构怎么拆、核心代码怎么写、部署文档怎么整理以及我在实际操作中遇到的那些文档里不会写的坑。先说清楚这个平台是干什么的。很多校园里的信息分散在年级群、社团群、表白墙、教务处通知栏学生想看个讲座预告、比赛报名、失物招领得翻好几个地方。这个校园资讯共享平台相当于把各类信息统一收口到一个小程序里支持分类浏览、关键词搜索、发布投稿、评论互动管理员后台可以审核置顶。技术上用的是微信小程序原生框架搭配云开发做后端不用自己买服务器、配域名对校内小团队来说性价比非常高。整个项目做下来源码结构和部署文档我一直在同步维护这也是给后面接手的人省事。下面按四个部分详细讲项目设计与选型、核心代码实现、部署流程、以及常见问题排查。1. 项目整体设计与选型思路1.1 核心需求解析动工之前先把需求理清。校园资讯平台的用户分三类普通学生、校内组织/社团的运营人员、平台的超级管理员。普通学生要的是打开小程序就能看到最新资讯能按讲座预告竞赛活动失物招领校园通知分类浏览能搜关键词能看详情并且评论、点赞也可以自己投稿一条资讯。组织/社团账号的诉求是可以发布资讯并附带联系方式和报名链接能够管理自己发过的内容看到浏览量。超管后台要做的审核所有待发布内容置顶重要通知删除违规资讯管理用户和评论。从这些需求出发前端页面最少要有首页信息流、分类页、发布页、个人中心、详情页。后端需要有用户体系、内容管理、评论管理、审核机制。如果全部自己写后端再买服务器成本高、部署麻烦所以我选了微信云开发它自带云数据库、云函数、云存储用户在小程序端可以直接调用免去了后端鉴权、数据库搭建一堆事。1.2 为什么选原生小程序 云开发技术栈上有两个大方向一个是原生小程序配合云开发另一个是uni-app配合自建后端。我们最后选了前者核心原因是团队里没人有后端经验而云开发把用户登录态、数据库操作、文件存储都封装好了。打个比方自建后端相当于自己开饭馆要管采购、切菜、炒菜、洗碗而云开发相当于去档口点菜菜已经洗好切好了你只需要告诉他要哪几样。对于校园项目来说快速上线比技术炫技重要得多。原生小程序的好处是可以直接使用微信官方给的组件和API调试工具也成熟遇到问题在网上搜到的案例基本都是原生写法的学习成本低。uni-app虽然能一套代码多端复用但很多uni-app特有的坑在小程序端会额外暴露出来比如样式兼容、组件属性透传反而不适合第一次做项目的人。云端部分我开了三个核心能力云数据库存资讯、用户、评论云存储存图片云函数做登录、审核、数据聚合等需要权限控制的操作对应项目里的目录分工也清晰miniprogram目录放前端页面cloudfunctions目录放云函数docs目录放部署文档和接口说明。1.3 前端目录结构与状态管理实际搭建的目录结构大致是这样├── cloudfunctions │ ├── login // 登录获取openid │ ├── submitPost // 提交资讯 │ ├── auditPost // 管理员审核 │ ├── getPostDetail // 获取资讯详情 │ └── dashboardCount // 后台统计 ├── miniprogram │ ├── pages │ │ ├── index // 首页信息流 │ │ ├── category // 分类浏览 │ │ ├── publish // 发布投稿 │ │ ├── detail // 资讯详情 │ │ └── mine // 个人中心与我的发布 │ ├── components │ │ ├── post-card // 资讯卡片 │ │ └── empty-view // 空状态占位 │ ├── utils │ │ ├── request.js // 对云函数调用的封装 │ │ ├── auth.js // 登录态处理 │ │ └── format.js // 时间、浏览量格式化 │ └── app.js └── docs ├── 部署文档.md ├── 接口说明.md └── 常见问题.md状态管理这块没有引入第三方库小程序本身有全局app.globalData再加上本地缓存wx.setStorageSync就够用了。登录态、用户基本信息放 globalData浏览历史、草稿箱放 storage这两种配合可以满足绝大多数场景。如果项目继续做大再考虑引入 mobx-miniprogram 之类的方案也不迟。2. 核心功能实现与代码讲解2.1 用户登录与授权流程用户登录是第一步。在校园平台里用户只要知道他是谁、属于哪个学院、是学生还是老师就足够做身份区分了。这里不要求用户填手机号因为微信小程序里的wx.login加上云开发可以很轻松拿到用户的 openid而 openid 在同一个微信小程序下是唯一的。登录云函数我写在cloudfunctions/login/index.js里核心逻辑就十几行const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { OPENID } cloud.getWXContext() const userCollection db.collection(users) let user await userCollection.where({ openid: OPENID }).get() if (user.data.length 0) { // 新用户默认昵称和头像 const initUser { openid: OPENID, nickname: 微信用户, avatar: , role: student, createTime: db.serverDate(), status: active } await userCollection.add({ data: initUser }) return { code: 0, data: { ...initUser, isNew: true } } } return { code: 0, data: { ...user.data[0], isNew: false } } }这里有个容易被忽略的点cloud.getWXContext()不能在小程序端直接调用只能放在云函数里否则拿不到 OPENID。这也是为什么登录逻辑必须走后端云函数而不是在前端直接读数据库。小程序端的utils/auth.js负责把登录流程封装好async function login() { const res await wx.cloud.callFunction({ name: login }) const user res.result.data wx.setStorageSync(userInfo, user) getApp().globalData.userInfo user return user }前端显示登录状态时先用本地缓存顶上去避免每次启动都白屏等待云函数返回等云函数返回后再刷新一遍。这个策略能明显减少用户感知到的加载时间。2.2 资讯列表与分类加载资讯列表是整个平台的数据核心。我把它做成了无限滚动列表下拉刷新 触底加载。数据查询放在云函数里而不是前端直接查数据库原因是前端直接查数据库会暴露集合名称而且无法做复杂的聚合。以首页信息流为例// cloudfunctions/getPostList/index.js const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { page 1, pageSize 10, category all } event const dbCmd db.command const where { status: published } if (category ! all) { where.category category } const countRes await db.collection(posts).where(where).count() const listRes await db.collection(posts) .where(where) .orderBy(createTime, desc) .skip((page - 1) * pageSize) .limit(pageSize) .get() return { code: 0, data: { list: listRes.data, total: countRes.total, hasMore: page * pageSize countRes.total } } }页面端对应的加载逻辑async function loadPosts() { const page this.data.page const res await wx.cloud.callFunction({ name: getPostList, data: { page, pageSize: 10, category: this.data.currentCategory } }) const { list, hasMore } res.result.data this.setData({ posts: page 1 ? list : this.data.posts.concat(list), hasMore, loading: false }) }关键细节在于page的控制下拉刷新时重置为 1触底时加 1同时用hasMore判断是否继续请求。如果不做这个判断用户快速滚动时会发大量多余请求白白消耗云函数调用次数。小程序onReachBottom的触底距离默认是 50px如果首页的资讯卡片比较矮建议在app.json里配置onReachBottomDistance: 100否则用户还没滑到底就加载了体验很差。2.3 发布投稿与图片上传发布页面的表单字段包括标题、分类、正文、封面图、联系方式、是否允许评论。这里要提一个坑button组件里如果嵌了图片选择器默认的button样式和小程序自带wx.chooseMedia的交互会冲突。我采用的方案是把图片选择器做成独立的view点击时调用wx.chooseMedia和发布按钮完全分离。图片上传的代码可以复用async function uploadImage(filePath) { const ext filePath.match(/\.(\w)$/)[1] const cloudPath posts/${Date.now()}-${Math.floor(Math.random() * 1000)}.${ext} const uploadRes await wx.cloud.uploadFile({ cloudPath, filePath }) return uploadRes.fileID }云存储路径我特意加了时间戳加随机数避免同名文件覆盖。上传完拿到 fileID 后不能直接把这个字段存进数据库就算完事因为如果用户在中途取消发布这张图片会变成孤儿文件长期占用存储空间。我的处理是用户点击发布时先上传图片拿到 fileID如果发布失败或取消再调用wx.cloud.deleteFile清理掉。后端云函数里也可以做定时清理但项目初期前端处理好就足够了。提交资讯时对敏感词做一个基础过滤用正则和关键词列表先挡一层管理员审核时再人工判断。这里说下我的实现思路const sensitiveWords [代写, 办证, 赌博] // 示例关键词可按学校情况扩充 function checkSensitive(text) { for (let word of sensitiveWords) { if (text.includes(word)) return word } return null }如果命中敏感词直接提示发布失败并指出问题词用户修改后再次提交。这样虽然防不住所有情况但能挡住大部分垃圾信息。2.4 评论互动与通知评论模块的核心是关联查询。每条评论除了存postId和内容还要冗余存一份评论者的昵称和头像。这么做的好处是列表加载时不需要再去查用户表性能好很多。坏处是如果用户改了昵称旧评论里的昵称不会变。对于校园资讯平台来说这个取舍完全能接受——评论展示的是当时的身份而且用户改昵称频率并不高。评论云函数的伪代码exports.main async (event, context) { const { postId, content } event const { OPENID } cloud.getWXContext() const user await getUserByOpenId(OPENID) const comment { postId, content, nickname: user.nickname, avatar: user.avatar || , createTime: db.serverDate(), status: visible } await db.collection(comments).add({ data: comment }) // 让资讯作者收到通知 await db.collection(notifications).add({ data: { userId: postAuthorId, type: comment, postId, content: ${user.nickname} 评论了你的资讯, createTime: db.serverDate(), read: false } }) }这属于最基本的通知实现没有用消息推送而是用站内信。用户打开小程序时拉取未读通知这个项目里够用了。如果想做到实时推送就得接微信订阅消息但订阅消息有模板限制而且用户授权一次只能发一条不适合做即时聊天式提醒反而是站内信更靠谱。小程序端详情页的点赞功能我采用的是一次性点赞没有做成取消点赞。因为校园资讯大多数是通知性质点赞更多代表看过/支持一下不需要反复横跳。简化逻辑后数据库里只存一个likeCount字段点过之后前端置灰后端用likedBy数组保存用户 openid 去重。3. 部署全流程记录3.1 部署前的环境准备部署文档是整个项目源码之外最重要的文件。我见过太多源码拿到手却跑不起来的案例问题不出在代码而在于环境不一致。微信小程序开发工具版本、Node.js 版本、云开发环境 ID这些都要在文档开头写清楚。我整理的部署环境清单是这样的项目要求说明微信开发者工具稳定版 1.06 以上推荐使用最新稳定版Node.js16.x 或 18.x云函数本地调试时需要微信小程序账号已注册且完成认证个人主体部分功能受限建议用学校主体云开发环境按量付费或基础套餐校园项目用基础套餐即可这里有个容易被忽略的细节云开发的环境 ID 会在很多配置里用到建议一律用cloud.DYNAMIC_CURRENT_ENV这种方式而不是在代码里写死。写死的问题在于如果你在开发环境调试完切换到生产环境所有代码都要改一遍还容易漏。3.2 小程序端部署步骤小程序端的部署分三步第一步在微信开发者工具里导入项目根目录然后确认project.config.json里的miniprogramRoot和cloudfunctionRoot字段分别指向miniprogram/和cloudfunctions/。project.config.json关键配置{ miniprogramRoot: miniprogram/, cloudfunctionRoot: cloudfunctions/, setting: { urlCheck: false, es6: true, enhance: true, postcss: true, minified: true }, compileType: miniprogram }urlCheck要设成 false否则本地调试时域名校验会拦截 request 请求。但要记住这只是本地调试用上线前云开发环境本身不需要配置业务域名。第二步开通云开发环境。在开发者工具点击云开发按钮创建环境。环境名称建议用campus-info-prod这种语义化名称不要用一串随机字符。第三步创建数据库集合并设置权限。按项目代码至少要建四个集合users、posts、comments、notifications。权限设置很关键users仅管理端可读写云函数可读写posts所有用户可读仅管理端可写用户投稿走云函数comments所有用户可读仅管理端可写评论走云函数notifications仅创建者可读我见过有同学把集合权限设成所有人可读写结果用户通过控制台直接把 post 集合里的数据改了后患无穷。哪怕是小程序内部也一定要求所有写操作通过云函数完成数据库权限尽量收紧。3.3 云函数部署与云端配置云函数不是把文件夹拖上去就能跑。每个云函数是一个独立的环境需要单独右键上传并部署。推荐的方式是右键云函数目录选择上传并部署云端安装依赖。如果用本地安装依赖再上传容易出现 Node 模块版本不匹配的问题。云端安装依赖会在云函数容器里自动执行npm install省心很多。云函数login不需要额外依赖getPostList等函数用到wx-server-sdk这个依赖在package.json里有声明云端会自动装好。有继承关系的项目建议在cloudfunctions根目录放一个package.json统一指定wx-server-sdk版本免得每个函数各装各的版本漂移。云函数部署完成后还要在云开发控制台设置里配置环境变量。我用了两个环境变量ADMIN_OPENID超级管理员的 openid用于审核权限判断DEFAULT_AVATAR默认头像的云存储 fileID代码里通过process.env.ADMIN_OPENID读取这样管理员身份判断不需要硬编码在源码里换人接手时在控制台改配置就行。3.4 上线审核与发布注意事项小程序审核是部署的最后一道关卡也是最容易出问题的环节。校园资讯平台常见审核被拒的原因有两个一个是没有《互联网信息服务资质》另一个是内容类目选择不对。针对第一个如果学校主体本身有相关资质直接用学校主体注册小程序就能覆盖。如果是个人开发、打算作为毕设或社团作品审核时可以不用以商业平台名义提审把类目选成教育-教育信息服务或者工具-信息查询类目要和实际功能匹配否则会被驳回。提审前要在app.json里把permission字段写全尤其是使用位置信息、相册等接口时要声明目的{ permission: { scope.writePhotosAlbum: { desc: 用于保存资讯图片到相册 } } }另外发布页的用户协议和隐私政策也要在审核前放到小程序里否则审核员会以未提供用户隐私保护指引为由驳回。可以在个人中心做一个关于平台页面把协议文本挂上去并在首次启动弹窗时引导用户阅读。4. 常见问题排查与优化记录4.1 登录态失效导致云函数调用报错一种常见的现象是用户长时间没有打开小程序再打开时页面数据加载不出来云函数返回用户信息不存在或登录失败。原因是云开发会在小程序端维护一个登录态有效期过期后wx.cloud的调用会失败。解决办法是做一个全局拦截// app.js onLaunch() { this.refreshLogin() }, async refreshLogin() { const { result } await wx.cloud.callFunction({ name: login }).catch(() ({ result: { code: -1 } })) if (result.code -1) { // 重新执行 wx.login 并再次调用 login 云函数 await new Promise((resolve) wx.login({ success: resolve })) await wx.cloud.callFunction({ name: login }) } }另外可以在utils/request.js里对所有云函数调用做统一拦截一旦返回特定错误码就自动刷新登录态后重试一次。实测这个方案在弱网和长时间挂后台的场景下特别管用。4.2 列表渲染卡顿与数据量大问题资讯列表页如果一次性加载太多数据小程序首屏渲染会很慢。我踩过最明显的坑是在onLoad里直接请求 50 条数据并全部渲染低端安卓机上滑动明显掉帧。优化手段有三层第一层分页加载每页 10 条这前面已经讲到了。第二层封面图懒加载。小程序image组件的lazy-load属性只对 page 滚动生效对 scroll-view 不生效。所以列表页我直接用 page 滚动不用 scroll-view让lazy-load正常工作。第三层避免在列表项里绑定复杂计算。资讯卡片里我用了独立的post-card组件组件的properties直接传对象。这里需要注意不要在observer里做深拷贝或正则匹配等耗时操作否则每条数据都会触发一次列表一长就卡。数据格式化尽量在setData之前完成。资讯的正文如果很长详情页不要一次性把全部内容塞进data再渲染。我做法是详情页只存正文的富文本内容和一个渲染完成标志图片部分用rich-text组件的默认懒加载配合image的lazy-load处理。4.3 图片上传失败和文件清理用户反馈图片一直转圈最后发布失败多数情况是网络问题但也有一个是云存储权限配置不对。云存储权限如果设置成仅创建者可读写用户上传的文件只有他自己的 openid 能访问别人看资讯时封面图会裂。正确权限应该设置成所有用户可读仅创建者可写并且上传时的cloudPath要保证唯一。还有一种情况发布成功后云函数里db.collection(posts).add()因为字段类型不一致失败。比如category字段前端传的是字符串但数据库集合里之前手动插过一条数字类型的记录这会导致查询时匹配异常。解决方案是在云函数入口做一次入参校验const { category } event if (typeof category ! string || ![lecture, competition, notice, lost, other].includes(category)) { return { code: 1, msg: 非法分类 } }4.4 审核被拒与类目调整印象最深的一次提审被拒是因为小程序的发布资讯功能被认为属于社交-社区类需要额外的《互联网信息服务许可》。当时我有点懵因为学校内部平台并没有商业性质。后来我把发布功能改成了用户投稿的表述并在提审页面把功能截图重点标注成校园助手工具类目选工具-信息查询。首页和发布页的 UI 也做了对应调整弱化社区感——比如去掉热门话题、关注流这种强社交元素突出分类查询、通知公告。审核被拒并不丢人关键是你得能看懂拒绝原因然后针对性地调整。官方反馈里如果写了请修改类目或删除相关功能那就照着做不要和审核机制较劲。5. 后续可以扩展的方向这个平台做完第一期后如果还想继续迭代我个人认为最有价值的方向是订阅消息通知。比如用户订阅了讲座预告分类讲座发布后通过微信订阅消息推给他会比让用户自己打开小程序再看要高效得多。订阅消息要在用户主动点击订阅后才能发送所以需要设计一个引导订阅的时机比如在详情页底部放一个订阅此类通知按钮。另一个方向是数据统计。云开发自带日志分析但默认提供的是请求量、错误量的粗粒度数据业务层面的统计比如哪个分类浏览量最高哪个时段发布最多需要自己埋点。可以在详情页访问时更新views字段每天定时触发一个云函数做聚合把结果存到analytics集合里后面做数据大屏或者周报都方便。还有一点如果以后用户量上来建议把云函数里的数据库查询加上缓存比如热门资讯列表用云开发自带的内存缓存cloud.getTempFileURL不行那是文件用的可以用云函数公共变量存 map或者接一层 redis 但成本高。小项目其实不必提前优化等用户量真到了几千再动手也不迟。最后说说文档维护这件事。我在docs目录下放了三个文件部署文档、接口说明、常见问题。每次修改代码我都会顺手更新接口说明避免文档和代码脱节。部署文档里还放了环境变量对照表、云函数部署顺序、数据库索引设置这些容易被跳过但很重要的信息。这样后面不管是社团学弟接手还是老师要验收都能按文档一步步把项目跑起来不用再来问我这里报错怎么办。做这个项目最大的感受是一个看似简单的校园资讯平台真正要跑顺需要把细节抠到很细才行。微信小程序 云开发这套组合特别适合校园团队因为前端、后端、运维可以集中到两三个人手里代码结构清晰部署成本也低。如果你正准备做类似的校园项目大胆去试吧比想象中能学到的东西多得多。
返回列表