Files
KnowledgeBase/app/Http/Controllers/Api/TerminalApiController.php
T
2026-03-24 15:33:10 +08:00

185 lines
5.7 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Guide;
use App\Models\GuidePage;
use App\Models\GuidePageEdge;
use App\Services\KnowledgeContextService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class TerminalApiController extends Controller
{
public function __construct(
private KnowledgeContextService $knowledgeService,
) {}
/**
* GET /api/terminal/config
* 返回终端配置
*/
public function config(Request $request): JsonResponse
{
$terminal = $request->attributes->get('terminal');
$terminal->load(['prompt', 'station']);
// 返回原始提示词模板
$systemPrompt = $terminal->prompt?->prompt_template ?? '';
// 获取终端所属线站的已发布指引数量(含全局指引)
$guideCount = $this->getTerminalGuides($terminal)->count();
return response()->json([
'terminal' => [
'id' => $terminal->id,
'terminal_name' => $terminal->name,
'terminal_code' => $terminal->code,
'station_name' => $terminal->station?->name,
'diagram_url' => $terminal->diagram_url,
'scada_data_url' => $terminal->scada_data_url,
'scada_tags_url' => $terminal->scada_tags_url,
'voice_wakeup_enabled' => $terminal->voice_wakeup_enabled,
'voice_wakeup_word' => $terminal->voice_wakeup_word,
],
'system_prompt' => $systemPrompt,
'guide_count' => $guideCount,
]);
}
/**
* GET /api/knowledge?query=xxx
* RAG知识搜索(由AI tool_call触发)
*/
public function knowledge(Request $request): JsonResponse
{
$request->validate([
'query' => 'required|string|max:500',
]);
$terminal = $request->attributes->get('terminal');
$result = $this->knowledgeService->search($request->input('query'), $terminal);
return response()->json($result);
}
/**
* GET /api/terminal/guides?category=operation
* 已发布的指引列表
*/
public function guides(Request $request): JsonResponse
{
$terminal = $request->attributes->get('terminal');
$query = $this->getTerminalGuides($terminal)->withCount('pages');
if ($category = $request->input('category')) {
$query->where('category', $category);
}
$guides = $query->orderBy('name')->get()->map(fn(Guide $guide) => [
'id' => $guide->id,
'name' => $guide->name,
'description' => $guide->description,
'category' => $guide->category,
'tags' => $guide->tags,
'page_count' => $guide->pages_count,
]);
return response()->json(['guides' => $guides]);
}
/**
* POST /api/terminal/guides/pages
* 返回指引页面(状态机格式,每页带 next 指针)
*/
public function guidePages(Request $request): JsonResponse
{
$request->validate([
'guide_ids' => 'required|array|min:1',
'guide_ids.*' => 'integer|exists:guides,id',
]);
$terminal = $request->attributes->get('terminal');
$accessibleIds = $this->getTerminalGuides($terminal)->pluck('guides.id')->toArray();
$guideIds = collect($request->input('guide_ids'))
->intersect($accessibleIds)
->values()
->toArray();
$pages = GuidePage::whereIn('guide_id', $guideIds)->get();
$edges = GuidePageEdge::whereIn('guide_id', $guideIds)
->orderBy('from_page_id')
->orderBy('sort')
->get();
$edgesByFrom = $edges->groupBy('from_page_id');
$hasIncoming = $edges->pluck('to_page_id')->unique()->flip();
$guides = [];
foreach ($pages->groupBy('guide_id') as $guideId => $guidePages) {
$entryPage = $guidePages->first(fn($p) => !$hasIncoming->has($p->id));
$pagesMap = [];
foreach ($guidePages as $page) {
$next = $edgesByFrom->get($page->id, collect())
->map(function (GuidePageEdge $e) {
$item = ['page_id' => $e->to_page_id];
if ($e->label !== null) {
$item['label'] = $e->label;
}
return $item;
})->values()->toArray();
$pagesMap[$page->id] = [
'id' => $page->id,
'title' => $page->title,
'html_url' => $page->html_url,
'next' => $next,
];
}
$guides[$guideId] = [
'entry_page_id' => $entryPage?->id,
'pages' => $pagesMap,
];
}
return response()->json([
'guides' => $guides,
]);
}
/**
* 获取终端可见的指引(线站关联 + 全局)
*/
private function getTerminalGuides($terminal)
{
$stationId = $terminal->station_id;
return Guide::published()->where(function ($q) use ($stationId) {
$q->whereDoesntHave('stations'); // 全局指引
if ($stationId) {
$q->orWhereHas('stations', fn($sq) => $sq->where('stations.id', $stationId));
}
});
}
/**
* POST /api/terminal/heartbeat
* 终端心跳上报
*/
public function heartbeat(Request $request): JsonResponse
{
$terminal = $request->attributes->get('terminal');
$terminal->update([
'is_online' => true,
'last_online_at' => now(),
]);
return response()->json(['status' => 'ok']);
}
}