每年到了毕业设计选题季,管理系统类题目永远是最大的“蓄水池”。“基于Spring Boot技术的衣物保养管理系统”这个题目刚到我手上时,我的第一反应和多数同学一样:这不就是给衣服做个增删改查吗?等到把需求细化完,才发现这个题目远不是“洗衣店管理系统”那么简单。它要处理的不只是“一件衣服在不在”,而是“这件衣服什么时候该保养了”,背后涉及周期计算、提醒机制、预约流转和保养记录回溯。
这篇内容会把我从选题、建模、编码到部署和答辩准备的完整过程拆开讲,适合还在纠结管理系统类毕设怎么做出差异化、或者已经选了衣物保养相关题目的同学参考。整个项目采用Spring Boot搭建后端接口,用前后端分离的方式实现了衣物档案、保养计划、保养记录、提醒服务和预约管理,运行稳定,演示效果好。
1. 为什么我用“衣物保养”而不是“洗衣管理”当毕业设计题目
1.1 管理系统类选题最大陷阱:一眼被看穿的纯CRUD
很多同学选管理系统,都是冲着“好写”去的,图书管理、宿舍管理、仓库管理,代码写完确实能跑,但答辩时很难讲出东西来。因为这些系统的核心动作就是“增删改查”,业务规则几乎为零,评委老师扫一眼页面就知道你是套模板。我一开始也是这么想的,直到真坐下来列功能,才发现衣物保养管理系统和普通管理系统的思考逻辑完全不一样。
普通衣物库存管理关心的是“库存还剩多少”,核心表是出入库流水;衣物保养管理系统关心的是“上次保养是什么时候,下次该什么时候”,核心对象是衣物档案、保养计划、保养记录。同样是穿在身上的东西,前者的故事讲不起来,后者天然有一条“时间线”。毕设答辩最怕的就是被问“你这个项目的难点在哪”,衣物保养这个题目自带难点:周期计算、提醒去重、状态流转,都不用硬凑。
1.2 “衣物保养”到底覆盖哪些场景
不要把保养理解成洗衣服。按我当时的拆解,衣物保养至少包括五个场景:
- 基础档案维护:品牌、材质、颜色、购买时间、穿着频率、存放位置;
- 周期保养规划:不同材质对应不同周期,比如羊毛大衣半年一次干洗,真丝衬衫建议低温水洗;
- 换季收纳提醒:羽绒服收纳前要清洗晾晒,毛衣入柜前要防虫;
- 服务预约流转:用户发起保养预约,护理师接单、处理、回填记录;
- 保养数据统计:每月保养花费、每件衣物累计保养次数、高频保养品类。
这五个场景直接决定了系统边界。我当时还想过要不要加在线支付、物流跟踪,后来全砍了。毕设不是商业项目,最重要是“闭环”:从登记衣物,到触发提醒,到完成一次保养,再到重新计算下一个提醒日期,整条链路跑通就足够有说服力。
1.3 功能边界的取舍标准
做毕业设计,功能不是越多越好,而是越“自洽”越好。我给自己定了三个原则:核心链路必须完整,非核心功能从简,展示型功能可以适当做。于是最终功能包固定为:衣物档案、保养计划、保养记录、提醒消息、预约管理、系统管理。
| 维度 | 必做 | 不做 |
|---|---|---|
| 衣物侧 | 档案、材质、照片 | 智能衣柜、硬件联动 |
| 保养侧 | 周期计划、记录、提醒 | 支付、物流、售后评价 |
| 预约侧 | 发起、接单、完成 | 在线打印小票 |
| 管理侧 | 用户、字典、简单统计 | 复杂报表引擎 |
判断标准只有一个:这个功能能不能让“保养”这条主线更完整。不能,就删。
2. 拆解系统模块:从用户、管理员到护理师的三类角色流程
2.1 角色设计不用复杂,三类就够
我把系统角色收敛成三类:普通用户、护理师、管理员。普通用户注册登录后管理自己的衣物和保养计划;护理师处理用户提交的保养预约,并填写保养记录;管理员维护基础数据、字典参数和用户状态,查看整体统计。
权限控制没有引入整套安全框架来增加负担,而是用了一个很实用的方案:后端拦截器校验请求头里的Token,解析出当前用户的角色标识,然后在需要权限的接口上做角色校验。Spring Boot写起来不复杂,三十多行代码就能搞定,答辩时还能顺带讲清楚“为什么不用每个接口都查库验权限”这个问题:Token里存了用户ID和角色,查一次Redis或数据库即可,后续请求直接用。
2.2 功能模块清单与对应角色
| 模块 | 主要功能 | 使用角色 |
|---|---|---|
| 衣物档案 | 新增、编辑、删除、列表、详情 | 用户、管理员 |
| 保养计划 | 按衣物配置保养类型、周期、提醒提前天数 | 用户 |
| 保养记录 | 登记保养方式、日期、费用、备注 | 护理师、用户 |
| 预约管理 | 发起预约、接单、处理中、完成、取消 | 用户、护理师 |
| 提醒消息 | 到期提醒生成、已读标记、列表 | 用户 |
| 系统管理 | 用户管理、字典管理、操作日志 | 管理员 |
这个清单最大的好处是“没有废模块”。护理师和管理员各管一段,普通用户是核心角色,三者在一单保养预约上汇合,演示的时候可以切换角色把整条链路走完。
2.3 主业务流程:一单保养是怎么跑通的
系统里最重要的一条流程,我建议所有做同类题目的人都记住:
- 用户注册登录,进入“我的衣柜”新增一件衣物,填写材质、颜色、存放位置。
- 系统根据衣物材质生成默认保养计划,用户可调整周期,比如“羊毛大衣,180天干洗一次,提前7天提醒”。
- 定时任务每天扫描一次,发现满足提醒条件的计划,生成站内消息。
- 用户看到提醒,发起保养预约,选择保养类型和期望日期。
- 护理师在后台接单,把状态改成“处理中”,完成时填入实际保养内容和费用。
- 系统读取这条保养记录,更新衣物对应计划的最近保养日期,重新计算下一次提醒日期。
- 管理员在统计页面看到该衣物的保养次数和累计费用。
这段流程就是整个项目的骨架。编码时所有功能都围绕它展开,答辩讲项目时也只要讲完这一条线,评委基本就认可了。
3. 数据模型设计:把“保养”翻译成数据库表
3.1 为什么必须先设计模型再写代码
我一开始图省事,边写Controller边建表,结果因为少考虑一个“一件衣服可以有多条保养规则”,返工改了三张表。后来老老实实把实体关系画清楚,后面写代码的速度反而快了很多。毕设阶段最怕的就是改表:实体类要变、Mapper要变、前端字典要变,牵一发动全身。
衣物保养系统的核心关系是:一个用户拥有多件衣物,一件衣物对应多条保养计划,一条保养记录变更某条计划的下次保养日期。这个关系虽然简单,但必须在一开始就定死,否则后面定时提醒的查询逻辑会非常别扭。
3.2 核心表结构一览
| 表名 | 说明 | 核心关系 |
|---|---|---|
| user | 用户表 | 一个用户多件衣物 |
| garment | 衣物档案表 | 用户一对多 |
| care_plan | 保养计划表 | 衣物一对多 |
| care_record | 保养记录表 | 衣物多对一,计划可空 |
| appointment | 预约单表 | 关联用户、衣物、护理师 |
| notice | 提醒消息表 | 关联用户、衣物、计划 |
| dict_item | 字典表 | 材质、保养方式等基础字典 |
3.3 衣物档案表:别漏了材质和穿着频率
衣物档案表是整个系统的主表。这是我当时用的建表脚本:
CREATE TABLE `garment` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` BIGINT NOT NULL COMMENT '所属用户', `name` VARCHAR(64) NOT NULL COMMENT '衣物名称', `category` VARCHAR(32) NOT NULL COMMENT '衣物分类:外套/衬衫/裤装/裙装/配饰', `material` VARCHAR(32) NOT NULL COMMENT '材质:羊毛/真丝/棉/化纤/混纺', `color` VARCHAR(32) DEFAULT NULL COMMENT '颜色', `seasons` VARCHAR(64) DEFAULT NULL COMMENT '穿着季节,多个用逗号分隔', `location` VARCHAR(128) DEFAULT NULL COMMENT '存放位置', `image_url` VARCHAR(255) DEFAULT NULL COMMENT '照片相对路径', `care_level` TINYINT DEFAULT 3 COMMENT '保养等级1-5,等级越高越娇贵', `status` TINYINT DEFAULT 1 COMMENT '状态:1在柜,0遗失/淘汰', `remark` VARCHAR(255) DEFAULT NULL COMMENT '备注', `create_time` DATETIME NOT NULL COMMENT '创建时间', `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_material` (`material`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='衣物档案表';这里有两个字段容易被忽略:seasons和care_level。seasons是为换季提醒准备的,虽然我后来没有单独做“换季批量提醒”,但留这个字段可以在答辩时说“后续可以基于它扩展”。care_level则直接影响保养计划是否允许某些操作,比如等级为5的衣物不允许普通水洗,页面端会根据这个字段做交互拦截,演示时非常直观。
3.4 保养计划表:周期字段才是业务灵魂
CREATE TABLE `care_plan` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `garment_id` BIGINT NOT NULL COMMENT '衣物ID', `care_type` VARCHAR(32) NOT NULL COMMENT '保养方式:干洗/水洗/熨烫/消毒/修补', `cycle_days` INT NOT NULL COMMENT '保养周期天数', `remind_days_before` INT DEFAULT 7 COMMENT '提前提醒天数', `last_care_date` DATE DEFAULT NULL COMMENT '最近一次保养日期', `next_care_date` DATE DEFAULT NULL COMMENT '下一次保养日期', `enabled` TINYINT DEFAULT 1 COMMENT '是否启用', `create_time` DATETIME NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_garment_id` (`garment_id`), KEY `idx_next_care_date` (`next_care_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='保养计划表';设计时我纠结过一个问题:要不要单独建保养计划表,还是直接在衣物表里存“下次保养日期”。答案是必须单独建。因为一件真丝衬衫可能同时有“水洗周期7天”和“熨烫周期60天”两条规则,如果只在衣物表里存一个日期,就只能表达一种保养方式,整个系统的业务就残废了。
另外,next_care_date看起来可以由last_care_date + cycle_days推导出来,但我还是选择冗余存储。原因是定时任务每天要扫描“哪些计划即将到期”,如果全靠实时计算,每次都要先查所有计划再逐条算日期,性能和SQL复杂度都不划算。冗余字段只要保证在写入保养记录的事务里同步更新,就不会出问题。
3.5 保养记录表和预约单表
保养记录表记录每一次实际发生的保养行为。字段包括 id、garment_id、plan_id(可空)、care_type、care_date、cost、description、operator_id、create_time。这里plan_id允许为空,因为存在“用户临时送一次修补,没有对应周期计划”的情况。若plan_id非空,新增记录后就要回写care_plan的last_care_date和next_care_date。
预约单表则用来承载主流程流转:id、user_id、garment_id、plan_id、appointment_date、service_type、status、operator_id、description、create_time。状态机是“待接单 -> 处理中 -> 已完成”,外加“已取消”。用字符串存状态,配合前端下拉框展示,不需要复杂状态表,毕设完全够用。
3.6 图片和字典:两个容易埋雷的小细节
衣物照片我存的是相对路径,不是base64,也不是绝对路径。图片文件通过一个上传接口保存到本地的upload/images目录,数据库只存/images/xxx.jpg。这样打包部署到别的服务器时,只要目录结构不变,图片就不会裂。最初我偷懒存过绝对路径,换机器跑直接打不开,后来才改的。
字典表单独建了一张dict_item,结构是item_type、item_code、item_name、sort_no、status。比如item_type='material'、item_code='WOOL'、item_name='羊毛'。前端下拉框的数据全部从字典接口拿,页面写死的话,以后想加一个“羊绒”材质就得改前端代码重新打包,非常麻烦。
4. Spring Boot工程落地的分层结构与核心接口
4.1 工程目录怎么摆,直接决定后面效率
我建议不要所有类都堆在几个包下面,按职责分清楚,代码量大了以后好找文件。我当时的目录结构大致是这样:
com.demo.clothingcare ├── common │ ├── result // 统一返回结构 R、ResultCode │ └── exception // 全局异常处理 ├── config // 跨域配置、MyBatisPlus配置、定时任务开关 ├── controller // 接口层 ├── service │ └── impl // 业务实现 ├── mapper // MyBatisPlus的Mapper接口 ├── entity // 数据库实体 ├── dto // 请求/响应对象 └── task // 定时任务分层原则很简单:Controller只做参数接收和结果返回,Service只写业务规则,Mapper只碰数据库。这样做的好处是答辩被问“这个查询条件在哪改”的时候,你能三秒钟定位到对应Service方法,而不是在一坨代码里翻。
4.2 依赖选型与版本怎么定
我用的组合是:Spring Boot 2.7.x + Java 8 + MyBatis-Plus 3.5.x + MySQL 8 + Hutool工具库 + Spring Boot Validation。前后端分离的话,再加Vue 3和Element Plus。
选这套没有特别高深的理由:Spring Boot生态成熟,网上问题多容易查;MyBatis-Plus让单表CRUD几乎不用手写SQL,可以把时间省给核心业务;Hutool处理日期格式、对象拷贝非常顺手。Java 8虽然老,但兼容性最稳,不管评委老师用的什么环境都容易复现。如果你对版本有更高追求,Java 17加Spring Boot 3也可以,不过查阅资料时要留意版本差异。
4.3 统一返回结构:整个项目的“公共语言”
管理系统的接口风格要统一,前端才好写。我定义了一个通用的R返回类,所有接口都返回同一套结构:
public class R<T> { private Integer code; private String message; private T data; public static <T> R<T> ok(T data) { R<T> r = new R<>(); r.setCode(200); r.setMessage("success"); r.setData(data); return r; } public static <T> R<T> fail(String message) { R<T> r = new R<>(); r.setCode(500); r.setMessage(message); return r; } }配合@RestControllerAdvice做全局异常处理,Service里抛业务异常时,前端拿到的也是这个结构。这样前端axios拦截器统一判断code是否为200,其余情况弹错误提示,联调阶段能省大量时间。
4.4 核心接口清单:覆盖整个保养闭环
| 方法 | 路径 | 作用 | 权限 |
|---|---|---|---|
| POST | /api/auth/register | 用户注册 | 公开 |
| POST | /api/auth/login | 登录 | 公开 |
| POST | /api/garment/add | 新增衣物 | 登录用户 |
| GET | /api/garment/list | 我的衣物列表 | 登录用户 |
| POST | /api/care/plan/save | 保存保养计划 | 登录用户 |
| GET | /api/care/plan/list | 查询衣物的保养计划 | 登录用户 |
| POST | /api/care/record/add | 新增保养记录 | 护理师/用户 |
| GET | /api/care/record/list | 衣物保养历史 | 登录用户 |
| POST | /api/appointment/create | 提交保养预约 | 登录用户 |
| POST | /api/appointment/handle | 护理师接单/完成 | 护理师 |
| GET | /api/notice/list | 我的提醒列表 | 登录用户 |
4.5 Service层最关键的方法:新增保养记录
新增保养记录是整个项目业务量最集中的地方,因为它同时做两件事:插入记录,然后更新对应计划的最近日期和下次日期。这两步必须放在一个事务里,否则会出现“保养记录写了,下次提醒没更新”的数据不一致。
@Service public class CareRecordServiceImpl implements CareRecordService { @Autowired private CareRecordMapper careRecordMapper; @Autowired private CarePlanMapper carePlanMapper; @Override @Transactional(rollbackFor = Exception.class) public void addRecord(CareRecordAddDTO dto) { CareRecord record = new CareRecord(); BeanUtil.copyProperties(dto, record); careRecordMapper.insert(record); if (dto.getPlanId() != null) { CarePlan plan = carePlanMapper.selectById(dto.getPlanId()); if (plan != null && plan.getEnabled() == 1) { LocalDate careDate = dto.getCareDate(); plan.setLastCareDate(careDate); plan.setNextCareDate(careDate.plusDays(plan.getCycleDays())); carePlanMapper.updateById(plan); } } } }这段代码就是整个周期提醒机制的数据基础。每次保养完成,系统马上算出下一次保养日期,定时任务只需要对这个日期做扫描,逻辑清晰,答辩时也容易讲。
5. 保养周期计算与提醒机制:最容易翻车的部分
5.1 为什么这是整个项目的核心难点
普通管理系统的数据不会“过期”,衣物保养系统最怕的就是数据“过期”了系统还不知道。如果只是把衣物档案和保养记录做成两张表,那这个系统跟一个普通的台账没有任何区别,真正让它“智能化”的就是周期提醒。这也是答辩时最值得展开讲的部分。
5.2 周期计算公式:所有逻辑都围绕它
我用的公式只有两行:
- 首次设置计划时:
next_care_date = 创建日期 + cycle_days - 完成一次保养记录后:
next_care_date = 本次保养日期 + cycle_days
提醒日期 =next_care_date - remind_days_before。
举个例子:一件羊毛大衣的干洗计划是cycle_days=180,remind_days_before=7。如果用户在6月1日完成了一次干洗,那么下次保养日期就是大约11月27日,系统会在11月20日开始提醒。如果用户拖到12月10日才补录这次保养,那么下次保养日期就从12月10日开始加180天,不会因为补录而出现“今天刚录完又提醒”的尴尬情况。
5.3 定时任务设计:每天扫一次,不要实时扫
我用Spring自带的任务调度@Scheduled实现每日扫描:
@Component public class CareRemindTask { @Autowired private CarePlanMapper carePlanMapper; @Autowired private NoticeMapper noticeMapper; @Scheduled(cron = "0 10 0 * * ?") public void scanCarePlanRemind() { LocalDate today = LocalDate.now(); List<CarePlan> plans = carePlanMapper.selectList( new LambdaQueryWrapper<CarePlan>() .eq(CarePlan::getEnabled, 1) .ge(CarePlan::getNextCareDate, today) .le(CarePlan::getNextCareDate, today.plusDays(7)) ); for (CarePlan plan : plans) { LocalDate remindDate = plan.getNextCareDate() .minusDays(plan.getRemindDaysBefore()); if (remindDate.isEqual(today) || (plan.getNextCareDate().isAfter(today) && plan.getNextCareDate().minusDays(7).isEqual(today))) { generateNotice(plan, today); } } } }扫描的范围是:next_care_date在未来7天内到期的计划,每天只跑一次。不要做成每小时扫一次,系统真实场景用不到,定时任务频率太高反而会让老师觉得你没有考虑成本和必要性。
5.4 重复提醒问题:必须保证同一天只发一次
这个坑很隐蔽。如果定时任务因为部署环境原因重复执行了一次,用户手机上可能连续收到两条一模一样的提醒,演示的时候会非常尴尬。我的办法是给notice表加一个唯一约束:(plan_id, notice_date)。插入前先查是否已存在,存在就跳过。
另外,提醒还涉及“提前7天提醒一次,还是提前7天内每天都提醒”。业务上我更建议“提前第7天提醒一次,到期当天再提醒一次”,而不是每天刷屏。实现方式就是上面代码里的日期判断:只有remindDate等于今天,或者next_care_date等于今天时才生成消息。这个规则要在答辩时说清楚,因为它决定了用户看到消息的频次。
5.5 时间处理常见的三个坑
- 日期用
LocalDate,时间用LocalDateTime,两者不要混用。数据库字段类型也要对应,不要用timestamp存“哪天该保养”,用date就够了。 - 数据库连接URL一定要配
serverTimezone=Asia/Shanghai,否则服务器时区不是东八区时,定时任务扫出来的结果会偏移。 - 定时任务方法里不要直接用
new Date()和数据库的date列比较,统一转成LocalDate后再查,避免类型转换问题。
这些坑我几乎都踩过一遍,尤其是时区问题,第一次部署到云服务器上时提醒时间差了整整一天,排查了好久才发现是连接参数没补。
6. 前端页面与联调:Thymeleaf还是Vue?
6.1 选型逻辑:我选了Vue,但不建议无脑选
做管理类毕设,前端无非两条路:服务端渲染Thymeleaf,或者前后端分离的Vue + Element Plus。我最终选了Vue 3 + Element Plus,原因很实际:组件现成、页面美观、调接口方便。对毕设来说,一个衣服卡片式展示页面远比一堆表格有视觉效果。
但前后端分离的代价是联调工作量更大。如果你对接口理解得不够熟,前端调不通就会非常痛苦。如果时间只剩两周,Thymeleaf加一个现成Admin模板反而更稳。这个选择没有标准答案,关键看你对接口调试的熟练程度。
6.2 页面规划:五个主页面覆盖整条链路
- 登录/注册页:登录后把Token存到localStorage;
- 我的衣柜:卡片式展示衣物,点击进入详情;
- 衣物详情页:Tab切换“保养计划”和“保养记录”;
- 预约管理页:普通用户发起预约,护理师角色显示可接单列表;
- 提醒消息页:顶部醒目显示“距离下次保养还剩几天”。
页面数量不算多,但每页都必须对应后端接口,这样才能讲清楚“前端页面不是静态的,而是数据驱动”。
6.3 联调时必须处理的三个坑
第一个:Long类型精度丢失。如果后端主键用了雪花算法生成的18位数字,前端JavaScript的Number类型存不下,会出现最后几位变成0的情况。虽然我为了省事用了自增ID,但如果你打算做雪花ID,一定要在后端把Long类型的ID序列化成字符串返回,前端才能安全使用。
第二个:日期格式统一。Spring Boot默认序列化LocalDate时可能输出数组,前端拿到就没法直接展示。我在配置文件里加了一段:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss再配合@JsonFormat(pattern = "yyyy-MM-dd")标注日期字段,保证前后端格式一致。
第三个:跨域。前端开发服务器跑在8080端口,后端跑在8081端口,浏览器会拦截跨域请求。我加了一个CorsFilter配置类,允许所有来源和常见请求头,开发环境调试非常方便。生产环境用Nginx反向代理后,跨域问题就不存在了,但开发阶段没有这个配置寸步难行。
6.4 前端调用接口的核心写法
我的前端代码里,axios实例统一设置了baseURL和请求头拦截器:
const service = axios.create({ baseURL: '/api', timeout: 15000 }); service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = token; } return config; });这样每个页面调接口都不需要重复写Token逻辑。后端拦截器从Authorization头取出用户信息,放入ThreadLocal,Service层就能直接拿到当前操作人。这套写法简单、实用,答辩提“无状态认证”时也说得通。
7. 部署与答辩准备:把项目“讲”成高分毕设
7.1 打包部署:一条命令跑起来
后端打包非常直接。在项目根目录执行:
mvn clean package -DskipTests得到target/garment-care.jar后,上传到服务器,安装好JDK和MySQL,执行:
java -jar garment-care.jar前端在服务器上跑npm run build,把dist目录放到Nginx的静态目录,反向代理/api到后端端口,整个系统就上线了。毕设阶段不需要上云原生那套复杂架构,但可以在答辩时提一句“后续可以容器化部署”,这是加分项,不是必备项。
7.2 演示数据一定要提前造好
这是我最想强调的一点。答辩现场临时录数据,一旦出状况就是大型翻车。我准备了一组固定的演示账号和场景:
| 数据 | 状态 | 用途 |
|---|---|---|
| 用户账号 | demo/123456 | 演示登录和衣柜 |
| 管理员账号 | admin/123456 | 演示系统管理 |
| 羊毛大衣 | 最近保养日期设置为5天后到期 | 演示提醒生成 |
| 真丝衬衫 | 无保养记录 | 演示首次设置计划 |
| 牛仔裤 | 已过期30天 | 演示补录保养记录后重算周期 |
演示路径是:登录用户,打开提醒消息,看到羊毛大衣即将到期,发起干洗预约,切到护理师角色接单并完成,再回用户端确认下一次保养日期已重新计算。这条路径从头到尾不到三分钟,但把系统所有核心功能都覆盖了。
7.3 答辩高频问题与应对思路
| 高频问题 | 核心答法 |
|---|---|
| 为什么选Spring Boot | 自动配置减少开发成本,内嵌Tomcat方便部署,生态成熟;相比传统SSM,省去大量XML配置 |
| 保养周期是怎么算的 | 讲公式:完成保养记录后,最近保养日期加周期天数得到下一次日期 |
| 定时任务会不会影响性能 | 扫描条件走next_care_date索引,并且只查启用状态的数据,任务执行在凌晨,对业务无影响 |
| 密码是怎么存的 | 使用BCrypt加密算法,不建议用MD5,因为MD5无盐值容易被彩虹表碰撞 |
| 项目最大的难点是什么 | 状态与时间的联动。普通CRUD不涉及时间流逝,这里每次保养记录都会改变未来提醒日期,必须用事务保证一致性 |
这几个问题只要提前准备好,现场基本不会卡壳。怕的就是只会背项目介绍,被问到具体算法时说不清楚。
7.4 我踩过的坑,希望你别再踩
第一个坑是字段命名。建表时我一度把备注字段命名为describe,结果在XML里写SQL时报语法错,白查了一个小时。命名时尽量避开保留字,用remark、description这类安全词。
第二个坑是时区。代码里明明显示中文日期,部署到服务器上提醒时间却不一致。后来在启动命令里加了-Duser.timezone=Asia/Shanghai,数据库连接参数也补上serverTimezone=Asia/Shanghai,问题才彻底解决。
第三个坑是现场演示时网络波动导致图片加载不出来。后来我把所有演示图片提前上传到本地,数据库里存的也都是相对路径,就算断网,页面核心功能也不受影响。
最后说点我的真实感受。整个项目做下来,我最深的体会是:毕业设计管理系统题目的竞争力,从来不在于表建得多复杂,而在于你愿不愿意把业务里最不起眼的那句规则想清楚。衣物保养这个题真正值钱的地方,是“上次干洗距离现在已经130天”这行字背后的周期计算和提醒逻辑。如果你的题目也是类似的系统,哪怕不是衣物保养,也建议先问自己一句:这个系统里最核心的业务规则是什么?把它想透,代码反而成了最简单的事。