SpringBoot建筑工程管理系统:从CRUD到业务流程闭环设计
2026/9/8 2:59:26 网站建设 项目流程

开工之前先聊两句。很多同学拿到“基于SpringBoot的建筑工程项目管理系统”这种毕设题目,第一反应是:这不就是个CRUD吗?表建好、页面搭好、增删改查一写就完事。但真做起来会发现,建筑工程管理系统跟普通的学生管理、图书管理不太一样,它有业务场景、有角色分工、有流程节点,也有行业特有的术语和数据结构。你拿这套系统去答辩,评委大概率不会只问“这个按钮怎么实现”,而是会问“项目立项之后合同跟进度怎么关联”“材料入库和成本台账怎么对得上”。这些问题如果心里没数,代码能跑也没用。

这篇文章我按自己实际做这类项目的思路,把这个“毕设源码+文档”级别的系统拆开来讲:从选题定位、技术栈选择、数据库设计,到核心功能实现、跑通步骤、答辩避坑,再到文档怎么写才像样。无论你是打算直接参考现有源码,还是想自己从零撸一套,这篇都能帮你少走弯路。

1. 项目定位与技术选型:为什么是SpringBoot加上建筑工程

1.1 这个系统到底要解决什么问题

建筑工程项目的管理链条其实很长:立项审批、合同签订、施工进度、材料采购、设备进出场、质量检查、人员安全、成本核算,每一环都有单据、有审批、有台账。传统做法是Excel加纸质单据,项目一多就乱,数据对不上、责任扯不清。管理系统要做的就是把这些线下流程搬到线上,让每个角色看到自己该看的数据,让每一步操作留痕可查。

作为毕设,没必要把整个ERP都塞进去,但要覆盖一条相对完整的主线。我建议把范围圈定在这几个核心模块:系统管理(用户、角色、权限)、项目管理(立项、基本信息)、合同管理(登记、付款计划、发票)、进度管理(节点计划、实际进度)、材料管理(采购、入库、领用)、质量管理(检查、整改)、报表统计(合同额、成本、进度汇总)。这样既贴合建筑行业,又能把常规技术点全部覆盖,答辩时有东西可讲。

1.2 技术栈选择背后的逻辑

选SpringBoot不是因为它“流行”,而是它做这类管理系统确实合适。毕设场景要的是快速开发、结构清晰、部署方便,SpringBoot的自动配置、内嵌Tomcat、Starter生态,能让你把精力放在业务代码上,而不是折腾XML配置。配合MyBatis Plus做数据访问,单表CRUD几乎不用写SQL,分页也直接有现成方案。

前端我用的是Vue3加Element Plus,后台管理界面的经典组合。Vue3的组合式API写起来比Vue2清爽,Element Plus的表格、表单、弹窗组件能覆盖90%的页面需求。前后端分离的好处是结构清楚,答辩时可以重点讲接口设计,也可以顺手展示一下前端工程化的内容。

认证这块,毕设级别用JWT加拦截器就够了,不必上Spring Security,除非你打算把权限模型做得特别细。我见过不少同学在Spring Security上卡了两三天,结果核心业务没时间写。自己用JWT生成令牌、写个拦截器校验,半小时就能搞定,而且原理讲得清。

1.3 交付物拆解:源码和文档各含什么

标题里写了“毕设源码+文档”,这两部分的交付内容要提前想清楚。

源码部分通常包含:后端工程(SpringBoot项目)、前端工程(Vue项目)、数据库初始化SQL脚本、README部署说明。文档部分一般就是毕业论文,核心章节包括:选题背景与意义、需求分析(含用例图)、系统设计(含架构图、功能模块图、数据库ER图)、系统实现(核心代码与截图)、系统测试(测试用例与结果)。文档不是代码的附属品,答辩时老师重点翻的就是它,后面专门用一节写怎么写。

2. 业务模块设计与数据库建模:前期想得越细,后期越省事

2.1 建筑工程的业务链条怎么落到系统里

很多同学数据库设计上来就建表,结果写到一半发现字段不够、关系不对,回头改表结构,代码也牵连着重写。正确顺序是先捋业务对象和它们的关系。

建筑工程管理系统的核心对象,我列成这样一张表:

业务对象核心字段要点主要关联
用户用户名、密码、角色、所属部门角色表、部门表
项目项目编号、名称、地址、面积、预算金额、开工日期、竣工日期合同、进度、质量
合同合同编号、名称、金额、签订日期、乙方单位、付款计划项目(多对一)
进度节点节点名称、计划开始/结束、实际开始/结束、完成百分比项目、人员
材料材料名称、规格、单位、单价、供应商入库单、领用单
入库单入库单号、材料、数量、入库日期、经办人材料、用户
领用单领用单号、材料、数量、领用人、用途材料、用户
质量检查检查单号、项目、检查部位、检查结果、整改要求项目、用户
通知公告标题、内容、类型、发布时间面向全体用户

这些对象之间大多是“一对多”关系:一个项目有多份合同、多个进度节点、多次质量检查;一份合同可以有多个付款计划。在设计时把主外键关系理清楚,后续写联表查询和统计报表就顺了。

2.2 核心表结构设计参考

拿几张核心表当例子,具体设计时可以照着扩展。

用户表和角色表是比较通用的:

CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL COMMENT '用户名', password VARCHAR(100) NOT NULL COMMENT '密码(BCrypt加密)', real_name VARCHAR(20) COMMENT '真实姓名', phone VARCHAR(20) COMMENT '联系电话', status TINYINT DEFAULT 1 COMMENT '状态:0禁用 1启用', create_time DATETIME COMMENT '创建时间' ); CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_code VARCHAR(50) NOT NULL COMMENT '角色编码:ADMIN/MANAGER/STAFF', role_name VARCHAR(20) NOT NULL COMMENT '角色名称' );

项目表是业务的核心,字段要覆盖建筑工程的关键信息:

CREATE TABLE project_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, project_no VARCHAR(30) NOT NULL COMMENT '项目编号', project_name VARCHAR(100) NOT NULL COMMENT '项目名称', project_address VARCHAR(200) COMMENT '项目地址', building_area DECIMAL(12,2) COMMENT '建筑面积(平方米)', budget_amount DECIMAL(15,2) COMMENT '预算金额(元)', project_manager VARCHAR(20) COMMENT '项目经理', start_date DATE COMMENT '计划开工日期', end_date DATE COMMENT '计划竣工日期', actual_start_date DATE COMMENT '实际开工日期', actual_end_date DATE COMMENT '实际竣工日期', progress_status VARCHAR(20) COMMENT '项目状态:筹备中/进行中/已竣工/已暂停', description TEXT COMMENT '项目描述', create_by BIGINT COMMENT '创建人', create_time DATETIME COMMENT '创建时间' );

注意DECIMAL的使用。金额字段一律用DECIMAL,千万别用FLOAT或DOUBLE,否则累计金额会出现精度问题,答辩时被问到就很尴尬。

材料表会涉及规格和单位,这两个字段建议分开存:

CREATE TABLE material_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, material_name VARCHAR(50) NOT NULL COMMENT '材料名称', material_spec VARCHAR(50) COMMENT '规格型号,如HRB400/25mm', unit VARCHAR(10) COMMENT '单位:吨/平方米/立方米', price DECIMAL(10,2) COMMENT '参考单价', supplier VARCHAR(50) COMMENT '主要供应商', stock_quantity DECIMAL(12,2) DEFAULT 0 COMMENT '当前库存量', warning_quantity DECIMAL(12,2) DEFAULT 0 COMMENT '库存预警值' );

2.3 字段设计上的几点经验

第一,所有表都建议加create_time、update_time,方便做审计和排错,MyBatis Plus里用MetaObjectHandler可以自动填充。第二,逻辑删除字段deleted建议加上,避免物理删除后数据无法追溯,尤其是合同、项目这类核心数据。第三,状态字段用TINYINT或VARCHAR存编码,不要存中文,前端再通过数据字典转换显示。第四,项目编号、合同编号这类业务编号不要依赖自增ID主键,使用序列或日期加随机数生成,更符合行业习惯。

数据库字符集统一用utf8mb4,排序规则用utf8mb4_general_ci,不然Emoji和生僻字可能存不进去。

3. 核心功能实现细节:从登录鉴权到数据导出

3.1 JWT登录鉴权的简单可靠实现

JWT是很多管理系统登录的标配方案。流程是这样的:用户提交用户名密码,后端校验通过后生成一个token返回给前端,前端存在本地,之后每次请求在请求头里带Authorization:Bearer ,后端写一个拦截器统一校验。

