☰
SpringBoot+Vue打造装饰工程管理系统:完整实战解析
2026/10/13 7:36:05 网站建设 项目流程

做装饰工程项目管理的系统,很多老牌软件公司都停留在单体架构时代,界面老旧、流程僵硬,项目经理宁可自己拉Excel表格也不愿意用。而新团队上来就搞微服务分布式,对一家中小型装饰公司来说,根本就是杀鸡用牛刀,维护成本高得吓人。我这次用SpringBoot+Vue这套主流组合,配合MySQL和MyBatis,完整做了一个装饰工程管理系统,从需求梳理到数据库设计再到前后端联调,整个过程踩了不少坑,也沉淀了不少经验,这篇文章就从头到尾掰开揉碎讲一遍。

这套系统不是那种只能跑demo的玩具项目,而是真正把装饰工程公司的核心业务链路打通了:项目立项、合同签订、材料采购、施工进度、验收结算,每个环节都有对应管理功能。角色权限也做了细分,老板、项目经理、材料员、财务各看各的数据,互不干扰。如果你正在学SpringBoot或者Vue,想找个真实业务场景练手,或者你所在的公司正缺一套轻量级的工程管理工具,这篇文章都能给你一个完整可复现的参考。

1. 项目定位与核心需求拆解

1.1 装饰工程行业的管理痛点在哪

做装饰工程管理系统之前,得先搞清楚一个事:这个行业的痛点和常规的电商、OA系统完全不一样。装饰工程的核心是“项目制 + 多方协同”,一个项目从签合同到最终验收,中间涉及业主、项目经理、设计师、施工队、材料供应商、财务,至少六方角色。

传统管理模式是微信群里报进度、Excel表格管材料、口头汇报定变更。项目少的时候还能靠人肉记忆撑着,项目一多就全乱了:材料采购和施工进度脱节,项目经理不知道库房还有多少瓷砖;变更签证没有留痕,结算时业主不认账;工地上的隐蔽工程验收记录找不到了,后期返工互相扯皮。

这套管理系统的设计目标,就是把这些散落的业务数据收拢到一个流程里。项目立项后自动生成唯一的项目编号,合同挂到项目下,材料采购单关联到具体施工节点,进度汇报按周更新,验收记录拍照上传留痕。数据一旦集中了,老板看板、项目经理排期、财务对账就都有了依据。

1.2 角色权限与业务闭环设计

系统设计时我划分了四个角色:管理员(一般是老板或者副总)、项目经理、材料员、财务。管理员拥有全部权限,可以创建项目、分配项目经理、查看所有数据报表;项目经理负责项目进度汇报、施工任务派发、材料需求申请;材料员负责供应商管理、采购单创建和入库确认;财务负责合同收款记录和项目成本核算。

业务闭环是这套系统能不能真正用起来的关键。我设计的主流程是:管理员创建项目并关联合同,合同审核通过后自动生成施工计划模板;项目经理根据模板创建装修任务,每个任务可以关联材料需求单;材料员收到需求单后创建采购单,采购单入库后库存自动更新;财务在项目关键节点录入收款记录,最终的毛利报表由系统自动汇总合同金额、采购成本和其他费用。

这样的设计避免了“功能一堆但流程走不通”的尴尬。权限隔离不是生硬地藏菜单,而是在数据层面做过滤,比如项目经理登录后只能看到自己负责的项目,材料员只能看到待处理的采购申请,改别人数据这种事从接口层面就拦死了。

1.3 功能模块清单梳理

系统的功能模块按照业务自然划分成六大块:项目管理(含项目CRUD和状态流转)、合同管理(含收付款记录)、施工任务管理(含进度百分比和验收记录)、材料管理(含供应商、采购单、库存)、人员管理(含账号分配和角色权限)、统计报表(含项目毛利和材料消耗汇总)。

这里要特别说一个容易被新手忽略的点:模块划分要跟着业务流程走,而不是跟着数据表走。比如“供应商”这个实体,它既可以挂在材料管理下,也可以单独做成一个模块。我最终把它放在材料管理下面,因为供应商的日常操作场景就是采购询价,和材料管理天然绑定。强行拆开反而会让用户使用时要频繁切换页面,体验很割裂。

2. 技术选型与前后端架构设计

2.1 SpringBoot + Vue这套组合的选型理由

选技术栈这事,很多人的第一反应是“哪个火选哪个”,但实际做项目时,匹配业务复杂度才是第一原则。装饰工程管理系统最核心的是业务流程管理,不是高并发秒杀,也不是海量数据存储,所以技术方案要满足三个条件:开发效率高、上手门槛低、部署维护简单。

