erp-java/docs/幂等性修复报告.md

8.0 KiB
Raw Blame History

幂等性检查与修复报告

任务编号: 2.4 执行时间: 2026-04-05 涉及服务: order-service, inventory-service, logistics-service


一、检查结果

1.1 订单创建 POST /api/orders/pull

  • 检查结果: 无幂等性保护
  • 问题描述: 重复调用 pullOrders / createOrders 会重复创建订单
  • 现有字段: platformOrderSn(平台订单号)存在,但无唯一约束和去重逻辑

1.2 库存扣减 POST /api/stock/deduct

  • 检查结果: 无幂等性保护
  • 问题描述: 网络重试导致重复扣减,可能出现库存为负数
  • 现有保护: 乐观锁 @Version 仅防止并发覆盖,不防止重复请求

1.3 发货回传 POST /api/logistics/callback/{carrier}

  • 检查结果: ⚠️ 部分幂等(轨迹去重,无运单状态幂等)
  • 问题描述: 同一运单状态更新回调重复处理,可能导致状态回退
  • 现有保护: logisticsTraceMapper.countExist() 对轨迹有去重,但运单状态更新无幂等

1.4 消息队列消费者

  • 检查结果: 无 MQ 消费者
  • 说明: 系统使用 Spring EventStockChangedEventListener)实现异步,非传统 MQ

二、修复方案

2.1 订单服务order-service

新增文件

文件 说明
service/impl/OrderIdempotencyService.java Redis 幂等性工具类

修改文件

文件 修改内容
service/impl/OrderServiceImpl.java 注入 OrderIdempotencyService,修改 createOrders()shipOrder() 添加幂等检查

幂等键设计

订单创建order:idempotent:{platform}:{shopId}:{platformOrderSn}
订单发货order:idempotent:ship:{orderId}

修复逻辑

  1. Redis 检查:请求到达时先查 Redis 是否有处理记录
  2. DB 兜底:数据库唯一约束 uk_orders_platform_order_sn(platform_order_sn, platform, shop_id) 防止并发插入
  3. 幂等标记:处理成功后写入 RedisTTL=24h

2.2 库存服务inventory-service

新增文件

文件 说明
service/impl/StockIdempotencyService.java Redis 幂等性工具类

修改文件

文件 修改内容
controller/StockFeignController.java 注入 StockIdempotencyService,修改 deductStock() / lockStock() / unlockStock() 添加幂等检查

幂等键设计

扣减库存stock:idempotent:{skuCode}:{warehouseId}:{relatedNo}
锁定库存stock:idempotent:lock:{skuCode}:{warehouseId}:{relatedNo}
解锁库存stock:idempotent:unlock:{skuCode}:{warehouseId}:{relatedNo}

修复逻辑

  1. Redis 检查:到达即查重
  2. 幂等响应:重复请求返回缓存结果而非错误,保证接口幂等
  3. DB 唯一索引uk_stocks_sku_warehouse(sku_code, warehouse_id) 防止库存记录重复

2.3 物流服务logistics-service

新增文件

文件 说明
service/impl/LogisticsIdempotencyService.java Redis 幂等性工具类

修改文件

文件 修改内容
service/TraceSyncService.java 注入 LogisticsIdempotencyService,修改 processCallback() 添加幂等检查

幂等键设计

回调处理logistics:idempotent:{carrierCode}:{waybillNo}:{status}:{latestTraceTime}

修复逻辑

  1. Redis 检查同一运单同一状态回调在24h内不重复处理
  2. 轨迹去重:数据库已有 uk_logistics_trace_waybill_time_location 保障轨迹不重复

三、数据库变更SQL Migration

文件: docs/database/V2.4__idempotency_indexes.sql

-- orders 表:平台订单号+平台+店铺 唯一索引
ALTER TABLE orders
ADD CONSTRAINT uk_orders_platform_order_sn
UNIQUE (platform_order_sn, platform, shop_id);

-- stocks 表SKU+仓库 唯一索引
ALTER TABLE stocks
ADD CONSTRAINT uk_stocks_sku_warehouse
UNIQUE (sku_code, warehouse_id);

-- logistics_trace 表:运单号+轨迹时间+地点 唯一索引
ALTER TABLE logistics_trace
ADD CONSTRAINT uk_logistics_trace_waybill_time_location
UNIQUE (waybill_no, trace_time, location);

四、幂等性保证层次

┌─────────────────────────────────────────────────────┐
│                    接入层                            │
│  幂等Token / 请求ID可选未来扩展                   │
├─────────────────────────────────────────────────────┤
│                   Redis 层                           │
│  SETNX 原子操作防并发重复请求TTL=24h               │
├─────────────────────────────────────────────────────┤
│                   数据库层                           │
│  唯一索引兜底UK(platform_order_sn,platform,shop_id) │
│              UK(sku_code, warehouse_id)              │
│              UK(waybill_no, trace_time, location)   │
├─────────────────────────────────────────────────────┤
│                   业务层                             │
│  状态机 + 乐观锁防止状态异常                          │
└─────────────────────────────────────────────────────┘

五、影响范围

接口 服务 幂等键 影响
POST /api/orders/pull order-service platform:shopId:platformOrderSn 已修复
POST /api/orders/{id}/ship order-service ship:orderId 已修复
POST /api/stock/deduct inventory-service skuCode:warehouseId:relatedNo 已修复
POST /api/stock/lock inventory-service lock:skuCode:warehouseId:relatedNo 已修复
POST /api/stock/unlock inventory-service unlock:skuCode:warehouseId:relatedNo 已修复
POST /api/logistics/callback/{carrier} logistics-service carrier:waybillNo:status:traceTime 已修复

六、注意事项

  1. Redis 连接:所有服务 pom.xml 均已包含 spring-boot-starter-data-redis,无需额外依赖
  2. 幂等TTL:统一设为 24 小时,可根据业务调整
  3. 数据库索引:执行 SQL migration 时注意在线表操作影响,建议在低峰期执行
  4. 消息队列:当前系统无 MQ 消费者(使用 Spring Event无需额外处理
  5. 测试建议:使用 Postman/Curl 重复调用上述接口,验证幂等返回而非重复创建

七、修改文件清单

services/order-service/src/main/java/com/erp/order/service/impl/
  + OrderIdempotencyService.java          [NEW]

services/order-service/src/main/java/com/erp/order/service/impl/OrderServiceImpl.java
  [MOD] 添加 OrderIdempotencyService 依赖
  [MOD] createOrders() 添加幂等检查
  [MOD] shipOrder() 添加幂等检查

services/inventory-service/src/main/java/com/erp/inventory/service/impl/
  + StockIdempotencyService.java         [NEW]

services/inventory-service/src/main/java/com/erp/inventory/controller/StockFeignController.java
  [MOD] 添加 StockIdempotencyService 依赖
  [MOD] deductStock() 添加幂等检查
  [MOD] lockStock() 添加幂等检查
  [MOD] unlockStock() 添加幂等检查

services/logistics-service/src/main/java/com/erp/logistics/service/impl/
  + LogisticsIdempotencyService.java      [NEW]

services/logistics-service/src/main/java/com/erp/logistics/service/TraceSyncService.java
  [MOD] 添加 LogisticsIdempotencyService 依赖
  [MOD] processCallback() 添加幂等检查

docs/database/V2.4__idempotency_indexes.sql   [NEW]
docs/幂等性修复报告.md                          [NEW]