| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- <?php
- namespace App\Services;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Collection;
- class StudentProgressService
- {
- /**
- * 计算学生学习进度(总体掌握度)
- * 公式:所有子知识点掌握度累加值 / 所有子知识点掌握度的累加值
- *
- * @param string $studentId
- * @return array
- */
- public function calculateLearningProgress(string $studentId): array
- {
- try {
- // 1. 获取所有父知识点代码(需要排除的)
- $parentKpCodes = $this->getParentKnowledgePointCodes();
- // 2. 获取学生所有知识点的掌握度数据(合并两个表)
- $allMasteryData = $this->getMergedMasteryData($studentId);
- // 3. 过滤掉父知识点,只保留子知识点
- $childMasteryData = $allMasteryData->filter(function ($item) use ($parentKpCodes) {
- return !in_array($item['kp_code'], $parentKpCodes);
- });
- if ($childMasteryData->isEmpty()) {
- return [
- 'success' => false,
- 'error' => '该学生没有子知识点的掌握度数据'
- ];
- }
- // 4. 计算总体掌握度
- $totalMasterySum = $childMasteryData->sum('mastery_level');
- $masteryCount = $childMasteryData->count();
- $overallMastery = $masteryCount > 0 ? $totalMasterySum / $masteryCount : 0.0;
- // 5. 统计信息
- $statistics = [
- 'total_child_kps' => $masteryCount,
- 'average_mastery' => round($overallMastery, 4),
- 'max_mastery' => round($childMasteryData->max('mastery_level'), 4),
- 'min_mastery' => round($childMasteryData->min('mastery_level'), 4),
- 'data_source' => $this->getDataSourceInfo($allMasteryData),
- 'parent_kp_excluded' => count($parentKpCodes),
- 'mastery_distribution' => $this->getMasteryDistribution($childMasteryData)
- ];
- $result = [
- 'student_id' => $studentId,
- 'overall_mastery' => round($overallMastery, 4),
- 'child_knowledge_points' => $childMasteryData->values()->toArray(),
- 'statistics' => $statistics,
- 'calculated_at' => now()->toISOString()
- ];
- Log::info('学生学习进度计算成功', [
- 'student_id' => $studentId,
- 'overall_mastery' => $overallMastery,
- 'child_kp_count' => $masteryCount,
- 'parent_kp_excluded' => count($parentKpCodes)
- ]);
- return [
- 'success' => true,
- 'data' => $result
- ];
- } catch (\Exception $e) {
- Log::error('计算学生学习进度失败', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage()
- ]);
- return [
- 'success' => false,
- 'error' => '计算学习进度时发生错误: ' . $e->getMessage()
- ];
- }
- }
- /**
- * 获取学生知识点掌握度详情
- *
- * @param string $studentId
- * @return array
- */
- public function getKnowledgePointDetails(string $studentId): array
- {
- try {
- // 获取合并的掌握度数据
- $masteryData = $this->getMergedMasteryData($studentId);
- // 获取父知识点列表
- $parentKpCodes = $this->getParentKnowledgePointCodes();
- // 分类数据
- $childData = [];
- $parentData = [];
- foreach ($masteryData as $item) {
- if (in_array($item['kp_code'], $parentKpCodes)) {
- $parentData[] = $item;
- } else {
- $childData[] = $item;
- }
- }
- // 获取知识点详细信息
- $knowledgePointDetails = $this->getKnowledgePointDetailsByCodes(
- array_column($masteryData->toArray(), 'kp_code')
- );
- // 合并知识点信息
- $enhancedData = [];
- foreach ($masteryData as $item) {
- $kpCode = $item['kp_code'];
- $detail = $knowledgePointDetails[$kpCode] ?? null;
- $enhancedData[] = array_merge($item, [
- 'knowledge_point_name' => $detail['name'] ?? '未知知识点',
- 'subject' => $detail['subject'] ?? null,
- 'grade' => $detail['grade'] ?? null,
- 'is_parent' => in_array($kpCode, $parentKpCodes),
- 'parent_kp_code' => $detail['parent_kp_code'] ?? null
- ]);
- }
- return [
- 'student_id' => $studentId,
- 'mastery_data' => $enhancedData,
- 'summary' => [
- 'total_kps' => count($enhancedData),
- 'child_kps' => count($childData),
- 'parent_kps' => count($parentData),
- 'overall_child_mastery' => count($childData) > 0 ?
- round(array_sum(array_column($childData, 'mastery_level')) / count($childData), 4) : 0.0
- ]
- ];
- } catch (\Exception $e) {
- Log::error('获取学生知识点掌握度详情失败', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage()
- ]);
- throw $e;
- }
- }
- /**
- * 批量计算学生学习进度
- *
- * @param array $studentIds
- * @return array
- */
- public function batchCalculateLearningProgress(array $studentIds): array
- {
- $results = [];
- foreach ($studentIds as $studentId) {
- $result = $this->calculateLearningProgress($studentId);
- $results[] = [
- 'student_id' => $studentId,
- 'success' => $result['success'],
- 'data' => $result['success'] ? $result['data'] : null,
- 'error' => $result['success'] ? null : $result['error']
- ];
- }
- return $results;
- }
- /**
- * 获取所有父知识点代码
- *
- * @return array
- */
- private function getParentKnowledgePointCodes(): array
- {
- try {
- $parentCodes = DB::connection('remote_mysql')
- ->table('knowledge_points')
- ->whereNotNull('parent_kp_code')
- ->distinct()
- ->pluck('parent_kp_code')
- ->toArray();
- return array_filter($parentCodes);
- } catch (\Exception $e) {
- Log::error('获取父知识点代码失败', ['error' => $e->getMessage()]);
- return [];
- }
- }
- /**
- * 获取合并的学生掌握度数据(从两个表)
- *
- * @param string $studentId
- * @return Collection
- */
- private function getMergedMasteryData(string $studentId): Collection
- {
- $mergedData = [];
- try {
- // 从 student_knowledge_mastery 表获取数据
- $detailedData = DB::connection('remote_mysql')
- ->table('student_knowledge_mastery')
- ->where('student_id', $studentId)
- ->select([
- 'kp_code',
- 'mastery_level',
- 'total_attempts',
- 'correct_attempts',
- 'updated_at'
- ])
- ->get()
- ->toArray();
- foreach ($detailedData as $item) {
- $mergedData[$item->kp_code] = [
- 'kp_code' => $item->kp_code,
- 'mastery_level' => (float) $item->mastery_level,
- 'total_attempts' => $item->total_attempts,
- 'correct_attempts' => $item->correct_attempts,
- 'source_table' => 'student_knowledge_mastery',
- 'updated_at' => $item->updated_at
- ];
- }
- } catch (\Exception $e) {
- Log::warning('从 student_knowledge_mastery 表获取数据失败', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage()
- ]);
- }
- try {
- // 从 student_mastery 表获取数据(补充或覆盖)
- $simpleData = DB::connection('remote_mysql')
- ->table('student_mastery')
- ->where('student_id', $studentId)
- ->select([
- 'kp as kp_code',
- 'mastery',
- 'attempts as total_attempts',
- 'correct as correct_attempts',
- 'updated_at'
- ])
- ->get()
- ->toArray();
- foreach ($simpleData as $item) {
- $kpCode = $item->kp_code;
- $masteryLevel = (float) $item->mastery;
- // 如果已存在,优先使用 mastery_level 更高的数据
- if (isset($mergedData[$kpCode])) {
- if ($masteryLevel > $mergedData[$kpCode]['mastery_level']) {
- $mergedData[$kpCode]['mastery_level'] = $masteryLevel;
- $mergedData[$kpCode]['source_table'] = 'student_mastery (updated)';
- }
- } else {
- $mergedData[$kpCode] = [
- 'kp_code' => $kpCode,
- 'mastery_level' => $masteryLevel,
- 'total_attempts' => $item->total_attempts ?? 0,
- 'correct_attempts' => $item->correct_attempts ?? 0,
- 'source_table' => 'student_mastery',
- 'updated_at' => $item->updated_at ?? null
- ];
- }
- }
- } catch (\Exception $e) {
- Log::warning('从 student_mastery 表获取数据失败', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage()
- ]);
- }
- return collect($mergedData)->values();
- }
- /**
- * 获取知识点详细信息
- *
- * @param array $kpCodes
- * @return array
- */
- private function getKnowledgePointDetailsByCodes(array $kpCodes): array
- {
- if (empty($kpCodes)) {
- return [];
- }
- try {
- $details = DB::connection('remote_mysql')
- ->table('knowledge_points')
- ->whereIn('kp_code', $kpCodes)
- ->select(['kp_code', 'name', 'subject', 'grade', 'parent_kp_code'])
- ->get()
- ->keyBy('kp_code')
- ->toArray();
- return $details;
- } catch (\Exception $e) {
- Log::warning('获取知识点详细信息失败', [
- 'kp_codes' => $kpCodes,
- 'error' => $e->getMessage()
- ]);
- return [];
- }
- }
- /**
- * 获取数据源信息
- *
- * @param Collection $masteryData
- * @return string
- */
- private function getDataSourceInfo(Collection $masteryData): string
- {
- $sourceTables = $masteryData->pluck('source_table')->unique()->toArray();
- if (count($sourceTables) === 1) {
- return $sourceTables[0];
- }
- return 'merged (' . implode(', ', $sourceTables) . ')';
- }
- /**
- * 获取掌握度分布
- *
- * @param Collection $masteryData
- * @return array
- */
- private function getMasteryDistribution(Collection $masteryData): array
- {
- $distribution = [
- 'excellent' => 0, // >= 0.9
- 'good' => 0, // 0.7 - 0.89
- 'fair' => 0, // 0.5 - 0.69
- 'poor' => 0, // < 0.5
- 'unknown' => 0 // 无数据
- ];
- foreach ($masteryData as $item) {
- $mastery = $item['mastery_level'];
- if ($mastery >= 0.9) {
- $distribution['excellent']++;
- } elseif ($mastery >= 0.7) {
- $distribution['good']++;
- } elseif ($mastery >= 0.5) {
- $distribution['fair']++;
- } elseif ($mastery > 0) {
- $distribution['poor']++;
- } else {
- $distribution['unknown']++;
- }
- }
- return $distribution;
- }
- }
|