news 2026/9/14 5:54:01

微信步数小程序源码解析:wx.getWeRunData授权与云函数解密实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信步数小程序源码解析:wx.getWeRunData授权与云函数解密实战

简介:微信步数主题的微信小程序页面源码,适用于入门小程序开发的初学者,或需要快速搭建运动健康类页面的开发者,能够直接参考其页面划分、数据绑定与基础交互实现。压缩包共32个文件,类型涵盖json配置、js逻辑、wxml页面结构、wxss样式、png和jpg图片素材以及md说明文档,整体仅445KB,体量小巧且目录层次清楚。目前已有81人学习下载。源码包含pages下多个功能页面、工具函数与全局配置,并配套sitemap及项目配置文件,可帮助读者理解小程序页面注册、公共样式引用、图片资源调用等环节;同时通过日志、个人中心等模块,能够掌握常用组件、事件绑定和页面间跳转的组织方式。这份源码还可作为二次开发或课程设计的基础模板,适合边读边改,快速积累小程序前端实战经验。

1. 微信步数小程序页面源码的资源拆解

拿到微信步数的微信小程序页面源码.zip,别急着点开微信开发者工具导入,先把它当成一个页面级工程来拆。它解决的不是“怎么注册小程序”,而是给你一套已经排好页面、tabBar、图片资源和工具函数的前端骨架,适合做微信小程序项目实例、课程设计或毕业设计底子。

这里有个反直觉结论:微信步数不是页面里自己累计算出来的。微信小程序中拿到步数只有一条常规路径——调用wx.getWeRunData,而且它返回的是加密数据,不能在前端直接解出步数。后面所有代码和报错都围绕这句话展开。

解压后能看到四页结构:pages/index是步数主页,pages/orgpkpages/myorg是业务扩展页,pages/logs是日志页。这个包没有附带后端解密服务,就算wx.getWeRunData调到成功,页面也拿不到明文步数,这是整套源码里最需要先认清的边界。

2. app.json 页面注册与底盘配置:先让小程序骨架跑起来

2.1 从文件清单反推页面模块

解压后的目录结构大致是这样:

├── app.js ├── app.json ├── app.wxss ├── project.config.json ├── sitemap.json ├── pages/ │ ├── index/ │ ├── orgpk/ │ ├── logs/ │ └── myorg/ ├── src/ │ ├── avatar_1.png │ ├── liked.png │ ├── cover_pic.jpg │ └── ... └── utils/ └── util.js

看目录就能猜出分工:pages是四个页面模块,src里的图片全部本地化,utils放纯函数,app.wxss是全局样式。这个结构和常见的 uniapp微信小程序工程不一样,uniapp 生成的小程序包会有pages.json和底层运行时文件,而这个包里只有原生小程序配置,说明它是纯原生写法,别用 hbuilderx 那套构建逻辑去套。

2.2 页面注册顺序决定启动页和 tabBar

app.json是原生小程序的全局配置,步数主页能跑起来,先看这段配置:

{ "pages": [ "pages/index/index", "pages/orgpk/orgpk", "pages/logs/logs", "pages/myorg/myorg" ], "window": { "navigationBarTitleText": "微信步数", "navigationBarBackgroundColor": "#2e5e4e", "navigationBarTextStyle": "white", "backgroundColor": "#f6f6f6" }, "tabBar": { "color": "#7f8487", "selectedColor": "#2e5e4e", "list": [ { "pagePath": "pages/index/index", "text": "步数" }, { "pagePath": "pages/myorg/myorg", "text": "我的" } ] }, "sitemapLocation": "sitemap.json" }

这段配置里有两个容易忽略的点。第一,pages数组的第一个元素决定启动页,这里把pages/index/index放在最前面,进入小程序第一眼看到的就是步数主页;orgpklogs即使页面文件存在,只要不出现在pages数组里,就不会作为独立页面编译。第二,tabBar.list里的pagePath必须同时存在于pages数组,否则编译直接报错,并且tabBar只能配置 2 到 5 个页面。这个包的orgpk没有进 tabBar,说明它只能通过wx.navigateTo跳转进入。

下面这张表是调试时最常用的几项:

配置项示例值作用
pages["pages/index/index"]声明页面路径,第一项为启动页
window.navigationBarTitleText微信步数顶部导航栏标题
tabBar.selectedColor#2e5e4e底部导航选中态颜色
sitemapLocationsitemap.json指定站点地图文件位置

2.3 sitemap.json 与 project.config.json 决定编译行为