生成JWT的代码模式大概是这样的:

@Component public class JwtUtil { @Value("${jwt.secret}") private String secret; @Value("${jwt.expire}") private Long expire; // 单位:秒 public String generateToken(Long userId, String username) { Date now = new Date(); Date expireDate = new Date(now.getTime() + expire * 1000); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim("username", username) .setIssuedAt(now) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } }

密码存储用BCrypt加密,Spring Security里自带BCryptPasswordEncoder,也可以单独引入jbcrypt依赖。要用明文存密码,答辩直接就会被质疑安全性。校验密码时调用matches方法即可。

拦截器里要注意放行登录接口、Swagger/Knife4j接口文档路径、静态资源路径,其他的统一校验。这个放行配置是许多同学容易搞错的地方,放多了接口裸奔,放少了前端连登录都进不去。

3.2 通用分页和多条件查询:又是老生常谈但必须写对

管理系统里最常用的接口就是分页加多条件查询。MyBatis Plus里用LambdaQueryWrapper可以链式拼条件:

public Page<ProjectInfo> queryProjectPage(ProjectQueryDTO dto) { Page<ProjectInfo> page = new Page<>(dto.getPageNum(), dto.getPageSize()); LambdaQueryWrapper<ProjectInfo> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(dto.getProjectName()), ProjectInfo::getProjectName, dto.getProjectName()) .eq(dto.getStatus() != null, ProjectInfo::getProgressStatus, dto.getStatus()) .ge(dto.getStartDate() != null, ProjectInfo::getStartDate, dto.getStartDate()) .le(dto.getEndDate() != null, ProjectInfo::getEndDate, dto.getEndDate()) .orderByDesc(ProjectInfo::getCreateTime); return projectInfoMapper.selectPage(page, wrapper); }

这里有几个要点:条件判断必须加上StringUtils.isNotBlank!= null,不然没传值时会拼出恒真条件,拖慢查询。模糊查询用like,注意like两边会自动加%,不需要手动拼。排序字段建议白名单校验,防止用户通过orderBy参数注入恶意排序字段或SQL。

前端表格控件会绑定pageNum、pageSize、total三个字段,element-plus的el-pagination组件直接对接,返回结构统一设计成{ code, message, data },data里面放分页对象即可。

3.3 合同金额和项目成本的统计报表怎么设计

报表是建筑工程管理系统里比较出彩的部分,做得好答辩时加分明显。常见的统计需求:各项目合同总金额汇总、月度材料入库金额统计、项目成本与预算对比。

用SQL聚合来实现这类统计很直接:

@Select("SELECT p.id, p.project_name, " + "IFNULL(SUM(c.contract_amount), 0) AS contract_total, " + "IFNULL(SUM(c.paid_amount), 0) AS paid_total, " + "p.budget_amount FROM project_info p " + "LEFT JOIN contract_info c ON p.id = c.project_id " + "GROUP BY p.id") List<ProjectCostStatsVO> selectProjectCostStats();

注意LEFT JOIN的用法。如果项目还没有合同,INNER JOIN会把项目过滤掉,统计就不全。IFNULL把金额空值变成0,前端图表渲染更省心。

前端展示用ECharts的柱状图或饼图,对比各项目预算和合同总额,图表能一眼看出哪些项目超预算了。这个组合统计在答辩演示里效果很好。

3.4 Excel导出和文件上传:实习单位最常用的功能

施工项目动不动要上报材料报表,系统里做Excel导出是刚需。我用的是EasyExcel,比POI写起来省太多事:

public void exportMaterialStock(HttpServletResponse response, String projectId) throws IOException { List<MaterialStockVO> list = materialMapper.selectStockList(projectId); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("材料库存表", "UTF-8").replace("+", "%20"); response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), MaterialStockVO.class) .sheet("材料库存") .doWrite(list); }

导出文件名有中文时,必须进行URL编码处理,否则浏览器下载时会出现乱码或直接报错。

文件上传主要用于合同附件、设计图纸、质量检查图片上传。上传目录不要放在前端静态资源里,推荐按日期分目录存储,生成UUID文件名防止重名和路径遍历攻击。后端返回文件访问URL,前端配合element-plus的upload组件处理即可。

4. 实操过程与部署跑通:拿到源码后别急着改代码

