先落一句话:Spring Boot 做企业合同管理系统,这个题在计算机毕业设计里属于典型的"看起来谁都能做、其实想拿高分并不容易"的类型。你要是只是把增删改查摆上去,那它就是一个管理系统;但如果你把合同从起草、审批、签署、履约到归档这条完整链路理清楚,再配合权限、提醒、统计这些细节,它就是一套能讲出业务逻辑的信息化系统。这恰恰是答辩时老师最想听到的东西。
这篇文章我就围绕这个题目,把我自己带毕设和参与实际合同系统开发时积累的思路、踩过的坑、以及可以直接抄的写法全部整理出来。不管你底子是刚学完 SSM 还是已经能独立写 Vue,按着这个框架走,至少能保证你的系统"业务完整、技术够用、答辩有话说"。
1. 项目定位与需求拆解:别一上来就写代码
很多同学拿到题目第一反应是建表、写接口、套页面,但我建议先停一下。合同管理系统和其他管理系统最大的区别在于,它的核心不是"管理"两个字,而是"合同"本身的业务属性。合同有生命周期,有法律效力,有审批流程,有付款节点,有附件归档。这些东西如果不先想明白,写出来的系统充其量是个"合同信息登记表"。
1.1 为什么企业合同管理系统适合做毕业设计
我见过不少毕设题目,什么"校园二手交易平台""在线考试系统",本质上都是单表 CRUD。合同管理系统不同,它天然带有业务流程,这个业务流给了你很大的发挥空间:
- 数据模型有层次:合同主表、客户信息、合同明细、审批记录、附件、变更记录,这本身就是多表关联。
- 有状态流转:从草稿到审批中,再到已生效、履行中、已归档,每一步都有操作者、时间、意见。
- 有权限需求:普通业务员只能看自己的合同,法务能审批,财务能看金额和付款节点,管理员能看全部。
- 有实用场景:合同到期提醒、付款计划提醒、统计报表,这些都是企业里真实会用的功能。
就凭这四点,这个题目的"业务复杂度"已经能支撑起一篇像样的论文,也能在答辩时把"系统设计"讲出内容来。你得选一个复杂度适中、能自圆其说的方案。我见过不少同学堆了一堆高大上的技术,比如工作流引擎、规则引擎、微服务,结果演示的时候自己都讲不清楚流程怎么走的,这种反而容易翻车。
1.2 核心业务模型:合同全生命周期
合同管理系统解决的核心问题,是把线下纸质合同的管理方式搬到线上。那首先就得梳理"合同从哪来、到哪去"。
我在做系统设计时,通常会把它拆成七个阶段:
- 合同起草:业务人员根据业务需求,创建合同,填写甲方乙方、合同金额、有效期、付款方式。
- 合同审批:提交后进入审批环节,通常是业务主管、法务、分管领导逐级审批。
- 合同签署:审批通过后,双方签字盖章,系统记录签署状态和签署时间。
- 履行跟踪:合同生效后的执行情况,包括付款计划、交付节点、验收情况。
- 变更管理:合同执行过程中可能出现金额调整、期限变更,需要留痕。
- 到期提醒:合同到期前需要续签或者终止,系统自动提醒相关人员。
- 归档管理:履行完毕或终止的合同进入归档状态,支持检索查看。
这里每一阶段都对应着数据库里的状态字段,也对应着前端页面的操作按钮。你不需要把每个阶段做得特别深,但至少把主干流程走通,剩下的是加分项。
1.3 用户角色与权限边界:三张表解决还是五张表解决
权限设计是这类系统里老师最爱问的点。方案有很多,最基础的是基于角色的访问控制(RBAC),涉及五张表:用户表、角色表、菜单权限表、用户角色关联表、角色菜单关联表。
我给你的建议是:五张表该建就建,不要让权限写死在代码里。这样做有两个好处:
- 答辩时你能讲清楚"不同角色登录后看到的内容不同,按钮权限是后端校验的"。
- 实际修改权限时,管理员在界面上勾选菜单就能完成,不用改代码重新上线。
操作权限的粒度要控制好。我见过有的系统把按钮权限都做到数据库里,每个按钮一个编码,配置起来极其痛苦。毕业设计做到"菜单权限 + 接口权限"就足够了:前端根据角色渲染菜单,后端接口用注解或拦截器校验角色。至于更细的数据权限(比如业务员只能看自己的合同),这个可以单独做一个维度,靠 MyBatis Plus 的拦截器拼 SQL 条件实现,属于亮点功能,可以写进论文但别在整个系统里都用它。
2. 技术选型:Spring Boot 为主线的合理搭配
技术选型这部分,我的核心建议是:用你熟悉的技术,但别整体意识流。毕设不是企业项目,不需要微服务,不需要分布式,不需要消息队列。Spring Boot 作为主线,再搭配几个真正有用的组件,完全够用。
2.1 后端:Spring Boot 2.x 还是 3.x
这是我自己带毕设时经常被问到的问题。我的回答是:除非你导师明确要求,否则用 Spring Boot 2.7.x。原因很现实:
- Spring Boot 3.x 基于 Spring Framework 6 和 Java 17,引入了 Jakarta EE 命名空间,很多教程和第三方组件的配置都变了。
- 目前网上绝大多数现成例子、毕业设计论文模板、视频教程都是基于 2.x 的,你照着做遇到问题更好查资料。
- 2.7.x 是 2.x 的最后一个维护版本,稳定性没问题,功能也够。
"springboot版本太高"这个体验我在不少群里见过——代码照着视频敲,结果依赖下载失败、注解不生效,最后发现是版本不匹配。所以我的原则很简单:能用稳定的就不追新。毕业设计追求的是把系统做完、把原理讲透,而不是展示你会用最新版本。
2.2 持久层框架、数据库与缓存选型
持久层我推荐 MyBatis Plus,理由只有两条:写 SQL 灵活,单表 CRUD 不用写 SQL。合同管理系统的查询大多数带条件:按合同编号、按客户名称、按状态、按日期范围,这种动态 SQL 用 MyBatis Plus 的 LambdaQueryWrapper 写起来非常清爽,而且代码量少,论文里也好贴代码。
数据库选 MySQL 8.0,这个是标配了。要注意的是数据库字符集统一用 utf8mb4,排序规则用 utf8mb4_general_ci。很多同学不设置字符集,插入合同条款里的特殊符号时直接报错或者存成乱码,这种问题排查起来非常浪费时间。
缓存方面,我建议用 Redis 做验证码缓存、合同详情缓存和数据字典缓存。注意,合同数据本身别轻易放缓存,因为涉及审批状态和金额,一旦脏读容易出大问题。缓存只是锦上添花,别为了用 Redis 而用 Redis。如果你的项目里用了 Redis,答辩时就能讲缓存穿透、缓存击穿、缓存雪崩这套理论,这是加分项。
2.3 前端方案:分离式 Vue 还是服务端模板
这是个做决策的关键点。如果你前后端都自己写,我推荐 Vue 3 + Element Plus + Axios 这种前后端分离方案,然后打包后把 dist 目录放进 Spring Boot 的 static 目录下,一个 jar 包直接启动。这个方案有几个切实的好处:
- 页面交互顺畅,做审批流、多标签、弹窗这些体验明显比 Thymeleaf 好。
- 前端代码结构清晰,论文和开题里能写"前后端分离架构"。
- 打包部署简单,不会出现"前端一个服务、后端一个服务"这种展示时手忙脚乱的情况。
把 Vue 打包放进 Spring Boot 的操作很简单:后端写好接口,Vue 里配置代理,开发时用 Vite 起 5173 端口联调,上线前执行 npm run build,生成 dist 目录,把里面的文件复制到 src/main/resources/static 下,重新打包 Spring Boot 工程即可。
如果你对 Vue 实在不熟,退而求其次用 Thymeleaf + Bootstrap 也是可以的,系统照样完整。答辩时老师更看重你把业务讲清楚,而不是页面花不花。
2.4 文件存储与定时任务方案
合同系统必然涉及附件:合同扫描件、PDF、Word 文档。文件存储我在毕设项目里推荐两种方案:
- 本地上传目录:在项目根目录建一个 upload 文件夹,通过配置映射成静态资源路径。好处是简单,拷贝项目就能跑。
- 云存储对象:比如阿里云 OSS。好处是演示时有逼格,但你需要注册、配置 Bucket,还要填密钥。注意这部分内容如果论文里写了,答辩时一定要能讲清楚,否则别写。
我强烈建议做合同到期提醒功能,这能让你的系统在功能上拉开差距。实现方案用 Spring Boot 自带的 @Scheduled 注解就够了:每天凌晨扫描合同表中状态为"已生效"且到期时间在七天内的记录,生成提醒消息写入提醒表,用户登录后在首页看到待办事项。这个功能既简单又实用,而且比你在论文里写一堆"通过 Redis 实现分布式锁"之类的东西靠谱得多。
3. 数据库设计与核心模块实现
数据库设计是整篇论文的逻辑骨架。很多同学一上来就用软件逆向生成表结构,字段含义自己都说不清楚,答辩一被问就卡壳。我建议你亲手建表,并且把每个字段的用途、类型、为什么这么设计想明白。
3.1 合同主表、流程记录表、附件表的字段设计
合同主表是整个系统的核心,字段至少要覆盖这些:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| contract_no | varchar(64) | 合同编号,唯一 |
| contract_name | varchar(128) | 合同名称 |
| party_a | varchar(128) | 甲方名称 |
| party_b | varchar(128) | 乙方名称 |
| contract_amount | decimal(18,2) | 合同金额 |
| start_date | date | 生效日期 |
| end_date | date | 到期日期 |
| status | int | 状态:1草稿、2审批中、3已生效、4已归档 |
| create_by | bigint | 创建人ID |
| create_time | datetime | 创建时间 |
| remark | varchar(500) | 备注 |
审批记录表用来保存每一步审批信息,这表是你能讲出"流程"两个字的底气所在:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| contract_id | bigint | 合同ID |
| approver_id | bigint | 审批人ID |
| approve_result | tinyint | 结果:1通过、2驳回 |
| approve_comment | varchar(500) | 审批意见 |
| approve_time | datetime | 审批时间 |
| step | int | 审批步骤序号 |
附件表比较常规,记录文件名、存储路径、上传人、上传时间即可。这里多说一句,存储路径千万别把项目绝对路径写进数据库,否则换电脑部署就全部失效。我在实际项目里吃过这个亏,最后统一改成"相对路径 + 配置项拼接"的方式,前端展示和文件下载都要通过后端接口去拼。
3.2 审批流程的实现思路:状态机模式
合同审批是流程类需求。毕业设计不建议上 Activiti 或 Flowable 工作流引擎,原因很简单:学习成本高、表结构复杂、演示时一旦流程定义配置不对就报错。更稳妥的做法是自己实现一个轻量状态机。
状态机的核心就三要素:当前状态、事件、目标状态。拿合同来说:
- 草稿状态,执行"提交审批"事件后变为"审批中"。
- 审批中状态,执行"审批通过"事件后变为"已生效"。
- 审批中状态,执行"审批驳回"事件后回到"草稿"。
- 已生效状态,执行"归档"事件后变为"已归档"。
代码上,我在 Service 层定义一个状态流转校验方法,每次更新状态前先判断当前状态是否允许执行该操作,不允许就直接抛业务异常。这个写法非常简单,但能有效防止客户端绕过前端乱传状态值。答辩时把"状态机"这三个字抛出来,老师一般会比较认可。
3.3 合同到期提醒与财务联动:定时任务怎么写
到期提醒的定时任务,我的实现思路是这样的:
@Component public class ContractRemindTask { @Scheduled(cron = "0 0 1 * * ?") public void remindExpiringContract() { // 1. 查询七天内到期的生效状态合同 // 2. 逐条生成提醒记录 // 3. 写入 remind 表 } }cron 表达式里 "0 0 1 * * ?" 表示每天凌晨一点执行。注意定时任务里不要直接调 Service 发送消息,正确做法是扫描数据、生成待办提醒,用户在登录后才看到。因为这种场景本身就不需要实时推送,定时任务足够了。
合同金额和付款节点我建议单建一张 payment_plan 表,字段包含合同ID、计划付款日期、付款金额、是否已付。合同生效后按计划生成付款记录,每次付款完成更新状态。这个模块做好之后,你可以在首页做一个统计看板,展示本月应付款项、合同总额、履行中合同数量等指标。这个功能在答辩演示时非常加分,因为它直接体现了"系统能辅助企业管理"的价值。
3.4 文件上传下载与在线预览的坑
文件上传下载这块,我给你列出几个我踩过的坑:
- 上传大小限制。Spring Boot 默认文件上传大小是 1MB,你需要把配置改大,比如 50MB。不加配置的话,传合同 PDF 超过 1MB 直接报错,而且报错的提示还很不直观。
- 下载文件名中文乱码。设置响应头时,需要先转成 URLEncoder,不然浏览器下载时文件名会变成一串百分号。这个细节我在实际项目里反复遇到过。
- 文件预览不要做复杂方案。在线预览 Office 文档确实有开源的方案,比如用 LibreOffice 转 PDF,但这对毕设来说太重了。我的建议是:合同附件以 PDF 为主,预览直接通过浏览器内置的 PDF 预览能力实现,把存储路径拼到接口返回的 URL 上,前端用 iframe 加载即可。
4. 核心代码思路与实操过程
这章我会把关键模块的实现顺序和核心代码思路过一遍,你可以直接照着搭。代码篇幅有限,我只贴最关键的部分,完整代码你自己补齐。
4.1 项目初始化与分层结构
用 IDEA 创建 Spring Boot 工程时,常见的方式是去 Spring Initializr 网站生成,或者直接在 IDEA 里新建。我的建议是到 start.spring.io 上生成,选好 Java 版本(Java 8 对应 Boot 2.7.x)、Group 和 Artifact,依赖勾选 Web、MySQL Driver、MyBatis Framework、Lombok 这几个就够。
项目分层我用的是 Controller、Service、Mapper、Entity 这套经典结构,外加 config、common、util 包。有同学喜欢用 controller、service、mapper 三层就完事,也行,但我会建议加一个 vo 包,用来放前端展示用的视图对象。因为数据库实体和前端展示字段经常不一致,硬拿 Entity 去接收前端参数,既不安全也不利于扩展。
4.2 合同创建与审批的 Service 层实现要点
合同创建的 Service 方法有几个细节需要处理:
- 生成合同编号。我的写法是"HT" + 年月日 + 四位随机数,同时用唯一索引兜底,插入时如果冲突就重新生成。
- 保存合同主表数据,同时保存附件列表。
- 创建审批记录,初始状态为草稿。
审批的代码逻辑可以这样写:
@Transactional public void approve(Long contractId, Integer result, String comment) { Contract contract = contractMapper.selectById(contractId); if (contract == null) { throw new BusinessException("合同不存在"); } // 状态机校验,状态不是审批中不允许审批操作 if (contract.getStatus() != ContractStatus.APPROVING) { throw new BusinessException("当前状态不允许审批"); } // 更新合同状态 if (result == ApproveResult.PASS) { contract.setStatus(ContractStatus.EFFECTIVE); } else { contract.setStatus(ContractStatus.DRAFT); } contractMapper.updateById(contract); // 写入审批记录 approveRecordMapper.insert(...); }注意 @Transactional 注解一定不能漏。审批涉及"更新合同状态 + 插入审批记录"两步操作,任何一步失败都要整体回滚,否则会出现审批记录写了但合同状态没变的脏数据。这是我反复强调的事务一致性问题。
4.3 权限控制:拦截器还是注解
Spring Boot 里实现登录权限控制有两种主流方案:HandlerInterceptor 和 Spring Security。作为毕设,我建议用拦截器加自定义注解,而不是引入 Spring Security。
原因很简单:Spring Security 的配置体系复杂,密码加密、用户认证、授权规则都是独立的知识体系,答辩时一旦被追问细节,基础不牢很容易卡住。你自己写拦截器,逻辑是透明的,被问到也能讲清楚。
实现思路:
- 登录成功后把用户 ID 和角色 ID 存进 Redis,key 为 token,设置过期时间。
- 前端每次请求在 Header 里带 token。
- 拦截器里校验 token,校验通过就把用户信息放入 ThreadLocal。
- 需要角色限制的接口上加 @RequireRole("ADMIN") 注解,拦截器里做角色校验。
这个方案代码量不大,但你能实实在在讲清楚 token 是什么、为什么要放 Redis、为什么用 ThreadLocal 传用户信息,这些在答辩时都是很好的问题点。
4.4 统计报表与导出
统计功能建议用一个独立的 DashboardController 来提供,主要接口包括:
- 合同总数、累计金额、履行中合同数、本月到期数
- 按项目来源统计的合同数量分布
- 按月份统计的合同签订数量趋势
图表展示用 ECharts,前端拉取统计数据后渲染折线图和柱状图。导出功能用 EasyPoi,简单用法是定义好实体类的 @Excel 注解,一行代码就能输出 Excel 文件。我见过用手写 Apache POI 逐行填充单元格的,代码又臭又长,完全没有必要。
5. 毕业设计避坑实录与答辩指南
最后这块,我根据自己带毕设和评审的经验,把高频问题集中整理一下,你按这个清单自查一遍,能少走很多弯路。
5.1 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 启动报错找不到数据源 | 没配 application.yml | 检查数据库连接、用户名密码、驱动依赖 |
| 文件上传报 1MB 限制 | Spring Boot 默认限制 | 配置 spring.servlet.multipart.max-file-size=50MB |
| 日期字段传值失败 | 前端格式不一致 | 统一使用 yyyy-MM-dd,加 @JsonFormat 注解 |
| 数据库中文乱码 | 库表字符集不是 utf8mb4 | 建库时指定 utf8mb4,连接 URL 加 characterEncoding=utf8 |
| 前端请求跨域 | 前后端分离联调 | 开发时配置 Vite 代理,上线后同源部署 |
| MyBatis Plus 分页不生效 | 缺少分页插件 | 配置 MybatisPlusInterceptor 并添加 PaginationInnerInterceptor |
| 定时任务执行两次 | 部署方式导致多实例 | 毕设单机部署不用管;如果用了多实例再考虑分布式锁 |
以上这些问题我都亲历过。比如分页不生效这个,很多人摔过,其实就是在配置类里少加了一个 MybatisPlusInterceptor Bean,加上就好了。
5.2 容易被答辩老师追问的点
回答问题的核心不是背答案,而是把"为什么这么设计"讲清楚。我把老师最爱问的问题大概梳理了一遍:
- 状态流转的合法性怎么保证?答:状态机校验,每个操作前检查当前状态是否匹配。
- 合同数据的一致性怎么保证?答:事务控制,关键写操作加 @Transactional。
- 权限控制是前端还是后端做的?答:前端控制菜单展示,后端做接口级校验,数据权限按需加。
- 如果多个部门同时审批怎么办?这是比较难的问题,坦白说毕设不会实现真实的并行审批,你在论文里写明"当前采用串行审批,未来可扩展并行审批节点"即可。
- 系统的安全性做了哪些措施?答:密码加密存储、登录验证码、SQL 注入预防(MyBatis 预编译)、XSS 过滤基础措施。
5.3 基于个人经验的实操建议
最后说几条实操层面的经验,都是我实际做项目时会注意的事:
演示用的数据一定要真实。我用的是某公司过往合同数据的脱敏版本,有完整的甲方乙方、金额、日期、审批意见,演示时比敲键盘造数据效果好得多。
代码要边写边提交管理。至少在你完成数据库设计、后端主体、前端页面这三个节点各做一次提交,万一改崩了可以回滚。
论文的技术选型和系统实现必须一致。这段话是发自肺腑的提醒:答辩时老师会翻论文看你写了什么技术,再打开系统看你能不能跑得起来。你要是论文里写了 Redis 缓存热点数据,结果演示时 Redis 没启动,系统照样跑,老师一问就露馅。技术栈可以简单,但前后要一致。
最后一个小技巧:做一份答辩演示清单,按照"系统登录 → 合同创建 → 审批流程 → 提醒功能 → 统计报表"的顺序走一遍,每步用一句话说清楚"当前在做什么、这个功能对应什么业务"。这个过程不用背,练两遍自然就顺了。
合同管理系统说到底就是个业务系统,它不炫技,但非常考验你对业务流程的理解和对工程细节的把控。把这个项目完整做下来,你对 Spring Boot 的掌握程度会有一个明显的提升。