mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-12 08:58:11 +00:00
- 新增 msg_vectors 表,支持消息全文/向量/混合搜索 - 采用 MVA 权限方案,allowed_users 内联存储 - 新增 /api/search/message API - 新增 manticore:sync-msgs 同步命令 - Observer 触发消息创建/更新/删除同步 - Observer 触发对话成员变更时更新 allowed_users
317 lines
11 KiB
PHP
317 lines
11 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use Request;
|
||
use App\Models\File;
|
||
use App\Models\User;
|
||
use App\Models\WebSocketDialogMsg;
|
||
use App\Module\Base;
|
||
use App\Module\Apps;
|
||
use App\Module\Manticore\ManticoreFile;
|
||
use App\Module\Manticore\ManticoreUser;
|
||
use App\Module\Manticore\ManticoreProject;
|
||
use App\Module\Manticore\ManticoreTask;
|
||
use App\Module\Manticore\ManticoreMsg;
|
||
|
||
/**
|
||
* @apiDefine search
|
||
*
|
||
* 智能搜索
|
||
*/
|
||
class SearchController extends AbstractController
|
||
{
|
||
/**
|
||
* @api {get} api/search/contact AI 搜索联系人
|
||
*
|
||
* @apiDescription 需要token身份,需要安装 Manticore Search 应用
|
||
* @apiVersion 1.0.0
|
||
* @apiGroup search
|
||
* @apiName contact
|
||
*
|
||
* @apiParam {String} key 搜索关键词
|
||
* @apiParam {String} [search_type] 搜索类型(text/vector/hybrid,默认:hybrid)
|
||
* @apiParam {Number} [take] 获取数量(默认:20,最大:50)
|
||
*
|
||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||
* @apiSuccess {Object} data 返回数据
|
||
*/
|
||
public function contact()
|
||
{
|
||
User::auth();
|
||
|
||
if (!Apps::isInstalled('manticore')) {
|
||
return Base::retError('Manticore Search 应用未安装');
|
||
}
|
||
|
||
$key = trim(Request::input('key'));
|
||
$searchType = Request::input('search_type', 'hybrid');
|
||
$take = min(50, max(1, intval(Request::input('take', 20))));
|
||
|
||
if (empty($key)) {
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
$results = ManticoreUser::search($key, $searchType, $take);
|
||
|
||
// 补充用户完整信息
|
||
$userids = array_column($results, 'userid');
|
||
if (!empty($userids)) {
|
||
$users = User::whereIn('userid', $userids)
|
||
->select(User::$basicField)
|
||
->get()
|
||
->keyBy('userid');
|
||
|
||
foreach ($results as &$item) {
|
||
$userData = $users->get($item['userid']);
|
||
if ($userData) {
|
||
$item = array_merge($userData->toArray(), [
|
||
'relevance' => $item['relevance'] ?? 0,
|
||
'introduction_preview' => $item['introduction_preview'] ?? null,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return Base::retSuccess('success', $results);
|
||
}
|
||
|
||
/**
|
||
* @api {get} api/search/project AI 搜索项目
|
||
*
|
||
* @apiDescription 需要token身份,需要安装 Manticore Search 应用
|
||
* @apiVersion 1.0.0
|
||
* @apiGroup search
|
||
* @apiName project
|
||
*
|
||
* @apiParam {String} key 搜索关键词
|
||
* @apiParam {String} [search_type] 搜索类型(text/vector/hybrid,默认:hybrid)
|
||
* @apiParam {Number} [take] 获取数量(默认:20,最大:50)
|
||
*
|
||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||
* @apiSuccess {Object} data 返回数据
|
||
*/
|
||
public function project()
|
||
{
|
||
$user = User::auth();
|
||
|
||
if (!Apps::isInstalled('manticore')) {
|
||
return Base::retError('Manticore Search 应用未安装');
|
||
}
|
||
|
||
$key = trim(Request::input('key'));
|
||
$searchType = Request::input('search_type', 'hybrid');
|
||
$take = min(50, max(1, intval(Request::input('take', 20))));
|
||
|
||
if (empty($key)) {
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
$results = ManticoreProject::search($user->userid, $key, $searchType, $take);
|
||
|
||
// 补充项目完整信息
|
||
$projectIds = array_column($results, 'project_id');
|
||
if (!empty($projectIds)) {
|
||
$projects = \App\Models\Project::whereIn('id', $projectIds)
|
||
->get()
|
||
->keyBy('id');
|
||
|
||
foreach ($results as &$item) {
|
||
$projectData = $projects->get($item['project_id']);
|
||
if ($projectData) {
|
||
$item = array_merge($projectData->toArray(), [
|
||
'relevance' => $item['relevance'] ?? 0,
|
||
'desc_preview' => $item['desc_preview'] ?? null,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return Base::retSuccess('success', $results);
|
||
}
|
||
|
||
/**
|
||
* @api {get} api/search/task AI 搜索任务
|
||
*
|
||
* @apiDescription 需要token身份,需要安装 Manticore Search 应用
|
||
* @apiVersion 1.0.0
|
||
* @apiGroup search
|
||
* @apiName task
|
||
*
|
||
* @apiParam {String} key 搜索关键词
|
||
* @apiParam {String} [search_type] 搜索类型(text/vector/hybrid,默认:hybrid)
|
||
* @apiParam {Number} [take] 获取数量(默认:20,最大:50)
|
||
*
|
||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||
* @apiSuccess {Object} data 返回数据
|
||
*/
|
||
public function task()
|
||
{
|
||
$user = User::auth();
|
||
|
||
if (!Apps::isInstalled('manticore')) {
|
||
return Base::retError('Manticore Search 应用未安装');
|
||
}
|
||
|
||
$key = trim(Request::input('key'));
|
||
$searchType = Request::input('search_type', 'hybrid');
|
||
$take = min(50, max(1, intval(Request::input('take', 20))));
|
||
|
||
if (empty($key)) {
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
$results = ManticoreTask::search($user->userid, $key, $searchType, $take);
|
||
|
||
// 补充任务完整信息
|
||
$taskIds = array_column($results, 'task_id');
|
||
if (!empty($taskIds)) {
|
||
$tasks = \App\Models\ProjectTask::whereIn('id', $taskIds)
|
||
->get()
|
||
->keyBy('id');
|
||
|
||
foreach ($results as &$item) {
|
||
$taskData = $tasks->get($item['task_id']);
|
||
if ($taskData) {
|
||
$item = array_merge($taskData->toArray(), [
|
||
'relevance' => $item['relevance'] ?? 0,
|
||
'desc_preview' => $item['desc_preview'] ?? null,
|
||
'content_preview' => $item['content_preview'] ?? null,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return Base::retSuccess('success', $results);
|
||
}
|
||
|
||
/**
|
||
* @api {get} api/search/file AI 搜索文件
|
||
*
|
||
* @apiDescription 需要token身份,需要安装 Manticore Search 应用
|
||
* @apiVersion 1.0.0
|
||
* @apiGroup search
|
||
* @apiName file
|
||
*
|
||
* @apiParam {String} key 搜索关键词
|
||
* @apiParam {String} [search_type] 搜索类型(text/vector/hybrid,默认:hybrid)
|
||
* @apiParam {Number} [take] 获取数量(默认:20,最大:50)
|
||
*
|
||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||
* @apiSuccess {Object} data 返回数据
|
||
*/
|
||
public function file()
|
||
{
|
||
$user = User::auth();
|
||
|
||
if (!Apps::isInstalled('manticore')) {
|
||
return Base::retError('Manticore Search 应用未安装');
|
||
}
|
||
|
||
$key = trim(Request::input('key'));
|
||
$searchType = Request::input('search_type', 'hybrid');
|
||
$take = min(50, max(1, intval(Request::input('take', 20))));
|
||
|
||
if (empty($key)) {
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
$results = ManticoreFile::search($user->userid, $key, $searchType, 0, $take);
|
||
|
||
// 补充文件完整信息
|
||
$fileIds = array_column($results, 'file_id');
|
||
if (!empty($fileIds)) {
|
||
$files = File::whereIn('id', $fileIds)
|
||
->get()
|
||
->keyBy('id');
|
||
|
||
$formattedResults = [];
|
||
foreach ($results as $item) {
|
||
$fileData = $files->get($item['file_id']);
|
||
if ($fileData) {
|
||
$formattedResults[] = array_merge($fileData->toArray(), [
|
||
'relevance' => $item['relevance'] ?? 0,
|
||
'content_preview' => $item['content_preview'] ?? null,
|
||
]);
|
||
}
|
||
}
|
||
return Base::retSuccess('success', $formattedResults);
|
||
}
|
||
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
/**
|
||
* @api {get} api/search/message AI 搜索消息
|
||
*
|
||
* @apiDescription 需要token身份,需要安装 Manticore Search 应用
|
||
* @apiVersion 1.0.0
|
||
* @apiGroup search
|
||
* @apiName message
|
||
*
|
||
* @apiParam {String} key 搜索关键词
|
||
* @apiParam {String} [search_type] 搜索类型(text/vector/hybrid,默认:hybrid)
|
||
* @apiParam {Number} [take] 获取数量(默认:20,最大:50)
|
||
*
|
||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||
* @apiSuccess {Object} data 返回数据
|
||
*/
|
||
public function message()
|
||
{
|
||
$user = User::auth();
|
||
|
||
if (!Apps::isInstalled('manticore')) {
|
||
return Base::retError('Manticore Search 应用未安装');
|
||
}
|
||
|
||
$key = trim(Request::input('key'));
|
||
$searchType = Request::input('search_type', 'hybrid');
|
||
$take = min(50, max(1, intval(Request::input('take', 20))));
|
||
|
||
if (empty($key)) {
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
|
||
$results = ManticoreMsg::search($user->userid, $key, $searchType, 0, $take);
|
||
|
||
// 补充消息完整信息
|
||
$msgIds = array_column($results, 'msg_id');
|
||
if (!empty($msgIds)) {
|
||
$msgs = WebSocketDialogMsg::whereIn('id', $msgIds)
|
||
->with(['user' => function ($query) {
|
||
$query->select(User::$basicField);
|
||
}])
|
||
->get()
|
||
->keyBy('id');
|
||
|
||
$formattedResults = [];
|
||
foreach ($results as $item) {
|
||
$msgData = $msgs->get($item['msg_id']);
|
||
if ($msgData) {
|
||
$formattedResults[] = [
|
||
'id' => $msgData->id,
|
||
'msg_id' => $msgData->id,
|
||
'dialog_id' => $msgData->dialog_id,
|
||
'userid' => $msgData->userid,
|
||
'type' => $msgData->type,
|
||
'msg' => $msgData->msg,
|
||
'created_at' => $msgData->created_at,
|
||
'user' => $msgData->user,
|
||
'relevance' => $item['relevance'] ?? 0,
|
||
'content_preview' => $item['content_preview'] ?? null,
|
||
];
|
||
}
|
||
}
|
||
return Base::retSuccess('success', $formattedResults);
|
||
}
|
||
|
||
return Base::retSuccess('success', []);
|
||
}
|
||
}
|
||
|