| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- <?php
- namespace App\Services;
- use Illuminate\Support\Facades\Http;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Facades\Cache;
- /**
- * 知识点掌握情况服务
- *
- * 提供:
- * 1. 获取学生知识点掌握情况统计
- * 2. 获取知识点图谱数据(考试快照)
- * 3. 获取知识点图谱快照列表
- */
- class KnowledgeMasteryService
- {
- protected string $learningAnalyticsBase;
- protected string $knowledgeServiceBase;
- protected int $timeout;
- public function __construct(?string $learningAnalyticsBase = null, ?string $knowledgeServiceBase = null, ?int $timeout = null)
- {
- // 已迁移到本地,使用MasteryCalculator
- $this->learningAnalyticsBase = '';
- $this->knowledgeServiceBase = rtrim(
- $knowledgeServiceBase
- ?: config('services.knowledge_service.url', env('KNOWLEDGE_SERVICE_API_BASE', 'http://localhost:5011')),
- '/'
- );
- $this->timeout = 20;
- }
- /**
- * 获取学生知识点掌握情况统计
- *
- * @param string $studentId 学生ID
- * @return array
- */
- public function getStats(string $studentId): array
- {
- try {
- // 使用本地的MasteryCalculator
- $masteryCalculator = app(MasteryCalculator::class);
- $overview = $masteryCalculator->getStudentMasteryOverview($studentId);
- // 转换为兼容格式
- $body = [
- 'student_id' => $studentId,
- 'total_knowledge_points' => $overview['total_knowledge_points'],
- 'average_mastery' => $overview['average_mastery_level'],
- 'mastered_count' => $overview['mastered_knowledge_points'],
- 'good_count' => $overview['good_knowledge_points'],
- 'weak_count' => $overview['weak_knowledge_points'],
- 'details' => $overview['details'],
- ];
- // 丰富知识点名称(如果有knowledge_points表)
- $body = $this->enrichWithKnowledgePointNames($body);
- // 添加知识图谱总数统计
- $graphStats = $this->getKnowledgeGraphStats();
- $body['graph_total_knowledge_points'] = $graphStats['total'] ?? 0;
- Log::info('KnowledgeMasteryService::getStats (Local)', ['student_id' => $studentId]);
- return [
- 'success' => true,
- 'data' => $body,
- ];
- } catch (\Throwable $e) {
- Log::error('Knowledge mastery stats exception', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage(),
- ]);
- return [
- 'success' => false,
- 'error' => '获取知识点掌握情况异常: ' . $e->getMessage(),
- ];
- }
- }
- /**
- * 丰富知识点名称
- */
- private function enrichWithKnowledgePointNames(array $data): array
- {
- if (empty($data['details'])) {
- return $data;
- }
- // 确保 details 是数组格式(处理 stdClass 对象)
- $details = $data['details'];
- if (!is_array($details)) {
- $details = [];
- } elseif (isset($details[0]) && is_object($details[0])) {
- // 将 stdClass 对象转换为关联数组
- $details = array_map(function ($item) {
- return (array) $item;
- }, $details);
- }
- // 收集所有kp_code
- $kpCodes = array_column($details, 'kp_code');
- if (empty($kpCodes)) {
- return $data;
- }
- // 批量获取知识点名称
- $kpNames = $this->getKnowledgePointNames($kpCodes);
- // 丰富details数据
- foreach ($details as &$detail) {
- $kpCode = $detail['kp_code'] ?? null;
- if ($kpCode && isset($kpNames[$kpCode])) {
- $detail['kp_name'] = $kpNames[$kpCode];
- } else {
- $detail['kp_name'] = $kpCode; // fallback to code
- }
- }
- // 更新原始数据
- $data['details'] = $details;
- return $data;
- }
- /**
- * 批量获取知识点名称
- */
- private function getKnowledgePointNames(array $kpCodes): array
- {
- $result = [];
- foreach ($kpCodes as $kpCode) {
- $name = $this->getKnowledgePointName($kpCode);
- if ($name) {
- $result[$kpCode] = $name;
- }
- }
- return $result;
- }
- /**
- * 获取单个知识点名称(带缓存)
- */
- private function getKnowledgePointName(string $kpCode): ?string
- {
- $cacheKey = "kp_name_{$kpCode}";
- return Cache::remember($cacheKey, 3600, function () use ($kpCode) {
- try {
- $response = Http::timeout(5)
- ->get($this->knowledgeServiceBase . '/knowledge-points/' . $kpCode);
- if ($response->successful()) {
- $data = $response->json();
- return $data['cn_name'] ?? $data['en_name'] ?? null;
- }
- } catch (\Throwable $e) {
- Log::debug('Failed to get knowledge point name', [
- 'kp_code' => $kpCode,
- 'error' => $e->getMessage(),
- ]);
- }
- return null;
- });
- }
- /**
- * 获取知识图谱统计信息(带缓存)
- */
- public function getKnowledgeGraphStats(): array
- {
- return Cache::remember('knowledge_graph_stats', 3600, function () {
- try {
- $response = Http::timeout(10)
- ->get($this->knowledgeServiceBase . '/knowledge-points/');
- if ($response->successful()) {
- $data = $response->json();
- $items = $data['data'] ?? $data ?? [];
- return [
- 'total' => count($items),
- 'updated_at' => now()->toISOString(),
- ];
- }
- } catch (\Throwable $e) {
- Log::error('Failed to get knowledge graph stats', [
- 'error' => $e->getMessage(),
- ]);
- }
- return ['total' => 0];
- });
- }
- /**
- * 获取学生知识点图谱数据
- *
- * @param string $studentId 学生ID
- * @param string|null $examId 考试ID(可选,不指定则返回最新快照)
- * @return array
- */
- public function getGraph(string $studentId, ?string $examId = null): array
- {
- try {
- // 使用本地的MasteryCalculator
- $masteryCalculator = app(MasteryCalculator::class);
- $overview = $masteryCalculator->getStudentMasteryOverview($studentId);
- // 转换为图谱格式
- $nodes = [];
- $edges = [];
- foreach ($overview['details'] as $detail) {
- $masteryLevel = floatval($detail->mastery_level ?? 0);
- $kpCode = $detail->kp_code;
- // 节点
- $nodes[] = [
- 'id' => $kpCode,
- 'label' => $kpCode,
- 'mastery' => $masteryLevel,
- 'mastery_level' => $this->getMasteryLevelLabel($masteryLevel),
- 'color' => $this->getMasteryColor($masteryLevel),
- 'size' => 20 + ($masteryLevel * 20),
- ];
- // 这里可以添加知识点之间的依赖关系边
- // 暂时为空,后续从knowledge_points表获取
- }
- $graphData = [
- 'nodes' => $nodes,
- 'edges' => $edges,
- 'statistics' => [
- 'total_nodes' => count($nodes),
- 'total_edges' => count($edges),
- 'average_mastery' => $overview['average_mastery_level'],
- ],
- ];
- Log::info('KnowledgeMasteryService::getGraph (Local)', ['student_id' => $studentId, 'exam_id' => $examId]);
- return [
- 'success' => true,
- 'data' => $graphData,
- ];
- } catch (\Throwable $e) {
- Log::error('Knowledge graph exception', [
- 'student_id' => $studentId,
- 'exam_id' => $examId,
- 'error' => $e->getMessage(),
- ]);
- return [
- 'success' => false,
- 'error' => '获取知识点图谱异常: ' . $e->getMessage(),
- ];
- }
- }
- /**
- * 获取掌握度等级标签
- */
- private function getMasteryLevelLabel(float $mastery): string
- {
- if ($mastery >= 0.85) return 'mastered';
- if ($mastery >= 0.70) return 'good';
- if ($mastery >= 0.50) return 'fair';
- return 'weak';
- }
- /**
- * 获取掌握度颜色
- */
- private function getMasteryColor(float $mastery): string
- {
- if ($mastery >= 0.85) return '#52c41a'; // 绿色 - 掌握
- if ($mastery >= 0.70) return '#1890ff'; // 蓝色 - 良好
- if ($mastery >= 0.50) return '#faad14'; // 橙色 - 一般
- return '#ff4d4f'; // 红色 - 薄弱
- }
- /**
- * 获取学生知识点图谱快照列表
- *
- * @param string $studentId 学生ID
- * @param int $limit 返回数量限制
- * @return array
- */
- public function getGraphSnapshots(string $studentId, int $limit = 10): array
- {
- try {
- // 从knowledge_point_mastery_snapshots表获取快照
- $snapshots = \DB::table('knowledge_point_mastery_snapshots')
- ->where('student_id', $studentId)
- ->orderBy('created_at', 'desc')
- ->limit($limit)
- ->get();
- $snapshotList = $snapshots->map(function ($snapshot) {
- // 解析掌握度数据
- $masteryData = json_decode($snapshot->mastery_data, true) ?: [];
- // 提取知识点信息
- $knowledgePoints = [];
- foreach ($masteryData as $kpData) {
- $knowledgePoints[] = [
- 'kp_code' => $kpData['kp_code'] ?? '',
- 'kp_name' => $kpData['kp_name'] ?? ($kpData['kp_code'] ?? ''),
- 'mastery_level' => floatval($kpData['mastery_level'] ?? 0),
- ];
- }
- return [
- 'snapshot_id' => $snapshot->snapshot_id,
- 'student_id' => $snapshot->student_id,
- 'overall_mastery' => floatval($snapshot->overall_mastery),
- 'weak_knowledge_points_count' => intval($snapshot->weak_knowledge_points_count),
- 'strong_knowledge_points_count' => intval($snapshot->strong_knowledge_points_count),
- 'knowledge_points' => $knowledgePoints,
- 'created_at' => $snapshot->created_at,
- 'snapshot_time' => $snapshot->snapshot_time,
- ];
- })->toArray();
- Log::info('KnowledgeMasteryService::getGraphSnapshots (Local)', ['student_id' => $studentId, 'limit' => $limit]);
- return [
- 'success' => true,
- 'data' => [
- 'snapshots' => $snapshotList,
- 'total' => count($snapshotList),
- ],
- ];
- } catch (\Throwable $e) {
- Log::error('Knowledge graph snapshots exception', [
- 'student_id' => $studentId,
- 'limit' => $limit,
- 'error' => $e->getMessage(),
- ]);
- return [
- 'success' => false,
- 'error' => '获取知识点图谱快照列表异常: ' . $e->getMessage(),
- ];
- }
- }
- /**
- * 获取学生知识点掌握摘要(简化版)
- *
- * @param string $studentId 学生ID
- * @return array
- */
- public function getSummary(string $studentId): array
- {
- $stats = $this->getStats($studentId);
- if (!$stats['success']) {
- return $stats;
- }
- $data = $stats['data'];
- return [
- 'success' => true,
- 'data' => [
- 'student_id' => $data['student_id'] ?? $studentId,
- 'total' => $data['total_knowledge_points'] ?? 0,
- 'mastered' => $data['mastered_knowledge_points'] ?? 0,
- 'unmastered' => $data['unmastered_knowledge_points'] ?? 0,
- 'mastery_rate' => $data['mastery_rate'] ?? 0.0,
- 'mastery_percentage' => round(($data['mastery_rate'] ?? 0) * 100, 1) . '%',
- 'graph_total' => $data['graph_total_knowledge_points'] ?? 0,
- ],
- ];
- }
- /**
- * 创建知识点掌握度快照
- *
- * @param string $studentId 学生ID
- * @param string $snapshotType 快照类型 (exam/report/manual/scheduled)
- * @param string|null $sourceId 来源ID
- * @param string|null $sourceName 来源名称
- * @param string|null $notes 备注
- * @return array
- */
- public function createSnapshot(
- string $studentId,
- string $snapshotType = 'report',
- ?string $sourceId = null,
- ?string $sourceName = null,
- ?string $notes = null
- ): array {
- try {
- // 使用LocalAIAnalysisService创建快照
- $localAI = app(LocalAIAnalysisService::class);
- // 获取当前掌握度数据
- $masteryCalculator = app(MasteryCalculator::class);
- $overview = $masteryCalculator->getStudentMasteryOverview($studentId);
- $snapshots = [];
- foreach ($overview['details'] as $detail) {
- $snapshotId = \DB::table('knowledge_point_mastery_snapshots')->insertGetId([
- 'student_id' => $studentId,
- 'kp_code' => $detail->kp_code,
- 'mastery_level' => $detail->mastery_level,
- 'confidence_level' => $detail->confidence_level,
- 'snapshot_type' => $snapshotType,
- 'source_id' => $sourceId,
- 'source_name' => $sourceName,
- 'notes' => $notes,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- $snapshots[] = [
- 'snapshot_id' => $snapshotId,
- 'kp_code' => $detail->kp_code,
- 'mastery_level' => $detail->mastery_level,
- ];
- }
- Log::info('KnowledgeMasteryService::createSnapshot (Local)', [
- 'student_id' => $studentId,
- 'snapshot_type' => $snapshotType,
- 'snapshot_count' => count($snapshots),
- ]);
- return [
- 'success' => true,
- 'data' => [
- 'snapshots' => $snapshots,
- 'total_snapshots' => count($snapshots),
- ],
- ];
- } catch (\Throwable $e) {
- Log::error('Create knowledge mastery snapshot exception', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage(),
- ]);
- return [
- 'success' => false,
- 'error' => '创建知识点掌握度快照异常: ' . $e->getMessage(),
- ];
- }
- }
- }
|