简介基于uniapp与hbuilderX联合开发的Java后端交友社交软件完整源码面向需要快速搭建多端社交应用的小程序/App开发者、Java工程师及高校毕业设计学生。项目采用前后端分离架构前端涵盖215个JavaScript文件实现页面逻辑与交互92个Java文件负责后端接口与业务处理另有47个HTML、31个CSS文件以及图片、字体等多格式素材共480个文件压缩包14.53MB结构规整便于二次开发。软件已实现小程序与App双端兼容核心功能包括用户展示、搜索匹配、私信互动及婚礼场景模板自定义等可直接作为交友、婚恋类应用的开发基底。项目遵循MVC模式前后端接口设计清晰数据交互规范适合学习uniapp跨端开发技巧、Java后端整合方案以及移动端社交产品从零到一的整体落地流程。已有412人学习下载对准备毕业设计或想了解多端社交软件实现细节的开发者有实际参考价值。1. uniapp hbuilderX Java 后端做交友软件源码给你了为什么还是跑不起来把一套“基于 uniapp hbuilderX 的 Java 后端交友社交软件”源码倒进 hbuilderX点运行十有八九不是直接进首页而是先跟报错打照面数据库连不上、接口返回 401、照片传不上去。源码本身不难难的是你不知道这份工程里哪些文件能改、哪些文件是编译期生成的这种黑匣子状态才是跑通的最大障碍。这类项目要解决的其实是三件事用 uniapp 写一套前端跑到 Android 和 iOS用 Java 后端把登录、匹配、聊天这几条业务链路撑起来再通过 hbuilderX 完成真机调试和打包。适合拿它做毕业设计、课程设计或者想在前后端分离项目实战里把多端 App 走通的人。2. 从源码包到本地跑通先分清 uniapp 前端工程和 Spring Boot 后端工程动手跑源码之前我建议你先花十分钟把目录看明白。很多人在这一步翻车不是代码报错而是用 hbuilderX 打开了错误目录导致整个工程识别不出来。2.1 解压后先找这几个文件目录结构决定你接下来一小时的动作常见的课程设计源码包长这样不是唯一标准但八九不离十unimatch/ ├── app/ # uniapp 前端工程用 hbuilderX 打开这一层 │ ├── pages/ # 页面登录、首页、匹配、聊天、我的 │ ├── static/ # 图标、默认头像等静态资源 │ ├── utils/ │ │ └── request.js # 封装的 uni.request统一带 token │ ├── manifest.json # 应用配置appid、权限、SDK 配置 │ ├── pages.json # 路由与 tabBar 配置 │ └── main.js # Vue 入口 ├── server/ # Java 后端工程Spring Boot Maven │ ├── pom.xml │ └── src/main/resources/ │ └── application.yml # 数据库、Redis、上传路径配置 └── sql/ └── init.sql # 建库建表 演示数据这里最关键的是hbuilderX 要打开app这一层不是打开整个unimatch。如果直接拖外层目录进去hbuilderX 很可能认不出这是 uniapp 项目控制台直接报“不是有效的 uni-app 工程”。后端工程用 IDEA 或 Eclipse 单独打开server跟前端没有耦合两个 IDE 可以同时开着。源码包里值得优先看的文件顺序我一般是这样先看application.yml确认数据库、Redis、上传目录配了哪些参数再看init.sql确认表结构和演示账号最后看pages.json了解前端有哪些页面、首页是哪个。交友软件的核心表通常围绕这几类用户表、位置表、喜欢/匹配记录表、聊天消息表、动态表。你先把表之间的关系理清后面调接口时才有方向。2.2 最小启动路径先起后端再用 hbuilderX 打开前端跑真机读源码不如跑源码。最小闭环只需要四步后端先起来前端再连# 1. 初始化数据库init.sql 里通常包含建库语句 mysql -uroot -p sql/init.sql # 2. 进入后端工程启动 Spring Boot cd server mvn spring-boot:run # 3. 后端起来后单独开一个终端验证健康检查 curl http://localhost:8080/api/healthmysql -uroot -p会提示输入密码执行前先确认 init.sql 里有没有CREATE DATABASE。如果脚本里只建表不建库你就得手动先建库再导入否则会看到Unknown database。mvn spring-boot:run适合开发调试改动 Java 代码后它会自动编译省去反复手工重启。如果电脑上没装 Maven或者下载依赖特别慢就改用 IDEA 自带的 Maven 面板操作并在 settings.xml 里配置国内镜像。跑起来之后用curl验证后端不算完事。你需要确认返回的是 JSON 而不是一串报错堆栈。常见失败有两种一是数据库连不上报错里会出现Access denied或Connection refused去application.yml里核对账号密码二是端口被占用报Port 8080 was already in use改server.port或者杀掉占用进程。2.3 运行成功标准前后端分离项目实战里的最小闭环后端通了前端才算真正开始。打开 hbuilderX导入app目录先别连手机点“运行到浏览器”最快。浏览器里能看到登录页说明 uniapp 工程本身没问题。接下来把接口地址指到后端。找到utils/request.js或者项目里的config.js把BASE_URL从http://localhost:8080改成你电脑的局域网 IP比如http://192.168.1.100:8080。这一步是前后端分离项目实战里最容易卡住的地方浏览器里访问localhost:8080没问题但手机上的localhost指向的是手机自己不是电脑。所以最小闭环的顺序应该是后端 health 检查通过前端跑浏览器能看到页面再用手机真机连同一个 Wi-Fi 做联调。这条链路走通才说明源码包在你手里是活的。不要一上来就折腾打包后端接口都没验证过打出来的包也只是个空壳。3. 把交友业务拆成三条核心链路登录鉴权、匹配推荐、即时聊天交友社交软件页面很多但业务核心就三条让用户进来、让用户匹配、让用户聊天。源码质量高低看的也是这三条链路写得干不干净。拆开讲每一部分都有自己容易踩的坑。3.1 登录鉴权链路Spring Boot 签发 JWTuniapp 端怎么存和带大多数 Java 后端交友项目都会用 JWT 做登录态而不是 Servlet 的 session。原因很直接uniapp 端要跑 App、小程序、H5session 的 Cookie 机制在 App 端并不好用JWT 是无状态的前端拿到 token 后自己存每次请求往 header 里带后端只需要解析校验。后端常见写法是 Spring Boot MyBatis Plus登录接口大致长这样PostMapping(/api/auth/login) public Result login(RequestBody LoginDTO dto) { User user userService.getOne(new LambdaQueryWrapperUser() .eq(User::getPhone, dto.getPhone())); if (user null) { return Result.error(手机号未注册); } if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.error(密码错误); } String token jwtUtil.createToken(user.getId(), user.getNickname()); return Result.ok(new LoginVO(token, user.getNickname())); }密码存储用的是 BCryptpasswordEncoder.matches负责把用户输入的明文和数据库里的密文做比对。千万不要在数据库里存明文密码答辩时这几乎是必被追问的安全问题。jwtUtil.createToken一般会把用户 id 和过期时间放进去过期时间建议设成 24 小时以上太短的话用户玩着玩着就被踢下线体验很差。前端这边所有请求最好统一走封装好的request.js不要在每一个页面里自己写uni.request// utils/request.js const BASE_URL http://192.168.1.100:8080 export function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, header: { Authorization: Bearer uni.getStorageSync(token), Content-Type: application/json }, success: (res) { if (res.data.code 401) { uni.removeStorageSync(token) uni.navigateTo({ url: /pages/login/index }) return } resolve(res.data) }, fail: reject }) }) }这里有两个容易对不上的地方。第一后端拦截器里如果要求Authorization带Bearer前缀那前端拼字符串时一个空格都不能少少了就 401。第二登录成功后要把 token 用uni.setStorageSync(token, token)存起来getStorageSync才能取到如果存的时候字段名写错了后面所有请求都拿不到 token。建议登录页跳转前打一条console.log确认 token 真的有值这是最直观的排错方式。3.2 匹配推荐链路附近的人 SQL 与滑动卡片的数据来源交友软件里的推荐列表源头大概率是两张表用户表和位置表。用户表存基本资料位置表存经纬度。滑动卡片要展示“附近的人”本质就是一次按距离排序的查询-- 附近的人用经纬度算距离按距离升序取前 20 个 SELECT u.id, u.nickname, u.avatar, u.bio, ROUND(6371 * 2 * ASIN(SQRT( POWER(SIN((#{lat} - u.latitude) * PI() / 180 / 2), 2) COS(#{lat} * PI() / 180) * COS(u.latitude * PI() / 180) * POWER(SIN((#{lng} - u.longitude) * PI() / 180 / 2), 2) )), 1) AS distance_km FROM user u WHERE u.id #{userId} AND u.status 1 HAVING distance_km #{radiusKm} ORDER BY distance_km LIMIT 20;这一段是哈弗辛公式算的是球面两点距离单位是公里。6371是地球半径#{lat}和#{lng}是当前用户的坐标u.latitude和u.longitude是对方的坐标。配合前端拿到定位后上传到后端用户每次打开推荐页都能拿到按距离排好的列表。这个写法的坑在于HAVING distance_km #{radiusKm}。HAVING是在查询结果集生成之后再做过滤的数据量小没问题但用户量过十万就开始明显变慢。课程设计阶段这个量级可以接受真要做产品要么换成 Redis GEO要么把经纬度计算下沉到数据库函数。不过对于这份源码我建议先跑通 SQL别一上来就引入中间件避免把自己绕进去。如果推荐页还要按兴趣标签筛选常见做法是维护一张user_tag关联表SELECT DISTINCT u.id FROM user u INNER JOIN user_tag t ON t.user_id u.id WHERE t.tag IN (跑步, 电影, 旅行) ORDER BY u.id DESC LIMIT 50;DISTINCT很关键因为一个用户可能同时命中多个标签不加上会返回重复用户。IN里的标签数量也不要太多两三个足够数量越多查询成本越高。3.3 即时聊天链路WebSocket 与轮询怎么选uniapp 端怎么接聊天是交友软件的灵魂。源码里如果用的是轮询实现最省事前端每隔 3 秒调一次/api/chat/poll把该会话的新消息拉回来。缺点是费流量、费电、消息延迟最坏有 3 秒。WebSocket 是长连接服务端有消息直接推给客户端体验明显更好代价是后端要多维护连接状态。课程设计源码我建议优先用 WebSocket答辩时能讲的东西多也能体现你对实时通信有基本理解。后端注册一个 WebSocket 端点Configuration public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(chatHandler(), /ws/chat) .addInterceptors(new ChatAuthInterceptor()) .setAllowedOrigins(*); } }chatHandler()是处理消息的类ChatAuthInterceptor负责在握手阶段解析 tokentoken 不过直接拒绝连接。setAllowedOrigins(*)在开发阶段放开让 uniapp 端能连上上线后要收紧只允许自己的域名。前端在 uniapp 里用uni.connectSocket连接代码并不复杂const socket uni.connectSocket({ url: ws://192.168.1.100:8080/ws/chat?token uni.getStorageSync(token), success: () console.log(连接成功) }) socket.onMessage((res) { const msg JSON.parse(res.data) // 把 msg 追加到当前会话消息列表并清除未读数 }) socket.onClose(() { // 断线后 3 秒重连重连逻辑要加防抖避免频繁重试 setTimeout(connectChat, 3000) })注意 token 是通过query参数传的不是放在 header 里。WebSocket 的浏览器端 API 不支持自定义 header这是 uniapp 和浏览器里都一样的限制。后端拦截器从request.getParameter(token)取即可。另外真机调试时用ws://正式环境如果是 HTTPS 域名必须改成wss://不然会被浏览器或 app 直接拦掉这是很多人上线后才发现的坑。4. 前后端分离项目实战的联调参数hbuilderX 真机调试、API 地址与跨域配置到了联调阶段源码本身基本不用动了动的是配置。这一章几乎所有问题都不在代码逻辑上而在“地址”和“环境”上。4.1 manifest.json 与环境地址为什么模拟器用 10.0.2.2真机必须写电脑局域网 IPhbuilderX 真机调试时手机和电脑要在同一个 Wi-Fi 下。你在浏览器里访问后端用的是localhost:8080但手机上这个地址指向手机自己。正确做法是把BASE_URL改成电脑的局域网 IP。Android 模拟器特殊一点模拟器里的localhost指向虚拟机自己访问宿主机要用10.0.2.2。所以很多源码默认配了10.0.2.2在模拟器上一切正常换成真机就全部超时。这不是代码写错了是模拟器和真机的网络模型不一样。我建议把环境地址单独放在一个配置文件里而不是散落在各个页面的请求中// config.js const ENV dev const API_BASE { dev: http://192.168.1.100:8080, // 真机调试写电脑局域网 IP h5: https://api.example.com, // H5 打包指向线上后端 prod: https://api.example.com // 正式包必须用 https } export const BASE_URL API_BASE[ENV]这就解决了另一个高频需求uniapp 封装 H5 如何指向两个域名。做法就是在config.js里维护多套环境用ENV切换如果是要根据当前访问域名自动选择可以用location.hostname判断后赋值给BASE_URL。开发环境、测试环境、正式环境各一套地址打包时不至于把192.168.x.x这种内网地址发到应用市场去。manifest.json里要确认两件事。第一uni-app 应用标识不要乱改改动后本地调试基座可能失效第二定位、相机、相册权限要在“App 模块配置”里勾上否则真机上uni.getLocation和uni.chooseImage会静默失败。4.2 照片上传链路uni.uploadFile 和 Java MultipartFile 的参数名必须一致交友软件的头像和相册上传是躲不开的。前端用uni.uploadFileuni.uploadFile({ url: BASE_URL /api/upload, filePath: tempFilePath, // uni.chooseImage 成功回调里的临时路径 name: file, // 必须和后端 RequestParam(file) 参数名一致 header: { Authorization: Bearer uni.getStorageSync(token) }, success: (res) { const data JSON.parse(res.data) console.log(上传成功, data.url) } })后端接口PostMapping(/api/upload) public Result upload(RequestParam(file) MultipartFile file) { if (file.getSize() 5 * 1024 * 1024) { return Result.error(图片不能超过 5MB); } String ext StringUtils.getFilenameExtension(file.getOriginalFilename()); if (!Arrays.asList(jpg, jpeg, png, webp).contains(ext.toLowerCase())) { return Result.error(仅支持 jpg、png、webp); } String filename UUID.randomUUID() . ext; file.transferTo(new File(uploadDir / filename)); return Result.ok(/images/ filename); }这里最鬼畜的坑是参数名对不上。前端name: file后端就必须是RequestParam(file)。有人前端写的name: image后端RequestParam(file)结果传上来的对象一直是 null代码翻半天也查不出问题。uploadDir建议在application.yml里配置成绝对路径并且启动时先创建目录。后端返回的是相对路径/images/xxx.jpg前端展示时要拼上BASE_URL也就是BASE_URL res.data.data.url。如果直接拿相对路径去uni.previewImage会显示图片加载失败。上传接口安全也值得提一句。只校验扩展名是不够的常见做法是同时限制文件大小、校验文件头读取前几个字节判断是不是 JPEG/PNG、把文件名改成 UUID 而不是信任原始文件名。课程设计源码普遍不注重这个答辩时补上这一条能明显加分。4.3 后端 CORS 配置H5 端跨域、App 端为什么不需要跨域uniapp 编译成 App 之后请求走的是原生网络栈不遵守浏览器的同源策略所以 App 端不会出现跨域。但 H5 端是跑在浏览器里的前端页面在http://localhost:8081后端接口在http://localhost:8080端口不同浏览器就会发起预检请求后端不处理就直接失败。Java 后端需要配置全局 CORSConfiguration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }addAllowedOriginPattern(*)配合setAllowCredentials(true)在 Spring 5.3 之后是允许的老版本里这两个配置同时出现会导致所有请求被拒绝。如果你用的源码里是addAllowedOrigin(*)就把setAllowCredentials(true)去掉二选一。另外后端拦截器要放行OPTIONS请求因为浏览器预检请求用的是OPTIONS方法拦住了同样会导致跨域失败。4.4 多端差异怎么使用 hbuilderX 制作 H5 程序和小程序/App 的联调区别hbuilderX 制作 H5 程序的路径是“发行 → 网站-H5手机版”生成的静态文件直接扔到服务器就行。开发阶段点“运行到浏览器”会在本地起一个 H5 服务方便快速看效果。不同端的联调差异要记住H5 端浏览器同源限制最严格必须处理后端 CORS。如果有多个部署域名就在config.js里做多环境配置。微信小程序端hbuilderX 里运行到微信开发者工具小程序要求所有请求域名必须是配置过的合法域名。开发阶段可以勾选“不校验合法域名”上线前在微信公众平台后台配好。Android/iOS App 端没有浏览器跨域限制但必须确认 manifest 里勾了网络权限和定位权限。真机调试时如果发现 uniapp 不打印日志信息先去 hbuilderX 控制台右上角确认有没有过滤掉 log 级别再用console.log输出到控制台。不要只盯着手机屏幕日志都在 hbuilderX 里。5. 交友社交项目源码的常见问题排查登录失效、上传失败、打包白屏这一章是我跑这类源码跑得最多之后沉淀下来的血泪经验。每一条都是真实会发生的也是答辩时面试官最爱挑的毛病。5.1 登录成功后请求依然 401现象是登录页能进首页数据加载失败控制台一片 401。查下来原因基本有三种。第一种是前端把 token 存成了别的字段名getStorageSync取不到header 里拼出来是Bearer undefined。第二种是后端拦截器配置了放行/api/auth/**但登录接口实际路径不是这个前缀导致登录自己也 401 或者被拦截器绕过了。第三种是 JWT 过期时间设置太短比如设成了 30 分钟调了半天就过期。解决方法是先在后端拦截器里临时打印request.getHeader(Authorization)确认 token 到底有没有传到后端再检查前端封装请求的代码里getStorageSync(token)字段名和登录后setStorageSync的字段名是否完全一致。用 hbuilderX 编辑代码时对 token 相关的函数名右键找引用或者全局搜索setStorageSync和getStorageSync能快速定位到是谁把 token 弄丢了。5.2 真机上传图片一直失败现象是模拟器上传一切正常换到真机就转圈、超时或者直接 500。原因集中在四个地方。第一后端配置的上传目录不存在文件transferTo时抛FileNotFoundException第二Windows 下路径写法是D:/upload/Linux 是/home/user/upload/写死了必翻车第三前端uni.chooseImage返回的临时路径在真机上可能是file://开头直接拿去当 URL 访问是不行的只能作为filePath传给uploadFile第四后端对文件大小做了限制真机拍出来的照片动不动好几 MB超出就报错。解决时先看后端日志Spring Boot 的异常堆栈会直接告诉你是哪一种。目录不存在就启动时自动创建大小限制在application.yml里调大临时路径问题就确认前端用的是tempFilePath而不是path。这里顺带说一句上传安全。后端正则校验后缀名只能防小白真正的姿势是扩展名白名单加文件头校验再限制大小。面试官如果问“上传漏洞怎么防”别只说改扩展名要把这三条一起说出来。5.3 hbuilderX 打包后首屏白屏现象是hbuilderX 里执行“运行到手机”好好的真机点击 App 图标启动页消失后就是白屏。这种问题最容易让人慌因为没法像接口一样打断点。常见原因有三个一是manifest.json中appid和当前工程不匹配导致代码被编译进去但运行时报错二是首页路由写错了pages.json里第一个页面不是你想要的pages/index/index启动后找不到页面直接白屏三是用了浏览器端特有的 API打包成 App 后调用报错异常中断在页面渲染前。解决时别盲猜先把 hbuilderX 控制台切到“App 运行日志”真机白屏时如果是 Vue 运行时报错控制台会打印。正式包没有日志窗口的话先用“运行到手机”模式跑自定义调试基座把错误打出来再改。检查pages.json的pages数组第一项是不是首页检查main.js里有没有#ifdef平台条件编译代码写错。5.4 WebSocket 连接秒断或消息发不出去现象是连接建立后 1 到 2 秒就触发onClose或者发送消息对方永远收不到。原因是多层面的。协议写错是最常见的H5 端是 HTTPS 域名时必须用wss://真机调试才能用ws://IP:端口。token 放错位置也常见WebSocket 不支持自定义 header必须放 query。后端如果配置了空闲超时客户端长期不发消息会被服务端断开前端要做心跳每 30 秒发一条ping后端收到后回pong连接就不会被回收。解决时先确认 URL 拼出来是什么直接在console.log里打印完整地址再确认后端收到的 token 是否有效无效会直接拒绝握手。重连逻辑要加防抖和最大重连次数不加的话手机锁屏再解锁可能会同时建立多条连接。5.5 附近的人查不到数据现象是推荐列表一直空但数据库里明明有测试用户。原因十有八九是经纬度数据不对。常见几种经纬度字段在数据库里是varcharSQL 计算时隐式转换成数值精度丢失前端uni.getLocation没有拿到授权上传到后端的是 0 和 0后端把经纬度当成字符串存了没做校验。解决时先执行原始 SQL把HAVING去掉看看裸数据能不能查出来。如果裸数据有加上HAVING distance_km 5就没了优先怀疑坐标数据如果裸数据也没有去看user_location表里的经纬度字段类型建议改成DECIMAL(10, 6)。定位权限的问题在manifest.json检查是否勾选了定位服务真机首次启动会弹授权框选“拒绝”之后必须去设置里手动打开。6. 验收源码前值得做一轮全链路冒烟接口顺序、打包检查、答辩被追问点源码跑通之后别急着打包交差。我习惯先按用户真实操作路径过一遍冒烟测试顺序固定成注册 → 登录 → 编辑资料 → 上传头像 → 查看推荐列表 → 滑动匹配 → 发起聊天 → 退出登录。每一步都要在真机上点一遍而不是只在模拟器里看。接口层可以用命令行快速验证不用打开 PostmanTOKEN$(curl -s -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {phone:13800138000,password:123456} | jq -r .data.token) curl -s http://localhost:8080/api/user/recommend \ -H Authorization: Bearer $TOKEN没有jq的话直接拿返回的 JSON 手动复制 token 也行只是慢一点。这条命令的价值在于验证 token 从登录到业务请求是贯通的前端白屏、上传失败、聊天断连大多数都能在这一步暴露。打包检查也有固定顺序先用 hbuilderX 发行 H5 到本地目录检查静态资源是否能被 Web 服务器访问再打包 Android 正式包确认签名证书配置正确。上架安卓应用市场前检查包名、版本号、权限声明和隐私政策有无遗漏权限不要一口气全勾只勾实际用到的。答辩或者面试时这三个问题几乎必被追问JWT 为什么不用 sessionWebSocket 和轮询的成本差异上传漏洞怎么防。回答思路分别是App 端 Cookie 不好用、无状态便于扩展WebSocket 省流量但服务端要维护长连接轮询简单但实时性差上传校验要扩展名白名单加文件头加大小限制。这些点如果你在源码里真的改过、踩过坑讲出来会自然很多。我做这类项目养成的习惯是每拿到一份源码先写一份接口清单再按用户路径过真机最后才看代码。这个习惯帮我避免了很多“代码能编译但业务走不通”的尴尬。希望这篇笔记能帮你把这份源码真正跑起来也少走一点我当时走过的弯路。本文还有配套的精品资源点击获取