☰
从Excel到SpringBoot:装潢公司管理系统设计与部署实战
2026/10/10 17:28:28 网站建设 项目流程

1. 装潢公司管理系统背后的真实痛点:为什么不能只用Excel

做了大半年装潢行业管理系统,接到这套基于SpringBoot的天盛装潢公司管理系统项目时,我一开始觉得就是个常规的业务系统——客户管理、合同管理、项目进度跟踪,这些模块在各类管理系统中都快做成标准件了。但真把需求梳理一遍后才发现,装潢行业的管理软件和通用的进销存、OA系统差别非常大,很多业务流程根本没法用现成的业务框架去套。

传统装潢公司最常见的运营状态是这样的:老板手里同时开着五六个工地,每个工地分布在城市不同角落,设计师在前端跟客户谈方案,项目经理在工地盯进度,材料供应商那边等着下单发货,财务这边还要按节点催工程款。信息基本都是靠微信群加Excel表格在流转——工地照片发在群里、材料清单存在Excel里、合同版本散落在各个人员的电脑上。听起来很落后,但这就是大量装潢公司真实的管理现状。

这个项目要解决的核心问题,并不是简单地把Excel搬到线上,而是要把装潢公司“以工地为中心、以进度为主线”的业务链条彻底理清楚。从客户第一次进店咨询,到设计方案确定、签合同、交底开工,再到水电木瓦油各阶段施工、中期验收、竣工验收,最后进入售后维保,整个生命周期里牵涉的人员角色很多:老板要看全局、设计要管方案、项目经理要管工地、采购要对材料、财务要管款项、工人要接收派工。

这个系统的目标用户也很明确:一类是装潢公司内部的管理者,需要随时掌握各个工地的实时进度和款项回笼情况;另一类是一线业务人员,设计师、项目经理、采购员需要在自己的环节里高效处理任务。后者更关心的是“我今天的活是什么”,前者更关心的是“这个月公司在建工地有多少、回款多少、哪些工地快要逾期了”。这两类需求,决定了系统的设计思路不能是简单堆功能,而是要有清晰的角色视角和数据视图。

装潢业务还有一个容易被外行忽略的特点——工程款的结算节点非常多。一般不是一次性收完全款,而是按照签约、开工、水电完工、泥木完工、油漆完工、竣工验收等多个节点分批收取。每个节点的金额比例、时间可控性、逾期风险都不一样,而且往往同一个工地有多笔款项交错进行,普通Excel管理方式很容易漏收、错收。这也是为什么系统里必须把“收款计划”和“实际收款记录”拆开设计,否则财务对账时一定出问题。

当时我拿到这个需求的时候,第一反应是:这个项目真正难的不在代码层面,而是要把装潢行业的业务语言准确地翻译成软件语言。比如“交底”,这个词在装潢行业指的是开工前设计、施工、客户三方对图纸和工艺进行现场确认的动作,普通的外行根本不知道这是什么。再比如“增项”——施工过程中客户临时增加的工程项目,这不只是一条文本记录,它会直接影响合同金额、工期、材料需求。如果系统设计者不懂这些业务细节,做出来的系统要么是个花架子,要么被人嫌弃“还不如Excel好用”。

2. 系统架构设计:基于SpringBoot的整体技术选型与模块拆解

2.1 技术栈选型的逻辑:为什么用SpringBoot这套组合

技术选型这一步,我基于项目现状做了几个权衡。后端主体框架锁定SpringBoot,这个基本不用纠结,社区生态完善、上手资料多、后续招聘技术人员也容易匹配。持久层用了MyBatis-Plus,因为装潢管理系统的数据模型关联关系并不像电商那么复杂,但动态SQL的诉求很常见——比如工地列表要根据状态、负责人、时间范围做多条件组合查询。MyBatis-Plus的LambdaQueryWrapper写这类动态条件非常舒服,还自带分页插件,可以省不少重复劳动。

数据库选型上用的MySQL,配合Redis做缓存。MySQL承载业务主数据,Redis用来缓存登录令牌、工地的实时进度摘要、以及一些高频查询的热数据。为什么要引入Redis而不是全部查MySQL?因为装潢公司老板会随时随地打开手机看工地状态,首页仪表盘要聚合多个工地的进度、待办、预警数据,每次都实时查库性能扛不住。用缓存把聚合结果存起来,数据变更时主动失效缓存,这个方案实测在几百个工地的数据量下非常稳。

