From d88c7d1c824a48607774cae66ee6f84dcadeb6d7 Mon Sep 17 00:00:00 2001 From: "TENCENT\\ivinwu" Date: Thu, 23 Nov 2017 19:45:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deployment.xml | 35 ++++++ .idea/encodings.xml | 6 + .idea/misc.xml | 16 +++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + .idea/weRequest.iml | 12 ++ .idea/workspace.xml | 281 +++++++++++++++++++++++++++++++++++++++++++ README.md | 228 ++++++++++++++++++++++++++++------- build/weRequest.js | 2 +- example/request.js | 45 +++++-- image/logo.png | Bin 0 -> 14141 bytes src/weRequest.js | 195 +++++++++++++++++++++++------- 12 files changed, 737 insertions(+), 97 deletions(-) create mode 100644 .idea/deployment.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/weRequest.iml create mode 100644 .idea/workspace.xml create mode 100644 image/logo.png diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..d667ac2 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b12c368 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d1fabd2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/weRequest.iml b/.idea/weRequest.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/weRequest.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..6119059 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + project + + + + + + + + + + + + + + + + project + + + true + + + + DIRECTORY + + false + + + + + + + + + 1507876610387 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 6e40405..ce119da 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,22 @@ -# 登录时序图 -![](http://mp.weixin.qq.com/debug/wxadoc/dev/image/login.png) +logo + +# weRequest + +_解决繁琐的小程序会话管理,一款自带登录态管理的网络请求组件。_ + +[![Travis](https://img.shields.io/travis/rust-lang/rust.svg)]() +[![Github All Releases](https://img.shields.io/github/downloads/IvinWu/weRequest/total.svg)]() +[![GitHub forks](https://img.shields.io/github/forks/IvinWu/weRequest.svg?style=social&label=Fork)]() +[![GitHub stars](https://img.shields.io/github/stars/IvinWu/weRequest.svg?style=social&label=Stars)]() +[![GitHub watchers](https://img.shields.io/github/watchers/IvinWu/weRequest.svg?style=social&label=Watch)]() +[![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg)]() + +## 简介 + +![登录时序图](http://mp.weixin.qq.com/debug/wxadoc/dev/image/login.png) 上图是小程序官方文档中的**登录时序图**。此图涵盖了前后端,详细讲解了包括登录态的生成,维护,传输等各方面的问题。 -# 发起网络请求的流程图 + 具体到业务开发过程中的前端来说,我认为上图还不够完整,于是我画了下面这张以**前端逻辑**为出发点的、包含循环的**流程图**。 我认为前端每一次**发起网络请求**,跟后台进行数据交互,都适用于下图的**流程**: ![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow_login.png) @@ -10,6 +24,7 @@ - **hasChecked:** 用一状态标识本生命周期内是否执行过`wx.checkSession`,判断该标识,若否,开始执行`wx.checkSession`,若是,进入下一步 - **wx.checkSession():** 调用接口判断登录态是否过期,若是,重新登录;若否,进入下一步 > wx.checkSession()是小程序提供的检测登录态是否过期的接口,生命周期内只需调用一次即可。用户越久未使用小程序,用户登录态越有可能失效。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明 + - **wx.getStorage(session):** 尝试获取本地的`session`。如果之前曾经登录过,则能获取到;否则,本地无`session` - **wx.login():** 小程序提供的接口,用于获取`code`(code有效期为5分钟) - **wx.request(code):** 将`code`通过后台提供的接口,换取`session` @@ -17,16 +32,13 @@ - **wx.request(session):** 真正发起业务请求,请求中带上`session` - **parse(data):** 对后台返回的数据进行预解析,若发现登录态失效,则重新执行登录;若成功,则真正获取到业务数据 -# 拓展小程序网络请求的能力 -只要遵循上图的流程,我们就无需在业务逻辑中关注登录态的问题了,相当于把登录态的管理问题**耦合**到了发起网络请求当中。 -一般情况下,我们程序设计都会遵循模块解耦的原则,尽可能将模块颗粒化到最小。这导致可能有些同学认为模块耦合不是好事情,但是我认为这是要分情况的: -- 小程序区别与传统的H5,不支持cookies,在代码层级上讲,这无形中就给登录态的管理增加了复杂度:cookies会在H5的每个请求中自动带上,但小程序的请求却每次都需要手动带上登录态参数 -- 小程序区别于基于公众号登录的H5来说,又存在一定的优势:登录授权时并不需要多次的页面跳转(Oauth),也正因为如此,小程序的请求在登录态失效时,需要具备重新登录并自动重试请求的能力(无页面刷新感,用户甚至都不能感知到进行了重新登录) +只要遵循上图的流程,我们就无需在业务逻辑中关注登录态的问题了,相当于把登录态的管理问题**耦合**到了发起网络请求当中,本组件则完成了上述流程的封装,让开发者不用再关心以上逻辑,把精力放回在业务的开发上。 + +## 目标 +让业务逻辑更专注,不用再关注底层登录态问题。小程序对比以往的H5,登录态管理逻辑要复杂很多。通过`weRequest`这个组件,希望能帮助开发者把更多精力放在业务逻辑上,而登录态管理问题只需通过一次简单配置,以后就不用再花精力管理了。 + +## 怎么使用 -以上两点虽然是登录态管理的问题,但从另外一个角度去理解,我更认为它是小程序网络请求的能力问题,所以,我认为**通过拓展小程序网络请求能力来实现登录态的自动管理**是非常合适的。 -# 通用组件——weRequest -一个通过拓展`wx.request`,从而实现自动管理登录态的组件。 -先来看看怎么使用: ```javascript var weRequest= require('../weRequest'); @@ -51,87 +63,213 @@ weRequest.request({ - 初始化组件配置 - **就像使用`wx.request`那样去使用它** -## 自动带上登录态参数 -我们来看看执行上面代码的DEMO效果: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/auto_session.png) +## 演示DEMO + +### 自动带上登录态参数 +![自动带上登录态参数](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/auto_session.png) 可以看到,通过`weRequest`发出的请求,将会自动带上登录态参数。 对应的流程为下图中**红色**的指向: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow1.png) +![自动带上登录态参数](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow1.png) -## 没有登录态时,自动登录 -那如果当前小程序并没有登录态的情况又会如何呢? -接下来我们来看看本地无登录态情况下的模拟: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/autoLogin.gif) +### 没有登录态时,自动登录 +![没有登录态时,自动登录](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/autoLogin.gif) 当本地没有登录态时,按照流程图,`weRequest`将会自动执行`wx.login()`后的一系列流程,得到`code`并调用后台接口换取`session`,储存在localStorage之后,重新发起业务请求。 对应的流程为下图中**红色**的指向: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow2.png) +![没有登录态时,自动登录](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow2.png) -## 登录态过期时,自动重新登录 -接下来我们再来看看,当本地储存的登录态过期之后,页面的行为如何: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/relogin.gif) +### 登录态过期时,自动重新登录 +![登录态过期时,自动重新登录](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/relogin.gif) 对后台数据进行预解析之后,发现登录态过期,于是重新执行登录流程,获取新的`session`之后,重新发起请求。 对应的流程为下图中**红色**的指向: -![](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow3.png) +![登录态过期时,自动重新登录](https://raw.githubusercontent.com/IvinWu/weRequest/master/image/flow3.png) + +## 文档 + +### .init(OBJECT) + +对组件进行初始化配置,使用组件发起请求前必须进行至少一次的配置 + +#### OBJECT参数说明 + +|参数名|类型|必填|默认值|说明| +| :-------- | :-------| :------ | :------ |:------ | +|sessionName|String|否|session|储存在localStorage的session名称,且CGI请求的data中会自动带上以此为名称的session值;可不配置,默认为session| +|urlPerfix|String|否||请求URL的固定前缀,如果配置了,后续请求的URL都会自动加上这个前缀| +|loginTrigger|Function|是||触发重新登录的条件;参数为CGI返回的数据,返回需要重新登录的条件| +|codeToSession|Object|是||用code换取session的CGI配置| +|reLoginLimit|Int|否|3|登录重试次数,当连续请求登录接口返回失败次数超过这个次数,将不再重试登录| +|successTrigger|Function|是||触发请求成功的条件;参数为CGI返回的数据,返回接口逻辑成功的条件| +|successData|Function|否||成功之后返回数据;参数为CGI返回的数据,返回逻辑需要使用的数据| +|errorTitle|String/Function|否|操作失败|接口逻辑失败时,错误弹窗的标题| +|errorContent|String/Function|否||接口逻辑失败时,错误弹窗的内容| +|errorCallback|Function|否||当出现接口逻辑错误时,会执行统一的回调函数,这里可以做统一的错误上报等处理| +|doNotCheckSession|Boolean|否|false|是否需要调用checkSession,验证小程序的登录态过期;若业务不需要使用到session_key,则可配置为true| +|reportCGI|Function|否||接口返回成功之后,会执行统一的回调函数,这里可以做统一的耗时上报等处理| +|mockJson|Object|否||可为接口提供mock数据| +|globalData|Object/Function|否||所有请求都会自动带上这里的参数| + +##### codeToSession参数说明 + +|参数名|类型|必填|默认值|说明| +| :-------- | :-------| :------ | :------ |:------ | +|url|String|是||CGI的url| +|method|String|否|GET|调用改CGI的方法| +|codeName|String|否|code|CGI中传参时,存放code的名称| +|data|Object|否||登录接口需要的其他参数| +|success|Function|是||接口返回成功的函数;需要返回session的值| +|fail|Function|否||code换取session的接口逻辑出错时,执行的函数,若配置了此函数,则不再默认弹窗报错| + +##### reportCGI返回参数说明 +|参数名|类型|说明| +| :-------- | :-------| :------ | +|name|String|调用的接口名字,可在request接口的report字段配置| +|startTime|Int|发起请求时的时间戳| +|endTime|Int|请求返回时的时间戳| +|request|Function|请求方法,可用于上报| + +#### 示例代码 -## 组件的配置项 -`weRequest`提供一个`init`方法,用于对组件的配置,以下展示所有的配置项: ```javascript weRequest.init({ - // 储存在localStorage的session名称,且CGI请求的data中会自动带上以此为名称的session值;可不传,默认为session sessionName: "session", - // 请求URL的固定前缀;可不传,默认为空 urlPerfix: "https://www.example.com/", // 触发重新登录的条件,res为CGI返回的数据 loginTrigger: function (res) { // 此处例子:当返回数据中的字段errcode等于-1,会自动触发重新登录 return res.errcode == -1; }, - // 用code换取session的CGI配置 codeToSession: { - // CGI的URL url: 'user/login', - // 调用改CGI的方法;可不传,默认为GET method: 'GET', - // CGI中传参时,存放code的名称,此处例子名称就是code;可不传,默认值为code codeName: 'code', + data: {}, // CGI中返回的session值 success: function (res) { // 此处例子:CGI返回数据中的字段session即为session值 return res.session; } }, - // 登录重试次数,当连续请求登录接口返回失败次数超过这个次数,将不再重试登录 reLoginLimit: 2, - // 触发请求成功的条件 successTrigger: function (res) { // 此处例子:当返回数据中的字段errcode等于0时,代表请求成功,其他情况都认为业务逻辑失败 return res.errcode == 0; }, - // 成功之后返回数据;可不传 successData: function (res) { // 此处例子:返回数据中的字段data为业务接受到的数据 return res.data; }, - // 当CGI返回错误时,弹框提示的标题文字 errorTitle: function(res) { // 此处例子:当返回数据中的字段errcode等于0x10040730时,错误弹框的标题是“温馨提示”,其他情况下则是“操作失败” return res.errcode == 0x10040730 ? '温馨提示' : '操作失败' }, - // 当CGI返回错误时,弹框提示的内容文字 errorContent: function(res) { // 此处例子:返回数据中的字段msg为错误弹框的提示内容文字 return res.msg + }, + errorCallback: function(obj, res) { + // do some report + }, + doNotCheckSession: true, + // 上报耗时的函数,name为上报名称,startTime为接口调用开始时的时间戳,endTime为接口返回时的时间戳 + reportCGI: function(name, startTime, endTime, request) { + // 这里可以自行上报耗时 + //wx.reportAnalytics(name, { + // time: endTime - startTime + //}); + //request({ + // url: 'reportCGI', + // data: { + // name: name, + // cost: endTime - startTime + // }, + // fail: function() { + // + // } + //}) + console.log(name + ":" + (endTime - startTime)); + }, + mockJson: require("../../mock.json"), + globalData: function() { + return { + version: getApp().version + } } }) ``` -# 让业务逻辑更专注,不用再关注底层登录态问题 -小程序对比以往的H5,登录态管理逻辑要复杂很多。通过`weRequest`这个组件,希望能帮助开发者把更多精力放在业务逻辑上,而登录态管理问题只需通过一次简单配置,以后就不用再花精力管理了。 -# FAQ -## 我希望在请求时候,页面能出现最简单的loading状态,该怎么办? +### .request(OBJECT) + +带上登录态发起一个请求,参数大部分与`wx.request`一致 + +#### OBJECT参数说明 + +|参数名|类型|必填|默认值|说明|是否与wx.request不一致| +| :-------- | :-------| :------ | :------ |:------ |:------ | +|url|String|是||开发者服务器接口地址,若在init()时有配置urlPerfix,则这里会自动拼接前缀|是| +|data|Object/String|否||请求的参数|| +|header|Object|否||设置请求的 header,header 中不能设置 Referer。|| +|method|String|否|GET|(需大写)有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT|| +|dataType|String|否|json|如果设为json,会尝试对返回的数据做一次 JSON.parse|| +|beforeSend|Function|否||发起请求前执行的函数|是| +|success|Function|否||收到开发者服务成功返回,且执行`successTrigger`成功后的回调函数,参数为`successData`返回的参数|是| +|fail|Function|否||接口调用失败,或执行`successTrigger`失败后的回调函数,若这里有配置,则不再默认弹窗报错|是| +|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)|| +|showLoading|Boolean|否|false|请求过程页面是否展示全屏的loading|是| +|report|String|否||接口请求成功后将自动执行init()中配置的reportCGI函数,其中的name字段值为这里配置的值|是| + +#### 示例代码 + +```javascript +weRequest.request({ + url: 'order/detail', + showLoading: true, + report: 'detail', + data: { + id: '123' + }, + success: function (data) { + console.log(data); + }, + fail: function(obj, res) { + } +}) +``` + +### .uploadFile(Object) + +带上登录态,将本地资源上传到开发者服务器,客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data,参数大部分与`wx.uploadFile`一致 + +#### OBJECT参数说明 + +|参数名|类型|必填|默认值|说明|是否与wx.uploadFile不一致| +| :-------- | :-------| :------ | :------ |:------ |:------ | +|url|String|是||开发者服务器接口地址,若在init()时有配置urlPerfix,则这里会自动拼接前缀|是| +|filePath|String|是||要上传文件资源的路径|| +|name|String|是||文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容|| +|header|Object|否||设置请求的 header,header 中不能设置 Referer。|| +|formData|Object|否||HTTP 请求中其他额外的 form data|| +|beforeSend|Function|否||发起请求前执行的函数|是| +|success|Function|否||收到开发者服务成功返回,且执行`successTrigger`成功后的回调函数,参数为`successData`返回的参数|是| +|fail|Function|否||接口调用失败,或执行`successTrigger`失败后的回调函数,若这里有配置,则不再默认弹窗报错|是| +|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)|| +|showLoading|Boolean|否|false|请求过程页面是否展示全屏的loading|是| +|report|String|否||接口请求成功后将自动执行init()中配置的reportCGI函数,其中的name字段值为这里配置的值|是| + +### .login() + +[不建议使用] 在不发起业务请求的情况下,单独执行登录逻辑 + +### .setSession(String) + +[不建议使用] 设置用户票据的值 + +## FAQ + +### 我希望在请求时候,页面能出现最简单的loading状态,该怎么办? + 只需要在请求的时候,加上参数`showLoading: true`即可,如: ```javascript weRequest.request({ @@ -147,7 +285,8 @@ weRequest.request({ ``` 当然,如果你希望使用个性化的loading样式,你可以直接使用beforeSend参数来进行自定义展示个性化的loading,并且在complete的时候将它隐藏。 -## 某些请求在返回错误时,我不希望触发通用的错误提示框,而想用特别的逻辑去处理,该怎么办? +### 某些请求在返回错误时,我不希望触发通用的错误提示框,而想用特别的逻辑去处理,该怎么办? + 只需要在请求的时候,加上参数`fail: function(){ ... }`即可,如: ```javascript weRequest.request({ @@ -166,7 +305,8 @@ weRequest.request({ ``` 此时,如果接口返回错误码,将触发这里定义的fail函数,且默认错误弹框将不会出现。 -## 为什么工具在发起请求之前,不主动去判断第三方session是否过期,而要通过接口结果来判断,这不是浪费了一次请求往返吗? +### 为什么工具在发起请求之前,不主动去判断第三方session是否过期,而要通过接口结果来判断,这不是浪费了一次请求往返吗? + 每个小程序对于自身生成的session都有自己的一套管理方案,微信官方也没有指明一套通用的方案来要求开发者,仅仅要求了**应该保证其安全性且不应该设置较长的过期时间**。 原文如下: >通过 wx.login() 获取到用户登录态之后,需要维护登录态。开发者要注意不应该直接把 session_key、openid 等字段作为用户的标识或者 session 的标识,而应该自己派发一个 session 登录态(请参考登录时序图)。对于开发者自己生成的 session,应该保证其安全性且不应该设置较长的过期时间。session 派发到小程序客户端之后,可将其存储在 storage ,用于后续通信使用。 diff --git a/build/weRequest.js b/build/weRequest.js index aaacf59..b07af43 100644 --- a/build/weRequest.js +++ b/build/weRequest.js @@ -1 +1 @@ -module.exports=function(t){function e(n){if(o[n])return o[n].exports;var c=o[n]={i:n,l:!1,exports:{}};return t[n].call(c.exports,c,c.exports,e),c.l=!0,c.exports}var o={};return e.m=t,e.c=o,e.i=function(t){return t},e.d=function(t,o,n){e.o(t,o)||Object.defineProperty(t,o,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var o=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(o,"a",o),o},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=1)}([function(t,e){function o(){wx.showToast({title:"加载中",icon:"loading",mask:!0,duration:6e4})}function n(){wx.hideToast()}t.exports={show:o,hide:n}},function(t,e,o){function n(t,e){M?c(t,e):(e.count++,wx.checkSession({success:function(){M=!0},fail:function(){C=""},complete:function(){e.count--,c(t,e)}}))}function c(t,e){C||e.isLogin?"function"==typeof t&&t():b?setTimeout(function(){c(t,e)},300):(b=!0,e.count++,wx.login({complete:function(){e.count--,"function"==typeof e.complete&&0==e.count&&e.complete()},success:function(o){if(o.code){var n;n="function"==typeof g.data?g.data():g.data||{},n[g.codeName]=o.code,e.count++,l({url:g.url,data:n,method:g.method,isLogin:!0,success:function(e){C=e,M=!0,"function"==typeof t&&t(),wx.setStorage({key:m,data:C})},complete:function(){e.count--,"function"==typeof e.complete&&0==e.count&&e.complete(),b=!1}})}else wx.showModal({title:"登录失败",content:"请稍后重试",showCancel:!1}),console.error(o),b=!1},fail:function(t){wx.showModal({title:"登录失败",content:t.errMsg||"请稍后重试",showCancel:!1}),console.error(t),b=!1}}))}function i(t){return"function"==typeof t.beforeSend&&t.beforeSend(),void 0===t.reLoginLimit?t.reLoginLimit=0:t.reLoginLimit++,void 0===t.count&&(t.count=0),t.showLoading&&(h.show(),t.complete=function(t){return function(){h.hide(),"function"==typeof t&&t.apply(this,arguments)}}(t.complete)),t}function u(t){t.count++,t.data||(t.data={}),t.url!=g.url&&(t.data[m]=C),t.method=t.method||"GET";var e=t.url.startsWith("http")?t.url:x+t.url;"GET"!=t.method&&(e.indexOf("?")>=0?e+="&"+m+"="+C:e+="?"+m+"="+C),wx.request({url:e,data:t.data,method:t.method,header:t.header||{},dataType:t.dataType||"json",success:function(e){if(200==e.statusCode)if(t.isLogin){var o="";try{o=g.success(e.data)}catch(t){}o?t.success(o):r(t,e)}else if(y(e.data)&&t.reLoginLimit=0?o+="&"+h+"="+E:o+="?"+h+"="+E);for(var n in e)o.indexOf("?")>=0?o+="&"+n+"="+e[n]:o+="?"+n+"="+e[n]}t.report&&(t._reportStartTime=(new Date).getTime()),wx.request({url:o,data:t.data,method:t.method,header:t.header||{},dataType:t.dataType||"json",success:function(e){if(200==e.statusCode)if(t.report&&"function"==typeof b&&(t._reportEndTime=(new Date).getTime(),b(t.report,t._reportStartTime,t._reportEndTime,r)),t.isLogin){var o="";try{o=w.success(e.data)}catch(t){}o?t.success(o):u(t,e)}else if(T(e.data)&&t.reLoginLimit=0?o+="&"+h+"="+E:o+="?"+h+"="+E);for(var n in e)o.indexOf("?")>=0?o+="&"+n+"="+e[n]:o+="?"+n+"="+e[n];t.report&&(t._reportStartTime=(new Date).getTime()),wx.uploadFile({url:o,filePath:t.filePath||"",name:t.name||"",formData:t.formData,success:function(e){if(200==e.statusCode&&"uploadFile:ok"==e.errMsg){if(t.report&&"function"==typeof b&&(t.endTime=(new Date).getTime(),b(t.report,t._reportStartTime,t._reportEndTime,r)),"json"==t.dataType)try{e.data=JSON.parse(e.data)}catch(o){return u(t,e),!1}T(e.data)&&t.reLoginLimitA`e!Ta7 z|9y95t=Tgx=bUFh&)$31WS>cxs`6(XOiD}u0D$vFPFfuRK!UxV!_bjlj|xd*3a!|XYRXVnP>@s*&tA-uRgqFul56J*LIy%# zUS5WdhX#)N8<$H7%JA)SzjX90cfND5IF;o05U z>iG&&1`~ZXonoOP^mLNBhN);IcK>G2vOxCFerMNKd*?=dWYv$~b>GN~$*8g@{i?ry zZ1||srA(erp3>nN*WxVGCQX<`U@>T}->X+TnUX!3O!yjW*GJc6(LCKZU1Bc2Q3o{jgrJEoH z5rH7Cc!#Yoefp%B!e;DwR6$%-!Yy7AU~u&Bo-PKJs^C1t`K+a=3K<2E>W-101`RO63 z{Q7}Hh3xomhs2P1Bi9EG6u_+}C@7$z{}lB( zPX!1k0%J5pIAE-~upCfhAgoW%M zFa2HQ)a*nk_1KAEkPWl%4Uiur2_USrwyfSj_MQM$r;OO6mAoB(XO+~3LC z_T_j@^e-IQ9%wJy%|qjUVdmM&`21O5`5M^WNM^mfPSO&6^y%FNfJGX9=s5-wXfgUZV0JBgYWQoXnR@W>m$-nvvJLitcIJ5#8Rb!ypu};B z_@26p(QW_HOtT_jwE<@Dwa;p^0QIcmYxkHNX-&3!!=w|5fu`h~DbEjOkUG+&{wz$5 zu_N!arf+=aD^Q)W<<>A8DeLR{v2*6nZ^TUwneViTnL0h*vL@AULRw>NlT_l_ChyWZ zUFrf!ne6Rwf6xYW5u1#$B7@9r3s-AGzn$;uFB zqm-oUP3u&Y**M{Ld8=;HT%pN)d>RQVL+PdoJrkP*k?hesME|LM$|(C+!l-|pmh3j; zuAj|n!EafT5~EF+`KaoI2Bh(?iI@mJ(xKrdsOX|osWgZx&6>yeBahE4xIaph(e^PH zrX3E$vXibZmi>nf*dYOX#CF=pTyn_WLOwP+MNP)_rNk>}mb1(1hlm$jw}tH3Wgs@? z+`Aq*-yiR!n7mx9#aOS#VkL64+MSnTl3m)XD5x)&^PN(2QSY}iPv=s$xf3S||4{tW zhRA$`_i{0LH$J;ig;Y($q63~Q|1N}!aV}2%qcyh{j2SW}DJbwHArcLahwDmJT zPK60>FO8aP3aMtnlFv$)KbX|mhlezl&moISk2B5@IVdOh&u|)Rx<|E7kfwf|DrkO2 z(nP}~@~L4tC6<{uDzmJ?{BBeJ*{H6snvM_Gx9bDbvEuxs0)JTscK`-4`0ev$6(E>F zH5O*wvZ}m)_hf$QdR^gWoD%*{7Et0Wy$5+Cb~3-aUA2fo+>!Ti8+s)1KDREwK1Gqk zv!0Qf>({NusvK`hZfj>Xt2Tl@^Cb0jj(bL$^c2Q+S$2+)>MxjR)`45WpVgUgDXB<& zr$Q&X!t?UE<5=_8eDf0+qy4Lsic9=fsB8js{r54@z1t~wk|6K**x^C}m>!RnKt24B zb8(m$oa*@-h;e2JQqb2IW2U)BQfsJi$$BS?n|2Y=rcxcH&tHhfqzT&2F!{*ZLla>i zcYInGrmxp7<=sm=M_?|I!V;=*)VQOsS=mv>tO+tZ6V5;1RF=U@vY7NZ|E)`FLrgE| zM4g+^Dt#ltZIo4%F&#m~Dg6=rYWs)=Sa_Tz$6h^Db*?O z$nY+J=7%eLM+9J;DS|SLmU+YT^S+yja4t{_nc2n~*1swwMqU#5NXY4anr@{_w$kQdh9*cMNs^sB! zwE0&?aG?7x)s|$;ILGJH^uoYtGfeh^cwdU%&EV~Nc&Zb4b;d}7wzE?UkpfuOCKvv_}$mGPQW(U$sOkJ6~U<-g~!?@RN5(Ob+2?U@y)40 zch~MR{O(7!K?Gg+nrX7X+Zgc=Vo2Y!z4ZGyv6?4v^OuFKj;Ef9;?IOh8JFS`vj=04 zJS{np?|wbWU76UEG~8BaFLp6(6BK7=X!szdru~F3P1br=xWI;SC3~RQ@WET$W<@=F z;xXDoQik~rg9n;9nP@E6a8?E8u+t(fO})qbmS_$7v56EBn9qS2{uHzjx}~ zqW9M7GR&$TXgj29>@Fj*nVO+1BH_4W*ruFgecz4N@iMLwLRf@kJVyRKJTK5YV{}2^ z@3hE;2^%4+jK7K^DMVfe3uw;2RyS4B)H@~*J$SI5SBjDZg&Y0rp2Tja^kc~nVZcgQcC zFBMMubYDfNlGW}lawGRo@Eqw(7sZ?yESl2j4OYU4SPZVv&lgXM-o;y+g-I6`+tLvp z@`xU+eSIJP{yOlma1%GrVyfsNtNi)&CgF2-I~K}-pfqhZEW$}4b4Ptqu(Tm-=0+vW zhRnx^t(OdpS#?W>BIj#VjQ%l2pR16$=sX6Ndm{eybkE#@=LbB)of>2ks$)1mHT1BC zu&gLsL(n;DTu{1)X6fwYbIUK( z4J;$o`;2Imx|FjpnIe5=AF8F;%qBYc=?P{334P<>I?t4FsrV~U;=2bc)hsejSKHbX zZO`8sdu4-T0@D2TmGx3qo?oqr@kVCIBR0dR$RCuhl8 z#U-|uPWLmr)uHsy^qzxnGW%NL~`q{ru8SN zxK(H8JIgqoRgxTnrwPK8pJFb`n0#+(kYCK+P|XgPw9z3hkXWiD zDY4c90?1^HRHfB44d+CSCo6sT6TV>njUQ7y)5w}INtEQ~@$mEbzP_S(s;H@lu+&%4 zelZ$gcX-#&WZN2bXPv~s{DY@W zxj*No!Z*7TDMkDH)j4m7vvPSJ`&>Mn6sGMj_S2x&$z(Mc>(-6aMrvDV?}8f(ex}0* zsgvSd__cxCNv0*_wWmfDc;*+RWNP!p6AOeTv7rN#;;h2R9&lBjdy~KD@kbu&SAqbx zy0(k?XNn@&=61LYJjkp}=Bu1z0r+^E%mNWQUHUt(~tX{M}Yi&2h71K2n#pL!qHWA4E>-tl>DD=3LYkSH^=W8=iP! zy?VYMBz93_w&e?O>pp+iMy3Sm<*wG)=)YC@YFUl}JfKf4`2kK_M_luUvZG#5EI`%tS`Z!KUtVKTZfUpa4xrR8wM9*$>PiRqolCT#=rZ9e-bYu__a*lFF$ef|5b{+LJwoo1dJm13k zyoIfN^(mVO8w{xv_ap42dSq%vu6A`3J5}Gva9Tp`i7`CIDqTMy3?yk+r8}`& zGy>8QKpMz7`%{sn9iEMV6NINz=W#j+BWz zWM+^aKJ3q%?yifJS_#l4F`ie)9b>u# zSx~@-kF^L3X9^#MI6m@ke)do^(-+=N=fePHcFW+Ohlb8i^^LHqqFFZ7kvePOw(13{ zHq1=9-|JCHh_ll4_8A6Sk=Uf@=>r{v8%7=trC@C+62D0#)Po)zsME*(aGsUMG0|0X1|fq-GwL=uIW}u7=a1rjryGFT%j>Ipk?58O zF*7-U4nDi!1>+FK;u`96W`hfeK|ClxYXf~ws`~yTF(#pf#p7fAJ@Io@?ABgq#XDCC zR_V2aM6E+CieRo&1e?lZUqY|JgsaTYk>og8Ajpo`6DZKW^lj+)>C}ULW=((01fBxh ztsPLt)$bv#fDGQKf!Ft8S>vLSsFsiZ+H@e6bQcF1?C-3tt$PpQ%EK^U{nHKkr;6O# z5(sf1sJB+>js$5sO~!$N(ZZgLKrYxAlrc55(@SbK-;X+xuGc2fpVXsQ;w43bzSfD& z&9|?3syS9cRrM<_6^scB(e^r>HdranaXQSVgD3-WLti%|E zaV-RN%UZ!C~@qWQxeCvMf8zh&8?GiLwlmWePSx3 z1#L7%^#}hQR1fimE=8<-tlG0i^hKX7`g`>>F!IAZUjP?gr0#XSwNNoVRE()==@fAv zTa$OmS^`IOUXmTLa08J9UmF@tMfc}+*cOjm{}Q$*=A@3%2OEf1Hq4dA2RiA=C=<2BYtC+8kI#eKZ0}I+h z4Cv5(`Gc=$`SK)Mb?5G7i8?JI8)Q*u(`hylDT+vG&h6T z${5&9r-Rckn+a^wF}<~*fT_zX3|@#Ti?F9TMTucdsu+87aER<{>H_OV!v#n8SS~YG ziJ^O*$$iaxiO1P*H5#Wqi9H1f@kn-nI>Gi(b^>HvzR{;zEikasWd4mc1B!DZ|lplSY<%K4o26SE{ z!LjC)>2LHCSk|iN*R2s@*R{Yi*bi(F5Lp_M;UJqZyw;}0nKP`Ost&CnK=yA%Q+w{x zU#olppp~>M4z4F&Twh+>fowqY*ZQ!*-ZFYvg4Ro5qht~k1zEM9W_3T1V^lCj!$GvF z7Ttlsfp6`dQRFZyy=u5RMk`++G!mjf@>Q5KDhvbiVUN-X`Kcb+6LN$>7d6Xa_yv-G zRr=n736_=V-GZ@u#S_jZ6M4ZoC;012;JKvy0eW0gc|AEO0vSPX`j!n0QPB=~-egi} zl0Cn)!+LUm7cdcf#<$j7a#_Wi0dc>Uz~dZp`#w-KP{dZ}&OG}G8ACVAb?^F5XsrP| z>KZ*zg5$Th6)P~i*dN!q$N1fk1z2ZY5E>om0xwGghQoc!D*KUg zkK_l?DAA7eecjh2At*Z`vj5-BB*1S7TB(Fz>}#{Vjvj_X3644PjA1~U3TFySp?V8G+Dx@I$yrSbDnmxtWRK%meLu_|6}_G}Y6Y_R zvWqx%qL=_NC5OfFk1$ug?jl`D$n1s-N%K%zS#e7dz>+Z# z`IYX9dvh!$FkmEQUi~r39v0L@#aDSR05Dh0O@1VbC{?=45x(^rCq=a46M?-|P5=1+ z@ZHp;&Q8GC2jD<-9${Y9A7=_f{th*VfoUOV=)D;?l!G|PM(!}>(>7@a{iLZk*OFlg zVa!ae6Y5ZQBIHCgjfrAGr9Z8VagJcK@_!spHD`lu0)C9hf8TOXteVL^4FtS#e9msK zJO5^WjOb3d_3mmm9FzIc#)7pey^the5)1ERqCVf#vip=!RWfK74d0~XqO-rmYG|;y|z)y>Blui?_LA4N- zBF(CbtkO=;Qz5;MPaH12i3JsQW7)E92y!VcPP{cU*n6fS$|)X&5FVmm<>}qa%sBFa z&%CHYCNZ6j`466Ou&$`5=Z$_#`wxF08u8C(NWxkCtvyqK>luP>wZ0aT5D(BF9{&ep zs0Anp-MN9iB;oSpScIf8Lmu}O*V%!f+?2nxQHI~NIU0YYRITYXsJ0M$f!)Av$($f0^Q^z zdq|M7E%qewE6><*j;Fu3cr3^dO~!8oI zJu>eJx(s>ok5|Le6(kEo6c{0OM9619Ca+ktTxBGh*3J(qiD;ufvKh1;wEqBgGzI+g z^B(I{_-Q9=KZs5Zn*F>mm^_IoyP(c3h{lM{cOP1e3~~*`*ErPBGnOraqF_+ zUM2A7inklc5j7E$_H*)T9ROc8Up}pG;Ni&Z&$;I53Kyg+=^yi zW-|I80$+2x1P(F%Cnk+&G~rb;YQ{e0Nf_`~0(LiSiJ_H@->OP5LBBeut8~|kJfQ_} zDQ;a~6=+-z<0zY{m&OowYnlU6*{L`Ou+gYy;OqzrD=gq-nM(BZ&YrraNknZu15~Tc zzen|C#)g|=K=L+_;lM36jujuz&!K{_OAK7gx${+Y!M@eAQYg&xJc%K$I3qKLxIA<|mXSVYy2^9dVQaU3Z$8TXbiy00hXJ)4R5WfSo{7DC=&9QOfVx2r2_SKMa zZ?DL8etX@VcV1B77F-+r2a+KA#hg&WL>mK!Chs4O-9Nr$sHm&(mLKNh{j}W9*k?D* z-*7YsTmD{#o>Rf*fDO+hWaQ?nbW#t z?ecr5G#w?|RSj=Y)j0wGOgAbnq znPi};8g-3yBR}Q2jSS_`i_xCuTt4HXUC=h7rFLDR>1W)s)ye+GivKVwdrx4c--OqWq6jwaL4s0^ZoJwwxr3ZcWc2G{mQn{8Q`wOLWx#Zmi@& z47_f(g3eH!8OI}m6`vR6@1IgSHi)tgo_}Ai9(EMjeHx=*nEbfFp6VWQ|6`aH7xXgx zMzSF#`95{eH<8t5w>sPm67=WL&mvwqOA;FY3S`HAf}f;ZcKlI7wLzT;M|kM4m46Hc zAy{Zn>Btn$>-F3HLaGcb{T-mUHck1vne{}?CXJ0fe+4U!c)?8vir_#~n zCU)bSVi6_zfUN8@TL~nON}|tFk(us8#or4os0Ixs!}HLJruez+8R{df@G2@gyF-pSSrxdFh0ieBI7>%=Ev!** zOB{3=%KwZC59WhVzpD12iF!yN+bHZKBDCgCdxjk7%b$^mn7WsR3{HEy%g`%<099kL zZxer+3szkSb1^Pf`9A+s+8A?l?w?BAWkfj`Q_~YzJrUV7McOZm}D3 zVBGyc{984pX~ispBrAAGQ{%q@4#|Dd^^GK7k**kFM;k8P^ctyi55`*gQ^$Su!Rdo$ zupcqpuflUHV;_s(*fGHmO{u6$F;YACEtD3lkay`Zl3~OUw@3FUVFD2(=knm~*(Jx| z@m+=UZB+V9=fKrNa?LeD?y4+o$-rV-3Pjt-j_L4T9HB?mQ3O%AK^FtDstPz}O5(A59#Te0ye%oG|j#wR=rbGpi?HVSY42w0nUJ9ZP_a#Ekq(n6P* z$-*^FI=}H$%~KKDW+iLt(^*=ngdZi){;i^I+7i>b>ZV8TaEhXe^z9kErWBK#QA%`u zFH2BV+0bizPR%xq15y z$@5?cUg-KvLKI}fq7-V)rpWX{pXEH;XvEMyrF2$jGJk*0m(h}!H|y#t**B;#3(+=+ z{;$xiDt<>=bXYJ_osV9S&j9>2mFQfBAUVzWPEJmnSS|pi9fMV|wac`0OV@5=d{z10 zupT z-hG;bq6caTpwFtF%>Kv^cn&%$fj$z)uRn!$_Tjv0Q#az1OEfpc^M)x2u*Q`^xyZ4E zi>dhlE}~xt?HUWO=u*;d2#B1adPh}uPdFN_T~27Hr1owCRX4*jHg-=6*KowrZ}U~0 z6q{AyTK3e{fm&Z(A-#VNOP=VXhylWV8C;yPO1q?1f;<>q-I^@#$AY26-nN?C_2LM$ zjT=gzea@LTLymt|ll_Vm)O3B63kC=dy8|(xYYx`i2vt1FjwV>I&H2Hy?V=O`(3;(* zgIV1Y`pcR)fdzC@XJdzQ*=T_1Qv26AI>R`(E%wgFp6p0@Pd##yEfDSZOoq!(K*a&Q zTiq(1RRX1dQ{AP&XWT!dNO4?6`L3ZDh}5J*gFY{9UPMR>TkikrD7KM@B>bb^qLMo;BI38o;-d13RdkNGKQ3lb5w=RvBntcBP?=-u{CC5zEzwpMV%QQP?&-&)=C|Ew zdCw*OZ{KdEMo%OM@H!z?BpMWdetmptH6F-G-we)yw0sXVdH|OhkuFKwbv*_`^1Vmljg}MtHn+j#dB6HB6nDM2{ki@=0 zHj=(0WJ@6cyC;9tETauFMf@4vk}E5VwT`N1MQUw1XK-kb$#Rk+Ts}e;bU^9BB87;< zhCz75cQ4zvH*5X68gk=z1oLdV*o0VZ&hNPKami%#4KV4ze(9>d60b^YJjhT2?YUL1 z7d_zisPr0@o}s`fjGiWLq>dH(QN-*-{9oRR9#rb6AWf>6>ZGUv%D%^rR6T9VhNA_G z!PQ3QVxO?e(GJhC^KCnhZ?xODy6jHH5vra4vv$0xB?Wotuo1tzZa}oyaBa5%3Nl@6 z$khDFis(iu2&roN<~rtJ487%dl2t*ERMScR3xlpURr!u9%tYDax3LS{@9)G5r`0LW zW@sG`uZI#s9F-6b4yzLJC_XVG9Fvuj6_Q?vpdTJkV!F_JcS+$BoD zKk_-kVZJE|w7v;+=b4|8ETD2?9u!UxA{#u6wyCRO*t;DQ@N;0Tp0mbuD;agK7LNG~ zf`lf0(0y_NC)r(hw&{Tj6PEl&1bfp3OOgTcQ3+Nr1&z6&0Xuveail^DJ+lsO8_~WQ zCmgPu%Z9xtVqJ~iY`p#rzlsCzL`lFG8TYv#($Qt?>ne~EAyNf1b-gnin6_D==IsX~ z&e$C(&w<1y_&+;7X3$Z>T0t8JOdIy`Oryr@J)m;CM2ox`aNaT&W5gt0wxPqq~MOn(MK;_Ab};M8xRX0y8!h|)vIXKv)EQ^y@O8SeWnW6mcUo-aO)VGDgvaj}#ifT0An=LTw=O>H;s_}Ub?|9mw{icv zaFnOMBb`?2Dru<xKNmlCAbqF43STWWL@N`;#&4@m_sMip>fWoahXRUA_)8rs2 zHoU#u5fcBY5^2j$O-e!l+76{y4ouVVp$GoUxphsEEr0D?|M3Qk!wT04hp~jdiqVRW zABb#*tpETMQcOiJgLzKA{$>*RI77q-*Jw0DrjnCsTZan8s|VcdWIv)~17 z-h4vnzNo^lfPVfoLYoO}=()v=3jlzcK@R}4X`DU%GPpByhfY5tNP-><9p1-EkB+`d zjb!Wy4KU}5a=xIyL5zq>zg`w^tzafA7;dno@v2)e#hKr!n9l03v$1P=UsV>Fab|rj zDw#4eWGwaW79Y0e$Ut?G}Pq&!L5TbUlz;D#zzzfSxp%f{ zj$6o}AiN9-zSGGC)(=70VxHyyZU3E^CFUFTU{QsYag`5HV`dv|<9@pl7pl)Vl#Tl> zUiIl-^75MD0r7Ek-n8#~g&Ns&^Rm?37OQv>-oT`YnPaXj*Wglvk63hlmDg6vd+8Iw z@9wQ;VCO@{V?YFf@|};opcv z$ET6^B<*f zEBmv;nyj^@U%sENR(j!)7I>cWCC*}7(>%)BhWLPPG(la)haPq9l10dIF#k+D>dCX_ z(0JPHp-c|8MGEDS6%Q?E!9;!6d%x!|i=!-eDDhvJ>%v`dZ<2qvVq+i(848KIxj(!M z8F3pYJPwPzBdouKW{~PDzcNwDL_bMm0Pdj#H2UeTH#LkwEXj+feKVa&+-(uKujS`?_8KU8THy+N!);!7lI-Jv zIudU6Q4A2~O_lAbwGs~o=mi1z)wlE^+IfY1JGY{y{-J2RTVjz zG+sjG%*^MA3?AwGRQJ)}%Tqj#niiwst%VQG(8PiYPJ*TR@OBVxVv3>!N0M@p9PMAU z6fVca)@2LPqJ4>G?zQr7xXUf_6=o$>?ZN7EN=msI!niGBJ)!FrTZiEfR_Hy`RH*l& zIAT9oF7sSeHi-4#yw;#>#(D1}etLF-VPeZgpuY+>`6xLP>yANcq#B9@+9?{3)pUx= zy4o^aO4on^@lZGFa93wUQRnPU(LasoQ{B7J3Nxol6%AWX{kEtXgx`LN;4*~SS}I4flsCPHkY40OZ{#RUgQXScrcjI?>1-4 z&i+KtX+)V)b8{MEKm#nlf5@8&@(qWF@l_f8oaSnBiz*E{et4toELI!vHHDgWjW>7$}@l`XU*)!W_iF!J%$g%rL~EYT>lr&Ir}oEqhXYhDcpG!i*lv~!d6Y~R#} zVb9i^60R{?tmW)>V38GbmGSe-!NNb;DA_y}UvS~s($l(lMwiT&dhyn;ubQl9WS`|V zloKUMp8vSftfs~}dgV)&&A!X%wm@9JKRF2%$Oshyjv z&7L@&Z-PPZ%!?Nm0Zgll6NE9vA=sUrnEp#5*_nL|hSqyy8IMNZTpLkXuq# z@H0pT(FoB<0E)39nGBj^DbPtumg*UEQ>5{v&5Q1fE9!~dstzS`^Kj0YKAaT)0#Sfq zp?b!{sljIjL`U7VBn0msqDz!=oR{U!@yez%%gzN3Vh`a|y)%qZM(9)^E@)S_&|UPa z*uA1wc9TQ)@3EnX@9ASXeXL2+$IGpsEUm4`X!>Y2jA%54I+QsAUO(h8Kx~G&9`R{N zz1h+&tqqlpn)S`H?C~<`+LyP>6vLp_9|o~HJhNsRiy4Y zE~PBhuQiWTmv?=MZf6PzCl-qv=*%0t#KtjKUi*kJfX?@wz|p=Arh>L)=nc<@K_Ob*o*h<5`By zMe-0nD3ytQr@8J8|1&RhT@K%HHy^W&mv*tc`lKi%DlGCEeMBPw6yb-s{t&CnhfUv) zt%GpmGpA+TN}rQyJ6)xqB{DI$6CN>%e^qMVe2He1_u~qVTq#(=V(>5j&6R@&sArAW z4Sy`}|M&LUM)-*|v{Fodv0*$ui`avbg*zNChjZ%9DW)lSy zxbmzeULr+;j1UC)kP^|7%r)LhnnvHhmqs^n z4y#|L)ZN#gRBNP@)7BkZ_4ypC)rbbvCpoxeGX^S&K8F%1O)E$2v1$*5rZRFj`gypu zCTUcpjBRXiBIM9-j)vT>d#!tA|M=uY7skB4$KKyYDaCix6=5(ldafc}LM|Ayp82kt z*@W7GW8N4YnaFb|G@3_dHnAL!zZdc0=j;1Ivq24Y@Vv0YSx2YS(JnT-h7~DIjsxHR zf}e843WkapI;>$i)x>`3Y@=!^+IG|GMyuyC3~eQcVaw~`Aa`V)1ODl-k;6J-2$JXb z92>jn-}p}(bu2MVpWLE(0)jbgIaGwwMK_WyJ%rnm|27{LTD38!zcgwrI4Q9Zm&CoX zs&(?*unR_k=LA)bEos)gfX8oyuxRw}aM#{i&1h`F8*kDQo+mbI zu?KhiU%WE4c!dFlEX15~V@a`+I>Vz_?|0`7U85=c1cE(z`oxCe|m3$|iHn}`=T_wX5!6!6F zM`x|I{oU2AuY1(qpMFMh)Mk!XaqhGIabZS*m3@xsco98bEJavb+i_@lD~dvDZ155i z2{d>mP-lCZvri`a+wga%bfSXC5+tb=d7)RG1W{oA>oTypunv|mpnmhQP!<{ND^R`A zY1OBVU3w36O@#?X=xBqo@wA=E&v9IcOtO}M|0JCCo&#J_daXb`Q+j(6OS z&%6$bbB)hp7UJZ_fxQk3qJDLS_>~YY42habPq1U~f5&{1(pEk{eU%#xls_JuG~>1M zzv~#huH$pU0C^1_UL#xwnc{pa&1>?T0|tcX;NKnlz0$L&A0SWm->ejwS;*`E-vD1^ Ll%*>rzXtw4H(8ek literal 0 HcmV?d00001 diff --git a/src/weRequest.js b/src/weRequest.js index ea317c7..51d7e16 100644 --- a/src/weRequest.js +++ b/src/weRequest.js @@ -11,17 +11,28 @@ var errorTitle = "操作失败"; var errorContent = function(res) {return res}; var reLoginLimit = 3; var errorCallback = null; +var reportCGI = false; +var mockJson = false; +var globalData = false; //global data var session = ''; var sessionIsFresh = false; // 正在登录中,其他请求轮询稍后,避免重复调用登录接口 var logining = false; +// 正在查询session有效期中,避免重复调用接口 +var isCheckingSession = false; function checkSession(callback, obj) { - if (!sessionIsFresh) { + if(isCheckingSession) { + setTimeout(function() { + checkSession(callback, obj) + }, 500); + } else if (!sessionIsFresh && session) { + isCheckingSession = true; obj.count ++; // 如果还没检验过session是否有效,则需要检验一次 + obj._checkSessionStartTime = new Date().getTime(); wx.checkSession({ success: function () { // 登录态有效,且在本生命周期内无须再检验了 @@ -32,7 +43,12 @@ function checkSession(callback, obj) { session = ''; }, complete: function () { + isCheckingSession = false; obj.count --; + obj._checkSessionEndTime = new Date().getTime(); + if(typeof reportCGI == "function") { + reportCGI('wx_checkSession', obj._checkSessionStartTime, obj._checkSessionEndTime, request); + } doLogin(callback, obj); } }) @@ -50,14 +66,21 @@ function doLogin(callback, obj) { // 正在登录中,请求轮询稍后,避免重复调用登录接口 setTimeout(function() { doLogin(callback, obj); - }, 300) + }, 500) } else { // 缓存中无session logining = true; obj.count ++; + // 记录调用wx.login前的时间戳 + obj._loginStartTime = new Date().getTime(); wx.login({ complete: function () { obj.count --; + // 记录wx.login返回数据后的时间戳,用于上报 + obj._loginEndTime = new Date().getTime(); + if(typeof reportCGI == "function") { + reportCGI('wx_login', obj._loginStartTime, obj._loginEndTime, request); + } typeof obj.complete == "function" && obj.count == 0 && obj.complete(); }, success: function (res) { @@ -77,6 +100,7 @@ function doLogin(callback, obj) { data: data, method: codeToSession.method, isLogin: true, + report: codeToSession.report || codeToSession.url, success: function (s) { session = s; sessionIsFresh = true; @@ -90,27 +114,20 @@ function doLogin(callback, obj) { obj.count --; typeof obj.complete == "function" && obj.count == 0 && obj.complete(); logining = false; - } + }, + fail: codeToSession.fail || null }); } else { - wx.showModal({ - title: '登录失败', - content: '请稍后重试', - showCancel: false - }) + fail(obj, res); console.error(res); - // 登录失败,解除锁,防止死循环 + // 登录失败,解除锁,防止死锁 logining = false; } }, fail: function (res) { - wx.showModal({ - title: '登录失败', - content: res.errMsg || '请稍后重试', - showCancel: false - }) + fail(obj, res); console.error(res); - // 登录失败,解除锁,防止死循环 + // 登录失败,解除锁,防止死锁 logining = false; } }) @@ -151,21 +168,47 @@ function request(obj) { obj.data = {}; } - if (obj.url != codeToSession.url) { + if (obj.url != codeToSession.url && session) { obj.data[sessionName] = session; } + // 如果有全局参数,则添加 + var gd = {}; + if(typeof globalData == "function") { + gd = globalData(); + } else if(typeof globalData == "object") { + gd = globalData; + } + obj.data = Object.assign({}, gd, obj.data); + obj.method = obj.method || 'GET'; // 如果请求的URL中不是http开头的,则自动添加配置中的前缀 var url = obj.url.startsWith('http') ? obj.url : (urlPerfix + obj.url); - // 如果请求不是GET,则在URL中自动加上登录态 + // 如果请求不是GET,则在URL中自动加上登录态和全局参数 if(obj.method != "GET") { - if(url.indexOf('?') >= 0) { - url += '&' + sessionName + '=' + session; - } else { - url += '?' + sessionName + '=' + session; + + if(session) { + if(url.indexOf('?') >= 0) { + url += '&' + sessionName + '=' + session; + } else { + url += '?' + sessionName + '=' + session; + } } + + // 如果有全局参数,则在URL中添加 + for(var i in gd) { + if(url.indexOf('?') >= 0) { + url += '&' + i + '=' + gd[i]; + } else { + url += '?' + i + '=' + gd[i]; + } + } + } + + // 如果有上报字段配置,则记录请求发出前的时间戳 + if(obj.report) { + obj._reportStartTime = new Date().getTime(); } wx.request({ @@ -176,6 +219,13 @@ function request(obj) { dataType: obj.dataType || 'json', success: function (res) { if (res.statusCode == 200) { + + // 如果有上报字段配置,则记录请求返回后的时间戳,并进行上报 + if(obj.report && typeof reportCGI == "function") { + obj._reportEndTime = new Date().getTime(); + reportCGI(obj.report, obj._reportStartTime, obj._reportEndTime, request); + } + if (obj.isLogin) { // 登录请求 var s = ""; @@ -222,12 +272,8 @@ function request(obj) { } }, fail: function (res) { - wx.showModal({ - title: "请求失败", - content: res.errMsg, - showCancel: false - }) fail(obj, res); + console.error(res); }, complete: function () { obj.count --; @@ -244,15 +290,57 @@ function uploadFile(obj) { } obj.formData[sessionName] = session; + // 如果有全局参数,则添加 + var gd = {}; + if(typeof globalData == "function") { + gd = globalData(); + } else if(typeof globalData == "object") { + gd = globalData; + } + obj.formData = Object.assign({}, gd, obj.formData); + obj.dataType = obj.dataType || 'json'; + // 如果请求的URL中不是http开头的,则自动添加配置中的前缀 + var url = obj.url.startsWith('http') ? obj.url : (urlPerfix + obj.url); + + // 在URL中自动加上登录态和全局参数 + if(session) { + if(url.indexOf('?') >= 0) { + url += '&' + sessionName + '=' + session; + } else { + url += '?' + sessionName + '=' + session; + } + } + + // 如果有全局参数,则在URL中添加 + for(var i in gd) { + if(url.indexOf('?') >= 0) { + url += '&' + i + '=' + gd[i]; + } else { + url += '?' + i + '=' + gd[i]; + } + } + + // 如果有上报字段配置,则记录请求发出前的时间戳 + if(obj.report) { + obj._reportStartTime = new Date().getTime(); + } + wx.uploadFile({ - url: urlPerfix + obj.url, + url: url, filePath: obj.filePath || '', name: obj.name || '', formData: obj.formData, success: function (res) { if (res.statusCode == 200 && res.errMsg == 'uploadFile:ok') { + + // 如果有上报字段配置,则记录请求返回后的时间戳,并进行上报 + if(obj.report && typeof reportCGI == "function") { + obj.endTime = new Date().getTime(); + reportCGI(obj.report, obj._reportStartTime, obj._reportEndTime, request); + } + if(obj.dataType == 'json') { try { res.data = JSON.parse(res.data); @@ -284,12 +372,8 @@ function uploadFile(obj) { } }, fail: function (res) { - wx.showModal({ - title: "请求失败", - content: res.errMsg, - showCancel: false - }) fail(obj, res); + console.error(res); }, complete: function () { obj.count --; @@ -321,8 +405,8 @@ function fail(obj, res) { } wx.showModal({ - title: title || "操作失败", - content: content || "服务器异常,请稍后重试", + title: title, + content: content || "网络或服务异常,请稍后重试", showCancel: false }) } @@ -355,13 +439,14 @@ function getCache(obj, callback) { } } +function login(callback) { + checkSession(callback, {}) +} + function init(params) { sessionName = params.sessionName || 'session'; loginTrigger = params.loginTrigger || function () { return false }; - codeToSession = Object.assign({}, { - method: 'GET', - codeName: 'code' - }, params.codeToSession); + codeToSession = params.codeToSession || {}; successTrigger = params.successTrigger || function () { return true }; urlPerfix = params.urlPerfix || ""; successData = params.successData || function (res) { return res }; @@ -370,6 +455,9 @@ function init(params) { reLoginLimit = params.reLoginLimit || 3; errorCallback = params.errorCallback || null; sessionIsFresh = params.doNotCheckSession || false; + reportCGI = params.reportCGI || false; + mockJson = params.mockJson || false; + globalData = params.globalData || false; try { session = wx.getStorageSync(sessionName) || ''; @@ -378,11 +466,16 @@ function init(params) { function requestWrapper(obj) { obj = preDo(obj); - getCache(obj, function() { - checkSession(function () { - request(obj); - }, obj)} - ) + if(mockJson && mockJson[obj.url]) { + // mock 模式 + mock(obj); + } else { + getCache(obj, function() { + checkSession(function () { + request(obj); + }, obj)} + ) + } } function uploadFileWrapper(obj) { @@ -397,9 +490,23 @@ function setSession(s) { sessionIsFresh = true; } +function mock(obj) { + var res = { + data: mockJson[obj.url] + }; + if (successTrigger(res.data) && typeof obj.success == "function") { + // 接口返回成功码 + obj.success(successData(res.data)); + } else { + // 接口返回失败码 + fail(obj, res); + } +} + module.exports = { init: init, request: requestWrapper, uploadFile: uploadFileWrapper, - setSession: setSession + setSession: setSession, + login: login }; \ No newline at end of file