ERP 订单管理系统:Spring Boot + Vue 3 全栈
完整的企业级 ERP 订单管理系统,Spring Boot 3 后端 + Vue 3 前端 + MySQL + RabbitMQ + Redis + Docker。
项目背景
这是一个完整的企业级 ERP 订单管理系统,采用前后端分离架构。前端使用 Vue 3 + TypeScript + Vite + Element Plus,后端使用 Spring Boot 3 + Spring Security + JWT。项目覆盖了企业订单管理的核心流程:订单创建、审批流转、商品管理、库存追踪、用户权限管理。
为什么做这个项目?ERP 系统是企业数字化的核心基础设施。虽然市面上有成熟的 ERP 解决方案(SAP、用友、金蝶),但对于中小企业和定制化场景,一个轻量级的自研 ERP 往往更灵活、成本更低。这个项目就是为此而生。
技术选型
| 组件 | 选择 | 理由 |
|---|---|---|
| 后端框架 | Spring Boot 3 + Java 21 | 最新 LTS,虚拟线程支持 |
| 安全框架 | Spring Security + JWT | 完善的认证授权体系 |
| ORM | MyBatis-Plus | 代码生成、分页、Lambda 查询 |
| 数据库 | MySQL | 成熟的关系型数据库 |
| 前端 | Vue 3 + TypeScript | 类型安全、组合式 API |
| UI 框架 | Element Plus | 企业级 UI 组件库 |
| 构建 | Vite | 极速开发体验 |
| 部署 | Docker Compose | 前后端一键部署 |
架构设计
系统采用经典的前后端分离架构:
前端 (Vue 3 + Element Plus)
│ REST API (JWT Auth)
▼
后端 (Spring Boot)
├── Controller 层
├── Service 层
├── Mapper 层 (MyBatis-Plus)
└── 数据库 (MySQL)
核心模块:
- 订单管理 — 订单 CRUD、状态流转(待审核→已审核→已发货→已完成→已取消)
- 商品管理 — 商品信息、分类、SKU 管理
- 用户权限 — 基于 JWT 的多角色权限控制(管理员/操作员/审核员)
- 库存管理 — 入库、出库、库存盘点
核心实现
Spring Security + JWT 配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
JWT 认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
try {
Claims claims = JwtUtil.parseToken(token);
String userId = claims.getSubject();
String role = claims.get("role", String.class);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userId, null,
List.of(new SimpleGrantedAuthority("ROLE_" + role))
);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.clearContext();
}
}
filterChain.doFilter(request, response);
}
}
订单状态机设计
订单管理中最重要的部分是状态流转。使用枚举定义所有状态和允许的转换:
public enum OrderStatus {
PENDING("待审核"),
APPROVED("已审核"),
SHIPPED("已发货"),
COMPLETED("已完成"),
CANCELLED("已取消");
private final String displayName;
public boolean canTransitionTo(OrderStatus target) {
return switch (this) {
case PENDING -> target == APPROVED || target == CANCELLED;
case APPROVED -> target == SHIPPED || target == CANCELLED;
case SHIPPED -> target == COMPLETED;
case COMPLETED, CANCELLED -> false; // 终态
};
}
}
@Service
public class OrderService {
@Transactional
public void updateStatus(Long orderId, OrderStatus newStatus) {
Order order = orderMapper.selectById(orderId);
if (!order.getStatus().canTransitionTo(newStatus)) {
throw new BusinessException("非法状态转换");
}
order.setStatus(newStatus);
orderMapper.updateById(order);
// 记录操作日志
operationLogService.log(orderId, "状态变更",
order.getStatus().getDisplayName() + " → " + newStatus.getDisplayName());
}
}
前端订单列表(Vue 3 + Element Plus)
<template>
<el-table :data="orders" v-loading="loading">
<el-table-column prop="orderNo" label="订单号" width="180" />
<el-table-column prop="customerName" label="客户名称" />
<el-table-column prop="totalAmount" label="总金额">
<template #default="{ row }">
¥{{ row.totalAmount.toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="statusType(row.status)">
{{ statusMap[row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button size="small" @click="viewDetail(row)">查看</el-button>
<el-button size="small" type="primary"
@click="handleApprove(row)"
v-if="row.status === 'PENDING'">审核</el-button>
</template>
</el-table-column>
</el-table>
</template>
踩坑记录
1. JWT Token 刷新机制
最初 JWT 的过期时间设置较短(30 分钟),用户体验很差,频繁需要重新登录。设置太长又存在安全风险。解决方案:实现了双 Token 机制——Access Token(15 分钟过期)+ Refresh Token(7 天过期),Access Token 过期后自动用 Refresh Token 刷新。
2. 跨域配置(CORS)
前后端分离架构下,前端开发环境(localhost:5173)访问后端(localhost:8080)会遇到 CORS 限制。解决方案:在 Spring Security 配置中添加 CORS 配置,允许开发环境来源。
3. 数据库事务边界
订单创建涉及多个表的写入(订单主表、订单明细表、库存扣减表),部分操作失败会导致数据不一致。解决方案:使用 @Transactional 注解确保整个操作的原子性,并引入乐观锁防止并发扣库存。
4. Element Plus 表格性能
当订单数据量较大(>1 万条)时,前端渲染卡顿。解决方案:后端实现分页查询 + 前端虚拟滚动,限制每页显示 20 条。
总结
这是一个完整的全栈项目,从前端界面到后端 API 到数据库设计,覆盖了企业级应用的完整链路。订单状态机的设计是 ERP 系统中最体现业务深度的部分——好的状态机设计能让代码逻辑清晰、业务边界明确。Spring Security + JWT 的整合是安全的核心,双 Token 机制在安全性和体验之间取得了平衡。
通过这个项目,我完整地实践了"前后端分离 + 企业级权限控制 + 复杂业务状态管理"的全栈开发模式,对 Spring Boot 和 Vue 3 的企业级开发有了深入的理解。