小程序获取微信运动步数并集成echarts报表显示

bigdata 2019-12-25 15:51:26 ⋅ 634 阅读

需求

现在运动计步非常的火,大家常用的计步工具一般有keep、咕咚、微信运动和其他移动设备等,本文是基于微信小程序获取用户的微信运动数据并可视化呈现出来。

先看一下最终实现效果:


微信运动规则

在开发之前需要了解一下微信运动信息的使用规则,这样会规避掉很多问题。
1、微信运动数据必须在微信生态内获取,即小程序、公众号等,且需要用户进行授权。
2、用户主动进入小程序时可以获取到最近30天的运动数据,且一次性获取全部,不能获取指定时间段内的运动数据。
3、目前只能获取到运动“步数”,其他的获取不到(例如:卡路里、公里数、运动轨迹等)。

实现步骤

如果要实现上图示例中的效果需要完成一下几个步骤:

1、调用小程序APIwx.login获取codesessionKey
2、调用小程序APIwx.getWeRunData 获取微信运动数据(加密);
3、调用后端API将运动数据进行解密(Java);
4、小程序集成echarts.js 实现线状图展示;

小程序代码(原生)

第一步:配置页面

在app.json 文件pages内增加WeRunData配置将会自动创建WeRunData.js、WeRunData.json、WeRunData.wxml和WeRunData.wxss 四个文件。

