feat: API for content

This commit is contained in:
2026-04-06 17:36:09 +08:00
parent ad0add4500
commit 295cf12899
3 changed files with 75 additions and 6 deletions

View File

@@ -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
* 终端心跳上报

View File

@@ -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) . '...';
}
}

View File

@@ -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']);
});
});