206 lines
8.0 KiB
Markdown
206 lines
8.0 KiB
Markdown
# 幂等性检查与修复报告
|
||
|
||
**任务编号:** 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]
|
||
```
|