前端采用的是Vue加Element UI,前后端分离。选这个组合主要考虑两点:一是装潢公司可能后续要对接小程序、移动端,前后端分离后接口可以直接复用;二是Element UI的表格、表单、树形控件比较全,做管理后台界面效率高。整个项目前端部署在Nginx,后端打包成可执行Jar包独立运行,后续如果需要水平扩容,加个负载均衡就能顶上。

2.2 业务模块怎么拆:从客户到售后的全链路覆盖

系统的业务模块,我按装潢公司的实际作业流程来划分,而不是按传统ERP的采购、销售、库存来生搬硬套:

  • 客户管理模块:记录意向客户、量房记录、跟进记录、客户来源渠道。核心字段包含客户需求描述、房屋面积、户型结构、预算区间、意向程度、下次跟进时间。
  • 设计管理模块:关联客户和工地,管理设计方案版本、效果图、施工图、报价明细。报价明细要支持按施工项目拆分,例如拆墙、水电改造、吊顶、墙面处理等单项明细,同时记录所选材料的品牌、型号、单价。
  • 合同管理模块:合同基本信息、合同金额、付款计划、增项记录、变更记录。付款计划是根据行业惯例拆分的多节点收款安排,每一期待收款项都有计划日期和实际收款日期。
  • 工地管理模块:工地从开工到竣工的状态流转,每个工地挂接施工进度计划、施工日志、现场照片、材料进场记录、验收记录、问题整改记录。
  • 材料管理模块:材料档案、材料库存、采购申请、采购入库、供应商管理。这个模块和工地进度是联动的,水电阶段要进场的是什么材料,瓦工阶段需要什么,系统里都要能提前预警。
  • 财务管理模块:收款计划、实际收款、退款、保证金管理,以及与收款节点联动的财务对账报表。
  • 人员与权限模块:员工账号管理、角色定义、工地负责人的分配、操作日志。权限粒度至少要控制到菜单和按钮,因为装潢公司很在乎数据隔离,设计师不应该看到财务部门的收支明细。

模块拆分的核心思路就是一句话——一切围绕工地转,工地是一切数据的聚合中心。客户可以对应多个工地(老客户介绍新项目),合同挂在客户下,但施工进度、材料消耗、工程款收付都必须挂在具体的工地上。

2.3 数据库设计里的关键一张表:工地状态流转表

整个系统数据库大概整理了30多张表,其中工作量最大也最容易踩坑的是工地状态相关的设计。我最初想用一个简单的status字段从0排到10,0表示待开工,1表示水电施工,2表示泥木施工……后来发现根本不够用,因为工地进度不是纯线性的,可能会出现“水电验收不合格需要返工”“瓦工进行中临时停工等材料”等分支状态。

最终的设计是把“工地基本信息表”和“工地状态日志表”拆开,基本信息表里只存当前状态,状态日志表记录每一次状态变更的时间、操作人、变更前状态、变更后状态、变更原因。这样既保留了实时状态查询的高效性,又能追溯整个工地的历史轨迹。老板看一个工地时,能一眼看到这个工地什么时候开工、什么时候水电验收、中间有没有停工,这些都是管理上有价值的线索。

同期还设计了收款计划表。每张合同对应一条或多条收款计划,计划字段包括批次、计划金额、计划日期、实际收款日期、实际收款金额、收款状态。这个表写起来不难,但业务上容易忽略的一点是——当合同发生增项时,增项金额需要分摊到后续未收的批次上还是单独追加一个收款批次?这里我和需求方反复确认过,最终确定的规则是增项单独生成收款批次,不打乱原来的计划节点。这样财务对账的时候,每一笔钱都能对上对应的业务事件。

3. 部署文档背后的完整流程:从环境准备到生产环境落地

3.1 环境版本踩坑:JDK版本、MySQL时区、Redis版本兼容

这套系统的部署文档是我踩了不少坑之后整理出来的,先说说最容易出问题的环境版本问题。

项目基于SpringBoot 2.x开发,这里有一个非常关键的版本兼容性问题——SpringBoot 2.x原生的JDK8没任何问题,但如果你用JDK11甚至JDK17去跑,部分老版本依赖会出现反射相关的报错。所以部署文档里我明确写了统一使用JDK8。有人可能觉得用新不用旧,但生产系统最重要的是稳定,JDK8配上SpringBoot 2.7.x是我实测最稳的组合。