sitemap.jsonrules规则控制页面是否被微信搜一搜建立索引。开发期保持放行即可:

{ "desc": "步数页面索引规则", "rules": [ { "action": "allow", "page": "*" } ] }

project.config.json则是微信开发者工具读的工程配置,核心是 AppID 和编译选项:

{ "compileType": "miniprogram", "appid": "touristappid", "projectname": "wechat-steps", "setting": { "es6": true, "postcss": true, "minified": true, "urlCheck": false } }

urlCheck设为 false 只能让开发者工具跳过 request 合法域名校验,真机预览时仍然会校验,后面走云函数就不需要担心这个。touristappid是游客模式,不能调用wx.getWeRunData,测试步数功能前必须换成真实 AppID,这是最多人卡住的第一步。

3. 微信步数核心链路:wx.getWeRunData 授权、加密数据与云函数解密

3.1 调用前先查 scope.werun 授权状态

微信小程序的步数接口不是随便调的,它属于隐私接口,绑定scope.werun授权。用户拒绝一次之后,开发者不能再次主动触发授权弹窗,只能引导用户去设置页手动打开。这个行为和scope.userInfo类似,所以第一步应该先查授权状态,而不是直接写一个wx.getWeRunData完事。

查授权状态的函数我一般写成 Promise 形式,后面配合async/await调用,避免回调嵌套:

function checkWeRunAuth() { return new Promise((resolve) => { wx.getSetting({ success(res) { const auth = res.authSetting['scope.werun']; if (auth === true) { resolve('authorized'); } else if (auth === false) { resolve('denied'); } else { resolve('notDetermined'); } }, fail() { resolve('unknown'); } }); }); }

这段代码的关键点是authSetting对象里scope.werun的三种取值:true表示已授权,false表示用户明确拒绝过,undefined表示还没决定。很多新手把undefined也当成拒绝,结果用户第一次还被弹了两次授权框。fail回调也不能简单忽略,基础库版本过低时getSetting也可能失败,返回unknown状态之后走默认调用链路会更稳妥。

3.2 三种授权状态下的调用分支

拿到状态后,按authorizednotDetermineddenied三个分支处理:

async function loadSteps() { const status = await checkWeRunAuth(); if (status === 'authorized' || status === 'notDetermined') { wx.getWeRunData({ success: (res) => { handleEncryptedData(res); }, fail: (err) => { console.error('getWeRunData fail', err); } }); } else { wx.showModal({ title: '需要微信运动权限', content: '请在设置中打开微信运动授权', confirmText: '去设置', success: (r) => { if (r.confirm) { wx.openSetting(); } } }); } }

这里最容易踩的坑是denied后直接再调一次wx.getWeRunData,实际上微信不会再弹授权框,而是直接走fail回调,写好的fail处理就会反复执行。所以denied分支要用wx.showModal解释原因,再通过wx.openSetting让用户手动打开。wx.openSetting必须在用户点击回调里调用,不能在onLoad生命周期直接调,否则开发者工具会提示openSetting:fail can only be invoked by user TAP gesturenotDetermined状态则什么都不用做,直接调接口就会触发授权弹窗。

3.3 加密数据怎么变成步数列表

授权通过后,wx.getWeRunData的 success 回调拿到的不是 JSON 步数列表,而是这样两段字符串:

{ "errMsg": "getWeRunData:ok", "encryptedData": "base64字符串", "iv": "base64字符串" }

encryptedData是 AES-128-CBC 加密后的用户信息和步数列表,iv是初始向量。真正解密的session_key只有后端从wx.login的 code 换出来,所以页面源码里看不到解密逻辑是正常的。之前有人在前端引入 crypto-js 去解,解出来也是乱码,因为拿不到正确的session_key。常见做法是把这个解密放到小程序云函数里,前端只把encryptedDataiv传过去。

下面是一个最常用的云函数解密模板,Node.js 环境自带 crypto,不需要额外装包:

// 云函数 getWeRunData const crypto = require('crypto'); exports.main = async (event) => { const { encryptedData, iv, sessionKey } = event; const decipher = crypto.createDecipheriv( 'aes-128-cbc', Buffer.from(sessionKey, 'base64'), Buffer.from(iv, 'base64') ); let decoded = decipher.update(encryptedData, 'base64', 'utf8'); decoded += decipher.final('utf8'); return JSON.parse(decoded); };

这段代码里aes-128-cbc对应微信官方加密算法,sessionKey理论上不应由前端传,生产环境应由云函数通过用户身份去换取或直接存到数据库;上面这样写在本地开发联调时比较方便,部署前要收紧。解密结果是一个 JSON 字符串,JSON.parse后就能拿到stepInfoList