SpringBoot解决的是后端开发效率问题。它把Spring生态里那些繁琐的Bean配置全自动装配了,内置Tomcat,打包成jar直接就能跑。加上Spring Security或者拦截器做鉴权,MyBatis做数据访问,一套组合下来代码量比传统的SSH架构少一半以上。

Vue解决的是前端开发体验问题。它最香的点在于组件化开发和响应式数据绑定。页面上一个底部导航栏、一个状态标签、一个表格组件,都是可复用的Vue组件,改一处全局生效。表单数据用data绑定之后,用户输入和页面状态自动同步,不用像JQuery时代那样手动操作DOM。

前端用Vue2配Element UI,后端用SpringBoot 2.7,数据库MySQL 8.0,前后端通过RESTful JSON接口交互。这套组合网上的资料极其丰富,遇到问题随便一搜就能找到答案,对于成长中的团队来说,这就是最大的隐形优势。

2.2 前后端分离架构带来的效率提升

前后端分离这个词喊了好多年,但很多人没理解它到底解决什么问题。既然要落地这套系统,我就说说我真实的感受。

以前写Session + JSP那种模式,前端页面嵌在后端项目里,改个按钮样式要重启服务器。前后端分离后,前端项目和后端项目完全独立部署,前端开发只需要Mock数据就能把页面写出来,后端开发只需要用Postman测试接口。两边并行开发,联调时再对准接口文档,工期至少缩短三分之一。

这套系统里,后端只负责提供JSON数据接口,前端的路由由Vue Router管理,页面跳转不经过服务器。登录鉴权用的是JWT(JSON Web Token),后端签发token,前端存到localStorage里,每次请求带在Authorization头里,后端过滤器校验token并提取用户信息。这套模式天然适合前后端分离,也不存在Session同步的问题。

2.3 MyBatis在工程类项目中的独特优势

MyBatis在Java圈子里的地位很奇怪,一方面有很多人喜欢,另一方面也有不少人觉得它太底层、不如JPA好用。但用在工程管理系统这种“业务关联复杂、查询条件灵活”的场景,MyBatis反而是最优解。

装饰工程项目的数据查询往往不是单表操作。比如“项目列表页”要展示项目名称、负责人、合同金额、累计收款、当前进度,这些数据分布在project、contract、project_progress三张表里,还要带条件筛选。用MyBatis写SQL可以精确控制每一行查询,关联查询、子查询、动态SQL都信手拈来。而用JPA虽然也能实现,但遇到复杂统计SQL时要么绕来绕去,要么就得写原生的@Query注解,反而没有MyBatis直接来得痛快。

MyBatis的另一个优势是动态SQL。项目列表的筛选条件是动态的:可以选择只看“施工中”的项目,也可以加上“2024年开工”的时间范围,还可以根据项目经理过滤。这种组合查询用MyBatis的<if>标签拼条件,比Java代码里一堆if (xxx != null) { sql += " and ..." }要优雅太多,而且不容易出SQL语法错误。

3. 数据库设计与核心表结构

3.1 核心数据表设计与关联关系

数据库设计是整系统的地基,地基歪了,后面业务代码写起来全是病。我画表结构花的时间比写代码的时间还多,反复调整了三次,最终定位的关键表有八张:sys_user(用户)、sys_role(角色)、project(项目)、contract(合同)、project_task(施工任务)、material(材料)、purchase_order(采购单)、purchase_item(采购明细)。

用户和项目之间是多对多关系,一个项目经理可以管多个项目,一个项目在特殊情况下也可以有多个负责人,所以中间加了一层project_manager_rel表。项目表和合同表是一对一关系,一个项目只能有一份主合同。合同表单独拆出来的原因是合同字段比较独立,包含合同编号、签约日期、合同金额、付款条件,放在项目表里会让项目表过于臃肿。

施工任务表是连接项目进度和材料消耗的关键节点。任务表里有关联的项目ID、任务名称、计划开始时间、实际开始时间、完成度百分比。材料需求单挂在任务下面,采购单又关联材料需求单,形成了一条完整的业务链。

3.2 关键字段设计与状态机约束

有几个字段的设计思路值得单独拎出来说说。

项目状态字段,我用的不是Integer而是一个字符串类型的枚举值:“未开始”“施工中”“已停工”“验收中”“已完工”。字符串枚举在代码里可读性强,也不容易出现魔数,配合状态校验器来控制状态流转。比如只有“施工中”的项目才能新建施工任务,“已完工”的项目不允许再添加采购单,这种业务约束写在Service层而不是Controller层,避免前端绕过校验直接调接口。