MySQL这边一个大坑是时区。装潢系统的登录记录、工地日志、收款记录全都是带时间的数据,如果MySQL连接串里不加serverTimezone参数,默认按服务器本地时区解析,本地测没问题,部署到云服务器上时间差8小时。部署文档里必须设置连接串为:jdbc:mysql://localhost:3306/tianzhuang?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai。这个问题不写清楚,用户第二天打开系统看到所有记录时间都晚8小时,第一反应就是系统有Bug,实际上只是时区配置漏了。

Redis版本选择上不用追求最新,稳定版即可。系统对Redis的依赖主要是缓存和登录态,单实例完全够用。部署时唯一要注意的是Redis密码策略,以及设置合理的过期时间。我把登录令牌的过期时间设为8小时,工地详情页缓存设置为10分钟,这样既保证了体验又不用频繁清理失效数据。

3.2 部署三步走:初始化脚本、配置文件外置、前后端分离发布

整个部署过程我总结成三步。

第一步,初始化数据库。源码包里有init.sql和data.sql两个脚本,init.sql负责建表结构,data.sql负责基础数据。初始化的时候一定要先执行建表脚本再执行数据脚本,而且建议用命令行或Navicat执行源文件,不要复制粘贴到查询窗口执行——文件大了容易漏掉部分语句。装潢公司系统的基础数据里有不少字典项,比如户型类型、装修风格、施工状态、材料分类,这些数据错了后面所有下拉框都跟着错。

第二步,配置外置。SpringBoot的Jar包默认是读取包内的application.yml,但生产环境你肯定不希望每次改数据库密码都要重新打包。我的做法是把配置文件外置:部署目录下放一个config文件夹,里面放application-prod.yml,启动命令加上--spring.profiles.active=prod --spring.config.location=classpath:/,file:./config/。这样Jar包内保留默认配置,外部配置优先覆盖,改数据库连接、Redis密码这类敏感信息完全不用重新构建工程。

第三步,前端发布与反向代理。前端Vue项目执行npm run build后生成dist目录,把dist里的静态文件放到Nginx的html目录下。Nginx配置一个server块监听80端口,root指向dist目录,同时把/api前缀的请求反向代理到后端服务。这个配置里最忌讳的是前端路由的history模式没配try_files,导致用户刷新页面时404。我在部署文档里写了完整配置块:

server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }

后端服务直接用systemd或supervisor托管进程,保证异常退出后能自动拉起。这里我推荐systemd,云服务器基本都自带,配置文件写好后systemctl enable就能开机自启,比手动nohup要规范得多。

3.3 部署后的自检清单:不是能打开页面就算成功

光把系统跑起来不算部署完成,我习惯在交付前按清单过一遍环境自检:

  • 用不同角色账号登录,确认权限菜单是否正确渲染,普通员工能不能访问管理端接口(前端隐藏菜单不算,后端接口必须做鉴权)。
  • 造一条测试数据走完“客户-合同-工地-收款”主链路,确认各环节状态流转正常,特别是收款计划是否按合同生成。
  • 检查定时任务是否在跑——系统里有个逾期收款提醒的定时任务,每天上午9点扫描待收款项并生成待办通知,部署后至少等一个触发周期观察日志。
  • 看日志文件路径是否正确,Logback配置里有没有按天滚动,磁盘空间能不能撑住。

这一步在这次项目中尤其关键,因为装潢公司一线员工的软件使用水平参差不齐,如果系统部署完首页就打不开或者登录都费劲,后面很难推动他们用起来。一次到位的部署体验,比事后弥补要重要得多。

4. 核心代码实现讲解:这几个模块的写法直接决定业务能不能跑通

4.1 多层架构与统一返回结构:少走弯路的工程规范

这套系统严格按照Controller-Service-Mapper三层结构组织,Controller层只做参数接收和结果包装,Service层承载业务逻辑,Mapper层负责数据访问。有人觉得三层结构啰嗦,但实际维护一段时间就会明白,没有分层约束的代码到最后一定是逻辑到处飞,改一个地方崩三个功能。

统一返回结构这块我定义了一个Result类,所有接口都返回这个结构,包含code、message、data三个字段。前端拿到code为200才渲染data,非200的code弹出对应message,这样前后端联调和错误处理都能统一。这个规范看起来简单,但确实值得写进代码讲解文档——装潢公司后续如果请外部团队二次开发,统一结构能极大降低沟通成本。

4.2 工地进度流转的实现:基于状态机的安全变更机制

工地状态流转是这个系统最核心的代码。我把状态用常量类集中定义,并且封装了一个状态流转方法,在Service层判断状态变更是否合法。比如一个工地不能从“待开工”直接跳到“竣工验收”,必须经过施工中、完工待验等中间状态。这个判断如果放在前端,用户通过接口直接调就能绕过校验,所以必须放后端。