3.4 数据为空和跨天问题的处理

解密后的stepInfoList是数组,每一项里的timestamp是当天零点的时间戳,单位是秒;step是该天总步数。注意step为 0 是合法值,不能写成if (!step)来过滤。日期换算时给timestamp乘 1000 再传给Date,否则会出现 1970。接口一般返回最近 30 天数据,但开发者工具里经常是模拟数据,真机上如果用户没开微信运动,返回的数组可能只有一条空数据。

提示:不要尝试在前端引入 crypto-js 硬解,既拿不到session_key,还会把小程序包体积撑大;解密这一步放在云函数或自有后端而不是页面源码里。

4. 步数页面的数据渲染:index 卡片、资源路径与 util.js 封装

4.1 本地图片资源为什么不走网络

src目录里放着 avatar_1.png、avatar_2.png、liked.png、unlike.png、cover_pic.jpg、hr.jpg 这些资源。页面引用本地图片的好处是列表渲染时不依赖网络,也不受 downloadFile 合法域名限制;如果包是从线上摘出来的,还要先确认这些图片的版权和大小。这个包已经把所有图片放在本地,写cover_pic.jpg这种封面图时直接填/src/cover_pic.jpg即可,不需要再去找微信小程序图片提取工具从 wxpkg 缓存里翻。本地图片路径一旦写错,控制台会直接报fail,所以路径里的/src/前缀不要省略。

4.2 用 wx:for 渲染步数卡片

步数主页的卡片列表一般就是wx:for渲染,核心模板如下:

<view class="step-card" wx:for="{{stepList}}" wx:key="date"> <view class="step-date">{{item.date}}</view> <view class="step-number">{{item.step}}</view> <image class="step-like" src="{{item.liked ? '/src/liked.png' : '/src/unlike.png'}}" /> </view>

这段模板里wx:for默认把当前项叫作itemwx:key="date"告诉框架用date字段做列表唯一标识;这里不能写wx:key="item.date",直接写字段名。item.liked是布尔值,如果后端返回的是 0/1,模板里true条件会不成立,所以最好在数据层把 0/1 转成布尔。imagesrc/src/liked.png这种绝对路径最稳,相对路径在分包场景容易算错,尤其是orgpk这类子页面引用根目录资源时。

4.3 util.js 里的日期格式化与排序

util.js里最常用的是把秒级时间戳变成日期字符串,以及给步数排序:

function formatDate(timestamp) { const d = new Date(timestamp * 1000); const month = d.getMonth() + 1; const day = d.getDate(); return ( d.getFullYear() + '-' + (month < 10 ? '0' + month : month) + '-' + (day < 10 ? '0' + day : day) ); } function compareStep(a, b) { return b.step - a.step; } module.exports = { formatDate, compareStep };

timestamp * 1000是为了把秒转成毫秒,getMonth()从 0 开始所以要加 1。compareStepb.step - a.step返回降序,如果orgpk页面要显示团队排行前三名,直接对这个排序结果取slice(0, 3)。排序会修改原数组,调用前先const sorted = list.slice().sort(compareStep),否则页面后面想按时间顺序展示时,原数组已经被改掉了。

4.4 orgpk 和 myorg 页面在工程里的定位

orgpk从命名看更像组织 PK 或团队排行,myorg是个人组织信息页,这类页面如果要显示当前用户头像,需要自己接wx.getUserProfile或云开发用户表,源码里没有现成接口时不要硬编码一个固定头像。logs页面放调试日志,上线前最好把wx.navigateTo的入口隐藏。如果myorg要自定义顶部导航,微信小程序顶部导航栏高度不能写死 64px,要用wx.getMenuButtonBoundingClientRect拿到胶囊按钮位置,再计算状态栏高度,否则不同机型的返回按钮会错位。

5. 真机预览前检查清单与步数异常排查技巧

5.1 让代码在开发者工具里先跑起来

第一步不写新业务代码,先跑通编译。把project.config.json里的appid换成自己的 AppID,使用测试号也能编译,但wx.getWeRunData在测试号上经常拿不到真实数据。导入项目时选择包含app.json的那个目录,若选了外层 zip 解压目录,开发者工具会报找不到app.json。编译通过后,在模拟器里先点开index页,看控制台有没有getWeRunData:fail can only be invoked by user TAP gesture,这类错误说明页面初始化阶段不该直接调隐私接口。

5.2 常见异常和对应处理

