拓冰建站拓冰建站
首页 / 资讯中心 / 正文

微信小程序步数排行榜开发实战:从登录、解密到榜单避坑

简介压缩包内提供了一款基于微信小程序的步数计数与排名应用werun的完整源码适合具备JavaScript基础的开发者作为微信小程序实战入门项目。源码通过调用微信运动开放接口获取用户每日步数并利用数组排序、页面渲染等机制实现数据统计和排行榜展示覆盖了网络请求、数据处理、用户授权、界面更新等关键开发环节。压缩包共21个文件包含9个js逻辑文件、4个wxml页面结构、4个wxss样式、4个json配置及1份md说明整体大小约27KB目录结构简单清晰便于对照学习。项目特别展示了如何组织小程序utils、pages、server等模块以及如何在用户授权后安全解析微信运动数据对理解小程序生命周期和组件化开发也有帮助。目前已有1873人学习对于希望快速上手微信小程序或完善步数类应用功能的人来说是一份轻量但完整的参考实现。 去年我接手了一个很有意思的小项目团队成员想做一个叫 werun 的微信小程序核心功能就两个读取微信运动步数、展示好友之间的步数排名。原本以为是个简单活儿结果从登录态到数据解密从榜单设计到真机调试踩了一圈坑才算真正跑通。这篇文章把整个项目的落地过程拆开讲清楚从思路设计到核心代码再到坑位排查打算做微信步数类小程序或类似社交排行场景的朋友可以直接抄作业。1. 项目整体设计与思路拆解1.1 核心需求解析与数据流设计werun 这个项目的需求听上去很清晰用户打开小程序授权读取微信运动步数然后能在一个排行榜里看到自己和好友的当日步数对比。但真动手做的时候你会发现这里牵扯到三条线微信运动数据、用户身份体系、排行榜展示逻辑。先说数据流。整个链路是这样的微信运动 - wx.getWeRunData() - 服务端解密存储 - 排行榜查询接口 - 前端列表渲染。这里面最容易被忽视的是“微信运动数据经过加密”这件事。wx.getWeRunData 返回的 encryptedData 是一段密文需要配合 session_key 用 AES-128-CBC 算法解密才能拿到真实的步数数组。而这个 session_key 只有在小程序登录wx.login之后由后端调用微信接口换取的 code2Session 才能获得。这就决定了步数功能不是纯前端能搞定的必须配套一个后端服务。另一个核心点是“好友”这个概念的实现。微信没有开放“读取用户好友列表”的接口你能拿到的是用户主动分享小程序后通过分享卡片进入的“同一个小程序使用者”。所以 werun 的排行榜本质上是一个“同小程序用户步数榜”而不是真正的微信好友榜。如果你在小程序里看到类似“好友排名”的文案那是基于“同一小组/同一群/同一批使用该小程序的人”做的圈子排名。这个定位想清楚才能合理设计榜单的数据隔离。1.2 技术选型为什么选原生小程序 自建后端关于技术栈我和团队当时比较过三个方案原生微信小程序、uni-app 跨端框架、Taro 跨端框架。虽然 uniapp 和 Taro 在跨端上有优势——以后还能编译成 H5 或 App但 werun 这个项目有强烈的“微信平台耦合性”加密数据解密、微信运动授权、订阅消息、开放能力分享这些都和微信原生接口强绑定。用跨端框架反而要在各种 API 差异上做兼容得不偿失。所以最终方案是原生小程序 自建 Node.js 后端 MySQL 数据库。选 Node.js 而不是 Java 或 Go原因也很简单小程序后端的核心就三件事——登录换 openid、解密微信运动数据、维护排行榜。Node.js 的生态里解密微信数据的 crypto 模块开箱即用再加上微信官方开源的解密算法示例本身就是 JavaScript 写的直接平移过来省了不少事。数据库用 MySQL 因为数据结构非常固定用户表openid、昵称、头像、步数表openid、日期、步数、好友关系表user_id、friend_id可选。这种结构化数据用关系型数据库最直观完全没必要上 MongoDB 或 Redis。这里有个注意点小程序后端一定要配置 HTTPS 合法域名。开发模式下可以在开发者工具里勾选“不校验合法域名”但上线后所有请求域名必须在小程序管理后台配置为 HTTPS 白名单否则线上请求会被拦截报request:fail url not in domain list。这一步很多人到真机调试阶段才想起来后面排查问题会非常被动。2. 微信步数获取与登录体系完整拆解2.1 登录机制code - openid - session_key 全流程微信登录是这个小程序的第一道关口。用户打开 werun前端先调wx.login()拿到一个临时的 code然后把这个 code 发送到自己的后端后端拿着 code 加上小程序的 appid 和 appsecret 请求微信接口https://api.weixin.qq.com/sns/jscode2session返回的响应里就有 openid用户唯一标识和 session_key用于解密敏感信息的密钥。这里有两个细节特别容易踩坑。第一个是 code 的时效性。code 五分钟内有效且只能用一次用完了就得重新 wx.login()。所以后端拿到 code 后应立即调用微信接口换取信息不要做缓存或延迟处理。第二个是 session_key 的保存策略。session_key 是敏感信息原则上不应该下发到前端因为一旦泄露别人就能解密你的微信运动数据。安全做法是后端拿到 session_key 后用你自己的业务逻辑生成一个自定义 token比如 UUID返回给前端做登录态。后续前端请求步数数据时只带这个 token后端再根据 token 找到对应的 session_key 来解密数据。这样一来session_key 全程不出服务器前端只是存了一个业务 token 而已。2.2 wx.getWeRunData 调用与 encryptedData 解密当用户完成登录并同意授权scope.werun后前端就可以调 wx.getWeRunData 了wx.getWeRunData({ success: (res) { // res.encryptedData: 加密的步数数据 // res.iv: 加密算法的初始向量 wx.request({ url: https://your-server.com/wechat/werun, method: POST, data: { encryptedData: res.encryptedData, iv: res.iv }, header: { Authorization: Bearer token // 自定义登录态 } }); } });后端拿到 encryptedData 和 iv 后用 AES-128-CBC 算法解密密钥是 session_key 的前 16 字节偏移量是 iv 的前 16 字节。解密后的 JSON 格式大致是这样的{ stepInfoList: [ { timestamp: 1722860405, step: 12345 }, { timestamp: 1722861000, step: 12456 } ] }stepInfoList 里存的是当天的分段步数快照不是每个用户一个值而是每几分钟一跳的累计步数。我们要的是“当日最新步数”直接取数组最后一条的 step 即可然后把 openid、日期、步数写入数据库。如果同一天已有数据就做更新不要重复插入。有一点必须提醒用户授权 scope.werun 之后不是每次 getWeRunData 都会弹框。首次访问如果拒绝授权后续再调用会直接走 fail 回调需要引导用户去设置页手动打开“微信运动”权限。具体的做法是调wx.openSetting()跳转到设置页让用户重新开启授权而不是反复弹授权框。这种交互细节处理不好用户会直接卸载小程序。2.3 用户昵称头像授权接口的演进与应对早期版本做用户信息收集很简单调wx.getUserInfo就能一步拿到昵称和头像。但微信后来调整了规则wx.getUserInfo不再弹出授权框而是默认返回匿名数据昵称变成“微信用户”头像变成灰色默认图。再后来微信又推出了“头像昵称填写能力”由开发者自己设计表单让用户填写。当时 werun 的做法是用默认昵称和头像初始化用户不强求用户完善资料。因为排行榜关注的是步数不是头像多好看。如果用户想自定义头像就提供一个“完善资料”入口用 input 让用户输入昵称用 button open-typechooseAvatar 选择头像。现在这么设计既规避了授权接口的坑又保持了用户体验算是踩过坑后的经验总结。3. 排行榜设计与关键实现3.1 排行榜数据模型与查询策略排行榜的核心是“当日步数排名”数据结构再简单不过CREATE TABLE user_step_daily ( id INT PRIMARY KEY AUTO_INCREMENT, openid VARCHAR(64) NOT NULL, step_date DATE NOT NULL, step_count INT DEFAULT 0, update_time DATETIME, UNIQUE KEY uk_openid_date (openid, step_date) );这里在 openid 和 step_date 上建了联合唯一索引目的是保证“一个用户同一天只有一条记录”用INSERT ... ON DUPLICATE KEY UPDATE就能实现步数的更新。查询排行榜时直接按当天步数倒序排SELECT u.nickname, u.avatar_url, s.step_count FROM user_step_daily s JOIN user u ON s.openid u.openid WHERE s.step_date CURDATE() ORDER BY s.step_count DESC, s.update_time ASC LIMIT 100;排序上加了一个update_time ASC的二级排序用来处理“步数相同但先达标的人排在前面”的场景。这个细节是产品同学提的需求意思是同样走了 10000 步你先走到就说明你更“自律”。这个排序逻辑写进去后来大家都觉得合理。3.2 榜单缓存与并发读优化等用户量上来之后每次打开排行榜都实时查 MySQL 也不算最优解。尤其是整点前后大家都喜欢刷新看谁登顶了数据库压力会瞬间飙升。一个低成本方案是在服务端加一层 Redis 缓存key 设计为rank:step:{yyyy-MM-dd}value 直接存 Top 100 的 JSON 数组缓存过期时间 5 分钟。每次排行榜接口先查缓存缓存不存在再查 MySQL 并回填。这样既保证了数据新鲜度5分钟内的排名够用又显著降低数据库负载。也可以更进一步用 Redis 的有序集合Sorted Set来做实时排名。member 是 openidscore 是步数每次用户上报步数时直接ZADD rank:step:20250110 12345 openid123查询排名用ZREVRANGE rank:step:20250110 0 99 WITHSCORES性能比 MySQL 快很多。但要注意Redis 数据要定期清理过期 key否则 365 天下来会攒出大量无用数据。werun 的实际用户量级不大我最后用的是“MySQL 主 Redis 缓存”的组合逻辑简单也够用没必要一开始就上复杂的实时榜单架构。3.3 前端展示的适配点安全区、导航栏高度、自定义 tabbar榜单页面从 UI 上不难但有几个小细节会在真机上翻车。一个就是自定义 tabbar。很多人习惯官方 tabbar 的简单省事但官方自带 tabbar 不支持中间凸起按钮、不支持更复杂的样式定制。werun 的榜单页面想要“首屏展示当前用户排名卡片 下方滚动榜单”这种布局用官方 tabbar 会显得很呆板所以我改成了自定义 tabbar 组件。自定义 tabbar 的坑在 iPhone 底部安全区老型号机型底部没有 Home 指示条新型号有如果 tabbar 高度写死 50px新款机器会显得下沉不足按钮很贴近底部。解决办法是在 tabbar 组件里读取wx.getSystemInfoSync().safeArea或直接用env(safe-area-inset-bottom)CSS 变量给 tabbar 底部加一个动态 padding才能在各机型上显示均匀。顺便说一句自定义 tabbar 必须在小程序后台的 app.json 里对应页面声明tabBar: {}然后每个 tab 页的自定义 tabbar 组件要保持一致的selected状态否则会出现“切换后按钮高亮不对”的诡异 bug。另一个坑是顶部导航栏高度。werun 不需要自定义导航栏但如果做“分享海报页”之类需要全屏沉浸的页面就必须设置navigationStyle: custom这时候顶部导航栏高度在不同机型上不是固定的。推荐用wx.getMenuButtonBoundingClientRect()获取胶囊按钮位置然后结合wx.getSystemInfoSync().statusBarHeight计算自定义导航栏高度这样能适配所有机型。4. 实操过程从零到一跑通核心链路4.1 项目初始化与权限声明新建一个原生微信小程序项目AppID 填你自己的开发工具版本建议保持最新。app.json 里核心配置如下{ pages: [ pages/index/index, pages/rank/rank, pages/profile/profile ], permission: { scope.werun: { desc: 用于读取你的微信运动步数以参与步数排名 } }, requiredPrivateInfos: [], style: v2 }这里permission里的scope.werun并不是“主动申请”用的而是描述“为什么要用这个权限”。真正申请权限是在代码里调wx.authorize({ scope: scope.werun })的时候弹出。如果用户拒绝过后续再调wx.authorize会直接 fail必须通过wx.openSetting跳转设置页重新打开。另外在后台的“开发管理-接口设置”里要确认wx.getWeRunData这个接口的状态是“已启用”。有些新创建的小程序默认部分接口不可用需要自己申请开通这个不留意的话真机测试时调用接口就会发现根本没有权限。4.2 步数上报接口的 Node.js 实现先看核心的解密代码const crypto require(crypto); function decryptWeRunData(sessionKey, encryptedData, iv) { // 密钥和偏移量都是 base64 解码且只取前 16 字节 const sessionKeyBuffer Buffer.from(sessionKey, base64); const ivBuffer Buffer.from(iv, base64); const encryptedDataBuffer Buffer.from(encryptedData, base64); const decipher crypto.createDecipheriv(aes-128-cbc, sessionKeyBuffer, ivBuffer); decipher.setAutoPadding(true); let decoded decipher.update(encryptedDataBuffer, base64, utf8); decoded decipher.final(utf8); return JSON.parse(decoded); }然后后端收到请求后做三件事校验 token 找到用户 - 解密步数数据 - 入库或更新。app.post(/wechat/werun, async (req, res) { const token req.headers.authorization.replace(Bearer , ); const user await getUserByToken(token); if (!user) { return res.status(401).json({ code: 401, message: 登录态失效 }); } const { encryptedData, iv } req.body; try { const stepData decryptWeRunData(user.sessionKey, encryptedData, iv); const stepList stepData.stepInfoList || []; const latest stepList[stepList.length - 1]; const today formatDate(new Date()); await db.query( INSERT INTO user_step_daily (openid, step_date, step_count, update_time) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE step_count VALUES(step_count), update_time NOW(), [user.openid, today, latest.step] ); res.json({ code: 0, data: { step: latest.step } }); } catch (e) { console.error(解密失败, e); res.status(500).json({ code: 500, message: 数据处理失败 }); } });注意一个细节stepInfoList的最后一条不一定就是“当前步数”它可能是几分钟前记录的快照。如果要拿到更准确的实时步数可以在前端隔几秒调一次wx.getWeRunData然后对比数据中最后的 timestamp取最新值。但频繁调用也会触发微信的接口频率限制实际项目里每 5 分钟同步一次就够了如果用户停留在页面上可以加一个下拉刷新的手势来触发手动同步。4.3 排行榜页面与分享卡片实现排行榜页面的实现分成接口层和视图层。接口层前端请求/wechat/rank?date2025-01-10返回当前 Top 100 和当前用户的排名位置。前端拿到数组后用scroll-view渲染列表即可重点在于“当前用户排名标红”和“Top 3 特殊样式”两个视觉效果。分享功能是 werun 拉新用户的关键路径。榜单页右上角胶囊菜单默认自带“转发”但默认分享卡片只能带 title 和 path朋友点开后是冷启动体验一般。如果你想让分享卡片更吸引人可以用wx.updateShareMenu开启withShareTicket: true这样就能够在用户从分享卡片进入后通过wx.getShareInfo拿到群 ID进而做一个“同一群聊的人”的专属榜。这是微信很早开放的能力但很多开发者不知道。当时 werun 做了一大半发现“好友排名”这个概念很难实现后来加了一个小功能把排行榜按“来自同一个分享群的用户”分组在榜上打上一个“同群”标签互动感瞬间强了不少。这个功能的门槛在于用户必须是通过分享卡片进入且在同一群内并且要确保分享时开了withShareTicket。5. 消息推送与自动化更新方案5.1 订阅消息每日步数提醒的实现边界用户打开小程序后如果想做“每日步数未达标提醒”官方现在只能走订阅消息。微信已经下架了长期模板消息只有一次性订阅消息也就是说用户点一次“允许订阅”你只能给他推送一条消息。如果想每天推就得让用户每天来点一次这个体验其实是反人类的。实操中我建议把“步数达标提醒”做成一个内嵌页面用户勾选开启后先调wx.requestSubscribeMessage请求一次订阅拿到一次性订阅资格后在特定时机推送给用户一次“昨日步数回顾”。等用户再次打开小程序时再引导他重新订阅。哪怕不能做到每日自动推送至少能做到“以用户打开频次为节奏”的提醒。这种设计微信生态是支持且合规的切记不要尝试用其他手段伪造长期订阅或绕过限制小程序一旦被封前面所有工作量都白费。5.2 步数数据追平服务端定时拉取的取舍理论上用户的微信运动步数是微信自己维护的werun 只能“被动”等用户打开小程序时上报数据。这就有一个问题如果用户一整天没打开小程序当天的步数就不会进数据库排行榜上就不会出现他。要不要做“服务端定时拉取”很遗憾微信没有开放“服务端通过 openid 直接获取用户步数”的接口wx.getWeRunData必须由用户端发起调用。所以榜单的数据完整度本质上取决于用户打开小程序的频率。这不仅是技术问题也是一个产品问题你想让用户天天来看榜单就得在运营上设计钩子比如“每日步数解锁勋章”“连续打卡 7 天获得徽章”否则榜单数据永远是残缺的用户留存也做不起来。对于“打开过一次但没有当天步数”的情况可以展示“今日未同步快去走两步吧”的占位状态引导用户再次打开更新数据。这个比看到自己在榜上消失要好得多至少给了用户一个回来的理由。6. 常见问题与排查技巧实录6.1 登录态与数据解密类现象可能原因解决办法获取登录后的微信用户失败code 被重复使用或已过期每次登录重新调 wx.login保证 code 是新的decrypt 解密失败报 500session_key 与 encryptedData 不匹配检查是否在 code2Session 后再调 getWeRunData确保 session_key 没有过期真机调试 failed: net::ERR_CONNECTION_RESET开发版未配置合法域名或后端服务未启动在开发者工具中勾选“不校验合法域名”用于本地联调线上必须配置 HTTPS 域名步数上报成功但榜单没更新前端传的日期和服务端时区不一致统一用服务端 UTC 时间换算当天日期或统一用北京时间有个真实案例卡了我一整天本地模拟器一切正常真机测试一调wx.getWeRunData就报解析失败。排查后发现是 iPhone 系统时间和中国标准时间差了几小时导致 timestamp 对应当天日期时数据越界。后来后端统一用Asia/Shanghai时区取日期问题消失。6.2 开发者工具与模拟器类“maximum setlocal recursion level reached” 这个报错我当时在开发者工具里看到都懵了感觉是 Windows 脚本的报错怎么跑到小程序项目里来了。后来查证发现这是某个版本的开发者工具在云开发控制台初始化时偶发的 Node 脚本问题解决办法很简单关闭开发者工具重新打开或把开发者工具升级到最新版本。这个报错跟你的业务代码基本无关不用花太多心思去追。还有一个小程序 id 不生效的问题你用 HBuilderX 改了 project.config.json 里的 appid但运行到微信开发者工具模拟器时显示的还是旧 appid。原因是微信开发者工具的项目缓存没刷新退出工具重新导入项目或手动清除缓存即可。这个在 wu 前端群里出现过很多次不是什么大问题但浪费时间。稳妥的做法是改完 appid 后确认 project.config.json 里appid字段改干净了再重启开发者工具。6.3 发布审核与体验版注意事项上传代码后先要生成体验版二维码扫码测试没问题再提交审核。体验版二维码在微信开发者工具右上角“版本管理”里点击生成不是用代码里的某个接口生成的。正式版本发布后用户搜到的是线上版与体验版数据隔离别搞混了。审核不通过最常见的坑就两个。第一个是隐私政策小程序涉及收集用户的步数数据后台必须配置“用户隐私保护指引”并在小程序内提供可查看的隐私政策。第二个是类目选择步数排行属于“工具-健康管理”类目如果你的小程序还涉及社交互动可能需要额外申请“社交”类目不然审核会被拒。我当时没提前看类目要求被拒了一次才补上审核周期白白多花了两三个工作日。7. 数据权限、隐私合规与一些真实的运营心得微信小程序对用户数据的合规要求越来越严尤其是运动步数这种敏感个人数据。2023 年之后新提交的版本如果在收集用户信息时没有弹窗告知“为什么要获取步数”连开发版都会受限。所以在项目上线前一定要去小程序管理后台的“设置-服务内容声明-用户隐私保护指引”里把“微信运动数据”这一项勾选上并写好用途说明。运营侧我还有一个真实的体会werun 这类轻量工具小程序的次日留存天然不高因为用户“看一眼榜单”就卸载这是数据模型决定的不是你代码写得不好。我做了一件事之后留存明显改善把“每周步数总计排名”加入了排行榜。虽然实现上只是多了一个周榜查询但用户的使用时长变长了因为他会想知道这一周的努力加起来能不能冲到前三。榜单类产品“周期感”很重要日榜是即时反馈周榜是沉淀月榜是仪式感。三者叠加起来一个看似简单的步数排行工具就变得有层次了。从技术架构角度讲werun 这个项目本身不算复杂但它把微信小程序开发中最常见的几个模块全部串了一遍登录、解密、数据库写入、榜单查询、订阅消息、分享、自定义 tabbar、合规配置、上线审核。把这个项目完整做下来小程序基础开发的功力基本就扎实了。如果后面想扩展可以沿着“好友分组排行、运动数据周期报表、团队步数挑战赛”这些方向继续深入底层逻辑都是相通的唯一变的是业务想象力。最后再分享一个真实经验做这种个人工具类的小程序开发时间往往只占 30%剩下 70% 的时间都花在“适配各种机型和版本差异”以及“和微信平台规则斗智斗勇”上。别嫌麻烦这些都是必经之路。保持耐心把每一个报错都录下来你的第二个小程序会比第一个顺利一倍以上。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门