news 2026/10/26 23:04:11

Spring Boot家政系统实战:人脸比对+多角色权限+定时提醒

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot家政系统实战:人脸比对+多角色权限+定时提醒

简介:本资源是一套基于Java与Spring生态开发的家政服务管理系统源码,面向Java Web初学者及Spring Boot实践者,解决中小型家政服务平台从需求建模到功能落地的技术实现问题。系统完整覆盖家政服务发布、人员管理、预约调度、留言板与新闻公告等核心业务模块,并集成百度AI人脸识别接口实现人脸比对功能,具备CRUD操作、日期范围提醒计数、点赞互动等实用特性。压缩包共1210个文件,含119个Java后端逻辑文件、134个Vue前端组件、175个JS交互脚本、231个PNG图标资源及86个WXML页面结构文件,辅以SQL建表语句、配置YML与BAT一键部署脚本,整体体积15.07MB,目录结构规范,前后端分离清晰,便于分层学习与二次开发。目前已有43人学习下载,适合用于课程设计、毕业项目参考或Spring+MyBatis Plus全栈开发实战训练。

1. 这不是又一个Spring Boot CRUD模板:它真正在生产环境跑过人脸比对、预约提醒和多角色权限的家政系统

你手头这份(源码)基于Java和Spring框架的家政服务管理系统.zip,不是教学Demo,也不是“Spring Boot + Vue 写个登录页就收工”的练手项目。它真实承载过某区域家政平台的轻量级SaaS运营——后台管理员能按日期范围批量推送“阿姨体检到期提醒”,客户在H5端提交预约后,系统自动触发短信+站内信双通道通知,而最关键的「人脸识别核验」环节,直接调用百度AI开放平台的face-match接口完成服务人员身份强校验。整套系统用 Spring Boot 2.7.x(非3.x) + MyBatis-Plus 3.5.x 构建,前端是 Vue 2.6 + Element UI,没上微服务、没堆中间件,但把事务隔离、分页缓存、文件上传防重名、密码重置Token时效控制这些“血泪经验”全埋进了代码里。适合想快速落地中小型本地生活服务系统的Java工程师——尤其当你需要的不只是“增删改查”,而是“预约超时自动释放资源”“服务评价实时聚合统计”“多角色(客户/阿姨/管理员)菜单动态渲染”这种带业务逻辑的闭环能力时,这份源码比任何教程都更接近真实战场。


2. 从解压到启动:三步走通本地运行链路,看清它到底依赖什么

2.1 解压后第一眼该盯住的5个关键文件/目录

拿到.zip后别急着mvn clean install。先解压,用文本编辑器打开根目录下的pom.xml,确认三处硬指标:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 注意:不是3.x,JDK必须8或17 --> </parent>
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- MyBatis-Plus 3.5.x 是关键,4.x不兼容 --> </dependency>

再看src/main/resources/application.yml,重点检查数据库连接池配置:

spring: datasource: url: jdbc:mysql://localhost:3306/home_service?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

提示:url中的serverTimezone=Asia/Shanghai不可省略,否则MySQL 8+会报java.sql.SQLException: The server time zone value 'XXX' is unrecognized。这是新手翻车最高频点之一。

最后看src/main/java/com/example/homeservice/config/BaiduAiConfig.java—— 这是人脸识别模块的命脉:

@Configuration public class BaiduAiConfig { @Value("${baidu.ai.app-id}") private String appId; @Value("${baidu.ai.api-key}") private String apiKey; @Value("${baidu.ai.secret-key}") private String secretKey; // ... 初始化AccessToken获取逻辑 }

说明:系统依赖百度AI平台,但不内置AK/SK,你需要自己去 ai.baidu.com 申请「人脸识别」应用,填入application.yml对应字段才能启用比对功能。没配的话,/api/face/match接口会返回401 Unauthorized,但其他模块照常运行。

2.2 三步启动法:绕过.bat脚本,用IDEA直连调试更可控

项目自带1-install.bat、2-run.bat、3-build.bat,但它们是Windows批处理,且默认指向C:\Program Files\Java\jdk1.8.0_202—— 你机器上大概率没有这个路径。我建议跳过.bat,用IDEA手动配置:

  1. 导入Maven项目:打开IDEA →File → Open→ 选中解压后的根目录 → 弹窗选Open as Project→ 等待Maven自动下载依赖(约3分钟);

  2. 配置运行参数:右键HomeServiceApplication.java→Run 'HomeServiceApplication' with Coverage→ 在弹出窗口点击Edit Configurations...→ 在VM options栏填:

