☰
SpringBoot3+Vue3交友平台系统设计实战:从架构到部署
2026/9/26 6:38:07 网站建设 项目流程

1. 从项目标题说起:这个交友平台到底在解决什么问题

拿到“springboot3基于vue3的交友平台系统设计(编号:146090174)”这个标题时,第一反应不是急着上手写代码,而是先琢磨清楚一件事:这类项目在毕设、课设、个人作品集里出现频率极高,但大多数人做出来的东西只是“能登录、能加好友、能发消息”的CRUD堆叠,根本没有触及交友平台的本质。

我理解的核心需求是这样的:交友平台不是普通的内容社区,它背后有一条完整的业务链——用户注册与身份认证、资料完善与兴趣标签、用户推荐与匹配、好友关系管理、即时聊天、动态互动,以及最容易被忽略的审核与安全机制。任何一个环节做得太浅,整个系统都会显得“假”。而选型定了 springboot3 和 vue3,说明你的技术栈是当前主流的前后端分离方案,这也意味着你需要把前端工程化、后端接口设计、数据库建模、部署上线整条链路都走通。

这篇博文我会从一个实战者的角度,把整个系统从设计到落地的关键环节拆开讲,重点说清楚三个问题:为什么这么做、怎么做、踩过哪些坑。文章末尾我会给出一份完整的核心代码片段和配置文件参考,让你拿去就能改、改完就能跑。

2. SpringBoot3 与 Vue3 的组合为什么是当前最优解

2.1 版本选型背后的技术逻辑

SpringBoot3 和 Vue3 不是凭空选出来的组合,而是被生态推着走的必然结果。SpringBoot3 在 2022 年底正式发布,底层基于 Spring Framework 6,最重要的变化是强制 JDK17+,同时全面拥抱 Jakarta EE 命名空间(javax 变 jakarta)。这意味着你不能再拿老一套 JDK8 的思维去写代码了,但也正因为如此,SpringBoot3 的启动速度、内存占用、响应式编程支持都比 SpringBoot2 有明显提升。

Vue3 这边,组合式 API 是最大的分水岭。Vue2 时代的 Options API 写业务逻辑时,一个组件里 data、methods、watch、computed 各据一方,代码一多就要靠 mixin 去抽公共逻辑,抽到最后依赖混乱到你自己都分不清这个方法是从哪个 mixin 进来的。Vue3 的 setup 语法糖配合 Composition API,让同一段业务逻辑天然归属在一起,算是真正解决了复杂组件状态管理的痛点。

这套组合放到交友平台场景里,优势非常具体:

  • 高并发下的响应能力:交友平台典型的“瞬间流量”发生在用户活跃高峰期,比如晚八点到十一点。SpringBoot3 内置的虚拟线程(JDK21 环境下)和处理模型比 SpringBoot2 更适合应对大量 IO 密集型请求,虽然个人项目未必真的能压到那个量级,但架构上不输。
  • 前后端独立开发效率:Vue3 + Vite 的开发服务器热更新速度比 Webpack 快一个数量级,改一行代码一秒钟内就能看到效果,这在实际开发中非常影响心情和节奏。
  • 生态成熟度:SpringBoot3 对应 Spring Security 6、MyBatis-Plus 3.5+、Knife4j 4.x 都已经稳定兼容;Vue3 这边有 Element Plus、Pinia(替代 Vuex)、Vue Router 4、Axios 全家桶,文档齐全,遇到问题搜得到答案。

2.2 技术栈清单与版本对应

我直接给你一份实践过的、能跑通的所有核心依赖版本对照,照着配不会踩版本坑。

组件版本建议说明
JDK17+SpringBoot3 强制要求,建议直接用 21,虚拟线程更好用
SpringBoot3.2.x3.1 后的版本更稳,3.2 对虚拟线程支持更完善
MyBatis-Plus3.5.5+升级了新分页插件,避免旧的 PaginationInnerInterceptor 报错
Spring Security6.2.x和 SpringBoot3 配套,配置方式有变化,注意不兼容旧代码
JWTjjwt 0.12.x新包名改成了 io.jsonwebtoken,和旧版本的 import 路径不同
Vue3.4.x安装时用 create-vue 脚手架,Vite 5
Vite5.x模板自带,无需单独选
Element Plus2.7.xUI 组件库,表单、表格、弹窗全用它
Pinia2.1.x状态管理,比 Vuex5 靠谱,官方推荐

