Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # entry/src/main/ets/mock/Judge.ets # entry/src/main/ets/pages/Judge/BaseJudgeBussines.ets
This commit is contained in:
		
						commit
						8a3bc5296b
					
				| @ -4,13 +4,13 @@ | |||||||
|       { |       { | ||||||
|         "name": "default", |         "name": "default", | ||||||
|         "material": { |         "material": { | ||||||
|           "certpath": "C:/Users/93218/.ohos/config/openharmony/default_harmony_vehicle_terminal_j6U20r4hUBYNJsocRUYxzV96xIPQLMTqn59Ua3h6tI4=.cer", |           "certpath": "/Users/wangzhongjie/.ohos/config/openharmony/default_car_next_xIuD6UMCLxZgyeiH-w2XdDck6DewIfdHAvOk_FUbNZo=.cer", | ||||||
|           "storePassword": "0000001A3FBAE9120A580DE1C9D5F8AB398DD7175A8B9FABFBA723C6EB0B2BB78768AAB20CD625EDB6A8", |           "storePassword": "0000001BD807731D6FB9F044E3DF0E0429F4BB4214FE1608E25B4197041FBF36DDA3C6760585312F2864B6", | ||||||
|           "keyAlias": "debugKey", |           "keyAlias": "debugKey", | ||||||
|           "keyPassword": "0000001AC5B516340EAB600B1F9452419BB8A854BB2BBCC2E47FCA97B67D93556BBF50F322FA80CC4A21", |           "keyPassword": "0000001BFA38BF5235541496B5FF9ED9FFA764AB53FABC873D21DC230060209C2FF24ACE5DA09413B0C138", | ||||||
|           "profile": "C:/Users/93218/.ohos/config/openharmony/default_harmony_vehicle_terminal_j6U20r4hUBYNJsocRUYxzV96xIPQLMTqn59Ua3h6tI4=.p7b", |           "profile": "/Users/wangzhongjie/.ohos/config/openharmony/default_car_next_xIuD6UMCLxZgyeiH-w2XdDck6DewIfdHAvOk_FUbNZo=.p7b", | ||||||
|           "signAlg": "SHA256withECDSA", |           "signAlg": "SHA256withECDSA", | ||||||
|           "storeFile": "C:/Users/93218/.ohos/config/openharmony/default_harmony_vehicle_terminal_j6U20r4hUBYNJsocRUYxzV96xIPQLMTqn59Ua3h6tI4=.p12" |           "storeFile": "/Users/wangzhongjie/.ohos/config/openharmony/default_car_next_xIuD6UMCLxZgyeiH-w2XdDck6DewIfdHAvOk_FUbNZo=.p12" | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     ], |     ], | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| { | { | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 2, | ||||||
|   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", |   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", | ||||||
|   "specifiers": {}, |   "specifiers": {}, | ||||||
|   "packages": {} |   "packages": {} | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import { GlobalConfigType } from '../model'; | |||||||
|  */ |  */ | ||||||
| export const GlobalConfig: GlobalConfigType = { | export const GlobalConfig: GlobalConfigType = { | ||||||
|   commonFileWriteAddress: '/mnt/hmdfs/100/account/device_view/local/files/duolun', |   commonFileWriteAddress: '/mnt/hmdfs/100/account/device_view/local/files/duolun', | ||||||
|  |   modelPath: "/mnt/hmdfs/100/account/device_view/local/files/duolun/models/model_enc", | ||||||
|   picSavePath: '/storage/cloud/100/files/Photo/', |   picSavePath: '/storage/cloud/100/files/Photo/', | ||||||
|   videoSavePath: '/storage/cloud/100/files/Videos/', |   videoSavePath: '/storage/cloud/100/files/Videos/', | ||||||
|   host: 'http://192.168.32.105:8089', |   host: 'http://192.168.32.105:8089', | ||||||
|  | |||||||
| @ -265,7 +265,8 @@ export const DefaultJudgeConfigData: DefaultJudgeConfigObj = { | |||||||
|   param_387: '0', |   param_387: '0', | ||||||
|   //监管模式有扣分续考(0-否++1-是+把上次未考完的扣分带下来重新考试) |   //监管模式有扣分续考(0-否++1-是+把上次未考完的扣分带下来重新考试) | ||||||
|   param_432: '1', |   param_432: '1', | ||||||
|   param_634: "1" |   // Todo | ||||||
|  |   param_634: '1' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //所有的科二 科目三项目 | //所有的科二 科目三项目 | ||||||
|  | |||||||
| @ -184,6 +184,8 @@ export interface GlobalConfigType { | |||||||
|   version: VersionType |   version: VersionType | ||||||
|   //   几代机 |   //   几代机 | ||||||
|   modelNo?: string |   modelNo?: string | ||||||
|  |   //   模型位置 | ||||||
|  |   modelPath?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface VersionType { | interface VersionType { | ||||||
|  | |||||||
| @ -13,10 +13,10 @@ import { | |||||||
|   ProjectInfo, |   ProjectInfo, | ||||||
|   ProjectInfos, |   ProjectInfos, | ||||||
|   SYSTEM_PARAM, |   SYSTEM_PARAM, | ||||||
|   User, |   User | ||||||
| } from '../../model' | } from '../../model' | ||||||
| import JudgeBusiness from './JudgeBusiness' | import JudgeBusiness from './JudgeBusiness' | ||||||
| import { JudgePage } from "../Judge" | import { JudgePage } from '../Judge' | ||||||
| import VoiceAnnounce from '../judgeSDK/utils/voiceAnnouncements' | import VoiceAnnounce from '../judgeSDK/utils/voiceAnnouncements' | ||||||
| import { dConsole } from '../../utils/LogWorker' | import { dConsole } from '../../utils/LogWorker' | ||||||
| import { JudgeConfig, JudgeTag } from '../../config' | import { JudgeConfig, JudgeTag } from '../../config' | ||||||
| @ -338,17 +338,17 @@ export class BaseJudge { | |||||||
|     const param302 = judgeUI.judgeConfigObj.param_302; |     const param302 = judgeUI.judgeConfigObj.param_302; | ||||||
|     //自动退出待验证并且不合格 |     //自动退出待验证并且不合格 | ||||||
|     if (!that.isManual && judgeUI.examSubject === "3" && (param302 === "1" || (judgeUI.singlePlay && param302 === "2")) && judgeUI.totalScore < judgeUI.passingScore) { |     if (!that.isManual && judgeUI.examSubject === "3" && (param302 === "1" || (judgeUI.singlePlay && param302 === "2")) && judgeUI.totalScore < judgeUI.passingScore) { | ||||||
|       that.avPlayer.playAudio([`voice/考试结束.mp3`]) |       that.avPlayer?.playAudio([`voice/考试结束.mp3`]) | ||||||
|     } |     } | ||||||
|     //联网模式下:项目没有做完、当前分数大于及格分;手动结束直接退出 |     //联网模式下:项目没有做完、当前分数大于及格分;手动结束直接退出 | ||||||
|     if (!judgeUI.singlePlay && that.isManual && !judgeUI.isAllProjectsEnd && judgeUI.totalScore >= judgeUI.passingScore) { |     if (!judgeUI.singlePlay && that.isManual && !judgeUI.isAllProjectsEnd && judgeUI.totalScore >= judgeUI.passingScore) { | ||||||
|       that.avPlayer.playAudio(['voice/empty.mp3'], true, () => { |       that.avPlayer?.playAudio(['voice/empty.mp3'], true, () => { | ||||||
|         router.back(); |         router.back(); | ||||||
|       }) |       }) | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     // |     // | ||||||
|     that.avPlayer.playAudio(['voice/exam_waiting.mp3'], judgeUI.singlePlay, async () => { |     that.avPlayer?.playAudio(['voice/exam_waiting.mp3'], judgeUI.singlePlay, async () => { | ||||||
|       try { |       try { | ||||||
|         if (!judgeUI.singlePlay) { |         if (!judgeUI.singlePlay) { | ||||||
|           // const bytes = await this.getMessageHeartbeat(true); |           // const bytes = await this.getMessageHeartbeat(true); | ||||||
| @ -472,21 +472,21 @@ export class BaseJudge { | |||||||
|     //结束考试时候是否播报一遍所有扣分 |     //结束考试时候是否播报一遍所有扣分 | ||||||
|     const param634 = judgeUI.judgeConfigObj.param_634; |     const param634 = judgeUI.judgeConfigObj.param_634; | ||||||
|     if (judgeUI.kfArr.length && ((judgeUI.examSubject === "2" && param634 === "1") || judgeUI.examSubject === "3")) { |     if (judgeUI.kfArr.length && ((judgeUI.examSubject === "2" && param634 === "1") || judgeUI.examSubject === "3")) { | ||||||
|       that.avPlayer.playAudio([`voice/kfdesc.mp3`], false, () => { |       that.avPlayer?.playAudio([`voice/kfdesc.mp3`], false, () => { | ||||||
|         try { |         try { | ||||||
|           judgeUI.kfArr.forEach((kf, index) => { |           judgeUI.kfArr.forEach((kf, index) => { | ||||||
|             score += Math.abs(Number(kf.score)); |             score += Math.abs(Number(kf.score)); | ||||||
|             //TODO 考试分数待替换 |             //TODO 考试分数待替换 | ||||||
|             if (score <= (judgeUI.examSubject === "3" ? 10 : 20)) { |             if (score <= (judgeUI.examSubject === "3" ? 10 : 20)) { | ||||||
|               if (judgeUI.kfArr.length - 1 === index) { |               if (judgeUI.kfArr.length - 1 === index) { | ||||||
|                 that.avPlayer.playAudio([`voice/${kf.markcatalog}.mp3`, voiceURL], false, () => { |                 that.avPlayer?.playAudio([`voice/${kf.markcatalog}.mp3`, voiceURL], false, () => { | ||||||
|                   router.back(); |                   router.back(); | ||||||
|                 }) |                 }) | ||||||
|                 throw new Error('End Loop') |                 throw new Error('End Loop') | ||||||
|               } |               } | ||||||
|               that.avPlayer.playAudio([`voice/${kf.markcatalog}.mp3`]) |               that.avPlayer?.playAudio([`voice/${kf.markcatalog}.mp3`]) | ||||||
|             } else { |             } else { | ||||||
|               that.avPlayer.playAudio([`voice/${kf.markcatalog}.mp3`, voiceURL], false, () => { |               that.avPlayer?.playAudio([`voice/${kf.markcatalog}.mp3`, voiceURL], false, () => { | ||||||
|                 router.back(); |                 router.back(); | ||||||
|               }) |               }) | ||||||
|               throw new Error('End Loop') |               throw new Error('End Loop') | ||||||
| @ -497,7 +497,7 @@ export class BaseJudge { | |||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|     } else { |     } else { | ||||||
|       that.avPlayer.playAudio([voiceURL], true, () => { |       that.avPlayer?.playAudio([voiceURL], true, () => { | ||||||
|         setTimeout(() => { |         setTimeout(() => { | ||||||
|           router.back(); |           router.back(); | ||||||
|         }, param302 === "8" ? 3000 : 0) |         }, param302 === "8" ? 3000 : 0) | ||||||
|  | |||||||
| @ -4,6 +4,37 @@ import { dConsole } from '../../utils/LogWorker'; | |||||||
| import http from '@ohos.net.http'; | import http from '@ohos.net.http'; | ||||||
| import Request from '../../utils/Request'; | import Request from '../../utils/Request'; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 定义批次进度更新时的回调函数类型 | ||||||
|  |  * @param totalTasks 本批次总任务数 | ||||||
|  |  * @param completedTasks 已完成任务数 (成功 + 失败) | ||||||
|  |  * @param successfulTasks 成功完成的任务数 | ||||||
|  |  * @param failedTasks 失败的任务数 | ||||||
|  |  * @param progressPercentage 当前进度百分比 (0-100) | ||||||
|  |  */ | ||||||
|  | export type OnBatchProgressUpdateCallback = ( | ||||||
|  |   totalTasks: number, | ||||||
|  |   completedTasks: number, | ||||||
|  |   successfulTasks: number, | ||||||
|  |   failedTasks: number, | ||||||
|  |   progressPercentage: number | ||||||
|  | ) => void; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 定义所有任务完成时的回调函数类型 | ||||||
|  |  * @param totalTasks 本批次总任务数 | ||||||
|  |  * @param completedTasks 已完成任务数 (成功 + 失败) | ||||||
|  |  * @param successfulTasks 成功完成的任务数 | ||||||
|  |  * @param failedTasks 失败的任务数 | ||||||
|  |  */ | ||||||
|  | export type OnAllTasksCompletedCallback = ( | ||||||
|  |   totalTasks: number, | ||||||
|  |   completedTasks: number, | ||||||
|  |   successfulTasks: number, | ||||||
|  |   failedTasks: number | ||||||
|  | ) => void; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| export class ProcessDataTaskPool { | export class ProcessDataTaskPool { | ||||||
|   private queue: RegulatoryInterfaceParams[] = []; |   private queue: RegulatoryInterfaceParams[] = []; | ||||||
|   private isProcessing: boolean = false; |   private isProcessing: boolean = false; | ||||||
| @ -13,10 +44,45 @@ export class ProcessDataTaskPool { | |||||||
|   private readonly minProcessingTime = 2000; |   private readonly minProcessingTime = 2000; | ||||||
|   /** 记录每个任务的开始处理时间 */ |   /** 记录每个任务的开始处理时间 */ | ||||||
|   private taskStartTime: number = 0; |   private taskStartTime: number = 0; | ||||||
|  |   /** 批次进度更新时的回调函数 */ | ||||||
|  |   private onBatchProgressUpdateCallback: OnBatchProgressUpdateCallback | null = null; | ||||||
|  |   /** 所有任务完成时的回调函数 (批次) */ | ||||||
|  |   private onAllTasksCompletedCallback: OnAllTasksCompletedCallback | null = null; | ||||||
|  |   // 用于跟踪批次任务完成情况 | ||||||
|  |   private totalTasksInCurrentBatch: number = 0; | ||||||
|  |   private completedTasksInCurrentBatch: number = 0; | ||||||
|  |   private successfulTasksInCurrentBatch: number = 0; | ||||||
|  |   private failedTasksInCurrentBatch: number = 0; | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * 设置批次进度更新时的回调函数 | ||||||
|  |    * @param callback 回调函数 | ||||||
|  |    */ | ||||||
|  |   public setOnBatchProgressUpdateCallback(callback: OnBatchProgressUpdateCallback): void { | ||||||
|  |     this.onBatchProgressUpdateCallback = callback; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 设置所有任务完成时的回调函数 | ||||||
|  |    * @param callback 回调函数 | ||||||
|  |    */ | ||||||
|  |   public setOnAllTasksCompletedCallback(callback: OnAllTasksCompletedCallback): void { | ||||||
|  |     this.onAllTasksCompletedCallback = callback; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 添加任务到队列。 | ||||||
|  |    * 注意:为了正确追踪批次,通常建议一次性添加一个批次的所有任务。 | ||||||
|  |    * 如果在处理过程中动态添加任务,会影响 `totalTasksInCurrentBatch` 的准确性, | ||||||
|  |    * 进而影响 `onAllTasksCompletedCallback` 的触发时机和进度计算。 | ||||||
|  |    * @param dataItem 任务数据 | ||||||
|  |    */ | ||||||
|   public addTask(dataItem: RegulatoryInterfaceParams): void { |   public addTask(dataItem: RegulatoryInterfaceParams): void { | ||||||
|     console.info(`[Queue] 新任务已添加: ${JSON.stringify(dataItem)},当前队列长度: ${this.queue.length + 1}`); |     console.info(`[Queue] 新任务已添加: ${JSON.stringify(dataItem)},当前队列长度: ${this.queue.length + 1}`); | ||||||
|     this.queue.push(dataItem); // 将任务添加到队尾 |     this.queue.push(dataItem); // 将任务添加到队尾 | ||||||
|  | 
 | ||||||
|  |     // 每次添加任务,增加当前批次的总任务数 | ||||||
|  |     this.totalTasksInCurrentBatch++; | ||||||
|     this.triggerProcessing(); // 尝试启动处理流程 |     this.triggerProcessing(); // 尝试启动处理流程 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -29,7 +95,6 @@ export class ProcessDataTaskPool { | |||||||
|       console.log('[Queue] 处理器正在运行中,新任务将在稍后被处理。'); |       console.log('[Queue] 处理器正在运行中,新任务将在稍后被处理。'); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     // [优化] 直接调用 async 函数,它会立即返回一个 Promise,不会阻塞当前执行流,效果与 Promise.resolve().then() 相同但更简洁。 |  | ||||||
|     this.processQueue(); |     this.processQueue(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -44,7 +109,6 @@ export class ProcessDataTaskPool { | |||||||
|       try { |       try { | ||||||
|         console.log(`[Queue] 开始处理任务: ${JSON.stringify(taskData)}`); |         console.log(`[Queue] 开始处理任务: ${JSON.stringify(taskData)}`); | ||||||
| 
 | 
 | ||||||
|         // 预先记录将要处理的数据 |  | ||||||
|         const obj: WuxiExamType = { |         const obj: WuxiExamType = { | ||||||
|           xtlb: taskData.xtlb, |           xtlb: taskData.xtlb, | ||||||
|           jkxlh: taskData.jkxlh, |           jkxlh: taskData.jkxlh, | ||||||
| @ -55,34 +119,43 @@ export class ProcessDataTaskPool { | |||||||
|         }; |         }; | ||||||
|         dConsole.writeProcessData(ProcessDataEnumType.WuxiExam, JSON.stringify(obj)); |         dConsole.writeProcessData(ProcessDataEnumType.WuxiExam, JSON.stringify(obj)); | ||||||
| 
 | 
 | ||||||
|         // 此方法若成功则正常返回,若永久失败则会抛出错误 |  | ||||||
|         await this.processSingleTaskWithRetries(taskData); |         await this.processSingleTaskWithRetries(taskData); | ||||||
| 
 | 
 | ||||||
|         // 任务成功,将其从队列中移除 |         // 任务成功 | ||||||
|         this.queue.shift(); |         this.queue.shift(); | ||||||
|         console.log(`[Queue] ✅ 任务处理成功,已从队列移除。剩余任务: ${this.queue.length}`); |         console.log(`[Queue] ✅ 任务处理成功,已从队列移除。剩余任务: ${this.queue.length}`); | ||||||
| 
 |         this.successfulTasksInCurrentBatch++; // 增加成功任务计数 | ||||||
|         // 计算任务实际耗时 |  | ||||||
|         const elapsedTime = Date.now() - this.taskStartTime; |  | ||||||
|         // 如果处理时间小于最小要求时间,则延迟剩余时间 |  | ||||||
|         if (elapsedTime < this.minProcessingTime) { |  | ||||||
|           const delayTime = this.minProcessingTime - elapsedTime; |  | ||||||
|           console.log(`[Queue] 任务处理耗时 ${elapsedTime}ms,需要延迟 ${delayTime}ms 以满足最小处理时间要求`); |  | ||||||
|           await this.delay(delayTime); |  | ||||||
|         } |  | ||||||
|       } catch (error) { |       } catch (error) { | ||||||
|         // **[健壮性改进]** 捕获到永久失败的错误。 |         // 任务永久失败 | ||||||
|         // 原有逻辑会清空整个队列,导致后续任务丢失。 |  | ||||||
|         // 优化后的逻辑是:仅移除当前失败的任务,并记录错误,然后继续处理队列中的下一个任务。 |  | ||||||
|         console.error(`[Queue] 🔥 任务永久失败: ${(error as Error).message}`); |         console.error(`[Queue] 🔥 任务永久失败: ${(error as Error).message}`); | ||||||
|         const failedTask = this.queue.shift(); // 移除失败的任务 |         const failedTask = this.queue.shift(); // 移除失败的任务 | ||||||
|         console.error(`[Queue] 失败的任务已被移除: ${JSON.stringify(failedTask)},将继续处理下一个任务。`); |         console.error(`[Queue] 失败的任务已被移除: ${JSON.stringify(failedTask)},将继续处理下一个任务。`); | ||||||
|  |         this.failedTasksInCurrentBatch++; // 增加失败任务计数 | ||||||
|  |       } finally { | ||||||
|  |         // 无论成功或失败,都增加已完成任务计数 | ||||||
|  |         this.completedTasksInCurrentBatch++; | ||||||
| 
 | 
 | ||||||
|         // 即使任务失败,也需要满足最小处理时间要求 |         // 每次任务完成(或失败),都更新进度 | ||||||
|  |         this.reportBatchProgress(); | ||||||
|  | 
 | ||||||
|  |         // 检查是否所有任务都已处理完 | ||||||
|  |         if (this.queue.length === 0 && this.completedTasksInCurrentBatch === this.totalTasksInCurrentBatch) { | ||||||
|  |           console.log('[Queue] 所有任务处理完毕!'); | ||||||
|  |           this.onAllTasksCompletedCallback?.( | ||||||
|  |             this.totalTasksInCurrentBatch, | ||||||
|  |             this.completedTasksInCurrentBatch, | ||||||
|  |             this.successfulTasksInCurrentBatch, | ||||||
|  |             this.failedTasksInCurrentBatch | ||||||
|  |           ); | ||||||
|  |           // 重置批次计数器,为下一批次做准备 | ||||||
|  |           this.resetBatchCounters(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 计算任务实际耗时,并根据需要延迟 | ||||||
|         const elapsedTime = Date.now() - this.taskStartTime; |         const elapsedTime = Date.now() - this.taskStartTime; | ||||||
|         if (elapsedTime < this.minProcessingTime) { |         if (elapsedTime < this.minProcessingTime) { | ||||||
|           const delayTime = this.minProcessingTime - elapsedTime; |           const delayTime = this.minProcessingTime - elapsedTime; | ||||||
|           console.log(`[Queue] 失败任务处理耗时 ${elapsedTime}ms,需要延迟 ${delayTime}ms 以满足最小处理时间要求`); |           console.log(`[Queue] 任务处理耗时 ${elapsedTime}ms,需要延迟 ${delayTime}ms 以满足最小处理时间要求`); | ||||||
|           await this.delay(delayTime); |           await this.delay(delayTime); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -92,6 +165,32 @@ export class ProcessDataTaskPool { | |||||||
|     console.log('[Queue] 处理器已停止。'); |     console.log('[Queue] 处理器已停止。'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * 报告批次进度 | ||||||
|  |    */ | ||||||
|  |   private reportBatchProgress(): void { | ||||||
|  |     if (this.onBatchProgressUpdateCallback && this.totalTasksInCurrentBatch > 0) { | ||||||
|  |       const progressPercentage = Math.floor((this.completedTasksInCurrentBatch / this.totalTasksInCurrentBatch) * 100); | ||||||
|  |       this.onBatchProgressUpdateCallback( | ||||||
|  |         this.totalTasksInCurrentBatch, | ||||||
|  |         this.completedTasksInCurrentBatch, | ||||||
|  |         this.successfulTasksInCurrentBatch, | ||||||
|  |         this.failedTasksInCurrentBatch, | ||||||
|  |         progressPercentage | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 重置批次任务计数器 | ||||||
|  |    */ | ||||||
|  |   private resetBatchCounters(): void { | ||||||
|  |     this.totalTasksInCurrentBatch = 0; | ||||||
|  |     this.completedTasksInCurrentBatch = 0; | ||||||
|  |     this.successfulTasksInCurrentBatch = 0; | ||||||
|  |     this.failedTasksInCurrentBatch = 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * 延迟指定时间 |    * 延迟指定时间 | ||||||
|    * @param ms 延迟的毫秒数 |    * @param ms 延迟的毫秒数 | ||||||
| @ -101,16 +200,12 @@ export class ProcessDataTaskPool { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async processSingleTaskWithRetries(taskData: RegulatoryInterfaceParams): Promise<void> { |   private async processSingleTaskWithRetries(taskData: RegulatoryInterfaceParams): Promise<void> { | ||||||
|     // 1 次初次尝试 + 5 次重试 |  | ||||||
|     for (let attempt = 0; attempt <= this.maxRetries; attempt++) { |     for (let attempt = 0; attempt <= this.maxRetries; attempt++) { | ||||||
|       const attemptNum = attempt + 1; |       const attemptNum = attempt + 1; | ||||||
|       try { |       try { | ||||||
|         const attemptType = attempt === 0 ? '初次尝试' : `重试 ${attempt}`; |         const attemptType = attempt === 0 ? '初次尝试' : `重试 ${attempt}`; | ||||||
|         console.log(`[Queue] 开始上传 (${attemptType}, 总共第 ${attemptNum} 次)`); |         console.log(`[Queue] 开始上传 (${attemptType}, 总共第 ${attemptNum} 次)`); | ||||||
| 
 | 
 | ||||||
|         // **注意**: 这里传递的是 taskData 的引用,为了防止 worker 中意外修改原始数据, |  | ||||||
|         // 最佳实践是在 worker 内部或传递前进行深拷贝。 |  | ||||||
|         // 但根据我们下面的修复,worker 不再修改原始数据,所以这里是安全的。 |  | ||||||
|         const result: WR = (await taskpool.execute(uploadWorkerTask, taskData)) as WR; |         const result: WR = (await taskpool.execute(uploadWorkerTask, taskData)) as WR; | ||||||
| 
 | 
 | ||||||
|         dConsole.writeProcessData(ProcessDataEnumType.WuxiExam, JSON.stringify(result)); |         dConsole.writeProcessData(ProcessDataEnumType.WuxiExam, JSON.stringify(result)); | ||||||
| @ -120,7 +215,6 @@ export class ProcessDataTaskPool { | |||||||
|         } |         } | ||||||
|         console.warn(`[Queue] ❌ 上传失败 (第 ${attemptNum} 次)。响应: ${result.message}`); |         console.warn(`[Queue] ❌ 上传失败 (第 ${attemptNum} 次)。响应: ${result.message}`); | ||||||
|       } catch (e) { |       } catch (e) { | ||||||
|         // **[健壮性改进]** 现在可以捕获从 Worker 抛出的更详细的异常信息 |  | ||||||
|         console.error(`[Queue] ❌ TaskPool 执行或网络错误 (第 ${attemptNum} 次): ${e}`); |         console.error(`[Queue] ❌ TaskPool 执行或网络错误 (第 ${attemptNum} 次): ${e}`); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -129,33 +223,22 @@ export class ProcessDataTaskPool { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 如果循环结束,意味着所有尝试都失败了 |  | ||||||
|     throw new Error(`任务 ${JSON.stringify(taskData)} 在 ${this.maxRetries + 1} 次尝试后永久失败。`); |     throw new Error(`任务 ${JSON.stringify(taskData)} 在 ${this.maxRetries + 1} 次尝试后永久失败。`); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const ProcessDataTaskPoolInstance = new ProcessDataTaskPool(); | export const ProcessDataTaskPoolInstance = new ProcessDataTaskPool(); | ||||||
| 
 | 
 | ||||||
| /** |  | ||||||
|  * 这是将在 Worker 线程中执行的任务函数。 |  | ||||||
|  * 它负责执行单次的上传尝试。 |  | ||||||
|  * @param data 需要上传的单条数据项 |  | ||||||
|  * @returns 一个包含本次上传尝试结果的对象 |  | ||||||
|  */ |  | ||||||
| export async function uploadWorkerTask(data: RegulatoryInterfaceParams): Promise<WR> { | export async function uploadWorkerTask(data: RegulatoryInterfaceParams): Promise<WR> { | ||||||
|   // 这两个变量似乎未在逻辑中使用,暂时保持原样 |  | ||||||
|   let singlePlay: boolean = false; |   let singlePlay: boolean = false; | ||||||
|   let isJGNew = false; |   let isJGNew = false; | ||||||
|   try { |   try { | ||||||
|     const response = await sendProcessData(data, singlePlay, isJGNew); |     const response = await sendProcessData(data, singlePlay, isJGNew); | ||||||
|     return response; |     return response; | ||||||
|   } catch (err) { |   } catch (err) { | ||||||
|     // [健壮性改进] 捕获请求过程中的异常,并重新抛出。 |  | ||||||
|     // 这能让主线程的 taskpool.execute() promise 变为 rejected 状态, |  | ||||||
|     // 从而被 processSingleTaskWithRetries 中的 try-catch 捕获,保留了详细的错误信息。 |  | ||||||
|     const error = err as Error; |     const error = err as Error; | ||||||
|     console.error(`[Worker] 上传时发生异常: ${error.message}`); |     console.error(`[Worker] 上传时发生异常: ${error.message}`); | ||||||
|     throw error; // 重新抛出异常 |     throw error; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -167,7 +250,9 @@ async function sendProcessData(data: RegulatoryInterfaceParams, singlePlay: bool | |||||||
|     //   调用新监管 |     //   调用新监管 | ||||||
|   } |   } | ||||||
|   data.drvexam = data.drvexam ?? {}; |   data.drvexam = data.drvexam ?? {}; | ||||||
|   data.drvexam.zp = data.drvexam.zp === undefined ? undefined : encodeURIComponent(data.drvexam.zp); |   if (data.drvexam.zp !== undefined && data.drvexam.zp !== null) { | ||||||
|  |     data.drvexam.zp = encodeURIComponent(data.drvexam.zp); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   const drvexamArr = Object.entries(data.drvexam) |   const drvexamArr = Object.entries(data.drvexam) | ||||||
|     .filter((item: [string, string]) => item[1] != undefined) |     .filter((item: [string, string]) => item[1] != undefined) | ||||||
| @ -176,7 +261,7 @@ async function sendProcessData(data: RegulatoryInterfaceParams, singlePlay: bool | |||||||
|   return await Request<object>({ |   return await Request<object>({ | ||||||
|     host: JGHOST, |     host: JGHOST, | ||||||
|     url: '/dems_ws/services/TmriOutAccess?wsdl', |     url: '/dems_ws/services/TmriOutAccess?wsdl', | ||||||
|     data: `<?xml version="1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" > <SOAP-ENV:Body> <writeObjectOut  xmlns="http://service.es.doron"> <xtlb>${data.xtlb}</xtlb> <jkxlh>${data.jkxlh}</jkxlh> <jkid>${data.jkid}</jkid> <UTF8XmlDoc> <![CDATA[ <?xm lversion="1.0 "encoding="GBK"?> <root> <drvexam> ${drvexamArr} </drvexam> </root> ]]> </UTF8XmlDoc> </writeObjectOut> </SOAP-ENV:Body> </SOAP-ENV:Envelope>`, |     data: `<?xml version="1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" > <SOAP-ENV:Body> <writeObjectOut  xmlns="http://service.es.doron"> <xtlb>${data.xtlb}</xtlb> <jkxlh>${data.jkxlh}</jkxlh> <jkid>${data.jkid}</jkid> <UTF8XmlDoc> <![CDATA[ <?xm lversion="1.0 "encoding="GBK"?> <root> <drvexam> ${drvexamArr.join('')} </drvexam> </root> ]]> </UTF8XmlDoc> </writeObjectOut> </SOAP-ENV:Body> </SOAP-ENV:Envelope>`, | ||||||
|     method: http.RequestMethod.POST, |     method: http.RequestMethod.POST, | ||||||
|     xml: true |     xml: true | ||||||
|   }) |   }) | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { GlobalConfig } from '../config'; | |||||||
| import { LogWorkerMessage, ProcessDataEnumType, WorkerMessageType } from '../model/index'; | import { LogWorkerMessage, ProcessDataEnumType, WorkerMessageType } from '../model/index'; | ||||||
| import dayTs from './Date'; | import dayTs from './Date'; | ||||||
| 
 | 
 | ||||||
| const MAX_MESSAGE_LENGTH = 300; | const MAX_MESSAGE_LENGTH = 400; | ||||||
| const LOG_CHUNK_PREFIX = '[切割消息序号'; | const LOG_CHUNK_PREFIX = '[切割消息序号'; | ||||||
| 
 | 
 | ||||||
| class logWorker { | class logWorker { | ||||||
| @ -37,6 +37,11 @@ class logWorker { | |||||||
|     this.logWithLevel('info', ...args); |     this.logWithLevel('info', ...args); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // 低优先级日志 | ||||||
|  |   low(...args: ESObject[]) { | ||||||
|  |     this.logWithLevel('low', ...args); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // 调试日志 |   // 调试日志 | ||||||
|   warn(msg: string) { |   warn(msg: string) { | ||||||
|     console.warn(msg) |     console.warn(msg) | ||||||
| @ -91,7 +96,7 @@ class logWorker { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // 通用日志方法 |   // 通用日志方法 | ||||||
|   private logWithLevel(level: 'log' | 'info' | 'error', ...args: ESObject[]): void { |   private logWithLevel(level: 'log' | 'info' | 'error' | 'low', ...args: ESObject[]): void { | ||||||
|     // console.log("当前环境查看", this.isLogEnabled, "日志级别:", level) |     // console.log("当前环境查看", this.isLogEnabled, "日志级别:", level) | ||||||
|     if (this.isLogEnabled === "1") { |     if (this.isLogEnabled === "1") { | ||||||
|       const message = this.formatMessage(...args); |       const message = this.formatMessage(...args); | ||||||
| @ -104,7 +109,7 @@ class logWorker { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // 日志输出到控制台 |   // 日志输出到控制台 | ||||||
|   private logConsole(level: 'log' | 'info' | 'error', msg: string) { |   private logConsole(level: 'log' | 'info' | 'error' | 'low', msg: string) { | ||||||
|     switch (level) { |     switch (level) { | ||||||
|       case 'log': |       case 'log': | ||||||
|         console.log(msg); |         console.log(msg); | ||||||
| @ -115,11 +120,14 @@ class logWorker { | |||||||
|       case 'error': |       case 'error': | ||||||
|         console.error(msg); |         console.error(msg); | ||||||
|         break; |         break; | ||||||
|  |       case 'low': | ||||||
|  |         console.log(msg); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // 处理长消息分割和记录 |   // 处理长消息分割和记录 | ||||||
|   private logLongMessage(level: 'log' | 'info' | 'error', message: string): void { |   private logLongMessage(level: 'log' | 'info' | 'error' | 'low', message: string): void { | ||||||
|     const chunks = this.splitLongMessage(message); |     const chunks = this.splitLongMessage(message); | ||||||
|     const chunkId = Math.random().toString(36).substring(2, 8); // 生成简短随机ID |     const chunkId = Math.random().toString(36).substring(2, 8); // 生成简短随机ID | ||||||
|     chunks.forEach((chunk, index) => { |     chunks.forEach((chunk, index) => { | ||||||
|  | |||||||
| @ -1,18 +1,20 @@ | |||||||
| { | { | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 2, | ||||||
|   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", |   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", | ||||||
|   "specifiers": { |   "specifiers": { | ||||||
|     "@ohos/crypto-js@2.0.3": "@ohos/crypto-js@2.0.3", |     "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19", | ||||||
|     "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" |     "@ohos/crypto-js@2.0.3": "@ohos/crypto-js@2.0.3" | ||||||
|   }, |   }, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "@ohos/crypto-js@2.0.3": { |  | ||||||
|       "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/crypto-js/-/crypto-js-2.0.3.har", |  | ||||||
|       "integrity": "sha512-LuHaR1kD5PxnOXnuR1fWvPwGtbed9Q/QGzk6JOh8y5Wdzvi8brPesODZiaWf9scOVRHsbTPOtZw91vWB35p1vQ==" |  | ||||||
|     }, |  | ||||||
|     "@ohos/hypium@1.0.19": { |     "@ohos/hypium@1.0.19": { | ||||||
|       "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", |       "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", | ||||||
|       "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==" |       "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", | ||||||
|  |       "registryType": "ohpm" | ||||||
|  |     }, | ||||||
|  |     "@ohos/crypto-js@2.0.3": { | ||||||
|  |       "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/crypto-js/-/crypto-js-2.0.3.har", | ||||||
|  |       "integrity": "sha512-LuHaR1kD5PxnOXnuR1fWvPwGtbed9Q/QGzk6JOh8y5Wdzvi8brPesODZiaWf9scOVRHsbTPOtZw91vWB35p1vQ==", | ||||||
|  |       "registryType": "ohpm" | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user