需求
現在運動計步非常的火,大家常用的計步工具一般有keep、咕咚、微信運動和其他移動裝置等,本文是基於微信小程式獲取使用者的微信運動資料並可視化呈現出來。
先看一下最終實現效果:
微信運動規則在開發之前需要了解一下微信運動資訊的使用規則,這樣會規避掉很多問題。1、微信運動資料必須在微信生態內獲取,即小程式、公眾號等,且需要使用者進行授權。2、使用者主動進入小程式時可以獲取到最近30天的運動資料,且一次性獲取全部,不能獲取指定時間段內的運動資料。3、目前只能獲取到運動“步數”,其他的獲取不到(例如:卡路里、公里數、運動軌跡等)。
實現步驟如果要實現上圖示例中的效果需要完成一下幾個步驟:
1、呼叫小程式API:wx.login獲取code和sessionKey;2、呼叫小程式API:wx.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 = '/file/2019/12/26/20191226143855_30814.jpg + 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-weixinWeRunData.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 視覺化實現的具體做法就分享到這裡了。如果大家有不明白的可以留言,需要原始碼的話可以留下郵箱,小編有時間將會把原始碼傳送給大家。
最新評論