基于spring boot的ERP进销存管理系统 单据流转 物流信息管理系统源码 物流信息系统 超市进销存管理系 药品管理系统 系统设计与开发 SSM框架、Java开发、vue开发、B/S架构、Java项目 亮点单据之间有关联可以实现单据的流转 前后端分离 本系统功能包括单据之间的流转、销售单单入库单出,库单采购申请单采购单等六大单据、库存管理、商品管理、商品分类、客户管理、供应商管理、商品客户供应商优先级排序、系统给多个用户管理员设置权限管理等 前端方面也进行了很多细节设计具体包括透明背景、信息提示框、按钮的提示语、关联单据只可选择一次选择过的会变灰不可选择的会隐藏等 会本人70页、2.3w字的程度报告作为使用参考最近在搞一个ERP系统开发发现单据流转这个功能坑是真的多。就拿采购单变入库单来说要是没处理好关联关系库存数据分分钟错得亲妈都不认识。今天就拿我们团队用Spring Boot撸的进销存系统举个栗子看看怎么让单据手拉手跳转不翻车。先看后端怎么玩关联单据。核心逻辑是给每个单据实体加个前任字段像这样Entity Table(name procurement_order) public class ProcurementOrder { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; OneToOne JoinColumn(name related_purchase_apply_id) private PurchaseApply relatedApply; // 关联的采购申请单 // 其他字段省略... } Service public class OrderService { Transactional public void convertToStockIn(ProcurementOrder order) { StockIn stockIn new StockIn(); stockIn.setSourceOrderId(order.getId()); stockIn.setSourceOrderType(OrderType.PROCUREMENT); // 复制商品明细等操作... stockInRepository.save(stockIn); order.setStatus(OrderStatus.CONVERTED); procurementOrderRepository.save(order); } }这里用JPA的OneToOne直接绑定采购申请单转换时把原单ID和类型都塞到新单据里。注意事务注解要加不然转换到一半崩了可就热闹了。前端防手贱设计才是真考验。Vue组件里控制关联单据选择选过的直接变灰template el-select v-modelselectedOrder :disabledisDisabled changehandleOrderSelect el-option v-fororder in availableOrders :keyorder.id :labelorder.code :valueorder.id :disabledorder.used /el-option /el-select /template script export default { computed: { availableOrders() { return this.orders.map(order ({ ...order, used: this.selectedOrders.includes(order.id) })) } } } /script这里用computed属性动态计算可选单据已经关联的直接打上禁用标记。注意要深拷贝原始数据不然会污染原始订单数据。基于spring boot的ERP进销存管理系统 单据流转 物流信息管理系统源码 物流信息系统 超市进销存管理系 药品管理系统 系统设计与开发 SSM框架、Java开发、vue开发、B/S架构、Java项目 亮点单据之间有关联可以实现单据的流转 前后端分离 本系统功能包括单据之间的流转、销售单单入库单出,库单采购申请单采购单等六大单据、库存管理、商品管理、商品分类、客户管理、供应商管理、商品客户供应商优先级排序、系统给多个用户管理员设置权限管理等 前端方面也进行了很多细节设计具体包括透明背景、信息提示框、按钮的提示语、关联单据只可选择一次选择过的会变灰不可选择的会隐藏等 会本人70页、2.3w字的程度报告作为使用参考权限控制这块有个骚操作用自定义注解拦截器实现Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface DataAuth { String value() default dept; } Component public class DataAuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { Method method ((HandlerMethod) handler).getMethod(); DataAuth annotation method.getAnnotation(DataAuth.class); User currentUser getCurrentUser(); if (dept.equals(annotation.value()) !currentUser.getDept().equals(dataOwnerDept)) { throw new AccessDeniedException(跨部门操作禁止); } return true; } }在Controller方法上加个DataAuth(dept)拦截器自动校验数据归属部门。比写一堆if else清爽多了权限规则调整也方便。库存计算这块有个坑要注意用Redis分布式锁防止超卖public void updateStock(Long productId, Integer quantity) { String lockKey stock_lock: productId; RLock lock redissonClient.getLock(lockKey); try { if (lock.tryLock(3, 5, TimeUnit.SECONDS)) { ProductStock stock stockRepository.findByProductId(productId); stock.setQuantity(stock.getQuantity() quantity); stockRepository.save(stock); } } finally { lock.unlock(); } }Redisson的分布式锁比手撸setnx靠谱注意锁的粒度控制在单品级别别傻乎乎给整个库存表加锁。系统里还藏了不少魔鬼细节。比如采购单转库存单时自动带出供应商信息销售单生成时自动关联客户信用额度校验。这些业务钩子用Spring的EventListener实现解耦EventListener public void handleOrderCreate(OrderCreateEvent event) { if (event.getOrderType() OrderType.SALES) { Customer customer customerService.getById(event.getCustomerId()); if (customer.getCredit() event.getTotalAmount()) { throw new BusinessException(客户信用额度不足); } } }事件驱动设计让核心流程不被各种校验逻辑搞成意大利面条代码。这个项目的坑基本都踩过了配套的2.3万字文档里记录了更多血泪史。比如单据编号生成规则怎么防并发百万级数据量下怎么优化关联查询都是实战中摔出来的经验。代码虽然不敢说多优雅但至少业务跑起来没出过大乱子中小企业的进销存需求基本都能扛得住。