金额字段全部使用decimal(10,2),不能图省事用float。工程款的精度问题在结算时会放大,float的二进制存储误差在累计多笔后会产生“对不上账”的问题。decimal类型存的是字符串形式的小数,精度可控,财务人员查账时也安心。

所有业务表都加了create_time、update_time、deleted这三个基础字段。deleted字段是逻辑删除的标志,因为工程管理系统里的数据有审计追溯需求,项目经理误删了一条采购记录,如果物理删了,后续对账就找不到痕迹了。逻辑删除只标记不删除,查询时统一加where deleted = 0条件。

3.3 索引设计与性能优化

数据量还不到写复杂索引优化的程度,但基础索引必不可少。外键字段一定要建立索引:project_id在任务表、合同表、采购单表里频繁作为查询条件,不建索引的话全表扫描是必然的。其次status字段也建了普通索引,因为列表页最常用的筛选项就是状态。

有个当时踩过的坑分享下:在联表查询时,MyBatis的<resultMap>里配置了级联查询,导致一条项目详情查询要额外执行三四条SQL。后来我把详情页的SQL改成了JOIN关联查询,一条SQL查出所有关联数据,耗时直接从200多毫秒降到了30毫秒。对中小型系统来说,减少SQL执行条数比技术的微优化来得更实在。

4. 后端接口设计与核心业务实现

4.1 统一接口规范与异常处理

后端接口的规范决定了前端开发效率。我设计了一套统一的响应结构ResponseResult,包含三个字段:code(业务状态码)、message(提示信息)、data(业务数据)。成功时code为200,业务规则校验不通过时code为4001,系统异常时code为5000。

这套结构的好处是前端Axios拦截器可以集中处理。接口返回200时正常渲染data;返回4001时直接弹出message提示;返回5000时统一跳转到错误页。不用每个接口单独做错误判断,节省大量重复代码。

异常处理这里我给个建议:不要只依赖全局@RestControllerAdvice兜底,业务里一定要主动抛自定义业务异常。比如创建采购单时如果材料库存不足,直接在Service层抛一个BusinessException,message写成“瓷砖库存不足,剩余xx箱”。这样的提示信息最准确,用户在页面上看到就知道怎么处理,不会一脸懵地看“系统错误”。

4.2 登录鉴权与权限控制实战

登录接口的设计流程是:用户提交用户名密码,后端用BCrypt加密算法校验密码,通过后生成JWT token,token里封装了用户ID和角色ID,有效期设置为2小时。

JWT token的生成用到了java-jwt库,核心代码大概长这样:

Algorithm algorithm = Algorithm.HMAC256(secretKey); String token = JWT.create() .withClaim("userId", user.getId()) .withClaim("roleId", user.getRoleId()) .withExpiresAt(new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000)) .sign(algorithm);

token生成后返回给前端,前端存储到localStorage并放入Axios请求拦截器。后端用一个拦截器JwtInterceptor统一校验每个请求的token,校验失败返回401状态码,前端Axios响应拦截器捕获401后跳转到登录页。

权限控制这块,我采用的是细粒度的接口权限而非简单的角色判断。比如项目经理能调用“创建施工任务”接口,材料员不能。我在接口上加了自定义注解@RequiresPermission("project:task:add"),拦截器里先解析token中的角色ID,再到数据库查这个角色的权限列表,判断是否包含所需权限。这套方案比硬编码if (roleId == 2)的方式可维护性强太多,新增角色和调整权限只需要改数据库,不用改代码。

4.3 项目进度与材料联动模块实现

这个模块是整个系统的业务亮点,也是技术实现上最值得讲的。施工任务完成后,系统要自动判断该项目下所有任务的完成情况,如果全部完成,则自动将项目状态更新为“验收中”。这个逻辑如果放在前端做,用户刷新页面后状态就会错乱,所以必须放在后端事务里。

实现方式是:项目级联任务完成度统计方法,事务里先更新任务状态,再查询该项目下“未完成”的任务数量,如果为0则更新项目状态:

@Transactional(rollbackFor = Exception.class) public void completeTask(Long taskId) { ProjectTask task = projectTaskMapper.selectById(taskId); task.setStatus("已完成"); task.setFinishTime(new Date()); projectTaskMapper.updateById(task); int remainingTasks = projectTaskMapper.countUnfinished(task.getProjectId()); if (remainingTasks == 0) { Project project = projectMapper.selectById(task.getProjectId()); project.setStatus("验收中"); projectMapper.updateById(project); } }