4.1 运行环境和初始化顺序

拿到项目先别急着双击打开,先把环境对齐,否则各种报错会让你怀疑人生。运行环境建议如下:

软件版本建议说明
JDK8或11SpringBoot 2.x用8没问题,3.x要17以上
Maven3.6+建议用IDEA自带的也行
MySQL5.7或8.0注意8.0的驱动配置差异
Node.js14到18都行前端构建工具要求
IDEIntelliJ IDEA社区版就跑得起来

初始化流程是先建数据库、导入SQL脚本,再启动后端,最后启动前端。很多人先启动了后端,结果报“数据库连接失败”,又慌慌张张去查端口、查防火墙,其实只是一开始就漏了导数据这一步。

4.2 后端配置里的几个关键点

application.yml配置看似简单,但有几个点值得单独拎出来说。

spring: datasource: url: jdbc:mysql://localhost:3306/construction_mgmt?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 50MB max-request-size: 50MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0

MySQL 8.0必须加serverTimezone,不然会报时区异常;驱动也换成com.mysql.cj.jdbc.Driver。大文件上传不调整multipart大小默认1MB,图纸肯定传不上去。map-underscore-to-camel-case打开后,数据库字段project_no才能自动映射到实体类的projectNo,写代码能省很多事。

4.3 前端联调里的跨域和代理配置

前后端分离一定遇到跨域问题。最省事的办法是在SpringBoot里加全局CORS配置,生产上可以换成网关或Nginx反向代理:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

如果是Vue开发环境,更推荐用vite的proxy代理,把/api开头请求代理到后端端口,这样浏览器就不会有跨域请求了。上线再通过Nginx做一次反向代理,前端和后端共用同一个入口,CORS问题彻底绕开。

4.4 租户和权限控制:角色不要只做展示

很多毕设系统的权限只做到“菜单显示与隐藏”,接口层面不控制。换句话说,普通用户直接调接口就能拿到管理员数据。这个点评委极爱问,答不上来很扣分。

我常用的方案是在拦截器里解析token里面的userId,查出对应的角色编码,然后在接口前置校验里判断角色权限,也可以结合Spring AOP自定义注解来做:

@RequireRole({"ADMIN", "PROJECT_MANAGER"}) @GetMapping("/project/delete/{id}") public Result deleteProject(@PathVariable Long id) { projectInfoService.removeById(id); return Result.success(); }

切面里判断当前用户角色是否在注解允许列表中,不在就抛异常返回403。这样实现的权限控制有真实逻辑,又不会像Shiro或Spring Security那么复杂。

5. 常见问题排查与答辩避坑实录

5.1 高频问题速查表

这些是我在做类似项目时经常碰到的问题,整理成表方便对照:

问题现象根本原因解决办法
启动报“Access denied for user”数据库账号或密码配置错误核对datasource配置,特别注意密码里的特殊字符
接口返回404但Controller存在请求路径与@RestController的@RequestMapping不一致检查后端ContextPath和前端baseURL是否匹配
表格数据日期比数据库少8小时MySQL连接时区与系统不一致在URL加serverTimezone=Asia/Shanghai
上传excel中文文件名乱码未做URLEncoder编码见3.4节代码
登录后访问其他接口401JWT放行路径配置覆盖了校验路径检查拦截器中excludePathPatterns是否正确
MyBatis Plus查询字段全部为null驼峰自动映射没开启配置map-underscore-to-camel-case: true
前端打包后刷新404Vue路由是history模式,Nginx没配置try_filesNginx无法匹配的路径指向index.html

5.2 答辩时容易被问倒的几个技术点

评委会顺着代码问下去,喜欢追问“为什么”。准备几个高频问题的回答思路。

问你为什么不用Restful风格。你可以说项目接口有统一的REST风格,GET对应查询、POST对应新增、PUT对应修改、DELETE对应删除,路径用名词复数,状态码统一返回200再加业务code。把response结构里的code、message、data讲清楚就行。

问你权限怎么实现的。先把JWT校验流程说清,再讲@RequireRole注解的AOP拦截。如果做过数据权限,比如项目经理只能看自己负责的项目,可以进一步说在查询时把当前用户ID作为过滤条件,但注意不要说得太绝对,否则老师追问细节容易露怯。

