ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

企业级西服定制管理系统:SpringBoot+Vue+MyBatis+MySQL全栈解析

企业级西服定制管理系统:SpringBoot+Vue+MyBatis+MySQL全栈解析 企业级私人西服定制管理系统这个方向关注的人其实比想象中多。一方面是定制行业本身在升级门店老板想把手里的客户、量体、面料、订单管明白另一方面是这套技术栈太经典了——SpringBoot Vue MyBatis MySQL几乎是国内全栈开发的标准配置拿来当实战项目拆解再合适不过。Leabo 这套西服定制管理系统源码就是把定制门店的完整业务链路——客户档案、量体数据、面料库存、定制订单、生产进度、财务结算——全部用这套技术组合落了地。我拿到这类管理系统源码习惯先不看代码先看业务。很多开发者一上来就打开 IDE 翻实体类这是大忌。西服定制不是一个标准的进销存它有很强的行业特性业务模型不捋清楚代码写得再漂亮上线也是给自己挖坑。下面我从业务拆解开始一路讲到数据库设计、后端实现、前端页面、部署排查把这套系统怎么想、怎么写、怎么跑一次讲透。1. 先拆业务西服定制店到底需要管什么1.1 非标品的特殊管理难点定制西服和卖成品衣服完全是两码事。成品衣是标准 SKU进销存搞定一切定制西服是典型的非标品一人一版每件衣服都对应一个具体的人的身体数据。这就带来了几个非常麻烦的问题第一量体数据多且会变。一套西服要量的部位少说十几个胸围、腰围、臀围、肩宽、袖长、衣长、后背宽、领围、大腿围、裤长有的还要量臂围、腕围。而且这些数据不是一成不变的客户半年后回来再定一套体重可能涨了数据全变了历史记录必须留得住不能覆盖。第二生产周期长、环节多。从量体到交付中间要经历面料确认、裁剪、缝制、半成品试穿、修改、整烫、终检、交付随便一个环节出问题返工成本极高。第三面料成本高、库存要准确。好的羊毛面料一米几百上千进货以匹为单位下订单要按米扣减库存差一截轻则补货延误工期重则客诉赔偿。第四定制订单金额高结算必须严谨。一套定制西服动辄几千上万行业惯例是先收定金量体选料后可能收中期款交付时结清尾款。三笔钱分属不同阶段不记录清楚月底对账纯靠记忆必乱。这四点就是 Leabo 这套系统要解决的核心问题。它本质上不是一套简单的客户管理软件而是一条覆盖获客—量体—选料—下单—生产—交付—收款的完整业务链路。1.2 功能板块与数据流这套系统的功能板块我拆成六块来看功能板块核心数据解决的业务问题客户管理基本信息、来源渠道、购买偏好客户档案沉淀老客户复购可查可追量体管理各部位尺寸、量体时间、操作人数据留痕订单关联到准确的体型数据面料管理面料编号、成分、颜色、库存米数库存实时可见下单自动扣减定制订单款式、面料、工期、金额、状态核心业务对象串联所有数据进度管理裁剪、缝制、试穿、整烫等节点记录门店内部协作客户问衣服到哪了有据可答财务结算定金、中期款、尾款的收支记录应收、实收、待收清晰可查数据流的主线是这样的销售录入客户 → 量体师上门或到店量体生成量体记录 → 客户选面料、选款式 → 创建订单关联客户、量体记录、面料生成定金支付记录 → 订单进入生产阶段每完成一个环节写一条进度 → 交付时结清尾款订单状态流转到已完成。这条链路设计得越清晰后面的数据库表结构和后端服务拆分就越简单。我在做类似项目时经常说一句话需求阶段多花一天想清楚数据流开发阶段就能少花三天返工。1.3 角色与权限设计既然是企业级系统角色权限就不能马虎。以西服定制门店为例通常有几类角色店长或销售、量体师、裁缝师傅、财务。销售负责客户录入、订单创建、与客户沟通量体师负责新增量体数据、修改订单的体型信息裁缝师傅主要看生产任务更新进度节点财务只管支付记录、账单、退款店长/管理员全部权限含数据报表、员工账号管理、系统参数配置。权限设计上这套系统可以走经典的 RBAC基于角色的访问控制模型用户表、角色表、菜单表、用户角色关联表、角色菜单关联表。前端根据角色渲染菜单后端在接口层做权限校验。单店场景下不用搞得太复杂但谁改了什么数据最好能留痕所以所有核心表都要有创建人、更新人字段配合操作日志表记录下来。2. 技术选型为什么是 SpringBoot Vue MyBatis MySQL2.1 SpringBoot 作为后端基座技术选型这块我见过太多为了新而选型的项目最后把自己坑进去。Leabo 这套系统选 SpringBoot是非常务实的选择。SpringBoot 最大的价值是带来了约定优于配置的开发模式。传统 SSM 项目光配置文件就好几个Spring 的 XML、MyBatis 的 XML、web.xml全是样板代码。SpringBoot 把内置容器、自动配置、依赖管理全部整合好一个 java -jar 就跑起来部署成本极低。对于西服定制门店这种业务方他们不可能养一个专业运维SpringBoot 的可维护性就特别重要。版本选择上我的建议是如果项目要跑在 JDK 8 上用 Spring Boot 2.7.x如果是新项目、可以接受 JDK 17再考虑 Spring Boot 3.x。Leabo 这套系统如果源码基于 2.7.x你直接用对应版本就行千万不要在没确认 JDK 版本的情况下强行升 3.x——Spring Boot 3 基于 Jakarta EE包名从 javax 改成 jakarta老代码迁移工作量不小。2.2 Vue 做管理端管理端为什么用 Vue 而不是 jQuery 时代的服务端渲染页面核心原因就两个交互复杂度以及团队效率。西服定制管理端虽然不像电商后台那么复杂但订单看板、量体表单、面料选择器、进度时间线这些页面都有大量的状态切换和组件复用。Vue 的组件化开发把量体表单从款式选择器里拆出来各自维护、各自复用Vue Router 做页面路由登录、首页、订单详情、报表一级二级路由清晰可控配合 Element Plus / Element UI 这种现成的组件库表格、弹窗、日期选择器、步骤条全都有开发效率非常高。而且 Vue 打包出来是纯静态资源部署非常灵活——可以直接扔进 SpringBoot 的 static 目录打成单 Jar 包也可以用 Nginx 托管下面我会详细讲这两条路怎么选。2.3 MyBatis 的理由持久层用 MyBatis 而不是 JPA/Hibernate这是很多人问的问题。我的理解是这种管理系统的 SQL 其实相当复杂而且对 SQL 的可控性要求很高。举个例子订单列表要按状态、时间、客户姓名、款式多个条件组合筛选JPA 虽然也能写 Specification但条件一多代码就会变得非常绕。MyBatis 动态 SQL 的优势就在这里where配if每个条件都是可见的 SQL 片段出了问题一眼能定位。另外这套系统会有一些报表类查询比如本月各款式销量面料库存按月消耗这种需要 group by、子查询、甚至多表 join 的统计 SQLMyBatis 写起来没有任何阻碍。如果你用 JPA遇到这类复杂查询还是要回头写原生 SQL等于把简单问题复杂化。关于 MyBatis 和 MyBatis-Plus 的选择我的观点是原生 MyBatis 更稳Plus 更省事。如果源码用的是原生 MyBatis你完全没必要改成 Plus逆向工程的工具多得很Mapper XML 和实体类生成一次就行。而且了解原生 MyBatis 的初始化流程对排查问题很有帮助——比如很多人面试时被问的XMLConfigBuilder 是怎么把 mybatis-config.xml 解析成 Configuration 对象的本质上就是 MyBatis 启动时读配置、构建 SqlSessionFactory 的过程这套源码里你都能看到实际应用。2.4 MySQL 与事务数据库选 MySQL 8.x没什么好争议的。单门店系统的数据量MySQL 的性能绰绰有余而且生态成熟安装、备份、迁移都有大量现成经验。真正要花心思的是事务和并发。定制门店虽然客流量比不上电商平台但同时有两个销售在给不同客户下单恰好都选了同一匹面料这种场景是真实存在的。如果库存扣减不做并发控制最后账面库存一定是错的。这块我放到后面的代码实现里详细讲这里先记住一个原则涉及钱和库存的操作必须在同一个数据库事务里完成。3. 数据库设计把量体、面料、订单、进度串起来3.1 核心表结构清单数据库设计是这套系统的地基我按业务主线整理出了核心表表名作用关键字段customer客户档案id, name, phone, gender, birthday, source, remarkmeasurement_record量体记录id, customer_id, measured_at, operator_id, chest, waist, hip, shoulder_width, sleeve_length, back_width, height, weightfabric面料库id, fabric_no, name, composition, color, weight_gsm, price_per_meter, stock_meterssuit_style款式库id, style_name, category(上衣/马甲/西裤/衬衫), descriptioncustom_order定制订单id, order_no, customer_id, measurement_id, style_id, fabric_id, status, total_amount, deposit_amount, order_date, delivery_date, remarkorder_item订单明细id, order_id, item_type, quantity, unit_price, amountpayment_record支付记录id, order_id, amount, pay_type(定金/中期款/尾款), pay_method, operator_id, pay_timeorder_progress生产进度id, order_id, stage, operator_id, start_time, finish_time, status, remarksys_user系统用户id, username, password, real_name, role_id, statussys_role角色id, role_name, descriptionoperation_log操作日志id, operator_id, module, action, detail, created_at这里有一个设计细节值得强调custom_order 里的 measurement_id 外键指向某一次量体记录而不是冗余存一份量体数据。这样做的好处是量体记录可以独立于订单存在——客户这次来没下单但量了体数据先存着下次下单直接选中最近一次量体甚至可以对比两次量体的差异判断客户体型变化趋势。3.2 量体数据的留痕设计量体数据怎么存是个容易踩坑的点。我见过有人把量体数据做成 JSON 字段存在订单表里理由是反正就门店内部看方便扩展。这个方案短期爽长期痛想做统计、想做历史对比、想针对某个部位筛选全部要解析 JSONSQL 根本没法写。我的建议是独立表加独立字段。上表的 measurement_record 就是这种设计每个量体部位一个字段每次量体生成一条新记录不做原地更新。量体师误操作了怎么办新增一条修正记录或者给原记录加一个作废标记但绝对不要直接改掉历史数据。这个留痕的思路正式一点的说法叫审计追踪在这个行业里就是命根子——客户来一句你上次给我量的是 98怎么这次变成 102 了你能拿历史记录说话这就是专业。3.3 订单状态机与进度表订单状态是整个系统最核心的状态流转。定制西服的订单状态我建议这样设计待确认销售已创建订单等待客户确认款式面料已量体量体数据已关联到订单生产中开始裁剪进入车间待试穿半成品完成约客户到店试穿已完成交付并结清尾款已取消客户取消订单走退款流程每个状态之间不是随便跳的比如待确认不能直接跳到已完成。这里我用一个 status 字段加上 order_progress 进度表来配合status 是订单的当前大状态order_progress 记录的是每一步操作的明细节点和时间。order_progress 表的价值在后期体现得特别明显。门店老板想看这个月有多少单卡在试穿环节没推进一条 SQL 就能查出来客户打电话问我的衣服到哪了前台输入订单号就能看到最新进度节点。没有这张表你的订单状态就是一个孤零零的数字什么都分析不出来。3.4 索引与事务边界索引设计上有几个必建索引的地方customer.phone客户手机号唯一索引防止同一个客户录两条档案custom_order.order_no唯一索引订单号全局唯一custom_order.customer_id status联合索引支撑查某客户的活跃订单这个高频场景order_progress.order_id stage唯一索引保证同一个订单的每个生产阶段只有一条记录。事务边界这里要先明确一件事哪些操作必须放在同一个事务里。我总结为三个必须一致的场景创建订单时必须同时扣面料库存、插入首笔定金支付记录——三者要么全成功要么全失败订单状态变更时必须同时写 order_progress 节点——状态和进度不能对不上退款时必须同时变更支付记录状态和订单状态——账目和订单不能对不上。这些边界如果在 Service 层没设计好就会出现订单建了但库存没扣的脏数据这种问题上线后极难追。4. 后端落地SpringBoot MyBatis 的关键实现4.1 分层与骨架后端工程结构我按市面上主流项目的习惯做了分层com.leabo ├── common // 通用返回结构、异常、工具类 ├── config // 配置类WebMvc、跨域、拦截器等 ├── controller // 接口层只做参数接收与响应 ├── dto // 入参出参对象 ├── entity // 数据库实体 ├── mapper // MyBatis 数据访问接口 ├── service │ └── impl // 业务逻辑 └── enums // 订单状态、支付类型等枚举这个分层的规矩很简单Controller 不写业务逻辑Service 不直接拼 SQLMapper 只负责数据访问。很多人觉得这是老生常谈但实际项目里最容易被破坏的就是这条线。我见过把查询条件拼在 Controller 里的代码后面加一个字段要找半天也见过 Service 里塞了几百行 SQL 的维护起来痛不欲生。架构这东西难的不是设计是坚持。4.2 统一返回与全局异常接口返回结构我用一个泛型包装类RTpublic class RT { private Integer code; private String message; private T data; public static T RT ok(T data) { RT r new R(); r.code 200; r.message success; r.data data; return r; } public static T RT fail(Integer code, String message) { RT r new R(); r.code code; r.message message; return r; } }配合全局异常处理器业务异常全部通过BizException抛出由RestControllerAdvice统一捕获转成R.fail返回。这个模式的好处是前端不用每一个接口都判断错误分支对接口层的代码量大为简化。前端拿到code 200就走成功逻辑否则统一弹 message省心。4.3 下单事务与库存扣减订单创建是这套系统里业务含量最高的一个方法我直接贴核心伪码思路Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto) { // 1. 校验客户 Customer customer customerMapper.selectById(dto.getCustomerId()); if (customer null) { throw new BizException(客户不存在); } // 2. 锁定面料行防止并发扣减库存 Fabric fabric fabricMapper.selectByIdForUpdate(dto.getFabricId()); if (fabric null) { throw new BizException(面料不存在); } if (fabric.getStockMeters() dto.getFabricMeters()) { throw new BizException(面料库存不足当前剩余 fabric.getStockMeters() 米); } // 3. 插入订单订单号由后端生成 Order order buildOrder(dto, fabric); orderMapper.insert(order); // 4. 扣减库存条件里带上库存校验双保险 int rows fabricMapper.deductStock(dto.getFabricId(), dto.getFabricMeters()); if (rows ! 1) { throw new BizException(面料库存扣减失败); } // 5. 生成定金支付记录 paymentMapper.insert(buildDepositPayment(order.getId(), dto.getDepositAmount())); return order.getId(); }这里的关键点是第二步的selectByIdForUpdate对应的 SQL 是SELECT * FROM fabric WHERE id #{id} FOR UPDATEFOR UPDATE的作用是在事务里给这行面料数据加上行级锁。两个销售同时给不同客户下同一匹面料的单时第二个事务会等待第一个事务提交后才继续执行这样库存判断就绝对不会出现两个人同时看到还剩 2 米、结果都下单成功、库存变成负数的情况。扣库存的 Mapper XML 长这样update iddeductStock UPDATE fabric SET stock_meters stock_meters - #{meters} WHERE id #{id} AND stock_meters #{meters} /updatestock_meters #{meters}这个条件就是兜底。哪怕某天有人把FOR UPDATE删了这个 UPDATE 影响行数为 0 时事务也会回滚。事务注解一定要写rollbackFor Exception.class。Spring 默认只对 RuntimeException 回滚如果你捕获了异常或者抛的是 checked exception事务不会自动回滚库存扣了但订单没建成功的情况就会发生。4.4 Mapper 动态 SQL 实战写法订单列表是管理系统里最高频的页面条件筛选特别多。用 MyBatis 动态 SQL 写代码简洁明了select idselectOrderPage resultTypecom.leabo.entity.Order SELECT * FROM custom_order where if teststatus ! null AND status #{status} /if if testcustomerName ! null and customerName ! AND customer_id IN ( SELECT id FROM customer WHERE name LIKE CONCAT(%, #{customerName}, %) ) /if if testbeginDate ! null AND order_date gt; #{beginDate} /if if testendDate ! null AND order_date lt; #{endDate} /if /where ORDER BY id DESC /selectwhere标签会自动去掉第一个 ANDif按条件拼 SQL不用写 11 那种 hack。这个写法几乎是 MyBatis 多条件查询的标准答案值得背下来。这里提醒一个新手易错点XML 里和是特殊字符直接写会解析报错必须用lt;和gt;转义。如果你用的是 MyBatis-Plus可以用Select注解加lt;或者用${ew.customSqlSegment}但原生 XML 里转义这件事躲不开。4.5 缓存怎么用才不出事故MyBatis 的缓存机制很多人只知道概念实际项目里踩坑踩得头破血流。我借着这套系统把话说透。一级缓存是 SqlSession 级别的默认开启。同一个 SqlSession 内执行两次同样的查询第二次直接走缓存。这个在 Spring 里有个陷阱——Spring 管理的 SqlSession 默认每次执行完自动关闭所以一级缓存实际发挥作用的场景很少。真正要注意的是如果在同一个方法里先查后改一级缓存里的旧数据可能会让你看不到刚改完的新值所以不要依赖一级缓存解决任何问题。二级缓存是 namespace 级别的默认关闭要显式配置cache/。Leabo 系统里我对 custom_order、order_progress、payment_record 这些表是明确不建议开二级缓存的。原因很简单订单相关数据变更极频繁一个订单从创建到交付状态要改十几次二级缓存命中率低不说还容易产生脏读。最经典的翻车案例是用户改名了order 关联查询还是旧名字——因为 order 的二级缓存命中了旧数据。这种事故在定制门店里一旦发生客户数据隐私和业务信任都会出问题。真正适合开二级缓存的是 fabric 里的面料字典、suit_style 款式库这类低频变更数据。二级缓存开启后记得在关联查询涉及的 mapper namespace 之间处理好缓存刷新关系否则你精心设计的缓存机制一定会给你上一课。4.6 SpringBoot 配置要点application.yml 里有几个配置我建议你重点检查spring: datasource: url: jdbc:mysql://localhost:3306/leabo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: ${DB_PASSWORD} hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true一个关键点characterEncodingutf8和serverTimezoneAsia/Shanghai这两个参数缺一不可。少了前者中文乱码少了后者时间会差 8 小时。这两个问题是管理系统上线初期最容易出现的两个灵异现象。map-underscore-to-camel-case: true也建议开着这样数据库的stock_meters字段能自动映射到实体的stockMeters属性。如果你不开就得给每个字段写 resultMap累死。开发阶段想打印 SQL加一行配置logging: level: com.leabo.mapper: debug这样 MyBatis 会打印标准输出日志接口排查效率至少翻一倍。生产环境记得去掉不然日志文件会疯长。5. 前端落地Vue 管理端的实现要点5.1 环境与工程结构前端这块开发环境的第一步就是 Node.js 的安装和环境配置这个环节很多人卡住。我建议 Node.js 装 LTS 版本npm 用之前先确认版本node -v npm -v如果 npm 下载依赖慢配置镜像源npm config set registry https://registry.npmmirror.comVue 工程创建可以用 ViteVue 3或 Vue CLIVue 2/3 都可以。Leabo 管理端如果基于 Vue 3强烈推荐 Vite启动和构建都快得多。工程目录结构参考src ├── api // 接口定义 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由 ├── store // 全局状态 ├── views // 页面 │ ├── customer │ ├── measurement │ ├── fabric │ ├── order │ ├── progress │ └── finance ├── utils // 工具类 ├── App.vue └── main.js组件库用 Element Plus表格、表单、弹窗、步骤条、时间线都有现成组件量体表单这种大量数值输入的页面用el-form加校验规则很快就能写得规整。5.2 路由守卫与登录状态管理端的路由设计我的建议是分两层考虑。第一层是基础路由登录页、404第二层是管理页首页、客户、量体、面料、订单、进度、财务、系统设置。路由守卫是必须写的。核心逻辑是没有 token 就跳登录页有 token 但访问了没有权限的页面跳 403。代码思路router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next({ path: /login }) } else if (token to.path /login) { next({ path: / }) } else { next() } })至于动态路由——根据用户角色从后端拉取可访问菜单然后addRoute——这套系统的业务场景用不上。单门店内部系统角色就那么几个静态路由加菜单显隐控制完全够用。动态路由适合那种多租户、多角色、菜单频繁调整的平台类项目不要为了炫技把复杂度拉高。5.3 核心页面量体表单与订单看板量体表单是这套系统里最有行业特征的页面。十几个数值输入框每个都要单位cm、kg、都要校验范围。我建议把量体表单做成独立组件参数就是客户信息和量体记录这样在新增量体和编辑订单关联量体两个场景都能复用。订单看板是另一个核心页面。我建议用两个视图列表视图和卡片视图。列表视图适合批量处理按状态、时间、客户筛选卡片视图适合老板和管理者日常盯盘每张卡片显示客户姓名、款式、面料、当前状态、预计交付日期状态用不同颜色标签区分。配合 Element Plus 的el-tag一眼就能看出哪些单子在哪个环节积压了。5.4 Axios 封装与拦截器管理端所有接口调用我都会封装一个统一的 Axios 实例。核心逻辑是请求拦截器加 token、响应拦截器统一处理业务码和错误import axios from axios import { ElMessage } from element-plus import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.message || 请求失败) return Promise.reject(error) } )这样做的好处是页面里不用每个接口都写错误处理。比如订单列表请求失败页面代码只需要关心数据返回后怎么渲染网络异常、鉴权失败这些杂事全部在拦截器里消化掉了。6. 部署、联调与常见问题排查6.1 MySQL 环境准备部署第一步是准备 MySQL。安装 8.0 版本时有几个坑是几乎人人都踩的第一认证插件问题。MySQL 8 默认使用caching_sha2_password老版本 JDBC 驱动不认识连接就报错。解决方案是用新版驱动8.x 的mysql-connector-j同时在 JDBC URL 里加上allowPublicKeyRetrievaltrue。第二SSL 连接问题。如果你看到类似 SSL connection error 的报错通常是因为连接串没配useSSLfalse。本地开发环境完全没有必要走 SSL 加密把它关了最快。第三建库字符集。建库时一定要指定 utf8mb4CREATE DATABASE leabo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;导入 SQL 文件mysql -uroot -p leabo leabo.sql如果 SQL 文件里有中文字符导入前确认命令行客户端的编码一致否则容易出现乱码进库的问题。6.2 后端打包与配置外置后端打包SpringBoot 项目用 Mavenmvn clean package -DskipTests打出来的 Jar 包含内置 Tomcat直接跑java -jar leabo-server.jar --spring.profiles.activeprod生产环境配置我建议走配置外置的思路。数据库密码、短信密钥这些敏感信息不要写死在application-prod.yml里用环境变量注入。前面给的password: ${DB_PASSWORD}就是这个意思部署时在系统环境变量里设置好DB_PASSWORD即可。这样即使代码仓库泄露数据库也不会直接暴露。6.3 前端构建与放进SpringBoot前端构建npm run build产物在dist目录。这里有两种部署路径取决于你的实际场景。路径一Vue 打包放进 SpringBoot。把dist里的文件全部拷到 SpringBoot 项目的src/main/resources/static目录下重新打包一个 Jar 就同时包含前后端。前端请求/api开头的接口正好命中后端的 Controller。这种方式的优点是部署成本极低门店一台服务器一个进程全搞定特别适合中小型定制门店。路径二Nginx 托管静态文件反向代理 API。Nginx 配置核心就两段location / { root /var/www/leabo; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; }try_files $uri $uri/ /index.html这行是解决 Vue Router history 模式刷新 404 的关键——前端路由在浏览器里看着是/order/detail/123但服务器上并没有这个物理文件如果不回退到 index.html一刷新就 404。这种方式适合后续要做负载均衡、多门店部署、前后端分离扩展的场景。6.4 开发排查高频问题速查表最后把我在开发这类系统时遇到过的高频问题整理成一张表都可以直接照着排查现象常见原因处理方式数据库连接失败/报 SSL 错误JDBC URL 缺参数加useSSLfalse和allowPublicKeyRetrievaltrue时间差 8 小时连接串没指定时区URL 加serverTimezoneAsia/Shanghai查询结果 null 但 SQL 正常驼峰映射没开启map-underscore-to-camel-case: true中文乱码字符集不一致数据库、连接串统一 utf8mb4事务没回滚方法内部调用 this.method() 或异常被吞代理调用、抛出 RuntimeException订单状态被覆盖并发修改同一订单加乐观锁 version 字段或状态校验Vue 刷新页面 404history 路由没有回退Nginx 配try_files端口被占用上次进程没杀干净netstat -ano/lsof -i:8080查 PID 后杀掉Maven 依赖下载慢默认中央仓库慢配置阿里云镜像SpringBoot 版本太高跑不起来JDK 版本不匹配先确认 JDK 版本再选 Boot 版本顺手说一个排查技巧接口出问题先看后端日志里的 SQL 打印再在 Navicat 里手动执行一遍同样的 SQL。如果 SQL 没问题、接口数据不对那问题基本在实体映射或参数绑定上如果 SQL 都不对那就是 MyBatis 动态 SQL 拼错了。这个思路能过滤掉八成的问题。这套 Leabo 系统跑起来之后我最深的体会反而不在技术上。量体数据留痕这个设计看着只是每次量体一条记录这样一句话实际落地之后门店的运营逻辑全变了师傅不用翻本子客户换人接待也能无缝衔接店长看一眼进度表就知道哪批单子要催。做管理系统技术选型、代码架构当然重要但真正产生价值的永远是先把业务状态梳理清楚——状态不闭环系统就是个高级 Excel状态闭环了系统才是业务本身。最后再分享一个小技巧订单状态不要只存一个数字把每一次状态流转都写进 order_progress 表。等系统跑两三个月你就能基于这些数据做各环节平均耗时面料用量趋势这类分析。到时候你就会发现当初多写一张表、多记一条进度是整件事里性价比最高的一个决定。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进