这套选型的核心原则只有一个:所有库都跟着官方最新稳定路线走,不贪新不守旧。比如你搜到一堆“MyBatis-Plus 新版分页插件写法变了”的帖子,就是因为有人用了 3.5.5 却拿着老版配置在抄,这种坑一旦踩上,排查时间远超重新搭一遍。

3. 交友平台核心模块拆解:不要做成普通“通讯录”

3.1 用户画像模块:交友推荐的基石

很多人做这个项目,用户表就三个字段:用户名、密码、性别。这太应付了。交友平台的核心价值在于“匹配”,而匹配需要的是用户画像维度:身高、学历、职业、城市、兴趣标签、性格标签、照片墙、个人签名、活跃度、在线状态。

我建议用户表保持基础字段简洁,个性信息单独拆一张 profile 表,用 user_id 一对一关联。这样做的好处是:以后扩展“更多维度”的筛选条件时,不需要动主表;查询列表时如果不需要详情,也可以避免大字段拖累 IO。

CREATE TABLE `user_profile` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '用户ID', `nickname` varchar(64) NOT NULL, `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', `gender` tinyint DEFAULT NULL COMMENT '1男 2女 0保密', `birthday` date DEFAULT NULL, `height_cm` int DEFAULT NULL, `education` tinyint DEFAULT NULL COMMENT '学历编码', `city` varchar(32) DEFAULT NULL, `job` varchar(64) DEFAULT NULL, `bio` varchar(500) DEFAULT NULL COMMENT '个性签名', `tags` varchar(255) DEFAULT NULL COMMENT '兴趣标签ID,逗号分隔', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户画像表';

这里有个容易被忽略的细节:tags字段不能只存中文标签字符串,否则将来做“按标签筛选用户”时 SQL 非常难写。正确做法是标签主表tag存标签定义,用户标签关系表user_tag存 userId + tagId 多对多关系,列表页展示时再聚合。如果你嫌复杂,也可以学我用“标签ID逗号分隔 + 后端解析后再批量查标签名”的折中方案,但基础表设计一定要留扩展余地。

3.2 匹配推荐模块:从“简单筛选”到“有点智能”

这一块是论文和答辩时最容易出彩的地方。推荐不能只靠一条 SQL 按城市+性别筛出来,那样显得太傻。用用户画像匹配,可以算出一个“相似度分数”,按分数倒序推荐。

我的做法是:把每个用户映射成一组特征标签,比如【健身】【摄影】【程序员】【养猫】,把标签集合转成向量,然后算当前用户和目标用户之间的 Jaccard 相似系数。公式很简单:

J(A, B) = |A ∩ B| / |A ∪ B|

打个比方:你的标签是【健身、摄影、读书】,对方的标签是【健身、跑步、读书】,交集是【健身、读书】= 2,并集是【健身、摄影、读书、跑步】= 4,相似度 = 2/4 = 0.5。分数越高越靠前。

这只是一个雏形,但论文里可以讲清楚“基于标签的协同过滤思想”,并留出扩展接口,以后换成 embedding 向量做深度推荐也不至于推翻整个架构。

实际实现时,你可以在后端用一次查询把候选用户的标签都拉出来,然后在内存里跑相似度计算。数据量小的时候这种方式最快,数据库不用做复杂 join,也没必要引入 Redis 做缓存(虽然加了 Redis 显得更高级,但小项目性能瓶颈不在这)。

3.3 好友关系与聊天模块:别把“关注”和“好友”混为一谈

交友平台的好友关系一般有三种状态:陌生人、已申请、已是好友。关系表很难只用一个字段表达清楚,建议用方向性设计:

CREATE TABLE `user_relation` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '主动方', `target_user_id` bigint NOT NULL COMMENT '被动方', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1待验证 2已通过 3已拉黑', `create_time` datetime NOT NULL, `update_time` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_target` (`user_id`, `target_user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

处理好友申请的逻辑很关键:你查看目标用户详情时,需要知道“我是主动方还是被动方”,因为不同的方向对应不同的按钮状态。如果我是主动方且 status=1,显示“等待对方验证”;如果我是被动方且 status=1,显示“接受/拒绝”。这一步查询逻辑看似简单,前后端联调时最容易乱,建议后端在返回用户详情 DTO 时直接附带relationStatus字段,让前端少做一层判断。

聊天模块我走了 WebSocket 的技术路线,SpringBoot3 里用spring-boot-starter-websocket比较顺。单机部署时可以在一个服务内维护 Session 映射,消息发出去后按接收方 userId 找到对应 WebSocket Session,直接推送。如果以后上集群,再引入 Redis 消息订阅来广播,架构上不冲突。

@Component public class ChatEndpoint extends TextWebSocketHandler { private static final Map<Long, WebSocketSession> SESSIONS = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Long userId = (Long) session.getAttributes().get("userId"); SESSIONS.put(userId, session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { JSONObject msg = JSON.parseObject(message.getPayload()); Long toUserId = msg.getLong("toUserId"); WebSocketSession toSession = SESSIONS.get(toUserId); if (toSession != null && toSession.isOpen()) { toSession.sendMessage(new TextMessage(msg.toJSONString())); } } }

这段代码是最简版本,没有存离线消息,也没做已读回执。如果你想在答辩时增加亮点,建议加一个消息发送记录到数据库,加一个is_read字段,并在对方下线时写离线消息表,等对方上线后再补推。这样一个简单的改动,工作量不大,但体现出的“需求完整性”远超普通课设。

3.4 动态广场与内容审核:交友平台不能变成“法外之地”

动态功能让平台有“社区感”,但它也是内容安全风险最集中的地方。我的意见是:哪怕是为了应付答辩,动态发布接口也必须做文本敏感词过滤和图片安全审核的预留位。

文本过滤我用了开源的 sensitive-words 库,基于 DFA 算法,在服务端对发布内容做敏感词检测,发现后拒绝发布或替换成*。图片审核方面,个人项目接阿里云/腾讯云的审核 API 需要实名认证且要花钱,可以做成一个策略接口,默认实现是本地占位,只有名称没有真实逻辑,论文里把这个设计写清楚,说明“接口可扩展接入云服务”,这比你硬说自己实现了什么更可信。

动态表的核心字段包括:user_id、content、images(JSON 数组字符串)、location、like_count、comment_count、status(0待审 1已发布 2已删除)。点赞和评论拆成单独的表,不要用一个大字段去统计数字,因为并发下要保证数据一致性,用 Redis 加缓存再定时落库是理想方案,但为了简单,我用的是“点赞记录表 + 动态表冗余计数字段,事务更新锁”的方式。

4. 前后端实操:从脚手架搭建到接口联调的完整流程

4.1 后端工程初始化与基础配置

创建项目我建议直接用 Spring Initializr(start.spring.io),选好 SpringBoot 3.2.x、Java 17、依赖选择 Web、Security、MySQL Driver、Validation、Lombok。生成完导入 IDE 后,先把application.yml按自己的环境配好。

我的核心配置参考如下:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/friend_platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver data: redis: host: localhost port: 6379 mybatis-plus: mapper-locations: classpath:mapper/*.xml global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0

两个提示,都是实战得来的教训:

  • SpringBoot3 的 MyBatis-Plus 分页插件写法变了。在新版中要这样注册:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

千万别再抄老博客里PaginationInterceptor的名字,类在 3.5.5 里已经没了。

  • Jackson 处理 LocalDateTime:SpringBoot3 默认会把LocalDateTime序列化成数组,前端拿到的不是字符串,非常难受。配置类里加一行:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai

如果你返回 JSON 里还有 Long 类型的 ID(雪花ID),记得对前端精度丢失做处理:把所有 Long 字段加@JsonFormat或者在配置里把 Long 转成 String。

4.2 Spring Security 与 JWT 鉴权实现

交友平台里每个需要登录的接口都要认证,但不能每个请求都去数据库查密码。我的方案是 JWT 无状态认证:用户登录成功后发放 token,后续请求在 header 里带Authorization: Bearer <token>,后端写一个 OncePerRequestFilter 去解析校验,把 userId 放进 SecurityContext。

Spring Security 6 的配置代码跟旧版有差异。最显著的变化是:WebSecurityConfigurerAdapter被废弃了,你必须用SecurityFilterChain+@EnableWebSecurity的方式。再造一个步骤列表给你,照做就通:

  1. 写JwtUtil工具类,负责生成 token 和解析 token。
  2. 写JwtAuthenticationFilter继承OncePerRequestFilter,从 header 取 token,解析出 userId,塞进 SecurityContext。
  3. 在 Security 配置类中设置csrf.disable(),sessionManagement为 STATELESS。
  4. 路径放行规则:/api/auth/**放行,其他接口需要鉴权。
  5. 注册PasswordEncoder为BCryptPasswordEncoder,注册过滤器。

登录接口的逻辑是流程里最容易出问题的环节:先查用户是否存在,再用passwordEncoder.matches(rawPwd, encodedPwd)比对,比对成功后签发 token。注意不要直接把密码字段返回前端,也别在日志里打密码。

4.3 前端 Vue3 项目搭建与页面结构设计

用 Vite 脚手架创建项目:

npm create vue@latest friend-frontend

交互式选择时,Router、Pinia、ESLint、Prettier 都选是,测试之类的可以不要。进入项目后先装依赖:

npm install npm install element-plus axios

前端目录结构建议按业务模块拆,而不是一直堆 components:

src/ api/ # 接口请求封装,按模块分文件 router/ # 路由配置 stores/ # Pinia 状态 views/ login.vue register.vue home.vue # 用户推荐列表 profile.vue # 个人中心 profile-edit.vue chat.vue # 聊天页面 moments.vue # 动态广场 components/ layout/ user-card.vue chat-window.vue utils/ request.js # axios 封装,拦截器

Axios 封装时一定要加请求拦截器和响应拦截器。请求拦截器统一带上 localStorage 里的 token,响应拦截器判断 status 码,如果是 401 就跳登录页。这个部分是所有页面联调的基础,写好后能省大量重复代码。

4.4 前端核心页面实现要点

登录页和注册页相当于门面,UI 做得好不好看在次要,逻辑没闭环才是大问题。注册时要校验用户名唯一性、密码两次输入一致、手机号/邮箱格式、年龄范围,这些前端校验和后端校验都要做。记住,前端校验是为了用户体验,后端校验才是安全底线。

推荐列表页home.vue是最核心的展示页面。页面加载时调用推荐接口,后端返回用户列表+相似度分数+是否已是好友,前端用卡片布局展示,点击“喜欢”或“不喜欢”按钮后,可以调用记录动作的接口,也可以先本地隐藏这张卡片,提高浏览流畅度。交互反馈一定要及时,否则用户会觉得卡。

聊天页的坑最多。WebSocket 连接要在用户进入聊天页时才建立,离开时关闭;消息气泡要用 v-for 渲染,自己发的靠右、对方发的靠左;收到 WebSocket 新消息时把消息 push 进列表,同时滚动到底部。这里有一个体验细节:连续 push 多条消息时,底部滚动如果每次都强制滚到底部,会导致用户中途想往上翻历史时被拽下来。建议只在用户当时位于底部时才自动滚底。

动态广场的页面是一个两列瀑布流布局,卡片展示内容和图片,点赞和评论使用局部刷新,不必整个页面重新请求。发布动态时用表单弹窗,上传图片组件调用后端upload接口,成功后把返回的 URL 拼在 images 数组里。

5. 数据库设计与接口规范:这些细节决定项目上限

5.1 核心表结构全景

我完整列出建表建议,共六张核心表,足够撑起一个能答辩的系统:

  • user: 账号表(id、username、password、phone、status、create_time)
  • user_profile: 用户画像表(同上文)
  • tag: 标签表(id、name、type)
  • user_tag: 用户标签关系表(id、user_id、tag_id)
  • user_relation: 好友关系表(上文已给出 SQL)
  • chat_message: 聊天消息表(id、from_user_id、to_user_id、content、type、is_read、create_time)
  • moment: 动态表(id、user_id、content、images、like_count、comment_count、status、create_time)
  • moment_like: 动态点赞表(id、moment_id、user_id、create_time)
  • moment_comment: 动态评论表(id、moment_id、user_id、content、create_time)

这些表之间不要在数据库层建外键,逻辑层约束就够了。外键在高并发时影响写入性能,也会让数据迁移变得僵硬,这是行业实践里普遍认同的做法。

5.2 API 接口路径规范

接口设计要遵循 RESTful 风格外,还要明确统一返回结构,最好定义成Result<T>包装类:

@Data public class Result<T> { private Integer code; private String message; private T data; }

code=200 表示成功,400 表示参数错误,401 表示未认证,500 表示服务异常。前端 axios 响应拦截器拿到 code 后统一提示,不用每个页面手写错误处理。

核心接口列表,按模块分类:

模块接口方法说明
认证/api/auth/registerPOST注册
认证/api/auth/loginPOST登录
用户/api/user/{id}GET获取用户详情+画像
用户/api/user/profilePUT修改资料
推荐/api/recommend/listGET推荐用户列表(分页)
关系/api/relation/addPOST发送好友申请
关系/api/relation/acceptPOST同意申请
聊天/api/chat/historyGET拉取二人聊天记录
动态/api/moment/pageGET动态分页列表
动态/api/moment/publishPOST发布动态
动态/api/moment/likePOST点赞/取消点赞
上传/api/upload/imagePOST图片上传

5.3 分页与条件查询的常见坑

推荐列表需要分页,但交友平台最好是“滑动加载”,后端接口传pageNum和pageSize,前端滚动到底部时把页码加一再去请求,然后追加到数组后面。MyBatis-Plus 的分页查询,注意返回的是IPage<T>,前端需要的字段是records、total、current、size,不要搞混。

条件筛选是另一个大坑:按照年龄、城市、学历筛选时,参数可能为空。SQL 语句里不能傻傻地拼where city='',要用 MyBatis-Plus 的 LambdaQueryWrapper 做条件判断:

LambdaQueryWrapper<UserProfile> wrapper = Wrappers.lambdaQuery(); wrapper.eq(StringUtils.hasText(city), UserProfile::getCity, city); wrapper.ge(minAge != null, UserProfile::getBirthday, maxBirthdayByAge); wrapper.le(maxAge != null, UserProfile::getBirthday, minBirthdayByAge);

注意年龄和生日的模糊转换:你想筛 20-30 岁,生日范围是倒过来的。如果前端传的是年龄区间,后端得想办法转换。我建议接口直接收birthdayStart、birthdayEnd,前端用日期选择器,这样最不容易出错。

6. 部署上线:从开发到公网可访问的完整链路

6.1 后端打包与运行

SpringBoot3 项目打包成可执行 jar 很简单:

mvn clean package -DskipTests

生成target/friend-platform.jar,放到服务器上运行。我用的是阿里云轻量服务器,2核4G 足够跑这套系统。先装好 JDK17 和 MySQL8:

yum install -y java-17-openjdk

启动命令:

nohup java -jar friend-platform.jar --spring.profiles.active=prod > app.log 2>&1 &

注意生产环境数据库密码不要写在 yml 里,用环境变量注入:

spring: datasource: password: ${DB_PASSWORD}

启动后访问 actuator 或写个简单的/api/ping接口测试连通性。

6.2 前端打包与 Nginx 配置

Vue3 项目执行npm run build后,静态文件在dist目录。用 Nginx 托管,转发/api和后端 WebSocket:

server { listen 80; server_name your-domain.com; root /usr/share/nginx/html/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /ws/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }

WebSocket 的代理配置很容易踩坑。如果少了proxy_set_header Upgrade $http_upgrade;那几行,浏览器会一直连接失败,控制台报 101 Switching Protocols 失败之类的错。别问我怎么知道的,我在这上面浪费过一下午。

还有一个前端路由问题:Vue Router 默认是 history 模式,刷新某个二级页面时 Nginx 会返回 404。解决办法是在 Nginx 加 try_files:

location / { try_files $uri $uri/ /index.html; }

这句话的意思是:找不到对应文件时,回退到 index.html,把路由交给前端处理。

6.3 HTTPS 证书配置(加分项)

就算个人项目,我也建议把 HTTPS 上了。免费证书一大把,申请简便。Nginx 里配置:

server { listen 443 ssl; ssl_certificate /etc/nginx/cert/your_domain.pem; ssl_certificate_key /etc/nginx/cert/your_domain.key; # 其他配置同 80 server }

配置完别忘了把 80 端口的请求重定向到 443。

7. 我从这个项目里挖出来的坑:问题排查与经验实录

7.1 SpringBoot3 + Spring Security 6 的 CORS 配置

前后端分离部署后,前端是 80 端口、后端是 8080 端口,跨域问题必然出现。Spring Security 6 的 CORS 配置要在 SecurityFilterChain 里加:

http.cors(Customizer.withDefaults());

光这样还不够,还要定义一个 CorsConfigurationSource Bean:

@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }

很多初学者只在 Controller 加@CrossOrigin,但对于需要走过滤器的 JWT 请求,仅注解往往无效,必须两边配合。

7.2 WebSocket Session 并发问题

ChatEndpoint 里用的 ConcurrentHashMap 表面上前线程安全,但实际上一个用户可能从多个设备/多个标签页登录,会产生多个 WebSocket Session。按 userId 覆盖存储,会踢掉前面登录的设备。我建议改成Map<Long, List<WebSocketSession>>,发送时遍历所有 session 推送。如果要求不高,覆盖方案也能用,但答辩时被老师问到“多端登录”就露馅了。

7.3 图片上传存哪里

直接用本地磁盘存文件,开发没问题,部署后会有坑:重启服务文件还在,但重新部署服务器时文件丢了。建议上传接口把文件写到服务器某个固定目录,比如/data/uploads,Nginx 再加一个location /uploads/映射到该目录。如果以后用云存储 OSS,只需要把 upload 服务改成对接 OSS 的上传 URL 即可,业务代码不用变。

7.4 数据库连接池报错

阿里云 MySQL 默认最大连接数可能不多,SpringBoot 默认连接池 HikariCP 的 maximum-pool-size 默认是 10,但如果你本地其他工具也占连接,可能会报Too many connections。调一下配置:

spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5

7.5 时间显示时区差 8 小时

这问题太经典了:数据库存的时间是2025-01-01 12:00:00,前端显示成了04:00:00。原因大概率是 JDBC 连接串没带serverTimezone,或者 Jackson 没配时区。解决方法是连接串加serverTimezone=Asia/Shanghai,Jackson 加time-zone: Asia/Shanghai,两边对齐。

8. 项目扩展方向与个人实操心得

如果按上面的内容全部做完,这已经是一个功能完整、能部署、有亮点的交友平台系统。但如果想让它在毕设或简历里更有分量,我建议你再做三件事:

一是加入基于 Redis 的在线状态管理。用 Redis 存一个online:userId的 key,WebSocket 连接时写入,断开时删除,前端再轮询或订阅这个状态,就能在好友列表里显示“在线/离线”。这个一眼就能让交互深度上一个台阶。

二是增加基于几何距离的附近的人功能。用 MySQL 的 longitude 和 latitude 字段存坐标,虽然实现的 SQL 比较粗暴,但配合 Haversine 公式可以在论文里讲得头头是道。如果数据量大,再提一句使用 GeoHash 做索引,理论深度就够了。

三是做消息推送通知。用户 A 给用户 B 发了好友申请,B 的页面上要有红点提醒。简单做法是每次请求接口时返回未读申请数量,也可以做一条notification表统一管理。

最后说点我自己的体会。这个项目最花时间的从来不是写代码,而是前后端接口设计时没有提前把数据格式定死。我今天定createTime,明天又改成create_time,前端跟着一遍遍改,心态崩了人只想摆烂。所以项目一开始,我强烈建议你先把所有接口的请求响应 JSON 样例文档写好,哪怕就写在 Markdown 里,前后端各拿一份对着开发,后面每天都能省下至少两个小时扯皮时间。

另外,做这类系统千万不要为了显得技术多而堆砌组件。你要明白,答辩老师问的问题不是“你用了什么热门的中间件”,而是“你在这个系统里解决了什么问题、怎么解决的”。把交友推荐、好友状态、消息可靠送达这些业务层面的细节讲透,比你说自己用了 K8s 更让人信服。老老实实把每一步做扎实,这套系统的含金量已经在绝大多数课程设计之上了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询