Forge幂等模块提供了企业级的分布式幂等性解决方案,支持多种幂等策略、Token机制、结果缓存和分布式锁。
<dependency>
<groupId>com.mdframe.forge</groupId>
<artifactId>forge-starter-idempotent</artifactId>
</dependency>
forge:
idempotent:
enabled: true
prefix: "idempotent:"
expire: 600
message: "请勿重复提交"
cache:
enabled: true
expire: 3600
token:
enabled: true
expire: 300
header: "X-Idempotent-Token"
lock:
enabled: true
wait-time: 3000
lease-time: 5000
适用场景:订单创建、支付处理等关键操作
@PostMapping("/order/create")
@Idempotent(
strategy = IdempotentStrategy.STRICT,
prefix = "order:",
key = "#orderRequest.orderId",
message = "订单正在处理中,请勿重复提交"
)
public RespInfo<Order> createOrder(@RequestBody OrderRequest orderRequest) {
return RespInfo.success(orderService.create(orderRequest));
}
行为:
IdempotentException适用场景:查询类操作、可重复操作
@GetMapping("/order/{orderId}")
@Idempotent(
strategy = IdempotentStrategy.RETURN_CACHE,
prefix = "order:query:",
key = "#orderId",
cacheExpire = 300
)
public RespInfo<Order> queryOrder(@PathVariable String orderId) {
return RespInfo.success(orderService.getById(orderId));
}
行为:
适用场景:防止前端重复提交、防重放攻击
// 前端调用API获取Token
const response = await axios.post('/api/idempotent/token/generate', {
prefix: 'payment'
});
const token = response.data.data.token; // 例如: "abc123def456..."
// 提交支付请求时携带Token
await axios.post('/api/payment/process', paymentData, {
headers: {
'X-Idempotent-Token': token
}
});
@PostMapping("/payment/process")
@Idempotent(
strategy = IdempotentStrategy.TOKEN_REQUIRED,
prefix = "payment:",
key = "#paymentRequest.paymentId"
)
public RespInfo<PaymentResult> processPayment(@RequestBody PaymentRequest paymentRequest) {
return RespInfo.success(paymentService.process(paymentRequest));
}
行为:
TokenInvalidException请求:
POST /api/idempotent/token/generate
Content-Type: application/json
{
"prefix": "order" // 可选,默认为 global
}
响应:
{
"code": 200,
"message": "success",
"data": {
"token": "abc123def456ghi789...",
"expireSeconds": 300,
"createTime": 1678923456789
}
}
请求:
POST /api/idempotent/token/batch-generate
Content-Type: application/json
{
"count": 10,
"prefix": "order"
}
响应:
{
"code": 200,
"message": "success",
"data": [
{ "token": "abc1...", "expireSeconds": 300 },
{ "token": "abc2...", "expireSeconds": 300 }
]
}
请求:
POST /api/idempotent/token/validate
Content-Type: application/json
{
"token": "abc123def456..."
}
响应:
{
"code": 200,
"message": "success",
"data": true
}
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
strategy |
IdempotentStrategy | RETURN_CACHE | 幂等策略 |
prefix |
String | "idempotent:" | 幂等Key前缀 |
key |
String | "" | 自定义Key(支持SpEL) |
expire |
int | 600 | 幂等有效期(秒) |
cacheExpire |
int | 3600 | 缓存有效期(秒) |
message |
String | "请勿重复提交" | 提示消息 |
deleteKeyAfterSuccess |
boolean | false | 成功后删除Key |
cacheResult |
boolean | true | 是否缓存结果 |
enableMetrics |
boolean | true | 是否开启监控 |
| 策略 | Code | 说明 |
|---|---|---|
| STRICT | strict | 严格拒绝重复请求 |
| RETURN_CACHE | return_cache | 返回缓存结果 |
| TOKEN_REQUIRED | token_required | 必须携带有效Token |
模块集成了Prometheus监控,提供以下指标:
| 指标名称 | 类型 | 说明 |
|---|---|---|
idempotent.requests.total |
Counter | 请求总数 |
idempotent.requests.success |
Counter | 成功次数 |
idempotent.requests.duplicate |
Counter | 重复次数 |
idempotent.cache.returned |
Counter | 缓存返回次数 |
idempotent.cache.hit.rate |
Gauge | 缓存命中率 |
idempotent.execution.time |
Timer | 执行耗时 |
@RestControllerAdvice
public class IdempotentExceptionHandler {
@ExceptionHandler(IdempotentException.class)
public RespInfo<Void> handleIdempotentException(IdempotentException e) {
return RespInfo.error(e.getMessage());
}
}
@ExceptionHandler(TokenInvalidException.class)
public RespInfo<Void> handleTokenInvalidException(TokenInvalidException e) {
return RespInfo.error("Token无效或已过期");
}
// ✅ 好的设计
@Idempotent(key = "#order.orderId")
// ❌ 不好的设计
@Idempotent(key = "#order.createTime") // 时间会变化
expire 应该大于业务处理时间cacheExpire 建议 ≤ expire,避免缓存残留确保Redis配置正确:
spring:
redis:
host: localhost
port: 6379
database: 0
timeout: 3000
原因:
解决:重新获取Token
原因:
解决:调整 cacheExpire 配置
原因:
解决:调整 lock.wait-time 配置
code-copilot/changes/idempotent-refactor/spec.mdcode-copilot/changes/idempotent-refactor/implementation-summary.mdAGENTS.md