merge: AI learning analysis authentication fix

bugfix/ai-assessment-evidence-rescore
chenyuan 1 month ago
commit 323d4eb483

@ -217,10 +217,13 @@ export function uploadResourceFile(data) {
} }
// 学情分析 // 学情分析
export function getLearningAnalysis(params) { export function getLearningAnalysis(params) {
let { stuNeed,stuData}=params const { stuNeed, stuData } = params
return request({ return request({
url: import.meta.env.VITE_APP_AI_API+'learnAnalysis/getStuPersonLearnAnalysis?stuNeed='+stuNeed, url: import.meta.env.VITE_APP_AI_API + 'learnAnalysis/getStuPersonLearnAnalysis',
method: 'POST', method: 'POST',
data:stuData params: { stuNeed },
data: stuData,
// AI 服务使用独立鉴权,不能携带本系统的登录令牌。
headers: { isToken: false }
}) })
} }

@ -12,7 +12,8 @@
<h3>此处为AI根据学情数据进行的智能分析,从个人成绩和实训时长角度给出分析结果,仅供参考</h3> <h3>此处为AI根据学情数据进行的智能分析,从个人成绩和实训时长角度给出分析结果,仅供参考</h3>
</div> </div>
<div class="content"> <div class="content">
<div class="analysis"> <el-empty v-if="scoreLoaded && !hasScoreData" class="empty-score" description="暂无成绩数据,暂时无法进行AI学情分析" />
<div v-else class="analysis">
<template v-for="item in dialogue" :key="item.role"> <template v-for="item in dialogue" :key="item.role">
<template v-for="chid in item.child" :key="chid" v-if="item.role == roleId"> <template v-for="chid in item.child" :key="chid" v-if="item.role == roleId">
<div class="analysis-item" @click="analysis(chid)">{{ chid.name }}</div> <div class="analysis-item" @click="analysis(chid)">{{ chid.name }}</div>
@ -57,21 +58,30 @@ const dialogue = ref([
}, },
]); ]);
const scoreList = ref([]); const scoreList = ref([]);
const getScoreList = () => { const scoreLoaded = ref(false);
const hasScoreData = ref(false);
const hasTeacherScoreData = (row) => {
return Number(row.completedTaskCount || 0) > 0 || moduleMap.some(({ key }) => row[`${key}Score`] !== null && row[`${key}Score`] !== undefined);
};
const getScoreList = async () => {
scoreLoaded.value = false;
scoreList.value = [];
hasScoreData.value = false;
proxy.$cache.session.remove("scoreList");
try {
if (roleId.value == 4) { if (roleId.value == 4) {
indexApi.getStuModuleDetail({ userId: userStore.userInfo.userId }).then((res) => { const res = await indexApi.getStuModuleDetail({ userId: userStore.userInfo.userId });
scoreList.value = res.data; scoreList.value = Array.isArray(res?.data) ? res.data : [];
proxy.$cache.session.setJSON("scoreList", scoreList.value); hasScoreData.value = scoreList.value.length > 0;
});
} else { } else {
scoreApi const res = await scoreApi.getScoreList({
.getScoreList({
index: 1, index: 1,
size: 1000, size: 1000,
schoolId: userStore.userInfo.schoolId, schoolId: userStore.userInfo.schoolId,
}) });
.then((res) => { const rows = Array.isArray(res?.data?.list) ? res.data.list : [];
const transformedData = res.data.list.map((item) => ({ hasScoreData.value = rows.some(hasTeacherScoreData);
scoreList.value = rows.map((item) => ({
name: item.name, name: item.name,
className: item.className, className: item.className,
schoolId: item.schoolId, schoolId: item.schoolId,
@ -84,12 +94,26 @@ const getScoreList = () => {
totalRank: item.stuRank || null, totalRank: item.stuRank || null,
})), })),
})); }));
proxy.$cache.session.setJSON("scoreList", transformedData); }
}); if (hasScoreData.value) {
proxy.$cache.session.setJSON("scoreList", scoreList.value);
} else {
proxy.$message.warning("暂无成绩数据,暂时无法进行AI学情分析");
}
} catch (error) {
proxy.$message.error("成绩数据加载失败,请稍后重试");
} finally {
scoreLoaded.value = true;
} }
}; };
// 分析 // 分析
const analysis = (val) => { const analysis = (val) => {
if (!scoreLoaded.value) {
return proxy.$message.warning("成绩数据正在加载,请稍候");
}
if (!hasScoreData.value) {
return proxy.$message.warning("暂无成绩数据,暂时无法进行AI学情分析");
}
proxy.$cache.session.setJSON("msg", val.msg); proxy.$cache.session.setJSON("msg", val.msg);
router.push("/ai/learning-situation-dialogue"); router.push("/ai/learning-situation-dialogue");
}; };
@ -165,6 +189,13 @@ onMounted(() => {
background: linear-gradient(-90deg, #1160ff 0%, #1994ff 100%); background: linear-gradient(-90deg, #1160ff 0%, #1994ff 100%);
} }
} }
.empty-score {
height: 100%;
--el-empty-description-margin-top: 16px;
:deep(.el-empty__description p) {
color: #ffffff;
}
}
.brief-introduction { .brief-introduction {
display: flex; display: flex;
justify-content: center; justify-content: center;

@ -159,16 +159,20 @@ const gInterrupt = () => {
} }
}; };
// 获取分析 // 获取分析
const getAnalysis = () => { const getAnalysis = async () => {
loading.value = true; loading.value = true;
AI.getLearningAnalysis({ stuNeed: proxy.$cache.session.getJSON("msg"), stuData: proxy.$cache.session.getJSON("scoreList") }).then((res) => { try {
const res = await AI.getLearningAnalysis({ stuNeed: proxy.$cache.session.getJSON("msg"), stuData: proxy.$cache.session.getJSON("scoreList") });
messages.value.push({ messages.value.push({
id: Date.now(), id: Date.now(),
role: "assistant", role: "assistant",
content: res, content: res,
}); });
} catch (error) {
proxy.$message.error("AI学情分析暂时不可用,请稍后重试");
} finally {
loading.value = false; loading.value = false;
}); }
}; };
// 计算高度的函数 // 计算高度的函数
const updateHeight = () => { const updateHeight = () => {

@ -0,0 +1,29 @@
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");
const assert = require("node:assert/strict");
const root = path.resolve(__dirname, "..");
const apiSource = fs.readFileSync(path.join(root, "src/api/AI.js"), "utf8");
const analysisSource = fs.readFileSync(path.join(root, "src/views/analysis-of-students/analysis.vue"), "utf8");
const dialogueSource = fs.readFileSync(path.join(root, "src/views/analysis-of-students/dialogue.vue"), "utf8");
test("learning analysis request does not send the commerce login token", () => {
const requestBlock = apiSource.match(/export function getLearningAnalysis[\s\S]*?\n}/)?.[0] || "";
assert.match(requestBlock, /headers:\s*\{\s*isToken:\s*false\s*}/);
assert.match(requestBlock, /params:\s*\{\s*stuNeed\s*}/);
});
test("learning analysis page handles empty and failed score data", () => {
assert.match(analysisSource, /暂无成绩数据,暂时无法进行AI学情分析/);
assert.match(analysisSource, /proxy\.\$cache\.session\.remove\("scoreList"\)/);
assert.match(analysisSource, /rows\.some\(hasTeacherScoreData\)/);
assert.match(analysisSource, /completedTaskCount/);
assert.match(analysisSource, /if \(!hasScoreData\.value\)/);
assert.match(analysisSource, /finally\s*\{\s*scoreLoaded\.value = true/);
});
test("AI analysis failure always closes the loading overlay", () => {
assert.match(dialogueSource, /AI学情分析暂时不可用,请稍后重试/);
assert.match(dialogueSource, /finally\s*\{\s*loading\.value = false/);
});
Loading…
Cancel
Save