From 48ef4cfdefd9bb91f4fd64dfd8ca72f109e50373 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 1 Jan 2026 03:16:03 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8=20Manticore=20Se?= =?UTF-8?q?arch=20=E6=9B=BF=E6=8D=A2=20SeekDB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...leToSeekDB.php => SyncFileToManticore.php} | 38 +- ...oSeekDB.php => SyncProjectToManticore.php} | 57 +- ...skToSeekDB.php => SyncTaskToManticore.php} | 61 +- ...erToSeekDB.php => SyncUserToManticore.php} | 49 +- app/Http/Controllers/Api/SearchController.php | 40 +- app/Http/Controllers/IndexController.php | 6 +- app/Module/Apps.php | 2 +- .../ManticoreBase.php} | 1043 ++++++++--------- .../ManticoreFile.php} | 93 +- app/Module/Manticore/ManticoreKeyValue.php | 139 +++ .../ManticoreProject.php} | 87 +- .../ManticoreTask.php} | 113 +- .../ManticoreUser.php} | 44 +- app/Module/SeekDB/SeekDBKeyValue.php | 181 --- app/Observers/FileObserver.php | 14 +- app/Observers/FileUserObserver.php | 8 +- app/Observers/ProjectObserver.php | 14 +- app/Observers/ProjectTaskObserver.php | 18 +- app/Observers/ProjectTaskUserObserver.php | 12 +- .../ProjectTaskVisibilityUserObserver.php | 12 +- app/Observers/ProjectUserObserver.php | 6 +- app/Observers/UserObserver.php | 10 +- ...ekDBSyncTask.php => ManticoreSyncTask.php} | 70 +- resources/assets/js/components/SearchBox.vue | 2 +- 24 files changed, 1017 insertions(+), 1102 deletions(-) rename app/Console/Commands/{SyncFileToSeekDB.php => SyncFileToManticore.php} (82%) rename app/Console/Commands/{SyncProjectToSeekDB.php => SyncProjectToManticore.php} (71%) rename app/Console/Commands/{SyncTaskToSeekDB.php => SyncTaskToManticore.php} (70%) rename app/Console/Commands/{SyncUserToSeekDB.php => SyncUserToManticore.php} (69%) rename app/Module/{SeekDB/SeekDBBase.php => Manticore/ManticoreBase.php} (59%) rename app/Module/{SeekDB/SeekDBFile.php => Manticore/ManticoreFile.php} (83%) create mode 100644 app/Module/Manticore/ManticoreKeyValue.php rename app/Module/{SeekDB/SeekDBProject.php => Manticore/ManticoreProject.php} (78%) rename app/Module/{SeekDB/SeekDBTask.php => Manticore/ManticoreTask.php} (82%) rename app/Module/{SeekDB/SeekDBUser.php => Manticore/ManticoreUser.php} (83%) delete mode 100644 app/Module/SeekDB/SeekDBKeyValue.php rename app/Tasks/{SeekDBSyncTask.php => ManticoreSyncTask.php} (73%) diff --git a/app/Console/Commands/SyncFileToSeekDB.php b/app/Console/Commands/SyncFileToManticore.php similarity index 82% rename from app/Console/Commands/SyncFileToSeekDB.php rename to app/Console/Commands/SyncFileToManticore.php index 588c57c52..7edd7f22e 100644 --- a/app/Console/Commands/SyncFileToSeekDB.php +++ b/app/Console/Commands/SyncFileToManticore.php @@ -4,12 +4,12 @@ namespace App\Console\Commands; use App\Models\File; use App\Module\Apps; -use App\Module\SeekDB\SeekDBFile; -use App\Module\SeekDB\SeekDBKeyValue; +use App\Module\Manticore\ManticoreFile; +use App\Module\Manticore\ManticoreKeyValue; use Cache; use Illuminate\Console\Command; -class SyncFileToSeekDB extends Command +class SyncFileToManticore extends Command { /** * 更新数据 @@ -21,16 +21,16 @@ class SyncFileToSeekDB extends Command * --c: 清除索引 */ - protected $signature = 'seekdb:sync-files {--f} {--i} {--c} {--u} {--batch=100}'; - protected $description = '同步文件内容到 SeekDB'; + protected $signature = 'manticore:sync-files {--f} {--i} {--c} {--u} {--batch=100}'; + protected $description = '同步文件内容到 Manticore Search'; /** * @return int */ public function handle(): int { - if (!Apps::isInstalled("seekdb")) { - $this->error("应用「SeekDB」未安装"); + if (!Apps::isInstalled("manticore")) { + $this->error("应用「Manticore Search」未安装"); return 1; } @@ -54,8 +54,8 @@ class SyncFileToSeekDB extends Command // 清除索引 if ($this->option('c')) { $this->info('清除索引...'); - SeekDBKeyValue::clear(); - SeekDBFile::clear(); + ManticoreKeyValue::clear(); + ManticoreFile::clear(); $this->info("索引删除成功"); $this->releaseLock(); return 0; @@ -64,7 +64,7 @@ class SyncFileToSeekDB extends Command // 仅同步文件用户关系 if ($this->option('u')) { $this->info('开始同步文件用户关系...'); - $count = SeekDBFile::syncAllFileUsers(function ($count) { + $count = ManticoreFile::syncAllFileUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -83,7 +83,7 @@ class SyncFileToSeekDB extends Command if ($this->option('f') || (!$this->option('i') && !$this->option('u'))) { // 全量同步:清空后重建 $this->info("\n全量同步文件用户关系..."); - $count = SeekDBFile::syncAllFileUsers(function ($count) { + $count = ManticoreFile::syncAllFileUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -92,7 +92,7 @@ class SyncFileToSeekDB extends Command } elseif ($this->option('i')) { // 增量同步:只同步新增的 $this->info("\n增量同步文件用户关系..."); - $count = SeekDBFile::syncFileUsersIncremental(function ($count) { + $count = ManticoreFile::syncFileUsersIncremental(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -161,8 +161,8 @@ class SyncFileToSeekDB extends Command private function syncFiles(): void { // 获取上次同步的最后ID - $lastKey = "sync:seekdbFileLastId"; - $lastId = $this->option('i') ? intval(SeekDBKeyValue::get($lastKey, 0)) : 0; + $lastKey = "sync:manticoreFileLastId"; + $lastId = $this->option('i') ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; if ($lastId > 0) { $this->info("\n同步文件数据({$lastId})..."); @@ -171,8 +171,8 @@ class SyncFileToSeekDB extends Command } // 查询条件:排除文件夹,使用最大文件限制 - // 具体的文件类型大小检查在 SeekDBFile::sync 中进行 - $maxFileSize = SeekDBFile::getMaxFileSize(); + // 具体的文件类型大小检查在 ManticoreFile::sync 中进行 + $maxFileSize = ManticoreFile::getMaxFileSize(); $query = File::where('id', '>', $lastId) ->where('type', '!=', 'folder') ->where('size', '<=', $maxFileSize); @@ -208,16 +208,16 @@ class SyncFileToSeekDB extends Command $this->setLock(); // 同步数据 - $lastNum = SeekDBFile::batchSync($files); + $lastNum = ManticoreFile::batchSync($files); $total += $lastNum; // 更新最后ID $lastId = $files->last()->id; - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); } while (count($files) == $batchSize); $this->info("同步文件结束 - 最后ID {$lastId}"); - $this->info("已索引文件数量: " . SeekDBFile::getIndexedCount()); + $this->info("已索引文件数量: " . ManticoreFile::getIndexedCount()); } } diff --git a/app/Console/Commands/SyncProjectToSeekDB.php b/app/Console/Commands/SyncProjectToManticore.php similarity index 71% rename from app/Console/Commands/SyncProjectToSeekDB.php rename to app/Console/Commands/SyncProjectToManticore.php index 3f4921675..ad52b03a8 100644 --- a/app/Console/Commands/SyncProjectToSeekDB.php +++ b/app/Console/Commands/SyncProjectToManticore.php @@ -4,12 +4,12 @@ namespace App\Console\Commands; use App\Models\Project; use App\Module\Apps; -use App\Module\SeekDB\SeekDBProject; -use App\Module\SeekDB\SeekDBKeyValue; +use App\Module\Manticore\ManticoreProject; +use App\Module\Manticore\ManticoreKeyValue; use Cache; use Illuminate\Console\Command; -class SyncProjectToSeekDB extends Command +class SyncProjectToManticore extends Command { /** * 更新数据 @@ -21,16 +21,16 @@ class SyncProjectToSeekDB extends Command * --c: 清除索引 */ - protected $signature = 'seekdb:sync-projects {--f} {--i} {--c} {--u} {--batch=100}'; - protected $description = '同步项目数据到 SeekDB'; + protected $signature = 'manticore:sync-projects {--f} {--i} {--c} {--u} {--batch=100}'; + protected $description = '同步项目数据到 Manticore Search'; /** * @return int */ public function handle(): int { - if (!Apps::isInstalled("seekdb")) { - $this->error("应用「SeekDB」未安装"); + if (!Apps::isInstalled("manticore")) { + $this->error("应用「Manticore Search」未安装"); return 1; } @@ -53,8 +53,7 @@ class SyncProjectToSeekDB extends Command // 清除索引 if ($this->option('c')) { $this->info('清除索引...'); - SeekDBProject::clear(); - SeekDBKeyValue::set('sync:seekdbProjectLastId', 0); + ManticoreProject::clear(); $this->info("索引删除成功"); $this->releaseLock(); return 0; @@ -63,7 +62,7 @@ class SyncProjectToSeekDB extends Command // 仅同步项目成员关系 if ($this->option('u')) { $this->info('开始同步项目成员关系...'); - $count = SeekDBProject::syncAllProjectUsers(function ($count) { + $count = ManticoreProject::syncAllProjectUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -78,18 +77,16 @@ class SyncProjectToSeekDB extends Command // 同步项目成员关系 if ($this->option('f') || (!$this->option('i') && !$this->option('u'))) { - // 全量同步:清空后重建 $this->info("\n全量同步项目成员关系..."); - $count = SeekDBProject::syncAllProjectUsers(function ($count) { + $count = ManticoreProject::syncAllProjectUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } }); $this->info("项目成员关系同步完成,共 {$count} 条"); } elseif ($this->option('i')) { - // 增量同步:只同步新增的 $this->info("\n增量同步项目成员关系..."); - $count = SeekDBProject::syncProjectUsersIncremental(function ($count) { + $count = ManticoreProject::syncProjectUsersIncremental(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -113,12 +110,13 @@ class SyncProjectToSeekDB extends Command private function setLock(): void { $lockKey = md5($this->signature); - Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 300); + Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 600); } private function releaseLock(): void { - Cache::forget(md5($this->signature)); + $lockKey = md5($this->signature); + Cache::forget($lockKey); } public function handleSignal(int $signal): void @@ -129,23 +127,25 @@ class SyncProjectToSeekDB extends Command private function syncProjects(): void { - $lastKey = "sync:seekdbProjectLastId"; - $lastId = $this->option('i') ? intval(SeekDBKeyValue::get($lastKey, 0)) : 0; + $lastKey = "sync:manticoreProjectLastId"; + $lastId = $this->option('i') ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; if ($lastId > 0) { - $this->info("\n增量同步项目数据(从ID: {$lastId})..."); + $this->info("\n同步项目数据({$lastId})..."); } else { - $this->info("\n全量同步项目数据..."); + $this->info("\n同步项目数据..."); } - // 只同步未归档的项目 + // 排除已归档项目 $query = Project::where('id', '>', $lastId) ->whereNull('archived_at'); $num = 0; $count = $query->count(); $batchSize = $this->option('batch'); + $total = 0; + $lastNum = 0; do { $projects = Project::where('id', '>', $lastId) @@ -160,19 +160,22 @@ class SyncProjectToSeekDB extends Command $num += count($projects); $progress = $count > 0 ? round($num / $count * 100, 2) : 100; - $this->info("{$num}/{$count} ({$progress}%) 正在同步项目ID {$projects->first()->id} ~ {$projects->last()->id}"); + if ($progress < 100) { + $progress = number_format($progress, 2); + } + $this->info("{$num}/{$count} ({$progress}%) 正在同步项目ID {$projects->first()->id} ~ {$projects->last()->id} ({$total}|{$lastNum})"); $this->setLock(); - $synced = SeekDBProject::batchSync($projects); - $total += $synced; + $lastNum = ManticoreProject::batchSync($projects); + $total += $lastNum; $lastId = $projects->last()->id; - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); } while (count($projects) == $batchSize); - $this->info("同步项目结束 - 最后ID {$lastId},共同步 {$total} 个项目"); - $this->info("已索引项目数量: " . SeekDBProject::getIndexedCount()); + $this->info("同步项目结束 - 最后ID {$lastId}"); + $this->info("已索引项目数量: " . ManticoreProject::getIndexedCount()); } } diff --git a/app/Console/Commands/SyncTaskToSeekDB.php b/app/Console/Commands/SyncTaskToManticore.php similarity index 70% rename from app/Console/Commands/SyncTaskToSeekDB.php rename to app/Console/Commands/SyncTaskToManticore.php index 216346927..ec8862580 100644 --- a/app/Console/Commands/SyncTaskToSeekDB.php +++ b/app/Console/Commands/SyncTaskToManticore.php @@ -4,12 +4,12 @@ namespace App\Console\Commands; use App\Models\ProjectTask; use App\Module\Apps; -use App\Module\SeekDB\SeekDBTask; -use App\Module\SeekDB\SeekDBKeyValue; +use App\Module\Manticore\ManticoreTask; +use App\Module\Manticore\ManticoreKeyValue; use Cache; use Illuminate\Console\Command; -class SyncTaskToSeekDB extends Command +class SyncTaskToManticore extends Command { /** * 更新数据 @@ -21,16 +21,16 @@ class SyncTaskToSeekDB extends Command * --c: 清除索引 */ - protected $signature = 'seekdb:sync-tasks {--f} {--i} {--c} {--u} {--batch=100}'; - protected $description = '同步任务数据到 SeekDB'; + protected $signature = 'manticore:sync-tasks {--f} {--i} {--c} {--u} {--batch=100}'; + protected $description = '同步任务数据到 Manticore Search'; /** * @return int */ public function handle(): int { - if (!Apps::isInstalled("seekdb")) { - $this->error("应用「SeekDB」未安装"); + if (!Apps::isInstalled("manticore")) { + $this->error("应用「Manticore Search」未安装"); return 1; } @@ -53,8 +53,7 @@ class SyncTaskToSeekDB extends Command // 清除索引 if ($this->option('c')) { $this->info('清除索引...'); - SeekDBTask::clear(); - SeekDBKeyValue::set('sync:seekdbTaskLastId', 0); + ManticoreTask::clear(); $this->info("索引删除成功"); $this->releaseLock(); return 0; @@ -63,7 +62,7 @@ class SyncTaskToSeekDB extends Command // 仅同步任务成员关系 if ($this->option('u')) { $this->info('开始同步任务成员关系...'); - $count = SeekDBTask::syncAllTaskUsers(function ($count) { + $count = ManticoreTask::syncAllTaskUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -78,18 +77,16 @@ class SyncTaskToSeekDB extends Command // 同步任务成员关系 if ($this->option('f') || (!$this->option('i') && !$this->option('u'))) { - // 全量同步:清空后重建 $this->info("\n全量同步任务成员关系..."); - $count = SeekDBTask::syncAllTaskUsers(function ($count) { + $count = ManticoreTask::syncAllTaskUsers(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } }); $this->info("任务成员关系同步完成,共 {$count} 条"); } elseif ($this->option('i')) { - // 增量同步:只同步新增的 $this->info("\n增量同步任务成员关系..."); - $count = SeekDBTask::syncTaskUsersIncremental(function ($count) { + $count = ManticoreTask::syncTaskUsersIncremental(function ($count) { if ($count % 1000 === 0) { $this->info(" 已同步 {$count} 条关系..."); } @@ -113,12 +110,13 @@ class SyncTaskToSeekDB extends Command private function setLock(): void { $lockKey = md5($this->signature); - Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 600); // 任务可能较多,10分钟 + Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 600); } private function releaseLock(): void { - Cache::forget(md5($this->signature)); + $lockKey = md5($this->signature); + Cache::forget($lockKey); } public function handleSignal(int $signal): void @@ -129,27 +127,31 @@ class SyncTaskToSeekDB extends Command private function syncTasks(): void { - $lastKey = "sync:seekdbTaskLastId"; - $lastId = $this->option('i') ? intval(SeekDBKeyValue::get($lastKey, 0)) : 0; + $lastKey = "sync:manticoreTaskLastId"; + $lastId = $this->option('i') ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; if ($lastId > 0) { - $this->info("\n增量同步任务数据(从ID: {$lastId})..."); + $this->info("\n同步任务数据({$lastId})..."); } else { - $this->info("\n全量同步任务数据..."); + $this->info("\n同步任务数据..."); } - // 只同步未归档的任务(包括软删除恢复的情况) + // 排除已归档和已删除的任务 $query = ProjectTask::where('id', '>', $lastId) - ->whereNull('archived_at'); + ->whereNull('archived_at') + ->whereNull('deleted_at'); $num = 0; $count = $query->count(); $batchSize = $this->option('batch'); + $total = 0; + $lastNum = 0; do { $tasks = ProjectTask::where('id', '>', $lastId) ->whereNull('archived_at') + ->whereNull('deleted_at') ->orderBy('id') ->limit($batchSize) ->get(); @@ -160,19 +162,22 @@ class SyncTaskToSeekDB extends Command $num += count($tasks); $progress = $count > 0 ? round($num / $count * 100, 2) : 100; - $this->info("{$num}/{$count} ({$progress}%) 正在同步任务ID {$tasks->first()->id} ~ {$tasks->last()->id}"); + if ($progress < 100) { + $progress = number_format($progress, 2); + } + $this->info("{$num}/{$count} ({$progress}%) 正在同步任务ID {$tasks->first()->id} ~ {$tasks->last()->id} ({$total}|{$lastNum})"); $this->setLock(); - $synced = SeekDBTask::batchSync($tasks); - $total += $synced; + $lastNum = ManticoreTask::batchSync($tasks); + $total += $lastNum; $lastId = $tasks->last()->id; - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); } while (count($tasks) == $batchSize); - $this->info("同步任务结束 - 最后ID {$lastId},共同步 {$total} 个任务"); - $this->info("已索引任务数量: " . SeekDBTask::getIndexedCount()); + $this->info("同步任务结束 - 最后ID {$lastId}"); + $this->info("已索引任务数量: " . ManticoreTask::getIndexedCount()); } } diff --git a/app/Console/Commands/SyncUserToSeekDB.php b/app/Console/Commands/SyncUserToManticore.php similarity index 69% rename from app/Console/Commands/SyncUserToSeekDB.php rename to app/Console/Commands/SyncUserToManticore.php index 5b5924512..28a5e7093 100644 --- a/app/Console/Commands/SyncUserToSeekDB.php +++ b/app/Console/Commands/SyncUserToManticore.php @@ -4,12 +4,12 @@ namespace App\Console\Commands; use App\Models\User; use App\Module\Apps; -use App\Module\SeekDB\SeekDBUser; -use App\Module\SeekDB\SeekDBKeyValue; +use App\Module\Manticore\ManticoreUser; +use App\Module\Manticore\ManticoreKeyValue; use Cache; use Illuminate\Console\Command; -class SyncUserToSeekDB extends Command +class SyncUserToManticore extends Command { /** * 更新数据 @@ -20,16 +20,16 @@ class SyncUserToSeekDB extends Command * --c: 清除索引 */ - protected $signature = 'seekdb:sync-users {--f} {--i} {--c} {--batch=100}'; - protected $description = '同步用户数据到 SeekDB(联系人搜索)'; + protected $signature = 'manticore:sync-users {--f} {--i} {--c} {--batch=100}'; + protected $description = '同步用户数据到 Manticore Search'; /** * @return int */ public function handle(): int { - if (!Apps::isInstalled("seekdb")) { - $this->error("应用「SeekDB」未安装"); + if (!Apps::isInstalled("manticore")) { + $this->error("应用「Manticore Search」未安装"); return 1; } @@ -52,8 +52,7 @@ class SyncUserToSeekDB extends Command // 清除索引 if ($this->option('c')) { $this->info('清除索引...'); - SeekDBUser::clear(); - SeekDBKeyValue::set('sync:seekdbUserLastId', 0); + ManticoreUser::clear(); $this->info("索引删除成功"); $this->releaseLock(); return 0; @@ -76,12 +75,13 @@ class SyncUserToSeekDB extends Command private function setLock(): void { $lockKey = md5($this->signature); - Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 300); + Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 600); } private function releaseLock(): void { - Cache::forget(md5($this->signature)); + $lockKey = md5($this->signature); + Cache::forget($lockKey); } public function handleSignal(int $signal): void @@ -92,16 +92,16 @@ class SyncUserToSeekDB extends Command private function syncUsers(): void { - $lastKey = "sync:seekdbUserLastId"; - $lastId = $this->option('i') ? intval(SeekDBKeyValue::get($lastKey, 0)) : 0; + $lastKey = "sync:manticoreUserLastId"; + $lastId = $this->option('i') ? intval(ManticoreKeyValue::get($lastKey, 0)) : 0; if ($lastId > 0) { - $this->info("\n增量同步用户数据(从ID: {$lastId})..."); + $this->info("\n同步用户数据({$lastId})..."); } else { - $this->info("\n全量同步用户数据..."); + $this->info("\n同步用户数据..."); } - // 只同步非机器人且未禁用的用户 + // 排除机器人和已禁用账号 $query = User::where('userid', '>', $lastId) ->where('bot', 0) ->whereNull('disable_at'); @@ -109,7 +109,9 @@ class SyncUserToSeekDB extends Command $num = 0; $count = $query->count(); $batchSize = $this->option('batch'); + $total = 0; + $lastNum = 0; do { $users = User::where('userid', '>', $lastId) @@ -125,19 +127,22 @@ class SyncUserToSeekDB extends Command $num += count($users); $progress = $count > 0 ? round($num / $count * 100, 2) : 100; - $this->info("{$num}/{$count} ({$progress}%) 正在同步用户ID {$users->first()->userid} ~ {$users->last()->userid}"); + if ($progress < 100) { + $progress = number_format($progress, 2); + } + $this->info("{$num}/{$count} ({$progress}%) 正在同步用户ID {$users->first()->userid} ~ {$users->last()->userid} ({$total}|{$lastNum})"); $this->setLock(); - $synced = SeekDBUser::batchSync($users); - $total += $synced; + $lastNum = ManticoreUser::batchSync($users); + $total += $lastNum; $lastId = $users->last()->userid; - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); } while (count($users) == $batchSize); - $this->info("同步用户结束 - 最后ID {$lastId},共同步 {$total} 个用户"); - $this->info("已索引用户数量: " . SeekDBUser::getIndexedCount()); + $this->info("同步用户结束 - 最后ID {$lastId}"); + $this->info("已索引用户数量: " . ManticoreUser::getIndexedCount()); } } diff --git a/app/Http/Controllers/Api/SearchController.php b/app/Http/Controllers/Api/SearchController.php index 1970f5ac4..689171f4f 100644 --- a/app/Http/Controllers/Api/SearchController.php +++ b/app/Http/Controllers/Api/SearchController.php @@ -7,10 +7,10 @@ use App\Models\File; use App\Models\User; use App\Module\Base; use App\Module\Apps; -use App\Module\SeekDB\SeekDBFile; -use App\Module\SeekDB\SeekDBUser; -use App\Module\SeekDB\SeekDBProject; -use App\Module\SeekDB\SeekDBTask; +use App\Module\Manticore\ManticoreFile; +use App\Module\Manticore\ManticoreUser; +use App\Module\Manticore\ManticoreProject; +use App\Module\Manticore\ManticoreTask; /** * @apiDefine search @@ -22,7 +22,7 @@ class SearchController extends AbstractController /** * @api {get} api/search/contact AI 搜索联系人 * - * @apiDescription 需要token身份,需要安装 SeekDB 应用 + * @apiDescription 需要token身份,需要安装 Manticore Search 应用 * @apiVersion 1.0.0 * @apiGroup search * @apiName contact @@ -39,8 +39,8 @@ class SearchController extends AbstractController { User::auth(); - if (!Apps::isInstalled('seekdb')) { - return Base::retError('SeekDB 应用未安装'); + if (!Apps::isInstalled('manticore')) { + return Base::retError('Manticore Search 应用未安装'); } $key = trim(Request::input('key')); @@ -51,7 +51,7 @@ class SearchController extends AbstractController return Base::retSuccess('success', []); } - $results = SeekDBUser::search($key, $searchType, $take); + $results = ManticoreUser::search($key, $searchType, $take); // 补充用户完整信息 $userids = array_column($results, 'userid'); @@ -78,7 +78,7 @@ class SearchController extends AbstractController /** * @api {get} api/search/project AI 搜索项目 * - * @apiDescription 需要token身份,需要安装 SeekDB 应用 + * @apiDescription 需要token身份,需要安装 Manticore Search 应用 * @apiVersion 1.0.0 * @apiGroup search * @apiName project @@ -95,8 +95,8 @@ class SearchController extends AbstractController { $user = User::auth(); - if (!Apps::isInstalled('seekdb')) { - return Base::retError('SeekDB 应用未安装'); + if (!Apps::isInstalled('manticore')) { + return Base::retError('Manticore Search 应用未安装'); } $key = trim(Request::input('key')); @@ -107,7 +107,7 @@ class SearchController extends AbstractController return Base::retSuccess('success', []); } - $results = SeekDBProject::search($user->userid, $key, $searchType, $take); + $results = ManticoreProject::search($user->userid, $key, $searchType, $take); // 补充项目完整信息 $projectIds = array_column($results, 'project_id'); @@ -133,7 +133,7 @@ class SearchController extends AbstractController /** * @api {get} api/search/task AI 搜索任务 * - * @apiDescription 需要token身份,需要安装 SeekDB 应用 + * @apiDescription 需要token身份,需要安装 Manticore Search 应用 * @apiVersion 1.0.0 * @apiGroup search * @apiName task @@ -150,8 +150,8 @@ class SearchController extends AbstractController { $user = User::auth(); - if (!Apps::isInstalled('seekdb')) { - return Base::retError('SeekDB 应用未安装'); + if (!Apps::isInstalled('manticore')) { + return Base::retError('Manticore Search 应用未安装'); } $key = trim(Request::input('key')); @@ -162,7 +162,7 @@ class SearchController extends AbstractController return Base::retSuccess('success', []); } - $results = SeekDBTask::search($user->userid, $key, $searchType, $take); + $results = ManticoreTask::search($user->userid, $key, $searchType, $take); // 补充任务完整信息 $taskIds = array_column($results, 'task_id'); @@ -189,7 +189,7 @@ class SearchController extends AbstractController /** * @api {get} api/search/file AI 搜索文件 * - * @apiDescription 需要token身份,需要安装 SeekDB 应用 + * @apiDescription 需要token身份,需要安装 Manticore Search 应用 * @apiVersion 1.0.0 * @apiGroup search * @apiName file @@ -206,8 +206,8 @@ class SearchController extends AbstractController { $user = User::auth(); - if (!Apps::isInstalled('seekdb')) { - return Base::retError('SeekDB 应用未安装'); + if (!Apps::isInstalled('manticore')) { + return Base::retError('Manticore Search 应用未安装'); } $key = trim(Request::input('key')); @@ -218,7 +218,7 @@ class SearchController extends AbstractController return Base::retSuccess('success', []); } - $results = SeekDBFile::search($user->userid, $key, $searchType, 0, $take); + $results = ManticoreFile::search($user->userid, $key, $searchType, 0, $take); // 补充文件完整信息 $fileIds = array_column($results, 'file_id'); diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index 0169860f0..9393e89f8 100755 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -22,7 +22,7 @@ use App\Tasks\DeleteBotMsgTask; use App\Tasks\CheckinRemindTask; use App\Tasks\CloseMeetingRoomTask; use App\Tasks\ZincSearchSyncTask; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; use App\Tasks\UnclaimedTaskRemindTask; use Hhxsv5\LaravelS\Swoole\Task\Task; use Laravolt\Avatar\Avatar; @@ -274,8 +274,8 @@ class IndexController extends InvokeController Task::deliver(new CloseMeetingRoomTask()); // ZincSearch 同步 Task::deliver(new ZincSearchSyncTask()); - // SeekDB 同步 - Task::deliver(new SeekDBSyncTask()); + // Manticore Search 同步 + Task::deliver(new ManticoreSyncTask()); return "success"; } diff --git a/app/Module/Apps.php b/app/Module/Apps.php index ef29dbd93..75bb1e46c 100644 --- a/app/Module/Apps.php +++ b/app/Module/Apps.php @@ -55,7 +55,7 @@ class Apps 'drawio' => 'Drawio', 'minder' => 'Minder', 'search' => 'ZincSearch', - 'seekdb' => 'SeekDB', + 'manticore' => 'Manticore Search', default => $appId, }; throw new ApiException("应用「{$name}」未安装", [], 0, false); diff --git a/app/Module/SeekDB/SeekDBBase.php b/app/Module/Manticore/ManticoreBase.php similarity index 59% rename from app/Module/SeekDB/SeekDBBase.php rename to app/Module/Manticore/ManticoreBase.php index e529e5389..c7eb3060c 100644 --- a/app/Module/SeekDB/SeekDBBase.php +++ b/app/Module/Manticore/ManticoreBase.php @@ -1,6 +1,6 @@ host = env('SEEKDB_HOST', 'seekdb'); - $this->port = (int) env('SEEKDB_PORT', 2881); - $this->user = env('SEEKDB_USER', 'root'); - $this->pass = env('SEEKDB_PASSWORD', ''); - $this->database = env('SEEKDB_DATABASE', 'dootask_search'); + $this->host = env('MANTICORE_HOST', 'manticore'); + $this->port = (int) env('MANTICORE_PORT', 9306); } /** @@ -41,31 +36,29 @@ class SeekDBBase */ private function getConnection(): ?PDO { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return null; } if (self::$pdo === null) { try { - // 先连接不指定数据库,用于初始化 - $dsn = "mysql:host={$this->host};port={$this->port};charset=utf8mb4"; - $pdo = new PDO($dsn, $this->user, $this->pass, [ + // Manticore 使用 MySQL 协议,不需要用户名密码 + $dsn = "mysql:host={$this->host};port={$this->port}"; + $pdo = new PDO($dsn, '', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_TIMEOUT => 30, ]); - // 初始化数据库和表 + // 初始化表结构 if (!self::$initialized) { - $this->initializeDatabase($pdo); + $this->initializeTables($pdo); self::$initialized = true; } - // 切换到目标数据库 - $pdo->exec("USE `{$this->database}`"); self::$pdo = $pdo; } catch (PDOException $e) { - Log::error('SeekDB connection failed: ' . $e->getMessage()); + Log::error('Manticore connection failed: ' . $e->getMessage()); return null; } } @@ -74,154 +67,108 @@ class SeekDBBase } /** - * 初始化数据库和表结构 + * 初始化表结构 */ - private function initializeDatabase(PDO $pdo): void + private function initializeTables(PDO $pdo): void { try { - // 创建数据库 - $pdo->exec("CREATE DATABASE IF NOT EXISTS `{$this->database}`"); - $pdo->exec("USE `{$this->database}`"); - // 创建文件向量表 $pdo->exec(" CREATE TABLE IF NOT EXISTS file_vectors ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - file_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - pshare BIGINT NOT NULL DEFAULT 0, - file_name VARCHAR(500), - file_type VARCHAR(50), - file_ext VARCHAR(20), - content LONGTEXT, - content_vector VECTOR(1536), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY uk_file_id (file_id), - KEY idx_userid (userid), - KEY idx_pshare (pshare), - FULLTEXT KEY ft_content (file_name, content) - ) + id BIGINT, + file_id BIGINT, + userid BIGINT, + pshare BIGINT, + file_name TEXT, + file_type STRING, + file_ext STRING, + content TEXT, + content_vector float_vector knn_type='hnsw' knn_dims='1536' hnsw_similarity='cosine' + ) charset_table='chinese' morphology='icu_chinese' "); // 创建键值存储表 $pdo->exec(" CREATE TABLE IF NOT EXISTS key_values ( - k VARCHAR(255) PRIMARY KEY, - v TEXT, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + id BIGINT, + k STRING, + v TEXT ) "); // 创建文件用户关系表(用于权限过滤) $pdo->exec(" CREATE TABLE IF NOT EXISTS file_users ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - file_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - permission TINYINT DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY uk_file_user (file_id, userid), - KEY idx_userid (userid), - KEY idx_file_id (file_id) + id BIGINT, + file_id BIGINT, + userid BIGINT, + permission INTEGER ) "); // 创建用户向量表(联系人搜索) $pdo->exec(" CREATE TABLE IF NOT EXISTS user_vectors ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - userid BIGINT NOT NULL, - nickname VARCHAR(200), - email VARCHAR(200), - tel VARCHAR(50), - profession VARCHAR(200), + id BIGINT, + userid BIGINT, + nickname TEXT, + email STRING, + tel STRING, + profession TEXT, introduction TEXT, - content_vector VECTOR(1536), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY uk_userid (userid), - FULLTEXT KEY ft_content (nickname, email, profession, introduction) - ) + content_vector float_vector knn_type='hnsw' knn_dims='1536' hnsw_similarity='cosine' + ) charset_table='chinese' morphology='icu_chinese' "); // 创建项目向量表 $pdo->exec(" CREATE TABLE IF NOT EXISTS project_vectors ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - project_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - personal TINYINT DEFAULT 0, - project_name VARCHAR(500), + id BIGINT, + project_id BIGINT, + userid BIGINT, + personal INTEGER, + project_name TEXT, project_desc TEXT, - content_vector VECTOR(1536), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY uk_project_id (project_id), - KEY idx_userid (userid), - FULLTEXT KEY ft_content (project_name, project_desc) - ) + content_vector float_vector knn_type='hnsw' knn_dims='1536' hnsw_similarity='cosine' + ) charset_table='chinese' morphology='icu_chinese' "); // 创建项目成员表(用于权限过滤) $pdo->exec(" CREATE TABLE IF NOT EXISTS project_users ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - project_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - - UNIQUE KEY uk_project_user (project_id, userid), - KEY idx_project_id (project_id), - KEY idx_userid (userid) + id BIGINT, + project_id BIGINT, + userid BIGINT ) "); // 创建任务向量表 $pdo->exec(" CREATE TABLE IF NOT EXISTS task_vectors ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - task_id BIGINT NOT NULL, - project_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - visibility TINYINT DEFAULT 1, - task_name VARCHAR(500), + id BIGINT, + task_id BIGINT, + project_id BIGINT, + userid BIGINT, + visibility INTEGER, + task_name TEXT, task_desc TEXT, - task_content LONGTEXT, - content_vector VECTOR(1536), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY uk_task_id (task_id), - KEY idx_project_id (project_id), - KEY idx_userid (userid), - KEY idx_visibility (visibility), - FULLTEXT KEY ft_content (task_name, task_desc, task_content) - ) + task_content TEXT, + content_vector float_vector knn_type='hnsw' knn_dims='1536' hnsw_similarity='cosine' + ) charset_table='chinese' morphology='icu_chinese' "); // 创建任务成员表(用于 visibility=2,3 的权限过滤) $pdo->exec(" CREATE TABLE IF NOT EXISTS task_users ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - task_id BIGINT NOT NULL, - userid BIGINT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - - UNIQUE KEY uk_task_user (task_id, userid), - KEY idx_task_id (task_id), - KEY idx_userid (userid) + id BIGINT, + task_id BIGINT, + userid BIGINT ) "); - Log::info('SeekDB database initialized successfully'); + Log::info('Manticore tables initialized successfully'); } catch (PDOException $e) { - Log::warning('SeekDB initialization warning: ' . $e->getMessage()); + Log::warning('Manticore initialization warning: ' . $e->getMessage()); // 不抛出异常,表可能已存在 } } @@ -240,7 +187,7 @@ class SeekDBBase */ public static function isInstalled(): bool { - return Apps::isInstalled("seekdb"); + return Apps::isInstalled("manticore"); } /** @@ -261,7 +208,7 @@ class SeekDBBase $stmt = $pdo->prepare($sql); return $stmt->execute($params); } catch (PDOException $e) { - Log::error('SeekDB execute error: ' . $e->getMessage(), [ + Log::error('Manticore execute error: ' . $e->getMessage(), [ 'sql' => $sql, 'params' => $params ]); @@ -288,7 +235,7 @@ class SeekDBBase $stmt->execute($params); return $stmt->rowCount(); } catch (PDOException $e) { - Log::error('SeekDB execute error: ' . $e->getMessage(), [ + Log::error('Manticore execute error: ' . $e->getMessage(), [ 'sql' => $sql, 'params' => $params ]); @@ -315,7 +262,7 @@ class SeekDBBase $stmt->execute($params); return $stmt->fetchAll(); } catch (PDOException $e) { - Log::error('SeekDB query error: ' . $e->getMessage(), [ + Log::error('Manticore query error: ' . $e->getMessage(), [ 'sql' => $sql, 'params' => $params ]); @@ -343,7 +290,7 @@ class SeekDBBase $result = $stmt->fetch(); return $result ?: null; } catch (PDOException $e) { - Log::error('SeekDB queryOne error: ' . $e->getMessage(), [ + Log::error('Manticore queryOne error: ' . $e->getMessage(), [ 'sql' => $sql, 'params' => $params ]); @@ -352,28 +299,27 @@ class SeekDBBase } /** - * 获取最后插入的 ID + * 转义 Manticore 全文搜索关键词 + * + * @param string $keyword 原始关键词 + * @return string 转义后的关键词 */ - public function lastInsertId(): ?int + public static function escapeMatch(string $keyword): string { - $pdo = $this->getConnection(); - if (!$pdo) { - return null; - } - - try { - return (int) $pdo->lastInsertId(); - } catch (PDOException $e) { - return null; + // Manticore 特殊字符转义 + $special = ['\\', '(', ')', '|', '-', '!', '@', '~', '"', '&', '/', '^', '$', '=', '<', '>', '*']; + foreach ($special as $char) { + $keyword = str_replace($char, '\\' . $char, $keyword); } + return $keyword; } // ============================== - // 静态便捷方法 + // 文件向量相关方法 // ============================== /** - * 全文搜索 + * 全文搜索文件 * * @param string $keyword 关键词 * @param int $userid 用户ID(0表示不限制权限) @@ -388,56 +334,72 @@ class SeekDBBase } $instance = new self(); - $likeKeyword = "%{$keyword}%"; + $escapedKeyword = self::escapeMatch($keyword); - // 构建 SQL - 同时搜索文件名和内容 - // 权限过滤通过 JOIN file_users 表实现 if ($userid > 0) { - // 用户可以看到:1) 自己的文件 2) 共享给自己或公开的文件 - // 注意:pshare 指向共享根文件夹的 ID,file_users 存储的是共享文件夹的权限关系 + // 带权限过滤的搜索 + // 先搜索文件,再通过应用层过滤权限 $sql = " - SELECT DISTINCT - fv.file_id, - fv.userid, - fv.file_name, - fv.file_type, - fv.file_ext, - SUBSTRING(fv.content, 1, 500) as content_preview, - ( - CASE WHEN fv.file_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(fv.content) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance - FROM file_vectors fv - LEFT JOIN file_users fu ON fv.pshare = fu.file_id AND fv.pshare > 0 - WHERE (fv.file_name LIKE ? OR MATCH(fv.content) AGAINST(? IN NATURAL LANGUAGE MODE)) - AND (fv.userid = ? OR fu.userid IN (0, ?)) + SELECT + id, + file_id, + userid, + pshare, + file_name, + file_type, + file_ext, + SUBSTRING(content, 1, 500) as content_preview, + WEIGHT() as relevance + FROM file_vectors + WHERE MATCH('@(file_name,content) {$escapedKeyword}') ORDER BY relevance DESC LIMIT " . (int)$limit . " OFFSET " . (int)$offset; - $params = [$likeKeyword, $keyword, $likeKeyword, $keyword, $userid, $userid]; + $results = $instance->query($sql); + + // 应用层权限过滤:用户自己的文件 或 共享文件 + if (!empty($results)) { + // 获取用户有权限的共享文件夹 + $shareFileIds = $instance->query( + "SELECT file_id FROM file_users WHERE userid IN (0, ?)", + [$userid] + ); + $allowedShares = array_column($shareFileIds, 'file_id'); + + $results = array_filter($results, function ($item) use ($userid, $allowedShares) { + // 自己的文件 + if ($item['userid'] == $userid) { + return true; + } + // 共享文件(pshare 在允许的共享列表中) + if ($item['pshare'] > 0 && in_array($item['pshare'], $allowedShares)) { + return true; + } + return false; + }); + $results = array_values($results); + } + + return $results; } else { - // 不限制权限(管理员或后台) + // 不限制权限 $sql = " SELECT + id, file_id, userid, file_name, file_type, file_ext, SUBSTRING(content, 1, 500) as content_preview, - ( - CASE WHEN file_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(content) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance + WEIGHT() as relevance FROM file_vectors - WHERE file_name LIKE ? OR MATCH(content) AGAINST(? IN NATURAL LANGUAGE MODE) + WHERE MATCH('@(file_name,content) {$escapedKeyword}') ORDER BY relevance DESC LIMIT " . (int)$limit . " OFFSET " . (int)$offset; - $params = [$likeKeyword, $keyword, $likeKeyword, $keyword]; + return $instance->query($sql); } - - return $instance->query($sql, $params); } /** @@ -455,47 +417,52 @@ class SeekDBBase } $instance = new self(); - $vectorStr = '[' . implode(',', $queryVector) . ']'; + $vectorStr = '(' . implode(',', $queryVector) . ')'; - if ($userid > 0) { - // 权限过滤:pshare 指向共享根文件夹的 ID - $sql = " - SELECT DISTINCT - fv.file_id, - fv.userid, - fv.file_name, - fv.file_type, - fv.file_ext, - SUBSTRING(fv.content, 1, 500) as content_preview, - COSINE_SIMILARITY(fv.content_vector, ?) AS similarity - FROM file_vectors fv - LEFT JOIN file_users fu ON fv.pshare = fu.file_id AND fv.pshare > 0 - WHERE fv.content_vector IS NOT NULL - AND (fv.userid = ? OR fu.userid IN (0, ?)) - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $sql = " + SELECT + id, + file_id, + userid, + pshare, + file_name, + file_type, + file_ext, + SUBSTRING(content, 1, 500) as content_preview, + KNN_DIST() as distance + FROM file_vectors + WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) + ORDER BY distance ASC + "; - $params = [$vectorStr, $userid, $userid]; - } else { - // 不限制权限 - $sql = " - SELECT - file_id, - userid, - file_name, - file_type, - file_ext, - SUBSTRING(content, 1, 500) as content_preview, - COSINE_SIMILARITY(content_vector, ?) AS similarity - FROM file_vectors - WHERE content_vector IS NOT NULL - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $results = $instance->query($sql); - $params = [$vectorStr]; + // 转换 distance 为 similarity(1 - distance 用于余弦距离) + foreach ($results as &$item) { + $item['similarity'] = 1 - ($item['distance'] ?? 0); } - return $instance->query($sql, $params); + // 权限过滤 + if ($userid > 0 && !empty($results)) { + $shareFileIds = $instance->query( + "SELECT file_id FROM file_users WHERE userid IN (0, ?)", + [$userid] + ); + $allowedShares = array_column($shareFileIds, 'file_id'); + + $results = array_filter($results, function ($item) use ($userid, $allowedShares) { + if ($item['userid'] == $userid) { + return true; + } + if ($item['pshare'] > 0 && in_array($item['pshare'], $allowedShares)) { + return true; + } + return false; + }); + $results = array_values($results); + } + + return array_slice($results, 0, $limit); } /** @@ -517,7 +484,7 @@ class SeekDBBase float $textWeight = 0.5, float $vectorWeight = 0.5 ): array { - // 分别执行两种搜索(权限过滤在各自方法内通过 JOIN 实现) + // 分别执行两种搜索 $textResults = self::fullTextSearch($keyword, $userid, 50, 0); $vectorResults = !empty($queryVector) ? self::vectorSearch($queryVector, $userid, 50) @@ -578,42 +545,21 @@ class SeekDBBase return false; } - // 检查是否存在 - $existing = $instance->queryOne( - "SELECT id FROM file_vectors WHERE file_id = ?", - [$fileId] - ); + // 先尝试删除已存在的记录 + $instance->execute("DELETE FROM file_vectors WHERE file_id = ?", [$fileId]); - if ($existing) { - // 更新 - $sql = "UPDATE file_vectors SET - userid = ?, - pshare = ?, - file_name = ?, - file_type = ?, - file_ext = ?, - content = ?, - content_vector = ?, - updated_at = NOW() - WHERE file_id = ?"; + // 插入新记录 + $vectorValue = $data['content_vector'] ?? null; + if ($vectorValue) { + // 向量格式转换:从 [1,2,3] 转为 (1,2,3) + $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $params = [ - $data['userid'] ?? 0, - $data['pshare'] ?? 0, - $data['file_name'] ?? '', - $data['file_type'] ?? '', - $data['file_ext'] ?? '', - $data['content'] ?? '', - $data['content_vector'] ?? null, - $fileId - ]; - } else { - // 插入 $sql = "INSERT INTO file_vectors - (file_id, userid, pshare, file_name, file_type, file_ext, content, content_vector, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())"; + (id, file_id, userid, pshare, file_name, file_type, file_ext, content, content_vector) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; $params = [ + $fileId, $fileId, $data['userid'] ?? 0, $data['pshare'] ?? 0, @@ -621,7 +567,22 @@ class SeekDBBase $data['file_type'] ?? '', $data['file_ext'] ?? '', $data['content'] ?? '', - $data['content_vector'] ?? null + $vectorValue + ]; + } else { + $sql = "INSERT INTO file_vectors + (id, file_id, userid, pshare, file_name, file_type, file_ext, content) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + + $params = [ + $fileId, + $fileId, + $data['userid'] ?? 0, + $data['pshare'] ?? 0, + $data['file_name'] ?? '', + $data['file_type'] ?? '', + $data['file_ext'] ?? '', + $data['content'] ?? '' ]; } @@ -641,10 +602,7 @@ class SeekDBBase } $instance = new self(); - return $instance->execute( - "DELETE FROM file_vectors WHERE file_id = ?", - [$fileId] - ); + return $instance->execute("DELETE FROM file_vectors WHERE file_id = ?", [$fileId]); } /** @@ -660,11 +618,10 @@ class SeekDBBase } $instance = new self(); - $placeholders = implode(',', array_fill(0, count($fileIds), '?')); + $placeholders = implode(',', array_map('intval', $fileIds)); return $instance->executeWithRowCount( - "DELETE FROM file_vectors WHERE file_id IN ({$placeholders})", - $fileIds + "DELETE FROM file_vectors WHERE file_id IN ({$placeholders})" ); } @@ -681,14 +638,19 @@ class SeekDBBase return 0; } + // Manticore 不支持批量 UPDATE,需要逐个更新 $instance = new self(); - $placeholders = implode(',', array_fill(0, count($fileIds), '?')); - $params = array_merge([$pshare], $fileIds); - - return $instance->executeWithRowCount( - "UPDATE file_vectors SET pshare = ?, updated_at = NOW() WHERE file_id IN ({$placeholders})", - $params - ); + $count = 0; + foreach ($fileIds as $fileId) { + $result = $instance->execute( + "UPDATE file_vectors SET pshare = ? WHERE file_id = ?", + [$pshare, (int)$fileId] + ); + if ($result) { + $count++; + } + } + return $count; } /** @@ -746,25 +708,18 @@ class SeekDBBase $instance = new self(); - // 检查是否存在 - $existing = $instance->queryOne( - "SELECT id FROM file_users WHERE file_id = ? AND userid = ?", + // 先删除已存在的记录 + $instance->execute( + "DELETE FROM file_users WHERE file_id = ? AND userid = ?", [$fileId, $userid] ); - if ($existing) { - // 更新 - return $instance->execute( - "UPDATE file_users SET permission = ?, updated_at = NOW() WHERE file_id = ? AND userid = ?", - [$permission, $fileId, $userid] - ); - } else { - // 插入 - return $instance->execute( - "INSERT INTO file_users (file_id, userid, permission) VALUES (?, ?, ?)", - [$fileId, $userid, $permission] - ); - } + // 插入新记录 + $id = $fileId * 1000000 + $userid; // 生成唯一 ID + return $instance->execute( + "INSERT INTO file_users (id, file_id, userid, permission) VALUES (?, ?, ?, ?)", + [$id, $fileId, $userid, $permission] + ); } /** @@ -790,15 +745,16 @@ class SeekDBBase foreach ($users as $user) { $userid = (int)($user['userid'] ?? 0); $permission = (int)($user['permission'] ?? 0); + $id = $fileId * 1000000 + $userid; $instance->execute( - "INSERT INTO file_users (file_id, userid, permission) VALUES (?, ?, ?)", - [$fileId, $userid, $permission] + "INSERT INTO file_users (id, file_id, userid, permission) VALUES (?, ?, ?, ?)", + [$id, $fileId, $userid, $permission] ); } return true; } catch (\Exception $e) { - Log::error('SeekDB syncFileUsers error: ' . $e->getMessage()); + Log::error('Manticore syncFileUsers error: ' . $e->getMessage()); return false; } } @@ -881,30 +837,24 @@ class SeekDBBase } $instance = new self(); - $likeKeyword = "%{$keyword}%"; + $escapedKeyword = self::escapeMatch($keyword); $sql = " SELECT + id, userid, nickname, email, tel, profession, SUBSTRING(introduction, 1, 200) as introduction_preview, - ( - CASE WHEN nickname LIKE ? THEN 10 ELSE 0 END + - CASE WHEN email LIKE ? THEN 5 ELSE 0 END + - IFNULL(MATCH(nickname, email, profession, introduction) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance + WEIGHT() as relevance FROM user_vectors - WHERE nickname LIKE ? OR email LIKE ? OR profession LIKE ? - OR MATCH(nickname, email, profession, introduction) AGAINST(? IN NATURAL LANGUAGE MODE) + WHERE MATCH('@(nickname,email,profession,introduction) {$escapedKeyword}') ORDER BY relevance DESC LIMIT " . (int)$limit . " OFFSET " . (int)$offset; - $params = [$likeKeyword, $likeKeyword, $keyword, $likeKeyword, $likeKeyword, $likeKeyword, $keyword]; - - return $instance->query($sql, $params); + return $instance->query($sql); } /** @@ -921,23 +871,31 @@ class SeekDBBase } $instance = new self(); - $vectorStr = '[' . implode(',', $queryVector) . ']'; + $vectorStr = '(' . implode(',', $queryVector) . ')'; $sql = " SELECT + id, userid, nickname, email, tel, profession, SUBSTRING(introduction, 1, 200) as introduction_preview, - COSINE_SIMILARITY(content_vector, ?) AS similarity + KNN_DIST() as distance FROM user_vectors - WHERE content_vector IS NOT NULL - ORDER BY similarity DESC - LIMIT " . (int)$limit; + WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) + ORDER BY distance ASC + "; - return $instance->query($sql, [$vectorStr]); + $results = $instance->query($sql); + + // 转换 distance 为 similarity + foreach ($results as &$item) { + $item['similarity'] = 1 - ($item['distance'] ?? 0); + } + + return $results; } /** @@ -1002,41 +960,41 @@ class SeekDBBase return false; } - $existing = $instance->queryOne("SELECT id FROM user_vectors WHERE userid = ?", [$userid]); + // 先删除已存在的记录 + $instance->execute("DELETE FROM user_vectors WHERE userid = ?", [$userid]); - if ($existing) { - $sql = "UPDATE user_vectors SET - nickname = ?, - email = ?, - tel = ?, - profession = ?, - introduction = ?, - content_vector = ?, - updated_at = NOW() - WHERE userid = ?"; + // 插入新记录 + $vectorValue = $data['content_vector'] ?? null; + if ($vectorValue) { + $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $params = [ - $data['nickname'] ?? '', - $data['email'] ?? '', - $data['tel'] ?? '', - $data['profession'] ?? '', - $data['introduction'] ?? '', - $data['content_vector'] ?? null, - $userid - ]; - } else { $sql = "INSERT INTO user_vectors - (userid, nickname, email, tel, profession, introduction, content_vector, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())"; + (id, userid, nickname, email, tel, profession, introduction, content_vector) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; $params = [ + $userid, $userid, $data['nickname'] ?? '', $data['email'] ?? '', $data['tel'] ?? '', $data['profession'] ?? '', $data['introduction'] ?? '', - $data['content_vector'] ?? null + $vectorValue + ]; + } else { + $sql = "INSERT INTO user_vectors + (id, userid, nickname, email, tel, profession, introduction) + VALUES (?, ?, ?, ?, ?, ?, ?)"; + + $params = [ + $userid, + $userid, + $data['nickname'] ?? '', + $data['email'] ?? '', + $data['tel'] ?? '', + $data['profession'] ?? '', + $data['introduction'] ?? '' ]; } @@ -1102,51 +1060,39 @@ class SeekDBBase } $instance = new self(); - $likeKeyword = "%{$keyword}%"; + $escapedKeyword = self::escapeMatch($keyword); - if ($userid > 0) { - // 权限过滤:只搜索用户参与的项目 - $sql = " - SELECT DISTINCT - pv.project_id, - pv.userid, - pv.personal, - pv.project_name, - SUBSTRING(pv.project_desc, 1, 300) as project_desc_preview, - ( - CASE WHEN pv.project_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(pv.project_name, pv.project_desc) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance - FROM project_vectors pv - JOIN project_users pu ON pv.project_id = pu.project_id - WHERE (pv.project_name LIKE ? OR MATCH(pv.project_name, pv.project_desc) AGAINST(? IN NATURAL LANGUAGE MODE)) - AND pu.userid = ? - ORDER BY relevance DESC - LIMIT " . (int)$limit . " OFFSET " . (int)$offset; + $sql = " + SELECT + id, + project_id, + userid, + personal, + project_name, + SUBSTRING(project_desc, 1, 300) as project_desc_preview, + WEIGHT() as relevance + FROM project_vectors + WHERE MATCH('@(project_name,project_desc) {$escapedKeyword}') + ORDER BY relevance DESC + LIMIT " . (int)$limit . " OFFSET " . (int)$offset; - $params = [$likeKeyword, $keyword, $likeKeyword, $keyword, $userid]; - } else { - // 不限制权限 - $sql = " - SELECT - project_id, - userid, - personal, - project_name, - SUBSTRING(project_desc, 1, 300) as project_desc_preview, - ( - CASE WHEN project_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(project_name, project_desc) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance - FROM project_vectors - WHERE project_name LIKE ? OR MATCH(project_name, project_desc) AGAINST(? IN NATURAL LANGUAGE MODE) - ORDER BY relevance DESC - LIMIT " . (int)$limit . " OFFSET " . (int)$offset; + $results = $instance->query($sql); - $params = [$likeKeyword, $keyword, $likeKeyword, $keyword]; + // 权限过滤 + if ($userid > 0 && !empty($results)) { + $memberProjects = $instance->query( + "SELECT project_id FROM project_users WHERE userid = ?", + [$userid] + ); + $allowedProjects = array_column($memberProjects, 'project_id'); + + $results = array_filter($results, function ($item) use ($allowedProjects) { + return in_array($item['project_id'], $allowedProjects); + }); + $results = array_values($results); } - return $instance->query($sql, $params); + return $results; } /** @@ -1164,42 +1110,43 @@ class SeekDBBase } $instance = new self(); - $vectorStr = '[' . implode(',', $queryVector) . ']'; + $vectorStr = '(' . implode(',', $queryVector) . ')'; - if ($userid > 0) { - $sql = " - SELECT DISTINCT - pv.project_id, - pv.userid, - pv.personal, - pv.project_name, - SUBSTRING(pv.project_desc, 1, 300) as project_desc_preview, - COSINE_SIMILARITY(pv.content_vector, ?) AS similarity - FROM project_vectors pv - JOIN project_users pu ON pv.project_id = pu.project_id - WHERE pv.content_vector IS NOT NULL AND pu.userid = ? - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $sql = " + SELECT + id, + project_id, + userid, + personal, + project_name, + SUBSTRING(project_desc, 1, 300) as project_desc_preview, + KNN_DIST() as distance + FROM project_vectors + WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) + ORDER BY distance ASC + "; - $params = [$vectorStr, $userid]; - } else { - $sql = " - SELECT - project_id, - userid, - personal, - project_name, - SUBSTRING(project_desc, 1, 300) as project_desc_preview, - COSINE_SIMILARITY(content_vector, ?) AS similarity - FROM project_vectors - WHERE content_vector IS NOT NULL - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $results = $instance->query($sql); - $params = [$vectorStr]; + foreach ($results as &$item) { + $item['similarity'] = 1 - ($item['distance'] ?? 0); } - return $instance->query($sql, $params); + // 权限过滤 + if ($userid > 0 && !empty($results)) { + $memberProjects = $instance->query( + "SELECT project_id FROM project_users WHERE userid = ?", + [$userid] + ); + $allowedProjects = array_column($memberProjects, 'project_id'); + + $results = array_filter($results, function ($item) use ($allowedProjects) { + return in_array($item['project_id'], $allowedProjects); + }); + $results = array_values($results); + } + + return array_slice($results, 0, $limit); } /** @@ -1216,7 +1163,6 @@ class SeekDBBase $textResults = self::projectFullTextSearch($keyword, $userid, 50, 0); $vectorResults = !empty($queryVector) ? self::projectVectorSearch($queryVector, $userid, 50) : []; - // RRF 融合 $scores = []; $items = []; $k = 60; @@ -1265,38 +1211,39 @@ class SeekDBBase return false; } - $existing = $instance->queryOne("SELECT id FROM project_vectors WHERE project_id = ?", [$projectId]); + // 先删除已存在的记录 + $instance->execute("DELETE FROM project_vectors WHERE project_id = ?", [$projectId]); - if ($existing) { - $sql = "UPDATE project_vectors SET - userid = ?, - personal = ?, - project_name = ?, - project_desc = ?, - content_vector = ?, - updated_at = NOW() - WHERE project_id = ?"; + // 插入新记录 + $vectorValue = $data['content_vector'] ?? null; + if ($vectorValue) { + $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $params = [ - $data['userid'] ?? 0, - $data['personal'] ?? 0, - $data['project_name'] ?? '', - $data['project_desc'] ?? '', - $data['content_vector'] ?? null, - $projectId - ]; - } else { $sql = "INSERT INTO project_vectors - (project_id, userid, personal, project_name, project_desc, content_vector, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())"; + (id, project_id, userid, personal, project_name, project_desc, content_vector) + VALUES (?, ?, ?, ?, ?, ?, ?)"; $params = [ + $projectId, $projectId, $data['userid'] ?? 0, $data['personal'] ?? 0, $data['project_name'] ?? '', $data['project_desc'] ?? '', - $data['content_vector'] ?? null + $vectorValue + ]; + } else { + $sql = "INSERT INTO project_vectors + (id, project_id, userid, personal, project_name, project_desc) + VALUES (?, ?, ?, ?, ?, ?)"; + + $params = [ + $projectId, + $projectId, + $data['userid'] ?? 0, + $data['personal'] ?? 0, + $data['project_name'] ?? '', + $data['project_desc'] ?? '' ]; } @@ -1361,18 +1308,17 @@ class SeekDBBase $instance = new self(); - $existing = $instance->queryOne( - "SELECT id FROM project_users WHERE project_id = ? AND userid = ?", + // 先删除已存在的记录 + $instance->execute( + "DELETE FROM project_users WHERE project_id = ? AND userid = ?", [$projectId, $userid] ); - if ($existing) { - return true; // 已存在 - } - + // 插入新记录 + $id = $projectId * 1000000 + $userid; return $instance->execute( - "INSERT INTO project_users (project_id, userid) VALUES (?, ?)", - [$projectId, $userid] + "INSERT INTO project_users (id, project_id, userid) VALUES (?, ?, ?)", + [$id, $projectId, $userid] ); } @@ -1431,15 +1377,16 @@ class SeekDBBase $instance->execute("DELETE FROM project_users WHERE project_id = ?", [$projectId]); foreach ($userids as $userid) { + $id = $projectId * 1000000 + (int)$userid; $instance->execute( - "INSERT INTO project_users (project_id, userid) VALUES (?, ?)", - [$projectId, (int)$userid] + "INSERT INTO project_users (id, project_id, userid) VALUES (?, ?, ?)", + [$id, $projectId, (int)$userid] ); } return true; } catch (\Exception $e) { - Log::error('SeekDB syncProjectUsers error: ' . $e->getMessage()); + Log::error('Manticore syncProjectUsers error: ' . $e->getMessage()); return false; } } @@ -1487,63 +1434,61 @@ class SeekDBBase } $instance = new self(); - $likeKeyword = "%{$keyword}%"; + $escapedKeyword = self::escapeMatch($keyword); - if ($userid > 0) { - // 复杂权限过滤: - // 1. 自己创建的任务 - // 2. visibility=1 且是项目成员 - // 3. visibility=2,3 且是任务成员 - $sql = " - SELECT DISTINCT - tv.task_id, - tv.project_id, - tv.userid, - tv.visibility, - tv.task_name, - SUBSTRING(tv.task_desc, 1, 300) as task_desc_preview, - SUBSTRING(tv.task_content, 1, 500) as task_content_preview, - ( - CASE WHEN tv.task_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(tv.task_name, tv.task_desc, tv.task_content) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance - FROM task_vectors tv - LEFT JOIN project_users pu ON tv.project_id = pu.project_id AND pu.userid = ? - LEFT JOIN task_users tu ON tv.task_id = tu.task_id AND tu.userid = ? - WHERE (tv.task_name LIKE ? OR MATCH(tv.task_name, tv.task_desc, tv.task_content) AGAINST(? IN NATURAL LANGUAGE MODE)) - AND ( - tv.userid = ? - OR (tv.visibility = 1 AND pu.userid IS NOT NULL) - OR (tv.visibility IN (2, 3) AND tu.userid IS NOT NULL) - ) - ORDER BY relevance DESC - LIMIT " . (int)$limit . " OFFSET " . (int)$offset; + $sql = " + SELECT + id, + task_id, + project_id, + userid, + visibility, + task_name, + SUBSTRING(task_desc, 1, 300) as task_desc_preview, + SUBSTRING(task_content, 1, 500) as task_content_preview, + WEIGHT() as relevance + FROM task_vectors + WHERE MATCH('@(task_name,task_desc,task_content) {$escapedKeyword}') + ORDER BY relevance DESC + LIMIT " . (int)$limit . " OFFSET " . (int)$offset; - $params = [$likeKeyword, $keyword, $userid, $userid, $likeKeyword, $keyword, $userid]; - } else { - // 不限制权限 - $sql = " - SELECT - task_id, - project_id, - userid, - visibility, - task_name, - SUBSTRING(task_desc, 1, 300) as task_desc_preview, - SUBSTRING(task_content, 1, 500) as task_content_preview, - ( - CASE WHEN task_name LIKE ? THEN 10 ELSE 0 END + - IFNULL(MATCH(task_name, task_desc, task_content) AGAINST(? IN NATURAL LANGUAGE MODE), 0) - ) AS relevance - FROM task_vectors - WHERE task_name LIKE ? OR MATCH(task_name, task_desc, task_content) AGAINST(? IN NATURAL LANGUAGE MODE) - ORDER BY relevance DESC - LIMIT " . (int)$limit . " OFFSET " . (int)$offset; + $results = $instance->query($sql); - $params = [$likeKeyword, $keyword, $likeKeyword, $keyword]; + // 权限过滤 + if ($userid > 0 && !empty($results)) { + // 获取用户参与的项目 + $memberProjects = $instance->query( + "SELECT project_id FROM project_users WHERE userid = ?", + [$userid] + ); + $allowedProjects = array_column($memberProjects, 'project_id'); + + // 获取用户参与的任务 + $memberTasks = $instance->query( + "SELECT task_id FROM task_users WHERE userid = ?", + [$userid] + ); + $allowedTasks = array_column($memberTasks, 'task_id'); + + $results = array_filter($results, function ($item) use ($userid, $allowedProjects, $allowedTasks) { + // 自己创建的任务 + if ($item['userid'] == $userid) { + return true; + } + // visibility=1 且是项目成员 + if ($item['visibility'] == 1 && in_array($item['project_id'], $allowedProjects)) { + return true; + } + // visibility=2,3 且是任务成员 + if (in_array($item['visibility'], [2, 3]) && in_array($item['task_id'], $allowedTasks)) { + return true; + } + return false; + }); + $results = array_values($results); } - return $instance->query($sql, $params); + return $results; } /** @@ -1561,52 +1506,60 @@ class SeekDBBase } $instance = new self(); - $vectorStr = '[' . implode(',', $queryVector) . ']'; + $vectorStr = '(' . implode(',', $queryVector) . ')'; - if ($userid > 0) { - $sql = " - SELECT DISTINCT - tv.task_id, - tv.project_id, - tv.userid, - tv.visibility, - tv.task_name, - SUBSTRING(tv.task_desc, 1, 300) as task_desc_preview, - SUBSTRING(tv.task_content, 1, 500) as task_content_preview, - COSINE_SIMILARITY(tv.content_vector, ?) AS similarity - FROM task_vectors tv - LEFT JOIN project_users pu ON tv.project_id = pu.project_id AND pu.userid = ? - LEFT JOIN task_users tu ON tv.task_id = tu.task_id AND tu.userid = ? - WHERE tv.content_vector IS NOT NULL - AND ( - tv.userid = ? - OR (tv.visibility = 1 AND pu.userid IS NOT NULL) - OR (tv.visibility IN (2, 3) AND tu.userid IS NOT NULL) - ) - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $sql = " + SELECT + id, + task_id, + project_id, + userid, + visibility, + task_name, + SUBSTRING(task_desc, 1, 300) as task_desc_preview, + SUBSTRING(task_content, 1, 500) as task_content_preview, + KNN_DIST() as distance + FROM task_vectors + WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) + ORDER BY distance ASC + "; - $params = [$vectorStr, $userid, $userid, $userid]; - } else { - $sql = " - SELECT - task_id, - project_id, - userid, - visibility, - task_name, - SUBSTRING(task_desc, 1, 300) as task_desc_preview, - SUBSTRING(task_content, 1, 500) as task_content_preview, - COSINE_SIMILARITY(content_vector, ?) AS similarity - FROM task_vectors - WHERE content_vector IS NOT NULL - ORDER BY similarity DESC - LIMIT " . (int)$limit; + $results = $instance->query($sql); - $params = [$vectorStr]; + foreach ($results as &$item) { + $item['similarity'] = 1 - ($item['distance'] ?? 0); } - return $instance->query($sql, $params); + // 权限过滤 + if ($userid > 0 && !empty($results)) { + $memberProjects = $instance->query( + "SELECT project_id FROM project_users WHERE userid = ?", + [$userid] + ); + $allowedProjects = array_column($memberProjects, 'project_id'); + + $memberTasks = $instance->query( + "SELECT task_id FROM task_users WHERE userid = ?", + [$userid] + ); + $allowedTasks = array_column($memberTasks, 'task_id'); + + $results = array_filter($results, function ($item) use ($userid, $allowedProjects, $allowedTasks) { + if ($item['userid'] == $userid) { + return true; + } + if ($item['visibility'] == 1 && in_array($item['project_id'], $allowedProjects)) { + return true; + } + if (in_array($item['visibility'], [2, 3]) && in_array($item['task_id'], $allowedTasks)) { + return true; + } + return false; + }); + $results = array_values($results); + } + + return array_slice($results, 0, $limit); } /** @@ -1623,7 +1576,6 @@ class SeekDBBase $textResults = self::taskFullTextSearch($keyword, $userid, 50, 0); $vectorResults = !empty($queryVector) ? self::taskVectorSearch($queryVector, $userid, 50) : []; - // RRF 融合 $scores = []; $items = []; $k = 60; @@ -1672,36 +1624,20 @@ class SeekDBBase return false; } - $existing = $instance->queryOne("SELECT id FROM task_vectors WHERE task_id = ?", [$taskId]); + // 先删除已存在的记录 + $instance->execute("DELETE FROM task_vectors WHERE task_id = ?", [$taskId]); - if ($existing) { - $sql = "UPDATE task_vectors SET - project_id = ?, - userid = ?, - visibility = ?, - task_name = ?, - task_desc = ?, - task_content = ?, - content_vector = ?, - updated_at = NOW() - WHERE task_id = ?"; + // 插入新记录 + $vectorValue = $data['content_vector'] ?? null; + if ($vectorValue) { + $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $params = [ - $data['project_id'] ?? 0, - $data['userid'] ?? 0, - $data['visibility'] ?? 1, - $data['task_name'] ?? '', - $data['task_desc'] ?? '', - $data['task_content'] ?? '', - $data['content_vector'] ?? null, - $taskId - ]; - } else { $sql = "INSERT INTO task_vectors - (task_id, project_id, userid, visibility, task_name, task_desc, task_content, content_vector, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())"; + (id, task_id, project_id, userid, visibility, task_name, task_desc, task_content, content_vector) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; $params = [ + $taskId, $taskId, $data['project_id'] ?? 0, $data['userid'] ?? 0, @@ -1709,7 +1645,22 @@ class SeekDBBase $data['task_name'] ?? '', $data['task_desc'] ?? '', $data['task_content'] ?? '', - $data['content_vector'] ?? null + $vectorValue + ]; + } else { + $sql = "INSERT INTO task_vectors + (id, task_id, project_id, userid, visibility, task_name, task_desc, task_content) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + + $params = [ + $taskId, + $taskId, + $data['project_id'] ?? 0, + $data['userid'] ?? 0, + $data['visibility'] ?? 1, + $data['task_name'] ?? '', + $data['task_desc'] ?? '', + $data['task_content'] ?? '' ]; } @@ -1731,7 +1682,7 @@ class SeekDBBase $instance = new self(); return $instance->execute( - "UPDATE task_vectors SET visibility = ?, updated_at = NOW() WHERE task_id = ?", + "UPDATE task_vectors SET visibility = ? WHERE task_id = ?", [$visibility, $taskId] ); } @@ -1794,18 +1745,17 @@ class SeekDBBase $instance = new self(); - $existing = $instance->queryOne( - "SELECT id FROM task_users WHERE task_id = ? AND userid = ?", + // 先删除已存在的记录 + $instance->execute( + "DELETE FROM task_users WHERE task_id = ? AND userid = ?", [$taskId, $userid] ); - if ($existing) { - return true; - } - + // 插入新记录 + $id = $taskId * 1000000 + $userid; return $instance->execute( - "INSERT INTO task_users (task_id, userid) VALUES (?, ?)", - [$taskId, $userid] + "INSERT INTO task_users (id, task_id, userid) VALUES (?, ?, ?)", + [$id, $taskId, $userid] ); } @@ -1864,15 +1814,16 @@ class SeekDBBase $instance->execute("DELETE FROM task_users WHERE task_id = ?", [$taskId]); foreach ($userids as $userid) { + $id = $taskId * 1000000 + (int)$userid; $instance->execute( - "INSERT INTO task_users (task_id, userid) VALUES (?, ?)", - [$taskId, (int)$userid] + "INSERT INTO task_users (id, task_id, userid) VALUES (?, ?, ?)", + [$id, $taskId, (int)$userid] ); } return true; } catch (\Exception $e) { - Log::error('SeekDB syncTaskUsers error: ' . $e->getMessage()); + Log::error('Manticore syncTaskUsers error: ' . $e->getMessage()); return false; } } diff --git a/app/Module/SeekDB/SeekDBFile.php b/app/Module/Manticore/ManticoreFile.php similarity index 83% rename from app/Module/SeekDB/SeekDBFile.php rename to app/Module/Manticore/ManticoreFile.php index 1aded75c6..3c8557a57 100644 --- a/app/Module/SeekDB/SeekDBFile.php +++ b/app/Module/Manticore/ManticoreFile.php @@ -1,6 +1,6 @@ getMessage()); + Log::error('Manticore search error: ' . $e->getMessage()); return self::searchByMysql($userid, $keyword, $from, $size); } } @@ -152,7 +151,7 @@ class SeekDBFile /** * 格式化搜索结果 * - * @param array $results SeekDB 返回的结果 + * @param array $results Manticore 返回的结果 * @return array 格式化后的结果 */ private static function formatSearchResults(array $results): array @@ -210,14 +209,14 @@ class SeekDBFile // ============================== /** - * 同步单个文件到 SeekDB + * 同步单个文件到 Manticore * * @param File $file 文件模型 * @return bool 是否成功 */ public static function sync(File $file): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } @@ -229,7 +228,7 @@ class SeekDBFile // 根据文件类型检查大小限制 $maxSize = self::getMaxFileSizeByExt($file->ext); if ($file->size > $maxSize) { - Log::info("SeekDB: Skip large file {$file->id} ({$file->size} bytes, max: {$maxSize})"); + Log::info("Manticore: Skip large file {$file->id} ({$file->size} bytes, max: {$maxSize})"); return true; } @@ -249,9 +248,8 @@ class SeekDBFile } } - // 写入 SeekDB - // pshare 指向共享根文件夹的 ID,用于权限过滤 - $result = SeekDBBase::upsertFileVector([ + // 写入 Manticore + $result = ManticoreBase::upsertFileVector([ 'file_id' => $file->id, 'userid' => $file->userid, 'pshare' => $file->pshare ?? 0, @@ -262,12 +260,9 @@ class SeekDBFile 'content_vector' => $embedding, ]); - // 注意:file_users 只需要同步共享文件夹的关系,不需要同步每个文件 - // 因为搜索时是通过 pshare 关联 file_users 表 - return $result; } catch (\Exception $e) { - Log::error('SeekDB sync error: ' . $e->getMessage(), [ + Log::error('Manticore sync error: ' . $e->getMessage(), [ 'file_id' => $file->id, 'file_name' => $file->name, ]); @@ -314,7 +309,7 @@ class SeekDBFile */ public static function batchSync(iterable $files): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -335,11 +330,11 @@ class SeekDBFile */ public static function delete(int $fileId): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - return SeekDBBase::deleteFileVector($fileId); + return ManticoreBase::deleteFileVector($fileId); } /** @@ -397,11 +392,11 @@ class SeekDBFile */ public static function clear(): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - return SeekDBBase::clearAllFileVectors(); + return ManticoreBase::clearAllFileVectors(); } /** @@ -411,11 +406,11 @@ class SeekDBFile */ public static function getIndexedCount(): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } - return SeekDBBase::getIndexedFileCount(); + return ManticoreBase::getIndexedFileCount(); } // ============================== @@ -423,14 +418,14 @@ class SeekDBFile // ============================== /** - * 同步单个文件的用户关系到 SeekDB + * 同步单个文件的用户关系到 Manticore * * @param int $fileId 文件ID * @return bool 是否成功 */ public static function syncFileUsers(int $fileId): bool { - if (!Apps::isInstalled("seekdb") || $fileId <= 0) { + if (!Apps::isInstalled("manticore") || $fileId <= 0) { return false; } @@ -447,16 +442,16 @@ class SeekDBFile }) ->toArray(); - // 同步到 SeekDB - return SeekDBBase::syncFileUsers($fileId, $users); + // 同步到 Manticore + return ManticoreBase::syncFileUsers($fileId, $users); } catch (\Exception $e) { - Log::error('SeekDB syncFileUsers error: ' . $e->getMessage(), ['file_id' => $fileId]); + Log::error('Manticore syncFileUsers error: ' . $e->getMessage(), ['file_id' => $fileId]); return false; } } /** - * 添加文件用户关系到 SeekDB + * 添加文件用户关系到 Manticore * * @param int $fileId 文件ID * @param int $userid 用户ID @@ -465,11 +460,11 @@ class SeekDBFile */ public static function addFileUser(int $fileId, int $userid, int $permission = 0): bool { - if (!Apps::isInstalled("seekdb") || $fileId <= 0) { + if (!Apps::isInstalled("manticore") || $fileId <= 0) { return false; } - return SeekDBBase::upsertFileUser($fileId, $userid, $permission); + return ManticoreBase::upsertFileUser($fileId, $userid, $permission); } /** @@ -481,15 +476,15 @@ class SeekDBFile */ public static function removeFileUser(int $fileId, ?int $userid = null): bool { - if (!Apps::isInstalled("seekdb") || $fileId <= 0) { + if (!Apps::isInstalled("manticore") || $fileId <= 0) { return false; } if ($userid === null) { - return SeekDBBase::deleteFileUsers($fileId); + return ManticoreBase::deleteFileUsers($fileId); } - return SeekDBBase::deleteFileUser($fileId, $userid); + return ManticoreBase::deleteFileUser($fileId, $userid); } /** @@ -500,7 +495,7 @@ class SeekDBFile */ public static function syncAllFileUsers(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -508,8 +503,8 @@ class SeekDBFile $lastId = 0; $batchSize = 1000; - // 先清空 SeekDB 中的 file_users 表 - SeekDBBase::clearAllFileUsers(); + // 先清空 Manticore 中的 file_users 表 + ManticoreBase::clearAllFileUsers(); // 分批同步 while (true) { @@ -523,7 +518,7 @@ class SeekDBFile } foreach ($records as $record) { - SeekDBBase::upsertFileUser($record->file_id, $record->userid, $record->permission); + ManticoreBase::upsertFileUser($record->file_id, $record->userid, $record->permission); $count++; $lastId = $record->id; } @@ -544,14 +539,14 @@ class SeekDBFile */ public static function syncFileUsersIncremental(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } $count = 0; $batchSize = 1000; - $lastKey = "sync:seekdbFileUserLastId"; - $lastId = intval(SeekDBKeyValue::get($lastKey, 0)); + $lastKey = "sync:manticoreFileUserLastId"; + $lastId = intval(ManticoreKeyValue::get($lastKey, 0)); // 分批同步新增的记录 while (true) { @@ -565,13 +560,13 @@ class SeekDBFile } foreach ($records as $record) { - SeekDBBase::upsertFileUser($record->file_id, $record->userid, $record->permission); + ManticoreBase::upsertFileUser($record->file_id, $record->userid, $record->permission); $count++; $lastId = $record->id; } // 保存进度 - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); if ($progressCallback) { $progressCallback($count); diff --git a/app/Module/Manticore/ManticoreKeyValue.php b/app/Module/Manticore/ManticoreKeyValue.php new file mode 100644 index 000000000..54a485950 --- /dev/null +++ b/app/Module/Manticore/ManticoreKeyValue.php @@ -0,0 +1,139 @@ +queryOne( + "SELECT v FROM key_values WHERE k = ?", + [$key] + ); + + return $result ? $result['v'] : $default; + } + + /** + * 设置值 + * + * @param string $key 键 + * @param mixed $value 值 + * @return bool 是否成功 + */ + public static function set(string $key, $value): bool + { + if (!Apps::isInstalled("manticore")) { + return false; + } + + $instance = new ManticoreBase(); + + // 先删除已存在的记录 + $instance->execute("DELETE FROM key_values WHERE k = ?", [$key]); + + // 生成唯一 ID(基于 key 的 hash) + $id = abs(crc32($key)); + + // 插入新记录 + return $instance->execute( + "INSERT INTO key_values (id, k, v) VALUES (?, ?, ?)", + [$id, $key, (string)$value] + ); + } + + /** + * 删除值 + * + * @param string $key 键 + * @return bool 是否成功 + */ + public static function delete(string $key): bool + { + if (!Apps::isInstalled("manticore")) { + return false; + } + + $instance = new ManticoreBase(); + return $instance->execute("DELETE FROM key_values WHERE k = ?", [$key]); + } + + /** + * 清空所有键值 + * + * @return bool 是否成功 + */ + public static function clear(): bool + { + if (!Apps::isInstalled("manticore")) { + return false; + } + + $instance = new ManticoreBase(); + return $instance->execute("TRUNCATE TABLE key_values"); + } + + /** + * 检查键是否存在 + * + * @param string $key 键 + * @return bool 是否存在 + */ + public static function exists(string $key): bool + { + if (!Apps::isInstalled("manticore")) { + return false; + } + + $instance = new ManticoreBase(); + $result = $instance->queryOne( + "SELECT id FROM key_values WHERE k = ?", + [$key] + ); + + return $result !== null; + } + + /** + * 获取所有键值对 + * + * @return array 键值对数组 + */ + public static function all(): array + { + if (!Apps::isInstalled("manticore")) { + return []; + } + + $instance = new ManticoreBase(); + $results = $instance->query("SELECT k, v FROM key_values"); + + $data = []; + foreach ($results as $row) { + $data[$row['k']] = $row['v']; + } + + return $data; + } +} + diff --git a/app/Module/SeekDB/SeekDBProject.php b/app/Module/Manticore/ManticoreProject.php similarity index 78% rename from app/Module/SeekDB/SeekDBProject.php rename to app/Module/Manticore/ManticoreProject.php index 8f6176d5b..6fbf8c292 100644 --- a/app/Module/SeekDB/SeekDBProject.php +++ b/app/Module/Manticore/ManticoreProject.php @@ -1,17 +1,16 @@ getMessage()); + Log::error('Manticore project search error: ' . $e->getMessage()); return []; } } @@ -110,7 +109,7 @@ class SeekDBProject /** * 格式化搜索结果 * - * @param array $results SeekDB 返回的结果 + * @param array $results Manticore 返回的结果 * @return array 格式化后的结果 */ private static function formatSearchResults(array $results): array @@ -135,14 +134,14 @@ class SeekDBProject // ============================== /** - * 同步单个项目到 SeekDB + * 同步单个项目到 Manticore * * @param Project $project 项目模型 * @return bool 是否成功 */ public static function sync(Project $project): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } @@ -164,8 +163,8 @@ class SeekDBProject } } - // 写入 SeekDB - $result = SeekDBBase::upsertProjectVector([ + // 写入 Manticore + $result = ManticoreBase::upsertProjectVector([ 'project_id' => $project->id, 'userid' => $project->userid ?? 0, 'personal' => $project->personal ?? 0, @@ -176,7 +175,7 @@ class SeekDBProject return $result; } catch (\Exception $e) { - Log::error('SeekDB project sync error: ' . $e->getMessage(), [ + Log::error('Manticore project sync error: ' . $e->getMessage(), [ 'project_id' => $project->id, 'project_name' => $project->name, ]); @@ -212,7 +211,7 @@ class SeekDBProject */ public static function batchSync(iterable $projects): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -233,14 +232,14 @@ class SeekDBProject */ public static function delete(int $projectId): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } // 删除项目索引 - SeekDBBase::deleteProjectVector($projectId); + ManticoreBase::deleteProjectVector($projectId); // 删除项目成员关系 - SeekDBBase::deleteAllProjectUsers($projectId); + ManticoreBase::deleteAllProjectUsers($projectId); return true; } @@ -252,12 +251,12 @@ class SeekDBProject */ public static function clear(): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - SeekDBBase::clearAllProjectVectors(); - SeekDBBase::clearAllProjectUsers(); + ManticoreBase::clearAllProjectVectors(); + ManticoreBase::clearAllProjectUsers(); return true; } @@ -269,11 +268,11 @@ class SeekDBProject */ public static function getIndexedCount(): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } - return SeekDBBase::getIndexedProjectCount(); + return ManticoreBase::getIndexedProjectCount(); } // ============================== @@ -281,7 +280,7 @@ class SeekDBProject // ============================== /** - * 添加项目成员到 SeekDB + * 添加项目成员到 Manticore * * @param int $projectId 项目ID * @param int $userid 用户ID @@ -289,11 +288,11 @@ class SeekDBProject */ public static function addProjectUser(int $projectId, int $userid): bool { - if (!Apps::isInstalled("seekdb") || $projectId <= 0 || $userid <= 0) { + if (!Apps::isInstalled("manticore") || $projectId <= 0 || $userid <= 0) { return false; } - return SeekDBBase::upsertProjectUser($projectId, $userid); + return ManticoreBase::upsertProjectUser($projectId, $userid); } /** @@ -305,22 +304,22 @@ class SeekDBProject */ public static function removeProjectUser(int $projectId, int $userid): bool { - if (!Apps::isInstalled("seekdb") || $projectId <= 0 || $userid <= 0) { + if (!Apps::isInstalled("manticore") || $projectId <= 0 || $userid <= 0) { return false; } - return SeekDBBase::deleteProjectUser($projectId, $userid); + return ManticoreBase::deleteProjectUser($projectId, $userid); } /** - * 同步项目的所有成员到 SeekDB + * 同步项目的所有成员到 Manticore * * @param int $projectId 项目ID * @return bool 是否成功 */ public static function syncProjectUsers(int $projectId): bool { - if (!Apps::isInstalled("seekdb") || $projectId <= 0) { + if (!Apps::isInstalled("manticore") || $projectId <= 0) { return false; } @@ -330,10 +329,10 @@ class SeekDBProject ->pluck('userid') ->toArray(); - // 同步到 SeekDB - return SeekDBBase::syncProjectUsers($projectId, $userids); + // 同步到 Manticore + return ManticoreBase::syncProjectUsers($projectId, $userids); } catch (\Exception $e) { - Log::error('SeekDB syncProjectUsers error: ' . $e->getMessage(), ['project_id' => $projectId]); + Log::error('Manticore syncProjectUsers error: ' . $e->getMessage(), ['project_id' => $projectId]); return false; } } @@ -346,7 +345,7 @@ class SeekDBProject */ public static function syncAllProjectUsers(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -354,8 +353,8 @@ class SeekDBProject $lastId = 0; $batchSize = 1000; - // 先清空 SeekDB 中的 project_users 表 - SeekDBBase::clearAllProjectUsers(); + // 先清空 Manticore 中的 project_users 表 + ManticoreBase::clearAllProjectUsers(); // 分批同步 while (true) { @@ -369,7 +368,7 @@ class SeekDBProject } foreach ($records as $record) { - SeekDBBase::upsertProjectUser($record->project_id, $record->userid); + ManticoreBase::upsertProjectUser($record->project_id, $record->userid); $count++; $lastId = $record->id; } @@ -390,14 +389,14 @@ class SeekDBProject */ public static function syncProjectUsersIncremental(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } $count = 0; $batchSize = 1000; - $lastKey = "sync:seekdbProjectUserLastId"; - $lastId = intval(SeekDBKeyValue::get($lastKey, 0)); + $lastKey = "sync:manticoreProjectUserLastId"; + $lastId = intval(ManticoreKeyValue::get($lastKey, 0)); // 分批同步新增的记录 while (true) { @@ -411,13 +410,13 @@ class SeekDBProject } foreach ($records as $record) { - SeekDBBase::upsertProjectUser($record->project_id, $record->userid); + ManticoreBase::upsertProjectUser($record->project_id, $record->userid); $count++; $lastId = $record->id; } // 保存进度 - SeekDBKeyValue::set($lastKey, $lastId); + ManticoreKeyValue::set($lastKey, $lastId); if ($progressCallback) { $progressCallback($count); diff --git a/app/Module/SeekDB/SeekDBTask.php b/app/Module/Manticore/ManticoreTask.php similarity index 82% rename from app/Module/SeekDB/SeekDBTask.php rename to app/Module/Manticore/ManticoreTask.php index 21ea7319b..fb92cb0d9 100644 --- a/app/Module/SeekDB/SeekDBTask.php +++ b/app/Module/Manticore/ManticoreTask.php @@ -1,6 +1,6 @@ getMessage()); + Log::error('Manticore task search error: ' . $e->getMessage()); return []; } } @@ -122,7 +121,7 @@ class SeekDBTask /** * 格式化搜索结果 * - * @param array $results SeekDB 返回的结果 + * @param array $results Manticore 返回的结果 * @return array 格式化后的结果 */ private static function formatSearchResults(array $results): array @@ -149,14 +148,14 @@ class SeekDBTask // ============================== /** - * 同步单个任务到 SeekDB + * 同步单个任务到 Manticore * * @param ProjectTask $task 任务模型 * @return bool 是否成功 */ public static function sync(ProjectTask $task): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } @@ -181,8 +180,8 @@ class SeekDBTask } } - // 写入 SeekDB - $result = SeekDBBase::upsertTaskVector([ + // 写入 Manticore + $result = ManticoreBase::upsertTaskVector([ 'task_id' => $task->id, 'project_id' => $task->project_id ?? 0, 'userid' => $task->userid ?? 0, @@ -195,7 +194,7 @@ class SeekDBTask return $result; } catch (\Exception $e) { - Log::error('SeekDB task sync error: ' . $e->getMessage(), [ + Log::error('Manticore task sync error: ' . $e->getMessage(), [ 'task_id' => $task->id, 'task_name' => $task->name, ]); @@ -298,7 +297,7 @@ class SeekDBTask */ public static function batchSync(iterable $tasks): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -319,14 +318,14 @@ class SeekDBTask */ public static function delete(int $taskId): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } // 删除任务索引 - SeekDBBase::deleteTaskVector($taskId); + ManticoreBase::deleteTaskVector($taskId); // 删除任务成员关系 - SeekDBBase::deleteAllTaskUsers($taskId); + ManticoreBase::deleteAllTaskUsers($taskId); return true; } @@ -340,11 +339,11 @@ class SeekDBTask */ public static function updateVisibility(int $taskId, int $visibility): bool { - if (!Apps::isInstalled("seekdb") || $taskId <= 0) { + if (!Apps::isInstalled("manticore") || $taskId <= 0) { return false; } - return SeekDBBase::updateTaskVisibility($taskId, $visibility); + return ManticoreBase::updateTaskVisibility($taskId, $visibility); } /** @@ -354,12 +353,12 @@ class SeekDBTask */ public static function clear(): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - SeekDBBase::clearAllTaskVectors(); - SeekDBBase::clearAllTaskUsers(); + ManticoreBase::clearAllTaskVectors(); + ManticoreBase::clearAllTaskUsers(); return true; } @@ -371,11 +370,11 @@ class SeekDBTask */ public static function getIndexedCount(): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } - return SeekDBBase::getIndexedTaskCount(); + return ManticoreBase::getIndexedTaskCount(); } // ============================== @@ -383,7 +382,7 @@ class SeekDBTask // ============================== /** - * 添加任务成员到 SeekDB + * 添加任务成员到 Manticore * * @param int $taskId 任务ID * @param int $userid 用户ID @@ -391,11 +390,11 @@ class SeekDBTask */ public static function addTaskUser(int $taskId, int $userid): bool { - if (!Apps::isInstalled("seekdb") || $taskId <= 0 || $userid <= 0) { + if (!Apps::isInstalled("manticore") || $taskId <= 0 || $userid <= 0) { return false; } - return SeekDBBase::upsertTaskUser($taskId, $userid); + return ManticoreBase::upsertTaskUser($taskId, $userid); } /** @@ -407,11 +406,11 @@ class SeekDBTask */ public static function removeTaskUser(int $taskId, int $userid): bool { - if (!Apps::isInstalled("seekdb") || $taskId <= 0 || $userid <= 0) { + if (!Apps::isInstalled("manticore") || $taskId <= 0 || $userid <= 0) { return false; } - return SeekDBBase::deleteTaskUser($taskId, $userid); + return ManticoreBase::deleteTaskUser($taskId, $userid); } /** @@ -426,7 +425,7 @@ class SeekDBTask */ public static function removeVisibilityUser(int $taskId, int $userid): bool { - if (!Apps::isInstalled("seekdb") || $taskId <= 0 || $userid <= 0) { + if (!Apps::isInstalled("manticore") || $taskId <= 0 || $userid <= 0) { return false; } @@ -450,10 +449,10 @@ class SeekDBTask return true; } - // 从 SeekDB 删除 - return SeekDBBase::deleteTaskUser($taskId, $userid); + // 从 Manticore 删除 + return ManticoreBase::deleteTaskUser($taskId, $userid); } catch (\Exception $e) { - Log::error('SeekDB removeVisibilityUser error: ' . $e->getMessage(), [ + Log::error('Manticore removeVisibilityUser error: ' . $e->getMessage(), [ 'task_id' => $taskId, 'userid' => $userid, ]); @@ -462,7 +461,7 @@ class SeekDBTask } /** - * 同步任务的所有成员到 SeekDB + * 同步任务的所有成员到 Manticore * * 包括:ProjectTaskUser 和 ProjectTaskVisibilityUser * @@ -471,7 +470,7 @@ class SeekDBTask */ public static function syncTaskUsers(int $taskId): bool { - if (!Apps::isInstalled("seekdb") || $taskId <= 0) { + if (!Apps::isInstalled("manticore") || $taskId <= 0) { return false; } @@ -490,10 +489,10 @@ class SeekDBTask // 合并去重 $allUserIds = array_unique(array_merge($taskUserIds, $visibilityUserIds)); - // 同步到 SeekDB - return SeekDBBase::syncTaskUsers($taskId, $allUserIds); + // 同步到 Manticore + return ManticoreBase::syncTaskUsers($taskId, $allUserIds); } catch (\Exception $e) { - Log::error('SeekDB syncTaskUsers error: ' . $e->getMessage(), ['task_id' => $taskId]); + Log::error('Manticore syncTaskUsers error: ' . $e->getMessage(), ['task_id' => $taskId]); return false; } } @@ -506,7 +505,7 @@ class SeekDBTask */ public static function syncAllTaskUsers(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -514,8 +513,8 @@ class SeekDBTask $lastId = 0; $batchSize = 1000; - // 先清空 SeekDB 中的 task_users 表 - SeekDBBase::clearAllTaskUsers(); + // 先清空 Manticore 中的 task_users 表 + ManticoreBase::clearAllTaskUsers(); // 同步 ProjectTaskUser while (true) { @@ -529,10 +528,10 @@ class SeekDBTask } foreach ($records as $record) { - SeekDBBase::upsertTaskUser($record->task_id, $record->userid); + ManticoreBase::upsertTaskUser($record->task_id, $record->userid); // 如果有父任务,也添加到父任务 if ($record->task_pid) { - SeekDBBase::upsertTaskUser($record->task_pid, $record->userid); + ManticoreBase::upsertTaskUser($record->task_pid, $record->userid); } $count++; $lastId = $record->id; @@ -556,7 +555,7 @@ class SeekDBTask } foreach ($records as $record) { - SeekDBBase::upsertTaskUser($record->task_id, $record->userid); + ManticoreBase::upsertTaskUser($record->task_id, $record->userid); $count++; $lastId = $record->id; } @@ -577,7 +576,7 @@ class SeekDBTask */ public static function syncTaskUsersIncremental(?callable $progressCallback = null): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -585,8 +584,8 @@ class SeekDBTask $batchSize = 1000; // 同步 ProjectTaskUser 新增 - $lastKey1 = "sync:seekdbTaskUserLastId"; - $lastId1 = intval(SeekDBKeyValue::get($lastKey1, 0)); + $lastKey1 = "sync:manticoreTaskUserLastId"; + $lastId1 = intval(ManticoreKeyValue::get($lastKey1, 0)); while (true) { $records = ProjectTaskUser::where('id', '>', $lastId1) @@ -599,15 +598,15 @@ class SeekDBTask } foreach ($records as $record) { - SeekDBBase::upsertTaskUser($record->task_id, $record->userid); + ManticoreBase::upsertTaskUser($record->task_id, $record->userid); if ($record->task_pid) { - SeekDBBase::upsertTaskUser($record->task_pid, $record->userid); + ManticoreBase::upsertTaskUser($record->task_pid, $record->userid); } $count++; $lastId1 = $record->id; } - SeekDBKeyValue::set($lastKey1, $lastId1); + ManticoreKeyValue::set($lastKey1, $lastId1); if ($progressCallback) { $progressCallback($count); @@ -615,8 +614,8 @@ class SeekDBTask } // 同步 ProjectTaskVisibilityUser 新增 - $lastKey2 = "sync:seekdbTaskVisibilityUserLastId"; - $lastId2 = intval(SeekDBKeyValue::get($lastKey2, 0)); + $lastKey2 = "sync:manticoreTaskVisibilityUserLastId"; + $lastId2 = intval(ManticoreKeyValue::get($lastKey2, 0)); while (true) { $records = ProjectTaskVisibilityUser::where('id', '>', $lastId2) @@ -629,12 +628,12 @@ class SeekDBTask } foreach ($records as $record) { - SeekDBBase::upsertTaskUser($record->task_id, $record->userid); + ManticoreBase::upsertTaskUser($record->task_id, $record->userid); $count++; $lastId2 = $record->id; } - SeekDBKeyValue::set($lastKey2, $lastId2); + ManticoreKeyValue::set($lastKey2, $lastId2); if ($progressCallback) { $progressCallback($count); diff --git a/app/Module/SeekDB/SeekDBUser.php b/app/Module/Manticore/ManticoreUser.php similarity index 83% rename from app/Module/SeekDB/SeekDBUser.php rename to app/Module/Manticore/ManticoreUser.php index 62fb4d52c..9a8ef63eb 100644 --- a/app/Module/SeekDB/SeekDBUser.php +++ b/app/Module/Manticore/ManticoreUser.php @@ -1,6 +1,6 @@ getMessage()); + Log::error('Manticore user search error: ' . $e->getMessage()); return []; } } @@ -102,7 +102,7 @@ class SeekDBUser /** * 格式化搜索结果 * - * @param array $results SeekDB 返回的结果 + * @param array $results Manticore 返回的结果 * @return array 格式化后的结果 */ private static function formatSearchResults(array $results): array @@ -127,14 +127,14 @@ class SeekDBUser // ============================== /** - * 同步单个用户到 SeekDB + * 同步单个用户到 Manticore * * @param User $user 用户模型 * @return bool 是否成功 */ public static function sync(User $user): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } @@ -161,8 +161,8 @@ class SeekDBUser } } - // 写入 SeekDB - $result = SeekDBBase::upsertUserVector([ + // 写入 Manticore + $result = ManticoreBase::upsertUserVector([ 'userid' => $user->userid, 'nickname' => $user->nickname ?? '', 'email' => $user->email ?? '', @@ -174,7 +174,7 @@ class SeekDBUser return $result; } catch (\Exception $e) { - Log::error('SeekDB user sync error: ' . $e->getMessage(), [ + Log::error('Manticore user sync error: ' . $e->getMessage(), [ 'userid' => $user->userid, 'nickname' => $user->nickname, ]); @@ -216,7 +216,7 @@ class SeekDBUser */ public static function batchSync(iterable $users): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } @@ -237,11 +237,11 @@ class SeekDBUser */ public static function delete(int $userid): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - return SeekDBBase::deleteUserVector($userid); + return ManticoreBase::deleteUserVector($userid); } /** @@ -251,11 +251,11 @@ class SeekDBUser */ public static function clear(): bool { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return false; } - return SeekDBBase::clearAllUserVectors(); + return ManticoreBase::clearAllUserVectors(); } /** @@ -265,11 +265,11 @@ class SeekDBUser */ public static function getIndexedCount(): int { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return 0; } - return SeekDBBase::getIndexedUserCount(); + return ManticoreBase::getIndexedUserCount(); } } diff --git a/app/Module/SeekDB/SeekDBKeyValue.php b/app/Module/SeekDB/SeekDBKeyValue.php deleted file mode 100644 index db0a856af..000000000 --- a/app/Module/SeekDB/SeekDBKeyValue.php +++ /dev/null @@ -1,181 +0,0 @@ - 'value1', 'key2' => 'value2']); - * - 批量获取: $values = batchGet(['key1', 'key2']); - */ -class SeekDBKeyValue -{ - /** - * 设置键值 - * - * @param string $key 键名 - * @param mixed $value 值(会被 JSON 编码) - * @return bool 是否成功 - */ - public static function set(string $key, mixed $value): bool - { - if (!Apps::isInstalled("seekdb") || empty($key)) { - return false; - } - - $instance = new SeekDBBase(); - $jsonValue = is_string($value) ? $value : json_encode($value, JSON_UNESCAPED_UNICODE); - - // 使用 REPLACE INTO 实现 upsert - $sql = "REPLACE INTO key_values (k, v, updated_at) VALUES (?, ?, NOW())"; - - return $instance->execute($sql, [$key, $jsonValue]); - } - - /** - * 获取键值 - * - * @param string $key 键名 - * @param mixed $default 默认值 - * @return mixed 值或默认值 - */ - public static function get(string $key, mixed $default = null): mixed - { - if (!Apps::isInstalled("seekdb") || empty($key)) { - return $default; - } - - $instance = new SeekDBBase(); - $result = $instance->queryOne( - "SELECT v FROM key_values WHERE k = ?", - [$key] - ); - - if (!$result || !isset($result['v'])) { - return $default; - } - - $value = $result['v']; - - // 尝试 JSON 解码 - $decoded = json_decode($value, true); - if (json_last_error() === JSON_ERROR_NONE) { - return $decoded; - } - - return $value; - } - - /** - * 删除键值 - * - * @param string $key 键名 - * @return bool 是否成功 - */ - public static function delete(string $key): bool - { - if (!Apps::isInstalled("seekdb") || empty($key)) { - return false; - } - - $instance = new SeekDBBase(); - return $instance->execute( - "DELETE FROM key_values WHERE k = ?", - [$key] - ); - } - - /** - * 批量设置键值 - * - * @param array $keyValues 键值对数组 - * @return bool 是否全部成功 - */ - public static function batchSet(array $keyValues): bool - { - if (!Apps::isInstalled("seekdb") || empty($keyValues)) { - return false; - } - - $instance = new SeekDBBase(); - $success = true; - - foreach ($keyValues as $key => $value) { - $jsonValue = is_string($value) ? $value : json_encode($value, JSON_UNESCAPED_UNICODE); - $result = $instance->execute( - "REPLACE INTO key_values (k, v, updated_at) VALUES (?, ?, NOW())", - [$key, $jsonValue] - ); - if (!$result) { - $success = false; - } - } - - return $success; - } - - /** - * 批量获取键值 - * - * @param array $keys 键名数组 - * @return array 键值对数组 - */ - public static function batchGet(array $keys): array - { - if (!Apps::isInstalled("seekdb") || empty($keys)) { - return []; - } - - $instance = new SeekDBBase(); - $placeholders = implode(',', array_fill(0, count($keys), '?')); - $results = $instance->query( - "SELECT k, v FROM key_values WHERE k IN ({$placeholders})", - $keys - ); - - $values = []; - foreach ($results as $row) { - $value = $row['v']; - $decoded = json_decode($value, true); - $values[$row['k']] = (json_last_error() === JSON_ERROR_NONE) ? $decoded : $value; - } - - // 填充未找到的键为 null - foreach ($keys as $key) { - if (!isset($values[$key])) { - $values[$key] = null; - } - } - - return $values; - } - - /** - * 清空所有键值 - * - * @return bool 是否成功 - */ - public static function clear(): bool - { - if (!Apps::isInstalled("seekdb")) { - return false; - } - - $instance = new SeekDBBase(); - return $instance->execute("TRUNCATE TABLE key_values"); - } -} - diff --git a/app/Observers/FileObserver.php b/app/Observers/FileObserver.php index 8290cb925..eb717306b 100644 --- a/app/Observers/FileObserver.php +++ b/app/Observers/FileObserver.php @@ -3,7 +3,7 @@ namespace App\Observers; use App\Models\File; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class FileObserver extends AbstractObserver { @@ -19,7 +19,7 @@ class FileObserver extends AbstractObserver if ($file->type === 'folder') { return; } - self::taskDeliver(new SeekDBSyncTask('file_sync', $file->toArray())); + self::taskDeliver(new ManticoreSyncTask('file_sync', $file->toArray())); } /** @@ -41,7 +41,7 @@ class FileObserver extends AbstractObserver ->pluck('id') ->toArray(); if (!empty($childFileIds)) { - self::taskDeliver(new SeekDBSyncTask('file_pshare_update', [ + self::taskDeliver(new ManticoreSyncTask('file_pshare_update', [ 'file_ids' => $childFileIds, 'pshare' => $newPshare, ])); @@ -53,7 +53,7 @@ class FileObserver extends AbstractObserver if ($file->type === 'folder') { return; } - self::taskDeliver(new SeekDBSyncTask('file_sync', $file->toArray())); + self::taskDeliver(new ManticoreSyncTask('file_sync', $file->toArray())); } /** @@ -64,7 +64,7 @@ class FileObserver extends AbstractObserver */ public function deleted(File $file) { - self::taskDeliver(new SeekDBSyncTask('file_delete', $file->toArray())); + self::taskDeliver(new ManticoreSyncTask('file_delete', $file->toArray())); } /** @@ -79,7 +79,7 @@ class FileObserver extends AbstractObserver if ($file->type === 'folder') { return; } - self::taskDeliver(new SeekDBSyncTask('file_sync', $file->toArray())); + self::taskDeliver(new ManticoreSyncTask('file_sync', $file->toArray())); } /** @@ -90,7 +90,7 @@ class FileObserver extends AbstractObserver */ public function forceDeleted(File $file) { - self::taskDeliver(new SeekDBSyncTask('file_delete', $file->toArray())); + self::taskDeliver(new ManticoreSyncTask('file_delete', $file->toArray())); } } diff --git a/app/Observers/FileUserObserver.php b/app/Observers/FileUserObserver.php index 7e89f246b..29513fc29 100644 --- a/app/Observers/FileUserObserver.php +++ b/app/Observers/FileUserObserver.php @@ -3,7 +3,7 @@ namespace App\Observers; use App\Models\FileUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class FileUserObserver extends AbstractObserver { @@ -15,7 +15,7 @@ class FileUserObserver extends AbstractObserver */ public function created(FileUser $fileUser) { - self::taskDeliver(new SeekDBSyncTask('file_user_add', [ + self::taskDeliver(new ManticoreSyncTask('file_user_add', [ 'file_id' => $fileUser->file_id, 'userid' => $fileUser->userid, 'permission' => $fileUser->permission, @@ -30,7 +30,7 @@ class FileUserObserver extends AbstractObserver */ public function updated(FileUser $fileUser) { - self::taskDeliver(new SeekDBSyncTask('file_user_add', [ + self::taskDeliver(new ManticoreSyncTask('file_user_add', [ 'file_id' => $fileUser->file_id, 'userid' => $fileUser->userid, 'permission' => $fileUser->permission, @@ -45,7 +45,7 @@ class FileUserObserver extends AbstractObserver */ public function deleted(FileUser $fileUser) { - self::taskDeliver(new SeekDBSyncTask('file_user_remove', [ + self::taskDeliver(new ManticoreSyncTask('file_user_remove', [ 'file_id' => $fileUser->file_id, 'userid' => $fileUser->userid, ])); diff --git a/app/Observers/ProjectObserver.php b/app/Observers/ProjectObserver.php index 668122518..00d2e8401 100644 --- a/app/Observers/ProjectObserver.php +++ b/app/Observers/ProjectObserver.php @@ -5,7 +5,7 @@ namespace App\Observers; use App\Models\Deleted; use App\Models\Project; use App\Models\ProjectUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class ProjectObserver extends AbstractObserver { @@ -17,7 +17,7 @@ class ProjectObserver extends AbstractObserver */ public function created(Project $project) { - self::taskDeliver(new SeekDBSyncTask('project_sync', $project->toArray())); + self::taskDeliver(new ManticoreSyncTask('project_sync', $project->toArray())); } /** @@ -49,9 +49,9 @@ class ProjectObserver extends AbstractObserver if ($isDirty) { if ($project->archived_at) { - self::taskDeliver(new SeekDBSyncTask('project_delete', ['project_id' => $project->id])); + self::taskDeliver(new ManticoreSyncTask('project_delete', ['project_id' => $project->id])); } else { - self::taskDeliver(new SeekDBSyncTask('project_sync', $project->toArray())); + self::taskDeliver(new ManticoreSyncTask('project_sync', $project->toArray())); } } } @@ -65,7 +65,7 @@ class ProjectObserver extends AbstractObserver public function deleted(Project $project) { Deleted::record('project', $project->id, $this->userids($project)); - self::taskDeliver(new SeekDBSyncTask('project_delete', ['project_id' => $project->id])); + self::taskDeliver(new ManticoreSyncTask('project_delete', ['project_id' => $project->id])); } /** @@ -77,7 +77,7 @@ class ProjectObserver extends AbstractObserver public function restored(Project $project) { Deleted::forget('project', $project->id, $this->userids($project)); - self::taskDeliver(new SeekDBSyncTask('project_sync', $project->toArray())); + self::taskDeliver(new ManticoreSyncTask('project_sync', $project->toArray())); } /** @@ -88,7 +88,7 @@ class ProjectObserver extends AbstractObserver */ public function forceDeleted(Project $project) { - self::taskDeliver(new SeekDBSyncTask('project_delete', ['project_id' => $project->id])); + self::taskDeliver(new ManticoreSyncTask('project_delete', ['project_id' => $project->id])); } /** diff --git a/app/Observers/ProjectTaskObserver.php b/app/Observers/ProjectTaskObserver.php index fa77ac9ce..f7a346f08 100644 --- a/app/Observers/ProjectTaskObserver.php +++ b/app/Observers/ProjectTaskObserver.php @@ -7,7 +7,7 @@ use App\Models\ProjectTask; use App\Models\ProjectTaskUser; use App\Models\ProjectTaskVisibilityUser; use App\Models\ProjectUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class ProjectTaskObserver extends AbstractObserver { @@ -19,7 +19,7 @@ class ProjectTaskObserver extends AbstractObserver */ public function created(ProjectTask $projectTask) { - self::taskDeliver(new SeekDBSyncTask('task_sync', $projectTask->toArray())); + self::taskDeliver(new ManticoreSyncTask('task_sync', $projectTask->toArray())); } /** @@ -32,8 +32,8 @@ class ProjectTaskObserver extends AbstractObserver { if ($projectTask->isDirty('visibility')) { self::visibilityUpdate($projectTask); - // 同步 visibility 变化到 SeekDB - self::taskDeliver(new SeekDBSyncTask('task_visibility_update', [ + // 同步 visibility 变化到 Manticore + self::taskDeliver(new ManticoreSyncTask('task_visibility_update', [ 'task_id' => $projectTask->id, 'visibility' => $projectTask->visibility, ])); @@ -59,9 +59,9 @@ class ProjectTaskObserver extends AbstractObserver if ($isDirty) { if ($projectTask->archived_at) { - self::taskDeliver(new SeekDBSyncTask('task_delete', ['task_id' => $projectTask->id])); + self::taskDeliver(new ManticoreSyncTask('task_delete', ['task_id' => $projectTask->id])); } else { - self::taskDeliver(new SeekDBSyncTask('task_sync', $projectTask->toArray())); + self::taskDeliver(new ManticoreSyncTask('task_sync', $projectTask->toArray())); } } } @@ -75,7 +75,7 @@ class ProjectTaskObserver extends AbstractObserver public function deleted(ProjectTask $projectTask) { Deleted::record('projectTask', $projectTask->id, self::userids($projectTask)); - self::taskDeliver(new SeekDBSyncTask('task_delete', ['task_id' => $projectTask->id])); + self::taskDeliver(new ManticoreSyncTask('task_delete', ['task_id' => $projectTask->id])); } /** @@ -87,7 +87,7 @@ class ProjectTaskObserver extends AbstractObserver public function restored(ProjectTask $projectTask) { Deleted::forget('projectTask', $projectTask->id, self::userids($projectTask)); - self::taskDeliver(new SeekDBSyncTask('task_sync', $projectTask->toArray())); + self::taskDeliver(new ManticoreSyncTask('task_sync', $projectTask->toArray())); } /** @@ -98,7 +98,7 @@ class ProjectTaskObserver extends AbstractObserver */ public function forceDeleted(ProjectTask $projectTask) { - self::taskDeliver(new SeekDBSyncTask('task_delete', ['task_id' => $projectTask->id])); + self::taskDeliver(new ManticoreSyncTask('task_delete', ['task_id' => $projectTask->id])); } /** diff --git a/app/Observers/ProjectTaskUserObserver.php b/app/Observers/ProjectTaskUserObserver.php index d80455cc7..2ef54b710 100644 --- a/app/Observers/ProjectTaskUserObserver.php +++ b/app/Observers/ProjectTaskUserObserver.php @@ -5,7 +5,7 @@ namespace App\Observers; use App\Models\Deleted; use App\Models\ProjectTaskUser; use App\Models\ProjectUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class ProjectTaskUserObserver extends AbstractObserver { @@ -22,14 +22,14 @@ class ProjectTaskUserObserver extends AbstractObserver Deleted::forget('projectTask', $projectTaskUser->task_pid, $projectTaskUser->userid); } - // 同步任务成员到 SeekDB - self::taskDeliver(new SeekDBSyncTask('task_user_add', [ + // 同步任务成员到 Manticore + self::taskDeliver(new ManticoreSyncTask('task_user_add', [ 'task_id' => $projectTaskUser->task_id, 'userid' => $projectTaskUser->userid, ])); // 如果是子任务,同时添加到父任务 if ($projectTaskUser->task_pid) { - self::taskDeliver(new SeekDBSyncTask('task_user_add', [ + self::taskDeliver(new ManticoreSyncTask('task_user_add', [ 'task_id' => $projectTaskUser->task_pid, 'userid' => $projectTaskUser->userid, ])); @@ -59,8 +59,8 @@ class ProjectTaskUserObserver extends AbstractObserver Deleted::record('projectTask', $projectTaskUser->task_id, $projectTaskUser->userid); } - // 从 SeekDB 删除任务成员关系 - self::taskDeliver(new SeekDBSyncTask('task_user_remove', [ + // 从 Manticore 删除任务成员关系 + self::taskDeliver(new ManticoreSyncTask('task_user_remove', [ 'task_id' => $projectTaskUser->task_id, 'userid' => $projectTaskUser->userid, ])); diff --git a/app/Observers/ProjectTaskVisibilityUserObserver.php b/app/Observers/ProjectTaskVisibilityUserObserver.php index a8ade67b3..10f7c2424 100644 --- a/app/Observers/ProjectTaskVisibilityUserObserver.php +++ b/app/Observers/ProjectTaskVisibilityUserObserver.php @@ -3,7 +3,7 @@ namespace App\Observers; use App\Models\ProjectTaskVisibilityUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; /** * ProjectTaskVisibilityUser 观察者 @@ -20,8 +20,8 @@ class ProjectTaskVisibilityUserObserver extends AbstractObserver */ public function created(ProjectTaskVisibilityUser $visibilityUser) { - // 将指定成员添加到 SeekDB 的 task_users 表 - self::taskDeliver(new SeekDBSyncTask('task_user_add', [ + // 将指定成员添加到 Manticore 的 task_users 表 + self::taskDeliver(new ManticoreSyncTask('task_user_add', [ 'task_id' => $visibilityUser->task_id, 'userid' => $visibilityUser->userid, ])); @@ -36,7 +36,7 @@ class ProjectTaskVisibilityUserObserver extends AbstractObserver public function updated(ProjectTaskVisibilityUser $visibilityUser) { // 通常不会更新,但如果更新了也同步 - self::taskDeliver(new SeekDBSyncTask('task_user_add', [ + self::taskDeliver(new ManticoreSyncTask('task_user_add', [ 'task_id' => $visibilityUser->task_id, 'userid' => $visibilityUser->userid, ])); @@ -50,10 +50,10 @@ class ProjectTaskVisibilityUserObserver extends AbstractObserver */ public function deleted(ProjectTaskVisibilityUser $visibilityUser) { - // 从 SeekDB 的 task_users 表删除该成员 + // 从 Manticore 的 task_users 表删除该成员 // 注意:需要检查该用户是否仍是任务的负责人/协作人 // 如果是,则不应该删除(因为 ProjectTaskUser 仍存在) - self::taskDeliver(new SeekDBSyncTask('task_visibility_user_remove', [ + self::taskDeliver(new ManticoreSyncTask('task_visibility_user_remove', [ 'task_id' => $visibilityUser->task_id, 'userid' => $visibilityUser->userid, ])); diff --git a/app/Observers/ProjectUserObserver.php b/app/Observers/ProjectUserObserver.php index 2ddb561bd..8436d1c1f 100644 --- a/app/Observers/ProjectUserObserver.php +++ b/app/Observers/ProjectUserObserver.php @@ -4,7 +4,7 @@ namespace App\Observers; use App\Models\Deleted; use App\Models\ProjectUser; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class ProjectUserObserver extends AbstractObserver { @@ -17,7 +17,7 @@ class ProjectUserObserver extends AbstractObserver public function created(ProjectUser $projectUser) { Deleted::forget('project', $projectUser->project_id, $projectUser->userid); - self::taskDeliver(new SeekDBSyncTask('project_user_add', [ + self::taskDeliver(new ManticoreSyncTask('project_user_add', [ 'project_id' => $projectUser->project_id, 'userid' => $projectUser->userid, ])); @@ -43,7 +43,7 @@ class ProjectUserObserver extends AbstractObserver public function deleted(ProjectUser $projectUser) { Deleted::record('project', $projectUser->project_id, $projectUser->userid); - self::taskDeliver(new SeekDBSyncTask('project_user_remove', [ + self::taskDeliver(new ManticoreSyncTask('project_user_remove', [ 'project_id' => $projectUser->project_id, 'userid' => $projectUser->userid, ])); diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 17a8d1b37..1c8eaf8b3 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -3,7 +3,7 @@ namespace App\Observers; use App\Models\User; -use App\Tasks\SeekDBSyncTask; +use App\Tasks\ManticoreSyncTask; class UserObserver extends AbstractObserver { @@ -19,7 +19,7 @@ class UserObserver extends AbstractObserver if ($user->bot) { return; } - self::taskDeliver(new SeekDBSyncTask('user_sync', $user->toArray())); + self::taskDeliver(new ManticoreSyncTask('user_sync', $user->toArray())); } /** @@ -48,9 +48,9 @@ class UserObserver extends AbstractObserver if ($isDirty) { // 如果用户被禁用,删除索引;否则更新索引 if ($user->disable_at) { - self::taskDeliver(new SeekDBSyncTask('user_delete', ['userid' => $user->userid])); + self::taskDeliver(new ManticoreSyncTask('user_delete', ['userid' => $user->userid])); } else { - self::taskDeliver(new SeekDBSyncTask('user_sync', $user->toArray())); + self::taskDeliver(new ManticoreSyncTask('user_sync', $user->toArray())); } } } @@ -63,7 +63,7 @@ class UserObserver extends AbstractObserver */ public function deleted(User $user) { - self::taskDeliver(new SeekDBSyncTask('user_delete', ['userid' => $user->userid])); + self::taskDeliver(new ManticoreSyncTask('user_delete', ['userid' => $user->userid])); } } diff --git a/app/Tasks/SeekDBSyncTask.php b/app/Tasks/ManticoreSyncTask.php similarity index 73% rename from app/Tasks/SeekDBSyncTask.php rename to app/Tasks/ManticoreSyncTask.php index 44e020dc0..4f704069f 100644 --- a/app/Tasks/SeekDBSyncTask.php +++ b/app/Tasks/ManticoreSyncTask.php @@ -7,20 +7,20 @@ use App\Models\User; use App\Models\Project; use App\Models\ProjectTask; use App\Module\Apps; -use App\Module\SeekDB\SeekDBBase; -use App\Module\SeekDB\SeekDBFile; -use App\Module\SeekDB\SeekDBUser; -use App\Module\SeekDB\SeekDBProject; -use App\Module\SeekDB\SeekDBTask; +use App\Module\Manticore\ManticoreBase; +use App\Module\Manticore\ManticoreFile; +use App\Module\Manticore\ManticoreUser; +use App\Module\Manticore\ManticoreProject; +use App\Module\Manticore\ManticoreTask; use Carbon\Carbon; use Illuminate\Support\Facades\Cache; /** - * 通用 SeekDB 同步任务 + * 通用 Manticore Search 同步任务 * * 支持文件、用户、项目、任务的同步操作 */ -class SeekDBSyncTask extends AbstractTask +class ManticoreSyncTask extends AbstractTask { private $action; @@ -35,7 +35,7 @@ class SeekDBSyncTask extends AbstractTask public function start() { - if (!Apps::isInstalled("seekdb")) { + if (!Apps::isInstalled("manticore")) { return; } @@ -46,21 +46,21 @@ class SeekDBSyncTask extends AbstractTask case 'file_sync': $file = File::find($this->data['id'] ?? 0); if ($file) { - SeekDBFile::sync($file); + ManticoreFile::sync($file); } break; case 'file_delete': $fileId = $this->data['id'] ?? 0; if ($fileId > 0) { - SeekDBFile::delete($fileId); + ManticoreFile::delete($fileId); } break; case 'file_user_sync': $fileId = $this->data['file_id'] ?? 0; if ($fileId > 0) { - SeekDBFile::syncFileUsers($fileId); + ManticoreFile::syncFileUsers($fileId); } break; @@ -69,7 +69,7 @@ class SeekDBSyncTask extends AbstractTask $userid = $this->data['userid'] ?? 0; $permission = $this->data['permission'] ?? 0; if ($fileId > 0) { - SeekDBFile::addFileUser($fileId, $userid, $permission); + ManticoreFile::addFileUser($fileId, $userid, $permission); } break; @@ -77,7 +77,7 @@ class SeekDBSyncTask extends AbstractTask $fileId = $this->data['file_id'] ?? 0; $userid = $this->data['userid'] ?? null; if ($fileId > 0) { - SeekDBFile::removeFileUser($fileId, $userid); + ManticoreFile::removeFileUser($fileId, $userid); } break; @@ -85,7 +85,7 @@ class SeekDBSyncTask extends AbstractTask $fileIds = $this->data['file_ids'] ?? []; $pshare = $this->data['pshare'] ?? 0; if (!empty($fileIds)) { - SeekDBBase::batchUpdatePshare($fileIds, $pshare); + ManticoreBase::batchUpdatePshare($fileIds, $pshare); } break; @@ -95,14 +95,14 @@ class SeekDBSyncTask extends AbstractTask case 'user_sync': $user = User::find($this->data['userid'] ?? 0); if ($user) { - SeekDBUser::sync($user); + ManticoreUser::sync($user); } break; case 'user_delete': $userid = $this->data['userid'] ?? 0; if ($userid > 0) { - SeekDBUser::delete($userid); + ManticoreUser::delete($userid); } break; @@ -112,14 +112,14 @@ class SeekDBSyncTask extends AbstractTask case 'project_sync': $project = Project::find($this->data['id'] ?? 0); if ($project) { - SeekDBProject::sync($project); + ManticoreProject::sync($project); } break; case 'project_delete': $projectId = $this->data['project_id'] ?? 0; if ($projectId > 0) { - SeekDBProject::delete($projectId); + ManticoreProject::delete($projectId); } break; @@ -127,7 +127,7 @@ class SeekDBSyncTask extends AbstractTask $projectId = $this->data['project_id'] ?? 0; $userid = $this->data['userid'] ?? 0; if ($projectId > 0 && $userid > 0) { - SeekDBProject::addProjectUser($projectId, $userid); + ManticoreProject::addProjectUser($projectId, $userid); } break; @@ -135,14 +135,14 @@ class SeekDBSyncTask extends AbstractTask $projectId = $this->data['project_id'] ?? 0; $userid = $this->data['userid'] ?? 0; if ($projectId > 0 && $userid > 0) { - SeekDBProject::removeProjectUser($projectId, $userid); + ManticoreProject::removeProjectUser($projectId, $userid); } break; case 'project_users_sync': $projectId = $this->data['project_id'] ?? 0; if ($projectId > 0) { - SeekDBProject::syncProjectUsers($projectId); + ManticoreProject::syncProjectUsers($projectId); } break; @@ -152,14 +152,14 @@ class SeekDBSyncTask extends AbstractTask case 'task_sync': $task = ProjectTask::find($this->data['id'] ?? 0); if ($task) { - SeekDBTask::sync($task); + ManticoreTask::sync($task); } break; case 'task_delete': $taskId = $this->data['task_id'] ?? 0; if ($taskId > 0) { - SeekDBTask::delete($taskId); + ManticoreTask::delete($taskId); } break; @@ -167,7 +167,7 @@ class SeekDBSyncTask extends AbstractTask $taskId = $this->data['task_id'] ?? 0; $visibility = $this->data['visibility'] ?? 1; if ($taskId > 0) { - SeekDBTask::updateVisibility($taskId, $visibility); + ManticoreTask::updateVisibility($taskId, $visibility); } break; @@ -175,7 +175,7 @@ class SeekDBSyncTask extends AbstractTask $taskId = $this->data['task_id'] ?? 0; $userid = $this->data['userid'] ?? 0; if ($taskId > 0 && $userid > 0) { - SeekDBTask::addTaskUser($taskId, $userid); + ManticoreTask::addTaskUser($taskId, $userid); } break; @@ -183,7 +183,7 @@ class SeekDBSyncTask extends AbstractTask $taskId = $this->data['task_id'] ?? 0; $userid = $this->data['userid'] ?? 0; if ($taskId > 0 && $userid > 0) { - SeekDBTask::removeTaskUser($taskId, $userid); + ManticoreTask::removeTaskUser($taskId, $userid); } break; @@ -192,14 +192,14 @@ class SeekDBSyncTask extends AbstractTask $taskId = $this->data['task_id'] ?? 0; $userid = $this->data['userid'] ?? 0; if ($taskId > 0 && $userid > 0) { - SeekDBTask::removeVisibilityUser($taskId, $userid); + ManticoreTask::removeVisibilityUser($taskId, $userid); } break; case 'task_users_sync': $taskId = $this->data['task_id'] ?? 0; if ($taskId > 0) { - SeekDBTask::syncTaskUsers($taskId); + ManticoreTask::syncTaskUsers($taskId); } break; @@ -219,22 +219,22 @@ class SeekDBSyncTask extends AbstractTask private function incrementalUpdate() { // 60分钟执行一次 - $time = intval(Cache::get("SeekDBSyncTask:Time")); + $time = intval(Cache::get("ManticoreSyncTask:Time")); if (time() - $time < 60 * 60) { return; } // 执行开始 - Cache::put("SeekDBSyncTask:Time", time(), Carbon::now()->addMinutes(60)); + Cache::put("ManticoreSyncTask:Time", time(), Carbon::now()->addMinutes(60)); // 执行增量同步(同时同步向量表和用户关系表的新增数据) - @shell_exec("php /var/www/artisan seekdb:sync-files --i 2>&1 &"); - @shell_exec("php /var/www/artisan seekdb:sync-users --i 2>&1 &"); - @shell_exec("php /var/www/artisan seekdb:sync-projects --i 2>&1 &"); - @shell_exec("php /var/www/artisan seekdb:sync-tasks --i 2>&1 &"); + @shell_exec("php /var/www/artisan manticore:sync-files --i 2>&1 &"); + @shell_exec("php /var/www/artisan manticore:sync-users --i 2>&1 &"); + @shell_exec("php /var/www/artisan manticore:sync-projects --i 2>&1 &"); + @shell_exec("php /var/www/artisan manticore:sync-tasks --i 2>&1 &"); // 执行完成 - Cache::put("SeekDBSyncTask:Time", time(), Carbon::now()->addMinutes(5)); + Cache::put("ManticoreSyncTask:Time", time(), Carbon::now()->addMinutes(5)); } public function end() diff --git a/resources/assets/js/components/SearchBox.vue b/resources/assets/js/components/SearchBox.vue index 8dfac028f..94b086cd4 100755 --- a/resources/assets/js/components/SearchBox.vue +++ b/resources/assets/js/components/SearchBox.vue @@ -156,7 +156,7 @@ export default { aiSearchAvailable() { return this.microAppsIds - && this.microAppsIds.includes('seekdb') + && this.microAppsIds.includes('manticore') && this.microAppsIds.includes('ai') },