问你不考虑工期的甘特图吗。如果你时间有余量,可以引入一个简单的时间轴图表页面,展示项目节点计划。但不建议贪多,基础功能做完比加不上线的鸡肋功能更值钱。

5.3 现场演示的加分细节

演示前把测试数据准备好,尤其要有不同状态的项目和合同。不要演示时现敲查询条件,一紧张打错字页面空白,印象分会掉。建议按这个顺序演示:首页仪表盘统计、项目全流程(新增、查询、编辑、删除)、合同关联项目、材料入库领用、报表导出、权限切换(管理员和普通用户看同一个菜单的差异)。

演示时特意展示一个报错场景的兜底处理,比如故意输入非法ID删除不存在的项目,系统返回友好提示且不崩溃。这个细节会让评委觉得你有工程意识。

6. 毕设文档怎么写才像样:源码之外的另一半“交付物”

6.1 需求分析不能抄模板,要能对上流程

论文里的需求分析章节,很多同学喜欢从网上下模板,复制一堆“本系统致力于提高管理效率”的空话。这样写老师一眼就能看出来。正确写法是先画出用例图,然后针对每个角色写功能需求,比如:

  • 管理员:用户管理、角色分配、系统公告、所有模块的数据维护。
  • 项目负责人:项目信息维护、进度节点更新、质量检查、合同登记。
  • 仓管员:材料入库、领用登记、库存预警处理。

每条需求都对应一个具体页面和一个接口,这样需求才跟系统对得上。数据字典也建议列出来,像项目状态是“0筹备中、1进行中、2已竣工、3已暂停”,写清楚枚举值,后续设计章节引用起来逻辑才顺。

6.2 数据库设计文档画清楚ER图和表关系

数据库设计章节重点不是贴建表SQL,而是讲清楚表和表之间的关系。一张项目表,对应多张合同表,关联字段是什么;合同表对应多个付款计划,通过外键关联。ER图可以用PowerDesigner或者简单的draw.io画,画得干净清晰,别用截图糊弄。

表格设计建议每张表都写:表名、字段名、数据类型、可否为空、默认值、注释说明。字段注释注意跟代码实体类对应一致,别出现代码里叫projectName、文档里写项目名称,到底对应哪个字段自己也说不清楚的情况。

6.3 测试报告和操作说明是很多人漏掉的加分项

测试章节不要只写“系统已完成测试、运行稳定”这种结论。要列测试用例,包括用例编号、测试步骤、预期结果、实际结果。比如:

用例编号测试步骤预期结果实际结果
TC001使用admin/admin123登录登录成功,跳转首页通过
TC002使用错误密码登录提示用户名或密码错误通过
TC003新增项目并填写必填字段保存成功,列表出现新记录通过
TC004不填写项目名称提交提示“项目名称不能为空”通过

加上几张核心页面截图,标注功能点,测试章节一个晚上就能搞定。操作说明要写清楚系统初始化和使用步骤,尤其是管理员如何创建用户、分配角色,这部分在演示环节也经常被用到。

7. 后续扩展建议:这套系统还能往哪些方向走

如果你不想止步于毕设,这套系统后续有不少可以扩展的方向。一是引入工作流引擎,把合同审批、材料采购审批变成可视化流程,项目经理提交、经理审批、财务复核,每个环节留痕,这个扩展能直接把系统从“信息台账”升级成“管理平台”。二是增加移动端适配,施工现场人员不一定方便用电脑,做一个H5或者小程序端,只做查询和审批操作,开发成本不高但行业贴合度好。

三是把统计报表做得更智能,比如对接ECharts画施工进度曲线、材料价格趋势图,甚至可以做一个超预算预警,一旦累计成本超过预算的90%就自动给项目经理发提醒。四是引入AI能力,用大模型自动生成进度周报摘要或者质量整改意见初稿,这个方向比较前沿,如果你有余力尝试,答辩效果会非常惊艳。

最后再分享一个个人体会:做这类项目,最忌讳拿到源码就急着跑起来,也不建议只盯着代码量。真正能把系统讲清楚、把每个设计决策背后的“为什么”答上来的同学,哪怕功能少一两个,分数也不会低。你把业务数据流理清楚了,把表结构设计依据讲明白了,这套SpringBoot架构下的建筑工程管理系统,就不只是一份期末作业,而是一份真正有行业价值的技术作品。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询