Http.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. class HTTP {
  2. constructor(baseUrl) {
  3. this.baseUrl = baseUrl;
  4. this.hangUp = [];
  5. this.intercept = false;
  6. this.logInAgain = null;
  7. this.requestTask = null;
  8. this._pendingRequests = new Map();
  9. // 默认请求配置
  10. this.defaultTimeout = 30000; // 30秒默认超时
  11. this.maxRetries = 2; // 最多重试次数
  12. this.updateList = (content, getList) => {
  13. content.copyContent = JSON.parse(JSON.stringify(content));
  14. content.pageSize = (content.pageNumber - 1) * (content.pageSize || 20);
  15. content.pageNumber = 1;
  16. getList()
  17. }
  18. this.formatNumber = (num, decimalPlaces = 2) => {
  19. if (!num && num !== 0) return '';
  20. let [integer, decimal] = String(num.toFixed(decimalPlaces)).split('.');
  21. integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  22. return decimal ? `${integer}.${decimal}` : integer;
  23. }
  24. this.getSpecifiedImage = (obj, getType = false) => {
  25. obj.url = this.getImageUrl(obj.url)
  26. try {
  27. let type = getType ? 'compressed' : 'thumbnail';
  28. let imgObj = obj.subfiles.find(v => v.type == type);
  29. return this.getImageUrl(imgObj.url || obj.url);
  30. } catch (error) {
  31. return this.getImageUrl(obj.url);
  32. }
  33. }
  34. this.getImageUrl = (url) => {
  35. if (!url) return '';
  36. if (!/^https?:\/\//.test(url)) {
  37. url = this.baseUrl + url
  38. }
  39. return url;
  40. }
  41. this.jsessionid = uni.getStorageSync('JSESSIONID') || '';
  42. console.log("接口地址", this.baseUrl);
  43. }
  44. // 生成请求唯一标识(用于防重复)
  45. _generateRequestKey(url, data, method) {
  46. return `${method}:${url}:${JSON.stringify(data)}`;
  47. }
  48. // 检查是否有相同请求正在执行
  49. _isRequestPending(key) {
  50. const pending = this._pendingRequests.get(key);
  51. if (pending && pending.task) {
  52. const isValid = pending.timestamp && (Date.now() - pending.timestamp < 5000);
  53. if (isValid) return true;
  54. this._pendingRequests.delete(key);
  55. }
  56. return false;
  57. }
  58. // 记录请求
  59. _addPendingRequest(key, task) {
  60. this._pendingRequests.set(key, { task, timestamp: Date.now() });
  61. // 5秒后自动清理
  62. setTimeout(() => this._pendingRequests.delete(key), 5000);
  63. }
  64. // 移除请求记录
  65. _removePendingRequest(key) {
  66. this._pendingRequests.delete(key);
  67. }
  68. request({
  69. url = "",
  70. data = {},
  71. method = "POST",
  72. header = {
  73. 'content-type': 'application/json',
  74. "accesstoken": data.accesstoken || uni.getStorageSync('userMsg')?.token || '',
  75. "Cookie": `JSESSIONID=${this.jsessionid}`
  76. },
  77. suffix = '/yos/rest',
  78. timeout = this.defaultTimeout,
  79. retry = this.maxRetries
  80. }) {
  81. // 分页预检:如果pageNumber大于pageTotal,直接返回空
  82. try {
  83. if (data.content && data.content.pageNumber && data.content.pageTotal) {
  84. if (data.content.pageNumber > data.content.pageTotal) {
  85. return Promise.resolve({ code: 0 });
  86. }
  87. }
  88. } catch (error) { }
  89. // 生成请求key,检查是否重复
  90. const requestKey = this._generateRequestKey(url, data, method);
  91. if (this._isRequestPending(requestKey)) {
  92. console.log('检测到重复请求,已忽略:', url);
  93. return Promise.resolve({ code: 0, message: 'duplicate_request' });
  94. }
  95. return new Promise((resolve, reject) => {
  96. let fullUrl = suffix.startsWith('http') ? suffix : (suffix == '/yos/rest' ? this.baseUrl : "") + suffix + url;
  97. if (fullUrl.includes('/index/loginbyaccount')) {
  98. fullUrl = fullUrl.replace('http://61.164.207.46:8900', 'http://61.164.207.46:8300');
  99. fullUrl = fullUrl.replace('https://meida.cnyunl.com:888', 'https://crm.meida.com:16691');
  100. }
  101. // 记录当前请求
  102. this._addPendingRequest(requestKey, true);
  103. this._requestWithRetry(fullUrl, resolve, reject, data, method, header, retry, requestKey, timeout);
  104. })
  105. }
  106. // 带重试的请求
  107. _requestWithRetry(url, resolve, reject, data, method, header, retriesLeft, requestKey, timeout) {
  108. const doRequest = () => {
  109. // 取消之前的请求(如果存在)
  110. if (this.requestTask) {
  111. this.requestTask.abort();
  112. }
  113. this._request(url, (result) => {
  114. this._removePendingRequest(requestKey);
  115. resolve(result);
  116. }, (err) => {
  117. if (retriesLeft > 0) {
  118. console.log(`请求失败,剩余重试次数: ${retriesLeft}`);
  119. // 网络错误时重试
  120. setTimeout(() => {
  121. this._requestWithRetry(url, resolve, reject, data, method, header, retriesLeft - 1, requestKey, timeout);
  122. }, 1000);
  123. } else {
  124. this._removePendingRequest(requestKey);
  125. reject(err);
  126. }
  127. }, data, method, header, null, timeout);
  128. };
  129. doRequest();
  130. }
  131. getRequest({
  132. url,
  133. method = "GET",
  134. header = {
  135. "Cookie": `JSESSIONID=${this.jsessionid}`
  136. },
  137. timeout = this.defaultTimeout
  138. }) {
  139. return new Promise((resolve, reject) => {
  140. this._request(url, resolve, reject, {}, method, header, null, timeout);
  141. })
  142. }
  143. // 取消所有请求
  144. cancelAllRequests() {
  145. if (this.requestTask) {
  146. this.requestTask.abort();
  147. this.requestTask = null;
  148. }
  149. this._pendingRequests.clear();
  150. }
  151. _request(url, resolve, reject, data, method, header, showLoading, timeout = this.defaultTimeout) {
  152. if (showLoading) uni.showLoading({
  153. title: showLoading,
  154. mask: true
  155. });
  156. this.requestTask = uni.request({
  157. url,
  158. data: data,
  159. method: method,
  160. header: header,
  161. timeout: timeout,
  162. success: (res) => {
  163. this._handleSessionCookies(res);
  164. try {
  165. if (res.data.pageNumber) {
  166. res.data.firstPage = res.data.pageNumber === 1;
  167. if (data.content && data.content.copyContent) {
  168. res.data.pageNumber = data.content.copyContent.pageNumber;
  169. res.data.pageTotal = data.content.copyContent.pageTotal;
  170. data.content.pageSize = data.content.copyContent.pageSize;
  171. delete data.content.copyContent
  172. }
  173. }
  174. } catch (error) { }
  175. resolve(res.data);
  176. },
  177. fail: (err) => {
  178. reject(err);
  179. },
  180. complete: (res) => {
  181. this.requestTask = null;
  182. if (showLoading) uni.hideLoading();
  183. if (res.errMsg && !res.errMsg.includes('request:ok') && !res.errMsg.includes('abort')) {
  184. uni.showToast({
  185. title: '网络异常,请稍后再试',
  186. icon: "none"
  187. });
  188. }
  189. // 会话过期处理 - 使用更安全的方式
  190. if (res.data && ['登录凭证已过期,请重新登录!', '身份令牌无效,请重新登陆!', '登陆状态已过期,请重新登陆!'].includes(res.data.msg)) {
  191. if (this.sessionExpiredNotified) return;
  192. this.sessionExpiredNotified = true;
  193. this.jsessionid = '';
  194. uni.removeStorageSync('JSESSIONID');
  195. // 使用 setTimeout 避免在页面生命周期中直接操作
  196. setTimeout(() => {
  197. const pages = getCurrentPages();
  198. const currentPage = pages.length > 0 ? pages[pages.length - 1] : null;
  199. const currentRoute = currentPage ? currentPage.route : '';
  200. if (currentRoute !== 'pages/login/login') {
  201. uni.showModal({
  202. title: '提示',
  203. content: '您的登录状态已过期,请重新登录。',
  204. showCancel: false,
  205. success: () => {
  206. this.sessionExpiredNotified = false;
  207. uni.reLaunch({
  208. url: '/pages/login/login',
  209. fail: () => uni.redirectTo({ url: '/pages/login/login' })
  210. });
  211. },
  212. fail: () => {
  213. this.sessionExpiredNotified = false;
  214. }
  215. });
  216. } else {
  217. this.sessionExpiredNotified = false;
  218. }
  219. }, 100);
  220. }
  221. }
  222. });
  223. }
  224. _handleSessionCookies(res) {
  225. try {
  226. const cookies = res.cookies || [];
  227. const headers = res.header || {};
  228. const setCookie = headers['Set-Cookie'] || headers['set-cookie'];
  229. if (setCookie) {
  230. const cookieArray = Array.isArray(setCookie) ? setCookie : [setCookie];
  231. cookies.push(...cookieArray);
  232. }
  233. for (const cookie of cookies) {
  234. const match = cookie.match(/JSESSIONID=([^;]+)/i);
  235. if (match && match[1]) {
  236. const newSessionId = match[1];
  237. if (newSessionId !== this.jsessionid) {
  238. this.jsessionid = newSessionId;
  239. uni.removeStorageSync('JSESSIONID');
  240. uni.setStorageSync('JSESSIONID', newSessionId);
  241. }
  242. break;
  243. }
  244. }
  245. } catch (error) {
  246. console.error('处理会话Cookie时出错:', error);
  247. }
  248. }
  249. }
  250. export { HTTP };