Spring Boot ERP进销存系统实战:单据流转与物流管理全解析
发布时间:2026/9/16 5:58:56 作者:尧图编辑部 阅读量:1,286

1. 这块系统到底解决什么问题我是做过几年进销存项目的开发每次看到有人把“进销存”理解成简单的增删改查都想拉着他把完整流程走一遍。这个词看着基础真正落地时涉及采购、销售、库存、财务对账、单据流转、物流追踪每一环都能写一篇文章。这篇想聊的是一个基于 Spring Boot 的 ERP 进销存管理系统重点落在单据流转和物流信息管理上。这个项目定位很明确给中小型贸易或生产企业用把线下靠 Excel、微信群、口头交代的进销存流程搬到线上。它不是一个 Demo而是能跑起来、能接真实业务、能部署到服务器上用的系统。核心功能包括采购入库、销售出库、库存实时查询、单据审核流转、物流单管理、物流轨迹追踪、基础资料维护这几大块。技术上以 Spring Boot 为主线配合 MyBatis Plus、MySQL、Redis、Vue 或 Thymeleaf 做前端整体是一套标准的 Java 全栈应用。适合谁参考两类人最需要一类是做课程设计或毕业设计的学生需要一套完整、能讲清楚业务逻辑的项目另一类是刚入行的 Java 开发想搞明白真实企业项目里单据流转、库存扣减、状态同步这些“看着简单、做起来坑多”的功能是怎么设计的。后面内容我会把核心模块拆开讲包括数据库怎么设计、单据状态怎么流转、物流轨迹怎么存、部署时有哪些坑全部按实战来。2. 技术选型和整体架构的取舍2.1 为什么选 Spring Boot 而不是 Spring MVC 或微服务选 Spring Boot 做这类系统不是因为它火而是因为它在“单体应用”这个层面效率最高。进销存系统本质上是企业内部管理系统并发量不会高到需要微服务拆分但业务流程复杂、表结构多、状态流转多单体架构反而更容易维护。Spring Boot 自动配置解决了传统 SSM 项目里大量 XML 配置的问题。一个进销存项目动辄四五十张表如果用 Spring MVC光配置文件就能让人头大。Spring Boot 可以做到零 XML通过注解和 YAML 配置完成大部分工作。测试和打包也方便内嵌 Tomcat 让本地开发和服务器部署保持一致避免“我本地能跑服务器上跑不起来”这种问题。当然如果业务真的发展到了多个团队协作、系统间独立部署的阶段微服务是下一步的事。但作为起点Spring Boot 单体应用把业务做扎实比一上来就拆服务要稳妥得多。这个项目里模块化做得好一点后期拆分微服务也容易。2.2 核心技术栈和版本选型整个系统的技术选型遵循“主流稳定、资料多、上手快”的原则。后端以 Spring Boot 2.x 为核心这是目前兼容性最好、社区资料最丰富的版本。如果选 3.x需要考虑 JDK 17 和 Jakarta EE 的迁移问题对初学者不够友好。ORM 用的是 MyBatis Plus它比 JPA 更符合国内开发者的习惯SQL 可控性强提供的分页插件、代码生成器对开发效率提升非常明显。数据库选择了 MySQL 5.7 或 8.0。进销存系统对事务要求高采购入库、销售出库这些操作涉及多表更新必须用支持事务的关系型数据库。MySQL 足够满足这类场景配合 InnoDB 引擎行级锁加上事务隔离能有效处理并发问题。缓存和分布式锁用 Redis主要解决库存扣减的并发安全和热点数据的高频查询。前端部分如果项目偏课程设计用 Thymeleaf 配合 Bootstrap 就够了开发效率高如果是商用项目推荐 Vue 3 加 Element Plus 做前后端分离。考虑到标题里含“源码”用哪种前端都能跑通核心是后端的业务逻辑要正确。2.3 项目目录结构和分层方式项目采用经典的分层架构Controller、Service、Mapper再加一个 common 包放通用工具和统一返回结果。这种结构虽然老但最清晰。我把目录结构按模块拆而不是按技术分层来分这样多人协作时冲突少一些。比如module-purchase、module-sale、module-inventory、module-logistics每个模块下面再按 Controller、Service、Mapper 分层。按业务拆模块后期找代码、改需求都更顺手也方便团队里不同的人负责不同业务线。统一的返回结果类ResultT和全局异常处理器GlobalExceptionHandler是必备的。前端拿到返回结果后统一处理状态码后端异常也统一走全局拦截不会出现“一个接口返回 JSON另一个接口直接给 500 错误页面”的情况。分页请求统一用PageResult封装封装了总条数、当前页数据、页码和每页大小所有列表接口返回结构一致。3. 进销存核心模块和数据表设计3.1 五大核心模块的功能边界进销存系统的核心模块可以划分为基础资料、采购管理、销售管理、库存管理和财务管理五部分。基础资料包括供应商、客户、商品、仓库、计量单位等基础数据是所有业务单据的数据源头。采购管理从采购订单开始到采购入库结束中间涉及收货、质检、入库确认整个流程。销售管理则从销售订单出发经历出库、发货、签收几个节点。库存管理负责出入库记录、实时库存查询、库存预警、盘点调整是整个系统的数据中枢。财务管理主要处理应收应付、往来对账、成本核算这部分可以从简但不能完全没有。各模块之间通过单据关联。比如一张采购订单审核确认后可以生成采购入库单一张销售订单审核后可以生成销售出库单。单据之间的上下游关系以及单据状态的一致性是系统设计中最需要重视的地方。3.2 核心数据表设计思路数据库设计决定系统能撑多大业务量也决定后续开发能少踩多少坑。先看最核心的库存表inventory它至少要包含商品 ID、仓库 ID、当前库存数量、锁定库存数量、库存预警阈值这几个关键字段。梳理一下关键状态和关键字。库存字段的表结构考虑这样一个设计字段名类型说明idbigint主键product_idbigint商品IDwarehouse_idbigint仓库IDavailable_qtydecimal(18,2)可用库存locked_qtydecimal(18,2)锁定库存total_qtydecimal(18,2)总库存warning_linedecimal(18,2)预警阈值锁定库存这个字段在很多简单课程设计里没有但在真实业务里非常重要。销售订单审核后未出库前这部分货被占用需要锁定订单取消或出库完成后再释放锁定库存。没有这个字段会出现超卖情况——一张采购订单把最后 5 件货都下单了另一张订单还能下 5 件。单据主表purchase_order和sale_order采用主从表结构。主表记录单号、供应商或客户 ID、单据日期、总金额、状态从表记录商品明细、数量、单价、金额。主表和从表通过order_id关联事务里同时保存明细没有主子表概念的单据是不完整的。单据状态字段建议用整数枚举值而不是字符串这样查询效率高状态扩展也方便。比如 0 草稿、1 待审核、2 已审核、3 已完成、4 已关闭比waiting_audit、audited这种字符串更简洁高效。物流相关表后面单独讲它和订单主表是松耦合关系更灵活也更容易对接外部物流系统。3.3 商品多单位和成本价格的处理商品信息表需要注意多单位问题。一件商品可能有“箱”和“个”两种单位箱和个之间有固定的换算比例。表设计上要加basic_unit、purchase_unit、sale_unit和unit_ratio字段。比如进货用箱销售用个比例是 1 箱等于 20 个。如果不做换算库存很容易出现小数盘点和对账特别麻烦。成本价格建议用移动加权平均法计算每次采购入库后更新商品的平均成本。计算方式是(原库存金额 本次入库金额) / (原库存数量 本次入库数量)。很多项目用先进先出或月末一次加权平均但代码实现复杂度高不少。移动加权平均计算简单适合多数中小企业的业务场景。涉及负数库存的情况要单独控制成本跑不通的问题很大程度上是负数出入库没有做校验导致的详细的分析在问题排查那一节展开。4. 单据流转的核心设计和状态机实现4.1 单据流转全链路流程没有单据流转概念的进销存系统本质上就是个“录入数据的网站”。真正的业务流转是采购员创建采购订单主管审核仓库收货入库财务核对发票最后付款。在这个项目里采购单的状态流转设计为草稿待提交待审核已审核部分入库已完成已关闭。销售单状态流转为草稿待审核已审核部分出库已出库已签收已关闭。每一步操作只能由具备对应权限的角色执行。在代码层面状态流转通过状态枚举加合法的nextStatusMap来限制不在状态机定义范围内的流转一律直接抛异常。这样做的好处很明显第一数据不会因为人为操作变成“死数据”第二审计追踪容易每一步都有操作记录和时间第三流程调整时只需要改状态机定义不用改业务代码。单据状态不要用 int 存完就不管建议建一张order_flow_log日志表记录从哪个状态变成哪个状态、操作人是谁、操作时间、备注这样才能追溯业务全过程。4.2 单据编号生成机制单据编号一旦生成就不允许修改后续所有环节都靠这个编号关联。我在项目里没有直接用数据库自增 ID 当单号而是单独实现了一套编号生成器。规则是前缀加日期加序列号比如CG20250115001采购订单、XS20250115001销售订单、RK20250115001入库单。这样做的好处是只是看单号就能知道业务类型和日期人工作业也方便查找。序列号用 Redis 的自增命令实现如果环境里没有 Redis可以用 MySQL 的SELECT FOR UPDATE或乐观锁机制保证并发下单号不重复。具体做法是每天零点或者首次使用时在当前日期下插入一条当日序列记录然后用UPDATE 序列 SET current current 1 WHERE 日期 当日的原子操作获取最新序列号。做课程设计的话用数据库唯一索引加锁已经足够不强制上 Redis。4.3 库存扣减的并发处理进销存系统最怕高并发下库存扣多或扣错这类问题如果不提前处理做出来的系统根本不敢让客户真实用。我在项目中同时用了 Redis 预扣库存和数据库最终扣减两层方案确保极端情况下数据不错。销售订单创建时先做 Redis Lua 脚本预扣库存防止超卖订单最终取消或出库完成时再同步更新数据库剩余量。真正出库时进入数据库事务对库存行执行SELECT FOR UPDATE行级锁防止多个事务同时修改。卡一个并发细节实际扣减时先锁库存行再更新可用数量和锁定数量最后写明细流水。锁的粒度一定要到具体商品和具体仓库不要锁整个库存表否则并发能力会明显下降。4.4 事务和异常回滚处理事务管理是单据流转环节最重要的保障。特别是采购入库、销售出库这类跨多表操作任何一个环节失败前面操作的数据必须全部回滚。Spring Boot 里用Transactional注解实现声明式事务但它只在运行时异常时回滚如果你的代码里 try-catch 把异常吞掉了事务不会正常回滚这是最容易踩的坑。我建议事务方法内部不要捕获异常并吞掉而是向上抛出让 Spring 统一处理。如果确实需要捕获后做补偿操作不要直接返回成功要标记事务setRollbackOnly或者在 catch 里重新抛出RuntimeException。另外还要注意Transactional的失效场景方法不是 public 的、同一个类内部方法互相调用不走代理、类没有被 Spring 管理这些都是常见的失效原因。5. 物流信息管理模块的实现5.1 物流模块的定位和业务边界这个系统的“物流信息管理”模块在很多课程设计里只是建一个表存物流单号但真实业务需要的远不止这些。完整的物流模块要解决三个问题发货单与订单的关联、物流轨迹的记录与管理、物流状态的同步与展示。它还要预留与顺丰、中通、圆通等外部物流平台对接的接口做到一键查询物流状态。物流模块与销售模块的业务流程是销售出库单完成后系统自动生成发货单物流人员维护物流公司和运单号然后通过接口推送物流系统定期同步物流轨迹。物流轨迹数据量大、增长快可以单独建表存储不需要每次都通过查询物流平台获取。5.2 物流相关表设计和节点状态跟踪物流轨迹建议采用两级表结构第一级是logistics_order物流订单表记录物流单号、承运商、发货仓库、收货人信息第二级是logistics_track物流轨迹表记录每一次状态变化比如已揽收、运输中、派送中、已签收。logistics_track表的时间字段记录节点发生时间默认当前时间防止因为异步同步导致的时间不准。物流状态以最新一条轨迹为准匹配物流公司的状态编码与系统内部状态编码。轨迹失效时间的处理也要注意超过一定时间没有新轨迹系统可以发出预警提示以便人工介入。状态编码我整理了一份常见的映射关系可以参考一下系统内部状态含义物流公司常见接口状态0待发货无1已揽收ACCEPT2运输中TRANSPORT3派送中DELIVERING4已签收SIGNED5异常EXCEPTION / TIMEOUT5.3 与快递鸟、快递100等 API 的对接思路如果需要对接第三方物流查询接口流程一般是商户注册快递鸟或快递100账号申请 API 密钥配置订阅推送地址系统将运单号推送至第三方平台平台通过回调接口推送轨迹数据。项目里要预先写好回调接口定义好接收参数的数据结构。需要注意回调接口的幂等性同一个快递单号、同一条轨迹可能因为网络重试推送多次必须通过物流单号加轨迹节点时间做唯一判断重复数据直接忽略。签名也很关键。第三方平台通常会要求接口请求带签名签名的计算规则各行各业有差异不要硬编码在业务代码里建议封装在独立的签名工具类中。对接过程中最容易碰到的问题是回调地址外网无法访问、推送数据格式对不上、物流单号过长数据库存不下。物流单号字段我建议直接定成 varchar(64)因为快递面单号位数在逐渐变长留足余量没有坏处。5.4 物流轨迹页面展示前端展示物流轨迹时不建议直接把数据库里的记录列表搬上去更合理的做法是按时间倒序排列并把每个节点归类到“已揽收、运输中、派送中、已签收”这几个大节点中。页面级联显示一条时间线每个节点显示时间和描述信息。在数据库取轨迹时一次性查出物流单对应的所有轨迹记录按时间正序返回然后在内存里做分类和格式化避免前端做二次请求。需要注意的是匿名收货信息展示已签收节点或签收人姓名需要做脱敏比如显示“张先生”中间几位用星号代替这个细节能在实际项目验收时加分也更安全合规。6. 实操过程从零搭建并跑通核心流程6.1 项目初始化和基础框架搭建我用 Spring Initializr 或直接在 IDEA 里创建 Spring Boot 项目Java 8 或 Java 11 版本都可以Spring Boot 版本选择 2.7.x。依赖项要引入spring-boot-starter-web、mybatis-plus-boot-starter、mysql-connector-java、spring-boot-starter-validation、spring-boot-starter-data-redis、lombok。application.yml配置里数据源部分推荐开启 SQL 日志方便开发阶段排查问题。Redis 配置设置合理的超时时间避免因连接池耗尽导致系统假死。这里有一段基础的配置参考spring: datasource: url: jdbc:mysql://localhost:3306/erp_stock?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 timeout: 3000ms lettuce: pool: max-active: 20 max-idle: 10 min-idle: 5 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0MyBatis Plus 配置了逻辑删除字段这样删除单据时不会物理删除对审计追责非常有价值。逻辑删除字段建议每个业务表都有统一叫deleted默认值 0删除后为 1。需要担心的是查表时记得所有 SQL 自动带deleted 0条件MyBatis Plus 已经处理了这点。6.2 核心业务代码实现示例库存扣减是订单创建里的核心环节下面的代码展示了基础实现思路需要注意事务边界和锁的粒度。Service public class StockService { Resource private InventoryMapper inventoryMapper; Resource private StockFlowMapper stockFlowMapper; Transactional(rollbackFor Exception.class) public void deductStock(StockDeductRequest request) { // 1. 锁定库存行防止并发同时修改 Inventory inventory inventoryMapper.selectForUpdate( request.getProductId(), request.getWarehouseId()); if (inventory null) { throw new BizException(库存记录不存在); } // 2. 校验可用库存是否充足 if (inventory.getAvailableQty().compareTo(request.getQty()) 0) { throw new BizException(可用库存不足); } // 3. 扣减可用库存增加锁定库存 inventory.setAvailableQty(inventory.getAvailableQty().subtract(request.getQty())); inventory.setLockedQty(inventory.getLockedQty().add(request.getQty())); inventoryMapper.updateById(inventory); // 4. 写库存流水方便追溯 StockFlow flow new StockFlow(); flow.setProductId(request.getProductId()); flow.setWarehouseId(request.getWarehouseId()); flow.setChangeQty(request.getQty()); flow.setFlowType(LOCK); flow.setOrderNo(request.getOrderNo()); stockFlowMapper.insert(flow); } }实现时有几个关键点值得留意。selectForUpdate必须是SELECT ... FOR UPDATE语句它在事务提交前锁住这行记录其他请求只能等待。所谓“先到先得”可在一定程度上避免超卖。第二步的校验很关键库存不足时直接抛业务异常并让事务回滚避免用户看到的扣减结果和实际数据不一致。写库存流水是很多项目会漏掉的一步没有流水后期库存对不上账时基本没法查。6.3 主从表订单保存逻辑保存采购订单或销售订单时主表和明细表必须在一个事务里完成。下面以采购订单为例展示核心逻辑Transactional(rollbackFor Exception.class) public Long createPurchaseOrder(PurchaseOrderRequest request) { // 1. 校验明细非空 if (CollUtil.isEmpty(request.getOrderItems())) { throw new BizException(订单明细不能为空); } // 2. 生成订单主表数据 PurchaseOrder order new PurchaseOrder(); order.setOrderNo(orderNoGenerator.generate(CG)); order.setSupplierId(request.getSupplierId()); order.setOrderDate(request.getOrderDate()); order.setStatus(OrderStatus.DRAFT.getValue()); order.setTotalAmount(calculateTotalAmount(request.getOrderItems())); purchaseOrderMapper.insert(order); // 3. 批量插入明细数量和金额都从数据库计算 for (PurchaseOrderItem item : request.getOrderItems()) { item.setOrderId(order.getId()); item.setAmount(item.getPrice().multiply(item.getQty())); purchaseOrderItemMapper.insert(item); } return order.getId(); }第三步的金额不允许前端直接传应该由后端根据单价乘以数量计算。这样做是为了防止有人通过篡改请求参数改价格造成对账问题。明细的增删改建议在订单处于草稿状态时才允许已经审核的单据如果要改只能通过“红冲”或“作废重开”的方式处理这在财务合规上是有硬性要求的。业务上可以灵活一点但账务上必须留痕。6.4 部署时数据库初始化和权限账号配置项目跑起来之前要先建数据库并执行建表 SQL。开发环境可以用 MyBatis Plus 的自动建表功能但生产环境强烈不建议手写 SQL 脚本管理表结构更可控。MySQL 字符集用utf8mb4因为商品名称、客户名称可能会包含生僻字或特殊符号utf8mb4比utf8mb3兼容性更广emoji 也能存避免写入报错。常用配置在这段脚本里CREATE DATABASE IF NOT EXISTS erp_stock DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER erp_userlocalhost IDENTIFIED BY erp_password; GRANT ALL PRIVILEGES ON erp_stock.* TO erp_userlocalhost; FLUSH PRIVILEGES;不建议用 root 账号连接业务库最小权限原则在项目初期就要建立。创建专用账号并只授权当前项目数据库权限控制在 SELECT、INSERT、UPDATE、DELETE、CREATE、INDEX 就够用。日志单独表在前审计时更高效。7. 常见问题排查和避坑技巧7.1 库存数据跑不通的原因分析这里的“数据跑不通”指的是系统可以操作但最终库存余额、成本金额与出入库流水对不上月末做账时账实不符。结合多数项目实践常见原因按概率高低排序如下首先是负数库存未拦截。采购单还没到货出库单已经审核导致库存被扣成负数后续再怎么补入库都对不上账。正确做法是出库时严格校验可用库存不允许负库存出库特殊业务确需允许负库存的系统要有独立的负库存出库权限控制并且每天生成负库存监控报表。其次是并发导致丢失更新。两张出库单同时读取到当前库存为 10各出库 8程序依次更新后更新的覆盖先更新的最终库存可能变成 2 而不是 -6 或者更离谱。解决方法是行级锁配合版本号乐观锁保证更新是原子的。还有单据状态与库存操作不一致。一张入库单在事务里已经扣减了库存但单据状态没有更新为已完成用户以为没入库又补录了一张导致重复入库。解决办法是把库存更新和单据状态更新放在同一个事务里参考 6.2 和 6.3 的代码模式两者要么同时成功要么同时失败。移动加权平均成本计算错误也容易造成成本跑不通。每次入库都要重新计算平均成本成本金额的精度要保留至少 4 位小数最后金额显示时再四舍五入到 2 位。否则角度偏差累计下来月底成本对账差异会很明显。7.2 单据状态不一致的处理单据状态不一致的典型表现是界面显示已审核数据库里还是待审核或者已出库单重新被提交审核系统报错。这类问题多是由接口重试、定时任务重复执行或前端重复提交引起。前端的保存按钮在请求发送后没有进行 loading 禁用用户连续点击多次就会创建多张相同内容的订单。解决方式是前端按钮防抖加后端幂等校验。后端可以用“请求唯一键”字段前端提交时生成一个 UUID后端根据这个 UUID 做防重判断已经处理过的直接返回上一次结果。定时任务同步物流轨迹时如果第三方接口超时重试同一批轨迹可能被写入多次。解决方式是在logistics_track表上建立联合唯一索引由物流单号加节点时间加节点描述组成重复数据插入直接冲突数据库层拦截住。7.3 事务失效和数据库连接池耗尽事务失效问题在前面的 4.4 节提过这里再说一个容易被忽视的场景Transactional注解加在 Controller 或同一类的私有方法上。Spring 事务是默认基于 AOP 代理的只有通过代理对象调用时才能拦截同类内部this.method()调用不会经过代理事务注解就形同虚设了。事务方法必须是 public且必须通过 Spring 注入的对象来调用。连接池耗尽的问题在高并发场景下容易出现。如果业务接口内部调用了第三方物流 API而且把整个流程包在一个大事务里数据库连接被事务长时间占用加上第三方接口响应慢连接池很容易被打满。解决方法是第三方接口调用放到事务方法外部或者使用编程式事务把事务边界控制得更精确。经验做法是事务只包裹数据库操作部分外部的远程调用、消息推送等不要放在事务内执行。7.4 物流回调接口和轨迹推送的坑物流回调接口是用钉钉或企业微信做消息推送时最容易踩的坑回调 URL 必须在公网可达且返回的响应格式要符合第三方要求。快递鸟的订阅推送一般是 POST 请求回调返回 JSON 格式。失败后第三方会重试推送幂等性保障一定要做避免轨迹重复入库。另一个坑是物流状态编码不一致。快递公司返回的节点描述五花八门“已签收”可能存在多种表达比如“签收成功”“已代收”“已妥投”。实现上不能完全依赖第三方编码要在系统内部维护一套关键词匹配规则比如描述包含“签收”或“妥投”就映射为已签收状态。关键词匹配规则定期更新因为快递公司的描述文案会变化。8. 项目上线前需要补充的能力8.1 权限控制和操作审计进销存系统的权限设计不能只停留在“菜单可见不可见”的层面还必须做数据权限和操作权限的细分。比如采购员只能看自己创建的订单销售经理能看本部门所有客户的订单财务只能看已审核单据的应付和应收数据。我在项目里用 RBAC 模型用户-角色-权限实现菜单和数据权限控制操作按钮也是归属权限点。所有关键操作都要记录审计日志包括单据审核通过、驳回、作废、库存调整、价格修改。审计日志不能只简单记录操作人是否做了这些动作要保存操作前快照和操作后快照便于对出问题的人或流程做复盘。进销存的往来资金是企业的命脉如果审计这块做了足够完善的设计项目在评优和实施验收时会有明显加分。8.2 报表统计和经营看板一个完整的 ERP 进销存系统不能没有报表。销售统计报表、采购统计报表、库存周转率报表、月度毛利统计报表这些是用数据的眼光看企业运营。报表建议直接用 SQL 聚合查询加定时任务汇总的方式实现。如果业务数据量不大实时查询完全可以如果数据量上去了可以增加汇总表和 Redis 缓存。经营看板上展示今日销售额、今日采购额、库存总量、库存预警数等关键指标。这些指标是给老板和管理层看的需要在一个页面上尽可能清晰地呈现当天经营情况。做看板时不是指标越多越好挑四到六个核心指标先让数字“活”起来后续再按需增加。8.3 Docker 部署和备份策略部署这块我强烈建议用 Docker Compose 编排整个环境。以下配置把 MySQL、Redis、后端服务一键拉起来相比手动安装环境效率和可维护性高不少version: 3 services: mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: erp_stock ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql redis: image: redis:6 ports: - 6379:6379 app: build: . ports: - 8080:8080 depends_on: - mysql - redis environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/erp_stock?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai SPRING_REDIS_HOST: redis生产环境日志要用 JSON 格式输出方便接入日志平台和链路追踪。MySQL 每天执行备份脚本是底线保留最近 7 天的备份文件。数据库可以用全量备份加 binlog 增量日志的配套方案既能把恢复时间控制在可接受范围也能防范误操作。备份这一步不要省略真出了事故才知道备份比功能开发都值钱。9. 最后分享一点项目心得做完一套进销存系统回头看的体会是这类项目最大的难点不在某个单独的技术点而在把多个模块的关联状态理顺。采购、销售、库存、物流一个环节的数据错了下游全乱。开发过程中最值得投资的精力不是前端页面做得多炫而是把单据状态机、库存流水、操作日志这三块地基打牢。如果这个项目后续要扩展优先级建议是先把报表和经营看板做起来然后是财务模块的应收应付和成本核算最后才是移动端适配或微服务拆分。能跑通核心业务闭环的进销存系统比堆砌一堆炫技技术的可维护系统要实在得多。我个人在实际操作中的体会是做这类系统的过程本质上是把企业业务流程翻译成数据流转的过程。翻译得越贴近真实业务系统上线后的阻力就越小。建议各位在动手前找一位做贸易或者生产管理的朋友聊一下业务流程哪怕只聊一个下午得到的业务认知也比闷头写代码强得多。