Explorar o código

refactor(http): 优化HTTP请求管理机制

- 引入PendingRequest机制处理并发请求
- 添加序列号生成器防止请求冲突
- 实现请求等待队列管理多个Promise
- 更新API工具类使用新的日志记录方式
- 修改登录相关API编号匹配逻辑
- 重构请求取消和清理流程
- 优化存储本地HTTP处理器列表的方式
徐勤民 hai 3 días
pai
achega
feafe3a382
Modificáronse 1 ficheiros con 279 adicións e 101 borrados
  1. 279 101
      src/main/ets/http/HttpHelper.ets

+ 279 - 101
src/main/ets/http/HttpHelper.ets

@@ -7,6 +7,19 @@ import { HttpHelperX, HttpParamsForm, HttpParamsGet, HttpParamsPost, HttpParamsU
 import { BusinessError } from '@kit.BasicServicesKit';
 import { image } from '@kit.ImageKit';
 
+type AnyResult = object | string | number | boolean | null | undefined;
+
+interface Waiters {
+  resolve: (v: AnyResult) => void;
+  reject: (e: Error) => void;
+}
+
+interface PendingRequest {
+  http?: http.HttpRequest
+  waiters: Array<Waiters>
+  seq: number
+}
+
 
 export class HttpHelper {
   private static instance: HttpHelper | null = null
@@ -24,9 +37,10 @@ export class HttpHelper {
   private httpHandlerList = new HashMap<string, http.HttpRequest>();
   // 并发白名单,这个名单里面的api,重复请求不会取消
   private concurrentList = new ArrayList<string>();
+  private pendingMap = new HashMap<string, PendingRequest>();
+  private seqGen = 0;
 
   constructor() {
-    this.httpHandlerList = new HashMap<string, http.HttpRequest>();
     SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
     SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length)
     try {
@@ -50,6 +64,7 @@ export class HttpHelper {
     } catch (error) {
     }
   }
+
   public addConcurrents(apiNo: string[]) {
     for (let apiNoElement of apiNo) {
       try {
@@ -69,6 +84,7 @@ export class HttpHelper {
     } catch (error) {
     }
   }
+
   public removeConcurrents(apiNo: string[]) {
     for (let apiNoElement of apiNo) {
       try {
@@ -88,19 +104,20 @@ export class HttpHelper {
    * @returns
    */
   public postJson<T>(params: HttpParamsPost, apiNo?: string, showLog?: boolean): Promise<T> {
-
     return new Promise<T>((resolve, reject) => {
 
       let httpRequest = http.createHttp();
-      this.setHandler(apiNo ?? params.url, httpRequest)
-
+      const key = apiNo ?? params.url
+      const pending = this.setHandler(key, httpRequest, {
+        resolve: (v: AnyResult) => resolve(v as T),
+        reject: (e: Error) => reject(e)
+      })
 
       const header = HttpHelperX.getHeaders("application/json;charset=UTF-8", params.headers)
 
       if (showLog) {
         LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params))
       }
-
       httpRequest.request(HttpHelperX.getUrl(params.url, params.query), {
         method: http.RequestMethod.POST,
         connectTimeout: 60000,
@@ -108,66 +125,120 @@ export class HttpHelper {
         header: header,
         extraData: params.data,
         usingCache: false,
-      })
-        .then((data: http.HttpResponse) => {
-          if (showLog) {
-            LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
-            LogHelper.print(data)
-          }
+      }).then((data: http.HttpResponse) => {
+        const latest = this.pendingMap.get(key)
+        if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
+          LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
+          LogHelper.print(data)
+        }
 
-          if (data.responseCode === 200) {
-            resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T)
+        if (data.responseCode === 200) {
+          const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
+
+          if (!pending) {
+            resolve(result)
           } else {
-            const err: Error = new Error()
-            err.name = data.responseCode.toString()
-            err.message = '服务异常'
+            if (!latest || latest.seq !== pending.seq) {
+              return
+            }
+            const result1 = result as AnyResult
+            latest.waiters.forEach(w => w.resolve(result1))
+          }
+        } else {
+          const err: Error = new Error()
+          err.name = data.responseCode.toString()
+          err.message = '服务异常'
+          if (!pending) {
             reject(err)
+          } else {
+            if (!latest || latest.seq !== pending.seq) {
+              return
+            }
+            latest.waiters.forEach(w => w.reject(err))
           }
-        }).catch((err: Error) => {
-        LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+        }
+
+      }).catch((err: Error) => {
+
+        let e = err
         if (err.message === 'Failed writing received data to disk/application') {
-          const error: Error = new Error()
-          error.name = 'cancel'
-          error.message = err.message
-          reject(error)
+          e.name = 'cancel'
+          e.message = err.message
+        }
+
+        if (!pending) {
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          reject(e)
         } else {
-          reject(err)
+          const latest = this.pendingMap.get(key)
+          if (!latest || latest.seq !== pending.seq) {
+            return
+          }
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          latest.waiters.forEach(w => w.reject(e))
         }
       }).finally(() => {
-        try {
-          if (this.httpHandlerList.hasKey(apiNo ?? params.url)) {
-            this.httpHandlerList.remove(apiNo ?? params.url)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
-              this.httpHandlerList.length)
+
+        if (pending) {
+          const latest = this.pendingMap.get(key)
+          if (latest && latest.seq === pending.seq) {
+            this.pendingMap.remove(key)
+            this.httpHandlerList.remove(key)
           }
-        } catch (error) {
+        } else {
+          this.httpHandlerList.remove(key)
         }
-      });
-    });
 
+        SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
+        SZYXLocalStorageHelper.storage.setOrCreate(
+          SZYXLocalStorageKeys.HttpHandlerListLength,
+          this.httpHandlerList.length
+        )
+      })
+    })
   }
 
-  setHandler(key: string, httpRequest: http.HttpRequest) {
-    try {
-      if (this.concurrentList.getIndexOf(key) === -1 &&
-      this.httpHandlerList.hasKey(key)) {
-        this.httpHandlerList.get(key).destroy()
-        this.httpHandlerList.remove(key)
-      }
-    } catch (error) {
+  setHandler(key: string, httpRequest: http.HttpRequest, waiters: Waiters): PendingRequest | null {
+    // 白名单:完全绕过
+    if (this.concurrentList.getIndexOf(key) !== -1) {
+      return null
     }
-    try {
-      if (this.concurrentList.getIndexOf(key) === -1) {
-        this.httpHandlerList.set(key, httpRequest)
+    if (this.seqGen > 99999) {
+      this.seqGen = 0
+    }
+    const seq = ++this.seqGen
+    let pending = this.pendingMap.get(key)
+
+    if (!pending) {
+      pending = {
+        waiters: [waiters],
+        seq,
+        http: httpRequest
       }
-    } catch (error) {
+      this.pendingMap.set(key, pending)
+    } else {
+      // cancel 旧请求
+      const h = pending.http
+      pending = {
+        waiters: [...pending.waiters, waiters],
+        seq,
+        http: httpRequest
+      }
+      this.pendingMap.set(key, pending)
+      h?.destroy()
     }
+
+    this.httpHandlerList.set(key, httpRequest)
+
     SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
-    SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
-      this.httpHandlerList.length)
+    SZYXLocalStorageHelper.storage.setOrCreate(
+      SZYXLocalStorageKeys.HttpHandlerListLength,
+      this.httpHandlerList.length
+    )
+    return pending
   }
 
+
   /**
    * postForm请求
    * @param url url地址
@@ -180,7 +251,11 @@ export class HttpHelper {
     return new Promise<T>((resolve, reject) => {
 
       let httpRequest = http.createHttp();
-      this.setHandler(apiNo ?? params.url, httpRequest)
+      const key = apiNo ?? params.url
+      const pending = this.setHandler(key, httpRequest, {
+        resolve: (v: AnyResult) => resolve(v as T),
+        reject: (e: Error) => reject(e)
+      })
 
       const header = HttpHelperX.getHeaders("application/x-www-form-urlencoded;charset=UTF-8", params.headers)
       let data = HttpHelperX.getContent(params.data)
@@ -198,39 +273,73 @@ export class HttpHelper {
         extraData: data ? encodeURI(data) : undefined
       })
         .then((data: http.HttpResponse) => {
-          if (showLog) {
+          const latest = this.pendingMap.get(key)
+          if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
             LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
             LogHelper.print(data)
           }
 
           if (data.responseCode === 200) {
-            resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T)
+            const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
+
+            if (!pending) {
+              resolve(result)
+            } else {
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              const result1 = result as AnyResult
+              latest.waiters.forEach(w => w.resolve(result1))
+            }
           } else {
             const err: Error = new Error()
             err.name = data.responseCode.toString()
             err.message = '服务异常'
-            reject(err)
+            if (!pending) {
+              reject(err)
+            } else {
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              latest.waiters.forEach(w => w.reject(err))
+            }
           }
         }).catch((err: Error) => {
-        LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+
+        let e = err
         if (err.message === 'Failed writing received data to disk/application') {
-          const error: Error = new Error()
-          error.name = 'cancel'
-          error.message = err.message
-          reject(error)
+          e.name = 'cancel'
+          e.message = err.message
+        }
+
+        if (!pending) {
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          reject(e)
         } else {
-          reject(err)
+          const latest = this.pendingMap.get(key)
+          if (!latest || latest.seq !== pending.seq) {
+            return
+          }
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          latest.waiters.forEach(w => w.reject(e))
         }
       }).finally(() => {
-        try {
-          if (this.httpHandlerList.hasKey(apiNo ?? params.url)) {
-            this.httpHandlerList.remove(apiNo ?? params.url)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
-              this.httpHandlerList.length)
+
+        if (pending) {
+          const latest = this.pendingMap.get(key)
+          if (latest && latest.seq === pending.seq) {
+            this.pendingMap.remove(key)
+            this.httpHandlerList.remove(key)
           }
-        } catch (error) {
+        } else {
+          this.httpHandlerList.remove(key)
         }
+
+        SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
+        SZYXLocalStorageHelper.storage.setOrCreate(
+          SZYXLocalStorageKeys.HttpHandlerListLength,
+          this.httpHandlerList.length
+        )
       });
     });
 
@@ -250,7 +359,11 @@ export class HttpHelper {
     return new Promise<T>((resolve, reject) => {
 
       let httpRequest = http.createHttp();
-      this.setHandler(apiNo ?? params.url, httpRequest)
+      const key = apiNo ?? params.url
+      const pending = this.setHandler(key, httpRequest, {
+        resolve: (v: AnyResult) => resolve(v as T),
+        reject: (e: Error) => reject(e)
+      })
 
       if (showLog) {
         LogHelper.debug(`GET:${apiNo}\n`, HttpHelperX.getUrl(params.url, params.query) + '\n',
@@ -266,44 +379,73 @@ export class HttpHelper {
         // extraData: params.data
       })
         .then((data: http.HttpResponse) => {
-          if (showLog) {
-            LogHelper.debug(`${apiNo}:\n${data.result as string}`)
+          const latest = this.pendingMap.get(key)
+          if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
+            LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
             LogHelper.print(data)
           }
 
           if (data.responseCode === 200) {
+            const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
 
-            if (typeof data.result === 'string') {
-              resolve(JSON.parse(data.result) as T)
+            if (!pending) {
+              resolve(result)
             } else {
-              resolve(data.result as T)
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              const result1 = result as AnyResult
+              latest.waiters.forEach(w => w.resolve(result1))
             }
           } else {
             const err: Error = new Error()
             err.name = data.responseCode.toString()
             err.message = '服务异常'
-            reject(err)
+            if (!pending) {
+              reject(err)
+            } else {
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              latest.waiters.forEach(w => w.reject(err))
+            }
           }
         }).catch((err: Error) => {
-        LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+
+        let e = err
         if (err.message === 'Failed writing received data to disk/application') {
-          const error: Error = new Error()
-          error.name = 'cancel'
-          error.message = err.message
-          reject(error)
+          e.name = 'cancel'
+          e.message = err.message
+        }
+
+        if (!pending) {
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          reject(e)
         } else {
-          reject(err)
+          const latest = this.pendingMap.get(key)
+          if (!latest || latest.seq !== pending.seq) {
+            return
+          }
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          latest.waiters.forEach(w => w.reject(e))
         }
       }).finally(() => {
-        try {
-          if (this.httpHandlerList.hasKey(apiNo ?? params.url)) {
-            this.httpHandlerList.remove(apiNo ?? params.url)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
-              this.httpHandlerList.length)
+
+        if (pending) {
+          const latest = this.pendingMap.get(key)
+          if (latest && latest.seq === pending.seq) {
+            this.pendingMap.remove(key)
+            this.httpHandlerList.remove(key)
           }
-        } catch (error) {
+        } else {
+          this.httpHandlerList.remove(key)
         }
+
+        SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
+        SZYXLocalStorageHelper.storage.setOrCreate(
+          SZYXLocalStorageKeys.HttpHandlerListLength,
+          this.httpHandlerList.length
+        )
       });
     });
 
@@ -321,7 +463,11 @@ export class HttpHelper {
     return new Promise<T>((resolve, reject) => {
 
       let httpRequest = http.createHttp();
-      this.setHandler(apiNo ?? params.url, httpRequest)
+      const key = apiNo ?? params.url
+      const pending = this.setHandler(key, httpRequest, {
+        resolve: (v: AnyResult) => resolve(v as T),
+        reject: (e: Error) => reject(e)
+      })
 
       if (showLog) {
         LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params))
@@ -341,40 +487,72 @@ export class HttpHelper {
         multiFormDataList: params.data,
       })
         .then((data: http.HttpResponse) => {
-          if (showLog) {
+          const latest = this.pendingMap.get(key)
+          if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
             LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
             LogHelper.print(data)
           }
           if (data.responseCode === 200) {
-            resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T)
+            const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
+
+            if (!pending) {
+              resolve(result)
+            } else {
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              const result1 = result as AnyResult
+              latest.waiters.forEach(w => w.resolve(result1))
+            }
           } else {
             const err: Error = new Error()
             err.name = data.responseCode.toString()
             err.message = '服务异常'
-            reject(err)
+            if (!pending) {
+              reject(err)
+            } else {
+              if (!latest || latest.seq !== pending.seq) {
+                return
+              }
+              latest.waiters.forEach(w => w.reject(err))
+            }
           }
         }).catch((err: Error) => {
 
-        LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+        let e = err
         if (err.message === 'Failed writing received data to disk/application') {
-          const error: Error = new Error()
-          error.name = 'cancel'
-          error.message = err.message
-          reject(error)
+          e.name = 'cancel'
+          e.message = err.message
+        }
+
+        if (!pending) {
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          reject(e)
         } else {
-          reject(err)
+          const latest = this.pendingMap.get(key)
+          if (!latest || latest.seq !== pending.seq) {
+            return
+          }
+          LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
+          latest.waiters.forEach(w => w.reject(e))
         }
       }).finally(() => {
-        httpRequest.off("dataSendProgress");
-        try {
-          if (this.httpHandlerList.hasKey(apiNo ?? params.url)) {
-            this.httpHandlerList.remove(apiNo ?? params.url)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
-            SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
-              this.httpHandlerList.length)
+
+        if (pending) {
+          const latest = this.pendingMap.get(key)
+          if (latest && latest.seq === pending.seq) {
+            this.pendingMap.remove(key)
+            this.httpHandlerList.remove(key)
           }
-        } catch (error) {
+        } else {
+          this.httpHandlerList.remove(key)
         }
+
+        SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
+        SZYXLocalStorageHelper.storage.setOrCreate(
+          SZYXLocalStorageKeys.HttpHandlerListLength,
+          this.httpHandlerList.length
+        )
       });
     });