1. 项目概述:企业级物资采购销售管理系统
这个基于微服务架构的物资管理系统,是我去年带队为一家中型制造企业实施的数字化解决方案。系统整合了采购、库存、销售、财务等核心业务流程,采用SpringBoot+Vue+SpringCloud技术栈实现,支持采购经理、仓库管理员、销售代表、财务人员等多角色协同工作。上线后企业采购周期缩短40%,库存周转率提升25%,最让我自豪的是系统成功扛住了双十一期间日均3万+订单的流量冲击。
2. 系统架构设计解析
2.1 微服务拆分策略
我们按照业务边界将系统拆分为6个核心服务:
- 采购服务(purchase-service)
- 库存服务(inventory-service)
- 销售服务(sales-service)
- 财务服务(finance-service)
- 用户中心(user-center)
- 网关服务(gateway)
每个服务独立数据库,通过Spring Cloud Alibaba Nacos实现服务注册与发现。这种设计使得去年客户新增跨境电商业务时,我们仅需新增一个跨境服务模块,不影响原有系统运行。
2.2 技术栈选型考量
后端选择SpringBoot的原因:
- 快速开发:自动配置减少了XML配置工作量
- 嵌入式Tomcat:简化部署流程
- 丰富的Starter:集成MyBatis、Redis等组件只需添加依赖
前端选择Vue.js的优势:
- 组件化开发:便于复用采购单、库存表等业务组件
- 响应式布局:适配PC、平板等多终端
- Vuex状态管理:解决多模块数据共享问题
SpringCloud核心组件:
- Nacos:替代Eureka+Config组合,同时实现服务注册和配置管理
- OpenFeign:声明式服务调用,比RestTemplate更优雅
- Sentinel:实现熔断降级,特别保护促销时段的销售服务
3. 核心业务模块实现
3.1 采购流程闭环设计
典型采购流程的技术实现:
- 采购申请(Vue前端表单校验)
- 供应商比价(Redis缓存供应商报价)
- 生成采购单(Spring事务保证数据一致性)
- 到货质检(WebSocket实时通知质检员)
- 入库操作(分布式锁防止超收)
java复制// 采购单生成核心逻辑
@Transactional
public PurchaseOrder createOrder(PurchaseDTO dto) {
// 校验供应商资质
Supplier supplier = supplierClient.checkValid(dto.getSupplierId());
// 锁定库存预算
budgetService.lockAmount(dto.getTotalAmount());
// 生成采购单号(雪花算法)
String orderNo = IdWorker.getSnowflakeNextIdStr();
// 保存主单
PurchaseOrder order = new PurchaseOrder();
BeanUtils.copyProperties(dto, order);
order.setOrderNo(orderNo);
order.setStatus(PurchaseStatus.APPROVING);
purchaseMapper.insert(order);
// 保存明细
dto.getItems().forEach(item -> {
item.setOrderId(order.getId());
purchaseItemMapper.insert(item);
});
// 发送审批消息
rocketMQTemplate.send("purchase-approve",
MessageBuilder.withPayload(orderNo).build());
return order;
}
3.2 库存管理关键技术
分布式库存扣减方案:
- 采用Redis+Lua脚本实现预扣减
- 通过Seata AT模式保证最终一致性
- 设置库存预警阈值(动态配置实时生效)
库存流水记录设计:
sql复制CREATE TABLE `inventory_log` (
`id` bigint NOT NULL COMMENT '主键',
`sku_code` varchar(32) NOT NULL COMMENT '商品编码',
`change_amount` int NOT NULL COMMENT '变更数量',
`current_stock` int NOT NULL COMMENT '变更后库存',
`biz_type` tinyint NOT NULL COMMENT '业务类型',
`biz_no` varchar(64) NOT NULL COMMENT '业务单号',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_sku` (`sku_code`),
KEY `idx_biz` (`biz_type`,`biz_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4. 多角色权限控制方案
4.1 RBAC模型实现
系统权限设计包含5个层级:
- 菜单权限(Vue路由级控制)
- 操作权限(按钮级控制)
- 数据权限(部门数据过滤)
- 字段权限(敏感字段脱敏)
- 流程权限(审批节点控制)
权限校验流程:
mermaid复制graph TD
A[用户登录] --> B[获取角色列表]
B --> C[加载菜单权限]
C --> D[前端渲染路由]
D --> E[接口访问鉴权]
E --> F[数据权限过滤]
4.2 典型角色功能配置
采购经理:
- 菜单:供应商管理、采购申请、比价单
- 操作:提交审批、撤销申请
- 数据:仅查看所属部门采购单
- 特殊:可设置采购审批流程
仓库管理员:
- 菜单:入库单、出库单、库存查询
- 操作:库存调整、盘点确认
- 限制:无价格查看权限
5. 分布式事务处理实践
5.1 典型业务场景
采购入库事务:
- 采购服务更新订单状态
- 库存服务增加库存数量
- 财务服务生成应付账款
5.2 解决方案对比
| 方案 | 实现复杂度 | 性能影响 | 数据一致性 | 适用场景 |
|---|---|---|---|---|
| 本地消息表 | 中 | 较小 | 最终一致 | 异步通知类 |
| TCC | 高 | 较大 | 强一致 | 资金交易类 |
| SAGA | 中 | 中等 | 最终一致 | 长流程业务 |
| Seata AT | 低 | 中等 | 弱一致 | 常规CRUD |
我们最终采用组合方案:
- 80%场景使用Seata AT模式
- 支付相关使用TCC模式
- 日志类操作使用本地消息表
6. 性能优化关键点
6.1 缓存设计策略
三级缓存架构:
- 本地缓存(Caffeine):缓存用户权限等高频访问数据
- Redis集群:缓存商品信息等热数据
- MySQL:持久化存储
缓存更新策略:
java复制@Cacheable(value = "material", key = "#skuCode",
cacheManager = "redisCacheManager")
public MaterialDTO getByCode(String skuCode) {
return materialMapper.selectByCode(skuCode);
}
@CacheEvict(value = "material", key = "#skuCode")
public void updateMaterial(Material material) {
materialMapper.updateById(material);
}
6.2 数据库优化实例
采购单表分库分表方案:
- 按年份分库(purchase_2023, purchase_2024)
- 按月分表(order_01, order_02,...)
- 使用ShardingSphere实现路由
查询优化示例:
sql复制-- 原始慢查询
SELECT * FROM purchase_order
WHERE create_time > '2023-01-01'
ORDER BY total_amount DESC
LIMIT 100000, 10;
-- 优化后
SELECT * FROM purchase_order o
JOIN (SELECT id FROM purchase_order
WHERE create_time > '2023-01-01'
ORDER BY total_amount DESC
LIMIT 100000, 10) tmp
ON o.id = tmp.id;
7. 安全防护措施
7.1 常见攻击防护
-
SQL注入:
- 全系统使用MyBatis预编译
- 定期执行SQL注入扫描
-
XSS攻击:
- Vue默认转义HTML输出
- 富文本内容使用DOMPurify过滤
-
CSRF防护:
- 关键操作校验CSRF Token
- 敏感接口限制Referer
7.2 审计日志实现
审计日志记录要素:
java复制@Log(title = "采购单删除", businessType = BusinessType.DELETE)
@PostMapping("/delete")
public Result delete(@RequestBody Long[] ids) {
// 业务逻辑
}
// 日志切面实现
@Around("@annotation(controllerLog)")
public Object around(ProceedingJoinPoint joinPoint, Log controllerLog) throws Throwable {
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 记录操作日志
SysOperLog operLog = new SysOperLog();
operLog.setTitle(controllerLog.title());
operLog.setBusinessType(controllerLog.businessType().ordinal());
// 保存日志
asyncService.recordOper(operLog);
return joinPoint.proceed();
}
8. 部署架构与监控
8.1 容器化部署方案
Docker Compose编排示例:
yaml复制version: '3'
services:
nacos:
image: nacos/nacos-server:2.0.3
ports:
- "8848:8848"
environment:
- MODE=standalone
purchase-service:
build: ./purchase
ports:
- "8001:8001"
depends_on:
- nacos
- redis
environment:
- SPRING_PROFILES_ACTIVE=prod
8.2 监控告警体系
监控指标看板包含:
- 微服务健康状态(Spring Boot Admin)
- JVM监控(Prometheus + Grafana)
- 业务指标(ELK日志分析)
- 链路追踪(SkyWalking)
告警规则示例:
- 库存服务TPS > 1000持续5分钟
- 采购单创建失败率 > 1%
- 平均响应时间 > 500ms
9. 项目演进路线
9.1 一期核心功能
- 基础采购流程
- 库存管理
- 多角色权限
9.2 二期增强功能
- 供应商评估体系
- 智能补货预测
- 移动端审批
9.3 三期优化方向
- 引入大数据分析
- 对接ERP系统
- 构建开放API平台
10. 典型问题排查记录
10.1 分布式ID冲突
现象:夜间批量导入时出现主键冲突
原因:多个实例的Snowflake workerId重复
解决:
java复制// 通过Nacos获取动态workerId
@PostConstruct
public void initWorkerId() {
String workerId = nacosConfig.getConfig("snowflake.workerId");
IdWorker.setWorkerId(Long.parseLong(workerId));
}
10.2 缓存穿透问题
现象:查询不存在的商品编码导致Redis压力大
解决方案:
- 布隆过滤器预过滤
- 缓存空值(设置短TTL)
- 接口限流
java复制public MaterialDTO getByCodeWithProtect(String skuCode) {
// 布隆过滤器判断
if (!bloomFilter.mightContain(skuCode)) {
return null;
}
// 查询缓存
String cacheKey = "material:" + skuCode;
MaterialDTO material = redisTemplate.opsForValue().get(cacheKey);
// 缓存空值处理
if ("NULL".equals(material)) {
return null;
}
if (material == null) {
material = materialMapper.selectByCode(skuCode);
if (material == null) {
// 缓存空值5分钟
redisTemplate.opsForValue().set(cacheKey, "NULL", 5, TimeUnit.MINUTES);
} else {
redisTemplate.opsForValue().set(cacheKey, material, 1, TimeUnit.HOURS);
}
}
return material;
}
这个项目让我深刻体会到,微服务架构下数据一致性和系统监控比单体架构复杂得多。建议在类似项目中尽早引入分布式追踪工具,我们是在系统上线后才集成SkyWalking,导致排查早期性能问题非常困难。另外,一定要为每个服务设计独立的降级方案,特别是采购和库存这类核心服务。