ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSM+Vue聊天管理系统毕设实战:从数据库到WebSocket完整指南

SSM+Vue聊天管理系统毕设实战:从数据库到WebSocket完整指南 又到一年毕设季。后台私信里问得最多的除了“有没有简单点的选题”就是“SSM 这东西都 2026 年了还能做吗”。我的答案很明确能而且“SSM Vue 聊天管理系统”这个组合是我眼里 JavaWeb 方向毕业设计里性价比非常高的一类选题。难度适中、功能完整、技术线清晰论文也好写。它不是一个企业级 IM比如微信那种量级而是把“用户、好友、会话、消息”这条完整链路走通的教学型系统单聊、群聊、聊天记录、在线状态、未读消息这些关键点都能覆盖。适合学过 Java 框架、但还没独立做过完整项目的同学也能让导师在开题和答辩时一眼看到你掌握的东西。这篇博文我会把选题思路、系统设计、数据库建模、WebSocket 消息收发、Vue 前端实现、部署联调和论文写作全部串起来讲。如果你正准备做这个题目或者已经在做的路上可以直接按这套流程“抄作业”踩过的坑我也都写在后面了。1. 项目定位与技术选型为什么是SSM Vue1.1 聊天管理系统到底要解决什么问题很多人一听“聊天系统”就觉得方向很虚觉得网上开源代码一大把。但毕设项目的好坏从来不在于题目多新鲜而在于功能闭环是否完整、技术点是否密集、论文是否有层次感。我建议把系统功能收敛为六个核心模块用户注册登录、好友管理、单聊、群聊、聊天记录、在线状态与未读消息。外加一个用户信息维护就足够。下面是我标注优先级的功能清单你照着做不会跑偏用户模块注册、登录、退出、密码加密存储好友模块搜索用户、发送好友申请或直接添加、好友列表、删除好友单聊两用户之间实时收发文本消息、消息落库、离线消息补收群聊建群、加群、退群、在群内实时收发消息会话列表展示最近联系人/群聊、显示最后一条消息、未读角标在线状态上下线广播、好友在线列表展示这样一个系统既有增删改查用户、好友又有实时通信WebSocket前端交互密度高会话切换、消息滚动、未读角标后端逻辑里还涉及并发和安全问题。工作量足够撑起一篇完整的毕业论文又不至于做到一半想放弃。1.2 2026年SSM还香吗选型逻辑与SSM常用注解我理解你的顾虑现在市面上的招聘要求都在往 SpringBoot、微服务上靠用 SSM 做毕设是不是太“复古”了实际情况是国内很多高校的软件工程、计算机专业课程仍然以 SSM 为主因为 Spring MVC、MyBatis 这些底层的配置和运行机制更“看得见摸得着”。SpringBoot 把大量东西封装掉了你很难跟答辩老师解释清楚一个请求到底是怎么从浏览器走到数据库的。而用 SSM你能清晰地说出“前端请求 - SpringMVC 前端控制器分发 - Controller 处理 - Service 业务 - MyBatis 查询数据库”这条链路。导师就问不倒你。这并不意味着 SSM 和 SpringBoot 对立。项目的 Service、Mapper 写法基本能无缝迁移到 SpringBoot。我给你的建议是毕设骨架按 SSM 搭论文里单独用一节讲清楚“如果是 SpringBoot哪些配置可以被自动装配替代”这句话写进论文反而是加分项。SSM 常用注解必须熟练掌握因为这是答辩时的高频提问点。我直接整理成一份速查清单注解作用位置核心用途Controller类标记控制器配合 SpringMVCResponseBody方法/类返回 JSON不走视图解析器前后端分离项目几乎每个接口都用RequestMapping类/方法映射请求 URL可组合 GetMapping / PostMappingPathVariable参数从 URL 路径取值如 /user/{id}RequestParam参数绑定请求参数可用于查询字符串和表单数据RequestBody参数接收前端 JSON 字符串并转成对象Service类业务层组件交给 Spring 容器管理Repository类数据访问层组件对应 Mapper 接口实现Autowired属性/构造器依赖注入默认按类型Transactional方法/类开启事务好友添加、发消息落库等组合操作必须加Param方法参数给 Mapper 接口参数起别名配合 XML 中的 #{ }写代码时Controller 层只做参数接收、调用 Service、返回统一结果Service 层写业务逻辑和事务Mapper 层只负责 SQL。这个分层如果乱了后面排查问题会非常痛苦答辩讲“高内聚低耦合”自己也心里有底。1.3 前端为什么选Vue而不选JSP如果你是纯后端思路可能会想着“JSP JQuery 也能把聊天页面写完”。确实可以但 2026 年的毕设我希望你的代码能经得起问“你前端的组件化怎么做的路由怎么管理的”。JSP 答不上来Vue 能。Vue 的核心优势在于三点。第一是响应式数据绑定消息数组变化直接驱动聊天界面更新代码量比 JQuery 手拼 DOM 少一个量级。第二是单文件组件.vue聊天窗口、会话列表、好友面板都能拆成独立组件论文里可以画组件结构图。第三是生态成熟Element Plus 提供现成 UI 组件路由用 vue-router状态用 Pinia这四个组合起来就是完整的 Vue3 全家桶。关于版本选择我建议直接上 Vue3 Vite Element Plus。Vue3 的 Composition API 写起来更清爽Vite 启动快而且 2026 年再用 Vue2 写新项目答辩老师会觉得你没跟上生态。如果你的指导老师对 Vue2 特别熟Vue2 也不会扣分但默认我还是推荐 Vue3。2. 系统整体设计与数据库建模2.1 前后端分离架构与目录规约整个项目结构分为 ssm-chat-server后端和 chat-web前端两个部分生产环境部署时前端打包产物放进后端 static 目录由一个 Tomcat 承载。开发环境则让前端跑在 Vite 的 8080 端口后端跑在 8081通过代理解决跨域。后端包结构我推荐这样规划com.example.chat ├── controller # 用户、好友、群组、会话相关接口 ├── service # 业务层接口 实现 ├── mapper # MyBatis Mapper接口 ├── entity # 实体类User, Message, Friend, Group等 ├── dto # 前端口径的传输对象避免直接暴露实体 ├── config # Spring、WebSocket、CORS配置 ├── interceptor # 登录拦截器 ├── websocket # WebSocket端点和消息处理 └── common # 统一返回结果、状态枚举、工具类前端目录按功能组织chat-web/src ├── api # axios请求封装 ├── assets # 静态资源 ├── components # 公共组件会话列表项、聊天气泡、好友卡片 ├── router # 路由配置 ├── store # Pinia状态管理 ├── views # 页面登录、注册、聊天主界面 └── utils # 时间格式化等工具函数前端页面不追求多四个页面足够登录页、注册页、聊天主界面、个人信息页。难点集中在聊天主界面的布局和状态管理上这个页面要承载会话列表、消息列表、好友面板、输入框、群聊信息等多个区域。2.2 数据库表设计六张表搞定核心业务数据库设计是论文里最容易被导师翻开看的部分字段冗余和逻辑混乱一眼就能看出来。我按最小可用但功能完整的原则设计了六张核心表。首先是用户表CREATE TABLE tb_user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT BCrypt或MD5加密后的密码, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, signature varchar(255) DEFAULT NULL COMMENT 个性签名, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;然后是好友关系表。好友是一种典型的双向关系但表设计上存一行即可查询时通过 or 条件同时匹配 user_id 和 friend_idCREATE TABLE tb_friend ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 用户ID, friend_id bigint NOT NULL COMMENT 好友ID, remark varchar(50) DEFAULT NULL COMMENT 备注名, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_user_friend (user_id, friend_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;消息表是系统里数据量最大、查询最频繁的表也是优化重点。我用to_type区分单聊和群聊用target_id表示“对方用户ID或群组ID”这样单聊和群聊的消息能共存在一张表里代码统一度更高CREATE TABLE tb_message ( id bigint NOT NULL AUTO_INCREMENT, from_user_id bigint NOT NULL COMMENT 发送人, to_type tinyint NOT NULL COMMENT 1单聊 2群聊, target_id bigint NOT NULL COMMENT 单聊为对方用户ID群聊为群组ID, content varchar(1000) NOT NULL COMMENT 消息内容, msg_type tinyint DEFAULT 1 COMMENT 1文本后续可扩展图片等, create_time datetime NOT NULL, PRIMARY KEY (id), KEY idx_target_query (to_type, target_id, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;群组和群成员表以及会话表CREATE TABLE tb_group ( id bigint NOT NULL AUTO_INCREMENT, group_name varchar(50) NOT NULL, owner_id bigint NOT NULL COMMENT 群主ID, avatar varchar(255) DEFAULT NULL, create_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_group_member ( id bigint NOT NULL AUTO_INCREMENT, group_id bigint NOT NULL, user_id bigint NOT NULL, join_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_group_user (group_id, user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE tb_session ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 会话归属人, target_type tinyint NOT NULL COMMENT 1单聊 2群聊, target_id bigint NOT NULL COMMENT 对方ID或群组ID, last_message varchar(500) DEFAULT NULL COMMENT 最后一条消息预览, unread_count int DEFAULT 0 COMMENT 未读消息数, update_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_user_target (user_id, target_type, target_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;会话表是关键。不是说消息表查一遍就能拼出会话列表而是每次收消息、发消息都要同步更新 tb_session 的 last_message 和 unread_count。会话列表页直接查这张表做排序性能比 group by 消息表快得多。这里还要强调两点字符集统一用 utf8mb4否则 emoji 和生僻字存进去报错所有外键用逻辑外键不建物理外键因为 MyBatis 环境下物理外键会给单元测试和删数据带来大量麻烦。2.3 Vue路由设计与前端状态管理聊天主界面是单页应用的核心。路由结构我设计成const routes [ { path: /login, component: Login }, { path: /register, component: Register }, { path: /chat, component: ChatLayout, redirect: /chat/session, meta: { requiresAuth: true }, children: [ { path: session, component: SessionView }, { path: friend, component: FriendView }, { path: profile, component: ProfileView } ] } ]注意二级路由不要独立出来。聊天页面本质是一整个工作台会话列表、好友列表、个人信息更像是这个工作台里的可切换面板。把背景缓存、滚动位置管理这些问题放在一个父组件里处理最简单。路由守卫是登录拦截的重点也是 Vue 面试高频题router.beforeEach((to, from, next) { const token localStorage.getItem(chat_token) if (to.meta.requiresAuth !token) { next(/login) } else if (to.path /login token) { next(/chat/session) } else { next() } })至于热词里提到的“vue 动态路由”在毕设阶段可以这样解释如果系统有管理员端需要按角色动态注册路由如果只有普通用户静态路由加守卫即可。聊天系统不需要复杂权限矩阵硬上动态路由反而容易把自己绕晕。我见过不少同学把管理员和用户的菜单混在一个路由文件里结果每次刷新都要重新登录定位这个坑在新手项目里太常见了。3. 核心功能实现与实操要点3.1 登录认证拦截器、Token与密码加密聊天系统的登录不能只靠 session因为 WebSocket 握手时拿不到传统的 cookie 信息就算能拿到也在跨域场景下很难维护。我推荐的做法是登录成功后后端生成一个 token 字符串返回给前端前端存储到 localStorage后续每次 HTTP 请求放在 Authorization 头里WebSocket 连接时也把它作为 query 参数传给后端校验。密码存储一定要加密。很多人初学 JavaWeb 喜欢用 MD5 甚至明文这是典型的答辩送分题。MD5 可以通过彩虹表直接反查至少做到 MD5 Salt更推荐用 BCrypt。Spring Security 单独引入太重你可以引入 jbcrypt 工具包public class PasswordUtils { public static String encode(String rawPassword) { return BCrypt.hashpw(rawPassword, BCrypt.gensalt()); } public static boolean matches(String rawPassword, String encodedPassword) { return BCrypt.checkpw(rawPassword, encodedPassword); } }登录拦截器是 SSM 里的重头戏实现HandlerInterceptor接口在 preHandle 中校验请求头里的 token校验通过后把用户信息放到 ThreadLocal 或 request attribute 里public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null token.startsWith(chat_)) { User currentUser tokenService.getUserByToken(token); if (currentUser ! null) { UserContext.set(currentUser); return true; } } response.setStatus(401); return false; } }WebConfig 里注册拦截器时一定把 login、register 接口排除掉否则前端第一次访问就直接 401registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/api/**) .excludePathPatterns(/api/user/login, /api/user/register);这里我建议所有接口都统一挂在/api前缀下前端代理和后端拦截器都好做论文里写“统一接口前缀设计”也是一个规范化亮点。3.2 聊天系统的命门WebSocket实时消息收发这是整个项目技术含量最高的地方答辩时导师大概率会问“为什么用 WebSocket 而不是轮询”。我两句话回答HTTP 是单向请求响应模型服务器无法主动推送聊天场景要求低延迟、少冗余WebSocket 一次握手建立全双工连接之后双方随时可以互发数据帧。轮询方案不仅延迟高还会造成大量无效请求消息翻转率一高就崩。SSM 项目里集成 WebSocket 有两种常见做法。第一是用javax.websocket原生注解ServerEndpoint代码简单但和 Spring 容器整合麻烦——端点里拿不到 Mapper。第二是使用spring-websocket模块配置 WebSocketHandler 和握手拦截器能和 Spring 容器无缝整合。我做毕设时采用的是第二种核心配置如下Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(chatWebSocketHandler(), /ws/chat) .addInterceptors(new ChatHandshakeInterceptor()) .setAllowedOrigins(*); } Bean public ChatWebSocketHandler chatWebSocketHandler() { return new ChatWebSocketHandler(); } }握手拦截器里拿到前端传来的 token校验登录态并把 userId 存到 WebSocketSession 的属性中public class ChatHandshakeInterceptor implements HandshakeInterceptor { Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) { String query request.getURI().getQuery(); MapString, String params parseQuery(query); String token params.get(token); // 校验token把userId放入attributes Long userId tokenService.getUserIdByToken(token); if (userId ! null) { attributes.put(userId, userId); return true; } return false; } }真正的消息处理在 TextWebSocketHandler 的 handleTextMessage 方法里。收到前端消息后先按业务类型解析然后分三路存库、更新会话表、推送给接收方/群成员Component public class ChatWebSocketHandler extends TextWebSocketHandler { // 用ConcurrentHashMap保存 userId - WebSocketSession 的在线映射 private static final MapLong, WebSocketSession ONLINE_USERS new ConcurrentHashMap(); Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { ChatMessageDTO dto JSON.parseObject(message.getPayload(), ChatMessageDTO.class); // 1. 消息落库 Message msg messageService.saveMessage(dto); // 2. 更新收发双方的会话列表 sessionService.updateSession(msg); // 3. 单聊推送给指定用户群聊推送给所有群成员 if (dto.getToType() 1) { pushToUser(dto.getTargetId(), formatMessage(msg)); } else if (dto.getToType() 2) { ListLong memberIds groupService.getMemberIds(dto.getTargetId()); for (Long memberId : memberIds) { pushToUser(memberId, formatMessage(msg)); } } } }这段逻辑里最少要踩两个坑。第一个JSON 序列化时不要把from_user_id这种字段名直接暴露给前端建议在 DTO 里额外放一个fromUserNickname和fromUserAvatar前端聊天界面渲染气泡时直接取不用拿着 ID 再调一次接口。消息发送者在发给别人之前自己也要通过 WebSocket 收到一条回执消息这样发送方界面才能即时出现自己发的气泡。第二个坑是并发问题。两个用户同时发消息数据库写入和会话更新要保证事务顺序saveMessage 和 updateSession 必须放在同一个Transactional方法里执行否则会出现“消息进了库会话列表不更新”的诡异 bug。3.3 会话列表、未读消息与在线状态会话列表页的核心数据来自 tb_session 的user_id 当前用户按 update_time 倒序排列。每次我收到 WebSocket 消息后如果正在聊天窗口页就清零 unread_count否则累加。这里有个关键设计未读数的维护不能靠前端自己数必须以服务端的 tb_session.unread_count 为准否则页面刷新后角标就丢了。在线状态我用一个简单方案WebSocket 连接建立时后端把该用户 ID 加入 ONLINE_USERS 映射并广播给其所有好友“我上线了”连接关闭或心跳超时后移除并广播“我下线了”。前端好友列表监听这两个广播事件实时更新在线小绿点。这一步给答辩带来的观感提升很明显——它说明你的系统不是“假聊天”而是真有实时消息驱动。3.4 Vue聊天页面的组件化实现与Vue插槽技巧聊天主界面布局上半区是会话列表左下是好友列表右上是消息区右下是输入框。这个页面不要堆在一个组件里我拆成五个子组件SessionList、FriendList、MessageList、ChatInput、ProfilePanel。消息列表组件用 v-for 渲染消息并按消息方向渲染不同气泡样式div v-formsg in currentMessages :keymsg.id classmessage-item :classmsg.fromUserId myUserId ? right : left el-avatar :srcmsg.fromUserAvatar/el-avatar div classbubble div classnickname{{ msg.fromUserNickname }}/div div classcontent{{ msg.content }}/div /div /div这里 Vue 插槽slot的作用就体现出来了。会话列表项在不同位置显示的内容不一样好友列表里显示“好友名片”会话列表里显示“最后一条消息预览 未读角标”但整个卡片的边框、点击效果、头像区域是一样的。把公共结构放进一个组件用插槽放差异化内容代码复用度立刻上来template div classcard click$emit(click) el-avatar :srcuser.avatar / div classcard-body slot nametitle{{ user.nickname }}/slot slot namesubtitle{{ user.signature }}/slot /div slot nameextra/slot /div /template还有一个热词提到“vue 内容折叠展开”这个用在会话列表的“分组”功能上很实用。比如好友列表可以按“我的好友 / 我的群聊”分组点击组标题展开收起用 el-collapse 或自己维护一个 isFold 布尔值配合 v-show 都能实现。我推荐用 Element Plus 的 el-collapse手写折叠容易在组件重渲染时丢失动画状态。4. 部署联调、论文写作与常见问题排查4.1 Vue安装与项目初始化从零开始不踩坑如果你电脑上还没有 Node.js第一件事是去官网下载 LTS 版本不要在终端里随手 brew install 或者用某个教程里的老版本。Node 版本过低会导致 Vite 报错。安装完用node -v和npm -v确认版本。国内网络环境建议把 npm 镜像切换为淘宝镜像否则npm install能卡到你怀疑人生npm config set registry https://registry.npmmirror.com创建 Vue3 项目我推荐用官方脚手架npm create vuelatest chat-web它会问你需不需要 TypeScript、Vue Router、Pinia我的选择是不需要 TypeScript毕设项目能少一个报错源就少一个、需要 Vue Router、需要 Pinia、需要 ESLint。装完以后npm run dev启动看到 Welcome 页面就说明环境通了。这里提醒一个容易心率骤停的错误。热词里有一条failed to load tsconfig vue/tsconfig/tsconfig.web.json: tsconfig not found这是你用了 TypeScript 模板但删除了相关依赖或 tsconfig 文件导致的。解决方案很简单要么别删要么回到npm create vuelatest时直接选纯 JavaScript。毕设项目不建议在 TS 的类型报错上花时间。4.2 跨域与本地联调开发环境的代理配置后端跑在 8081前端跑在 8080前端直接请求http://localhost:8081/api/user/login会触发跨域。方案有两个后端全开 CORS或者前端配置代理。我推荐前端代理因为生产环境打包后同源部署代理配置不影响线上清晰且安全。在chat-web/vite.config.js里配置export default defineConfig({ server: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true, ws: true } } } })注意ws: true必须开否则 WebSocket 的ws://localhost:8080/ws/chat代理不到后端。很多人 HTTP 请求全通了唯独消息收发一直失败查了半天最后发现是这里漏了。如果生产环境不走打包而用 Nginx 独立部署前端需要在 Nginx 上做/api和/ws两处反向代理并且 WebSocket 的代理要显式加上 Upgrade 头否则连接一次次 502。我建了个速查表后面有。4.3 前端打包放进SpringBoot经常被问到“前端打包怎么放进 SpringBoot”。其实这点在 SSM 里也一样把 Vue 项目构建后的 dist 目录整体拷贝到后端src/main/resources/static下Tomcat 启动后直接访问http://localhost:8080/index.html就能看到页面后端接口走/api路径不需要跨域。打包前有几个必须检查的点。第一前端请求后端的地址不要写成写死的http://localhost:8081开发时用代理打包时要改成相对路径/api/...否则线上环境接口全挂。第二路由要使用 hash 模式而不是 history 模式。Vue Router 的 history 模式在前端打包放进 SpringBoot 后刷新/chat/session页面会 404因为后端没有对应的路由映射。hash 模式的 URL 形如#/chat/session服务端永远只看到/路径刷新不出问题。这是新手最容易摔跤的地方const router createRouter({ history: createWebHashHistory(), // 不要用createWebHistory routes })打包命令非常统一cd chat-web npm run build然后dist目录里的index.html、assets文件夹考入后端 static。如果你在开发环境这样操作觉得麻烦可以写一个 maven 插件在 package 阶段把 dist 拷贝到 static 里但对毕设来说手工拷贝足够论文截图还更好排版。4.4 常见故障速查表与排查技巧实录我把毕设过程中最容易踩的坑整理成一个速查表你可以直接存下来当笔记现象常见原因解决方向登录后请求接口 401拦截器放行了所有请求或前端没带 token检查 excludePathPatterns 和 axios 请求头前端能登录但聊不了天Vite 代理漏了ws: true补上代理 ws 配置WebSocket 频繁断开重连缺少心跳机制或代理超时前端每隔 30 秒发 ping服务端回 pong推送消息时中文乱码数据库和连接串字符集不一致统一 utf8mb4连接串加characterEncodingutf8mb4JSON 报循环引用用户对象里含消息列表消息里又含用户用 DTO 对象隔离不直接返回 entity时间字段显示为数组Jackson 序列化 LocalDateTime 失败配置 Jackson JavaTimeModuleAutowired在 WebSocket 端点里是 null原生 WebSocket 端点不归 Spring 管理改用 spring-websocket 的 Handler或静态工具类获取 Bean消息发出去但对方会话列表没更新saveMessage 与 updateSession 不在同一事务合并为一个 Transactional 方法聊天记录一次查太多卡住消息表没有按会话索引建idx_target_query索引前端做分页加载前端打包后页面空白publicPath 或 hash 路由没配置配置 hash 路由检查静态资源相对路径两个最好用的排查技巧也分享给你。第一个是 F12 里看 Network聊天消息无非就是 HTTP 请求 WebSocket 帧两类分别在 Network 和 Messages 面板里能看到断开连接时的 close code 能直接说明原因比如 1006 是代理异常1000 是正常关闭。第二个是后端日志一定要打印 WebSocket 连接和断开的 userId连接建立后第一件事就是从 attributes 里取 userId 打日志排查“谁没连上”会快十倍。4.5 论文怎么写才能和程序严丝合缝“论文程序”这个组合最容易被导师挑毛病的不是代码而是论文内容和代码对不上。写到第几章就拿第几章的截图去对照实物别从网上抄一堆概念然后程序里根本没有。论文结构按标准八章走摘要、绪论、相关技术介绍、系统需求分析、系统设计、数据库设计、系统详细实现、系统测试。相关技术介绍这一章SSM 三个框架各写 2-3 页Vue 和 WebSocket 单独开一节重点是“为什么选她”不是给框架做百科。系统实现章节要和第 3 节的核心功能闭环一一对应登录认证、好友管理、单聊、群聊、会话列表、未读消息、在线状态每个功能一个二级节。每个功能至少一张界面截图 一段核心代码 一段设计说明三百字左右。测试章节用表格列测试用例覆盖正常流程、异常流程和边界条件比如“未登录访问接口”“群聊中退群后收不到消息”这类用例比光写“系统通过测试”有说服力得多。大标题不要离程序太远。如果程序里没有管理员后台、没有商品管理、没有支付功能论文里就不要出现这些字眼。毕设项目不怕小怕虚。4.6 聊聊升级方向与答辩经验做完这套系统以后如果你想在功能上加分我的建议优先级是消息撤回、图片/表情消息、文件传输、语音消息、消息置顶。不要贪多选一个功能做到完整闭环就足够。比如消息撤回需要处理“撤回按钮只对本人可见”“撤回后对方会话里显示一条撤回提示”这两个细节做完就是亮点。图片消息可以考虑前端用 OSS 或后端静态上传目录聊天消息里存图片 URL比把图片二进制存数据库靠谱。答辩时导师常问的几个问题提前准备好答案心里不慌“WebSocket 和 HTTP 的区别是什么”“为什么不用轮询”“token 和 session 的区别”“消息发到一半服务端挂了怎么办”“离线消息怎么补收”。最后一个问题属于加分题你可以在实现里加上“用户上线后按会话拉取最近 20 条未读消息”的接口这也正好呼应会话表的 unread_count 字段。最后说点我个人的经验。做毕设最忌讳的是前两周热血沸腾把界面画了一堆后端一张表没建后两周疯狂赶代码连事务和异常处理都来不及补。我建议把项目排期固定成第一周搭环境和数据库第二周做用户和好友模块第三周做 WebSocket 和单聊第四周做群聊和会话列表第五周做前端美化、打包部署和测试用例最后两周集中写论文。这套节奏我带过不少学生验证过按部就班走下来基本都能在答辩前一周安心改论文而不是熬夜调 bug。
返回列表