# 幂等性检查与修复报告 **任务编号:** 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 Event(`StockChangedEventListener`)实现异步,非传统 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. **幂等标记**:处理成功后写入 Redis,TTL=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` ```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] ```