# WebSocket 实施计划 ## 🎯 项目信息 - **项目名称**:英星AI - 浙江科技大学招生咨询系统 - **技术栈**:uni-app + Vue 2 - **后端**:ASP.NET Core(推测) - **方案选择**:WebSocket 原生方案 ✅ --- ## 📋 实施步骤 ### 阶段一:前端准备(已完成 ✅) #### 1. WebSocket 管理器 - ✅ 文件:`utils/websocket-manager.js` - ✅ 功能:自动重连、心跳检测、消息队列、事件监听 #### 2. WebSocket 配置 - ✅ 文件:`config/websocket.config.js` - ✅ 功能:集中管理 WebSocket 配置 #### 3. 使用文档 - ✅ 文件:`docs/WebSocket使用示例.md` - ✅ 文件:`docs/实时通讯方案对比.md` - ✅ 文件:`docs/实时通讯快速选型.md` --- ### 阶段二:后端开发(待实施 ⏳) #### 选项 A:ASP.NET Core WebSocket 服务(推荐) **时间**:3-5 天 **工作量:** 1. 创建 WebSocket 中间件(1 天) 2. 实现消息路由(1 天) 3. 集成用户认证(1 天) 4. 消息持久化(1 天) 5. 测试调试(1 天) **文件清单:** - `WebSocketMiddleware.cs` - WebSocket 中间件 - `WebSocketConnectionManager.cs` - 连接管理器 - `WebSocketMessageHandler.cs` - 消息处理器 - `ChatController.cs` - HTTP API(获取历史消息) - `MessageRepository.cs` - 消息存储 **后端代码**:见下文 `.NET Core WebSocket 服务端代码` #### 选项 B:使用现有 SignalR(备选) 你的项目已经有 SignalR: ```javascript // main.js var connection = new HubConnectionBuilder() .withUrl("http://sl.vrgon.com:8003/ChatHub") .build(); ``` **建议:** - 如果 SignalR 服务正常,可以继续使用 - 如果需要切换到原生 WebSocket,按选项 A 实施 --- ### 阶段三:前端集成(2-3 天) #### 1. 在 App.vue 初始化 WebSocket **文件**:`App.vue` ```javascript import wsManager from '@/utils/websocket-manager.js' import wsConfig from '@/config/websocket.config.js' export default { onLaunch() { // 登录后连接 WebSocket if (this.vuex_token) { this.connectWebSocket() } }, methods: { connectWebSocket() { // 构建 WebSocket URL(带 token) const wsUrl = `${wsConfig.url}?token=${this.vuex_token}` // 连接 wsManager.connect(wsUrl, { reconnectInterval: wsConfig.reconnect.interval, maxReconnectAttempts: wsConfig.reconnect.maxAttempts, heartbeatInterval: wsConfig.heartbeat.interval }) // 监听事件 this.setupWebSocketListeners() }, setupWebSocketListeners() { // 连接成功 wsManager.on('open', () => { console.log('[App] WebSocket 已连接') }) // 收到消息 wsManager.on('message', (data) => { this.handleWebSocketMessage(data) }) // 连接关闭 wsManager.on('close', () => { console.log('[App] WebSocket 已断开') }) }, handleWebSocketMessage(data) { // 根据消息类型处理 switch (data.type) { case 'message': // 新消息提醒 this.$store.commit('addMessage', data) break case 'system': // 系统通知 uni.showToast({ title: data.content, icon: 'none' }) break } } } } ``` #### 2. 修改聊天页面 **文件**:`pages/message/dialogBox/dialogBox.vue` 需要: 1. 导入 WebSocket 管理器 2. 监听实时消息 3. 发送消息通过 WebSocket 4. 加载历史消息通过 HTTP API **代码示例**:见 `docs/WebSocket使用示例.md` #### 3. 添加聊天相关 API **文件**:`common/http.api.js` ```javascript // 获取聊天历史记录 let getChatHistory = (params = {}) => vm.$u.get('api/Chat/GetHistory', params); // 发送离线消息(WebSocket 断开时使用) let sendOfflineMessage = (params = {}) => vm.$u.post('api/Chat/SendOffline', params); // 标记消息已读 let markMessageRead = (params = {}) => vm.$u.post('api/Chat/MarkRead', params); ``` --- ### 阶段四:测试验证(2-3 天) #### 1. 功能测试 - [ ] 连接建立 - [ ] 发送文字消息 - [ ] 接收文字消息 - [ ] 心跳保持 - [ ] 自动重连 - [ ] 离线消息推送 - [ ] 消息已读回执 #### 2. 场景测试 - [ ] 单人聊天 - [ ] 多人并发 - [ ] 网络断开 - [ ] 网络恢复 - [ ] App 切换后台 - [ ] App 恢复前台 - [ ] 登录/登出 #### 3. 性能测试 - [ ] 消息延迟 < 200ms - [ ] 重连时间 < 5s - [ ] 内存占用正常 - [ ] CPU 占用正常 - [ ] 100+ 并发用户 #### 4. 兼容性测试 - [ ] H5(Chrome、Safari) - [ ] 微信小程序 - [ ] Android App - [ ] iOS App --- ## 🔧 后端实现 ### .NET Core WebSocket 服务端代码 #### 1. WebSocket 中间件 **文件**:`WebSocketMiddleware.cs` ```csharp using System; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; public class WebSocketMiddleware { private readonly RequestDelegate _next; private readonly WebSocketConnectionManager _connectionManager; public WebSocketMiddleware(RequestDelegate next, WebSocketConnectionManager connectionManager) { _next = next; _connectionManager = connectionManager; } public async Task InvokeAsync(HttpContext context) { // 检查是否是 WebSocket 请求 if (!context.WebSockets.IsWebSocketRequest) { await _next(context); return; } // 验证 token var token = context.Request.Query["token"].ToString(); var userId = ValidateToken(token); if (string.IsNullOrEmpty(userId)) { context.Response.StatusCode = 401; await context.Response.WriteAsync("Unauthorized"); return; } // 接受 WebSocket 连接 var webSocket = await context.WebSockets.AcceptWebSocketAsync(); // 保存连接 _connectionManager.AddConnection(userId, webSocket); Console.WriteLine($"[WebSocket] 用户 {userId} 已连接"); try { await HandleWebSocketAsync(userId, webSocket); } finally { // 移除连接 _connectionManager.RemoveConnection(userId); Console.WriteLine($"[WebSocket] 用户 {userId} 已断开"); } } private async Task HandleWebSocketAsync(string userId, WebSocket webSocket) { var buffer = new byte[1024 * 4]; while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync( new ArraySegment(buffer), CancellationToken.None ); if (result.MessageType == WebSocketMessageType.Close) { await webSocket.CloseAsync( WebSocketCloseStatus.NormalClosure, "关闭连接", CancellationToken.None ); break; } // 解析消息 var message = Encoding.UTF8.GetString(buffer, 0, result.Count); await HandleMessageAsync(userId, message); } } private async Task HandleMessageAsync(string userId, string message) { try { var data = JsonConvert.DeserializeObject(message); switch (data.Type) { case "message": // 处理聊天消息 await HandleChatMessageAsync(userId, data); break; case "ping": // 处理心跳 await SendToUserAsync(userId, new { type = "pong", timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() }); break; case "read": // 处理已读回执 await HandleReadReceiptAsync(userId, data); break; default: Console.WriteLine($"未知消息类型: {data.Type}"); break; } } catch (Exception ex) { Console.WriteLine($"消息处理失败: {ex.Message}"); } } private async Task HandleChatMessageAsync(string fromUserId, WebSocketMessage data) { var toUserId = data.ToUserId; var content = data.Content; // 保存消息到数据库 var messageId = await SaveMessageAsync(fromUserId, toUserId, content); // 构建消息对象 var messageObj = new { type = "message", messageId = messageId, fromUserId = fromUserId, toUserId = toUserId, content = content, timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() }; // 发送给接收者 await SendToUserAsync(toUserId, messageObj); // 发送给发送者(确认消息已发送) await SendToUserAsync(fromUserId, messageObj); } private async Task SaveMessageAsync(string fromUserId, string toUserId, string content) { // TODO: 保存消息到数据库 // 返回消息 ID return Guid.NewGuid().ToString(); } private async Task HandleReadReceiptAsync(string userId, WebSocketMessage data) { // TODO: 更新消息已读状态 Console.WriteLine($"用户 {userId} 已读消息 {data.MessageId}"); } private async Task SendToUserAsync(string userId, object message) { var webSocket = _connectionManager.GetConnection(userId); if (webSocket != null && webSocket.State == WebSocketState.Open) { var json = JsonConvert.SerializeObject(message); var bytes = Encoding.UTF8.GetBytes(json); await webSocket.SendAsync( new ArraySegment(bytes), WebSocketMessageType.Text, true, CancellationToken.None ); } } private string ValidateToken(string token) { // TODO: 验证 JWT token,返回用户 ID // 这里简化处理,直接返回一个用户 ID return "user123"; } } // 消息实体 public class WebSocketMessage { public string Type { get; set; } public string FromUserId { get; set; } public string ToUserId { get; set; } public string Content { get; set; } public string MessageId { get; set; } public long Timestamp { get; set; } } ``` #### 2. 连接管理器 **文件**:`WebSocketConnectionManager.cs` ```csharp using System.Collections.Concurrent; using System.Net.WebSockets; public class WebSocketConnectionManager { // 存储所有连接(用户 ID -> WebSocket) private readonly ConcurrentDictionary _connections = new ConcurrentDictionary(); // 添加连接 public void AddConnection(string userId, WebSocket webSocket) { _connections.TryAdd(userId, webSocket); } // 移除连接 public void RemoveConnection(string userId) { _connections.TryRemove(userId, out _); } // 获取连接 public WebSocket GetConnection(string userId) { _connections.TryGetValue(userId, out var webSocket); return webSocket; } // 获取所有在线用户 public IEnumerable GetOnlineUsers() { return _connections.Keys; } // 获取在线用户数 public int GetOnlineCount() { return _connections.Count; } } ``` #### 3. 注册中间件 **文件**:`Startup.cs` ```csharp public class Startup { public void ConfigureServices(IServiceCollection services) { // 注册连接管理器为单例 services.AddSingleton(); // 其他服务... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 启用 WebSocket app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(30) }); // 注册 WebSocket 中间件 app.Map("/ws/chat", builder => { builder.UseMiddleware(); }); // 其他中间件... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } ``` #### 4. HTTP API(获取历史消息) **文件**:`ChatController.cs` ```csharp [ApiController] [Route("api/[controller]")] public class ChatController : ControllerBase { // 获取聊天历史 [HttpGet("GetHistory")] public async Task GetHistory(string userId, int page = 1, int pageSize = 50) { // TODO: 从数据库查询历史消息 var messages = new List { new { messageId = "msg001", fromUserId = "user123", toUserId = userId, content = "你好", timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() } }; return Ok(new { success = true, data = messages, total = messages.Count }); } // 发送离线消息 [HttpPost("SendOffline")] public async Task SendOffline([FromBody] SendMessageRequest request) { // TODO: 保存离线消息到数据库 return Ok(new { success = true }); } // 标记已读 [HttpPost("MarkRead")] public async Task MarkRead([FromBody] MarkReadRequest request) { // TODO: 更新消息已读状态 return Ok(new { success = true }); } } public class SendMessageRequest { public string ToUserId { get; set; } public string Content { get; set; } } public class MarkReadRequest { public string MessageId { get; set; } } ``` --- ## 📊 时间安排 | 阶段 | 工作内容 | 预计时间 | 负责人 | |------|----------|----------|--------| | **阶段一** | 前端准备 | ✅ 已完成 | 前端 | | **阶段二** | 后端开发 | 3-5 天 | 后端 | | **阶段三** | 前端集成 | 2-3 天 | 前端 | | **阶段四** | 测试验证 | 2-3 天 | 全员 | | **总计** | | **7-11 天** | | --- ## 🎯 下一步行动 ### 立即可做(前端) 1. **在 App.vue 中初始化 WebSocket** - 导入 `websocket-manager.js` - 在 `onLaunch` 中调用 `wsManager.connect()` - 设置事件监听 2. **测试 WebSocket 管理器** - 使用在线 WebSocket 测试服务(如 `wss://echo.websocket.org`) - 验证连接、发送、接收功能 ### 需要后端配合 1. **确认后端技术栈** - 是否是 ASP.NET Core? - 后端开发人员是谁? 2. **部署 WebSocket 服务** - 使用上面提供的 .NET Core 代码 - 或者告诉我你的后端技术,我提供对应代码 3. **配置服务器** - 确保服务器支持 WebSocket - 配置 Nginx 反向代理(如果需要) --- ## 📞 联系与支持 如果你需要: 1. ✅ 其他后端语言的 WebSocket 服务端代码(Node.js/Java/Go/Python) 2. ✅ 帮助修改 `dialogBox.vue` 集成 WebSocket 3. ✅ Nginx 配置 WebSocket 反向代理 4. ✅ 调试和测试支持 随时告诉我!🚀