|
|
@@ -32,7 +32,13 @@ import {
|
|
|
EXAM_PAGE_OPTIONS,
|
|
|
EXAM_DATA
|
|
|
} from '@/types/injectionSymbols';
|
|
|
+import { useAppStore } from '@/store/appStore';
|
|
|
+import { useEnv } from '@/hooks/useEnv';
|
|
|
+import { useReport } from '@/hooks/useReport';
|
|
|
|
|
|
+const { report, events } = useReport();
|
|
|
+const appStore = useAppStore();
|
|
|
+const { platform } = useEnv();
|
|
|
const userStore = useUserStore();
|
|
|
// import { Examinee, ExamPaper, ExamPaperSubmit } from '@/types/study';
|
|
|
const { prevData, transferBack, transferTo } = useTransferPage<Transfer.ExamAnalysisPageOptions, {}>();
|
|
|
@@ -207,10 +213,14 @@ const handleSubmit = (tempSave: boolean = false) => {
|
|
|
duration: practiceDuration.value
|
|
|
};
|
|
|
console.log('提交试卷参数', params)
|
|
|
+ const start = Date.now();
|
|
|
await commitExamineePaper(params);
|
|
|
+ const costTime = Date.now() - start;
|
|
|
+ report(events.ExamStartSubmitSuccess, { time: costTime });
|
|
|
if (isSimulationExam.value || isTestExam.value) {
|
|
|
if (!tempSave) {
|
|
|
setTimeout(async () => {
|
|
|
+ report(events.ExamStartExit);
|
|
|
uni.$ie.hideLoading();
|
|
|
await nextTick();
|
|
|
confirmQuit.value = true;
|
|
|
@@ -234,6 +244,7 @@ const handleSubmit = (tempSave: boolean = false) => {
|
|
|
} else if (isPracticeExam.value) {
|
|
|
if (!tempSave) {
|
|
|
setTimeout(async () => {
|
|
|
+ report(events.ExamStartExit);
|
|
|
uni.$ie.hideLoading();
|
|
|
await nextTick();
|
|
|
confirmQuit.value = true;
|
|
|
@@ -250,6 +261,7 @@ const handleSubmit = (tempSave: boolean = false) => {
|
|
|
});
|
|
|
}, 2500);
|
|
|
} else {
|
|
|
+ report(events.ExamStartExit);
|
|
|
uni.$ie.hideLoading();
|
|
|
confirmQuit.value = true;
|
|
|
confirmShowing.value = false;
|
|
|
@@ -291,6 +303,135 @@ const restoreQuestion = (savedQuestion: Study.ExamineeQuestion[], fullQuestion:
|
|
|
}
|
|
|
return fullQuestion;
|
|
|
}
|
|
|
+/**
|
|
|
+ * 带超时检测的 getOpenExaminee 请求
|
|
|
+ * 在 3s, 5s, 10s, 15s, 20s 这 5 个时间点检测超时并上报
|
|
|
+ * 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ */
|
|
|
+const getOpenExamineeWithTimeoutCheck = async (params: Study.OpenExamineeRequestDTO) => {
|
|
|
+ // 超时时间档次(单位:毫秒)
|
|
|
+ const timeoutLevels = [3000, 5000, 10000, 15000, 20000];
|
|
|
+ // 存储所有定时器ID,用于清理
|
|
|
+ const timers: number[] = [];
|
|
|
+ // 请求是否已完成
|
|
|
+ let isCompleted = false;
|
|
|
+
|
|
|
+ // 创建超时检测定时器
|
|
|
+ timeoutLevels.forEach((timeout) => {
|
|
|
+ const timer = setTimeout(() => {
|
|
|
+ // 如果请求已完成,不进行上报
|
|
|
+ if (isCompleted) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 上报超时数据,超时时间单位为秒
|
|
|
+ // 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ report(events.ExamStartLoadSlow, { time: timeout });
|
|
|
+ }, timeout) as unknown as number;
|
|
|
+ timers.push(timer);
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 执行请求
|
|
|
+ const result = await getOpenExaminee(params);
|
|
|
+ // 标记请求已完成
|
|
|
+ isCompleted = true;
|
|
|
+ // 清除所有定时器
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ return result;
|
|
|
+ } catch (error) {
|
|
|
+ // 请求失败时也要清除定时器
|
|
|
+ isCompleted = true;
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 带超时检测的 getPaper 请求
|
|
|
+ * 在 3s, 5s, 10s, 15s, 20s 这 5 个时间点检测超时并上报
|
|
|
+ * 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ */
|
|
|
+const getPaperWithTimeoutCheck = async (params: Study.GetExamPaperRequestDTO) => {
|
|
|
+ // 超时时间档次(单位:毫秒)
|
|
|
+ const timeoutLevels = [3000, 5000, 10000, 15000, 20000];
|
|
|
+ // 存储所有定时器ID,用于清理
|
|
|
+ const timers: number[] = [];
|
|
|
+ // 请求是否已完成
|
|
|
+ let isCompleted = false;
|
|
|
+
|
|
|
+ // 创建超时检测定时器
|
|
|
+ timeoutLevels.forEach((timeout) => {
|
|
|
+ const timer = setTimeout(() => {
|
|
|
+ // 如果请求已完成,不进行上报
|
|
|
+ if (isCompleted) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 上报超时数据,超时时间单位为秒
|
|
|
+ // 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ report(events.ExamStartGetPaperSlow, { time: timeout });
|
|
|
+ }, timeout) as unknown as number;
|
|
|
+ timers.push(timer);
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 执行请求
|
|
|
+ const result = await getPaper(params);
|
|
|
+ // 标记请求已完成
|
|
|
+ isCompleted = true;
|
|
|
+ // 清除所有定时器
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ return result;
|
|
|
+ } catch (error) {
|
|
|
+ // 请求失败时也要清除定时器
|
|
|
+ isCompleted = true;
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 带超时检测的 beginExaminee 请求
|
|
|
+ * 在 3s, 5s, 10s, 15s, 20s 这 5 个时间点检测超时并上报
|
|
|
+ * 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ */
|
|
|
+const beginExamineeWithTimeoutCheck = async (examineeId: number) => {
|
|
|
+ // 超时时间档次(单位:毫秒)
|
|
|
+ const timeoutLevels = [3000, 5000, 10000, 15000, 20000];
|
|
|
+ // 存储所有定时器ID,用于清理
|
|
|
+ const timers: number[] = [];
|
|
|
+ // 请求是否已完成
|
|
|
+ let isCompleted = false;
|
|
|
+
|
|
|
+ // 创建超时检测定时器
|
|
|
+ timeoutLevels.forEach((timeout) => {
|
|
|
+ const timer = setTimeout(() => {
|
|
|
+ // 如果请求已完成,不进行上报
|
|
|
+ if (isCompleted) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 上报超时数据,超时时间单位为秒
|
|
|
+ // 每个时间点如果请求还未返回,都会上报一次
|
|
|
+ report(events.ExamStartBeginExamineeSlow, { time: timeout });
|
|
|
+ }, timeout) as unknown as number;
|
|
|
+ timers.push(timer);
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 执行请求
|
|
|
+ const result = await beginExaminee(examineeId);
|
|
|
+ // 标记请求已完成
|
|
|
+ isCompleted = true;
|
|
|
+ // 清除所有定时器
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ return result;
|
|
|
+ } catch (error) {
|
|
|
+ // 请求失败时也要清除定时器
|
|
|
+ isCompleted = true;
|
|
|
+ timers.forEach((timer) => clearTimeout(timer));
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 1、加载知识点练习数据
|
|
|
const loadPracticeData = async () => {
|
|
|
const { paperType, readonly, practiceInfo } = prevData.value;
|
|
|
@@ -301,17 +442,25 @@ const loadPracticeData = async () => {
|
|
|
data = res.data;
|
|
|
}
|
|
|
} else {
|
|
|
- const params = {
|
|
|
- paperType: paperType,
|
|
|
- relateId: practiceInfo?.relateId,
|
|
|
- } as Study.OpenExamineeRequestDTO;
|
|
|
- if (userStore.isVHS) {
|
|
|
- params.questionType = practiceInfo?.questionType;
|
|
|
- } else {
|
|
|
- params.directed = practiceInfo?.directed || false;
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ paperType: paperType,
|
|
|
+ relateId: practiceInfo?.relateId,
|
|
|
+ } as Study.OpenExamineeRequestDTO;
|
|
|
+ if (userStore.isVHS) {
|
|
|
+ params.questionType = practiceInfo?.questionType;
|
|
|
+ } else {
|
|
|
+ params.directed = practiceInfo?.directed || false;
|
|
|
+ }
|
|
|
+ const start = Date.now();
|
|
|
+ const res = await getOpenExamineeWithTimeoutCheck(params);
|
|
|
+ data = res.data || {};
|
|
|
+ const costTime = Date.now() - start;
|
|
|
+ console.log('开卷用时:', costTime);
|
|
|
+ report(events.ExamStartLoadSuccess, { time: costTime, request: params, response: res.data || {} });
|
|
|
+ } catch (error) {
|
|
|
+ report(events.ExamStartLoadError, { error: error });
|
|
|
}
|
|
|
- const res = await getOpenExaminee(params);
|
|
|
- data = res.data || {};
|
|
|
}
|
|
|
|
|
|
if (!data) {
|
|
|
@@ -332,8 +481,16 @@ const loadExamData = async () => {
|
|
|
const res = await getExamineeResult(simulationInfo.examineeId);
|
|
|
data = res.data;
|
|
|
} else {
|
|
|
- const res = await beginExaminee(simulationInfo.examineeId);
|
|
|
- data = res.data || {};
|
|
|
+ try {
|
|
|
+ const start = Date.now();
|
|
|
+ const res = await beginExamineeWithTimeoutCheck(simulationInfo.examineeId);
|
|
|
+ data = res.data || {};
|
|
|
+ const costTime = Date.now() - start;
|
|
|
+ report(events.ExamStartBeginExamineeSuccess, { time: costTime, request: { examineeId: simulationInfo.examineeId }, response: res.data || {} });
|
|
|
+ } catch (error) {
|
|
|
+ report(events.ExamStartBeginExamineeError, { error: error });
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
}
|
|
|
if (!data) {
|
|
|
uni.$ie.hideLoading();
|
|
|
@@ -371,16 +528,49 @@ const loadExamData = async () => {
|
|
|
// }
|
|
|
const combinePaperData = async (examinee: Study.Examinee, paperType: EnumPaperType) => {
|
|
|
examineeId.value = examinee.examineeId;
|
|
|
- if (examinee.paperId) {
|
|
|
- const res = await getPaper({
|
|
|
+ report(events.ExamStartCombineData, {
|
|
|
+ envUser: localStorage.getItem('ie-user'),
|
|
|
+ envApp: localStorage.getItem('ie-app'),
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!examinee.paperId) {
|
|
|
+ report(events.ExamStartGetPaperError, {
|
|
|
+ error: '获取试卷数据失败,没有paperId',
|
|
|
+ examineeId: examinee.examineeId
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第一步:请求试卷数据(单独 try-catch)
|
|
|
+ let res;
|
|
|
+ try {
|
|
|
+ const start = Date.now();
|
|
|
+ res = await getPaperWithTimeoutCheck({
|
|
|
type: paperType,
|
|
|
id: examinee.paperId
|
|
|
});
|
|
|
+ const costTime = Date.now() - start;
|
|
|
+ report(events.ExamStartGetPaperSuccess, {
|
|
|
+ time: costTime,
|
|
|
+ paperId: examinee.paperId,
|
|
|
+ paperType: paperType
|
|
|
+ });
|
|
|
+ } catch (error: any) {
|
|
|
+ // 请求失败的错误上报
|
|
|
+ report(events.ExamStartGetPaperError, {
|
|
|
+ error: error
|
|
|
+ });
|
|
|
+ throw error; // 继续抛出,让外层处理
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二步:处理和组装数据(单独 try-catch)
|
|
|
+ try {
|
|
|
paperData.value = res.data;
|
|
|
paperData.value.questions = restoreQuestion(examinee.questions, res.data.questions);
|
|
|
console.log('初始化数据', paperData.value.questions)
|
|
|
setQuestionList(paperData.value.questions);
|
|
|
setDuration(examinee.duration || 0);
|
|
|
+
|
|
|
await nextTick();
|
|
|
const targetQuestion = flatQuestionList.value.find(item => item.id === prevData.value.questionId);
|
|
|
if (targetQuestion) {
|
|
|
@@ -412,6 +602,16 @@ const combinePaperData = async (examinee: Study.Examinee, paperType: EnumPaperTy
|
|
|
startTime();
|
|
|
}
|
|
|
}
|
|
|
+ report(events.ExamStartCombineDataSuccess, {
|
|
|
+ paperId: examinee.paperId,
|
|
|
+ paperType: paperType
|
|
|
+ });
|
|
|
+ } catch (error: any) {
|
|
|
+ // 数据处理失败的错误上报
|
|
|
+ report(events.ExamStartCombineDataError, {
|
|
|
+ error: error
|
|
|
+ });
|
|
|
+ throw error; // 继续抛出,让外层处理
|
|
|
}
|
|
|
}
|
|
|
const handleSwiperTipNext = () => {
|
|
|
@@ -427,19 +627,25 @@ const handleGuideClose = () => {
|
|
|
const loadData = async () => {
|
|
|
uni.$ie.showLoading();
|
|
|
const { paperType } = prevData.value;
|
|
|
- if (paperType === EnumPaperType.PRACTICE || paperType === EnumPaperType.COURSE) {
|
|
|
- loadPracticeData();
|
|
|
- } else if (paperType === EnumPaperType.SIMULATED || paperType === EnumPaperType.TEST) {
|
|
|
- // if (paperType === EnumPaperType.SIMULATED && userStore.isVHS) {
|
|
|
- // loadVHSPaperData();
|
|
|
- // } else {
|
|
|
- // loadExamData();
|
|
|
- // }
|
|
|
- loadExamData();
|
|
|
+ try {
|
|
|
+ if (paperType === EnumPaperType.PRACTICE || paperType === EnumPaperType.COURSE) {
|
|
|
+ loadPracticeData();
|
|
|
+ } else if (paperType === EnumPaperType.SIMULATED || paperType === EnumPaperType.TEST) {
|
|
|
+ loadExamData();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ report(events.ExamStartInitError, { error });
|
|
|
+ uni.$ie.showToast('加载失败,请稍后重试');
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.$ie.hideLoading();
|
|
|
+ transferBack();
|
|
|
+ }, 1000);
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
onLoad(() => {
|
|
|
console.log(prevData.value)
|
|
|
+ report(events.ExamStartEnter);
|
|
|
loadData();
|
|
|
uni.addInterceptor('navigateBack', {
|
|
|
invoke: (e) => {
|