上个月有个做高定西装的朋友来喝茶,说店里手工单、Excel表和微信聊天记录并存,客户来了谁接待、衣服做到哪道工序、上次量体的袖长数据在哪个文档里,全都靠翻手机。其实这不是他一个人遇到的问题,而是整个定制行业的通病。今天分享的这套私人西服定制Leabo信息管理系统,就是冲着这个痛点来的——SpringBoot后端做业务服务,Vue前端做交互界面,MySQL存储数据,源码拿到后配好环境即可运行。我替朋友实际部署过一版,订单查询、量体录入、状态流转这些核心链路都能顺畅走通,既适合直接部署到门店使用,也很适合刚接触前后端分离项目的开发者当一个完整参照。
1. 定制店的信息盲区:这套系统到底在解决什么问题
1.1 客户档案是中心,而不是订单的附属
定制西服的客户数据有个特点:低频、高客单、强关系。一个客户订购一套西装后,可能半年后再来,期间换了手机甚至搬家。如果客户信息只存在于订单表里,下次销售想“查这个客户以前是不是做过驼色面料”就很费劲。项目里单独划出customer档案,保存姓名、手机号、生日、来源渠道、偏好面料、体型备注等基础信息,并通过客户id关联订单和量体记录。接口设计上,销售顾问登录后默认渲染自己名下客户,管理员和店长可查看全量。这样做还有个隐藏好处:回访提醒、生日营销、复购统计都能以客户表为基准,而不是临时扫订单表。
有人会问,客户偏好、微信来源这类信息,放一个JSON字段里不行吗?短时间可以,但一旦想在列表页显示“最近一次量体日期”,或者做“近6个月回头客”统计,关联查询就会变得很别扭。所有稳定字段都应该在客户表上建列,临时备注才适合宽松字段。这个朴素的原则在后期做报表时帮了大忙。
1.2 量体数据不是JSON字段,而是可计算的结构
西服定制最重要的数据不是订单金额,是量体数据。不少早期管理系统喜欢在measure表里加一个body_data JSON,把胸围、肩膀、袖长、腰围一股脑塞进去。这种方案前期很爽,真到后端要执行“找出胸围在95到100之间的客户”时,就得全表扫JSON,MySQL里能用JSON_EXTRACT写,但性能不佳,代码也不好读。
这个项目把量体主表和量体明细表拆分:主表保存customer_id、tailor_id、measure_date、body_remark;明细表保存part_code、base_value、ease_value。part_code是固定字典,例如shoulder、chest、waist、hip、sleeve、cuff、jacket_length、trousers_length、crotch_height。前端传入一组数组,后端批量保存。一次量体二三十个部位,每个部位都包含净尺寸和放松量,结构统一之后,版型师可以按part_code像查字典一样取值。
保存成功后,前端能马上展示“量体记录详情”页面,按部位分组显示历史数据。回看历史时可以比较两次量体的差值,这对体型波动明显的客户特别实用,版型师也能在试衣调整时快速判断变化趋势。
1.3 订单流程不是“待付款/已完成”,而是可追溯的工序节点
普通电商的两三个状态无法描述定制西服。从预约定制开始,一般经历:意向/预选面料、量体、版型制作、胚样试穿、调整、成衣交付、免费返修。中间还可能有面料到店延迟、客户出差改约、胚样不合适返工重做。如果只用一张订单表的status字段,每次只能保存当前状态,操作日志、修改原因都会丢失。
项目里设计了订单状态主表和状态变更日志表。主表保存最新状态即可,日志表记录每次变更的操作人、变更前状态、变更后状态、备注说明。前端订单看板只需查主表保持轻量,后台管理或售后纠纷时直接查日志。这个设计看似多写了一张表,但正式用起来之后非常值得,尤其是店里几个销售同时处理订单的时候,谁在哪个节点改了什么都一清二楚。
2. SpringBoot后端:把定制业务的复杂度装进清晰的模块里
2.1 分层结构:从Controller路径就能看懂定制业务
后端采用常见的分层结构,但包划分比较讲究。按业务域拆包,而不是按技术层堆大包,这样从包名就能看出项目的核心业务。大致结构如下:
com.leabo ├── common # 通用返回、异常、工具类 ├── config # 配置类、安全配置、跨域配置 ├── controller # API接口层 ├── service # 业务逻辑层 ├── mapper # MyBatis数据访问 ├── entity # 数据库实体 └── model # DTO、VO、查询参数Controller层比较薄,订单、客户、量体、面料、用户、报表各自对应一个资源类。接口路径尽量资源化,比如POST /api/customer、GET /api/customer/{id}/measures、POST /api/order/{id}/status。这样前端团队不用翻文档也能猜出接口用途,也方便后接Swagger自动生成在线文档。
项目里有个细节值得抄作业:返回统一封装为Result ,包含code、message、data三个字段。业务异常抛自定义BizException,由全局异常处理器统一转换,省掉了每层重复的try/catch。我见过很多项目一百个接口有一百种返回结构,前端对接起来相当痛苦,统一返回真的能少吵很多架。
2.2 订单状态流转:用状态机而不是随便update
很多后台管理系统会把订单状态做成“前端传什么就改成什么”,开发确实快,但业务会失控。最典型的例子是“已完成”订单被误操作退回“量体中”,或者还没量体就先跳转到“交付”。这个项目在OrderStatusService里维护了一张合法流转映射,代码思路大概是这样的:
private static final Map<OrderStatus, Set<OrderStatus>> TRANSITIONS = new EnumMap<>(OrderStatus.class); static { TRANSITIONS.put(OrderStatus.INIT, Set.of(OrderStatus.FABRIC_SELECT, OrderStatus.MEASURE, OrderStatus.CANCELLED)); TRANSITIONS.put(OrderStatus.MEASURE, Set.of(OrderStatus.PATTERN, OrderStatus.FITTING_1, OrderStatus.CANCELLED)); TRANSITIONS.put(OrderStatus.FITTING_1, Set.of(OrderStatus.FITTING_2, OrderStatus.COMPLETE, OrderStatus.REWORK)); // 其他节点同理 } public void changeStatus(Order order, OrderStatus target, String operator, String remark) { Set<OrderStatus> allowed = TRANSITIONS.get(order.getStatus()); if (allowed == null || !allowed.contains(target)) { throw new BizException("非法的订单状态流转: " + order.getStatus() + " -> " + target); } // 更新主表状态 // 写入状态日志表 }这样既保留灵活性,又不会让状态乱飞。FITTING_1、FITTING_2这种渐变状态能很好应对二次试衣的场景,很多定制店都会经历胚样试穿后调肩、收腰的过程,多一个试衣节点比简单写“进行中”更有业务含义。
2.3 JWT鉴权和三个角色的权限边界
项目里用户角色分为admin(店长/管理员)、tailor(版型师/量体师)、sales(销售顾问)。登录接口/api/auth/login校验用户密码后,用HMAC密钥生成JWT,默认有效期8小时。前端把token存在localStorage,axios请求拦截器统一携带Authorization头。后端通过Spring Security过滤器解析token,放行登录接口和静态资源,其余接口校验角色权限。
这里必须强调:前端隐藏按钮只是体验,真正安全在后端。比如销售顾问调用GET /api/customer/all时,因为接口上标了@PreAuthorize("hasRole('ADMIN')"),请求会被Spring Security直接拒绝,而不是前端不展示菜单就完事。权限粒度不需要细到字段级,但至少要到接口级,否则一个普通店员就能改版型参数,后期很难扯清楚。
3. Vue前端:让量体师和销售都能顺手,界面才真正有用
3.1 量体录入页面:按移动端操作习惯来设计
量体师的工作场景不是在台式机前,而是在门店里、客户家里。如果量体页面只适配电脑,实际使用时会非常别扭。项目里的量体页面特意做了移动优先布局,左边是垂直部位列表,右侧是输入面板。
每个部位用两个input分别填净尺寸和放松量,单位默认厘米。页面内把胸围、腰围、臀围、肩宽这些关键数值做范围校验,比如胸围70到160、腰围50到150、袖长40到80。超出范围时前端立即红框提示,不提交给后端。同样的规则在后端也要做一遍:前端保证体验,后端保证数据可靠。
一次量体通常要连续录十几二十项,录入体验必须减少点击次数。这个项目实现了三个小功能:录完一个部位按回车自动跳到下一项;支持键盘上下键调整数值;支持从上次量体记录一键复制,方便身形稳定的老客户复测。这些细节虽然不起眼,但店面实际用起来,版型师的接受度完全不一样。
3.2 订单看板:状态色块比Excel表格直观多了
订单列表如果只是一张白底表格,销售扫一眼根本分不清轻重缓急。项目里订单看板把需要紧急处理的状态用颜色标识:待量体是黄色,试衣中是蓝色,返修是红色,交付逾期超过5天自动置灰。页面通过Vuex的orderModule保存列表数据,状态修改成功后不整页刷新,只更新store里的对应记录并重新排序。销售接待客户时,扫一眼屏幕就能知道今天哪些订单卡住了,这个体验很实际。
列表查询条件包括客户姓名、手机号、状态、时间范围。这些条件不是在前端做内存过滤,而是拼成参数提交给后端由SQL查询,因为后端的订单表建了联合索引,数据量过万时也不会明显卡顿。如果哪天订单量继续涨,还能平滑切换到分页和懒加载。
3.3 动态菜单、路由守卫和防误触
router.beforeEach里读取本地角色,然后生成动态路由。admin能看到系统管理菜单,tailor能看到量体单和订单管理但看不到客户价格字段,sales看不到版型参数。菜单隐藏只是第一步,真正的数据保护靠后端接口权限控制。项目里还做了一个“离开页面前提示未保存修改”的组件,量体师填写数据时如果误点其他菜单,会弹窗确认,避免半小时的工作消失。这个组件看起来无关紧要,实际使用中救了很多次急。
4. MySQL建模:服装定制行业里几个容易翻车的表结构设计
4.1 尺码体系:用数值字段而不是“合身/宽松”选项
定制店客户的体型差异很大,尤其是高定人群。设计表结构时,量体的每个部位必须用数值字段存储,同时单独用一个放松量字段。成品成衣尺寸可以理解为净尺寸加放松量。放松量要根据面料弹性和客户喜好变化。如果系统只存一个“合身/宽松”的枚举,版型师根本不知道具体改多少。
量体明细表的大致结构可以用下面这段SQL理解:
CREATE TABLE measure_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, measure_id BIGINT NOT NULL COMMENT '量体主表ID', part_code VARCHAR(32) NOT NULL COMMENT '部位编码', base_value DECIMAL(5,1) NOT NULL COMMENT '净尺寸', ease_value DECIMAL(5,1) DEFAULT 0 COMMENT '放松量', sort_no INT DEFAULT 0, UNIQUE KEY uk_measure_part (measure_id, part_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;part_code统一维护成字典,不要随便写“胸围1”“胸围2”这类不稳定的编码。编码稳定之后,前端模板、后端计算、未来可能的智能推荐都能直接复用。
4.2 订单价格用快照,不要实时关联价格表
服装定制行业面料价格波动、加工费随工艺变化是常态。下单时可能某块面料180一米,出货后已经涨到220。如果订单表里只存fabric_id,统计历史订单金额时反复关联当前价格表,最终拿到的数字可能和下单时完全对不上。
项目在订单主表冗余保存了下单时的面料单价、加工费、优惠金额和最终总价。后续价格调整完全不影响历史订单,财务对账时以订单快照为准。报表统计也以订单表为基准,不来回JOIN价格表。这种“交易快照”思想在进销存场景里非常通用,不只适合服装行业。
4.3 外键、索引、字符集这些基础但总被忽略的点
很多练手项目会故意去掉所有外键,只用应用层逻辑保证引用关系。对纯demo可以,但在这个系统里,order表、measure表之间的外键我建议保留,因为业务数据需要强一致,不能允许量体单落到不存在的客户名下。外键会带来一定写入开销,但门店每天几十笔交易的量级完全不是问题。
索引方面,order表建议建(customer_id, status, created_at)联合索引,measure表建议建(customer_id, measure_date)联合索引。这些索引直接服务于订单看板和客户历史量体查询,SQL执行计划能明显不一样。
字符集统一使用utf8mb4,因为客户备注里可能写入生僻字或emoji。排序规则用utf8mb4_general_ci就够用,不需要搞得太复杂。日期字段建议用DATETIME,直接以业务本地时间存储,不要用TIMESTAMP依赖数据库时区,否则部署到不同机房会出现8小时偏移。
5. 从源码到直接运行:环境、部署和跑项目时踩过的坑
5.1 依赖版本别乱配,不是越新越好
拿到源码后第一步不是打开IDEA一顿狂敲,而是先确认本地依赖版本。这个项目基于Spring Boot 2.7系列,如果直接用Spring Boot 3.x,会牵扯到JDK版本、Jakarta命名空间迁移、Spring Security配置变更,改动量会突然变大。
| 依赖 | 建议版本 | 说明 |
|---|---|---|
| JDK | 1.8 或 8+ | Spring Boot 2.7默认兼容 |
| Maven | 3.6+ | 能正常拉取依赖 |
| Spring Boot | 2.7.x | 生产环境稳定 |
| MySQL | 5.7 或 8.0 | 字符集、索引、JSON能力都足够 |
| Node.js | 14到16 LTS | Vue CLI 5兼容性最好 |
| Vue CLI | 4.x或5.x | 前端工程直接使用 |
切记:源码如果是Vue2加Element UI的项目,不要顺手升级到Vue3加Element Plus。能用能跑的稳定版本比什么都强,业务跑通后再考虑技术栈升级。
5.2 数据库初始化:时区、编码、导入顺序
MySQL 8.0连接时,如果JDBC URL不指定时区,经常会报“The server time zone value 'CST' is unrecognized”。解决办法是在连接串加:
jdbc:mysql://localhost:3306/leabo?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai数据库初始化还有一个容易出问题的地方:外键导入顺序。项目里存在外键约束,如果一次性导入全量SQL,部分表因为被引用表还没建好而报错。最稳妥的做法是先用建库脚本创建库,再按依赖顺序依次导入表结构,最后导入初始数据。常用的偷懒方式是在建表脚本前面加SET FOREIGN_KEY_CHECKS=0,导入完成后恢复为1,但要在正式环境操作时多留个心眼。
5.3 前端打包塞进SpringBoot:两种部署路径的个人建议
开发环境用Vue CLI最舒服。在vue.config.js里配置devServer.proxy,把/api代理到http://localhost:8080,前端开发服务器和后端接口可以并行跑,改前端页面还能热更新。
生产环境推荐把前端build产物直接放进SpringBoot的静态资源目录。具体做法是:先在前端项目里设置publicPath为相对路径,避免打包后的js/css路径变成绝对路径导致404。执行npm run build,然后把dist目录下的所有文件复制到src/main/resources/static。SpringBoot启动后,访问http://localhost:8080就能看到前端页面。
这里有个高频问题:Vue Router如果使用history模式,直接在浏览器刷新/order/list这类子路由会404。最简单的解决办法是改用hash模式,生产环境下不用额外配置服务端fallback。对于门店内部系统,hash模式完全够用,还省心。
5.4 我实际部署时遇到的高频报错和排查思路
我把最常见的几个报错列出来,按这个顺序排查通常能解决80%的问题:
- 端口被占用:SpringBoot默认8080,启动报“Web server failed to start. Port 8080 was already in use”。用lsof -i:8080或者netstat -ano | findstr 8080查占用进程,也可以直接改application.yml里的server.port。
- 数据库连接失败:报Communications link failure时,先确认MySQL有没有启动,再检查application.yml里的地址、用户名、密码是否匹配。
- Token过期前端空白:JWT默认8小时过期,前端如果没在axios响应拦截器里处理401,用户第二天打开页面可能一直白屏。建议统一判断code等于401时清除本地token并跳转登录页。
- 跨域问题:开发环境下如果不想用proxy,后端要正确配置CORS允许http://localhost:8080来源。生产环境前后端同端口部署就不需要CORS。
6. 拿来直接用之前的最后思考:二次开发可以优先改哪些方向
6.1 订单号生成规则建议改成自己的
项目默认的订单号生成规则是前缀加日期加流水号,比如LB20250513001。不同店面可能有不同习惯,有人喜欢带门店代码,有人喜欢带工位编号。二次开发时可以把订单号生成逻辑抽成一个独立组件,底层用一张order_sequence表,用数据库唯一索引防止并发重复,避免两个人同时下单生成一样的单号。
6.2 面料库存与采购联动可以补上
系统把面料作为基础档案,保存名称、成分、色号、米价、库存米数。最理想的情况是下单时检查面料库存并锁定可用数量,不足时生成采购提醒。很多定制店虽然是按单采购,但面料库存管理依然有价值,尤其是常用面料频繁使用的时候。二次开发可以加一张面料出入库流水表,记录每次采购入库和订单消耗,这样就知道哪些面料真正动销。
6.3 沉淀版型库和历史量体数据
定制店经营几年后,历史量体数据和版型调整记录是最值钱的无形资产。项目现在把量体数值全部结构化存储了,二次开发时可以增加附件表,把每次试衣照片、版型调整单、客户确认单都按订单和量体主表归档。以后来了新版型师,直接翻历史记录就能快速理解老客户的体型特征和偏好,不用全靠口口相传。
6.4 报表看板是见效最快的改造点
基础版报表模块目前能提供订单状态统计,但店长更想看到量体完成数、订单一周交付趋势、返修率、面料使用排行、客户回头周期。二次开发时可以用ECharts在前端画趋势图,后端提供聚合接口。订单数据量上来之后,建议把统计结果定期写入独立的统计表,不要每次都实时group by大表,否则报表查询高峰期会拖慢主线业务。
最后说点个人的感受。我部署过好几个类似的定制店管理项目,最大的教训永远是:先把量体数据和订单状态的数据结构定稳,再去优化界面和加功能。这套Leabo系统的核心价值,正是把西服定制行业最特殊的两块数据——量体明细和状态流转——用比较规矩的方式沉淀了下来。如果你的下一步刚好就是给定制店做数字化,或者想拿一套前后端分离项目练手,先把它跑通,然后从订单号规则和报表两个口子动手改造,会是比较顺手的路线。