feat: 完成第三批任务 (Task 8.1-8.3, 5.3-5.4, 6.1-6.2)
Task 8.1-8.3 - 错误处理和日志系统: - GlobalExceptionHandler: 全局异常处理中间件 - 统一错误响应格式 (ErrorResponse) - 自动分类处理各种异常类型 - 支持 Serilog 日志配置 (appsettings.json) - 请求日志中间件集成 - 详细的日志输出和错误追踪 Task 5.3-5.4 - CLI 高级功能: - CliConfiguration: CLI 配置文件管理 - stats 命令:显示转换统计信息 - config 命令:配置 CLI 参数 - 支持用户级别配置文件 (~/.codeplay/config.json) - 可配置的默认语言和验证轮次 - 并发控制选项 Task 6.1-6.2 - 报告展示完善: - ReportView.vue: 报告管理界面 - 统计卡片展示(总转换、项目、问题、平均行数) - 报告列表表格(支持排序和筛选) - 报告详情对话框 - 代码对比视图 - TODO 和问题列表展示 - 导出和删除功能 - 路由配置更新 测试:42 个 (41 通过,1 跳过) ✅ 新增文件: - CodePlay.WebAPI/Middleware/GlobalExceptionHandler.cs - CodePlay.WebAPI/appsettings.json (Serilog 配置) - CodePlay.CLI/Config/CliConfiguration.cs - CodePlay.Web/src/views/ReportView.vue Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CodePlay.WebAPI.Middleware;
|
||||
|
||||
/// <summary>
|
||||
/// 全局异常处理中间件
|
||||
/// </summary>
|
||||
public class GlobalExceptionHandler
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<GlobalExceptionHandler> _logger;
|
||||
|
||||
public GlobalExceptionHandler(RequestDelegate next, ILogger<GlobalExceptionHandler> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HandleExceptionAsync(context, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||
{
|
||||
var response = context.Response;
|
||||
response.ContentType = "application/json";
|
||||
|
||||
var errorResponse = new ErrorResponse
|
||||
{
|
||||
RequestId = context.TraceIdentifier,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Path = context.Request.Path,
|
||||
Method = context.Request.Method
|
||||
};
|
||||
|
||||
switch (exception)
|
||||
{
|
||||
case UnauthorizedAccessException:
|
||||
response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
errorResponse.Code = "UNAUTHORIZED";
|
||||
errorResponse.Message = "未授权访问";
|
||||
_logger.LogWarning(exception, "未授权访问尝试");
|
||||
break;
|
||||
|
||||
case ArgumentException argumentEx:
|
||||
response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
errorResponse.Code = "BAD_REQUEST";
|
||||
errorResponse.Message = argumentEx.Message;
|
||||
_logger.LogWarning(exception, "参数验证失败");
|
||||
break;
|
||||
|
||||
case KeyNotFoundException:
|
||||
response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
errorResponse.Code = "NOT_FOUND";
|
||||
errorResponse.Message = "资源不存在";
|
||||
_logger.LogWarning(exception, "资源未找到");
|
||||
break;
|
||||
|
||||
case InvalidOperationException invalidEx:
|
||||
response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||
errorResponse.Code = "CONFLICT";
|
||||
errorResponse.Message = invalidEx.Message;
|
||||
_logger.LogWarning(exception, "操作无效");
|
||||
break;
|
||||
|
||||
case TimeoutException:
|
||||
response.StatusCode = (int)HttpStatusCode.GatewayTimeout;
|
||||
errorResponse.Code = "TIMEOUT";
|
||||
errorResponse.Message = "请求超时";
|
||||
_logger.LogError(exception, "请求超时");
|
||||
break;
|
||||
|
||||
default:
|
||||
response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
errorResponse.Code = "INTERNAL_ERROR";
|
||||
errorResponse.Message = "服务器内部错误";
|
||||
_logger.LogError(exception, "未处理的异常");
|
||||
break;
|
||||
}
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
var jsonResponse = JsonSerializer.Serialize(errorResponse, options);
|
||||
await response.WriteAsync(jsonResponse);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误响应模型
|
||||
/// </summary>
|
||||
public class ErrorResponse
|
||||
{
|
||||
public string RequestId { get; set; } = "";
|
||||
public DateTime Timestamp { get; set; }
|
||||
public string Code { get; set; } = "";
|
||||
public string Message { get; set; } = "";
|
||||
public string Path { get; set; } = "";
|
||||
public string Method { get; set; } = "";
|
||||
public Dictionary<string, string>? Details { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中间件扩展
|
||||
/// </summary>
|
||||
public static class GlobalExceptionHandlerExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder app)
|
||||
{
|
||||
return app.UseMiddleware<GlobalExceptionHandler>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"System": "Warning"
|
||||
},
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"System": "Warning"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "Console",
|
||||
"Args": {
|
||||
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": {
|
||||
"path": "logs/codeplay-.log",
|
||||
"rollingInterval": "Day",
|
||||
"retainedFileCountLimit": 30,
|
||||
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message}{NewLine}{Exception}",
|
||||
"fileSizeLimitBytes": 10485760
|
||||
}
|
||||
}
|
||||
],
|
||||
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Cors": {
|
||||
"AllowedOrigins": [ "http://localhost:5173", "http://localhost:3000" ]
|
||||
},
|
||||
"RateLimit": {
|
||||
"MaxRequestsPerMinute": 60
|
||||
},
|
||||
"Jwt": {
|
||||
"SecretKey": "YourSuperSecretKeyThatIsAtLeast32CharactersLongForSecurity",
|
||||
"Issuer": "CodePlay",
|
||||
"Audience": "CodePlayUsers",
|
||||
"ExpirationMinutes": 60
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user