mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-12 03:01:12 +00:00
feat: MCP增加文件管理功能,支持获取文件访问URL、文件列表和文件搜索
This commit is contained in:
parent
29df864ecb
commit
477bb1ac8f
@ -64,6 +64,9 @@ class FileController extends AbstractController
|
|||||||
* @apiParam {Number|String} id
|
* @apiParam {Number|String} id
|
||||||
* - Number 文件ID(需要登录)
|
* - Number 文件ID(需要登录)
|
||||||
* - String 链接码(不需要登录,用于预览)
|
* - String 链接码(不需要登录,用于预览)
|
||||||
|
* @apiParam {String} [with_url] 是否返回文件访问URL
|
||||||
|
* - no: 不返回(默认)
|
||||||
|
* - yes: 返回content_url字段
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
@ -72,11 +75,12 @@ class FileController extends AbstractController
|
|||||||
public function one()
|
public function one()
|
||||||
{
|
{
|
||||||
$id = Request::input('id');
|
$id = Request::input('id');
|
||||||
|
$with_url = Request::input('with_url', 'no');
|
||||||
//
|
//
|
||||||
$permission = 0;
|
$permission = 0;
|
||||||
if (Base::isNumber($id)) {
|
if (Base::isNumber($id)) {
|
||||||
$user = User::auth();
|
$user = User::auth();
|
||||||
$file = File::permissionFind(intval($id), $user, 0, $permission);
|
$file = File::permissionFind(intval($id), $user, $with_url === 'yes' ? 1 : 0, $permission);
|
||||||
} elseif ($id) {
|
} elseif ($id) {
|
||||||
$fileLink = FileLink::whereCode($id)->first();
|
$fileLink = FileLink::whereCode($id)->first();
|
||||||
$file = $fileLink?->file;
|
$file = $fileLink?->file;
|
||||||
@ -101,6 +105,12 @@ class FileController extends AbstractController
|
|||||||
//
|
//
|
||||||
$array = $file->toArray();
|
$array = $file->toArray();
|
||||||
$array['permission'] = $permission;
|
$array['permission'] = $permission;
|
||||||
|
|
||||||
|
// 如果请求返回文件URL
|
||||||
|
if ($with_url === 'yes') {
|
||||||
|
$array['content_url'] = FileContent::getFileUrl($file->id);
|
||||||
|
}
|
||||||
|
|
||||||
return Base::retSuccess('success', $array);
|
return Base::retSuccess('success', $array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -152,6 +152,23 @@ class FileContent extends AbstractModel
|
|||||||
return Base::retSuccess('success', [ 'content' => $content ]);
|
return Base::retSuccess('success', [ 'content' => $content ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件访问URL
|
||||||
|
* @param int $fileId 文件ID
|
||||||
|
* @return string|null 返回完整的文件URL,如果文件无内容则返回null
|
||||||
|
*/
|
||||||
|
public static function getFileUrl($fileId)
|
||||||
|
{
|
||||||
|
$content = self::whereFid($fileId)->orderByDesc('id')->first();
|
||||||
|
if ($content) {
|
||||||
|
$contentData = Base::json2array($content->content ?: []);
|
||||||
|
if (!empty($contentData['url'])) {
|
||||||
|
return Base::fillUrl($contentData['url']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件内容
|
* 获取文件内容
|
||||||
* @param $id
|
* @param $id
|
||||||
|
|||||||
182
electron/lib/mcp.js
vendored
182
electron/lib/mcp.js
vendored
@ -4,7 +4,7 @@
|
|||||||
* DooTask 的 Electron 客户端集成了 Model Context Protocol (MCP) 服务,
|
* DooTask 的 Electron 客户端集成了 Model Context Protocol (MCP) 服务,
|
||||||
* 允许 AI 助手(如 Claude)直接与 DooTask 任务进行交互。
|
* 允许 AI 助手(如 Claude)直接与 DooTask 任务进行交互。
|
||||||
*
|
*
|
||||||
* 提供的工具(共 21 个):
|
* 提供的工具(共 24 个):
|
||||||
*
|
*
|
||||||
* === 用户管理 ===
|
* === 用户管理 ===
|
||||||
* - get_users_basic - 根据用户ID列表获取基础信息,便于匹配负责人/协助人
|
* - get_users_basic - 根据用户ID列表获取基础信息,便于匹配负责人/协助人
|
||||||
@ -26,6 +26,11 @@
|
|||||||
* - create_project - 创建新项目
|
* - create_project - 创建新项目
|
||||||
* - update_project - 修改项目信息(名称、描述等)
|
* - update_project - 修改项目信息(名称、描述等)
|
||||||
*
|
*
|
||||||
|
* === 文件管理 ===
|
||||||
|
* - list_files - 获取项目文件列表,支持按父级文件夹筛选
|
||||||
|
* - search_files - 按关键词搜索文件,支持搜索文件名称或ID
|
||||||
|
* - get_file_detail - 获取文件详情,返回 content_url 可配合 WebFetch 读取文件内容
|
||||||
|
*
|
||||||
* === 工作报告 ===
|
* === 工作报告 ===
|
||||||
* - list_received_reports - 获取我接收的汇报列表,支持按类型/状态/部门/时间筛选
|
* - list_received_reports - 获取我接收的汇报列表,支持按类型/状态/部门/时间筛选
|
||||||
* - get_report_detail - 获取汇报详情,包括完整内容、汇报人、接收人、AI分析等
|
* - get_report_detail - 获取汇报详情,包括完整内容、汇报人、接收人、AI分析等
|
||||||
@ -61,6 +66,12 @@
|
|||||||
* - "我有哪些项目?"
|
* - "我有哪些项目?"
|
||||||
* - "查看项目5的详情,包括所有列和成员"
|
* - "查看项目5的详情,包括所有列和成员"
|
||||||
*
|
*
|
||||||
|
* 文件管理:
|
||||||
|
* - "查看我的文件列表"
|
||||||
|
* - "搜索包含'设计稿'的文件"
|
||||||
|
* - "显示文件123的详细信息"
|
||||||
|
* - "帮我分析这个文档的内容"
|
||||||
|
*
|
||||||
* 工作报告:
|
* 工作报告:
|
||||||
* - "查看未读的工作汇报"
|
* - "查看未读的工作汇报"
|
||||||
* - "生成本周周报"
|
* - "生成本周周报"
|
||||||
@ -797,7 +808,7 @@ class DooTaskMCP {
|
|||||||
url: file.path,
|
url: file.path,
|
||||||
thumb: file.thumb,
|
thumb: file.thumb,
|
||||||
userid: file.userid,
|
userid: file.userid,
|
||||||
download: file.download,
|
download_count: file.download,
|
||||||
created_at: file.created_at,
|
created_at: file.created_at,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -1397,15 +1408,28 @@ class DooTaskMCP {
|
|||||||
// 工作报告:获取汇报详情
|
// 工作报告:获取汇报详情
|
||||||
this.mcp.addTool({
|
this.mcp.addTool({
|
||||||
name: 'get_report_detail',
|
name: 'get_report_detail',
|
||||||
description: '获取指定工作汇报的详细信息,包括完整内容、汇报人、接收人列表、AI分析等。返回的 content 字段为 Markdown 格式。',
|
description: '获取指定工作汇报的详细信息,包括完整内容、汇报人、接收人列表、AI分析等。返回的 content 字段为 Markdown 格式。支持通过报告ID或分享码访问。',
|
||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
report_id: z.number()
|
report_id: z.number()
|
||||||
|
.optional()
|
||||||
.describe('报告ID'),
|
.describe('报告ID'),
|
||||||
|
share_code: z.string()
|
||||||
|
.optional()
|
||||||
|
.describe('报告分享码'),
|
||||||
}),
|
}),
|
||||||
execute: async (params) => {
|
execute: async (params) => {
|
||||||
const result = await this.request('GET', 'report/detail', {
|
if (!params.report_id && !params.share_code) {
|
||||||
id: params.report_id,
|
throw new Error('必须提供 report_id 或 share_code 参数之一');
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const requestData = {};
|
||||||
|
if (params.report_id) {
|
||||||
|
requestData.id = params.report_id;
|
||||||
|
} else if (params.share_code) {
|
||||||
|
requestData.code = params.share_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.request('GET', 'report/detail', requestData);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
@ -1687,6 +1711,152 @@ class DooTaskMCP {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 文件管理:获取文件列表
|
||||||
|
this.mcp.addTool({
|
||||||
|
name: 'list_files',
|
||||||
|
description: '获取项目文件列表,支持按父级文件夹筛选。可以浏览文件夹结构,查看所有文件和子文件夹。',
|
||||||
|
parameters: z.object({
|
||||||
|
pid: z.number()
|
||||||
|
.optional()
|
||||||
|
.describe('父级文件夹ID,0或不传表示根目录'),
|
||||||
|
}),
|
||||||
|
execute: async (params) => {
|
||||||
|
const pid = params.pid !== undefined ? params.pid : 0;
|
||||||
|
|
||||||
|
const result = await this.request('GET', 'file/lists', {
|
||||||
|
pid: pid,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = Array.isArray(result.data) ? result.data : [];
|
||||||
|
|
||||||
|
const simplified = files.map(file => ({
|
||||||
|
id: file.id,
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
ext: file.ext || '',
|
||||||
|
size: file.size || 0,
|
||||||
|
pid: file.pid,
|
||||||
|
userid: file.userid,
|
||||||
|
created_id: file.created_id,
|
||||||
|
share: file.share ? true : false,
|
||||||
|
created_at: file.created_at,
|
||||||
|
updated_at: file.updated_at,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify({
|
||||||
|
pid: pid,
|
||||||
|
total: simplified.length,
|
||||||
|
files: simplified,
|
||||||
|
}, null, 2)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文件管理:搜索文件
|
||||||
|
this.mcp.addTool({
|
||||||
|
name: 'search_files',
|
||||||
|
description: '按关键词搜索文件,支持搜索文件名称或ID。可以快速定位文件位置。',
|
||||||
|
parameters: z.object({
|
||||||
|
keyword: z.string()
|
||||||
|
.min(1)
|
||||||
|
.describe('搜索关键词,支持文件名称或文件ID'),
|
||||||
|
take: z.number()
|
||||||
|
.optional()
|
||||||
|
.describe('返回数量,默认50,最大100'),
|
||||||
|
}),
|
||||||
|
execute: async (params) => {
|
||||||
|
const take = params.take && params.take > 0 ? Math.min(params.take, 100) : 50;
|
||||||
|
|
||||||
|
const result = await this.request('GET', 'file/search', {
|
||||||
|
key: params.keyword,
|
||||||
|
take: take,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = Array.isArray(result.data) ? result.data : [];
|
||||||
|
|
||||||
|
const simplified = files.map(file => ({
|
||||||
|
id: file.id,
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
ext: file.ext || '',
|
||||||
|
size: file.size || 0,
|
||||||
|
pid: file.pid,
|
||||||
|
userid: file.userid,
|
||||||
|
created_id: file.created_id,
|
||||||
|
share: file.share ? true : false,
|
||||||
|
created_at: file.created_at,
|
||||||
|
updated_at: file.updated_at,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify({
|
||||||
|
keyword: params.keyword,
|
||||||
|
total: simplified.length,
|
||||||
|
files: simplified,
|
||||||
|
}, null, 2)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文件管理:获取文件详情
|
||||||
|
this.mcp.addTool({
|
||||||
|
name: 'get_file_detail',
|
||||||
|
description: '获取指定文件的详细信息,包括类型、大小、共享状态、创建者等。返回的 content_url 可以配合 WebFetch 工具读取文件内容进行分析。支持通过文件ID或分享码访问。',
|
||||||
|
parameters: z.object({
|
||||||
|
file_id: z.union([z.number(), z.string()])
|
||||||
|
.describe('文件ID(数字)或分享码(字符串)'),
|
||||||
|
}),
|
||||||
|
execute: async (params) => {
|
||||||
|
const result = await this.request('GET', 'file/one', {
|
||||||
|
id: params.file_id,
|
||||||
|
with_url: 'yes',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = result.data;
|
||||||
|
|
||||||
|
const fileDetail = {
|
||||||
|
id: file.id,
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
ext: file.ext || '',
|
||||||
|
size: file.size || 0,
|
||||||
|
pid: file.pid,
|
||||||
|
userid: file.userid,
|
||||||
|
created_id: file.created_id,
|
||||||
|
share: file.share ? true : false,
|
||||||
|
content_url: file.content_url || null,
|
||||||
|
created_at: file.created_at,
|
||||||
|
updated_at: file.updated_at,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify(fileDetail, null, 2)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动 MCP 服务器
|
// 启动 MCP 服务器
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user