基于Spring Boot的交友平台设计与实现:从数据库到WebSocket聊天全解析
又到了一年一度的毕业设计季节每到这个时候私信里问得最多的就是同一个问题有没有一个不烂大街、又能顺利过答辩、代码量还撑得住设计与实现四个字的题目如果你也在为选题发愁这套基于Spring Boot的交友平台设计与实现可以说是一个性价比非常高的选择——业务场景贴近真实产品技术栈覆盖全面从后端接口到数据库再到部署链路都能讲出东西来。这篇博文不卖关子、不贴整套源码而是把我做这个项目时的完整思路、核心模块的拆解、表结构的设计以及答辩时老师大概率会追问的点一次性讲清楚。先说清楚这个项目适合谁。如果你是计算机相关专业的本科生正在准备毕业设计或者想自己练一个能写进简历的完整项目这套交友平台的设计思路完全可以拿来直接用。它比传统的XX管理系统有区分度又不至于像电商、社交大厂项目那样复杂到一个人做不完。技术上它用到了Spring Boot、MyBatis-Plus、WebSocket、JWT鉴权、Redis缓存每一块都能在答辩时单独展开讲两分钟撑起核心功能和系统设计两个章节完全没问题。1. 交友平台这个选题到底在考核什么能力1.1 为什么我不推荐你选图书馆管理系统每年毕设题目里管理系统占了半壁江山。这类题目的问题在于CRUD四件套写完就结束了存储过程都没用上更别提什么实时通信、匹配算法、并发控制。答辩时老师问一句你这个系统的难点在哪里很多人只能回答前端页面比较多场面很尴尬。交友平台这个题目天然避开了这个坑。它的功能边界是完整的业务闭环用户注册登录、个人资料维护、标签匹配、滑卡浏览、关注/喜欢、私信聊天、系统通知。这些功能串起来就是一个简化版的社交产品。它考验的不是你能不能写增删改查而是你会不会把一个真实业务抽象成数据模型再把它实现成可用的接口。老师看到用户画像互相喜欢才能解锁聊天这样的设计第一印象就会好很多。1.2 一个交友平台的最小功能集做毕设最忌讳一开始就想做中国版Tinder。功能列得越多最后的完成度就越差。我给你的建议是把功能收敛到下面这张表里先把这些做扎实了再考虑加东西。模块核心功能必做理由用户认证注册、登录、JWT鉴权、退出所有业务的前提个人中心头像上传、基本信息编辑、兴趣标签匹配的基础数据来源发现页推荐用户列表、滑卡/浏览、筛选条件平台的核心流量入口互动喜欢/不喜欢、互相喜欢、关注建立用户关系链聊天好友会话、私信列表、未读计数留住用户的关键功能管理后台用户管理、内容审核、数据统计体现系统完整度答辩加分项这六个模块下来后台接口数量大概在30个左右实体表8到10张。对于一个毕业设计的完成度来说这个量刚刚好——太多了一个人写不完太少了撑不起设计与实现几个字。1.3 技术栈选择的逻辑够用 可解释我给这个项目的定位是用主流技术做朴素实现。Spring Boot 3.x MyBatis-Plus MySQL Redis WebSocket这套组合的好处是每条技术选型都有明确的理由可以讲。Spring Boot 3.x目前最新的稳定主线自带Spring Doc接口文档解决了以前Springfox不兼容的问题。MyBatis-Plus单表CRUD不用写SQL分页插件比手写PageHelper省事得多代码量直接少三分之一。Redis用来存验证码、Session状态、用户在线状态、热点数据缓存。用它不是因为大家都用所以我也用而是交友场景里验证码过期、在线状态高频读写、发现页列表缓存这些都是真实需求。WebSocket私信聊天的实时推送。你要是用HTTP轮询也能做但答辩时老师只要问为什么不用轮询你就有了一个标准的发挥空间轮询存在空轮询浪费、消息延迟、服务器压力大三个问题WebSocket全双工通信能解决这些。这套组合全都不需要额外收费、社区资料齐全、教程一搜一大把。把每一层的选型逻辑吃透答辩时技术选型提问环节基本稳了。2. 数据库设计交友平台的核心是关系不是用户2.1 从用户到关系的表结构梳理数据库设计是这种项目的灵魂。交友平台跟商城、博客最大的区别在于它的核心价值是用户与用户之间的关系而不只是用户本身的数据。所以我在设计表结构时始终把关系两个字放在第一位。首先是用户主体相关的表users账号表只存登录凭证用户名、密码、手机号、状态、角色。跟展示相关的详细信息比如昵称、性别、生日、城市、个性签名、头像URL我放在user_profile里单独存。这样做的好处是用户列表页只需要查profile表不需要每次都把大字段带出来登录时只压account表查询效率也高。两张表通过user_id关联一对一关系设计上很干净。然后是标签系统。交友平台的标签是推荐算法的核心输入。我建了三张表tag存标签字典比如运动、音乐、读书、旅行user_tag存用户和标签的关联关系tag_category给标签分组。用户选择标签时前端拿到的是全部分类下的标签列表存的时候往关联表里批量插入。查询某人有哪些标签就是一条联表SQL匹配时也是基于标签做交集计算。2.2 匹配与关系的三张关键表user_matched用户匹配记录存user_id和target_id、匹配时间、匹配类型喜欢、互相喜欢、超级喜欢、状态。发现页的喜欢操作就是往这张表插一条记录同时检查对方是否已经喜欢过我如果是就把双方的状态都更新成互相喜欢并触发一条聊天会话的创建。chat_session会话表存user_id、other_user_id、最后一条消息、最后消息时间、未读数。这里给user_id和other_user_id做联合唯一索引保证两个用户之间只有一个会话。chat_message聊天消息表存会话ID、发送者、消息类型文本/图片/系统、内容、发送时间、是否已读。这三张表是整个交友业务的主干。一个用户从发现别人到建立关系到聊天所有关键状态都落在这三张表里。设计的时候我特意没有把喜欢和匹配的动作混在一起因为这个项目后期如果扩展成类似探探的玩法喜欢是异步的、匹配是双向的两者分开以后逻辑上清晰很多。2.3 索引、字段类型和冗余的取舍在设计表的时候有几个细节经验值得说说。一是会话表的冗余字段。chat_session里存final_message和final_time虽然违反了严格意义的第三范式但这是有意为之。聊天列表页加载的时候不需要去查最新的消息记录直接读session表就行避免每进入一次会话列表就做一次全表子查询的尴尬。对于毕设这个规模的数据量这个冗余换取的是极大的查询性能提升。二是datetime类型统一用datetime(0)不要用timestamp。MySQL的timestamp范围只到2038年而且有时间 zone的问题做得好的项目基本都避开它。相关时间字段全部建上索引尤其是user_matched里的create_time和chat_message里的send_time因为列表页都按时间倒序。三是软删除 vs 物理删除。用户注销、消息撤回这类操作我统一用deleted字段做逻辑删除。对C端产品来说数据是可追溯的资产物理删除后无法恢复。而且MyBatis-Plus内置的逻辑删除支持配置一下全局deleted就行不费什么功夫。3. 后端核心模块的实现从登录鉴权到实时聊天3.1 JWT鉴权让无状态接口撑住移动端和前端这个项目我选择了JWTJSON Web Token替代传统Session。Spring Boot生态里Spring Security集成JWT是最常见的做法但配置起来确实绕。我个人的建议是如果只是为了毕设可以不用Spring Security那一套重武器直接在拦截器里校验JWT代码量少、逻辑直观、答辩时更容易讲清楚。核心流程是这样的用户登录成功后后端生成一个JWT返回给前端前端存储起来并在每次请求的请求头里带上Authorization: Bearer token。后端写一个JwtInterceptor拦截器在WebMvcConfigurer里注册对需要登录的接口做拦截。拦截器里做的事情只有两件解析token判断是否过期、把用户信息放入ThreadLocal供后续业务使用。public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); } // 解析token校验签名和过期时间 Claims claims JwtUtil.parseToken(token); if (claims null) { response.setStatus(401); return false; } // 用户信息放入ThreadLocal UserContext.set(claims.get(userId).toString()); return true; } }这里有个坑要提醒JWT的过期时间不宜过长。很多项目直接把过期时间设成7天甚至30天看似方便实际上是有安全风险的——token一旦泄漏攻击者能拿到一个长期有效的身份通行证。我建议access token过期时间设为2小时前端在收到401时自动跳登录页重新登录。如果想做得更完善可以引入Refresh Token机制但对毕设来说2小时的过期时间加上重新登录已经足够了。3.2 发现页推荐匹配度的计算不复杂关键是可解释发现页是交友平台最核心的页面。我给它的定位是给用户推送一批可能感兴趣的人按匹配度倒序排列。匹配度的计算不需要用什么机器学习模型一个简单的加权打分就够用了。我给每个维度分配了权重标签重合度占50%、年龄相近度占20%、距离城市占15%、活跃度占15%。标签重合度用交集数量除以并集数量得到相似度年龄相近度按年龄差的绝对值映射到0到1活跃度按最近7天登录天数正则化。public double calcMatchScore(Long userId, Long targetId) { UserProfile me profileMapper.selectById(userId); UserProfile other profileMapper.selectById(targetId); double tagScore calcTagSimilarity(userId, targetId); // 标签相似度 double ageScore calcAgeSimilarity(me.getBirthday(), other.getBirthday()); double activeScore calcActiveScore(userId, targetId); // 最近7天活跃度 return tagScore * 0.5 ageScore * 0.2 activeScore * 0.15 locationScore * 0.15; }页面加载时只查当前用户滑到的位置用offset做分页推荐列表本身就按分数排好了。这个算法的好处是答辩时你能清清楚楚地说出我的匹配度由四个维度构成权重分别是多少比模糊地说用了协同过滤但说不出细节要强太多。这里的性能优化我做得比较简单直接给user_profile表加索引gender、city、age查询时根据当前用户的性别偏好先过滤性别再取一定数量的候选集做打分排序。数据量大的场景可以引入Redis缓存每日推荐结果不过对于毕设系统SQL层面优化已经足够了。3.3 互相喜欢解锁聊天业务规则和并发边界交友平台的聊天跟微信不一样——不是任何人之间都能聊而是互相喜欢才能建立会话。这个规则的实现要非常小心因为涉及两个用户同时操作对方喜欢按钮的并发场景。我的实现逻辑是这样的用户A点击喜欢用户B 1. 校验A和B是否已经配对 2. 插入一条user_matched记录typelike, user_idA, target_idB 3. 查询是否已存在 user_idB, target_idA 且 typelike 的记录 4. 如果存在把两条记录状态都更新为matchedtrue创建聊天会话 5. 如果不存在只保留A喜欢B的状态这里最怕两个人同时点喜欢对方导致双方都只查到对方没有喜欢我从而错过配对。解决方式是在第3步的查询里加一个事务把插入A喜欢B和查询B是否喜欢A放在同一个事务中同时给user_matched表加唯一索引user_id, target_id防止重复插入。事务隔离级别用默认的即可用索引兜底防并发这个方案在毕设体量下是足够正确的。3.4 WebSocket聊天握手和消息推送里最容易被问到的三个点聊天功能是交友平台里最能拿得出手的亮点模块。技术选型上WebSocket是唯一合理的方案——HTTP轮询在消息实时性和服务器开销上都是劣化选择。实现细节上我有三个环节想重点说一下。第一个是握手阶段的鉴权。WebSocket的握手请求没法像普通HTTP请求那样自定义请求头很方便浏览器原生WebSocket API不支持所以我把token放在URL参数里传递ServerEndpoint(/ws/chat?token{token})握手时从URL里取token再校验。这里建议在ServerEndpoint配置里写一个Configurator来处理请求头的读取因为URL传token会被记录在日志里严格来说存在泄露风险。不过毕设层面大多数人都直接URL传参我在答辩时如果被问到就明确说生产环境会用Subprotocol或握手请求头方式传token毕设简化处理。第二个是消息的持久化与推送分离。用户A发消息给用户B后端流程是先把消息落到数据库然后通过WebSocket推送给B同时在A自己的聊天窗口也回显一条一般由前端自己处理本地状态。落库后再推送这样即使推送失败消息也不会丢可以下次拉取时补偿。会话表里的last_message和未读数在这个流程里一并更新。第三个是未读消息数的维护。每次发送消息除了插消息表还要把会话表里对方的unread_count加1。用户打开会话窗口时要置零。这个字段我放在Redis里做准实时更新定期同步到MySQL。不过说实话经过个人实测对毕设这个规模直接用MySQL字段更新完全够用因为并发量根本打不上去。没必要为了高性能给自己增加分布式复杂度。如果答辩时有老师追问高并发场景怎么办你可以说Redis作为缓存层MySQL做持久化最终一致性保障这就够了。4. 从零到一跑通这个项目的完整步骤4.1 环境准备和项目初始化这个项目的开发环境我用的是JDK 17、Maven 3.9、MySQL 8.0、Redis 6.x。前两个是Spring Boot 3.x的硬性要求如果你的JDK还是8不要挣扎直接装17或者用Spring Boot 2.7。因为Spring Boot 3.x基于Jakarta EE脱离javax包JDK8根本编译不过。初始化项目我推荐直接用IDEA的Spring Initializr创建依赖选上Spring Web、MyBatis-Plus手动引入、MySQL Driver、Lombok、Validation、Spring Boot Actuator。Lombok能省掉大量getter/setter这对于代码量焦虑的同学是巨大福音。组件版本上用Spring Boot自带的版本管理管住不需要手动指定版本号省掉了一堆版本冲突的麻烦。项目结构上我推荐按模块分包controller、service、mapper、entity、config、common、dto、vo。不要把所有类平铺在一个包下等你写到第30个类的时候就会感谢当初的分包结构。4.2 核心接口开发顺序建议接口开发的顺序会影响你的调试效率。我建议按依赖关系从底层往上写认证模块注册、登录、验证码。这个不做后面所有接口都没法测个人资料模块查看/编辑资料、上传头像、设置标签。发现页模块推荐列表、喜欢/不喜欢、互相喜欢逻辑。聊天模块会话列表、消息记录、WebSocket推送。后台管理模块用户列表、数据统计。每一步写完都立刻用Postman或者Apifox测试不要攒到最后统一测。很多同学喜欢全部写完再测结果一个变量名写错了报错信息在国际里面绕了一大圈才排查出来。开发时把接口测试和编码并行着做效率能提升50%。4.3 本地联调和常见报错的排查链路联调过程中的坑我挑三个最常见的说一下。第一个坑跨域问题。前端在8080端口后端在8081端口AJAX请求一报跨域错误。解决方案是在后端加一个CorsFilter注册到WebMvcConfigurer允许所有源和所有请求头开发环境就完事了。生产环境再收紧到前端域名。第二个坑日期格式化。后端返回的LocalDateTime默认格式是2026-04-05T10:15:30前端显示出来很丑。在application.yml里配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8前后端时间格式就对齐了。这个配置极其常见几乎每个项目都会遇到。第三个坑MyBatis-Plus分页插件没配置。直接调用PageUser发现返回的总条数不对甚至分页不生效。这是因为MP的分页插件需要手动配置一个拦截器否则分页参数被吞掉Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这个配置加上分页就正常了。凡是遇到分页查询结果异常先检查是不是忘了这条。4.4 打包部署一键把毕设变成线上地址毕设做到最后很多同学能本地跑起来就万事大吉了。但一个能通过域名访问的项目和一个只能在IDEA里跑的项目答辩时的观感是两个级别。部署其实不复杂。首先在pom.xml里确保spring-boot-maven-plugin存在执行mvn clean package -DskipTests得到可执行Jar包。服务器上装好JDK17直接nohup java -jar xxx.jar app.log 21 就能跑起来。如果你不想买服务器用内网穿透工具把本地8000端口服映射出去也行演示效果一样。生产环境的配置要单独处理别把localhost的地址打包进去。我在application.yml里用application-prod.yml专门放生产配置部署时启动命令加上--spring.profiles.activeprod指定环境。数据库密码和Redis密码不要明文写在配置文件里用环境变量注入java -jar app.jar --SPRING_PROFILES_ACTIVEprod --DB_PASSWORDxxxxLinux上还可以配合nohup做日志输出重定向方便后端排查问题。5. 管理后台和数据安全答辩时最容易出彩的部分5.1 管理后台不是凑数的它是安全观的体现很多毕设的管理后台都是拿来自欺欺人的——能登录、能看到用户列表、然后没了。我建议你在设计管理后台时把内容审核和数据统计两个功能做出来这是体现系统完整性最有效的两块。内容审核功能面向的是交友平台最敏感的问题用户上传的头像、个性签名、私聊文本可能含有违规内容。作为毕设来讲不需要你训练一个AI模型来做自动审核但你可以在引入相关工具进行文本审核和分析对昵称、签名、聊天消息做敏感词过滤命中后自动标记待人工审核。这一整套审核写入后台的审核任务列表里管理员可以查看处理。这个设计在答辩时有很好的故事性你能从交友平台为什么需要审核讲到C端产品如何做合规再落到我的系统实现了哪些具体拦截策略。每一句话都是干货老师会看到你对产品有完整的思考。5.2 数据分析用最简单的SQL画出指挥官的仪表盘数据统计方面不需要用复杂的数据可视化框架后端提供接口前端通过框架渲染几个图表就行。我做的统计包括每日新增用户数、男女比例、活跃用户数、匹配成功数、最近7天消息量趋势。其实配了MyBatis-Plus后这些数据用简单的SQL聚合就能查出来。比如近7天消息量趋势SELECT DATE(send_time) AS day, COUNT(*) AS msg_count FROM chat_message WHERE send_time DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(send_time) ORDER BY day;这种SQL言简意赅地展示了业务数据的走势。真正的后台首页图表面板加上这些数据整个系统的完成度立刻就不一样了。5.3 认证与授权普通用户和管理员不能混用一套接口权限控制是系统设计里最后一个必须交代清楚的问题。我的方案是JWT里带role字段拦截器校验登录状态后再判断接口需要的角色。管理员接口加上RequireRole(ADMIN)注解在拦截器里统一校验。具体实现不复杂自定义一个注解在HandlerInterceptor里读取方法上的注解比对当前用户的角色。这样用户接口和管理接口完全分离登录接口不能访问管理端管理端接口也不能被普通用户调用。这里顺带说一句密码存储务必用BCrypt加密严禁明文或MD5存储。Spring Security的BCryptPasswordEncoder可以直接拿出来用不需要引入完整的Spring Security框架。答辩时如果问密码安全你就回答BCrypt是自适应哈希算法自带随机盐能够抵抗彩虹表攻击这一句就够硬了。6. 源码分享里的加分细节和答辩话术6.1 让源码看起来更专业的几个小习惯看别人的毕设源码和看大厂项目的源码观感差距很多时候不在功能多寡而在代码卫生。下面这些习惯花不了多少时间但能让你的代码质量感知提升一个档次。统一返回结果封装。不要一个接口返回Map一个接口返回JSONObject。封装一个ResultT类code、message、data三个字段所有接口统一返回。全局异常处理。用RestControllerAdvice统一捕获业务异常、参数校验异常、未知异常返回统一格式。不要一报错就把500堆栈直接抛给前端。VO/DTO不要混用。数据库实体类跟接口返回对象分开避免直接把密码字段序列化返回给前端。一个简单的JsonIgnore注解就能解决密码泄露问题但是更职业的做法是建对应的VO类。接口注释必须写。用Swagger注解或者只用Javadoc都行但每三层看能不能看懂代码在答辩自述环节你能流畅说清每个接口的作用本身就是印象分。6.2 答辩环节的高频问题和参考回答思路我带过不少学生的毕业设计答辩交友平台这个题目被提问的问题相对集中在以下几个方面提前准备好基本没有死角。问题一为什么选择WebSocket而不是普通HTTP轮询参考思路HTTP轮询存在消息实时性差、无效请求占用带宽、服务器连接资源消耗高三个问题。WebSocket在建立连接后保持长连接双向通信服务端可以主动推送消息延迟低至毫秒级。结合聊天业务频繁、消息体小的特点WebSocket是最合适的。前端断线时还有心跳检测和重连机制兜底。问题二你的推荐算法和大型平台的推荐算法有什么差距参考思路大型平台使用协同过滤、深度学习排序模型等本体量差异很大。但我的设计采用了特征加权打分模型通过标签重合度、年龄相近度、活跃度等维度给候选用户打分排序并在架构上预留了后续引入更复杂算法的接口可以平滑演进。这个回答的核心是先承认差距再强调演进空间。问题三如何保证互相喜欢这个操作的并发安全参考思路从数据库层面使用唯一索引防止重复关系从事务层面通过事务包裹插入喜欢记录-查询反向喜欢两步操作保证原子性从业务层面支持幂等重试。三个层面分别作答逻辑清晰对方自然会认为你理解了并发控制的本质。问题四系统上线后最需要关注什么参考思路交友平台的核心风险是内容和安全合规——垃圾注册、骚扰消息、违规内容。我在系统里实现了敏感词过滤、账号状态管理和人工审核机制生产环境还需要接入更完善的实名认证机制和更强大的风控策略。这个问题只要答到内容和安全合规这个点上就已经超过绝大多数学生了。问题五你这套系统的瓶颈在哪里参考思路当前系统的性能瓶颈在数据库层面单库单表单表用户量达到一定规模后需要分库分表、读写分离。WebSocket的连接数也有上限大规模部署时需要引入消息中间件做广播。这样的回答展示了你有全局架构的视野而不是只盯着自己的代码。6.3 给源码加分的隐藏项接口文档、Dockerfile和README最后说三个很多学生容易忽略、但性价比极高的加分项。第一个是自动生成接口文档。集成springdoc-openapiSpring Boot 3对应版本是springdoc-openapi-starter-webmvc-ui完全零配置启动后访问/swagger-ui.html就能看到所有接口的说明。比手写Word文档简洁得多而且永远和代码同步。第二个是写一个Dockerfile。项目根目录放一个Dockerfile内容就几行FROM openjdk:17-jdk-alpine COPY target/app.jar /app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app.jar]三行指令就把我会容器化部署这件事写到了明面上。答辩时老师如果问部署方式你就可以很自然地说Docker部署支持快速水平扩展。第三个是认真写README.md。把项目简介、技术栈、数据库初始化方式、启动步骤、默认账号密码、核心接口说明都写清楚。一来帮助自己回忆代码二来老师拿过去之后可以直接运行起来验收体验完全不同。很多学生的源码发给老师后根本跑不起来毛病就出在缺了一个能说清楚怎么跑起来的文档上。我在实际做这套项目时最深的体会是毕业设计不是要你做出一个改变世界的产品而是要用完整的工程思维把一个选题做完。从数据库设计到接口开发从联调部署到答辩演示每一个环节都留下清晰的设计痕迹和实现记录评审老师自然会给高分。这套Spring Boot交友平台恰好在这条路径上的每一个节点都给你留好了足够的发挥空间。只要按着上面的思路走下来它一定是你大学四年里最拿得出手的一个项目。