"pages":[
"pages/WeRunData/WeRunData"
],
第二步:获取微信授权
var app = getApp()
var userInfo = null;
Page({
globalData: {
appid: 'wx4167******16a0a1',//appid需自己提供,此处的appid我随机编写
secret: '5498fcab20f********df26bf854ba89',//secret需自己提供,此处的secret我随机编写
},
data: {
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo'),
encryptedData:null,
sessionkey:null,
iv:null
},
onLoad: function () {
var that = this;
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse) {
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
//登录凭证校验。通过 wx.login() 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
wx.login({
success: function (res) {
if (res.code) {
console.log("res.code:" + res.code);
var d = that.globalData;//这里存储了appid、secret、token串
var l = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + d.appid + '&secret=' + d.secret + '&js_code=' + res.code + '&grant_type=authorization_code';
wx.request({
url: l,
data: {},
method: 'GET',
success: function (res) {
var obj = {};
obj.openid = res.data.openid;
console.log("openid:" + obj.openid);
console.log("session_key:" + res.data.session_key);
obj.expires_in = Date.now() + res.data.expires_in;
that.setData({
sessionkey: res.data.session_key,
})
wx.setStorageSync('user', obj);//存储openid
wx.getWeRunData({
success(res) {
// 拿 encryptedData 到开发者后台解密开放数据
const encryptedData = res.encryptedData
console.log("encryptedData:" + encryptedData)
// 或拿 cloudID 通过云调用直接获取开放数据
const cloudID = res.cloudID
console.log("cloudID:" + cloudID)
console.log("iv:" + res.iv)
// 解密运动数据
that.setData({
encryptedData: res.encryptedData,
iv: res.iv
})
// 调用第三步去解密
that.getEncryptedData();
}
})
}
});
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
});
},
getUserInfo: function (e) {
console.log(e)
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
}
})
第三步:解密运动数据

WeRunData.js 解密

getEncryptedData: function () {
var that = this;
wx.request({
url: 'http://127.0.0.1:8080/getEncryptedData', // 这里需要去请求后端代码进行解密,示例中使用Java实现。
method: "POST",
data: {
encryptedData: this.data.encryptedData,
sessionkey: this.data.sessionkey,
iv: this.data.iv
},
header: {
"Content-Type": "application/json"
},
success: function (res) {
console.log("解密后的数据为:", res);
if (res.statusCode == 200) {
let stepInfoList = res.data.stepInfoList;
let data = [];
let categories = [];
for (let i = 0; i < stepInfoList.length; i++) {
categories.push(stepInfoList[i].stepTime);
data.push(stepInfoList[i].step);
}
chartData.main.categories = categories;
chartData.main.data = data;
// 调用第四步 可视化加载
that.stepChartLine();
}
}
})
},
第四步:集成echarts.js 运动数据可视化

集成步骤可以参考Echarts官方步骤:https://github.com/ecomfe/echarts-for-weixin
WeRunData.js 渲染图表

import * as echarts from '../../ec-canvas/echarts';
var chartData = {
main: {
data: [], // 运动步数集合
categories: [] // 运动日期集合
}
};

//初始化图表
init_echarts: function () {
this.echartsComponnet.init((canvas, width, height) => {
// 初始化图表
const Chart = echarts.init(canvas, null, {
width: width,
height: height
});
Chart.setOption(this.getOption());
// 注意这里一定要返回 chart 实例,否则会影响事件处理等
return Chart;
});
},
// 获取数据
getOption: function () {
var that = this
var legendList = []
var option = {
title: {
left: 'center'
},
color: ["#37A2DA"],
grid: {
containLabel: true
},
tooltip: {
show: true,
trigger: 'axis'
},
xAxis: {
type: 'category',
boundaryGap: false,
data: chartData.main.categories
},
yAxis: {
x: 'center',
type: 'value',
splitLine: {
lineStyle: {
type: 'dashed'
}
}
},
series: [{
type: 'line',
smooth: true,
data: chartData.main.data
}]
};
return option
}

第五步:页面布局

WeRunData.wxml

<view class="userinfo">
<button wx:if="{{!hasUserInfo && canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo" class="userinfo-btn"> 点击微信授权 </button>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
</view>
<view class="container">
<ec-canvas id="mychart-dom-line" canvas-id="mychart-line" ec="{{ ec }}"></ec-canvas>
</view>

WeRunData.wxss

.userinfo {
display: flex;
flex-direction: column;
align-items: center;
background: #f0145a;
width: 100%;
height: 300rpx;
}
.userinfo-btn{
margin-top: 50rpx;
background: none !important;
color: #fff !important;
font-size: 40rpx;
}
.userinfo-avatar {
width: 108rpx;
height: 108rpx;
margin: 40rpx;
border-radius: 50%;
}
.userinfo-nickname {
color: #fff;
}
ec-canvas {
width: 100%;
height: 70%;
position: absolute;
margin-top: 300rpx;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

后端解密代码(Java)

EncryptedDataController.java

// 解密微信运动数据
@ApiImplicitParam(name = "map",value = "{\"encryptedData\":\"001\",\"sessionkey\":\"2123\",\"iv\":\"111\"}" )
@PostMapping(value = "/getEncryptedData", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public WxChartStepInfoDTO getEncryptedData(@RequestBody Map<String,String> map) {
String encryptedData = map.get("encryptedData");
String sessionkey = map.get("sessionkey");
String iv = map.get("iv");
// 被加密的数据
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decode(sessionkey);
// 偏移量
byte[] ivByte = Base64.decode(iv);
try {
// 如果密钥不足16位,那么就补足
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
System.out.println("result:" + result);
Gson gson = new Gson();
WxChartStepInfoDTO wxChartStepInfoDTO = gson.fromJson(result,WxChartStepInfoDTO.class);
return wxChartStepInfoDTO;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidParameterSpecException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
return null;
}

WxChartStepInfoDTO.java

@NoArgsConstructor
@Data
@ApiModel(value = "微信运动实体")
public class WxChartStepInfoDTO implements Serializable{
private static final long serialVersionUID = -4526066242748484991L;
private List<StepListBean> stepInfoList;
private WatermarkBean watermark;
@NoArgsConstructor
@Data
public static class StepListBean implements Serializable {
Long timestamp; // 运动日期
Integer step; // 运动步数
@JsonFormat(pattern = "MM-dd")
Date stepTime; // 运动日期(格式化为:yyyy-MM-dd HH:mm:ss)
public Date getStepTime() {
return StringUtil.getSecondToDate(this.timestamp,"yyyy-MM-dd HH:mm:ss");
}
}
@NoArgsConstructor
@Data
public static class WatermarkBean implements Serializable {
@ApiModelProperty(value = "同步微信运动时间")
private Long timestamp;
@ApiModelProperty(value = "appid",required = true)
private String appid;
@ApiModelProperty(value = "格式化运动时间")
@JsonFormat(pattern = "MM-dd")
Date time; // 运动日期(格式化为:yyyy-MM-dd HH:mm:ss)
public Date getTime() {
return StringUtil.getSecondToDate(timestamp,"yyyy-MM-dd HH:mm:ss");
}
}
}

最后

好了,小程序获取微信运动步数并集成echarts.js 可视化实现的具体做法就分享到这里了。如果大家有不明白的可以留言,需要源码的话也可以关注“IT实战联盟”公众号留下邮箱,小编有时间将会把源码发送给大家。



全部评论: 0

    我有话说:

    程序:最新登录授权获取openid等信息

    为优化用户体验,使用 wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持。从2018年4月30日开始,程序游戏的体验版、开发版调用 wx.getUserInfo 接口,将

    程序商城(九):授权实现个人中心页面页面

    实现商城的授权获取用户信息和个人中心页面布局

    程序商城(七):动态API实现商品分类

    程序商品分类页面布局并且调用动态API获取数据加载

    程序商城(一):https框架搭建实现导航功能

    本文将带领大家搭建https的程序框架,实现动态获取数据展示效果!

    程序实战篇:基于wxcharts.js绘制移动报表

    程序图表插件(wx-charts)是基于canvas绘制,体积巧,支持图表类型饼图、线图、柱状图 ......

    程序抖音实战-首页(上)

    你也可以用程序编写一个抖音

    程序抖音实战-首页(下)

    抖音程序首页动态数据获取

    程序-Image的widthFix属性和rpm尺寸的使用

    在做程序的商品详情页,商品的详情是图片集合,渲染完成后发现图片加载的很不自然

    程序实现商品数量加减

    这是一个用程序原生代码实现的数量加减demo,主要是用于商品购物车或者商品详情修改数量使用

    程序电商实战-首页(下)

    上一篇:程序电商实战-首页(上)好了,上一期我们把首页搜索、导航栏和广告轮播给做完了,那么接下来会继续

    程序电商实战-首页(上)

    上一篇:程序电商实战-入门篇 嗨,大家好!经过近两周的精心准备终于开始程序电商实战之路喽。那么最终会做成什么样呢?好了,不啰嗦了 我们先看首页长什么样吧!   首页效果图

    程序 - iconfont 图标字体

    你还在使用图片作为程序的图标?大猪告诉大家如何在程序上使用iconfont字体图标

    程序电商实战-商品列表流式布局

    今天给大家分享一下程序中商品列表的流式布局方式,根据文章内容操作就可以看到效果哦~~~

    程序实战篇:如何解决https域名问题 ?

    开发自己的程序绕不开https问题,为了能在程序中调用我们自己的API服务请打开看一看吧!!!

    程序实战篇:程序之页面数据传递

    我们在写程序的时候经常会遇到子页面向主页面回传数据或者普通页面跳转到tabBar 页面携带......

    程序实战篇:商品属性联动选择(案例)

    本期的程序实战篇来做一个电商网站经常用到的-商品属性联动选择的效果,素材参考了一点点

    程序电商实战-入门篇

    程序开发工具有新版本更新啦!开发体验更好了,接下来一起为电商程序做一下准备前期准备工作~~

    程序电商实战-商品详情(上)

    先看一下今天要实现的程序商品详情页吧!