材料联动的场景是:项目经理创建任务时填写材料需求单,材料员根据需求单采购,采购入库后系统自动扣减库存。库存逻辑需要注意一个并发问题:两个材料员同时处理同一份采购单会导致库存重复扣减。解决办法是在采购单上加上版本号字段version,更新时执行update stock set version = version + 1 where id = ? and version = ?,影响行数为0就说明数据被其他人改过,需要重试或报错。

5. 前端Vue实现与交互细节打磨

5.1 前端工程结构与核心配置

前端工程用的是Vue CLI脚手架创建的标准结构,目录划分比较常规:api目录统一放接口请求定义,router目录配置路由表,views目录放页面组件,components目录放公共组件,utils目录放Axios实例和公共工具函数。

Axios实例的封装是整个前端工程的基础,核心配置在api/request.js里。除了配置基础URL和超时时间,关键是请求拦截器和响应拦截器的编写。请求拦截器负责从localStorage获取token并加到Authorization头;响应拦截器负责判断HTTP状态码和业务code,401跳转登录页,业务错误弹Message提示。

service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config })

路由守卫用Vue Router的beforeEach钩子实现:判断进入的目标路由是否需要登录,需要的话检查是否有token,没有就重定向到登录页并且带上redirect参数,登录成功后自动跳回原页面。

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } })

5.2 Element UI组件与业务页面实现思路

页面整体用的是Element UI的布局组件,侧边栏菜单根据用户角色动态生成。管理员和项目经理看到的菜单不完全一样,材料员登录后菜单直接少了几项。这里用了Element UI的el-menu和Vue Router的路由meta关联来实现。

项目列表页是核心页面,参考了常见的管理后台风格:顶部筛选区、中间表格区、底部操作区。筛选区放了项目状态下拉框、时间选择器、关键词输入框和搜索按钮。表格列包括项目名称、项目地点、负责人、合同金额、状态标签、创建时间、操作按钮。状态标签用el-tag根据枚举值显示不同颜色,让用户一眼能看出项目处于哪个阶段。

表单页的设计需要注意细节。创建项目时,合同金额输入框要做千分位格式化,用户输入数字后失焦时自动带上逗号分隔。编辑项目时,负责人下拉框的数据要实时从后端拉取用户列表。提交表单前要做前端基础校验(必填项、金额格式、日期范围),校验通过后才发起请求。

5.3 状态流转与页面的联动交互

状态流转在项目列表页和任务详情页都能触发。项目经理在自己的任务列表里点击“完成”按钮,弹窗确认后调用后端接口。接口返回成功后,前端要刷新项目详情页的任务进度条。进度条我用了Element UI的el-progress组件,百分比通过计算任务完成数占总任务数的比例得到。

细节上有个体验问题值得注意:新增任务表单完成后,前端要手动把新任务数据追加进当前页面列表,而不是reload整个路由刷新页面。reload会丢失当前筛选条件和页码,用户还得重新设置一遍,体验很差。正确做法是调用列表接口重新拉少量数据,或者本地push进数组后重新计算进度条。

交互提示上,遇到逻辑删除的情况时要给用户明确的按钮。删除项目时确认弹窗文案写明“项目删除后将无法恢复”,避免误操作。因为逻辑删除后列表页看不到,但数据仍在数据库里,如果用户想恢复还要走后台操作,前端体验要尽量把这个流程想清楚。

6. 环境搭建、部署与常见问题排查

6.1 本地开发环境和启动步骤

把代码从仓库clone下来之后,第一步不是直接启动后端,而是先准备环境和配置文件。

后端是Spring Boot标准的Maven工程,需要JDK 1.8+、Maven 3.6+、MySQL 5.7+。配置文件里修改数据库连接账号密码,初始化数据库的SQL脚本执行后,项目里还配置了首次启动自动建表的选项(spring.jpa.hibernate.ddl-auto=update),但从正规项目的角度讲,我还是建议用SQL脚本迁移,避免自动建表导致的字段遗漏。

前端是标准的Node工程,需要Node.js 14+和npm。依赖安装时建议用npm install初始化,如果依赖拉不下来就检查registry源,切换成国内镜像源后一般都能解决。开发模式下前端启动在8080端口,后端启动在8081端口,两者之间通过后端的cors配置允许跨域访问。生产部署时再前端的Vite base路径配置和后端Spring Boot的静态资源映射,然后统一放到一个Web服务下。

下面给一个启动步骤清单:

  1. 新建MySQL数据库,编码utf8mb4,执行项目根目录下的init.sql脚本
  2. 后端配置application.yml,填入数据库账号密码,通过Maven打包成jar
  3. 后端执行java -jar decoration-system.jar启动,确认8081端口监听
  4. 前端配置.env文件,设置VUE_APP_BASE_URL=http://localhost:8081/api
  5. 前端执行npm run serve启动,浏览器访问8080端口

