fix: tree & guide
This commit is contained in:
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -90,7 +91,7 @@ class TerminalApiController extends Controller
|
||||
|
||||
/**
|
||||
* POST /api/terminal/guides/pages
|
||||
* 组合多个指引的页面,返回递归树形结构
|
||||
* 返回指引页面(状态机格式,每页带 next 指针)
|
||||
*/
|
||||
public function guidePages(Request $request): JsonResponse
|
||||
{
|
||||
@@ -102,104 +103,54 @@ class TerminalApiController extends Controller
|
||||
$terminal = $request->attributes->get('terminal');
|
||||
$accessibleIds = $this->getTerminalGuides($terminal)->pluck('guides.id')->toArray();
|
||||
|
||||
$guideIds = $request->input('guide_ids');
|
||||
$pages = [];
|
||||
$guideIds = collect($request->input('guide_ids'))
|
||||
->intersect($accessibleIds)
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
foreach ($guideIds as $guideId) {
|
||||
if (!in_array($guideId, $accessibleIds)) {
|
||||
continue;
|
||||
$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,
|
||||
];
|
||||
}
|
||||
|
||||
$guide = Guide::with(
|
||||
$this->buildEagerLoadArray('trunkPages', 5)
|
||||
)->find($guideId);
|
||||
|
||||
if (!$guide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($guide->trunkPages as $page) {
|
||||
$pages = array_merge($pages, $this->flattenSequentialPages($page, $guide->name, $guide->id));
|
||||
}
|
||||
$guides[$guideId] = [
|
||||
'entry_page_id' => $entryPage?->id,
|
||||
'pages' => $pagesMap,
|
||||
];
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'pages' => $pages,
|
||||
'total_pages' => count($pages),
|
||||
'guides' => $guides,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将树形页面结构展平:顺序节点(无 options)平铺,分支节点保留嵌套
|
||||
*/
|
||||
private function flattenSequentialPages(GuidePage $page, string $guideName, int $guideId): array
|
||||
{
|
||||
$data = [
|
||||
'id' => $page->id,
|
||||
'guide_id' => $guideId,
|
||||
'guide_name' => $guideName,
|
||||
'page_number' => $page->page_number,
|
||||
'title' => $page->title,
|
||||
'html_url' => $page->html_url,
|
||||
];
|
||||
|
||||
if ($page->options && $page->branchChildren->isNotEmpty()) {
|
||||
$data['options'] = $page->options;
|
||||
$branches = [];
|
||||
foreach ($page->branchChildren as $child) {
|
||||
$branches[$child->branch_option][] =
|
||||
$this->buildPageTree($child, $guideName, $guideId);
|
||||
}
|
||||
$data['branches'] = $branches;
|
||||
return [$data];
|
||||
}
|
||||
|
||||
if ($page->branchChildren->isNotEmpty()) {
|
||||
$result = [$data];
|
||||
foreach ($page->branchChildren as $child) {
|
||||
$result = array_merge($result, $this->flattenSequentialPages($child, $guideName, $guideId));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return [$data];
|
||||
}
|
||||
|
||||
private function buildPageTree(GuidePage $page, string $guideName, int $guideId): array
|
||||
{
|
||||
$data = [
|
||||
'id' => $page->id,
|
||||
'guide_id' => $guideId,
|
||||
'guide_name' => $guideName,
|
||||
'page_number' => $page->page_number,
|
||||
'title' => $page->title,
|
||||
'html_url' => $page->html_url,
|
||||
];
|
||||
|
||||
if ($page->options && $page->branchChildren->isNotEmpty()) {
|
||||
$data['options'] = $page->options;
|
||||
$branches = [];
|
||||
foreach ($page->branchChildren as $child) {
|
||||
$branches[$child->branch_option][] =
|
||||
$this->buildPageTree($child, $guideName, $guideId);
|
||||
}
|
||||
$data['branches'] = $branches;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function buildEagerLoadArray(string $base, int $depth): array
|
||||
{
|
||||
$loads = [$base => fn($q) => $q->orderBy('sort_order')];
|
||||
$current = $base;
|
||||
for ($i = 0; $i < $depth; $i++) {
|
||||
$current .= '.branchChildren';
|
||||
$loads[$current] = fn($q) => $q->orderBy('sort_order');
|
||||
}
|
||||
return $loads;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取终端可见的指引(线站关联 + 全局)
|
||||
*/
|
||||
@@ -210,7 +161,7 @@ class TerminalApiController extends Controller
|
||||
return Guide::published()->where(function ($q) use ($stationId) {
|
||||
$q->whereDoesntHave('stations'); // 全局指引
|
||||
if ($stationId) {
|
||||
$q->orWhereHas('stations', fn ($sq) => $sq->where('stations.id', $stationId));
|
||||
$q->orWhereHas('stations', fn($sq) => $sq->where('stations.id', $stationId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user