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

206 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 幂等性检查与修复报告
**任务编号:** 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. **幂等标记**:处理成功后写入 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`
```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]
```