using LMS.Common.Extensions; using LMS.DAO; using LMS.Repository.DB; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; namespace LMS.Tools.MJPackage { public class TaskService(ITokenService tokenService, ILogger logger, ApplicationDbContext dbContext, ITaskConcurrencyManager taskConcurrencyManager) : ITaskService { private readonly ITokenService _tokenService = tokenService; private readonly ILogger _logger = logger; private readonly ApplicationDbContext _dbContext = dbContext; private readonly ITaskConcurrencyManager _taskConcurrencyManager = taskConcurrencyManager; public async Task?> FetchTaskAsync(MJApiTasks mJApiTasks) { try { // 获取UseToken,先尝试 Token,再尝试 TokenId var tokenConfig = await _tokenService.GetTokenAsync(mJApiTasks.Token); string useToken = string.Empty; if (tokenConfig == null) { // Token 没找到 尝试用 TokenId 查找 MJApiTokens? mJApiTokens = await _tokenService.GetMJapiTokenByIdAsync(mJApiTasks.TokenId); if (mJApiTokens == null) { return null; } useToken = mJApiTokens.UseToken; } else { useToken = tokenConfig.UseToken; } if (string.IsNullOrWhiteSpace(useToken)) { _logger.LogInformation($"Token is empty for task ID: {mJApiTasks.TaskId}"); return null; } // 尝试备用API var backupResult = await TryBackupApiAsync(mJApiTasks.ThirdPartyTaskId, useToken); if (string.IsNullOrWhiteSpace(backupResult)) { // 没有找到数据 _logger.LogInformation($"备用API没有返回数据,TaskId: {mJApiTasks.TaskId}"); return null; } var properties = new Dictionary(); try { // 不为空 开始解析数据 properties = JsonConvert.DeserializeObject>(backupResult); } catch (JsonException ex) { _logger.LogError($"解析备用API返回数据失败: {ex.Message}"); return null; } if (properties == null) { _logger.LogInformation($"备用API返回数据为空,TaskId: {mJApiTasks.TaskId}"); return null; } return properties; } catch (Exception ex) { // 记录异常日志 _logger.LogError($"Error fetching task: {ex.Message}"); return null; } } private async Task TryBackupApiAsync(string id, string useToken) { string mjAPIBasicUrl = await _tokenService.GetMJAPIBasicUrl(); string backupUrl = $"{mjAPIBasicUrl}/mj/task/{id}/fetch"; const int maxRetries = 3; const int baseDelayMs = 1000; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "sk-" + useToken); client.Timeout = TimeSpan.FromSeconds(30); for (int attempt = 1; attempt <= maxRetries; attempt++) { try { var response = await client.GetAsync(backupUrl); var content = await response.Content.ReadAsStringAsync(); // 判断请求是不是报错 if (!response.IsSuccessStatusCode) { _logger.LogWarning("备用API调用返回错误状态码,TaskId: {TaskId}, Attempt: {Attempt}, StatusCode: {StatusCode}", id, attempt, response.StatusCode); return null; } return content; } catch (Exception ex) when (IsRetriableException(ex)) { if (attempt < maxRetries) { var delay = baseDelayMs * (int)Math.Pow(2, attempt - 1); _logger.LogWarning(ex, "备用API调用失败,TaskId: {TaskId}, Attempt: {Attempt}, 将在{Delay}ms后重试", id, attempt, delay); await Task.Delay(delay); } else { _logger.LogError(ex, "备用API调用最终失败,TaskId: {TaskId}, MaxAttempts: {MaxAttempts}", id, maxRetries); } } catch (Exception ex) { _logger.LogError(ex, "备用API调用发生不可重试异常,TaskId: {TaskId}, Attempt: {Attempt}", id, attempt); break; } } return null; } private static bool IsRetriableException(Exception ex) { return ex is HttpRequestException || ex is TaskCanceledException || ex is SocketException; } } }