public void changeStatus(Long projectId, Integer targetStatus, String operatorId, String reason) { Project project = projectMapper.selectById(projectId); FlagValidator.checkNotNull(project, "工地不存在"); // 校验状态流转是否合法 if (!StatusTransition.isValid(project.getStatus(), targetStatus)) { throw new BusinessException("非法状态流转:" + project.getStatus() + " -> " + targetStatus); } // 更新当前状态 project.setStatus(targetStatus); projectMapper.updateById(project); // 记录状态日志 ProjectStatusLog log = new ProjectStatusLog(); log.setProjectId(projectId); log.setFromStatus(project.getStatus()); log.setToStatus(targetStatus); log.setOperatorId(operatorId); log.setReason(reason); statusLogMapper.insert(log); }

这段代码里还有一处细节,就是状态日志表里记录的是变更之前的状态(fromStatus)和变更之后的状态(toStatus),而不是只记录当前状态。有了日志,出问题的时候就能完整还原每个工地的历史轨迹。这次开发中一个客户曾质疑“工地明明6月就该完工,为什么拖到8月”,凭状态日志就能查到中间停工待料的记录和原因,这就是日志表存在的价值。

4.3 收款模块的事务与并发控制:钱相关的逻辑不能马虎

财务模块的代码最需要谨慎,涉及金额的写操作必须加事务控制。以“确认收款”这个接口为例,流程是更新收款计划的实收金额和收款状态、更新合同的已收金额/待收金额、写入资金流水表,这三个操作要么全成功要么全失败,所以直接在Service方法上标注@Transactional。

并发控制这块也踩过坑。设想一下:财务和业务员同时操作同一笔收款,财务点确认收款,业务员同时点申请退款,如果没有锁控制,数据库里的金额就可能出现覆盖。我的处理方案是使用悲观锁——查询收款计划时加上for update,锁住这行数据直到事务结束。并发量不高的管理后台场景下,悲观锁足够安全,而且实现简单,不用引入分布式锁组件。

@Transactional(rollbackFor = Exception.class) public void confirmPayment(Long paymentPlanId, BigDecimal actualAmount) { PaymentPlan plan = paymentPlanMapper.selectByIdForUpdate(paymentPlanId); FlagValidator.checkNotNull(plan, "收款计划不存在"); if (plan.getStatus() != PaymentStatus.PENDING) { throw new BusinessException("当前状态不可确认收款"); } // 更新收款计划 plan.setActualAmount(actualAmount); plan.setActualDate(LocalDate.now()); plan.setStatus(PaymentStatus.PAID); paymentPlanMapper.updateById(plan); // 更新合同收付汇总 contractService.refreshPaymentSummary(plan.getContractId()); // 写入资金流水 fundFlowService.record(plan); }

这段代码里selectByIdForUpdate是Mapper里自定义的SQL,加上FOR UPDATE子句。这类写操作必须明确要求加事务,不加事务的话任何一步失败都会留下脏数据。装潢公司的财务人员是每天都要对账的,一旦对不上账,系统信任度马上归零。

4.4 权限拦截与操作日志:Spring Security和AOP的落地实践

系统里我整合了Spring Security和JWT做认证授权。登录成功后签发JWT令牌,后续请求在拦截器里解析令牌并获取当前用户信息。权限模型是RBAC角色权限模型,角色分为超级管理员、老板、设计师、项目经理、财务、采购、普通员工,每个角色授予不同的菜单和数据权限。

这里提醒一个细节:很多系统只做登录拦截,所有登录用户都能查全部数据,这样老板一定不满意。工地数据、材料采购数据、财务数据都要按归属人做隔离。项目经理只能看到自己负责的工地,财务只能操作收款相关功能,设计师只能看到关联到自己名下的客户。这个数据权限不做好,后面上线一定会被吐槽。

操作日志我用Spring AOP做的,自定义了一个@OperationLog注解,标注了注解的Service方法在执行完成后自动记录操作人和操作内容。为什么不用拦截器做?因为拦截器拿不到Service层的业务语义,比如“确认收款”这个方法被调用时,拦截器只知道有人调了这个接口,但不知道是哪笔收款。在方法上通过SpEL表达式解析参数,才能明确记录到“用户A确认了合同B的第三期收款C”,这对后期审计非常有用。

5. 开发部署中实测遇到的典型问题与排查思路

5.1 跨域问题:前后端分离后的第一道坎

前后端分离开发和部署时,跨域问题几乎是必踩。前端跑在http://localhost:8081,后端在http://localhost:8080,直接调接口就遇到CORS拦截。解决方式有两个层级:开发环境用Vue的devServer配置proxy代理,把请求转发到后端,浏览器认为所有请求都是同源的;生产环境走Nginx反向代理,/api开头的请求全部转发给后端,天然没有跨域问题。

我在后端还加了CORS全局配置作为兜底,但这里有个安全细节:CORS的allowedOrigins不要写*,尤其不要打开allowCredentials模式的同时又允许所有来源。这次系统里我把允许来源配置写在application.yml里,生产环境只放开公司域名和本机回环地址,这样既不影响使用,又不至于让第三方网页任意调用接口。

5.2 金额字段精度问题:JAVA浮点类型计算结果对不上账

财务模块的金额字段如果设计不对,上线就是事故。开发初期有人图省事用了Double类型存金额,测试数据少的时候看不出问题,数据一多,对账就会出现几分钱的差异。这个问题的本质是浮点数在二进制表示下的精度损失,Java中用0.1加0.2都未必等于0.3。

后来我把数据库金额字段统一改成decimal(14,2),Java实体对应BigDecimal,所有金额计算使用BigDecimal的add和subtract方法。同时,前端传给后端的金额参数一律按字符串处理再转BigDecimal,不用double传参。前端展示时统一ToString保留两位小数。这套规范写进代码讲解文档后,再没出现过金额对不上的情况。

5.3 权限修改后JWT仍然有效的坑:Redis令牌检验才是正解

JWT令牌有个天然特点——签发后无法在服务端主动失效。假设项目经理离职了,系统管理员把他的账号禁用,但用户原本拿到的JWT令牌在过期前仍然有效,他还能继续调接口拉取工地数据。这个风险在装潢公司尤其不能忽视,人员流动性大,离职人员拿着令牌访问系统是很真实的威胁。

解决方案是在Redis里维护一份令牌白名单,拦截器在解析JWT后,再校验Redis中是否有对应的令牌记录,如果账号被禁用或权限被修改,服务端直接把Redis中的记录删除,下次请求拦截器发现没有白名单记录就拒绝放行。这样既保留了JWT无状态的优势,又解决了主动失效的短板。

5.4 定时任务重复执行:多实例部署必须上分布式锁

系统里的逾期收款提醒用Spring的@Scheduled实现的。单机跑没问题,但如果后面系统扩展成多实例部署,同一时刻两台机器上的定时任务都会触发,短信提醒就会发两遍,开工待办通知也重复生成。

我用的方案是Redis分布式锁。任务执行前先去Redis尝试加锁,加锁成功才继续执行,失败说明有其他实例正在执行。这个锁要设置合理的过期时间,防止实例宕机后锁不释放。用Redisson或手写SETNX都可以,考虑到依赖最少,这里用RedisTemplate的SETNX方式实现,加上过期时间兜底,代码量很少效果也可靠。

定时任务的重复执行问题现在看着不大,但真到了多实例阶段才补救就需要改业务逻辑了。趁着数据量不大、业务规则清晰的时候先做掉,比将来返工要舒服得多。

6. 给准备做同类型管理系统的人:我的几点直接经验

整套系统从开发、部署到后续维护,我最深的感受是——管理系统的技术难度通常不在技术上,而在业务流程的理解上。SpringBoot是成熟的框架,增删改查谁都会写,但装潢公司这种传统行业的管理系统,真正值钱的是对业务的深刻理解。就拿“工地状态机”来说,如果不懂装潢施工流程是水电、泥木、油漆、竣工这样的先后关系,可能就做成一个自由文本字段,那系统就失去了管理意义。

还有一点是要重视部署文档和代码讲解文档。这套系统的源码交付后,是装潢公司内部的技术或外部外包团队做维护,文档写清楚环境要求、部署步骤、模块结构、核心逻辑,能大幅降低二次开发和故障排查的成本。我见过很多项目代码写得不错,但文档缺失,出了问题后面的人接手全靠猜,再好的系统运维体验也上不去。

如果后续要在这套系统上做扩展,我觉得最值得投入的是移动端和流程审批。装潢公司的项目经理和老板大部分时间在工地,手机端查看工地进度、上传照片、审批增项需求是刚需。目前的JWT接口都是现成的,写一个移动端壳子对接就可以,工作重心反而在移动端UI和针对碎片时间的交互设计上。再往前一步,把材料下单和供应商送货流程打通,系统价值还会再翻一层。

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

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

立即咨询