真机调试时,步数相关的报错有规律,下面是我遇到最多的几类:

现象原因处理
fail:auth deny用户拒绝过授权wx.openSetting引导打开
encryptedData无法解析前端直接尝试解密把解密放到云函数或后端
开发者工具有数据,真机没有测试 AppID 或基础库版本不一致换成正式 AppID,切换基础库版本
日期全是 1970timestamp没有乘 1000检查formatDate入参

除了表格里的原因,还有一个延迟问题:微信运动的数据不是实时写入的,用户刚走完步数立刻调接口,stepInfoList里可能还是旧值。比较稳妥的做法是在页面onShow时拉一次,下拉刷新再拉一次,而不是用onLoad只拉一次。

5.3 用 useMock 开关让页面脱离授权调试

对页面渲染来说,反复授权很浪费时间,我习惯在app.globalData里放一个useMock开关:

const useMock = true; if (useMock) { const mockList = [ { date: '2024-01-01', step: 10234, liked: false }, { date: '2024-01-02', step: 8721, liked: true } ]; this.setData({ stepList: mockList }); } else { loadSteps(); }

useMock为 true 时直接喂假数据,页面所有wx:for、图片切换和排序逻辑都能正常走一遍;联调后端时改成 false 即可。setData传整个数组在几十条数据时没问题,如果步数列表规划超过 20 条,前端可以做分页或改用recycle-view,避免一次setData传递大数组导致滚动掉帧。把useMock的切换和真实请求放到同一个入口函数里,后面接云函数时只需要替换else分支,页面模板保持不动。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 5:53:39

Embedding四大技术前提:从one-hot到语义计算的底层契约

1. 为什么“讲透 Embedding 本质”这件事&#xff0c;十年来没人真正做完&#xff1f; 我第一次在2014年读到Mikolov那篇《Efficient Estimation of Word Representations in Vector Space》时&#xff0c;手边摊着三本不同出版社的NLP教材。一本说“word2vec是种无监督预训练方…

作者头像 李华
网站建设 2026/9/14 5:52:49

SpringBoot+Vue全栈足球社区系统开发实践

1. 足球社区管理系统概述足球社区管理系统是一款面向足球爱好者群体的全栈Web应用&#xff0c;采用SpringBootVueMySQL技术栈实现。这个系统解决了传统足球社区信息分散、管理低效的痛点&#xff0c;将球队管理、赛事组织、社交互动等功能集成到统一平台。我在实际开发中发现&a…

作者头像 李华
网站建设 2026/9/14 5:52:16

Java短视频双端音视频链路实战:从MediaCodec硬编码到LiteAVSDK集成

简介&#xff1a;这是一套面向Java中高级开发者与移动开发学习者的短视频APP双端实战源码&#xff0c;聚焦原生Android&#xff08;Java&#xff09;与iOS&#xff08;Objective-C&#xff09;协同开发及音视频核心能力训练&#xff0c;解决跨平台短视频应用从UI架构、媒体编解…

作者头像 李华
网站建设 2026/9/14 5:50:52

Chrome侧边栏免安装Android投屏方案

1. 项目概述&#xff1a;为什么“免安装Chrome侧边栏”成了Android投屏的新解法&#xff1f;最近在几个开发者群和远程协作小组里&#xff0c;反复看到有人问&#xff1a;“QtScrcpy用着挺好&#xff0c;但每次换电脑都要装ADB、配环境、跑服务端&#xff0c;有没有更轻量的方案…

作者头像 李华
网站建设 2026/9/14 5:50:16

C语言/C++课设实战:火柴人躲炸弹游戏完整实现与源码解析

简介&#xff1a;面向高校计算机专业学生的C语言/C课设大作业『火柴人躲炸弹』&#xff0c;是一套开箱即用、可完美运行的编程实践项目&#xff0c;以游戏形式将C/C知识融入真实问题&#xff0c;涵盖角色移动控制、碰撞检测、得分机制等核心游戏逻辑&#xff1b;通过实现躲避炸…

作者头像 李华
网站建设 2026/9/14 5:50:04

HC-SR501人体红外传感器驱动全解析:GPIO检测与状态机实现

简介&#xff1a;面向嵌入式开发者与创客的人体红外传感器驱动资源&#xff0c;整合了基于STM32的完整工程&#xff0c;用于快速实现热释电红外人体检测。包内共203个文件、约7.44MB&#xff0c;以C/H源码为主&#xff0c;包括71个H头文件与58个C文件&#xff0c;辅以3份PDF文档…

作者头像 李华