小程序獲取微信運動步數並集成echarts報表顯示

需求

現在運動計步非常的火,大家常用的計步工具一般有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 = '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實戰聯盟”公衆號留下郵箱,小編有時間將會把源碼發送給大家。

貢獻者

更多精彩內容可以關注“IT實戰聯盟”公號哦~~~

image

發佈了116 篇原創文章 · 獲贊 91 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章