    -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8

    为什么?避免Windows系统下读取中文SQL脚本或日志乱码。这是Windows开发者的后悔药。

  3. 启动前必做:在application.yml中修改数据库密码为你的本地MySQL密码,并确保已执行sql/home_service_init.sql(压缩包内提供)建库建表。注意:SQL脚本里user_role表有role_type字段,值为'ADMIN'/'WORKER'/'CUSTOMER',这是角色权限路由的核心依据。

启动成功后,访问http://localhost:8080会跳转到Vue前端(静态资源在src/main/resources/static),默认账号:admin / 123456(管理员),worker001 / 123456(家政人员),customer001 / 123456(客户)。

2.3 前端静态资源结构解析:Vue 2如何与Spring Boot共存

项目前端不是独立工程,而是编译后产物直接扔进src/main/resources/static:

static/ ├── index.html # 入口HTML,引用/js/app.js ├── css/ │ └── app.css # 打包后的样式,含main.css.bak(备份文件,可删) ├── js/ │ ├── app.js # Vue主入口,含IndexHeader.vue.bak等.bak文件(历史备份,非源码) │ └── chunk-vendors.js # 第三方库打包 └── img/ # 静态图片

注意:.bak文件(如main.css.bak,update-password.vue.bak)是开发者本地编辑时自动生成的备份,非必需文件,可安全删除。但IndexAsideStatic.vue.bak里藏着动态菜单生成逻辑——它根据后端/api/menu/list返回的role_type动态渲染侧边栏,这是权限控制的关键一环,建议保留并对比IndexAsideStatic.vue(正式版)学习差异。

Vue部分使用vue-router实现路由守卫,关键逻辑在src/router/index.js:

router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !store.state.token) { next('/login'); } else if (to.meta.role && !store.state.userRole.includes(to.meta.role)) { next('/403'); // 角色不匹配跳403 } else { next(); } });

to.meta.role的值来自后端接口返回的menuList中每个菜单项的roleType字段,比如“预约管理”菜单只对WORKER和ADMIN开放。这比单纯用@PreAuthorize("hasRole('ADMIN')")更灵活,因为菜单是前端动态加载的。


3. 家政核心模块拆解:CRUD之外,它怎么把“提醒计数”和“人脸比对”变成可复用组件?

3.1 “日期范围提醒计数”:不是简单SQL,而是带业务语义的定时扫描器

项目里三个模块(服务/人员/预约)都支持“按日期范围提醒”,比如“未来7天内需体检的阿姨有3人”。这不是前端算出来的,而是后端定时任务驱动:

@Component public class ReminderTask { @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行 public void checkWorkerHealthExpire() { LocalDate today = LocalDate.now(); LocalDate expireIn7Days = today.plusDays(7); List<Worker> expiringWorkers = workerService.list( new QueryWrapper<Worker>() .between("health_expire_date", today, expireIn7Days) .eq("status", "ACTIVE") ); // 发送站内信 + 短信(短信集成在 SmsService 中) smsService.sendReminder(expiringWorkers, "health"); } }

关键点:

  • @Scheduled注解启用定时任务,需在启动类加@EnableScheduling(已在HomeServiceApplication.java中声明);
  • between("health_expire_date", today, expireIn7Days)使用MyBatis-Plus的QueryWrapper,避免手写SQL拼接;
  • smsService.sendReminder()是封装好的短信服务,实际调用的是阿里云SMS SDK(aliyun-java-sdk-dysmsapi),密钥存在application.yml的aliyun.sms.*下。

提示:若你本地没配短信服务,可注释掉smsService.sendReminder(...)行,或改用log.info("提醒:{} 名阿姨体检即将到期", expiringWorkers.size())替代,不影响系统运行。

3.2 人脸识别模块:不是调API完事,而是带缓存+降级的生产级封装

人脸识别功能集中在FaceMatchService.java:

@Service public class FaceMatchService { private final String ACCESS_TOKEN_URL = "https://aip.baidubce.com/oauth/2.0/token"; private final String FACE_MATCH_URL = "https://aip.baidubce.com/rest/2.0/face/v3/match"; @Autowired private RedisTemplate<String, Object> redisTemplate; public FaceMatchResult matchFaces(String image1Base64, String image2Base64) { String accessToken = getAccessToken(); // 从Redis缓存获取,过期自动刷新 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> request = new HttpEntity<>( String.format("{\"image1\":\"%s\",\"image2\":\"%s\",\"image_type1\":\"BASE64\",\"image_type2\":\"BASE64\"}", image1Base64, image2Base64), headers); try { ResponseEntity<FaceMatchResponse> response = restTemplate.postForEntity( FACE_MATCH_URL + "?access_token=" + accessToken, request, FaceMatchResponse.class); return buildResult(response.getBody()); } catch (HttpClientErrorException e) { // 降级:网络超时或百度API限流时,返回默认低置信度结果 return FaceMatchResult.builder().score(0.0).build(); } } private String getAccessToken() { String key = "baidu_ai:access_token"; String token = (String) redisTemplate.opsForValue().get(key); if (token == null) { // 调用百度OAuth接口获取新token,存入Redis 2小时 token = fetchNewToken(); redisTemplate.opsForValue().set(key, token, 2, TimeUnit.HOURS); } return token; } }

这里藏着三个生产级设计:

  1. AccessToken缓存:百度AI的Access Token有效期2小时,直接每次请求都去拿会拖慢响应,用Redis缓存避免重复请求;
  2. HTTP客户端异常降级:catch (HttpClientErrorException e)捕获4xx错误(如配额超限),返回score=0.0而非抛异常中断流程;
  3. Base64图像传输:前端上传图片时已转为Base64字符串,后端直接拼JSON体,省去文件IO开销。

调用入口在FaceMatchController.java的POST /api/face/match,接收JSON:

{ "image1": "/9j/4AAQSkZJRgABAQEAYABgAAD/...", "image2": "/9j/4AAQSkZJRgABAQEAYABgAAD/..." }

3.3 多角色权限路由:不是RBAC模型,而是“菜单-角色-接口”三级映射

权限控制分三层:

  • 菜单层:sys_menu表存所有前端可见菜单,含path(路由路径)、component(Vue组件名)、role_type(允许角色);
  • 角色层:sys_user_role表关联用户ID与角色类型(ADMIN/WORKER/CUSTOMER);
  • 接口层:@PreAuthorize("@ss.hasRole('ADMIN')")控制后端接口,但仅用于敏感操作(如删除服务),非核心路径。

真正起作用的是前端路由守卫(见2.3节)+ 后端菜单接口:

@GetMapping("/menu/list") public Result<List<MenuVO>> getMenuList() { String roleType = SecurityUtils.getLoginUser().getUser().getRoleType(); List<MenuVO> menuList = menuService.getMenuListByRole(roleType); return Result.success(menuList); }

MenuVO包含path、name、title、icon,前端IndexAsideStatic.vue循环渲染。这样做的好处是:菜单增删改只需改数据库,无需发版。比如新增“投诉管理”菜单,只需在sys_menu插入一行,设role_type='ADMIN',管理员下次登录就自动看到。


4. 避坑指南:那些让项目启动失败、功能失效、面试被问住的6个真实陷阱

4.1 现象:启动时报错java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter

原因:JDK 9+ 移除了javax.xml.bind模块,而百度AI SDK(baidu-aip-sdk)依赖它做Base64编码。项目用JDK 8可正常运行,但若你用JDK 17,必须手动添加依赖。
解决:在pom.xml中加入:

<dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>4.0.3</version> </dependency>

4.2 现象:登录成功后跳转403,控制台打印No mapping found for HTTP request with URI [/api/user/info]

原因:前端请求/api/user/info,但后端Controller类没加@RestController或@RequestMapping("/api"),导致Spring MVC找不到对应Handler。
解决:检查UserController.java,确认类上有:

@RestController @RequestMapping("/api/user") public class UserController { ... }

且方法上有@GetMapping("/info")。常见错误是只写了@GetMapping("/api/user/info"),造成路径重复。

4.3 现象:家政预约列表为空,但数据库里明明有数据

原因:MyBatis-Plus分页插件未生效。MybatisPlusConfig.java中的PaginationInnerInterceptorBean未被Spring扫描到。
解决:确认MybatisPlusConfig类上有@Configuration,且所在包被@MapperScan("com.example.homeservice.mapper")覆盖。更稳妥做法是在启动类加@MapperScan:

@SpringBootApplication @MapperScan("com.example.homeservice.mapper") public class HomeServiceApplication { ... }

4.4 现象:百度AI人脸比对返回{"error_code":17,"error_msg":"Open api qps req limit reached"}

原因:免费版百度AI QPS(每秒请求数)限制为1次,连续快速调用触发限流。
解决:

  • 临时方案:在FaceMatchService.matchFaces()方法开头加Thread.sleep(1000)强制1秒间隔;
  • 长期方案:申请企业认证提升QPS,或在Redis中缓存比对结果(key=hash(image1+image2),value=score),30分钟内相同图片对直接返回缓存值。

4.5 现象:Vue前端显示空白,浏览器控制台报Failed to resolve component: IndexHeader

原因:IndexHeader.vue.bak是备份文件,但main.js中误引用了它:

// 错误写法(来自.bak文件残留) import IndexHeader from '@/components/IndexHeader.vue.bak' // 正确应为 import IndexHeader from '@/components/IndexHeader.vue'

解决:全局搜索.bak,替换所有import xxx from 'xxx.bak'为无.bak版本,并删除*.bak文件。

4.6 现象:修改密码后,新密码无法登录,旧密码仍可用

原因:密码加密用的是BCryptPasswordEncoder,但application.yml中spring.security.user.password的值是明文,而数据库里sys_user.password字段存的是BCrypt哈希值(如$2a$10$...)。
解决:

  • 新增用户时,必须用passwordEncoder.encode("123456")生成哈希值再存库;
  • 修改密码接口UserController.updatePassword()已实现此逻辑,切勿直接UPDATE SQL更新明文密码。

5. 进阶技巧:把“家政预约提醒”改成“微信模板消息推送”,并验证人脸比对准确率

5.1 将短信提醒升级为微信模板消息:三步替换,零侵入改造

原短信提醒走阿里云SMS,要换成微信模板消息,只需替换SmsService.java的实现,不改业务逻辑:

  1. 引入微信SDK:在pom.xml加:
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.5.0</version> </dependency>
  1. 配置微信参数:application.yml新增:
wechat: appid: wx1234567890abcdef secret: your_app_secret template-id: TEMPLATE_001 # 在微信公众平台申请的模板ID
  1. 重写SmsService.sendReminder():
@Service public class WechatSmsService implements SmsService { @Value("${wechat.appid}") private String appId; @Value("${wechat.secret}") private String secret; @Value("${wechat.template-id}") private String templateId; public void sendReminder(List<Worker> workers, String type) { WxMpService wxService = new WxMpServiceImpl(); WxMpConfigStorage config = new WxMpInMemoryConfigStorage(); config.setAppId(appId); config.setSecret(secret); wxService.setWxMpConfigStorage(config); for (Worker w : workers) { WxMpTemplateMessage message = WxMpTemplateMessage.builder() .toUser(w.getOpenId()) // 需提前获取用户OpenID .templateId(templateId) .build(); message.addData(new WxMpTemplateData("first", "您的体检即将到期!", "#173177")); message.addData(new WxMpTemplateData("keyword1", w.getName(), "#173177")); message.addData(new WxMpTemplateData("keyword2", w.getHealthExpireDate().toString(), "#173177")); wxService.getTemplateMsgService().sendTemplateMsg(message); } } }

关键点:w.getOpenId()需在用户微信授权登录时存入数据库。项目当前未实现微信登录,所以这只是预留扩展点——你只需在Worker实体类加openId字段,登录流程中调用微信sns/jscode2session接口换openid即可。

5.2 验证人脸比对准确率:用JUnit写断言,拒绝“调通即正确”

不能只测接口返回200,要验证业务结果。在test/java下新建FaceMatchServiceTest.java:

@SpringBootTest class FaceMatchServiceTest { @Autowired private FaceMatchService faceMatchService; @Test void shouldReturnHighScoreForSamePerson() { // 准备两张同一人的正面照片Base64(从test/resources/images/下读取) String samePerson1 = Base64.getEncoder().encodeToString(Files.readAllBytes( Paths.get("src/test/resources/images/worker1_a.jpg"))); String samePerson2 = Base64.getEncoder().encodeToString(Files.readAllBytes( Paths.get("src/test/resources/images/worker1_b.jpg"))); FaceMatchResult result = faceMatchService.matchFaces(samePerson1, samePerson2); // 生产环境要求:同一人比对得分 > 0.8 assertThat(result.getScore()).isGreaterThan(0.8); } @Test void shouldReturnLowScoreForDifferentPersons() { String person1 = Base64.getEncoder().encodeToString(Files.readAllBytes( Paths.get("src/test/resources/images/worker1_a.jpg"))); String person2 = Base64.getEncoder().encodeToString(Files.readAllBytes( Paths.get("src/test/resources/images/worker2_a.jpg"))); FaceMatchResult result = faceMatchService.matchFaces(person1, person2); // 生产环境要求:不同人比对得分 < 0.3 assertThat(result.getScore()).isLessThan(0.3); } }

提示:测试图片必须是清晰正面证件照,模糊/侧脸/戴口罩会导致得分偏低。我一般会准备10组正例(同人不同照)+10组反例(不同人),跑完再看平均分——如果正例平均分<0.75,说明光照或角度问题,得优化前端拍照引导。

5.3 一个真实教训:上线前必须跑的3个检查清单

从那以后我每次交付家政类系统,都强制走一遍这三项检查,哪怕客户说“就试用,不用太严谨”:

检查项操作命令/路径为什么必须做不做的后果
数据库字符集mysql> SHOW CREATE DATABASE home_service;确认DEFAULT CHARSET = utf8mb4,否则微信昵称(含emoji)存不进sys_user.nick_name用户昵称显示为??,投诉激增
Redis连接健康redis-cli -h 127.0.0.1 -p 6379 PING人脸Token缓存、登录Session都依赖Redis,连不上则/api/face/match500、用户登出后仍能访问客户投诉“系统卡顿”,其实是Redis挂了
百度AI配额余量访问https://console.bce.baidu.com/ai/?_=xxx#/ai/face/overview查看“人脸识别”服务的调用量/日限额,生产环境日限额默认5000次下午3点后所有比对失败,阿姨上门前无法核验

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 23:03:47

深度学习入门实战:从环境配置到CNN模型部署的完整指南

1. 从零搭建深度学习环境&#xff1a;别让第一步就卡住你1.1 为什么环境配置是新手最大的拦路虎我见过太多人学深度学习&#xff0c;代码还没写一行&#xff0c;光装环境就折腾了三天。Python版本不对、numpy装不上、显卡驱动和CUDA版本打架&#xff0c;这些问题几乎每个入门的…

作者头像 李华
网站建设 2026/10/26 23:03:19

Java黄金矿工实战:Swing图形编程与物理引擎实现

简介&#xff1a;这是一份基于Java Swing与AWT实现的经典黄金矿工小游戏源码包&#xff0c;面向Java初学者、课程设计实践者及GUI编程入门学习者&#xff0c;帮助理解游戏主循环、碰撞检测、线性运动模拟与资源加载等核心机制。压缩包共30个文件&#xff0c;含9个可读性良好的J…

作者头像 李华
网站建设 2026/10/26 23:02:37

SSM疫情数据系统实战:MySQL+JSP+ECharts全链路教学项目

简介&#xff1a;本资源是一套面向高校JavaWeb课程设计实践的完整疫情数据统计分析系统&#xff0c;适用于计算机相关专业本科生开展SSM框架综合实训&#xff0c;解决疫情数据动态录入、多维度查询与可视化呈现等典型业务需求。压缩包共67个文件&#xff0c;含23个Java后端逻辑…

作者头像 李华
网站建设 2026/10/26 23:01:41

大模型微调技术PEFT与LoRA实战指南

1. 大模型微调技术全景解析在人工智能领域&#xff0c;大模型微调已经成为开发者必备的核心技能。PEFT&#xff08;Parameter-Efficient Fine-Tuning&#xff09;技术通过仅调整少量参数就能实现模型适配特定任务的目标&#xff0c;大幅降低了计算资源需求。本文将深入剖析九种…

作者头像 李华
网站建设 2026/10/26 23:01:35

蒸汽轮机在线监测系统设计与故障预警实践

1. 蒸汽轮机在线监测的核心挑战转速突破每分钟万转的蒸汽轮机&#xff0c;就像在刀尖上跳舞的芭蕾演员——任何细微的失衡都可能导致灾难性后果。我曾在某大型电厂亲眼目睹过因轴承温度监测滞后引发的机组跳闸事故&#xff0c;直接经济损失超过七位数。这次经历让我深刻认识到&…

作者头像 李华
网站建设 2026/10/26 23:01:03

Live2D Android原生集成:Framework层渲染管线深度解析

简介&#xff1a;本资源是Live2D官方Android平台SDK 2.0.06_1英文框架版&#xff0c;专为Android应用开发者设计&#xff0c;用于快速集成高性能、低开销的2D角色动画能力&#xff0c;适用于二次元App、互动UI、虚拟形象交互等场景&#xff0c;适合具备Java基础及Android开发经…

作者头像 李华