|
@@ -517,13 +517,104 @@ class ExamAnswerAnalysisService
|
|
|
'previous_mastery' => $historyMasteryLevel,
|
|
'previous_mastery' => $historyMasteryLevel,
|
|
|
'confidence' => $newConfidence,
|
|
'confidence' => $newConfidence,
|
|
|
'change' => $newMastery - $historyMasteryLevel,
|
|
'change' => $newMastery - $historyMasteryLevel,
|
|
|
- 'weight' => $currentWeight
|
|
|
|
|
|
|
+ 'weight' => $currentWeight,
|
|
|
|
|
+ 'is_parent' => false
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 【修复】计算并更新父节点掌握度,同时添加到返回数组中
|
|
|
|
|
+ $parentMasteryData = $this->updateParentMasteryLevels($studentId, array_keys($knowledgeMasteryVector));
|
|
|
|
|
+
|
|
|
|
|
+ // 合并父节点数据到返回数组
|
|
|
|
|
+ $updatedMastery = array_merge($updatedMastery, $parentMasteryData);
|
|
|
|
|
+
|
|
|
return $updatedMastery;
|
|
return $updatedMastery;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算并更新父节点掌握度
|
|
|
|
|
+ */
|
|
|
|
|
+ private function updateParentMasteryLevels(string $studentId, array $childKpCodes): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $parentMasteryData = [];
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 获取所有子节点的父节点
|
|
|
|
|
+ $parentKpCodes = DB::connection('mysql')
|
|
|
|
|
+ ->table('knowledge_points')
|
|
|
|
|
+ ->whereIn('kp_code', $childKpCodes)
|
|
|
|
|
+ ->whereNotNull('parent_kp_code')
|
|
|
|
|
+ ->distinct()
|
|
|
|
|
+ ->pluck('parent_kp_code')
|
|
|
|
|
+ ->toArray();
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($parentKpCodes as $parentKpCode) {
|
|
|
|
|
+ // 使用MasteryCalculator计算父节点掌握度
|
|
|
|
|
+ $parentMastery = $this->masteryCalculator->calculateParentMastery($studentId, $parentKpCode);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取父节点历史数据
|
|
|
|
|
+ $historyParentMastery = DB::connection('mysql')
|
|
|
|
|
+ ->table('student_knowledge_mastery')
|
|
|
|
|
+ ->where('student_id', $studentId)
|
|
|
|
|
+ ->where('kp_code', $parentKpCode)
|
|
|
|
|
+ ->first();
|
|
|
|
|
+
|
|
|
|
|
+ $previousMastery = $historyParentMastery->mastery_level ?? 0.5;
|
|
|
|
|
+
|
|
|
|
|
+ // 保存父节点掌握度到数据库
|
|
|
|
|
+ DB::connection('mysql')
|
|
|
|
|
+ ->table('student_knowledge_mastery')
|
|
|
|
|
+ ->updateOrInsert(
|
|
|
|
|
+ ['student_id' => $studentId, 'kp_code' => $parentKpCode],
|
|
|
|
|
+ [
|
|
|
|
|
+ 'mastery_level' => $parentMastery,
|
|
|
|
|
+ 'confidence_level' => 0.8, // 父节点置信度默认为0.8
|
|
|
|
|
+ 'total_attempts' => 1, // 父节点不记录答题次数
|
|
|
|
|
+ 'correct_attempts' => 0, // 父节点不记录正确次数
|
|
|
|
|
+ 'mastery_trend' => 'calculated', // 标记为计算得出
|
|
|
|
|
+ 'last_mastery_update' => now(),
|
|
|
|
|
+ 'updated_at' => now(),
|
|
|
|
|
+ ]
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 添加到返回数组中
|
|
|
|
|
+ $parentMasteryData[$parentKpCode] = [
|
|
|
|
|
+ 'kp_id' => $parentKpCode,
|
|
|
|
|
+ 'current_mastery' => $parentMastery,
|
|
|
|
|
+ 'previous_mastery' => $previousMastery,
|
|
|
|
|
+ 'confidence' => 0.8,
|
|
|
|
|
+ 'change' => $parentMastery - $previousMastery,
|
|
|
|
|
+ 'weight' => 1,
|
|
|
|
|
+ 'is_parent' => true
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ Log::debug('父节点掌握度已更新', [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'parent_kp_code' => $parentKpCode,
|
|
|
|
|
+ 'parent_mastery' => $parentMastery
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($parentKpCodes)) {
|
|
|
|
|
+ Log::info('父节点掌握度计算完成', [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'child_count' => count($childKpCodes),
|
|
|
|
|
+ 'parent_count' => count($parentKpCodes),
|
|
|
|
|
+ 'parent_kp_codes' => $parentKpCodes
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (\Exception $e) {
|
|
|
|
|
+ Log::warning('计算父节点掌握度失败', [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'child_kp_codes' => $childKpCodes,
|
|
|
|
|
+ 'error' => $e->getMessage()
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $parentMasteryData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 生成题目维度分析(包含AI分析和解题思路)
|
|
* 生成题目维度分析(包含AI分析和解题思路)
|
|
|
*/
|
|
*/
|
|
@@ -730,21 +821,37 @@ class ExamAnswerAnalysisService
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 计算平均掌握度
|
|
|
|
|
- $averageMastery = array_sum(array_column($knowledgePoints, 'current_mastery')) / count($knowledgePoints);
|
|
|
|
|
|
|
+ // 计算平均掌握度(只计算子节点,不包括父节点)
|
|
|
|
|
+ $childNodes = array_filter($knowledgePoints, fn($kp) => !($kp['is_parent'] ?? false));
|
|
|
|
|
+ $totalChildNodes = count($childNodes);
|
|
|
|
|
|
|
|
- // 掌握度分布
|
|
|
|
|
- $mastered = array_filter($knowledgePoints, fn($kp) => $kp['current_mastery'] >= 0.85);
|
|
|
|
|
- $good = array_filter($knowledgePoints, fn($kp) => $kp['current_mastery'] >= 0.70 && $kp['current_mastery'] < 0.85);
|
|
|
|
|
- $weak = array_filter($knowledgePoints, fn($kp) => $kp['current_mastery'] < 0.70);
|
|
|
|
|
|
|
+ if ($totalChildNodes === 0) {
|
|
|
|
|
+ // 如果没有子节点,只计算所有节点
|
|
|
|
|
+ $averageMastery = array_sum(array_column($knowledgePoints, 'current_mastery')) / count($knowledgePoints);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 计算所有节点的平均掌握度(包括父节点)
|
|
|
|
|
+ $allMastery = array_column($knowledgePoints, 'current_mastery');
|
|
|
|
|
+ $averageMastery = array_sum($allMastery) / count($allMastery);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 掌握度分布(只统计子节点)
|
|
|
|
|
+ $mastered = array_filter($childNodes, fn($kp) => $kp['current_mastery'] >= 0.85);
|
|
|
|
|
+ $good = array_filter($childNodes, fn($kp) => $kp['current_mastery'] >= 0.70 && $kp['current_mastery'] < 0.85);
|
|
|
|
|
+ $weak = array_filter($childNodes, fn($kp) => $kp['current_mastery'] < 0.70);
|
|
|
|
|
+
|
|
|
|
|
+ // 排序找出优势和薄弱点(只包括子节点)
|
|
|
|
|
+ usort($childNodes, fn($a, $b) => $b['current_mastery'] <=> $a['current_mastery']);
|
|
|
|
|
+ $topStrengths = array_slice($childNodes, 0, 3);
|
|
|
|
|
+ $topWeaknesses = array_slice(array_reverse($childNodes), 0, 3);
|
|
|
|
|
|
|
|
- // 排序找出优势和薄弱点
|
|
|
|
|
- usort($knowledgePoints, fn($a, $b) => $b['current_mastery'] <=> $a['current_mastery']);
|
|
|
|
|
- $topStrengths = array_slice($knowledgePoints, 0, 3);
|
|
|
|
|
- $topWeaknesses = array_slice(array_reverse($knowledgePoints), 0, 3);
|
|
|
|
|
|
|
+ // 统计父子节点数量
|
|
|
|
|
+ $childCount = count(array_filter($knowledgePoints, fn($kp) => !($kp['is_parent'] ?? false)));
|
|
|
|
|
+ $parentCount = count(array_filter($knowledgePoints, fn($kp) => $kp['is_parent'] ?? false));
|
|
|
|
|
|
|
|
return [
|
|
return [
|
|
|
- 'total_knowledge_points' => count($knowledgePoints),
|
|
|
|
|
|
|
+ 'total_knowledge_points' => $totalChildNodes, // 只统计子节点数量
|
|
|
|
|
+ 'child_nodes_count' => $childCount,
|
|
|
|
|
+ 'parent_nodes_count' => $parentCount,
|
|
|
'average_mastery' => round($averageMastery, 4),
|
|
'average_mastery' => round($averageMastery, 4),
|
|
|
'mastery_distribution' => [
|
|
'mastery_distribution' => [
|
|
|
'mastered' => count($mastered),
|
|
'mastered' => count($mastered),
|
|
@@ -765,7 +872,13 @@ class ExamAnswerAnalysisService
|
|
|
{
|
|
{
|
|
|
$recommendations = [];
|
|
$recommendations = [];
|
|
|
|
|
|
|
|
|
|
+ // 只对子节点生成推荐,忽略父节点
|
|
|
foreach ($updatedMastery as $kpId => $data) {
|
|
foreach ($updatedMastery as $kpId => $data) {
|
|
|
|
|
+ // 跳过父节点
|
|
|
|
|
+ if ($data['is_parent'] ?? false) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$mastery = $data['current_mastery'];
|
|
$mastery = $data['current_mastery'];
|
|
|
$confidence = $data['confidence'];
|
|
$confidence = $data['confidence'];
|
|
|
$weight = $data['weight'];
|
|
$weight = $data['weight'];
|