diff --git a/Repository/TransactionRecordRepository.cs b/Repository/TransactionRecordRepository.cs index ad8d0c0..bc575ff 100644 --- a/Repository/TransactionRecordRepository.cs +++ b/Repository/TransactionRecordRepository.cs @@ -57,6 +57,7 @@ public interface ITransactionRecordRepository : IBaseRepository /// 年份 /// 月份 + /// /// 每天的消费笔数和金额详情 Task> GetDailyStatisticsAsync(int year, int month, string? savingClassify = null); @@ -65,6 +66,7 @@ public interface ITransactionRecordRepository : IBaseRepository /// 开始日期 /// 结束日期 + /// /// 每天的消费笔数和金额详情 Task> GetDailyStatisticsByRangeAsync(DateTime startDate, DateTime endDate, string? savingClassify = null); @@ -149,7 +151,6 @@ public interface ITransactionRecordRepository : IBaseRepository /// 根据关键词查询交易记录(模糊匹配Reason字段) /// - /// 关键词 /// 匹配的交易记录列表 Task> QueryByWhereAsync(string sql); @@ -471,7 +472,7 @@ public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository r.Id).ToList(), TotalAmount = Math.Abs(group.TotalAmount) }); @@ -549,7 +550,7 @@ public class TransactionRecordRepository(IFreeSql freeSql) : BaseRepository t.Classify ?? "未分类") + .GroupBy(t => t.Classify) .Select(g => new CategoryStatistics { Classify = g.Key, diff --git a/Service/ConfigService.cs b/Service/ConfigService.cs index 8550e27..7282359 100644 --- a/Service/ConfigService.cs +++ b/Service/ConfigService.cs @@ -43,12 +43,12 @@ public class ConfigService(IConfigRepository configRepository) : IConfigService var config = await configRepository.GetByKeyAsync(key); var type = typeof(T) switch { - Type t when t == typeof(bool) => ConfigType.Boolean, - Type t when t == typeof(int) - || t == typeof(double) - || t == typeof(float) - || t == typeof(decimal) => ConfigType.Number, - Type t when t == typeof(string) => ConfigType.String, + { } t when t == typeof(bool) => ConfigType.Boolean, + { } t when t == typeof(int) + || t == typeof(double) + || t == typeof(float) + || t == typeof(decimal) => ConfigType.Number, + { } t when t == typeof(string) => ConfigType.String, _ => ConfigType.Json }; var valueStr = type switch diff --git a/Service/EmailServices/EmailHandleService.cs b/Service/EmailServices/EmailHandleService.cs index 17652b7..bf0d2ab 100644 --- a/Service/EmailServices/EmailHandleService.cs +++ b/Service/EmailServices/EmailHandleService.cs @@ -1,4 +1,4 @@ -using Service.EmailParseServices; +using Service.EmailServices.EmailParse; namespace Service.EmailServices; diff --git a/Service/EmailServices/EmailParse/EmailParseForm95555.cs b/Service/EmailServices/EmailParse/EmailParseForm95555.cs index 400473a..eb87910 100644 --- a/Service/EmailServices/EmailParse/EmailParseForm95555.cs +++ b/Service/EmailServices/EmailParse/EmailParseForm95555.cs @@ -1,4 +1,4 @@ -namespace Service.EmailParseServices; +namespace Service.EmailServices.EmailParse; public class EmailParseForm95555( ILogger logger, @@ -26,7 +26,7 @@ public class EmailParseForm95555( return true; } - public override async Task<( + public override Task<( string card, string reason, decimal amount, @@ -51,7 +51,7 @@ public class EmailParseForm95555( if (matches.Count <= 0) { logger.LogWarning("未能从招商银行邮件内容中解析出交易信息"); - return []; + return Task.FromResult<(string card, string reason, decimal amount, decimal balance, TransactionType type, DateTime? occurredAt)[]>([]); } var results = new List<( @@ -85,7 +85,7 @@ public class EmailParseForm95555( results.Add((card, reason, amount, balance, type, occurredAt)); } } - return results.ToArray(); + return Task.FromResult(results.ToArray()); } private DateTime? ParseOccurredAt(string value) diff --git a/Service/EmailServices/EmailParse/EmailParseFormCCSVC.cs b/Service/EmailServices/EmailParse/EmailParseFormCCSVC.cs index e2cfd6a..9f0c956 100644 --- a/Service/EmailServices/EmailParse/EmailParseFormCCSVC.cs +++ b/Service/EmailServices/EmailParse/EmailParseFormCCSVC.cs @@ -1,6 +1,6 @@ using HtmlAgilityPack; -namespace Service.EmailParseServices; +namespace Service.EmailServices.EmailParse; public class EmailParseFormCcsvc( ILogger logger, @@ -44,11 +44,6 @@ public class EmailParseFormCcsvc( // 1. Get Date var dateNode = doc.DocumentNode.SelectSingleNode("//font[contains(text(), '您的消费明细如下')]"); - if (dateNode == null) - { - logger.LogWarning("Date node not found"); - return []; - } var dateText = dateNode.InnerText.Trim(); // "2025/12/21 您的消费明细如下:" @@ -63,104 +58,87 @@ public class EmailParseFormCcsvc( decimal balance = 0; // Find "可用额度" label var limitLabelNode = doc.DocumentNode.SelectSingleNode("//font[contains(text(), '可用额度')]"); - if (limitLabelNode != null) { // Go up to TR var tr = limitLabelNode.Ancestors("tr").FirstOrDefault(); if (tr != null) { var prevTr = tr.PreviousSibling; - while (prevTr != null && prevTr.Name != "tr") prevTr = prevTr.PreviousSibling; + while (prevTr.Name != "tr") prevTr = prevTr.PreviousSibling; - if (prevTr != null) - { - var balanceNode = prevTr.SelectSingleNode(".//font[contains(text(), '¥')]"); - if (balanceNode != null) - { - var balanceStr = balanceNode.InnerText.Replace("¥", "").Replace(",", "").Trim(); - decimal.TryParse(balanceStr, out balance); - } - } + var balanceNode = prevTr.SelectSingleNode(".//font[contains(text(), '¥')]"); + var balanceStr = balanceNode.InnerText.Replace("¥", "").Replace(",", "").Trim(); + decimal.TryParse(balanceStr, out balance); } } // 3. Get Transactions var transactionNodes = doc.DocumentNode.SelectNodes("//span[@id='fixBand4']"); - if (transactionNodes != null) + foreach (var node in transactionNodes) { - foreach (var node in transactionNodes) + try { - try + // Time + var timeNode = node.SelectSingleNode(".//span[@id='fixBand5']//font"); + var timeText = timeNode.InnerText.Trim(); // "10:13:43" + + DateTime? occurredAt = date; + if (!string.IsNullOrEmpty(timeText) && DateTime.TryParse($"{date:yyyy-MM-dd} {timeText}", out var dt)) { - // Time - var timeNode = node.SelectSingleNode(".//span[@id='fixBand5']//font"); - var timeText = timeNode?.InnerText.Trim(); // "10:13:43" - - DateTime? occurredAt = date; - if (!string.IsNullOrEmpty(timeText) && DateTime.TryParse($"{date:yyyy-MM-dd} {timeText}", out var dt)) - { - occurredAt = dt; - } - - // Info Block - var infoNode = node.SelectSingleNode(".//span[@id='fixBand12']"); - if (infoNode == null) continue; - - // Amount - var amountNode = infoNode.SelectSingleNode(".//font[contains(text(), 'CNY')]"); - var amountText = amountNode?.InnerText.Replace("CNY", "").Replace(" ", "").Trim(); - if (!decimal.TryParse(amountText, out var amount)) - { - continue; - } - - // Description - var descNode = infoNode.SelectSingleNode(".//tr[2]//font"); - var descText = descNode?.InnerText ?? ""; - // Replace   and non-breaking space (\u00A0) with normal space - descText = descText.Replace(" ", " "); - descText = HtmlEntity.DeEntitize(descText).Replace((char)160, ' ').Trim(); - - // Parse Description: "尾号4390 消费 财付通-luckincoffee瑞幸咖啡" - var parts = descText.Split([' '], StringSplitOptions.RemoveEmptyEntries); - - string card = ""; - string reason = descText; - TransactionType type = TransactionType.Expense; - - if (parts.Length > 0 && parts[0].StartsWith("尾号")) - { - card = parts[0].Replace("尾号", ""); - } - - if (parts.Length > 1) - { - var typeStr = parts[1]; - type = DetermineTransactionType(typeStr, reason, amount); - } - - if (parts.Length > 2) - { - reason = string.Join(" ", parts.Skip(2)); - } - - // 招商信用卡特殊,消费金额为正数,退款为负数 - if(amount > 0) - { - type = TransactionType.Expense; - } - else - { - type = TransactionType.Income; - amount = Math.Abs(amount); - } - - result.Add((card, reason, amount, balance, type, occurredAt)); + occurredAt = dt; } - catch (Exception ex) + + // Info Block + var infoNode = node.SelectSingleNode(".//span[@id='fixBand12']"); + + // Amount + var amountNode = infoNode.SelectSingleNode(".//font[contains(text(), 'CNY')]"); + var amountText = amountNode.InnerText.Replace("CNY", "").Replace(" ", "").Trim(); + if (!decimal.TryParse(amountText, out var amount)) { - logger.LogError(ex, "Error parsing transaction node"); + continue; } + + // Description + var descNode = infoNode.SelectSingleNode(".//tr[2]//font"); + var descText = descNode.InnerText; + // Replace   and non-breaking space (\u00A0) with normal space + descText = descText.Replace(" ", " "); + descText = HtmlEntity.DeEntitize(descText).Replace((char)160, ' ').Trim(); + + // Parse Description: "尾号4390 消费 财付通-luckincoffee瑞幸咖啡" + var parts = descText.Split([' '], StringSplitOptions.RemoveEmptyEntries); + + string card = ""; + string reason = descText; + TransactionType type; + + if (parts.Length > 0 && parts[0].StartsWith("尾号")) + { + card = parts[0].Replace("尾号", ""); + } + + if (parts.Length > 2) + { + reason = string.Join(" ", parts.Skip(2)); + } + + // 招商信用卡特殊,消费金额为正数,退款为负数 + if(amount > 0) + { + type = TransactionType.Expense; + } + else + { + type = TransactionType.Income; + amount = Math.Abs(amount); + } + + result.Add((card, reason, amount, balance, type, occurredAt)); + } + catch (Exception ex) + { + logger.LogError(ex, "Error parsing transaction node"); } } diff --git a/Service/EmailServices/EmailParse/IEmailParseServices.cs b/Service/EmailServices/EmailParse/IEmailParseServices.cs index c79597a..915696a 100644 --- a/Service/EmailServices/EmailParse/IEmailParseServices.cs +++ b/Service/EmailServices/EmailParse/IEmailParseServices.cs @@ -1,4 +1,4 @@ -namespace Service.EmailParseServices; +namespace Service.EmailServices.EmailParse; public interface IEmailParseServices { diff --git a/Service/EmailServices/EmailSyncService.cs b/Service/EmailServices/EmailSyncService.cs index 677a922..77f471a 100644 --- a/Service/EmailServices/EmailSyncService.cs +++ b/Service/EmailServices/EmailSyncService.cs @@ -182,7 +182,7 @@ public class EmailSyncService( var unreadMessages = await emailFetchService.FetchUnreadMessagesAsync(); logger.LogInformation("邮箱 {Email} 获取到 {MessageCount} 封未读邮件", email, unreadMessages.Count); - foreach (var (message, uid) in unreadMessages) + foreach (var (message, _) in unreadMessages) { try { diff --git a/Service/Jobs/EmailSyncJob.cs b/Service/Jobs/EmailSyncJob.cs index 4af8b29..5b5c2b2 100644 --- a/Service/Jobs/EmailSyncJob.cs +++ b/Service/Jobs/EmailSyncJob.cs @@ -127,7 +127,7 @@ public class EmailSyncJob( var unreadMessages = await emailFetchService.FetchUnreadMessagesAsync(); logger.LogInformation("邮箱 {Email} 获取到 {MessageCount} 封未读邮件", email, unreadMessages.Count); - foreach (var (message, uid) in unreadMessages) + foreach (var (message, _) in unreadMessages) { try { diff --git a/Service/NotificationService.cs b/Service/NotificationService.cs index d1d8e54..e5a0cf8 100644 --- a/Service/NotificationService.cs +++ b/Service/NotificationService.cs @@ -1,5 +1,4 @@ -using System.Net; -using WebPush; +using WebPush; using PushSubscription = Entity.PushSubscription; namespace Service; diff --git a/Service/OpenAiService.cs b/Service/OpenAiService.cs index d74f3d9..7de4f2b 100644 --- a/Service/OpenAiService.cs +++ b/Service/OpenAiService.cs @@ -158,10 +158,8 @@ public class OpenAiService( var json = JsonSerializer.Serialize(payload); using var content = new StringContent(json, Encoding.UTF8, "application/json"); - using var request = new HttpRequestMessage(HttpMethod.Post, url) - { - Content = content - }; + using var request = new HttpRequestMessage(HttpMethod.Post, url); + request.Content = content; using var resp = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); if (!resp.IsSuccessStatusCode) @@ -232,10 +230,8 @@ public class OpenAiService( using var content = new StringContent(json, Encoding.UTF8, "application/json"); // 使用 SendAsync 来支持 HttpCompletionOption - using var request = new HttpRequestMessage(HttpMethod.Post, url) - { - Content = content - }; + using var request = new HttpRequestMessage(HttpMethod.Post, url); + request.Content = content; using var resp = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); if (!resp.IsSuccessStatusCode) diff --git a/WebApi/Controllers/BillImportController.cs b/WebApi/Controllers/BillImportController.cs index 4adf0fb..f97389c 100644 --- a/WebApi/Controllers/BillImportController.cs +++ b/WebApi/Controllers/BillImportController.cs @@ -50,9 +50,6 @@ public class BillImportController( return "文件大小不能超过 10MB".Fail(); } - // 生成唯一文件名 - var fileName = $"{type}_{DateTime.Now:yyyyMMddHHmmss}_{Guid.NewGuid():N}{fileExtension}"; - // 保存文件 var ok = false; var message = string.Empty; @@ -69,6 +66,11 @@ public class BillImportController( } } + if (!ok) + { + return message.Fail(); + } + return message.Ok(); } catch (Exception ex) @@ -77,4 +79,4 @@ public class BillImportController( return $"文件上传失败: {ex.Message}".Fail(); } } -} +} \ No newline at end of file diff --git a/WebApi/Controllers/BudgetController.cs b/WebApi/Controllers/BudgetController.cs index a3b087e..7815ea5 100644 --- a/WebApi/Controllers/BudgetController.cs +++ b/WebApi/Controllers/BudgetController.cs @@ -138,7 +138,7 @@ public class BudgetController( Type = dto.Type, Limit = limit, Category = dto.Category, - SelectedCategories = dto.SelectedCategories != null ? string.Join(",", dto.SelectedCategories) : string.Empty, + SelectedCategories = string.Join(",", dto.SelectedCategories), StartDate = dto.StartDate ?? DateTime.Now, NoLimit = dto.NoLimit, IsMandatoryExpense = dto.IsMandatoryExpense @@ -182,7 +182,7 @@ public class BudgetController( budget.Type = dto.Type; budget.Limit = limit; budget.Category = dto.Category; - budget.SelectedCategories = dto.SelectedCategories != null ? string.Join(",", dto.SelectedCategories) : string.Empty; + budget.SelectedCategories = string.Join(",", dto.SelectedCategories); budget.NoLimit = dto.NoLimit; budget.IsMandatoryExpense = dto.IsMandatoryExpense; if (dto.StartDate.HasValue) diff --git a/WebApi/Controllers/Dto/EmailMessageDto.cs b/WebApi/Controllers/Dto/EmailMessageDto.cs index 592bf81..679e1c5 100644 --- a/WebApi/Controllers/Dto/EmailMessageDto.cs +++ b/WebApi/Controllers/Dto/EmailMessageDto.cs @@ -31,7 +31,7 @@ public class EmailMessageDto /// /// 从实体转换为DTO /// - public static EmailMessageDto FromEntity(Entity.EmailMessage entity, int transactionCount = 0) + public static EmailMessageDto FromEntity(EmailMessage entity, int transactionCount = 0) { return new EmailMessageDto { @@ -44,7 +44,7 @@ public class EmailMessageDto CreateTime = entity.CreateTime, UpdateTime = entity.UpdateTime, TransactionCount = transactionCount, - ToName = entity.To?.Split('<').FirstOrDefault()?.Trim() ?? "未知" + ToName = entity.To.Split('<').FirstOrDefault()?.Trim() ?? "未知" }; } } diff --git a/WebApi/Controllers/Dto/PagedResponse.cs b/WebApi/Controllers/Dto/PagedResponse.cs index 5441be8..1cb5acb 100644 --- a/WebApi/Controllers/Dto/PagedResponse.cs +++ b/WebApi/Controllers/Dto/PagedResponse.cs @@ -2,7 +2,7 @@ public class PagedResponse : BaseResponse { - public long LastId { get; set; } = 0; + public long LastId { get; set; } /// /// 最后一条记录的时间(用于游标分页) diff --git a/WebApi/Controllers/EmailMessageController.cs b/WebApi/Controllers/EmailMessageController.cs index f2b09f7..f253349 100644 --- a/WebApi/Controllers/EmailMessageController.cs +++ b/WebApi/Controllers/EmailMessageController.cs @@ -1,6 +1,6 @@ using Service.EmailServices; -namespace WebApi.Controllers.EmailMessage; +namespace WebApi.Controllers; [ApiController] [Route("api/[controller]/[action]")] diff --git a/WebApi/Controllers/TransactionRecordController.cs b/WebApi/Controllers/TransactionRecordController.cs index d6f7297..068a0cd 100644 --- a/WebApi/Controllers/TransactionRecordController.cs +++ b/WebApi/Controllers/TransactionRecordController.cs @@ -268,9 +268,9 @@ public class TransactionRecordController( var statistics = await transactionRepository.GetDailyStatisticsAsync(year, month, savingClassify); var result = statistics.Select(s => new DailyStatisticsDto( - s.Key, - s.Value.count, - s.Value.expense, + s.Key, + s.Value.count, + s.Value.expense, s.Value.income, s.Value.saving )).ToList(); @@ -303,13 +303,13 @@ public class TransactionRecordController( var savingClassify = await configService.GetConfigByKeyAsync("SavingsCategories"); var statistics = await transactionRepository.GetDailyStatisticsByRangeAsync( - effectiveStartDate, + effectiveStartDate, effectiveEndDate, savingClassify); var result = statistics.Select(s => new DailyStatisticsDto( - s.Key, - s.Value.count, - s.Value.expense, + s.Key, + s.Value.count, + s.Value.expense, s.Value.income, s.Value.income - s.Value.expense )).ToList(); @@ -383,7 +383,8 @@ public class TransactionRecordController( } catch (Exception ex) { - logger.LogError(ex, "获取趋势统计数据失败,开始年份: {Year}, 开始月份: {Month}, 月份数: {Count}", startYear, startMonth, monthCount); + logger.LogError(ex, "获取趋势统计数据失败,开始年份: {Year}, 开始月份: {Month}, 月份数: {Count}", startYear, startMonth, + monthCount); return $"获取趋势统计数据失败: {ex.Message}".Fail>(); } } @@ -403,9 +404,16 @@ public class TransactionRecordController( return; } - await smartHandleService.AnalyzeBillAsync(request.UserInput, async chunk => + await smartHandleService.AnalyzeBillAsync(request.UserInput, async void (chunk) => { - await WriteEventAsync(chunk); + try + { + await WriteEventAsync(chunk); + } + catch (Exception e) + { + logger.LogError(e, "流式写入账单分析结果失败"); + } }); } @@ -490,11 +498,18 @@ public class TransactionRecordController( return; } - await smartHandleService.SmartClassifyAsync(request.TransactionIds.ToArray(), async chunk => + await smartHandleService.SmartClassifyAsync(request.TransactionIds.ToArray(), async void (chunk) => { - var (eventType, content) = chunk; - await TrySetUnconfirmedAsync(eventType, content); - await WriteEventAsync(eventType, content); + try + { + var (eventType, content) = chunk; + await TrySetUnconfirmedAsync(eventType, content); + await WriteEventAsync(eventType, content); + } + catch (Exception e) + { + logger.LogError(e, "流式写入智能分类结果失败"); + } }); await Response.Body.FlushAsync(); @@ -514,7 +529,7 @@ public class TransactionRecordController( var classify = jsonObject?["Classify"]?.GetValue() ?? string.Empty; var typeValue = jsonObject?["Type"]?.GetValue() ?? -1; - if(id == 0 || typeValue == -1 || string.IsNullOrEmpty(classify)) + if (id == 0 || typeValue == -1 || string.IsNullOrEmpty(classify)) { logger.LogWarning("解析智能分类结果时,发现无效数据,内容: {Content}", content); return; @@ -565,14 +580,17 @@ public class TransactionRecordController( { record.Type = item.Type.Value; } - if(!string.IsNullOrEmpty(record.Classify)) + + if (!string.IsNullOrEmpty(record.Classify)) { record.UnconfirmedClassify = null; } - if(record.Type == item.Type) + + if (record.Type == item.Type) { record.UnconfirmedType = TransactionType.None; } + var success = await transactionRepository.UpdateAsync(record); if (success) successCount++; @@ -735,18 +753,7 @@ public record CreateTransactionDto( decimal Amount, TransactionType Type, string? Classify -) -{ - public string OccurredAt { get; init; } = OccurredAt; - - public string? Reason { get; init; } = Reason; - - public decimal Amount { get; init; } = Amount; - - public TransactionType Type { get; init; } = Type; - - public string? Classify { get; init; } = Classify; -} +); /// /// 更新交易记录DTO @@ -758,20 +765,7 @@ public record UpdateTransactionDto( decimal Balance, TransactionType Type, string? Classify -) -{ - public long Id { get; init; } = Id; - - public string? Reason { get; init; } = Reason; - - public decimal Amount { get; init; } = Amount; - - public decimal Balance { get; init; } = Balance; - - public TransactionType Type { get; init; } = Type; - - public string? Classify { get; init; } = Classify; -} +); /// /// 日历统计响应DTO @@ -782,28 +776,14 @@ public record DailyStatisticsDto( decimal Expense, decimal Income, decimal Balance -) -{ - public string Date { get; init; } = Date; - - public int Count { get; init; } = Count; - - public decimal Expense { get; init; } = Expense; - - public decimal Income { get; init; } = Income; - - public decimal Balance { get; init; } = Balance; -} +); /// /// 智能分类请求DTO /// public record SmartClassifyRequest( List? TransactionIds = null -) -{ - public List? TransactionIds { get; init; } = TransactionIds; -} +); /// /// 批量更新分类项DTO @@ -812,14 +792,7 @@ public record BatchUpdateClassifyItem( long Id, string? Classify, TransactionType? Type = null -) -{ - public long Id { get; init; } = Id; - - public string? Classify { get; init; } = Classify; - - public TransactionType? Type { get; init; } = Type; -} +); /// /// 按摘要批量更新DTO @@ -828,24 +801,14 @@ public record BatchUpdateByReasonDto( string Reason, TransactionType Type, string Classify -) -{ - public string Reason { get; init; } = Reason; - - public TransactionType Type { get; init; } = Type; - - public string Classify { get; init; } = Classify; -} +); /// /// 账单分析请求DTO /// public record BillAnalysisRequest( string UserInput -) -{ - public string UserInput { get; init; } = UserInput; -} +); /// /// 抵账请求DTO @@ -853,23 +816,12 @@ public record BillAnalysisRequest( public record OffsetTransactionDto( long Id1, long Id2 -) -{ - public long Id1 { get; init; } = Id1; - - public long Id2 { get; init; } = Id2; -} +); public record ParseOneLineRequestDto( string Text -) -{ - public string Text { get; init; } = Text; -} +); public record ConfirmAllUnconfirmedRequestDto( long[] Ids -) -{ - public long[] Ids { get; init; } = Ids; -} \ No newline at end of file +); \ No newline at end of file diff --git a/WebApi/Program.cs b/WebApi/Program.cs index 51e9619..e4d2e27 100644 --- a/WebApi/Program.cs +++ b/WebApi/Program.cs @@ -22,21 +22,21 @@ builder.Host.UseSerilog((context, loggerConfig) => }); // Add services to the container. -builder.Services.AddControllers(options => +builder.Services.AddControllers(mvcOptions => { var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); - options.Filters.Add(new AuthorizeFilter(policy)); + mvcOptions.Filters.Add(new AuthorizeFilter(policy)); }); builder.Services.AddOpenApi(); builder.Services.AddHttpClient(); // 配置 CORS -builder.Services.AddCors(options => +builder.Services.AddCors(corsOptions => { - options.AddDefaultPolicy(policy => + corsOptions.AddDefaultPolicy(policy => { policy.WithOrigins("http://localhost:5173") .AllowAnyHeader() @@ -55,14 +55,14 @@ var jwtSettings = builder.Configuration.GetSection("JwtSettings"); var secretKey = jwtSettings["SecretKey"]!; var key = Encoding.UTF8.GetBytes(secretKey); -builder.Services.AddAuthentication(options => +builder.Services.AddAuthentication(authenticationOptions => { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + authenticationOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + authenticationOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) -.AddJwtBearer(options => +.AddJwtBearer(jwtBearerOptions => { - options.TokenValidationParameters = new TokenValidationParameters + jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, @@ -73,7 +73,7 @@ builder.Services.AddAuthentication(options => IssuerSigningKey = new SymmetricSecurityKey(key), ClockSkew = TimeSpan.Zero }; - options.Events = new JwtBearerEvents + jwtBearerOptions.Events = new JwtBearerEvents { OnChallenge = async context => { diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..f8c5909 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,46 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# + +################################################################################# +# WARNING: Do not store sensitive information in this file, # +# as its contents will be included in the Qodana report. # +################################################################################# +version: "1.0" + +#Specify IDE code to run analysis without container (Applied in CI/CD pipeline) +ide: QDNET + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +# Quality gate. Will fail the CI/CD pipeline if any condition is not met +# severityThresholds - configures maximum thresholds for different problem severities +# testCoverageThresholds - configures minimum code coverage on a whole project and newly added code +# Code Coverage is available in Ultimate and Ultimate Plus plans +#failureConditions: +# severityThresholds: +# any: 15 +# critical: 5 +# testCoverageThresholds: +# fresh: 70 +# total: 50