diff --git a/client/src/views/telegram/media/externalLink.vue b/client/src/views/telegram/media/externalLink.vue
index 7b0bcf65..0d2767f6 100644
--- a/client/src/views/telegram/media/externalLink.vue
+++ b/client/src/views/telegram/media/externalLink.vue
@@ -202,8 +202,10 @@ const handleCurrentChange = (page: number) => {
const handleCreateExternalLink = async () => {
try {
await externalLinkApi.getExternalLinkStatus();
- ElMessage.success("外部链接添加成功");
- loadTableData();
+ ElMessage.success("外链生成任务已提交,正在后台处理...");
+ setTimeout(() => {
+ loadTableData();
+ }, 5000);
} catch (error) {
console.error("添加外部链接失败:", error);
ElMessage.error("添加外部链接失败");
diff --git a/src/DFApp.Web/Background/LotteryResultJob.cs b/src/DFApp.Web/Background/LotteryResultJob.cs
index c9208f72..bd0a31f8 100644
--- a/src/DFApp.Web/Background/LotteryResultJob.cs
+++ b/src/DFApp.Web/Background/LotteryResultJob.cs
@@ -115,27 +115,24 @@ private async Task StartWork(string lotteryType, string lotteryTypeEng, string c
if (result1 == null || result1.Count <= 0)
{
_logger.LogInformation("今天没有数据,开始获取最新数据");
- _lotteryResultRepository.BeginTran();
+
+ _logger.LogInformation("开始更新奖级信息");
try
{
- _logger.LogInformation("开始更新奖级信息");
await UpdatePrizegrades(lotteryType, lotteryTypeEng);
-
- _logger.LogInformation("获取最新一期开奖结果作为起始点");
- LotteryResult lotteryResult = _resultReadOnly.GetQueryable().OrderByDescending(x => x.Code).First();
- string dayStart = (lotteryResult.Date!.Split('('))[0];
- _logger.LogInformation("从 {DayStart} 开始获取最新数据", dayStart);
-
- await GetCurrentLotteryResult(dayStart, 0, lotteryTypeEng);
- _lotteryResultRepository.CommitTran();
- _logger.LogInformation("最新数据获取完成并提交事务");
}
catch (Exception ex)
{
- _logger.LogError(ex, "获取最新数据时发生异常");
- _lotteryResultRepository.RollbackTran();
- throw;
+ _logger.LogError(ex, "更新奖级信息时发生异常,继续获取最新数据");
}
+
+ _logger.LogInformation("获取最新一期开奖结果作为起始点");
+ LotteryResult lotteryResult = _resultReadOnly.GetQueryable().OrderByDescending(x => x.Code).First();
+ string dayStart = (lotteryResult.Date!.Split('('))[0];
+ _logger.LogInformation("从 {DayStart} 开始获取最新数据", dayStart);
+
+ await GetCurrentLotteryResult(dayStart, 0, lotteryTypeEng);
+ _logger.LogInformation("最新数据获取完成");
}
else
{
@@ -152,30 +149,67 @@ private async Task StartWork(string lotteryType, string lotteryTypeEng, string c
}
}
- private async Task GetCurrentLotteryResult(string dayStart, int pageNo, string lotteryType)
+ ///
+ /// 逐条处理开奖结果,每条独立写入数据库,某条失败不影响其他条目
+ ///
+ private async Task ProcessResultsIndividually(List items)
{
- string dayEnd = DateTime.Now.ToString("yyyy-MM-dd");
- _logger.LogInformation("获取当前彩票结果 - 起始日期: {DayStart}, 结束日期: {DayEnd}, 彩票类型: {LotteryType}, 页码: {PageNo}", dayStart, dayEnd, lotteryType, pageNo);
-
- LotteryInputDto dto = await GetLotteryResult(dayStart, dayEnd, pageNo, lotteryType);
+ int successCount = 0;
+ int skipCount = 0;
+ int failCount = 0;
- if (dto.Result != null && dto.Result.Count > 0)
+ foreach (var item in items)
{
- _logger.LogInformation("获取到 {Count} 条数据,开始映射并保存到数据库", dto.Result.Count);
- List result = dto.Result.Select(item => _mapper.MapToEntityFromExternalResultItem(item)).ToList();
-
try
{
- await _lotteryResultRepository.InsertAsync(result);
- _logger.LogInformation("成功保存 {Count} 条彩票结果到数据库", result.Count);
+ // 按期号+彩票类型去重检查
+ bool exists = await _resultReadOnly.AnyAsync(r => r.Code == item.Code && r.Name == item.Name);
+ if (exists)
+ {
+ _logger.LogDebug("跳过已存在的记录 - 彩票类型: {Name}, 期号: {Code}", item.Name, item.Code);
+ skipCount++;
+ continue;
+ }
+
+ LotteryResult entity = _mapper.MapToEntityFromExternalResultItem(item);
+ await _lotteryResultRepository.InsertAsync(entity);
+ successCount++;
+
+ // 同时写入该条结果对应的奖级信息
+ if (item.Prizegrades != null && item.Prizegrades.Count > 0)
+ {
+ var prizeEntities = item.Prizegrades.Select(pg =>
+ {
+ var prizeEntity = _mapper.MapToEntityFromExternalPrizegradesItem(pg);
+ prizeEntity.LotteryResultId = entity.Id;
+ return prizeEntity;
+ }).ToList();
+
+ await _lotteryPrizegradesRepository.InsertAsync(prizeEntities);
+ }
}
catch (Exception ex)
{
- _logger.LogError(ex, "保存彩票结果到数据库时发生异常");
- throw;
+ failCount++;
+ _logger.LogError(ex, "逐条写入失败 - 彩票类型: {Name}, 期号: {Code},继续处理下一条", item.Name, item.Code);
}
+ }
+
+ _logger.LogInformation("逐条处理完成 - 成功: {Success}, 跳过(已存在): {Skip}, 失败: {Fail}", successCount, skipCount, failCount);
+ }
+
+ private async Task GetCurrentLotteryResult(string dayStart, int pageNo, string lotteryType)
+ {
+ string dayEnd = DateTime.Now.ToString("yyyy-MM-dd");
+ _logger.LogInformation("获取当前彩票结果 - 起始日期: {DayStart}, 结束日期: {DayEnd}, 彩票类型: {LotteryType}, 页码: {PageNo}", dayStart, dayEnd, lotteryType, pageNo);
+
+ LotteryInputDto dto = await GetLotteryResult(dayStart, dayEnd, pageNo, lotteryType);
+
+ if (dto.Result != null && dto.Result.Count > 0)
+ {
+ _logger.LogInformation("获取到 {Count} 条数据,开始逐条处理", dto.Result.Count);
+ await ProcessResultsIndividually(dto.Result);
- // 检查是否需要获取下一页数据
if (dto.PageNo < dto.PageNum)
{
_logger.LogInformation("当前页 {PageNo} 小于总页数 {PageNum},继续获取下一页数据", dto.PageNo, dto.PageNum);
@@ -200,21 +234,9 @@ private async Task GetAllLotteryResults(string dayStart, string dayEnd, int page
if (dto.Result != null && dto.Result.Count > 0)
{
- _logger.LogInformation("获取到 {Count} 条历史数据,开始映射并保存到数据库", dto.Result.Count);
- List result = dto.Result.Select(item => _mapper.MapToEntityFromExternalResultItem(item)).ToList();
-
- try
- {
- await _lotteryResultRepository.InsertAsync(result);
- _logger.LogInformation("成功保存 {Count} 条历史彩票结果到数据库", result.Count);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "保存历史彩票结果到数据库时发生异常");
- throw;
- }
+ _logger.LogInformation("获取到 {Count} 条历史数据,开始逐条处理", dto.Result.Count);
+ await ProcessResultsIndividually(dto.Result);
- // 检查是否需要获取下一页数据
if (dto.PageNo < dto.PageNum)
{
_logger.LogInformation("当前页 {PageNo} 小于总页数 {PageNum},继续获取下一页历史数据", dto.PageNo, dto.PageNum);
diff --git a/src/DFApp.Web/Services/Media/ExternalLinkService.cs b/src/DFApp.Web/Services/Media/ExternalLinkService.cs
index 8523e290..3c1a3cb7 100644
--- a/src/DFApp.Web/Services/Media/ExternalLinkService.cs
+++ b/src/DFApp.Web/Services/Media/ExternalLinkService.cs
@@ -17,6 +17,7 @@
using DFApp.Web.Permissions;
using DFApp.Web.Queue;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
namespace DFApp.Web.Services.Media;
@@ -107,6 +108,7 @@ public Task GetExternalLink()
var mediaInfoRepository = scope.ServiceProvider.GetRequiredService>();
var externalLinkRepository = scope.ServiceProvider.GetRequiredService>();
var mediaExternalLinkMediaIdRepository = scope.ServiceProvider.GetRequiredService>();
+ var logger = scope.ServiceProvider.GetRequiredService>();
var returnDownloadUrlPrefix = await configurationInfoRepository.GetConfigurationInfoValue("ReturnDownloadUrlPrefix", MediaBackgroudConst.ModuleName);
if (string.IsNullOrWhiteSpace(returnDownloadUrlPrefix))
@@ -127,9 +129,12 @@ public Task GetExternalLink()
if (temp == null || temp.Count <= 0)
{
+ logger.LogWarning("没有符合条件的外链生成媒体(IsExternalLinkGenerated=false 且 IsDownloadCompleted=true)");
return;
}
+ logger.LogInformation("找到 {Count} 条符合条件的外链生成媒体,开始处理", temp.Count);
+
string datetimeName = DateTime.Now.ToString("yyyyMMddHHmmss");
string zipPhotoName = $"{datetimeName}.zip";
string zipPhotoPathName = Path.Combine(Path.GetDirectoryName(photoSavePath)!, zipPhotoName);
@@ -155,6 +160,11 @@ public Task GetExternalLink()
continue;
}
+ if (!File.Exists(mediaInfo.SavePath))
+ {
+ logger.LogWarning("媒体文件不存在,跳过:{SavePath}(MediaId={MediaId})", mediaInfo.SavePath, mediaInfo.Id);
+ }
+
stringBuilder.AppendLine($"{Path.Combine(returnDownloadUrlPrefix, mediaInfo.SavePath.Replace(replaceUrlPrefix, string.Empty).Replace("\\", "/"))}");
mediaInfo.IsExternalLinkGenerated = true;
}
@@ -202,6 +212,16 @@ public Task GetExternalLink()
}
await externalLinkRepository.InsertAsync(mediaExternalLink);
+
+ // 插入后获取外链 ID,为子记录赋值并批量插入
+ foreach (var item in mediaExternalLinkMediaIds)
+ {
+ item.MediaExternalLinkId = mediaExternalLink.Id;
+ }
+ await mediaExternalLinkMediaIdRepository.InsertAsync(mediaExternalLinkMediaIds);
+
+ logger.LogInformation("外链生成完成,ID={Id},包含 {Count} 条媒体记录,耗时 {ElapsedMs}ms",
+ mediaExternalLink.Id, mediaExternalLinkMediaIds.Count, stopwatch.ElapsedMilliseconds);
}
});