Files
KnowledgeBase/app/Services/DocumentSearchService.php

152 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Models\Document;
use App\Models\KnowledgeBase;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
class DocumentSearchService
{
/**
* 搜索文档
*
* @param string $query 搜索关键词
* @param array $accessibleStationIds 权限边界的线站 IDs空=不限制)
* @param array $filters 用户主动筛选:
* - station_ids: int[] 线站 ID
* - knowledge_base_ids: int[] 知识库 ID
* - uploaded_by: int 上传者 ID
* @return Collection
*/
public function search(string $query, array $accessibleStationIds = [], array $filters = []): Collection
{
try {
$kbIds = $this->resolveKnowledgeBaseIds($accessibleStationIds, $filters);
$searchBuilder = Document::search($query);
if (!empty($filters['uploaded_by'])) {
$searchBuilder->where('uploaded_by', $filters['uploaded_by']);
}
if (!empty($kbIds)) {
$searchBuilder->query(fn ($q) => $q->whereIn('knowledge_base_id', $kbIds));
}
return $searchBuilder->get();
} catch (\Exception $e) {
Log::error('文档搜索失败', [
'query' => $query,
'filters' => $filters,
'error' => $e->getMessage(),
]);
return new Collection();
}
}
/**
* 解析最终可搜索的知识库 ID 列表
*
* 1. 从 accessibleStationIds 得到可访问 KB全局 KB + 关联 station 的 KB
* 2. 从 filters 得到用户指定的 KBstation_ids + knowledge_base_ids 并集)
* 3. 取交集(若有 filter 指定),否则用可访问范围
*/
private function resolveKnowledgeBaseIds(array $accessibleStationIds, array $filters): array
{
// 权限边界
$accessibleKbIds = null;
if (!empty($accessibleStationIds)) {
$accessibleKbIds = KnowledgeBase::where(function ($q) use ($accessibleStationIds) {
$q->whereDoesntHave('stations')
->orWhereHas('stations', fn ($sq) => $sq->whereIn('stations.id', $accessibleStationIds));
})->pluck('id')->toArray();
}
// 用户主动筛选
$kbIdsFromStations = [];
if (!empty($filters['station_ids'])) {
$kbIdsFromStations = KnowledgeBase::where(function ($q) use ($filters) {
$q->whereDoesntHave('stations')
->orWhereHas('stations', fn ($sq) => $sq->whereIn('stations.id', $filters['station_ids']));
})->pluck('id')->toArray();
}
$kbIdsFromFilter = $filters['knowledge_base_ids'] ?? [];
if (!empty($kbIdsFromStations) && !empty($kbIdsFromFilter)) {
$filterKbIds = array_values(array_intersect($kbIdsFromStations, $kbIdsFromFilter));
} elseif (!empty($kbIdsFromStations)) {
$filterKbIds = $kbIdsFromStations;
} elseif (!empty($kbIdsFromFilter)) {
$filterKbIds = $kbIdsFromFilter;
} else {
$filterKbIds = [];
}
// 合并
if ($accessibleKbIds === null) {
return $filterKbIds; // 无权限限制
}
if (empty($filterKbIds)) {
return $accessibleKbIds; // 无主动筛选,用权限范围
}
return array_values(array_intersect($filterKbIds, $accessibleKbIds)); // 取交集
}
/**
* 准备文档的可搜索数据
*/
public function prepareSearchableData(Document $document): array
{
return [
'id' => $document->id,
'title' => $document->title,
'description' => $document->description,
'markdown_content' => $document->getMarkdownContent(),
'knowledge_base_id' => $document->knowledge_base_id,
'uploaded_by' => $document->uploaded_by,
'created_at' => $document->created_at?->timestamp,
'updated_at' => $document->updated_at?->timestamp,
];
}
public function indexDocument(Document $document): void
{
try {
if (!$document->shouldBeSearchable()) {
return;
}
$document->searchable();
} catch (\Exception $e) {
Log::error('文档索引失败', ['document_id' => $document->id, 'error' => $e->getMessage()]);
}
}
public function updateDocumentIndex(Document $document): void
{
try {
if ($document->shouldBeSearchable()) {
$document->searchable();
} else {
$this->removeDocumentFromIndex($document);
}
} catch (\Exception $e) {
Log::error('文档索引更新失败', ['document_id' => $document->id, 'error' => $e->getMessage()]);
}
}
public function removeDocumentFromIndex(Document $document): void
{
try {
$document->unsearchable();
} catch (\Exception $e) {
Log::error('文档索引移除失败', ['document_id' => $document->id, 'error' => $e->getMessage()]);
}
}
}