6.2 常见问题排查与解决实录

开发过程中遇到的最典型问题是跨域,前端访问后端接口报错blocked by CORS policy。排查思路是先确认是跨域问题还是接口404问题,浏览器Network面板看请求是否发出去了。跨域解决方案用了@CrossOrigin注解加在Controller类上,但这只解决了开发环境的问题,生产部署时还是要把两个服务部署到同一域下,用Nginx做前端静态资源的反向代理并转发API请求。

第二个常见问题是数据库连接失败。后端启动时报Access denied for user 'root'@'localhost',几乎可以肯定是application.yml里的密码不对或者权限不对。排查时用命令行先试一下mysql -uroot -p能不能连上,再核对配置文件。

第三个常见问题是数据库初始化脚本的版本不一致,多张表没有建立外键关系,导致联表查询时结果为空。所以初始化顺序要明确:先建主表(用户、项目、合同),再建关联表(任务、采购单),最后插入初始化数据,外键级联删除要从子表开始删。

第四个高频问题是前端接口请求报401。这里有个坑:用户登录成功拿到token后,发请求时config.headers['Authorization']没有带上,或者baseURL配置错误。排查时先在浏览器控制台打印token是否存入了localStorage,再看请求头有没有带上正确字段。

如果你在实际开发中也遇到过接口返回正常但页面渲染数据为空的情况,那大概率是前端字段名和后端字段名不一致。比如后端的projectName字段,前端写成project_name,JSON返回后数据匹配不上。这种情况没有捷径,就是多看接口文档多对齐字段名,后端接口写好后可以先导出Swagger文档,前端照着字段名做渲染就稳了。

6.3 系统上线前要注意的几个细节

上线前还有一个特别容易被忽略的点:默认账号的密码太简单。系统初始化时管理员账号密码是admin/123456,如果上线不修改,等于给安全留下了隐患。我在系统里加了首次登录强制改密的逻辑,管理员用自己的账号密码登录后,系统检查到密码强度不满足要求,会强制跳转到修改密码页面,改完才能继续操作。

生产环境的数据库连接串要配置连接池参数。Spring Boot默认的HikariCP池是够用的,但数据库连接超时和最大连接数要调到合理的值。我在配置里将maximum-pool-size设为20,connection-timeout设为30000ms,这样能保证多人同时使用系统时不会因为连接数不够导致卡顿。

部署时万一遇到端口占用,可以先查一下端口是否被其他程序占用,再杀掉进程重新启动,不需要急着改代码。系统上线后最好配置一个监控页面,监听系统健康状态,出问题能第一时间发现问题,这也是工程管理系统在运维上比普通项目更重要的环节。

7. 从开发到落地的个人项目体会

7.1 从开发到落地的几个关键认知

这套装饰工程管理系统从需求调研到正式投入使用,前后花了大概一个半月。回头复盘,最深的体会是:一个系统能不能用起来,技术只占三分,业务逻辑梳理和操作体验占七分。最开始交付第一版时,项目经理反馈操作流程和线下习惯对不上,材料员觉得每次开采购单要填写太多字段。于是产品细节做了调整:采购单增加复制上一条功能、项目列表增加导出Excel、进度汇报支持手机端拍照上传。这些改动看着不起眼,但恰恰是这些不起眼的小功能让用户愿意天天用。

技术层面来说,我是建议把SpringBoot+Vue+MySQL+MyBatis这套技术栈吃透再考虑其他框架的。装饰工程管理系统这个业务正好卡在“单块应用足够搞定、微服务反而带来负担”的甜蜜点上,用这套技术做表单密集、状态流转多、权限要求细的管理系统,效率真的拉满。MyBatis让你能精确控制每一条SQL行为,Vue让你能把交互细节做得细腻,SpringBoot让后端开发像搭积木一样顺畅。

这个系统后续如果想继续迭代,还可以扩展的方向不少:比如接入工作流引擎让审批流程更灵活,增加项目维度的成本核算和利润分析报表,抑或是加一个大屏看板把老板关心的月度产值、回款率、材料消耗趋势可视化展示。核心就是把数据沉淀好,后续扩展就很容易接上。

如果你也在做类似的管理系统,我的建议是先把核心业务流程捋清楚再动手写代码,业务梳理清楚了,技术选型和代码结构都水到渠成。遇到问题也不要慌,这套组合在互联网上已经被问烂了,搜一搜肯定有答案。

最后分享一个我在写这套系统时的体会:代码不是越花哨越好,能跑上生产环境的代码,比任何炫技的实现都值钱。

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

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

立即咨询