搞 JavaWeb 毕设的同学十个里有八个会选这类“信息登记 审核流程”方向走失儿童认领与登记系统更是常客。标题里写着“宝贝回家”听着很有社会价值实际做起来却没那么浪漫——它本质上是一套给家属、管理员、认领申请人用的多角色业务系统。这些年我经手过不少类似项目见过太多人把精力砸在花里胡哨的页面上结果答辩被一个“状态怎么流转”问得哑口无言。这篇就把这类系统的需求拆解、技术选型、数据库设计、部署交付、论文演示一条线讲透适合正在做毕设、或者刚拿到一套源码准备二次开发的人直接参考。1. 这类“宝贝回家”系统需求本身比技术更值得先想清楚1.1 为什么一旦做偏项目就容易变成“花瓶”我见过一个典型的失败案例花了两周时间研究人脸识别接口最后因为免费额度用完演示时候只能拿一张奥特曼图片测试连后台审核功能都没写完。这里有个残酷的事实——本科毕设和课设的评分点大概率不在算法炫技而在业务闭环是否完整、权限是否清晰、数据设计是否合理、文档是否能自圆其说。走失儿童认领与登记系统从功能形态上看就是一个“信息报备 审核 公示 认领申请 结案归档”的流程系统。你不需要真的做出警方的人脸比对、不需要接地图定位、也不需要搞在线聊天。做的是把一条完整业务链跑通让每个角色都有事可做、有据可查。1.2 核心角色与基本流程一个标准的毕设版本至少要有三类角色系统管理员审核走失报备信息、审核认领申请、管理公告、查看统计报表报备人家属或监护人录入走失儿童信息、上传照片、填写体貌特征、查看审核进度认领申请人其他用户/机构/热心人浏览公示中的儿童信息、提交认领申请、上传关系证明材料。对应到代码里就是一套基于 Spring Boot 的后端接口加上一套 Vue 管理后台和用户端。因为 Vue 是前后端分离路由权限要做好不同角色登录后看到的菜单、能调的接口都不一样这是最容易被提问的点。1.3 状态机设计是整张“业务骨架”任何审核类系统都离不开状态。走失报备信息从出生到结束至少应该经过这样一个状态链路报备提交待审核→ 管理员审核通过公示中→ 用户提交认领申请认领审核中→ 管理员核对材料并确认认领成功/已结案→ 归档。不能只有“审核通过”和“未通过”两个状态那样太单薄了。我建议至少设计 6 个状态状态含义谁触发说明0待审核报备人提交初始状态1公示中管理员审核通过信息进入公开列表2已驳回管理员审核不通过可附驳回原因3认领审核中用户提交认领申请详情页附带申请记录4已结案管理员最终确认状态不可逆归档5已撤回报备人主动撤回公示前可操作请一定把这个状态流转画成一张图写进论文里它会成为你系统设计的亮点。很多同学的答辩PPT里只有页面截图却拿不出流程图这是丢分重灾区。1.4 功能边界别为了凑页数乱加模块我看到过有人给走失儿童系统加了支付功能理由是想做“打赏寻人”结果被老师一个问题打回来你如何保证打赏资金流向合法合规。这类“伪需求”在毕设里不但不加分反而暴露需求分析能力不足。可以适当加分的方向是公告管理、操作日志、按年龄段/地区/特征标签的搜索、待办提醒。这些和业务匹配度高代码也好实现写进论文还能体现“系统完整性”。2. 技术选型Spring Boot 配 Vue版本搭配先定死2.1 版本搭配建议原则只有一个用自己学校环境能稳定跑的版本组合不要追新。我给大多数毕设的保守组合是技术推荐版本原因JDK8 或 11老师机器兼容性最高Spring Boot2.7.x和 JDK8 搭配最稳第三方资料最多MyBatis-Plus3.5.xCRUD 效率高分页写论文好解释MySQL5.7 或 8.0都行连接串注意时区配置Vue2 Element UI教程最多、报错搜得到同学习惯度高Node14 或 16Vue2 生态下没那么多编译坑如果你非要用 Spring Boot 3就要接受 JDK 17、jakarta 包名变化、部分老教程失效的事实。反正我经手的课设项目凡是版本太“超前”的最后调研代码时痛苦指数翻倍。2.2 后端工程骨架和最小依赖后端我强烈建议按标准分层来做controller、service、mapper、entity、dto、vo、config。哪怕你项目不大也按这个分包因为论文里的结构图需要你讲得出来。pom.xml 里的核心依赖不用太多够用即可dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.2/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency再准备 JWT 工具用 jjwt 或 hutool 的 jwt 模块都行不用引入太重的安全框架。登录、鉴权、权限判断写一个拦截器或过滤器就够写进论文时也好讲。2.3 前端工程结构和路由控制Vue 部分建议用 Vue CLI 创建工程目录结构最好能让答辩老师一眼看懂src ├── api // 所有后端接口封装 ├── router // 路由配置 ├── store // 登录信息、用户状态 ├── views │ ├── admin // 管理员页面 │ ├── user // 报备人/认领申请页面 │ └── public // 公示列表、详情 ├── components └── utils // axios 实例、token 工具路由守卫一定要有这是前后端分离项目的“门禁”router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.meta.requiresAuth !token) { next(/login); } else { next(); } });再把动态菜单、按钮级权限做一下后端接口同时做校验答辩时候老师问“前端不能直接改按钮骗过系统么”你就可以理直气壮回答所有敏感操作后端也会校验角色。2.4 MyBatis-Plus 分页插件别省这一步很多同学一套代码里分页全自己写 limit费劲还容易出错。MyBatis-Plus 已经把页面封装好了只需要注册一个拦截器Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }容易踩的坑有三个第一拦截器虽然配了但 mapper 接口忘了加 MapperScan 注解导致 mapper 接口没被扫描到第二分页对象 Page 的参数没继续传给前端前端拿不到总数 total第三联表查询的时候 QueryWrapper 拼条件位置不对导致 count 统计结果异常。调试时先把 SQL 日志打开MyBatis-Plus 配置项里加一行mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl看 SQL 是最直接的排查方式。3. 数据库设计发力点状态和权限落到表结构里才有底气3.1 核心表别少于五张这种系统的表设计少了没法讲多了也容易乱。一个能拿出手的设计至少包含sys_user系统用户表id、username、password、real_name、phone、role、status、create_timelost_report走失报备表id、user_id、child_name、gender、birth_date、lost_date、lost_place、feature_desc、clothing_desc、contact_name、contact_phone、image_url、status、audit_opinion、create_time、update_timeclaim_application认领申请表id、report_id、applicant_name、applicant_phone、relation_desc、evidence_desc、evidence_images、status、create_timeaudit_record审核记录表id、biz_id、action、operator_id、comment、create_timenotice公告表 operation_log操作日志表。lost_report 表是核心。字段设计时要考虑列表页展示需要哪些字段、详情页需要哪些字段、搜索条件有哪些。别一股脑全塞进去也别存大字段文本不加约束。3.2 状态字段用数字还是字符串我的建议是数据库里存 int 或 tinyint然后在 Java 里定义一个枚举类统一管理。比如public enum ReportStatus { PENDING(0, 待审核), PUBLIC(1, 公示中), REJECTED(2, 已驳回), CLAIMING(3, 认领审核中), CLOSED(4, 已结案), WITHDRAWN(5, 已撤回); private final Integer code; private final String desc; // 构造器和 getter 省略 }好处有三避免魔法数字散落代码、前端展示时可以映射中文状态、写论文时状态表可以直接粘贴为“系统状态枚举设计”。坏处是如果你偷懒把枚举写在 controller 里后面改状态流转时就很痛苦。3.3 儿童隐私字段要格外处理这是很多系统思考不足的地方。走失儿童信息涉及隐私至少要做到两点第一公开列表页只展示脱敏信息比如联系电话显示成138****1234家庭地址不进入列表详情也只在登录后可见。第二数据库层面可以把联系信息拆成“公开字段”和“私密字段”私密字段只允许管理员和报备人本人通过接口查询。这一点做好不仅系统更成熟答辩时还会被高看一眼。往大里说这和真实寻亲场景中保护未成年人隐私的理念一致。3.4 图片和附件存哪里毕设阶段最简单的方案就是本地磁盘存储 虚拟路径映射。配置示例upload: local-path: /opt/app/upload/ url-prefix: /upload/再写一个 WebMvcConfigurer 把路径映射上registry.addResourceHandler(/upload/**) .addResourceLocations(file:/opt/app/upload/);如果你是在 Windows 上开发路径要自己注意斜杠问题。还有一种更省事的方案是用 MinIO 自建对象存储Docker 一行启动嫌装环境麻烦就直接用云 OSS 的免费额度不过毕设不推荐折腾本地存储 Nginx 代理足够交代清楚。3.5 数据权限普通用户只能动自己的数据很多系统的 bug 不是代码崩了而是越权。A 用户登录后修改 URL 里的 id 就能看别人的报备详情这在大作业里会直接扣分而且答辩老师非常喜欢演示这个场景。所以你必须在 service 层统一加数据过滤。报备人查询列表时SQL 条件是user_id 当前登录用户ID管理员查询时不分用户认领申请记录则通过 report_id 关联逐个校验归属。不要只靠前端隐藏按钮。4. 前后端联调里的真实坑跨域、JWT、XSS、图片上传、日期时区4.1 开发环境的跨域和生产环境不一样本地开发时Vue 项目默认跑在 8080 端口后端跑在 8081 端口前端直接调接口必然跨域。最简单的方案是配置 Vue Cli 的 devServer.proxy把/api请求转发到后端devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } }后端也可以顺手配一个 CORS。但注意一点生产环境如果用了 Nginx 反代CORS 配置就不要写“允许所有来源”而是放开前端正式域名即可否则很容易在安全上加一个减分项。4.2 Token 过期、401 统一处理JWT 登录逻辑不复杂但前端处理要闭环。axios 拦截器统一把 token 放进 Authorizationservice.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token); router.push(/login); } return Promise.reject(error); } );token 过期策略建议简单点登录接口签发 token设置 2 小时过期前端每次请求校验一次过期就跳登录页。别搞什么刷新 token毕设阶段反而容易引入复杂度和 bug。4.3 全局过滤器处理 XSS 攻击热词里正好有“springboot项目全局过滤器处理上传pdf文件时xss攻击”。意思大家都懂——凡是把用户输入当参数回显的地方都可能被塞脚本。常规做法是定义一个 XssFilter用 HttpServletRequestWrapper 包装请求对参数做 HTML 转义public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { Override public String getParameter(String name) { String value super.getParameter(name); return HtmlUtils.htmlEscape(value); } Override public String getHeader(String name) { String value super.getHeader(name); return HtmlUtils.htmlEscape(value); } Override public ServletInputStream getInputStream() throws IOException { // 对 JSON 请求体单独处理这里需要重写读取逻辑 } }两个最容易踩的坑一是只重写了 getParameter没重写 getInputStream导致 JSON 体里的字段没被过滤二是把所有输入都转义了结果富文本、公告内容里的正常 HTML 标签显示成一堆编码字符。所以动态决定哪些字段需要过滤、哪些字段允许保留格式是这部分的加分细节。4.4 图片上传的路径、大小、类型上传接口用 MultipartFile 接收注意三点限制文件大小比如 5MB、校验 ContentType、文件名重命名。String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString().replace(-, ) ext;预留漏洞的地方是文件名拼接路径所以一定要过滤掉..、/这类字符。上传成功后返回给前端http://ip:port/upload/xxx.jpg或者开发环境直接返回/upload/xxx.jpg让浏览器自动拼接当前域名。4.5 日期格式与时区后端用 LocalDateTime 时很容易出现 JSON 序列化成时间戳或带 T 的字符串。建议在 application.yml 统一spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/ShanghaiMySQL 连接串务必带上serverTimezoneAsia/Shanghai否则你入库的时间差八小时。前端展示用 dayjs 格式统一一下即可。5. 打包部署一条龙从 Windows 开发机到 Linux 服务器5.1 打包前的检查清单每次帮人排部署问题我第一句话都是问开发环境跑通了吗跑通了再按这个清单逐项检查数据库连接地址改成生产环境账号密码不硬编码在 application.yml 里用--spring.profiles.activeprod或环境变量关闭 debug 日志级别避免日志刷爆磁盘初始化 SQL 脚本单独放一个文件包含建库、建表、初始账号密码前端所有请求 baseURL 改成线上域名不要用 localhost图片上传目录提前建好文件夹权限要正确。5.2 后端 jar 打包和启动脚本后端打包最简单mvn clean package -DskipTests然后 target 下会生成 jar。启动别直接 java -jar 挂前台用 nohup 或 systemd。写一个 deploy.sh#!/bin/bash APP_NAMEmissing-child.jar PID$(pgrep -f $APP_NAME) if [ -n $PID ]; then kill -9 $PID fi nohup java -jar /opt/app/$APP_NAME \ --spring.profiles.activeprod \ --server.port8081 \ /opt/app/logs/app.log 21 echo started启动后必须验证curl http://localhost:8081/api/health能通再用tail -f /opt/app/logs/app.log看日志。5.3 前端 build 与 Nginx 配置前端npm run build后生成 dist上传到服务器。Nginx 配置是毕设部署里的重点很多同学就在这卡住。server { listen 80; server_name your.domain.com; client_max_body_size 20m; location / { root /opt/app/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8081/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /upload/ { alias /opt/app/upload/; } }try_files $uri $uri/ /index.html;这一行是给 vue-router 的 history 模式用的少了它一刷新页面就是 404。这个问题在答辩现场特别常见提前测一下能避免尴尬。5.4 数据库初始化与数据备份生产库不要直接拿开发库导出的乱数据。初始化脚本按编号来比如V1__init.sql、V2__data.sql至少包含建表语句、默认管理员账号、几组演示数据待审核、公示中、已结案各一两条。演示数据多弄几组不同状态的演示视频里就不用现场填表等审核。备份命令随手记一笔mysqldump -u root -p missing_child_db /backup/missing_child_$(date %F).sql5.5 部署文档写多细才算“能复现”拿到一套源码最怕的不是没文档而是文档写了等于没写去网上找个通用模板把项目名替换一下就完事。这种文档毫无意义。真正可复现的部署文档至少包含以下部分环境要求JDK 版本、MySQL 版本、Node 版本、Nginx 版本端口规划前端 80、后端 8081、MySQL 3306数据库导入步骤先创建数据库再导入 SQL 脚本后端 jar 放置路径、启动命令、停止命令前端 dist 上传位置、Nginx 配置完整示例验收用例列出至少五个“怎么做、预期结果是什么”的检查项比如“用管理员账号登录后进入待审核列表点击通过状态变为公示中”。6. LW论文/文档和演示视频毕设答辩的“缓存得分点”6.1 LW 怎么写不丢分拿到源码包里的 LW千万别直接交。先通读一遍逻辑再把不匹配自己演示过程的地方改掉。正常的论文结构就是这样几条主线选题背景与意义、需求分析、系统设计总体架构、用例图、ER 图、数据库设计、核心功能模块实现、系统测试、总结。写的时候每张图和每段代码都要能讲出“为什么”。比如状态机设计要说明为什么有“已撤回”状态数据权限为什么要在后端过滤公示列表为什么脱敏。这些内容比贴一百行代码都值钱。四月底到五月中旬的答辩季我基本每周都会被问“老师会不会查重”。我的回答始终一样先理解后复述该自己写的图表自己画直接抄的不但要改还得能讲。这不是套话是无数次现场血的教训。6.2 演示视频脚本讲业务而不是讲页面演示视频不是录屏流水账。建议五分钟到十分钟按场景走管理员登录进入待审核报备列表审核一条新报备状态变为公示中普通用户登录搜索到这条公示信息查看脱敏详情用户提交认领申请填写关系、上传证明材料管理员进入认领审核查看材料审核通过报备状态变为已结案再演示一条被驳回、一条被撤回的信息展示异常分支。录屏的时候用 OBS 就行免费且操作简单。开录之前先写稿每句话控制在十五秒内宁慢勿快。录完了自己先看一遍注意别把浏览器地址栏里的 localhost 露出来后端接口如果是localhost:8081最好统一改成线上域名。6.3 答辩常见追问提前准备好答案这类系统答辩时高阶问题相对集中我先列几个高频的状态为什么用数字存答统一枚举维护避免中文状态导致代码里到处比较字符串。同一个孩子被两方认领可以吗答设计上不允许状态进入认领审核中后其他用户无法再提交申请管理员结合材料审核不通过则退回公示中。别人恶意提交不真实的走失信息怎么办答管理员审核 操作日志追踪可封禁用户。数据权限怎么做的答后端 service 根据登录用户角色拼接过滤条件不是前端隐藏按钮。这些问题平时没准备现场基本要卡壳。建议把答案写成一个 QA 文档放在 LW 附录里自己心里也有底。6.4 关于“全 bao 一条龙交付”的边界与责任我必须直说完整源码、LW、部署说明、演示视频这套东西的价值在于省时间不在于“一定能过”。能不能过取决于你拿到之后有没有真正跑一遍有没有把核心代码看明白。我见过太多人把源码包往 U 盘里一放答辩前一天才开始看结果连接口地址都说不出来。另外这类系统到底能不能用于真实寻亲场景答案是不能把它当正式平台用。真实走失儿童的寻亲工作有专门的警方流程和公益组织渠道课程设计里模拟的审核、认领流程跟我们现实中的走失儿童救助体系完全是两回事。做项目时可以借鉴“信息登记 多方确认”的思路但不要夸张宣传自己的系统能“帮助寻找失踪儿童”。写论文、录演示视频时也要注意用“模拟数据”不要使用真实儿童的隐私信息。这是负责任的做法也是论文里“社会价值”部分要写得克制的原因。我在实际带项目时经常强调一件事把状态机、权限设计、部署链路讲清楚比堆一堆第三方 SDK 更接近一个合格后端工程师的素养。这套系统做完你至少会经历一次完整的“数据库表设计 - 接口开发 - 前后端联调 - 部署上线 - 写文档录演示”全过程这个过程本身就是最大的收获。
