# 重试机制使用说明

本项目提供了两种不同的重试机制，用于不同的业务场景。

## 🔄 两种重试机制对比

| 特性 | @RepeatCall 注解 | retry() 方法 |
|------|-----------------|--------------|
| **触发条件** | 无论成功失败都执行指定次数 | 只有在请求失败时才重试 |
| **适用场景** | 确保脚本端接收到关键操作 | 网络不稳定的查询操作 |
| **执行次数** | 固定执行N次 | 最多重试N次（成功则停止） |
| **性能影响** | 较大（总是执行多次） | 较小（成功时只执行1次） |

## 🎯 @RepeatCall 注解机制

### 功能介绍
无论成功还是失败都会执行指定次数，主要用于确保脚本端能够可靠接收到请求。

### 使用方法

```java
@RepeatCall(times = 3, description = "设置次数")
public Mono<String> setTimes(String codeNo, int times) {
    // 无论成功失败都会执行3次
    return webClient.post()...;
}
```

### 参数说明
- `times`: 重复执行次数（默认3次）
- `delayMs`: 每次调用之间的延迟时间（毫秒，默认0）
- `logEachCall`: 是否记录每次调用的详细日志（默认true）
- `description`: 描述信息，用于日志记录（默认为方法名）

### 已应用的方法
- `setTimes()` - 设置次数
- `selectRegion()` - 选区操作  
- `refresh()` - 刷新操作
- `checkRefresh()` - 判断刷新
- `saveTotalTimes()` - 保存总次数
- `refundOrder()` - 退单操作

## 🔁 retry() 重试机制

### 功能介绍
只有在请求失败（超时、404、网络错误等）时才会重试，成功则立即返回结果。

### 使用方法

```java
public Mono<byte[]> getQrPng(String path) {
    return webClient.get()
            .uri(path)
            .retrieve()
            .bodyToMono(byte[].class)
            .timeout(Duration.ofSeconds(5))
            .retry(3)  // 失败时重试3次
            .doOnError(e -> log.warn("获取失败: {}", e.toString()));
}
```

### 已应用的方法
- `getQrPng()` - 获取二维码图片
- `getImagePng()` - 获取通用图片
- `getText()` - 获取文本内容
- `checkAvailableDevice()` - 检查空闲设备
- `checkLoginStatus()` - 检查上号状态
- `getDeviceQrCode()` - 获取设备二维码
- `getTargetScore()` - 获取目标分数
- `getDeviceStatus()` - 获取设备状态

## 📋 使用场景选择指南

### 选择 @RepeatCall 的场景：
✅ **关键状态同步操作**
- 设置游戏次数
- 选择游戏区域
- 刷新操作
- 退单操作

✅ **需要确保脚本端接收的操作**
- 重要参数设置
- 状态变更通知

### 选择 retry() 的场景：
✅ **数据查询操作**
- 获取图片资源
- 检查设备状态
- 获取分数信息

✅ **可能因网络问题失败的操作**
- 文件下载
- API 调用
- 状态检查

## 💡 最佳实践

1. **@RepeatCall**：用于重要的业务操作，确保执行到位
2. **retry()**：用于查询操作，提高成功率但不影响性能
3. **超时设置**：两种机制都应配合合理的超时时间
4. **日志记录**：重要操作要有详细的日志记录

## ⚠️ 注意事项

1. **@RepeatCall** 会显著增加执行时间，请谨慎使用
2. **retry()** 对幂等操作更安全
3. 需要添加 `spring-boot-starter-aop` 依赖支持 @RepeatCall
4. 建议根据业务重要性选择合适的重试机制
