From 295cf12899a9fe2cf8055f697d4eaa2fb5ffa062 Mon Sep 17 00:00:00 2001 From: 7IN0SAN9 Date: Mon, 6 Apr 2026 17:36:09 +0800 Subject: [PATCH] feat: API for content --- .../Controllers/Api/TerminalApiController.php | 62 +++++++++++++++++++ app/Services/KnowledgeContextService.php | 10 ++- routes/api.php | 9 ++- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Api/TerminalApiController.php b/app/Http/Controllers/Api/TerminalApiController.php index 9cd70ae..2d2e933 100644 --- a/app/Http/Controllers/Api/TerminalApiController.php +++ b/app/Http/Controllers/Api/TerminalApiController.php @@ -3,9 +3,11 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Models\Document; use App\Models\Guide; use App\Models\GuidePage; use App\Models\GuidePageEdge; +use App\Models\KnowledgeBase; use App\Services\KnowledgeContextService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -168,6 +170,66 @@ class TerminalApiController extends Controller }); } + /** + * GET /api/terminal/documents/{document}/content + * 读取文档全文或指定行号区间 + */ + public function documentContent(Request $request, int $documentId): JsonResponse + { + $request->validate([ + 'start_line' => 'sometimes|integer|min:1', + 'end_line' => 'sometimes|integer|min:1', + ]); + + $terminal = $request->attributes->get('terminal'); + + // Find document and verify access through station → knowledge_base + $accessibleKbIds = KnowledgeBase::where(function ($q) use ($terminal) { + $q->whereDoesntHave('stations'); // global knowledge bases + if ($terminal->station_id) { + $q->orWhereHas('stations', fn ($sq) => $sq->where('stations.id', $terminal->station_id)); + } + })->where('status', 'active')->pluck('id'); + + $document = Document::where('id', $documentId) + ->whereIn('knowledge_base_id', $accessibleKbIds) + ->where('conversion_status', 'completed') + ->first(); + + if (!$document) { + return response()->json(['error' => 'Document not found'], 404); + } + + $content = $document->getMarkdownContent(); + if ($content === null) { + return response()->json(['error' => 'Document content unavailable'], 404); + } + + $lines = explode("\n", $content); + $totalLines = count($lines); + + $startLine = $request->integer('start_line', 1); + $endLine = $request->integer('end_line', min($startLine + 49, $totalLines)); + $endLine = min($endLine, $totalLines); + + if ($startLine > $totalLines) { + return response()->json([ + 'error' => "start_line ({$startLine}) exceeds total lines ({$totalLines})", + ], 422); + } + + $slice = array_slice($lines, $startLine - 1, $endLine - $startLine + 1); + + return response()->json([ + 'id' => $document->id, + 'title' => $document->title, + 'total_lines' => $totalLines, + 'start_line' => $startLine, + 'end_line' => $endLine, + 'content' => implode("\n", $slice), + ]); + } + /** * POST /api/terminal/heartbeat * 终端心跳上报 diff --git a/app/Services/KnowledgeContextService.php b/app/Services/KnowledgeContextService.php index 5f09de5..cac550c 100644 --- a/app/Services/KnowledgeContextService.php +++ b/app/Services/KnowledgeContextService.php @@ -37,9 +37,12 @@ class KnowledgeContextService } $context .= $snippet . "\n\n"; + + $fullContent = $document->getMarkdownContent() ?? ''; $sources[] = [ 'title' => $document->title, - 'id' => 'kb-doc-' . str_pad($document->id, 3, '0', STR_PAD_LEFT), + 'id' => $document->id, + 'total_lines' => $fullContent !== '' ? substr_count($fullContent, "\n") + 1 : 0, ]; } @@ -52,11 +55,12 @@ class KnowledgeContextService private function extractSnippet($document): string { $content = $document->getMarkdownContent() ?? $document->description ?? ''; + $header = "【{$document->title}】(ID:{$document->id})"; if (mb_strlen($content) <= 500) { - return "【{$document->title}】\n{$content}"; + return "{$header}\n{$content}"; } - return "【{$document->title}】\n" . mb_substr($content, 0, 500) . '...'; + return "{$header}\n" . mb_substr($content, 0, 500) . '...'; } } diff --git a/routes/api.php b/routes/api.php index ea3261d..4632e93 100644 --- a/routes/api.php +++ b/routes/api.php @@ -4,12 +4,15 @@ use App\Http\Controllers\Api\TerminalApiController; use Illuminate\Support\Facades\Route; Route::middleware('identify.terminal')->group(function () { - Route::get('/knowledge', [TerminalApiController::class, 'knowledge']); - Route::prefix('terminal')->group(function () { + Route::post('/heartbeat', [TerminalApiController::class, 'heartbeat']); + Route::get('/config', [TerminalApiController::class, 'config']); + Route::get('/guides', [TerminalApiController::class, 'guides']); Route::post('/guides/pages', [TerminalApiController::class, 'guidePages']); - Route::post('/heartbeat', [TerminalApiController::class, 'heartbeat']); + + Route::get('/knowledge', [TerminalApiController::class, 'knowledge']); + Route::get('/documents/{document}/content', [TerminalApiController::class, 'documentContent']); }); });