feat(dialog): add send_ai_assistant endpoint for AI assistant identity messaging

New endpoint POST api/dialog/msg/send_ai_assistant sends messages
as the AI assistant identity (userid=-1). Supports both dialog_id
(direct) and task_id (with auto-creation) parameters.

Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kuaifan 2026-02-22 08:33:24 +00:00
parent c60dff0950
commit f78d3f3aff

View File

@ -21,6 +21,9 @@ use App\Module\TimeRange;
use App\Module\MsgTool;
use App\Models\FileContent;
use App\Models\ProjectTask;
use App\Models\ProjectTaskUser;
use App\Models\ProjectTaskVisibilityUser;
use App\Models\ProjectUser;
use App\Models\AbstractModel;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
@ -1696,6 +1699,106 @@ class DialogController extends AbstractController
return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', $msgData, $botUser->userid, false, false, $silence);
}
/**
* @api {post} api/dialog/msg/send_ai_assistant 以AI助手身份发送消息到对话
*
* @apiDescription 需要token身份以AI助手身份(userid=-1)发送消息到对话。支持两种方式:
* 1. 通过 dialog_id 直接发送到指定对话
* 2. 通过 task_id 发送到任务对话(自动创建对话如不存在)
* 两个参数至少提供一个,同时提供时优先使用 dialog_id
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__send_ai_assistant
*
* @apiParam {Number} [dialog_id] 对话ID与task_id二选一
* @apiParam {Number} [task_id] 任务ID与dialog_id二选一自动创建对话
* @apiParam {String} text 消息内容
* @apiParam {String} [text_type=md] 消息格式md html
* @apiParam {String} [silence=no] 是否静默发送yes/no
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__send_ai_assistant()
{
$user = User::auth();
//
$dialog_id = intval(Request::input('dialog_id'));
$task_id = intval(Request::input('task_id'));
$text = trim(Request::input('text'));
$text_type = strtolower(trim(Request::input('text_type'))) ?: 'md';
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
$markdown = in_array($text_type, ['md', 'markdown']);
//
if (empty($dialog_id) && empty($task_id)) {
return Base::retError('dialog_id 或 task_id 至少提供一个');
}
if (empty($text)) {
return Base::retError('消息内容不能为空');
}
if (mb_strlen($text) > 200000) {
return Base::retError('消息内容最大不能超过200000字');
}
//
if ($dialog_id) {
// Direct dialog mode: verify user is a member
WebSocketDialog::checkDialog($dialog_id);
} else {
// Task mode: resolve task -> dialog_id (auto-create if needed)
$task = ProjectTask::find($task_id);
if (!$task) {
return Base::retError('任务不存在');
}
if (!ProjectUser::whereProjectId($task->project_id)->whereUserid($user->userid)->exists()) {
return Base::retError('没有权限操作此任务');
}
// 任务可见性校验(与 task__one 一致)
if ($task->visibility != 1) {
$project_userid = ProjectUser::whereProjectId($task->project_id)->whereOwner(1)->value('userid');
if ($user->userid != $project_userid) {
$visibleUserids = array_merge(
ProjectTaskUser::whereTaskId($task_id)->pluck('userid')->toArray(),
ProjectTaskUser::whereTaskPid($task_id)->pluck('userid')->toArray(),
ProjectTaskVisibilityUser::whereTaskId($task_id)->pluck('userid')->toArray()
);
if (!in_array($user->userid, $visibleUserids)) {
return Base::retError('没有权限操作此任务');
}
}
}
if (!$task->dialog_id) {
$dialog = WebSocketDialog::createGroup($task->name, $task->relationUserids(), 'task');
if ($dialog) {
$task->dialog_id = $dialog->id;
$task->save();
$task->pushMsg('dialog');
} else {
return Base::retError('无法创建任务对话');
}
}
$dialog_id = $task->dialog_id;
}
//
$msgData = ['text' => $text];
if ($markdown) {
$msgData['type'] = 'md';
}
//
$result = WebSocketDialogMsg::sendMsg(
null,
$dialog_id,
'text',
$msgData,
\App\Module\AiTaskSuggestion::AI_ASSISTANT_USERID,
true, // push_self
false, // push_retry
$silence
);
//
return $result;
}
/**
* @api {post} api/dialog/msg/sendlocation 发送位置消息
*