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); } });