Spring Boot衣物保养管理系统:数据库设计与定时任务实战解析

发布时间:2026/10/20 4:47:11
Spring Boot衣物保养管理系统:数据库设计与定时任务实战解析 每年毕业季前后总能在各种技术群里看到有人问同一个问题“毕设选题到底选什么好”我看了很多题目之后发现像“基于Spring Boot的衣物保养管理系统”这种题目出镜率相当高。原因是它看起来不像电商、外卖那么复杂但里面的业务逻辑却比普通的增删改查完整得多特别适合用来展示一个学生的数据库设计能力、业务建模能力和 Java 后端基本功。我前后帮几个同学完整梳理过这套系统从需求分析到数据库设计再到核心代码落地每一步都踩过一些坑。这篇文章就按实际开发顺序把这个项目从选题到部署的关键环节拆开讲清楚重点解释每个设计决策背后的原因也给准备做相似管理系统的同学一份可以直接参考的实操方案。1. 项目概述与需求拆解1.1 选题背景它到底解决什么问题衣物保养这件事听起来很生活化但真管理起来是有痛点。普通人家里有几十件衣服哪些需要干洗哪些必须手洗哪些换季之后要特别收纳时间一长根本记不住。如果放到干洗店、小型服装护理店或者学校洗衣房这类场景里问题会被放大客户送来的衣物不知道上次保养时间是什么时候不同材质的衣服保养周期也不一样全靠店员脑袋记很容易漏掉重要提醒。这个项目本质上做的就是把“衣物信息、保养周期、保养任务、执行记录、到期提醒”这条链路搬到线上。Spring Boot 作为后端框架正好能承担起用户管理、衣物档案维护、任务生成和提醒查询这些核心接口。对于毕设而言这个选题的价值在于它既有基本的 CRUD又有业务状态变化、定时任务、报表统计这些加分项不会显得太单薄也不至于复杂到做不完。1.2 需求梳理两个角色和一条业务主链做系统之前第一步不是写代码而是先把角色理清楚。这个管理系统我一般建议分成两个核心角色普通用户可以注册登录录入自己的衣物查看保养计划确认保养任务查看历史保养记录。管理员除了拥有普通用户功能外还可以管理用户账号、查看所有衣物统计信息、维护衣物分类和保养项目模板。如果用户量不大这两种角色共用同一套用户表用一个role字段区分就行不要建两张独立的用户表那样反而增加维护成本。系统跑起来之后核心业务主线是用户登记衣物 - 系统根据衣物类别和上次保养时间自动生成保养计划 - 用户执行保养并填写记录 - 系统在下次保养时间到来之前发出提醒。这条主链一旦打通项目演示时无论问哪个模块你都能把逻辑串起来讲清楚。1.3 功能边界与亮点设计很多同学做毕设喜欢堆功能觉得页面越多越好。但衣物保养管理系统最忌讳的就是把简单事情复杂化。我建议把功能控制在这个范围用户注册登录、个人资料修改。衣物信息的新增、编辑、删除、分页查询。衣物图片上传与展示。保养任务自动生成、手动确认完成。保养到期提醒登录后待办提醒即可。按衣物类别、保养状态、保养完成率做统计图表。如果还想加点亮点可以加入“保养日历”把每个月的保养任务散点展示在日历上这个功能本质还是查表只是展示形式更好看答辩时也比较容易把思路讲明白。2. 技术选型与架构设计2.1 为什么坚定选择 Spring Boot题目里点名要 Spring Boot这本身就说明它适合做这类系统。对比之前的 SSM 组合Spring Boot 最大的变化是省掉了大量 XML 配置只需要在pom.xml里引入依赖再配合自动配置就能启动 Web 项目。对于几个月的毕设周期来说把时间省下来去打磨业务细节比花时间在配置上更有价值。另外 Spring Boot 内嵌了 Tomcat本地开发不需要单独装服务器打包之后一个java -jar命令就能跑起来。这对后面部署演示来说非常友好即使答辩现场的电脑环境比较干净只要装了 JDK 就能运行系统。生态方面Spring Boot 接入 MyBatis、Redis、Spring Security 这些常用组件都相当顺手真遇到问题网上的资料也最多。2.2 前端方案从模板渲染到前后端分离前端怎么选主要看你的时间预算。我有两种推荐方案第一种是后端使用 Thymeleaf 模板引擎加上 Bootstrap 或者 Layui 做样式。这种方式适合开发周期很紧、对前端不熟的同学所有页面都由后端渲染不需要单独启动前端服务代码量相对少。第二种是前后端分离前端用 Vue 3 Element Plus后端只提供 JSON 接口。这种方式结构清晰答辩时也可以强调“前后端分离架构”但需要额外处理跨域、Token 存储、接口联调这些问题。我带的同学里有一个选了第一种两个月就完成了另一个选了第二种多花了三周时间但最终效果确实更现代。如果你已经有一定 Vue 基础建议选第二种如果时间真的不够用模板渲染也完全能拿得出手重点还是在后端业务逻辑。2.3 分层架构与项目目录规划Spring Boot 项目启动很快但如果刚开始就把所有代码堆到一个类里后面越写越难受。我会在项目初始化的时候就把包结构固定下来com.example.clothes ├── common // 统一返回结果、异常处理、常量 ├── config // 配置类如跨域、拦截器、静态资源映射 ├── controller // 接口层 ├── service // 业务逻辑层 ├── mapper // 数据访问层 ├── entity // 数据库实体 ├── dto // 入参出参对象 └── task // 定时任务这个分层的好处是每一层职责单一比如Controller只负责接收参数和返回结果Service只处理业务逻辑Mapper只跟数据库打交道。后面做事务控制、权限校验的时候改动范围也很明确不容易出现改一个功能把另一个功能带崩的情况。3. 数据库设计与业务建模3.1 核心表设计逻辑数据库设计是整个项目的地基表设计得好后面的代码能省一半力气。这套系统我建议设计六张核心表分别是用户表、衣物表、保养任务表、保养记录表、提醒表、操作日志表。用户表sys_user主要字段有id、username、password、nickname、phone、role、status、create_time。密码字段存储加密后的结果不要存明文这样演示安全功能的时候也能讲两句。衣物表clothing是系统的核心字段设计一定要想清楚。我常用的字段如下id主键。user_id所属用户。name衣物名称。category衣物类别比如外套、裤装、裙装。fabric材质如羊毛、棉麻、化纤。colour颜色。image_url图片地址。purchase_time购买时间。care_cycle_type保养周期类型是按天还是按月。care_cycle_value周期数值比如 30 天。last_care_time上次保养时间。next_care_time下次保养时间。status当前状态在保养中、正常、待处理。这里的关键是last_care_time和next_care_time几乎所有跟保养相关的功能都要查这两个字段。比如用户登录之后系统要计算哪些衣物已经超过next_care_time就需要用当前时间和它做对比。保养任务表care_task用于记录每一条待办任务字段包括id、clothing_id、user_id、task_type、plan_time、actual_time、status、operator、remark。任务状态我习惯用0表示待处理1表示已完成2表示已取消。这样后面做状态统计时直接用group by status就行了。保养记录表care_record用于保存用户实际操作过什么字段包括clothing_id、care_type、care_time、cost、content、next_care_time。任务表和记录表的区别要讲清楚任务是“计划要做的事”记录是“已经做过的事”。很多同学会把这两张表合在一起结果逻辑越写越混乱我强烈建议分开。提醒表remind_record用于记录系统产生的提醒消息字段有user_id、clothing_id、task_id、remind_time、is_read。操作日志表operation_log则记录谁在什么时候做了什么操作主要给管理员查看顺便也能作为答辩时体现系统完整性的一个点。3.2 关键字段与约束细节字段设计有几个细节直接影响后面编码难度状态字段统一用tinyint不要用字符串。字符串1和0看着直观但查询效率和存储空间都不如数值还容易写错。金额字段用decimal(10,2)如果保养需要记录费用别用double浮点计算会有精度问题。时间字段统一用datetime不要混用date和timestamp否则处理时间区间查询时很容易出问题。图片地址不要直接存二进制blob存一个可访问的 URL 字符串就行文件放到服务器或对象存储上。另外索引设计也很重要。clothing表的user_id和next_care_time要加索引因为首页查询“我的衣物待保养列表”时最常用的就是这两个条件。care_task表的user_id和status同样建议加联合索引。虽然学生项目数据量不大索引影响不明显但设计文档里写上索引答辩老师会觉得你考虑得比较全面。3.3 数据权限与状态机设计那么问题来了数据权限怎么控制用户登录之后只能看到自己的衣物和任务管理员可以看全部。这个逻辑不能只靠 SQL 里固定写死比较好的做法是在 Service 层先从登录信息里取出userId再作为查询条件传给 Mapper。也就是说后端的每一个查询接口都要有当前用户身份的参与不能提供不受限制的全量查询接口。状态流转也要提前规划。衣物的状态不是随便改的通常的流转路径是登记 - 待保养 - 保养中 - 已保养。如果代码里到处允许把“已保养”直接改成“登记”整个系统就会显得不严谨。我一般会在 Service 层写一个状态校验只有合法的状态迁移才允许执行。例如执行保养时只有待保养状态的任务才能置为完成如果是已经完成的任务再提交就直接抛出业务异常。4. 核心功能模块实现4.1 衣物档案管理从表单到上传衣物管理模块是整个项目的基础包含新增、编辑、删除、列表查询四个接口。新增和编辑可以合并成一个保存接口通过传入的id判断是新增还是更新。衣物图片上传是大多数同学卡住的地方。前端选完图片后后端接口接收MultipartFile然后把文件保存到本地目录再把访问路径返回。这里我建议分两步配置静态资源映射把/upload/**映射到本机的/data/upload/目录。文件保存时生成唯一文件名比如用UUID.randomUUID()加上原始扩展名避免中文文件名乱码和重名覆盖。一个简单的上传接口示例PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file) { if (file.isEmpty() || file.getSize() 5 * 1024 * 1024) { throw new BusinessException(文件为空或超过5MB); } String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String filename UUID.randomUUID().toString().replace(-, ) ext; File dest new File(uploadDir filename); file.transferTo(dest); return Result.success(/upload/ filename); }注意文件大小和类型都要校验类型可以用扩展名白名单比如只允许jpg、png、jpeg、webp。如果不做校验上传一个.exe文件也会成功从系统安全角度说不过去。4.2 保养任务自动生成定时体系怎么搭任务自动生成是这个系统最需要讲清楚的功能。它本质上是一段定时任务每天扫描一次所有衣物查找当前时间已经超过next_care_time且没有生成过任务的衣服然后生成一条care_task。Spring Boot 里开启定时任务很简单启动类上加上EnableScheduling然后在任务类的方法上标注Scheduled。为了不每分钟跑一次太浪费我通常配置成每天零点执行一次或者每隔一小时执行一次。核心思路如下Component public class CareTaskGenerator { Scheduled(cron 0 0 2 * * ?) public void generateTasks() { ListClothing overdueList clothingMapper.selectOverdue(DateTime.now()); for (Clothing clothing : overdueList) { int count careTaskMapper.countPendingTask(clothing.getId()); if (count 0) { continue; } CareTask task new CareTask(); task.setClothingId(clothing.getId()); task.setUserId(clothing.getUserId()); task.setStatus(0); task.setPlanTime(LocalDateTime.now()); careTaskMapper.insert(task); } } }这里的countPendingTask非常重要它用来防止重复生成任务。如果某件衣服已经有一条“待处理”状态的任务就不应该再生成第二条。我就见过一个同学没写这一步跑了一晚上定时任务第二天数据库里多了几百条重复数据演示的时候尴尬得不行。4.3 提醒通知模块实现提醒功能不需要做得太复杂站内信形式就足够。用户每次登录后在首页查询未读提醒展示“您的羊毛大衣已超过保养时间3天请及时处理”这样的消息。点进去之后可以把is_read置为 1也可以直接点击跳转到对应衣物详情。查询示例可以这样写public ListRemindVO listUnRead(Long userId) { ListRemindRecord records remindMapper.selectUnRead(userId); return records.stream().map(record - { RemindVO vo new RemindVO(); Clothing clothing clothingMapper.selectById(record.getClothingId()); vo.setClothingName(clothing.getName()); vo.setDays(ChronoUnit.DAYS.between(record.getRemindTime(), LocalDateTime.now())); return vo; }).collect(Collectors.toList()); }除了站内信如果想展示更多技术点还可以在提醒模块里引入邮件通知。用 Spring Boot 的spring-boot-starter-mail依赖配置一个发件邮箱在生成任务的同时发送一封保养提醒邮件。注意邮件发送过程可能比较慢不要在用户请求线程里同步发送可以丢到异步线程池处理或者直接用Async注解让方法异步执行。4.4 统计报表与数据看板管理员首页一般要展示几个核心指标衣物总数、待保养任务数、本月完成保养次数、各衣物类别占比。这些数据用 SQL 聚合查询很快就能得到。例如统计每个类别的衣物数量SELECT category, COUNT(*) AS total FROM clothing GROUP BY category;统计任务完成率SELECT COUNT(*) AS total_task, SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) AS finished_task FROM care_task;前端展示可以用 ECharts 的饼图和柱状图这个库接入简单、效果也不错。我建议后端把统计结果封装成一个StatisticVO一次性返回多个指标避免前端频繁调接口。比如public StatisticVO getStatistics() { StatisticVO vo new StatisticVO(); vo.setClothingTotal(clothingMapper.selectCount(null)); vo.setPendingTask(careTaskMapper.selectPendingCount()); vo.setFinishedTask(careTaskMapper.selectFinishedCount()); vo.setCategoryStats(clothingMapper.selectCategoryStats()); return vo; }有同学会问统计报表是不是要实时计算其实不需要。这个项目的实时计算代价很低因为数据量小聚合查询基本都是毫秒级没必要引入缓存和中间件。但如果以后衣物表数据量上来了可以把统计结果定时计算好存到一张统计表再用定时任务每天刷新。5. 实操过程与部署落地5.1 从零初始化一个Spring Boot项目项目初始化我推荐用 IDEA 自带的 Spring Initializr也可以用官网的在线生成工具。选择项目构建工具 Maven 或 Gradle我个人更建议 Maven因为毕业设计用的人最多遇到问题容易找到解决方案。依赖选择上核心的几个必须勾上Spring Web。MyBatis Framework或Spring Data JPA。MySQL Driver。Lombok减少实体类 getter/setter 代码。Validation做参数校验。如果是前后端分离还需要额外引入Spring Security或自己写 JWT 拦截器。这里提醒一下Spring Security 学习曲线略陡很多同学配了半天发现登录一直被拦截。如果时间不够可以自己写一个简单的 JWT 过滤器核心逻辑就是校验请求头里的 Token 是否存在且合法最多几十行代码完全够用。application.yml里最关键的是数据源配置。本地开发可以直接spring: datasource: url: jdbc:mysql://localhost:3306/clothes_care?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 5MB max-request-size: 10MB mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true如果使用 MyBatis-Plusmap-underscore-to-camel-case这个配置很重要它能让数据库字段next_care_time自动映射到实体的nextCareTime省掉大量手写resultMap的麻烦。5.2 核心的Service层代码怎么写更稳Service 层是业务逻辑最集中的地方最容易出现的问题就是代码越长越乱最后所有方法堆在一起。我的习惯是每个核心业务定义一个 Service 接口和一个实现类接口只暴露业务方法实现类里再把公共逻辑抽成私有方法。比如说衣物保存不能只写一句 insert。完整流程应该是参数校验。判断是新增还是更新。如果新增设置创建时间和初始状态。根据care_cycle_type和care_cycle_value计算出next_care_time。执行插入或更新。这里的计算逻辑我习惯单独抽一个方法private LocalDateTime calculateNextCareTime(LocalDateTime lastCareTime, Integer type, Integer value) { if (type 1) { return lastCareTime.plusDays(value); } else if (type 2) { return lastCareTime.plusMonths(value); } return lastCareTime.plusDays(30); }这么做的原因是后期如果保养周期规则变了只需要改这一个方法其他地方不用动。同时Service 层所有对外方法都要抛统一的业务异常错误信息用中文直接描述例如“该衣物不存在”“当前状态不允许执行该操作”。不要直接把底层异常抛给前端否则页面会看到一堆看不懂的英文堆栈。5.3 打包部署从本地到服务器本地跑通之后打包部署也是一个环节。在项目根目录执行mvn clean package -DskipTests打包完成后target目录下会生成一个clothes-care-0.0.1-SNAPSHOT.jar文件。开发环境可以这样启动java -jar target/clothes-care-0.0.1-SNAPSHOT.jar如果放到服务器上建议把上传目录也放到非用户目录比如/data/clothes/upload并用application-prod.yml隔离线上配置。再写一个启动脚本包含启动、停止、重启功能会显得专业很多。演示的时候最稳的方式是提前在答辩电脑上装好 JDK 和 MySQL把项目打包成 jar 包启动所有数据提前录好。不过要记住服务器上的 MySQL 字符集一定要设置为utf8mb4否则用户注册中文昵称时会出现乱码。6. 常见问题与避坑实录6.1 周期计算总是差一天这个我见过太多次了。很多同学计算下次保养时间喜欢用时间戳相减或者用Date进行计算结果把时区问题带进来用户在前天提交的保养记录系统算出来的下次保养时间差了整整一天。解决办法是全部使用LocalDate或LocalDateTime不要用java.util.Date做加减法。并且要保证数据库连接的serverTimezoneAsia/Shanghai。另外算天数差的时候注意“当天”的定义比如今天是保养日的当天到底算不算已过期要在代码里定好。我建议用“超过”而不是“大于等于”也就是当前日期晚于next_care_time时才触发提醒这样用户体验比较好。6.2 定时任务出现重复数据前面提到过重复数据的问题这里再展开讲讲。定时任务本身不是只跑一次它会按周期重复执行如果上一次还没执行完下一次又开始跑了就可能重复。虽然 Spring Boot 默认定时任务是单线程串行执行但假如你把Scheduled放在多个方法上或者把方法执行时间拉得很长还是有机会出现并发问题。对策有三个层次第一层在生成任务前查一下是否已经存在待处理任务。第二层给care_task表加上clothing_id status的唯一索引从数据库层面防止重复。第三层如果后面部署了多实例就需要引入 Redis 分布式锁。学生项目做到前两层就够了。6.3 图片上传后访问404上传成功后图片路径如果访问不到大概率是静态资源映射没配置。Spring Boot 默认只把classpath:/static/作为静态资源目录你保存到本机/data/upload/的图片不会自动被访问到。需要在配置类里做映射Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadDir); }注意uploadDir末尾要带/不然拼接路径时会出问题。我在本地调试时也踩过一次最后发现是 Windows 路径分隔符和 Linux 不一致导致的后来统一用Paths.get(uploadDir).toAbsolutePath()来生成路径兼容性就好很多。6.4 前后端联调时的数据格式冲突前后端分离模式下最容易出现两个问题跨域和日期格式。跨域解决方案很简单写一个配置类实现WebMvcConfigurer放行所有来源即可用于开发环境registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true);日期格式问题也很典型。后端把LocalDateTime序列化之后前端收到的可能是2025-05-12T10:30:00带个T页面显示不好看。两种处理办法一种是前端解析的时候替换掉T另一种是后端在application.yml里统一配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai推荐第二种一劳永逸。写在最后的一些体会这个项目从题目看似乎不复杂但真把它做完整涉及到的东西并不少角色权限、衣物建模、保养周期计算、定时任务、状态流转、统计报表、文件上传、前后端联调、打包部署每一块都能单独拿出来给面试官讲十分钟。我个人的体会是做这类管理系统前期花在数据库设计上的时间绝对值得表结构理清了后面的 Service 层代码会写得非常顺反而是急着写代码的同学后面改表结构改到崩溃。最后再分享一个小技巧演示的时候不要只演示正常流程可以提前准备一件“已经超过保养时间”的衣物数据登录后首页立刻出现提醒然后你现场点一下“确认保养”再去看统计页面数字也会同步变化。这套连招走下来整个业务闭环特别完整比单纯展示列表和表单有说服力得多。希望这篇文章能帮到正在设计和实现这个系统的你也祝答辩顺利。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询