diff --git a/src/DFApp.Web/Background/LotteryResultJob.cs b/src/DFApp.Web/Background/LotteryResultJob.cs
index 97c209e0..28874e81 100644
--- a/src/DFApp.Web/Background/LotteryResultJob.cs
+++ b/src/DFApp.Web/Background/LotteryResultJob.cs
@@ -152,7 +152,7 @@ private async Task StartWork(string lotteryType, string lotteryTypeEng, string c
///
/// 逐条处理开奖结果,每条独立写入数据库,某条失败不影响其他条目
///
- private async Task ProcessResultsIndividually(List items)
+ private async Task ProcessResultsIndividually(List items)
{
int successCount = 0;
int skipCount = 0;
@@ -171,7 +171,7 @@ private async Task ProcessResultsIndividually(List
continue;
}
- LotteryResult entity = _mapper.MapToEntityFromExternalResultItem(item);
+ LotteryResult entity = _mapper.MapToEntityFromResultItem(item);
// 使用 InsertReturnIdAsync 获取自增 Id,InsertAsync 不会回填自增主键
entity.Id = await _lotteryResultRepository.InsertReturnIdAsync(entity);
successCount++;
@@ -181,7 +181,7 @@ private async Task ProcessResultsIndividually(List
{
var prizeEntities = item.Prizegrades.Select(pg =>
{
- var prizeEntity = _mapper.MapToEntityFromExternalPrizegradesItem(pg);
+ var prizeEntity = _mapper.MapToEntityFromPrizegradesItem(pg);
prizeEntity.LotteryResultId = entity.Id;
return prizeEntity;
}).ToList();
@@ -305,7 +305,7 @@ private async Task UpdatePrizegrades(string lotteryType, string lotteryTypeEng)
var prizeEntities = resultItem.Prizegrades.Select(pg =>
{
- var entity = _mapper.MapToEntityFromExternalPrizegradesItem(pg);
+ var entity = _mapper.MapToEntityFromPrizegradesItem(pg);
entity.LotteryResultId = item.Id;
return entity;
}).ToList();
@@ -342,89 +342,126 @@ private async Task UpdatePrizegrades(string lotteryType, string lotteryTypeEng)
private async Task GetLotteryResult(string dayStart, string dayEnd, int pageNo, string lotteryType)
{
- // 使用代理服务器获取数据
string proxyServerUrl = LotteryConst.GetLotteryProxyUrl(_configuration);
string requestUrl = $"{proxyServerUrl}/api/proxy/lottery/findDrawNotice?name={lotteryType}&dayStart={dayStart}&dayEnd={dayEnd}&pageNo={pageNo}&pageSize=30&week=&systemType=PC";
_logger.LogInformation("开始通过代理获取彩票数据 - 彩票类型: {LotteryType}, 开始日期: {DayStart}, 结束日期: {DayEnd}, 页码: {PageNo}", lotteryType, dayStart, dayEnd, pageNo);
- _logger.LogInformation("代理请求URL: {RequestUrl}", requestUrl);
- try
+ const int maxRetries = 3;
+ const int retryDelaySeconds = 5;
+
+ for (int attempt = 1; attempt <= maxRetries; attempt++)
{
- using var client = _httpClientFactory.CreateClient();
- // 设置超时时间
- client.Timeout = TimeSpan.FromSeconds(60);
+ try
+ {
+ using var client = _httpClientFactory.CreateClient();
+ client.Timeout = TimeSpan.FromSeconds(60);
- _logger.LogInformation("发送代理HTTP请求...");
+ _logger.LogInformation("发送代理HTTP请求 (尝试 {Attempt}/{MaxRetries})...", attempt, maxRetries);
- HttpResponseMessage message = await client.GetAsync(requestUrl);
+ using HttpResponseMessage message = await client.GetAsync(requestUrl);
- _logger.LogInformation("代理HTTP响应状态码: {StatusCode} ({Status})", (int)message.StatusCode, message.StatusCode);
+ _logger.LogInformation("代理HTTP响应状态码: {StatusCode} ({Status})", (int)message.StatusCode, message.StatusCode);
- message.EnsureSuccessStatusCode();
+ string responseContent = await message.Content.ReadAsStringAsync();
- string responseContent = await message.Content.ReadAsStringAsync();
- _logger.LogInformation("代理响应内容长度: {Length} 字符", responseContent.Length);
+ if (!message.IsSuccessStatusCode)
+ {
+ _logger.LogError("代理请求失败 - 状态码: {StatusCode}, 响应内容: {Content}",
+ (int)message.StatusCode,
+ responseContent.Length > 500 ? responseContent.Substring(0, 500) : responseContent);
- // 记录响应内容(仅前500字符,避免日志过长)
- if (responseContent.Length > 500)
- {
- _logger.LogInformation("代理响应内容前500字符: {Content}...", responseContent.Substring(0, 500));
- }
- else
- {
- _logger.LogInformation("代理响应内容: {Content}", responseContent);
- }
+ if ((int)message.StatusCode == 502 || (int)message.StatusCode == 504)
+ {
+ if (attempt < maxRetries)
+ {
+ _logger.LogWarning("遇到网关错误 {StatusCode},等待 {Delay} 秒后重试 (尝试 {Attempt}/{MaxRetries})",
+ (int)message.StatusCode, retryDelaySeconds * attempt, attempt, maxRetries);
+ await Task.Delay(TimeSpan.FromSeconds(retryDelaySeconds * attempt));
+ continue;
+ }
- LotteryInputDto? dto = JsonSerializer.Deserialize(responseContent);
+ throw new HttpRequestException(
+ $"代理请求在 {maxRetries} 次尝试后仍然失败,最后状态码: {(int)message.StatusCode},响应: {(responseContent.Length > 200 ? responseContent.Substring(0, 200) : responseContent)}");
+ }
- if (dto == null)
- {
- _logger.LogWarning("反序列化代理响应失败,响应为null,创建空对象");
- dto = new LotteryInputDto();
- }
- else
- {
- _logger.LogInformation("反序列化代理响应成功 - 总数据量: {Total}, 当前页: {PageNo}/{PageNum}, 每页大小: {PageSize}", dto.Total, dto.PageNo, dto.PageNum, dto.PageSize);
+ throw new HttpRequestException(
+ $"代理请求失败,状态码: {(int)message.StatusCode},响应: {(responseContent.Length > 200 ? responseContent.Substring(0, 200) : responseContent)}");
+ }
+
+ _logger.LogInformation("代理响应内容长度: {Length} 字符", responseContent.Length);
+
+ if (responseContent.Length > 500)
+ {
+ _logger.LogInformation("代理响应内容前500字符: {Content}...", responseContent.Substring(0, 500));
+ }
+ else
+ {
+ _logger.LogInformation("代理响应内容: {Content}", responseContent);
+ }
+
+ LotteryInputDto? dto = JsonSerializer.Deserialize(responseContent);
- if (dto.Result != null)
+ if (dto == null)
+ {
+ _logger.LogWarning("反序列化代理响应失败,响应为null,创建空对象");
+ dto = new LotteryInputDto();
+ }
+ else
{
- _logger.LogInformation("当前页数据条数: {Count}", dto.Result.Count);
+ _logger.LogInformation("反序列化代理响应成功 - 总数据量: {Total}, 当前页: {PageNo}/{PageNum}, 每页大小: {PageSize}", dto.Total, dto.PageNo, dto.PageNum, dto.PageSize);
- // 记录第一条数据的详细信息
- if (dto.Result.Count > 0)
+ if (dto.Result != null)
{
- var firstResult = dto.Result[0];
- _logger.LogInformation("第一条数据 - 彩票类型: {Name}, 期号: {Code}, 开奖日期: {Date}, 红球: {Red}, 蓝球: {Blue}", firstResult.Name, firstResult.Code, firstResult.Date, firstResult.Red, firstResult.Blue);
+ _logger.LogInformation("当前页数据条数: {Count}", dto.Result.Count);
+
+ if (dto.Result.Count > 0)
+ {
+ var firstResult = dto.Result[0];
+ _logger.LogInformation("第一条数据 - 彩票类型: {Name}, 期号: {Code}, 开奖日期: {Date}, 红球: {Red}, 蓝球: {Blue}", firstResult.Name, firstResult.Code, firstResult.Date, firstResult.Red, firstResult.Blue);
+ }
+ }
+ else
+ {
+ _logger.LogWarning("代理响应中的Result字段为null");
}
}
- else
+
+ return dto;
+ }
+ catch (HttpRequestException)
+ {
+ throw;
+ }
+ catch (JsonException ex)
+ {
+ _logger.LogError(ex, "代理JSON解析异常: {Message}", ex.Message);
+ throw;
+ }
+ catch (TaskCanceledException ex)
+ {
+ _logger.LogError(ex, "代理请求超时 (尝试 {Attempt}/{MaxRetries}): {Message}", attempt, maxRetries, ex.Message);
+
+ if (attempt == maxRetries)
{
- _logger.LogWarning("代理响应中的Result字段为null");
+ throw;
}
+
+ await Task.Delay(TimeSpan.FromSeconds(retryDelaySeconds * attempt));
}
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "代理未知异常 (尝试 {Attempt}/{MaxRetries}): {Message}", attempt, maxRetries, ex.Message);
- return dto;
- }
- catch (HttpRequestException ex)
- {
- _logger.LogError(ex, "代理HTTP请求异常: {Message}", ex.Message);
- throw;
- }
- catch (TaskCanceledException ex)
- {
- _logger.LogError(ex, "代理请求超时: {Message}", ex.Message);
- throw;
- }
- catch (JsonException ex)
- {
- _logger.LogError(ex, "代理JSON解析异常: {Message}", ex.Message);
- throw;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "代理未知异常: {Message}", ex.Message);
- throw;
+ if (attempt == maxRetries)
+ {
+ throw;
+ }
+
+ await Task.Delay(TimeSpan.FromSeconds(retryDelaySeconds * attempt));
+ }
}
+
+ throw new InvalidOperationException($"获取彩票数据失败,已重试 {maxRetries} 次");
}
}
diff --git a/src/DFApp.Web/DTOs/Lottery/LotteryInputDto.cs b/src/DFApp.Web/DTOs/Lottery/LotteryInputDto.cs
index 0f2adc55..e660422c 100644
--- a/src/DFApp.Web/DTOs/Lottery/LotteryInputDto.cs
+++ b/src/DFApp.Web/DTOs/Lottery/LotteryInputDto.cs
@@ -1,50 +1,28 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
-using ResultItemDtoType = DFApp.Lottery.ResultItemDto;
namespace DFApp.Web.DTOs.Lottery
{
- ///
- /// 彩票数据输入 DTO,用于反序列化代理服务器返回的彩票开奖数据
- ///
public class LotteryInputDto
{
- ///
- /// 无参构造函数
- ///
public LotteryInputDto()
{
- Result = new List();
+ Result = new List();
}
- ///
- /// 数据总数
- ///
[JsonPropertyName("total")]
public int Total { get; set; }
- ///
- /// 当前页码
- ///
[JsonPropertyName("pageNo")]
public int PageNo { get; set; }
- ///
- /// 总页数
- ///
[JsonPropertyName("pageNum")]
public int PageNum { get; set; }
- ///
- /// 每页大小
- ///
[JsonPropertyName("pageSize")]
public int PageSize { get; set; }
- ///
- /// 开奖结果列表(使用旧命名空间类型,与 LotteryMapper 的 External 映射方法签名匹配)
- ///
[JsonPropertyName("result")]
- public List Result { get; set; }
+ public List Result { get; set; }
}
}
diff --git a/src/DFApp.Web/Services/Lottery/LotteryDataFetchService.cs b/src/DFApp.Web/Services/Lottery/LotteryDataFetchService.cs
index e70f8041..9e6138f7 100644
--- a/src/DFApp.Web/Services/Lottery/LotteryDataFetchService.cs
+++ b/src/DFApp.Web/Services/Lottery/LotteryDataFetchService.cs
@@ -132,13 +132,13 @@ public async Task FetchLotteryData(LotteryDataFetch
foreach (var item in dto.Result)
{
- var lotteryResult = _mapper.MapToEntityFromExternalResultItem(item);
+ var lotteryResult = _mapper.MapToEntityFromResultItem(item);
// 使用映射器处理 Prizegrades
if (item.Prizegrades != null && item.Prizegrades.Count > 0)
{
lotteryResult.Prizegrades = item.Prizegrades
- .Select(p => _mapper.MapToEntityFromExternalPrizegradesItem(p))
+ .Select(p => _mapper.MapToEntityFromPrizegradesItem(p))
.ToList();
}