import 'dart:async'; import 'dart:convert'; import 'package:stream_channel/stream_channel.dart'; import 'package:uuid/uuid.dart'; import 'package:web_socket_channel/status.dart' as status; import 'package:web_socket_channel/web_socket_channel.dart'; import '../models/chat_models.dart'; class WebSocketService { WebSocketService({ this.baseUrl = 'ws://localhost:8080/chat', }); final String baseUrl; final Map _connections = {}; // 创建聊天连接 Future createChatConnection(String sessionId) async { // 在第二周迭代中,我们不实际连接WebSocket,而是模拟 // 返回一个模拟的WebSocketChannel final channel = MockWebSocketChannel(sessionId: sessionId); _connections[sessionId] = channel; return channel; } // 关闭连接 void closeConnection(String sessionId) { if (_connections.containsKey(sessionId)) { _connections[sessionId]?.sink.close(status.goingAway); _connections.remove(sessionId); } } // 关闭所有连接 void closeAllConnections() { for (final connection in _connections.values) { connection.sink.close(status.goingAway); } _connections.clear(); } } // 简化的模拟WebSocketChannel实现 class MockWebSocketChannel extends StreamChannelMixin implements WebSocketChannel { MockWebSocketChannel({required this.sessionId}) { _sink = MockWebSocketSink(_sinkController); // 监听发送的消息,模拟响应 _sinkController.stream.listen(_handleMessage); } final String sessionId; final StreamController _controller = StreamController(); final StreamController _sinkController = StreamController(); late final MockWebSocketSink _sink; @override Stream get stream => _controller.stream; @override WebSocketSink get sink => _sink; // 处理发送的消息,模拟响应 void _handleMessage(dynamic message) { if (message is String) { try { final Map data = jsonDecode(message); if (data.containsKey('action') && data['action'] == 'cancel') { // 模拟取消请求 _controller.add(jsonEncode({ 'done': true, 'message': '请求已取消', })); return; } if (data.containsKey('message')) { // 模拟流式响应 _simulateStreamingResponse(data['message'] as String); } } catch (e) { _controller.addError('解析消息失败: $e'); } } } // 模拟流式响应 void _simulateStreamingResponse(String message) async { // 根据消息内容生成不同的响应 String response; List actions = []; if (message.contains('角色')) { response = '角色设计是小说创作中的重要环节。好的角色应该有鲜明的性格特点、合理的动机和明确的目标。'; actions.add(MessageAction( id: const Uuid().v4(), label: '创建角色', type: ActionType.createCharacter, data: {'suggestion': '根据对话创建新角色'}, )); } else if (message.contains('情节')) { response = '情节发展需要有起承转合,保持读者的兴趣。一个好的情节应该包含引人入胜的开端、不断升级的冲突、出人意料的转折和合理的结局。'; actions.add(MessageAction( id: const Uuid().v4(), label: '生成情节', type: ActionType.generatePlot, data: {'suggestion': '根据当前内容生成情节'}, )); } else { response = '感谢您的提问。作为您的AI写作助手,我很乐意帮助您解决创作中遇到的问题。请告诉我您需要什么样的帮助?'; } // 始终添加一个应用到编辑器的操作 actions.add(MessageAction( id: const Uuid().v4(), label: '应用到编辑器', type: ActionType.applyToEditor, data: {'suggestion': '将AI回复应用到编辑器'}, )); // 模拟流式响应,将响应分成多个块发送 final chunks = _splitIntoChunks(response, 10); for (int i = 0; i < chunks.length; i++) { // 添加随机延迟,模拟网络延迟 await Future.delayed(Duration(milliseconds: 100 + (50 * i))); // 发送块 _controller.add(jsonEncode({ 'chunk': chunks[i], })); } // 发送完成信号和操作 await Future.delayed(const Duration(milliseconds: 500)); _controller.add(jsonEncode({ 'done': true, 'actions': actions.map((a) => a.toJson()).toList(), })); } // 将文本分成多个块 List _splitIntoChunks(String text, int chunkSize) { final chunks = []; for (int i = 0; i < text.length; i += chunkSize) { final end = (i + chunkSize < text.length) ? i + chunkSize : text.length; chunks.add(text.substring(i, end)); } return chunks; } @override Future close([int? closeCode, String? closeReason]) { _sinkController.close(); return _controller.close(); } // WebSocketChannel 接口所需的属性 @override int? get closeCode => null; @override String? get closeReason => null; @override String? get protocol => null; @override Future get ready => Future.value(); } // 模拟的WebSocketSink class MockWebSocketSink implements WebSocketSink { MockWebSocketSink(this._controller); final StreamController _controller; @override void add(dynamic data) { _controller.add(data); } @override void addError(Object error, [StackTrace? stackTrace]) { _controller.addError(error, stackTrace); } @override Future addStream(Stream stream) async { await for (final data in stream) { add(data); } } @override Future close([int? closeCode, String? closeReason]) async { // 不实际关闭,因为这是模拟的 return Future.value(); } @override Future get done => Future.value(); }