import { Controller, Get, Post, Patch, Delete, Body, Param, Res, Query, HttpCode } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import type { Response } from 'express'; import { RagChatService } from './rag-chat.service'; import type { CreateSessionResult } from './rag-chat.service'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import type { UserPayload } from '../../common/types'; @ApiTags('rag-chat') @Controller('rag-chat') @ApiBearerAuth() export class RagChatController { constructor(private readonly svc: RagChatService) {} @Post('sessions') @ApiOperation({ summary: '创建/打开对话 (open-or-create)' }) async createSession( @CurrentUser() user: UserPayload, @Res({ passthrough: true }) res: Response, @Body() dto: { scopeType: string; scopeId?: string | null; parentKnowledgeBaseId?: string | null; createdFrom?: string; title?: string; }, ) { const result: CreateSessionResult = await this.svc.createSession(String(user.id), { scopeType: dto.scopeType, scopeId: dto.scopeId ?? null, parentKnowledgeBaseId: dto.parentKnowledgeBaseId ?? null, createdFrom: dto.createdFrom, title: dto.title, }); res.status(result.isNew ? 201 : 200); return result.session; } @Get('sessions') @ApiOperation({ summary: '对话列表' }) async listSessions( @CurrentUser() user: UserPayload, @Query('scopeType') scopeType?: string, @Query('scopeId') scopeId?: string, @Query('parentKnowledgeBaseId') parentKnowledgeBaseId?: string, @Query('knowledgeBaseId') kbId?: string, @Query('page') page?: string, @Query('limit') limit?: string, ) { return this.svc.listSessions(String(user.id), { scopeType, scopeId, parentKnowledgeBaseId, kbId, page: page ? parseInt(page, 10) : undefined, limit: limit ? parseInt(limit, 10) : undefined, }); } @Get('sessions/:id/messages') @ApiOperation({ summary: '对话历史' }) async messages(@CurrentUser() user: UserPayload, @Param('id') id: string) { return this.svc.getMessages(String(user.id), id); } @Post('sessions/:id/messages') @ApiOperation({ summary: '发送消息(同步)' }) async sendMessage(@CurrentUser() user: UserPayload, @Param('id') id: string, @Body() dto: { content: string }) { return this.svc.sendMessage(String(user.id), id, dto.content); } @Post('sessions/:id/stream') @ApiOperation({ summary: '发送消息(SSE 流式,支持思考过程)' }) async sendMessageStream(@CurrentUser() user: UserPayload, @Param('id') id: string, @Body() dto: { content: string }, @Res() res: Response) { res.setHeader('Content-Type', 'text/event-stream; charset=utf-8'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('X-Accel-Buffering', 'no'); const userId = String(user.id); for await (const chunk of this.svc.sendMessageStream(userId, id, dto.content)) { if (res.destroyed) break; res.write(`data: ${JSON.stringify(chunk)}\n\n`); } res.end(); } @Patch('sessions/:id') @ApiOperation({ summary: '更新会话属性' }) async updateSession( @CurrentUser() user: UserPayload, @Param('id') id: string, @Body() dto: { title?: string; isPinned?: boolean; isArchived?: boolean; modelMode?: string; modelId?: string | null; }, ) { return this.svc.updateSession(String(user.id), id, dto); } @Delete('sessions/:id') @ApiOperation({ summary: '删除对话(软删除)' }) async deleteSession(@CurrentUser() user: UserPayload, @Param('id') id: string) { return this.svc.deleteSession(String(user.id), id); } }