ARTICLE DETAIL

资讯详情

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

校园失物招领微信小程序毕设实战:Spring Boot前后端分离与部署全攻略

校园失物招领微信小程序毕设实战:Spring Boot前后端分离与部署全攻略 简介这是一套面向计算机专业本科生与微信小程序初学者的毕业设计级实战源码聚焦校园失物招领场景解决高校日常物品遗失后信息触达低、匹配效率差、管理缺闭环等实际问题。资源包含100个文件涵盖21个核心业务逻辑JS文件如publish.js、detail.js、all.js、14个页面结构WXML、17个样式WXSS、25个配置与数据JSON辅以PNG/JPG图标资源及5份Markdown部署说明文档整体3.21MB结构清晰、模块解耦明确便于理解小程序生命周期、云开发数据库操作与地图API集成。已有499人学习下载配套详细部署指南覆盖环境配置、云函数上传、数据库初始化及审核后台启用全流程读者可直接部署上线亦可基于mine.js、profile.js等模块快速拓展用户中心功能或结合home.jpg、publish.jpg等素材开展UI优化实践。 毕业设计选了“校园失物招领系统”这个题目最后做出来的是微信小程序Spring Boot的完整前后端分离项目源码加详细部署说明都已经整理好。这几天把整套东西从技术选型到部署上线重新梳理了一遍发现网上相关的资料虽然不少但能一篇讲透、照着一路做下来就能跑通的文章确实不多。这篇文章就把这个项目的全部核心细节、部署步骤、踩坑经验一次性说清楚希望对同样在准备毕设、想要快速落地一个微信小程序项目的同学有帮助。1. 项目整体设计与功能拆解1.1 这个系统到底解决了什么问题校园失物招领这个场景表面上看就是一个发布信息和查看信息的平台但如果认真想一想实际使用者的真实需求会发现它远不止“贴个公告栏”这么简单。我走访了一下身边的同学发现传统的校园失物招领基本就三条路表白墙发一条、QQ群转发一下、线下失物招领处碰碰运气。这三条路都有明显的痛点表白墙和QQ群的信息很快会被闲聊刷掉东西丢了大半天再想找那条信息得翻好久线下失物招领处能覆盖的范围又太小等你想起来去看可能物品已经被清理了。更深一层的问题是信息之间没有匹配机制——有人丢了电脑充电器有人恰好捡到一个电脑充电器但这两条信息如果没有被同一个人看到就永远不会自动“对上”。所以这个系统的核心设计目标就是两件事第一让失物和招领信息以结构化、可检索的形式沉淀下来第二在失主和拾主之间建立消息触达的通道让信息不再是“孤岛”。要做到这两点光有一个信息发布的页面是不够的还需要分类体系、搜索能力、认领申请与审核流程、状态管理、消息通知以及最容易被忽略的管理员后台。从技术角度看这个小程序的前端界面要覆盖首页信息流、发布页、详情页、消息中心、个人中心这五个主要入口后端要提供信息的增删改查、图片上传、认领流程的状态流转、用户登录鉴权等一系列接口。看起来页面不多但每个业务动作背后都对应着一套完整的前后端交互逻辑。1.2 角色权限与核心业务流程设计整个系统涉及三类角色每类角色能看到的东西和能做的事完全不同。普通游客未登录状态可以看到信息列表和详情但不能发布、不能申请认领这样设计的考虑是失物招领场景中的联系方式比较敏感如果完全开放给未登录用户随意查看容易导致信息被恶意抓取和滥用。注册登录后成为普通用户就可以发布失物信息、发布招领信息、对别人发布的招领信息发起认领申请也可以对自己发布的信息进行状态管理。第三类角色是管理员。管理员本质上也是普通用户但通过一个 role 字段做区分。管理员登录后在“管理”入口进入后台可以删除违规信息、管理用户、管理分类。在所有设计中管理员不需要独立的App直接复用同一个小程序只是根据权限动态显示管理入口这样能减少开发量也方便演示和答辩。关键的业务流程有两条流程一是“丢东西的人”的主线发布失物信息 → 等待系统推送到“捡到东西的人” → 如果有人发起认领申请 → 失主查看申请人的描述和联系方式 → 确认匹配 → 状态置为“已找到”。流程二是“捡到东西的人”的主线发布招领信息 → 失主看到信息并主动发起认领申请 → 拾主查看申请 → 确认归还 → 状态置为“已归还”。这里有一个细节我特意做了区分失物信息的终结状态叫“已找到”招领信息的终结状态叫“已归还”。两个字不一样但含义非常准确。如果你在做类似的毕设建议在字段设计上就把这种业务语义的区别体现清楚答辩时这是一个很自然的亮点。2. 技术选型与数据库设计详解2.1 前端为什么选原生微信小程序而不是uni-app这个项目的技术选型是很多同学会纠结的问题。市面上有原生微信小程序、uni-app、Taro等方案可选。我最终选了原生微信小程序而不是当前热度很高的uni-app原因有三个。第一毕设的核心目标是“把完整业务流程跑通”而不是“展示跨端能力”。uni-app的优势是一次编写多端运行但这个项目只需要在微信小程序端跑跨端能力用不上却白白增加了一层编译链的复杂度。遇到问题排查时要多绕一层“到底是小程序的问题还是uni-app的问题”对毕设来说完全是给自己挖坑。第二原生小程序的官方文档和社区生态最完整开发调试工具也最稳定。微信开发者工具对原生项目的支持、断点调试、性能面板、真机预览都是开箱即用的。反观uni-app调试时有时会遇到热更新不生效、HBuilderX和微信开发者工具之间同步异常的问题对追求稳定交付的毕设来说没必要冒这个风险。第三原生小程序的WXML结构和传统HTML很像对于有前端基础的同学上手成本很低。它和Vue的模板语法也有不少相似之处但更加轻量。不过要说明一点如果你本身想用Vue的语法习惯或者有“以后还要发布到支付宝小程序、抖音小程序”的计划那uni-app是完全有理由优先的。这里没有绝对的对错只有适合不适合项目场景的问题。2.2 后端为什么选Spring Boot后端我用了Spring Boot配合MySQL数据库。选择它的理由很务实生态太成熟了遇到任何问题都能搜到解决方案。这在小程序项目里格外重要因为小程序的登录态、会话管理、文件上传等环节和传统Web项目有一些差异如果没有成熟框架的支撑调试成本会明显上升。Spring Boot在毕设场景中还有一个隐性的优势它自带内嵌Tomcat本地开发时直接在IDE里跑 main 方法就能启动不需要单独配置外部Tomcat。部署到服务器时一个java -jar命令就搞定不需要处理繁琐的Web服务器配置。这对只熟悉开发、不太熟悉Linux运维的同学来说友好度直接拉满。还有一点Spring Boot的spring-boot-starter-web自带Jackson做JSON序列化前端传对象可以直接收到格式化好的JSONspring-boot-starter-validation可以做参数校验spring-boot-starter-data-jpa或MyBatis可以快速接入数据库。开发效率非常高。2.3 数据库设计核心表结构与字段含义数据库是失物招领系统最核心的部分表结构设计得合理后面的开发会非常顺畅。我把整个系统的数据表拆成了五张核心表外加一个字典分类表。用户表sys_user存储用户的基础信息和登录凭证字段名类型含义idbigint主键自增usernamevarchar(50)用户名小程序端唯一标识passwordvarchar(255)密码的加密存储nicknamevarchar(50)昵称student_novarchar(20)学号phonevarchar(20)联系电话roletinyint1普通用户2管理员create_timedatetime注册时间信息表item_info同时承载失物信息和招领信息字段名类型含义idbigint主键user_idbigint发布者IDtypetinyint1失物2招领titlevarchar(100)物品标题descriptiontext物品详细描述category_idbigint分类IDimage_urlvarchar(500)图片地址locationvarchar(100)丢失/拾取地点happen_timedatetime丢失/拾取时间statustinyint0寻找中/待认领1已找到/已归还is_deletetinyint逻辑删除标记create_timedatetime发布时间这里采用“一张表承载两种类型”的设计是一种常见的业务设计模式。它的好处是查询逻辑统一列表页一次SQL就能同时拿到失物和招领数据只用type字段做区分坏处是某些字段对特定类型没有意义比如“丢失地点”和“拾取地点”是同一个location字段语义上有点混合。对于毕设来说这个取舍完全值得因为代码会更简洁不会出现两个相似接口互相复制的冗余代码。认领申请表claim_record字段名类型含义idbigint主键item_idbigint物品IDapply_user_idbigint申请人IDcontact_infovarchar(100)申请人联系方式apply_reasonvarchar(500)认领理由描述细节statustinyint0待审核1通过2拒绝create_timedatetime申请时间消息通知表notice_message字段名类型含义idbigint主键user_idbigint接收者IDcontentvarchar(500)消息内容typetinyint1系统通知2认领申请3处理结果is_readtinyint0未读1已读create_timedatetime创建时间分类表category就简单了就是id、name、sort_order三个字段。分类的默认数据我在初始化SQL里直接插入了几条常用项证件类、电子产品、书籍资料、生活用品、钥匙、其他。这里推荐一个实操习惯写SQL建表脚本时从一开始就要加create_time字段。很多同学初期不加后面做列表排序时发现缺一个“按最新发布排序”的字段又要回头改表非常麻烦。另外所有表都加is_delete逻辑删除标记做软删除而不是物理删除这样数据不会真正丢答辩时讲解也更加灵活只能说演示真正上线时要有定期清理策略但毕设场景下足够用了。3. 核心功能模块的实现思路与关键代码3.1 用户登录与鉴权流程别想复杂了小程序端的登录是一个绕不开的环节。很多同学一上来就想接微信官方的wx.login换 openid再搞一套 session 管理其实毕设根本不需要做那么复杂。微信官方那个wx.login流程需要后端调用code2Session接口拿 openid然后自己管理会话状态对于只用来做毕设的场景实现成本偏高。我采用了一个更简单但完全够用的方案手机号密码注册登录。在小程序端用wx.setStorageSync做登录态持久化wx.request请求时在header里带上一个token字段后端用Spring Boot拦截器统一校验登录状态。后端登录接口的核心逻辑是这样的PostMapping(/api/user/login) public Result login(RequestBody LoginDTO dto) { // 根据用户名查询用户 User user userMapper.selectByUsername(dto.getUsername()); if (user null) { return Result.error(用户不存在); } // 密码比对这里使用MD5加密存储 String md5Password DigestUtils.md5DigestAsHex(dto.getPassword().getBytes()); if (!user.getPassword().equals(md5Password)) { return Result.error(密码错误); } // 生成一个简单的token实际项目建议用JWT String token UUID.randomUUID().toString().replace(-, ); redisTemplate.opsForValue().set(token, user.getId().toString(), 7, TimeUnit.DAYS); return Result.success(token); }我最终没有用JWT而是用Redis存了一个随机token映射到用户ID。原因是毕设项目不需要分布式会话Redis管理比JWT更好控制过期时间也不用处理JWT的刷新逻辑。如果你不想引入Redis完全可以用一个内存Map来存token重启失效也没关系毕设演示时完全可以接受。这个小程序端的登录页面表单就只有用户名、密码两个输入框和登录/注册两个按钮背后的wx.request调用也非常直观。3.2 信息发布与图片上传的实现细节发布信息页面是整个小程序里交互最复杂的页面因为它除了普通表单之外还涉及到图片上传。这个环节我踩过一个坑一开始把图片直接转成base64放在JSON里提交结果后端一接数据就发现请求体严重超出Spring默认的2MB限制接口直接报错。后来改成wx.uploadFile做多部分表单上传一次传一个文件字段每个文件单独请求一次上传接口拿到了图片URL之后再和其他文本字段一起组成发布请求。这样整体的稳定性高了很多请求体大小也在合理范围内。关键的上传代码小程序端wx.chooseMedia({ count: 3, mediaType: [image], sourceType: [album, camera], success(res) { const tempFiles res.tempFiles; // 逐张上传 tempFiles.forEach((file, index) { wx.uploadFile({ url: app.globalData.baseUrl /api/file/upload, filePath: file.tempFilePath, name: file, success(uploadRes) { const data JSON.parse(uploadRes.data); // 保存返回的URL imageUrls.push(data.data); } }); }); } });后端接收文件的ControllerPostMapping(/api/file/upload) public Result upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { return Result.error(上传文件为空); } String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); // 生成唯一文件名防止重名覆盖 String newFileName UUID.randomUUID().toString().replace(-, ) suffix; // 存储路径按日期分目录 String datePath new SimpleDateFormat(yyyyMMdd).format(new Date()); String filePath uploadPath / datePath / newFileName; File dest new File(filePath); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } file.transferTo(dest); // 返回访问URL String url /upload/ datePath / newFileName; return Result.success(url); }这里有两个值得强调的细节。第一个是文件名必须用UUID重命名不能用用户原始文件名否则不同用户上传同名图片会互相覆盖这是最基础的文件上传规范。第二个是图片访问路径必须和静态资源映射配置对应。由于本项目用的是Spring Boot需要把/upload/**路径映射到实际的磁盘目录否则前端拿到/upload/20240601/xxx.jpg根本访问不到图片。配置代码很短Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(uploadPath /); }3.3 分类检索与关键词搜索的SQL实现首页信息流是整个小程序曝光量最高的页面。我设计成顶部是分类筛选栏下面是信息卡片列表支持上下拉加载更多和下拉刷新。搜索框支持按关键字匹配标题和描述。列表查询的SQL是核心select idselectItemPage resultTypecom.demo.entity.ItemInfo SELECT * FROM item_info where is_delete 0 if testtype ! null AND type #{type} /if if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND (title LIKE CONCAT(%, #{keyword}, %) OR description LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} /select这里有一个性能相关的坑要提醒LIKE %keyword%这种模糊查询在数据量大了之后会全表扫描性能会明显下降。但毕设场景下数据量撑死几千条完全不需要考虑加索引优化的问题。答辩时建议主动提一句“生产环境建议使用全文搜索引擎如Elasticsearch替换LIKE查询”这句话能体现出你有一定的系统设计前瞻性能加不少印象分。分类筛选用category_id做精确匹配失物/招领用type做精确匹配这样筛选的组合逻辑非常清晰。小程序端通过swiper或scroll-view实现横向滑动分类标签栏选中的分类高亮显示点击后重新加载列表数据交互流畅度和体验都不错。3.4 认领申请与状态流转的后端逻辑认领流程是这个系统业务上的关键点。它的设计逻辑是一个“招领信息”允许很多用户发起认领申请但最终只能有一个申请人被通过。一个“失物信息”的发布者也会收到多个“自称丢失者”的申请同样只能认定一个。后端接口设计成四个核心接口发起认领申请、查看我收到的申请列表、同意认领申请、拒绝认领申请。这里最核心的就是“同意认领”之后的状态流转逻辑PostMapping(/api/item/confirmClaim) public Result confirmClaim(RequestBody ConfirmDTO dto) { // 1. 校验当前操作者是否为物品发布者 ItemInfo item itemMapper.selectById(dto.getItemId()); if (item null) { return Result.error(物品不存在); } if (!item.getUserId().equals(currentUserId)) { return Result.error(无权操作); } // 2. 更新物品状态为已找到/已归还 ItemInfo updateInfo new ItemInfo(); updateInfo.setId(item.getId()); if (item.getType() 1) { updateInfo.setStatus(1); // 失物已找到 } else { updateInfo.setStatus(1); // 招领已归还 } itemMapper.updateById(updateInfo); // 3. 更新该认领申请状态为通过 claimMapper.updateStatus(dto.getClaimId(), 1); // 4. 拒绝其他所有待审核的申请 claimMapper.rejectOtherClaims(dto.getItemId(), dto.getClaimId()); // 5. 给申请人发送站内信通知 noticeMapper.insertNotice( claim.getApplyUserId(), 恭喜您的认领申请已通过请及时和发布者联系取回物品, 3 ); return Result.success(操作成功); }这个流程的第4步很容易漏掉。当确认了一个申请后如果其他申请还停留在“待审核”状态后面的人再来查看时就会被卡住。所以必须在同一事务里把其他申请全部置为“拒绝”这样才能保证业务闭环。这个细节在答辩时被老师追问过我自己当时解释为“保证同一时间只有一个有效申请”老师是认可的。3.5 站内消息通知功能的设计取舍消息通知是失物招领系统提升“活跃度”和“有用性”的关键设计。我最终用的是站内信方案而不是微信订阅消息推送。原因是微信订阅消息有严格的审核要求和使用场景限制对于毕设项目来说申请模板权限的过程非常繁琐而且订阅消息是一次性的——用户订阅一次只能收到一次通知下次需要再次订阅这种机制对于“物品发布后长期等待认领”的业务场景来说并不友好。站内信的方案就简单直接多了。当某个用户发布的信息被其他人申请时后端会自动往消息表插入一条记录接收者的消息中心就能看到一个未读的红点。用户打开消息列表时调用接口把is_read置为1红点消失。这种设计虽然没有“推送”能力但对失物招领这种打开频率不高的应用来说体验完全够用而且实现成本极低只需要一张消息表和两个接口。消息中心的红点功能我特别做了实时性小程序首页的onShow生命周期里调用一次“未读消息数”接口返回大于0就在底部Tab上显示右上角的红点。由于小程序页面每次从后台切回都会触发onShow所以用户切换到首页就能看到消息提醒体验上和推送差不了太多。4. 源码部署全流程实操详解4.1 本地开发环境的准备与初始化部署这套系统的第一步是准备好本地环境。总共需要四样东西JDK 8或以上、Maven 3.6、MySQL 5.7或以上、微信开发者工具。我把具体的环境搭建过程记录一下照着做就行。首先在MySQL中创建数据库CREATE DATABASE IF NOT EXISTS campus_lost_found DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;要强调一下建库时一定要指定utf8mb4字符集不要用utf8。如果你用MySQL 5.7utf8实际上不是真正的UTF-8表情符号和生僻字会保存失败。失物招领场景中物品描述里偶尔会有特殊符号用utf8mb4是最稳妥的选择。导入项目自带的SQL脚本mysql -uroot -p campus_lost_found sql/init.sql然后打开后端项目修改application.yml中的数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/campus_lost_found?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 30MB server: port: 8080 # 自定义配置文件上传目录 file: upload-dir: /data/upload/这里的serverTimezoneAsia/Shanghai是我被坑过的地方。MySQL 8.0版本驱动对时区很敏感不配的话会导致时间字段相差8小时甚至直接启动失败。即使不出现异常后期时间显示漂移也是特别隐蔽的bug排查起来非常麻烦所以建议在一开始就把时区配置好。4.2 后端项目打包与启动配置修改完成后在项目根目录执行Maven打包命令mvn clean package -DskipTests-DskipTests是跳过单元测试因为毕设项目通常没有充分写测试用例执行测试还会增加打包时间。打包成功后target目录下会生成campus-lost-found-0.0.1-SNAPSHOT.jar文件。直接启动java -jar campus-lost-found-0.0.1-SNAPSHOT.jar看到Spring Boot的启动日志出现Started Application in x.xxx seconds就说明服务已经正常启动了。如果想在服务器上后台运行用 nohup 命令nohup java -jar campus-lost-found-0.0.1-SNAPSHOT.jar --server.port8080 app.log 21 这里我建议端口默认用8080不要用一些看起来很酷的随机端口。原因是小程序端的request合法域名有强制要求生产环境必须走443端口的HTTPS本地调试时8080又是最通用的默认端口不容易和其他服务冲突。4.3 小程序端的导入与请求配置小程序端的配置核心就一处接口地址。在app.js的globalData里设置App({ globalData: { baseUrl: http://localhost:8080 } });如果你在开发者工具里预览需要把baseUrl改成你后端的局域网IP比如http://192.168.1.100:8080这样手机在同一个WiFi下也能访问到后端。还需要在微信开发者工具的“详情 → 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”否则开发阶段所有请求都会被拦截。这个是微信官方为开发阶段提供的调试便利仅限真机调试和模拟器预览正式上线发布时必须有正规的HTTPS域名。导入项目后如果页面白屏优先检查控制台是否有报错信息。最常见的错误是request:fail或url not in domain list遇到这种情况第一反应就是去查baseUrl配置以及是否勾选了不校验合法域名。4.4 服务器部署与HTTPS证书配置上线向如果你打算把这个系统真正上线给同学用就不能只在本地跑了。上线需要准备一台云服务器和一个已备案的域名以及在微信公众平台配置request合法域名。服务器上部署的关键步骤是配置Nginx反向代理。因为小程序要求所有请求必须HTTPS所以不能直接暴露8080端口需要让Nginx监听443端口做TLS终结再把请求转发到本机的8080端口。一个完整可用的Nginx配置如下server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/nginx/cert/yourdomain.pem; ssl_certificate_key /etc/nginx/cert/yourdomain.key; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }配置完成后在微信公众平台小程序的“开发管理 → 服务器域名”里把https://yourdomain.com加入request合法域名。注意必须是https并且域名要有备案否则保存时会报错。SSL证书的获取非常方便现在主流云厂商都提供免费的DV证书有效期一年申请后下载Nginx版本的证书文件上传到服务器指定目录就行。4.5 生产环境与开发环境的关键差异很多同学本地调试没问题一上线就出各种问题这里我把我遇到过的生产环境特有情况说一下。文件上传路径问题。本地开发时我用的file.upload-dir是/data/upload/这在Windows上会变成项目所在盘符下的\data\upload\目录创建逻辑会混乱。我在Windows上跑的时候改成相对路径./upload/绕过上线Linux时再改回绝对路径。如果不想每次手动改可以通过Spring Boot的Value注入环境变量或者用application-prod.yml做环境区分。MySQL 5.7和8.0的驱动差异。MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver5.7及以前是com.mysql.jdbc.Driver。如果你的服务器数据库版本和本地不一样启动时可能会报ClassNotFoundException直接把配置里的driver-class-name改掉就可以。内存问题。Spring Boot默认JVM堆内存是物理内存的四分之一如果云服务器只有2G内存跑一个MySQL加一个Java服务可能内存吃紧导致启动缓慢甚至被杀进程。建议启动时限制JVM内存nohup java -Xms256m -Xmx512m -jar campus-lost-found-0.0.1-SNAPSHOT.jar app.log 21 虽然这个系统业务简单几百兆内存足够但在服务器上跑起来稳定才是第一位的。5. 常见问题与排查技巧全实录5.1 微信开发者工具白屏与请求失败类问题这个小程序开发里最常见的问题就是白屏。我总结了一下遇到白屏按下面顺序排查90%都能解决。第一步看模拟器有没有报错信息。打开调试器的Console面板如果有红色报错直接把报错内容复制到搜索引擎里搜这是最直接的排查方式。第二步检查app.js里的baseUrl。如果onLaunch里第一个请求就失败页面就会一直停在默认的空白首页。我曾有一次把baseUrl写错了导致所有页面数据都加载不出来页面看起来就是白屏但其实不是真的白屏是数据没渲染。第三步检查是否勾选了“不校验合法域名”。有时改动配置后开发者工具不会立即生效建议改动后重新编译一次。第四步确认后端服务是否真的在运行。用浏览器直接访问http://localhost:8080/api/health如果能返回JSON说明后端正常问题在前端如果打不开先排查后端的启动日志。我这里做了个小笔记把你可能遇到的请求类报错按症状整理成一个速查表症状原因解决方案request:fail后端未启动或地址错误检查后端进程和baseUrlurl not in domain list未勾选忽略域名校验详情 → 本地设置 → 勾选不校验合法域名statusCode 404接口路径不对检查后端Controller的PostMapping/GetMapping路径statusCode 403被拦截器拦截检查请求头是否携带tokentoken是否有效请求超时网络不通或后端响应慢检查网络连接查看后端日志是否有异常堆栈5.2 图片上传失败与404访问不到图片图片上传的问题通常有两类上传接口报错或者上传成功但图片访问404。上传接口报错最常见的是MaxUploadSizeExceededException这是Spring的max-file-size默认值太小导致的。Spring Boot 2.x的默认单文件大小是1MB传一张手机拍的照片很可能超过这个值。解决方案是我上面写过的在配置里调大spring: servlet: multipart: max-file-size: 10MB max-request-size: 30MB图片上传成功但访问404一定是前面提到的路径映射问题。请确认addResourceHandlers方法中addResourceHandler(/upload/**)的路径和上传代码中返回的URL开头是否一致。如果上传后返回的是/upload/20240601/xxx.jpg那么addResourceHandler的参数必须是/upload/**多一个少一个斜杠都不行。另外提示一点本地调试时上传目录我用的是./upload/相对项目运行目录这个目录不在classpath下所以IDEA重启后有时会找不到历史图片。这不是代码bug是目录位置的问题。生产环境用固定绝对路径就不会有这个问题。5.3 真机预览异常与局域网访问问题用真机预览时遇到过两个经典问题。第一个是手机无法访问局域网内电脑的后端服务表现为点击登录或刷新时request:fail。原因是大部分手机默认把WiFi网络设置为“私有”不同子网之间无法互通。解决方法是确认手机和电脑连的是同一个WiFi并且电脑防火墙允许Java进程通过。Windows电脑需要在“防火墙 → 允许应用通过防火墙”里勾选Java或者在入站规则中放行8080端口。第二个问题是关于localhost的。真机预览时手机上的localhost指的是手机自己不是电脑。所以baseUrl必须填电脑的局域网IP比如http://192.168.1.100:8080不能写http://localhost:8080。这个坑非常多见很多同学本地模拟器正常一到真机就全挂了多半就是这个问题。获取电脑局域网IP的方法是Windows在命令行输入ipconfigmacOS在终端输入ifconfig | grep inet找到类似192.168.x.x的地址就行。5.4 数据库连接与时间相关的疑难杂症数据库方面的坑主要集中在连接失败和时间显示不正确。连接失败时看到Access denied for user rootlocalhost说明用户名密码不对检查application.yml里的数据库密码。看到Unknown database campus_lost_found说明数据库没创建成功重新执行建库语句。还有一个隐蔽的问题MySQL 8.0默认的认证插件是caching_sha2_password但有些老版本的JDBC驱动不支持这个认证方式会报Public Key Retrieval is not allowed错误。解决方案有两个在JDBC URL后面加allowPublicKeyRetrievaltrue或者把MySQL用户改成mysql_native_password认证。我用的是第一个方案。时间显示问题基本就是serverTimezone没有配置或者配置错误。如果发现查询出来的时间比实际时间早8小时那一定是时区没配对。把serverTimezone设置为Asia/Shanghai就能解决。如果你用的是MySQL 5.7这个参数通常不加也能跑但MySQL 8.0建议必须加否则启动阶段就报错。5.5 当前系统还能怎么扩展每个人的毕设都不会是完美的我的项目做完后有些地方自己没有实现但复盘时想到了很好的扩展方向也提供给你参考。第一个方向是接入微信订阅消息。刚才说了站内信的方案更简单可控但如果你想让系统真正“主动找人”而不是“用户打开才知道有消息”就需要接订阅消息了。微信的订阅消息支持一次性订阅用户在小程序里主动订阅后后端可以调用subscribeMessage.send接口发送模板消息。这个功能在答辩时可以重点展开因为它是微信生态的特色能力评委一般比较感兴趣。第二个方向是基于地理位置的信息推荐。目前的列表排序是纯按时间倒序如果物品的丢失地点和用户当前的位置匹配度更高可以按距离排序让用户优先看到身边的失物信息。实现上可以借用腾讯地图的逆地址解析接口把地点字符串解析成经纬度再用Haversine公式计算距离。这个方案不复杂但能明显提升系统的实用性。第三个方向是管理后台的数据可视化。目前管理员后台是大而全的列表管理运维同学打开后只能看到数据看不到趋势。如果加一个按天统计的发布量折线图或者按分类的柱状图系统就更有“平台”的味道了。这个可以结合ECharts做后端写一个汇总查询接口前端用ECharts的图表组件渲染工作量不大但是加分很多。6. 一些项目交付的个人经验整个项目从设计到开发再到部署最大的体会是毕设项目的核心不是功能多而是“每一块功能都能讲清楚为什么这么做”。评委老师问的问题通常不是“这个功能怎么实现的”而是“你为什么用这个方案有没有考虑过别的方案”。这篇文章里我记录的每一个选型理由、每一个踩坑过程其实都是用来回答这类问题的素材。比如说当我被问“为什么不用云开发”时我说的理由有两个一是云开发虽然部署快但它绑定在微信生态里后端逻辑无法复用而这套Spring Boot的代码可以迁移到任何Java Web项目里二是云开发在免费额度外的定价偏高而自建后端在校园项目这个数据量级上几乎零成本。这种回答既展示了独立思考也展示了成本意识比单纯说“我觉得这样方便”要更有说服力。部署说明文档也很重要。我最后的交付物里包含了一份完整的部署文档从环境准备、数据库初始化、后端启动到小程序调试每一步都配有截图和命令。这份文档不仅毕业后留给学校存档很正式自己以后再看也能快速回忆起来项目的整体结构值得花时间整理好。我自己的体会是文档写清楚的过程正是你把这套系统彻底理解透的过程很多开发时“顺手就过”的细节在写文档时才会真正去验证也会更仔细地核对配置项和路径是否正确。如果你正在做同类型的题目希望这篇文章能帮你节约至少两个星期的踩坑时间把注意力放在真正重要的事情上让业务逻辑正确、让用户体验顺畅、让自己能应对答辩时候的各种提问。本文还有配套的精品资源点击获取
返回列表