217 lines
7.2 KiB
PHP
217 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace app\controller\api;
|
|
|
|
use app\model\AiModel;
|
|
use app\model\Conversation as ConversationModel;
|
|
use app\model\Message;
|
|
use app\service\ComfyUIService;
|
|
use app\service\PermissionService;
|
|
use think\exception\HttpResponseException;
|
|
|
|
class Conversation extends BaseApi
|
|
{
|
|
public function index()
|
|
{
|
|
$user = $this->authUser();
|
|
$page = max(1, (int) $this->request->get('page', 1));
|
|
$limit = min(50, max(1, (int) $this->request->get('limit', 20)));
|
|
|
|
$query = ConversationModel::alias('c')
|
|
->leftJoin('ai_models m', 'c.model_id = m.id')
|
|
->where('c.user_id', $user['id'])
|
|
->whereNull('c.deleted_at')
|
|
->field('c.*,m.name as model_name,m.provider as model_provider')
|
|
->order(['c.is_pinned' => 'desc', 'c.updated_at' => 'desc']);
|
|
|
|
$total = (clone $query)->count();
|
|
$list = $query->page($page, $limit)->select();
|
|
|
|
return $this->success([
|
|
'list' => $list,
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
]);
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
$user = $this->authUser();
|
|
PermissionService::checkConversationLimit($user);
|
|
|
|
$input = $this->request->post();
|
|
$modelId = $input['model_id'] ?? null;
|
|
if ($modelId !== null && $modelId !== '') {
|
|
$modelId = (int) $modelId;
|
|
if ($modelId <= 0) {
|
|
$modelId = null;
|
|
}
|
|
} else {
|
|
$modelId = null;
|
|
}
|
|
|
|
$conversation = ConversationModel::create([
|
|
'user_id' => $user['id'],
|
|
'title' => trim($input['title'] ?? '新对话'),
|
|
'model_id' => $modelId,
|
|
]);
|
|
|
|
return $this->success($this->findConversation((int) $conversation->id, (int) $user['id']), '创建成功');
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$user = $this->authUser();
|
|
return $this->success($this->findConversation((int) $id, (int) $user['id']));
|
|
}
|
|
|
|
public function update($id)
|
|
{
|
|
$user = $this->authUser();
|
|
$conversation = ConversationModel::where('id', $id)
|
|
->where('user_id', $user['id'])
|
|
->whereNull('deleted_at')
|
|
->find();
|
|
|
|
if (!$conversation) {
|
|
return $this->error('会话不存在', 404);
|
|
}
|
|
|
|
$input = $this->request->put();
|
|
$data = [];
|
|
if (isset($input['title'])) {
|
|
$data['title'] = trim($input['title']);
|
|
}
|
|
if (isset($input['is_pinned'])) {
|
|
$data['is_pinned'] = (int) $input['is_pinned'];
|
|
}
|
|
if (array_key_exists('model_id', $input)) {
|
|
$mid = $input['model_id'];
|
|
if ($mid === null || $mid === '') {
|
|
$data['model_id'] = null;
|
|
} else {
|
|
$data['model_id'] = (int) $mid;
|
|
}
|
|
}
|
|
|
|
if (empty($data)) {
|
|
return $this->error('无更新内容');
|
|
}
|
|
|
|
$conversation->save($data);
|
|
return $this->success($this->findConversation((int) $id, (int) $user['id']));
|
|
}
|
|
|
|
public function delete($id)
|
|
{
|
|
$user = $this->authUser();
|
|
$conversation = ConversationModel::where('id', $id)
|
|
->where('user_id', $user['id'])
|
|
->whereNull('deleted_at')
|
|
->find();
|
|
|
|
if (!$conversation) {
|
|
return $this->error('会话不存在', 404);
|
|
}
|
|
|
|
$conversation->save(['deleted_at' => date('Y-m-d H:i:s')]);
|
|
return $this->success(null, '删除成功');
|
|
}
|
|
|
|
public function messages($id)
|
|
{
|
|
$user = $this->authUser();
|
|
$this->findConversation((int) $id, (int) $user['id']);
|
|
|
|
$rows = Message::where('conversation_id', $id)
|
|
->field('id,role,content,content_type,attachments,created_at')
|
|
->order('created_at', 'asc')
|
|
->select();
|
|
|
|
$list = [];
|
|
foreach ($rows as $item) {
|
|
$attachments = $item->attachments;
|
|
if (is_string($attachments)) {
|
|
$attachments = json_decode($attachments, true) ?: [];
|
|
}
|
|
if (!is_array($attachments)) {
|
|
$attachments = [];
|
|
}
|
|
|
|
// 刷新后恢复未完成的 Comfy 生图任务
|
|
$stillPending = false;
|
|
$pendingJob = ComfyUIService::findPendingJob($attachments);
|
|
if ($pendingJob && $item->role === 'assistant') {
|
|
$modelId = (int) ($pendingJob['model_id'] ?? 0);
|
|
$model = $modelId > 0 ? AiModel::find($modelId) : null;
|
|
if ($model) {
|
|
$recovered = ComfyUIService::recoverPendingMessage([
|
|
'id' => (int) $item->id,
|
|
'content' => (string) $item->content,
|
|
'attachments' => $attachments,
|
|
], $model, (int) $user['id']);
|
|
|
|
$item->content = $recovered['content'];
|
|
$item->attachments = $recovered['attachments'];
|
|
$attachments = $recovered['attachments'];
|
|
$stillPending = empty($recovered['finished']);
|
|
|
|
Message::where('id', $item->id)->update([
|
|
'content' => $recovered['content'],
|
|
'attachments' => json_encode($recovered['attachments'], JSON_UNESCAPED_UNICODE),
|
|
]);
|
|
} else {
|
|
$stillPending = true;
|
|
}
|
|
}
|
|
|
|
if ($item->role === 'assistant' && trim((string) $item->content) === '') {
|
|
if (empty($attachments) || !ComfyUIService::hasImageAttachments($attachments)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 前端只展示真实媒体附件;pending 任务信息已体现在 content 文案中
|
|
$visibleAttachments = array_values(array_filter(
|
|
$attachments,
|
|
fn ($a) => is_array($a) && ($a['type'] ?? '') !== ComfyUIService::JOB_TYPE
|
|
));
|
|
|
|
$list[] = [
|
|
'id' => $item->id,
|
|
'role' => $item->role,
|
|
'content' => $item->content,
|
|
'content_type' => $item->content_type,
|
|
'attachments' => $visibleAttachments,
|
|
'pending_job' => $stillPending,
|
|
'pending_image_count' => $stillPending ? (int) ($pendingJob['image_count'] ?? 0) : 0,
|
|
'created_at' => $item->created_at,
|
|
];
|
|
}
|
|
|
|
return $this->success($list);
|
|
}
|
|
|
|
private function findConversation(int $id, int $userId): array
|
|
{
|
|
$conversation = ConversationModel::alias('c')
|
|
->leftJoin('ai_models m', 'c.model_id = m.id')
|
|
->where('c.id', $id)
|
|
->where('c.user_id', $userId)
|
|
->whereNull('c.deleted_at')
|
|
->field('c.*,m.name as model_name,m.provider as model_provider')
|
|
->find();
|
|
|
|
if (!$conversation) {
|
|
throw new HttpResponseException(json([
|
|
'code' => 1,
|
|
'message' => '会话不存在',
|
|
'data' => null,
|
|
], 404));
|
|
}
|
|
|
|
return $conversation->toArray();
|
|
}
|
|
}
|