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 <[email protected]>
434 lines
16 KiB
C#
434 lines
16 KiB
C#
using System.CommandLine;
|
|
using System.CommandLine.Builder;
|
|
using System.CommandLine.Parsing;
|
|
using System.Text.Json;
|
|
using CodePlay.Core.Models;
|
|
using CodePlay.Core.Common;
|
|
using CodePlay.Core.Services;
|
|
|
|
namespace CodePlay.CLI;
|
|
|
|
public class Program
|
|
{
|
|
public static async Task<int> Main(string[] args)
|
|
{
|
|
// 定义源语言选项
|
|
var sourceLanguageOption = new Option<LanguageType>(
|
|
name: "--source-language",
|
|
description: "源语言 (CSharp, Java, CPlusPlus)"
|
|
);
|
|
sourceLanguageOption.AddAlias("-s");
|
|
sourceLanguageOption.IsRequired = true;
|
|
|
|
// 定义目标语言选项
|
|
var targetLanguageOption = new Option<LanguageType>(
|
|
name: "--target-language",
|
|
description: "目标语言 (CSharp, Java, CPlusPlus)"
|
|
);
|
|
targetLanguageOption.AddAlias("-t");
|
|
targetLanguageOption.IsRequired = true;
|
|
|
|
// 定义输入文件选项
|
|
var inputOption = new Option<FileInfo>(
|
|
name: "--input",
|
|
description: "输入文件路径或目录"
|
|
);
|
|
inputOption.AddAlias("-i");
|
|
inputOption.IsRequired = true;
|
|
|
|
// 定义输出文件/目录选项
|
|
var outputOption = new Option<FileInfo>(
|
|
name: "--output",
|
|
description: "输出文件路径或目录"
|
|
);
|
|
outputOption.AddAlias("-o");
|
|
|
|
// 定义批量转换模式选项
|
|
var batchOption = new Option<bool>(
|
|
name: "--batch",
|
|
description: "启用批量转换模式(目录转换)"
|
|
);
|
|
batchOption.AddAlias("-b");
|
|
|
|
// 定义递归子目录选项
|
|
var recursiveOption = new Option<bool>(
|
|
name: "--recursive",
|
|
description: "递归处理子目录",
|
|
getDefaultValue: () => true
|
|
);
|
|
recursiveOption.AddAlias("-r");
|
|
|
|
// 定义验证轮次选项
|
|
var validationRoundsOption = new Option<int>(
|
|
name: "--validation-rounds",
|
|
getDefaultValue: () => 2,
|
|
description: "验证轮次 (1-3)"
|
|
);
|
|
validationRoundsOption.AddAlias("-v");
|
|
|
|
// 定义配置文件选项
|
|
var configOption = new Option<FileInfo>(
|
|
name: "--config",
|
|
description: "配置文件路径"
|
|
);
|
|
configOption.AddAlias("-c");
|
|
|
|
// 定义详细输出选项
|
|
var verboseOption = new Option<bool>(
|
|
name: "--verbose",
|
|
description: "显示详细输出信息"
|
|
);
|
|
verboseOption.AddAlias("--verbose");
|
|
|
|
// 定义转换命令
|
|
var convertCommand = new Command("convert", "转换代码文件或目录")
|
|
{
|
|
sourceLanguageOption,
|
|
targetLanguageOption,
|
|
inputOption,
|
|
outputOption,
|
|
batchOption,
|
|
recursiveOption,
|
|
validationRoundsOption,
|
|
configOption,
|
|
verboseOption
|
|
};
|
|
|
|
convertCommand.SetHandler(async (context) =>
|
|
{
|
|
var sourceLang = context.ParseResult.GetValueForOption(sourceLanguageOption);
|
|
var targetLang = context.ParseResult.GetValueForOption(targetLanguageOption);
|
|
var inputFile = context.ParseResult.GetValueForOption(inputOption);
|
|
var outputFile = context.ParseResult.GetValueForOption(outputOption);
|
|
var isBatch = context.ParseResult.GetValueForOption(batchOption);
|
|
var isRecursive = context.ParseResult.GetValueForOption(recursiveOption);
|
|
var validationRounds = context.ParseResult.GetValueForOption(validationRoundsOption);
|
|
var configFile = context.ParseResult.GetValueForOption(configOption);
|
|
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
|
|
|
try
|
|
{
|
|
if (isBatch || inputFile.Attributes.HasFlag(FileAttributes.Directory))
|
|
{
|
|
// 批量转换模式
|
|
Console.WriteLine("📁 批量转换模式启动");
|
|
Console.WriteLine($"源目录:{inputFile.FullName}");
|
|
|
|
var batchService = new BatchConversionService(
|
|
new ConversionService(),
|
|
new ReportStorageService()
|
|
);
|
|
|
|
var options = new ConversionOptions
|
|
{
|
|
KeepComments = true,
|
|
KeepDocStrings = true
|
|
};
|
|
|
|
var targetDir = outputFile?.FullName ??
|
|
Path.Combine(Path.GetDirectoryName(inputFile.FullName)!,
|
|
$"{sourceLang}_to_{targetLang}_output");
|
|
|
|
Console.WriteLine($"目标目录:{targetDir}");
|
|
Console.WriteLine($"递归:{isRecursive}");
|
|
Console.WriteLine();
|
|
|
|
var result = await batchService.ConvertDirectoryAsync(
|
|
inputFile.FullName,
|
|
targetDir,
|
|
sourceLang,
|
|
targetLang,
|
|
options,
|
|
context.GetCancellationToken()
|
|
);
|
|
|
|
PrintBatchResult(result, verbose);
|
|
context.ExitCode = result.Success ? 0 : 1;
|
|
}
|
|
else
|
|
{
|
|
// 单文件转换模式
|
|
Console.WriteLine($"📄 正在读取文件:{inputFile.FullName}");
|
|
var sourceCode = await File.ReadAllTextAsync(inputFile.FullName);
|
|
|
|
var options = LoadConfiguration(configFile.FullName);
|
|
|
|
Console.WriteLine($"$\color{green}{正在转换:{sourceLang} → {targetLang}}");
|
|
var conversionService = new ConversionService();
|
|
var result = await conversionService.ConvertAsync(
|
|
sourceCode, sourceLang, targetLang, options, context.GetCancellationToken());
|
|
|
|
if (result.Success)
|
|
{
|
|
Console.WriteLine($"✅ 转换成功!");
|
|
Console.WriteLine($"转换行数:{result.Report?.LinesConverted}");
|
|
Console.WriteLine($"转换类数:{result.Report?.ClassesConverted}");
|
|
Console.WriteLine($"转换方法数:{result.Report?.MethodsConverted}");
|
|
|
|
if (outputFile != null)
|
|
{
|
|
await File.WriteAllTextAsync(outputFile.FullName, result.TransformedCode);
|
|
Console.WriteLine($"已输出到:{outputFile.FullName}");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("\n==== 转换结果 ====");
|
|
Console.WriteLine(result.TransformedCode);
|
|
}
|
|
|
|
PrintConversionDetails(result, verbose);
|
|
context.ExitCode = 0;
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"❌ 转换失败:{result.ErrorMessage}");
|
|
context.ExitCode = 1;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"❌ 错误:{ex.Message}");
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine($"详情:{ex}");
|
|
}
|
|
context.ExitCode = 1;
|
|
}
|
|
});
|
|
|
|
// 定义 list 命令
|
|
var listCommand = new Command("list", "列出支持的转换");
|
|
listCommand.SetHandler((context) =>
|
|
{
|
|
var conversionService = new ConversionService();
|
|
var supported = conversionService.GetSupportedConversions();
|
|
|
|
Console.WriteLine("支持的转换:");
|
|
foreach (var (source, target) in supported)
|
|
{
|
|
Console.WriteLine($" {source} → {target}");
|
|
}
|
|
|
|
context.ExitCode = 0;
|
|
});
|
|
|
|
// 定义 check 命令
|
|
var checkCommand = new Command("check", "检查是否支持指定的转换")
|
|
{
|
|
sourceLanguageOption,
|
|
targetLanguageOption
|
|
};
|
|
checkCommand.SetHandler((context) =>
|
|
{
|
|
var sourceLang = context.ParseResult.GetValueForOption(sourceLanguageOption);
|
|
var targetLang = context.ParseResult.GetValueForOption(targetLanguageOption);
|
|
|
|
var conversionService = new ConversionService();
|
|
var isSupported = conversionService.IsConversionSupported(sourceLang, targetLang);
|
|
|
|
if (isSupported)
|
|
{
|
|
Console.WriteLine($"✅ 支持 {sourceLang} → {targetLang} 转换");
|
|
context.ExitCode = 0;
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"❌ 不支持 {sourceLang} → {targetLang} 转换");
|
|
context.ExitCode = 1;
|
|
}
|
|
});
|
|
|
|
// 创建根命令
|
|
var rootCommand = new RootCommand("CodePlay 代码转换工具 - 支持 C#、Java、C++ 之间的代码转换")
|
|
{
|
|
convertCommand,
|
|
listCommand,
|
|
checkCommand
|
|
};
|
|
|
|
var parser = new CommandLineBuilder(rootCommand)
|
|
.UseDefaults()
|
|
.Build();
|
|
|
|
return await parser.InvokeAsync(args);
|
|
}
|
|
|
|
private static void PrintBatchResult(BatchConversionResult result, bool verbose)
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("==== 批量转换完成 ====");
|
|
Console.WriteLine($"源目录:{result.SourceDirectory}");
|
|
Console.WriteLine($"目标目录:{result.TargetDirectory}");
|
|
Console.WriteLine($"总文件数:{result.TotalFiles}");
|
|
Console.WriteLine($"成功:{result.SuccessfulFiles}");
|
|
Console.WriteLine($"失败:{result.FailedFiles}");
|
|
Console.WriteLine($"耗时:{result.Duration.TotalSeconds:F2} 秒");
|
|
|
|
if (result.ConvertedFiles.Any())
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("成功转换的文件:");
|
|
foreach (var file in result.ConvertedFiles)
|
|
{
|
|
Console.WriteLine($" ✅ {Path.GetFileName(file.SourceFile)} → {Path.GetFileName(file.TargetFile)}");
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine($" 行数:{file.LinesConverted}, 类:{file.ClassesConverted}, 方法:{file.MethodsConverted}");
|
|
if (file.Warnings > 0 || file.Issues > 0)
|
|
{
|
|
Console.WriteLine($" ⚠️ 警告:{file.Warnings}, 问题:{file.Issues}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.FailedFileList.Any())
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("转换失败的文件:");
|
|
foreach (var file in result.FailedFileList)
|
|
{
|
|
Console.WriteLine($" ❌ {Path.GetFileName(file.SourceFile)}");
|
|
if (verbose)
|
|
{
|
|
Console.WriteLine($" 错误:{file.ErrorMessage}");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.Success)
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("🎉 所有文件转换成功!");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine($"⚠️ {result.FailedFiles} 个文件转换失败");
|
|
}
|
|
}
|
|
|
|
private static void PrintConversionDetails(ConversionResult result, bool verbose)
|
|
{
|
|
if (!verbose) return;
|
|
|
|
if (result.Report?.TodoItems.Count > 0)
|
|
{
|
|
Console.WriteLine("\n⚠️ 需要注意的 TODO 项:");
|
|
foreach (var todo in result.Report.TodoItems)
|
|
{
|
|
Console.WriteLine($" - {todo.Description}");
|
|
Console.WriteLine($" 原因:{todo.WhyNotDirect}");
|
|
Console.WriteLine($" 建议:{todo.RecommendedAlternative}");
|
|
}
|
|
}
|
|
|
|
if (result.Report?.Issues.Count > 0)
|
|
{
|
|
Console.WriteLine("\n⚠️ 需要注意的问题:");
|
|
foreach (var issue in result.Report.Issues)
|
|
{
|
|
Console.WriteLine($" - {issue.Description}");
|
|
Console.WriteLine($" 建议:{issue.Suggestion}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static ConversionOptions? LoadConfiguration(string? configPath)
|
|
{
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(configPath) && File.Exists(configPath))
|
|
{
|
|
if (configPath.EndsWith(".json"))
|
|
{
|
|
var json = File.ReadAllText(configPath);
|
|
var options = JsonSerializer.Deserialize<ConversionOptions>(json);
|
|
return options;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"⚠️ 加载配置文件失败:{ex.Message},使用默认配置");
|
|
}
|
|
|
|
return new ConversionOptions
|
|
{
|
|
KeepComments = true,
|
|
KeepDocStrings = true
|
|
};
|
|
}
|
|
}
|
|
|
|
// 定义 stats 命令 - 显示转换统计
|
|
var statsCommand = new Command("stats", "显示转换统计信息");
|
|
statsCommand.SetHandler(async (context) =>
|
|
{
|
|
Console.WriteLine("📊 CodePlay 转换统计");
|
|
Console.WriteLine("====================");
|
|
|
|
var reportService = new ReportStorageService();
|
|
var stats = await reportService.GetStatisticsAsync();
|
|
|
|
Console.WriteLine($"总转换次数:{stats.TotalConversions}");
|
|
Console.WriteLine($"总项目数:{stats.TotalProjects}");
|
|
Console.WriteLine($"平均每行转换:{stats.AverageLinesConverted:F0}");
|
|
Console.WriteLine($"总问题数:{stats.TotalIssuesDetected}");
|
|
Console.WriteLine($"总 TODO 数:{stats.TotalTODOs}");
|
|
|
|
if (stats.ConversionsByLanguage.Any())
|
|
{
|
|
Console.WriteLine("\n按目标语言统计:");
|
|
foreach (var (lang, count) in stats.ConversionsByLanguage)
|
|
{
|
|
Console.WriteLine($" {lang}: {count} 次");
|
|
}
|
|
}
|
|
|
|
context.ExitCode = 0;
|
|
});
|
|
|
|
// 定义 config 命令 - 配置 CLI
|
|
var configCommand = new Command("config", "配置 CLI 参数")
|
|
{
|
|
new Option<string>("--set", "设置配置项 (key=value)"),
|
|
new Option<bool>("--show", "显示当前配置")
|
|
};
|
|
configCommand.SetHandler(async (context) =>
|
|
{
|
|
var show = context.ParseResult.GetValueForOption(configCommand.Options.First(o => o.Name == "--show")!);
|
|
var set = context.ParseResult.GetValueForOption(configCommand.Options.First(o => o.Name == "--set")!);
|
|
|
|
var config = await Config.CliConfiguration.LoadAsync();
|
|
|
|
if (show)
|
|
{
|
|
Console.WriteLine("当前配置:");
|
|
Console.WriteLine($" 默认源语言:{config.DefaultSourceLanguage}");
|
|
Console.WriteLine($" 默认目标语言:{config.DefaultTargetLanguage}");
|
|
Console.WriteLine($" 验证轮次:{config.DefaultValidationRounds}");
|
|
Console.WriteLine($" 保持注释:{config.KeepComments}");
|
|
Console.WriteLine($" 并发数:{config.MaxConcurrency}");
|
|
}
|
|
else if (!string.IsNullOrEmpty(set))
|
|
{
|
|
var parts = set.Split('=');
|
|
if (parts.Length == 2)
|
|
{
|
|
var key = parts[0];
|
|
var value = parts[1];
|
|
|
|
// TODO: 动态设置配置
|
|
Console.WriteLine($"✅ 配置已更新:{key}={value}");
|
|
}
|
|
}
|
|
|
|
context.ExitCode = 0;
|
|
});
|
|
|
|
// 添加到根命令
|
|
rootCommand.Add(statsCommand);
|
|
rootCommand.Add(configCommand);
|