diff --git a/CodePlay.Core/Converters/CSharpToCppConverter.cs b/CodePlay.Core/Converters/CSharpToCppConverter.cs
new file mode 100644
index 0000000..d9c53fa
--- /dev/null
+++ b/CodePlay.Core/Converters/CSharpToCppConverter.cs
@@ -0,0 +1,84 @@
+using CodePlay.Core.Interfaces;
+using CodePlay.Core.Models;
+using System.Text;
+using CodePlay.Core.Common;
+
+namespace CodePlay.Core.Converters;
+
+///
+/// C# 到 C++ 转换器
+///
+public class CSharpToCppConverter : IConverter
+{
+ public async Task ConvertAsync(
+ Interfaces.SyntaxTree syntaxTree,
+ LanguageType targetLanguage,
+ ConversionOptions? options = null,
+ CancellationToken cancellationToken = default)
+ {
+ var result = new ConversionResult
+ {
+ Success = false,
+ Warnings = new List(),
+ Report = new ConversionReport()
+ };
+
+ if (targetLanguage != LanguageType.CPlusPlus)
+ {
+ result.ErrorMessage = "This converter only supports C# to C++ conversion";
+ return result;
+ }
+
+ try
+ {
+ var cppCode = ConvertToCpp(syntaxTree, result.Report, options);
+
+ result.Success = true;
+ result.TransformedCode = cppCode;
+ result.Report.ClassesConverted = 1;
+ }
+ catch (Exception ex)
+ {
+ result.Success = false;
+ result.ErrorMessage = ex.Message;
+ }
+
+ return await Task.FromResult(result);
+ }
+
+ private string ConvertToCpp(Interfaces.SyntaxTree parsed, ConversionReport report, ConversionOptions? options)
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine();
+
+ ExtractAndConvertClasses(parsed.Root, sb, report);
+
+ return sb.ToString();
+ }
+
+ private void ExtractAndConvertClasses(SyntaxNode node, StringBuilder sb, ConversionReport report)
+ {
+ if (node.Type == SyntaxNodeType.Class)
+ {
+ var className = node.Metadata.TryGetValue("Name", out var name) ? name?.ToString() ?? "Unknown" : "Unknown";
+
+ sb.AppendLine($"class {className} {{");
+ sb.AppendLine("public:");
+ sb.AppendLine($" {className}() {{}}");
+ sb.AppendLine("};");
+ sb.AppendLine();
+
+ report.ClassesConverted++;
+ }
+
+ foreach (var child in node.Children)
+ {
+ ExtractAndConvertClasses(child, sb, report);
+ }
+ }
+}
diff --git a/CodePlay.Core/Converters/JavaToCppConverter.cs b/CodePlay.Core/Converters/JavaToCppConverter.cs
new file mode 100644
index 0000000..394691b
--- /dev/null
+++ b/CodePlay.Core/Converters/JavaToCppConverter.cs
@@ -0,0 +1,84 @@
+using CodePlay.Core.Interfaces;
+using CodePlay.Core.Models;
+using System.Text;
+using CodePlay.Core.Common;
+
+namespace CodePlay.Core.Converters;
+
+///
+/// Java 到 C++ 转换器
+///
+public class JavaToCppConverter : IConverter
+{
+ public async Task ConvertAsync(
+ Interfaces.SyntaxTree syntaxTree,
+ LanguageType targetLanguage,
+ ConversionOptions? options = null,
+ CancellationToken cancellationToken = default)
+ {
+ var result = new ConversionResult
+ {
+ Success = false,
+ Warnings = new List(),
+ Report = new ConversionReport()
+ };
+
+ if (targetLanguage != LanguageType.CPlusPlus)
+ {
+ result.ErrorMessage = "This converter only supports Java to C++ conversion";
+ return result;
+ }
+
+ try
+ {
+ var cppCode = ConvertToCpp(syntaxTree, result.Report, options);
+
+ result.Success = true;
+ result.TransformedCode = cppCode;
+ result.Report.ClassesConverted = 1;
+ }
+ catch (Exception ex)
+ {
+ result.Success = false;
+ result.ErrorMessage = ex.Message;
+ }
+
+ return await Task.FromResult(result);
+ }
+
+ private string ConvertToCpp(Interfaces.SyntaxTree parsed, ConversionReport report, ConversionOptions? options)
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine("#include ");
+ sb.AppendLine();
+
+ ExtractAndConvertClasses(parsed.Root, sb, report);
+
+ return sb.ToString();
+ }
+
+ private void ExtractAndConvertClasses(SyntaxNode node, StringBuilder sb, ConversionReport report)
+ {
+ if (node.Type == SyntaxNodeType.Class)
+ {
+ var className = node.Metadata.TryGetValue("Name", out var name) ? name?.ToString() ?? "Unknown" : "Unknown";
+
+ sb.AppendLine($"class {className} {{");
+ sb.AppendLine("public:");
+ sb.AppendLine($" {className}() {{}}");
+ sb.AppendLine("};");
+ sb.AppendLine();
+
+ report.ClassesConverted++;
+ }
+
+ foreach (var child in node.Children)
+ {
+ ExtractAndConvertClasses(child, sb, report);
+ }
+ }
+}
diff --git a/CodePlay.Core/Parsers/CppParser.cs b/CodePlay.Core/Parsers/CppParser.cs
new file mode 100644
index 0000000..5847157
--- /dev/null
+++ b/CodePlay.Core/Parsers/CppParser.cs
@@ -0,0 +1,52 @@
+using CodePlay.Core.Interfaces;
+using CodePlay.Core.Models;
+using CodePlay.Core.Common;
+
+namespace CodePlay.Core.Parsers;
+
+///
+/// C++ 解析器
+///
+public class CppParser : BaseParser
+{
+ public override LanguageType SupportedLanguage => LanguageType.CPlusPlus;
+
+ public override Task ParseAsync(string sourceCode, CancellationToken cancellationToken = default)
+ {
+ var tree = CreateSyntaxTree();
+ tree.SourceCode = sourceCode;
+ tree.Root = ParseRoot(sourceCode);
+
+ foreach (var line in sourceCode.Split('\n'))
+ {
+ if (line.Trim().StartsWith("//"))
+ {
+ AddComment(tree, line.Trim().TrimStart('/').Trim(), CommentType.SingleLine, 0);
+ }
+ }
+
+ return Task.FromResult(tree);
+ }
+
+ private SyntaxNode ParseRoot(string sourceCode)
+ {
+ var root = new SyntaxNode { Type = SyntaxNodeType.CompilationUnit, Text = sourceCode };
+
+ var classPattern = @"(public|private|protected)?\s*class\s+(\w+)";
+ var matches = System.Text.RegularExpressions.Regex.Matches(sourceCode, classPattern);
+
+ foreach (System.Text.RegularExpressions.Match match in matches)
+ {
+ var className = match.Groups[2].Value;
+ var classNode = new SyntaxNode
+ {
+ Type = SyntaxNodeType.Class,
+ Text = match.Value,
+ Metadata = { ["Name"] = className }
+ };
+ root.Children.Add(classNode);
+ }
+
+ return root;
+ }
+}
diff --git a/CodePlay.Core/Validators/CppCompilerValidator.cs b/CodePlay.Core/Validators/CppCompilerValidator.cs
new file mode 100644
index 0000000..14d70a9
--- /dev/null
+++ b/CodePlay.Core/Validators/CppCompilerValidator.cs
@@ -0,0 +1,52 @@
+using System.Diagnostics;
+using CodePlay.Core.Interfaces;
+using CodePlay.Core.Models;
+
+namespace CodePlay.Core.Validators;
+
+///
+/// C++ 编译验证器
+///
+public class CppCompilerValidator
+{
+ public string Language => "C++";
+
+ public async Task ValidateAsync(string code, string? outputPath = null, CancellationToken cancellationToken = default)
+ {
+ var report = new ConversionReport();
+
+ var lines = code.Split('\n');
+ for (int i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i].Trim();
+ if (line.Length > 0 &&
+ !line.StartsWith("#") &&
+ !line.StartsWith("//") &&
+ !line.EndsWith(";") &&
+ !line.EndsWith("{") &&
+ !line.EndsWith("}") &&
+ !line.EndsWith(")") &&
+ !line.StartsWith("class") &&
+ !line.StartsWith("namespace"))
+ {
+ report.TodoItems.Add(new TodoItem
+ {
+ Description = $"可能缺少分号 (行 {i + 1})",
+ LineNumber = i + 1,
+ OriginalSyntax = "语句",
+ WhyNotDirect = "语法检查提示"
+ });
+ }
+ }
+
+ await Task.CompletedTask;
+
+ report.IssueCount = report.TodoItems.Count;
+
+ return new ConversionResult
+ {
+ TransformedCode = code,
+ Report = report
+ };
+ }
+}
diff --git a/CodePlay.E2E/package-lock.json b/CodePlay.E2E/package-lock.json
new file mode 100644
index 0000000..b7c1afe
--- /dev/null
+++ b/CodePlay.E2E/package-lock.json
@@ -0,0 +1,78 @@
+{
+ "name": "codeplay-e2e",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "codeplay-e2e",
+ "version": "1.0.0",
+ "devDependencies": {
+ "@playwright/test": "^1.40.0"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
+ "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.60.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
+ "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.60.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.60.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
+ "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ }
+ }
+}
diff --git a/CodePlay.E2E/package.json b/CodePlay.E2E/package.json
new file mode 100644
index 0000000..414bc85
--- /dev/null
+++ b/CodePlay.E2E/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "codeplay-e2e",
+ "version": "1.0.0",
+ "description": "CodePlay E2E Tests with Playwright",
+ "scripts": {
+ "test": "playwright test",
+ "test:ui": "playwright test --ui",
+ "test:headed": "playwright test --headed",
+ "test:debug": "playwright test --debug"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.40.0"
+ }
+}
diff --git a/CodePlay.E2E/playwright.config.ts b/CodePlay.E2E/playwright.config.ts
new file mode 100644
index 0000000..c314d82
--- /dev/null
+++ b/CodePlay.E2E/playwright.config.ts
@@ -0,0 +1,28 @@
+import { defineConfig, devices } from '@playwright/test';
+
+export default defineConfig({
+ testDir: './tests',
+ fullyParallel: true,
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 2 : 0,
+ workers: process.env.CI ? 1 : undefined,
+ reporter: 'html',
+ use: {
+ baseURL: 'http://localhost:5173',
+ trace: 'on-first-retry',
+ },
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+ ],
+});
diff --git a/CodePlay.E2E/tests/auth.spec.ts b/CodePlay.E2E/tests/auth.spec.ts
new file mode 100644
index 0000000..691d3bd
--- /dev/null
+++ b/CodePlay.E2E/tests/auth.spec.ts
@@ -0,0 +1,25 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('CodePlay 认证功能', () => {
+ test('登录成功', async ({ page }) => {
+ await page.goto('/login');
+
+ // 填写登录表单
+ await page.fill('input[name="username"]', 'admin');
+ await page.fill('input[name="password"]', 'admin123');
+
+ // 提交登录
+ await page.click('button:has-text("登录")');
+ await page.waitForTimeout(1000);
+
+ // 验证登录成功,跳转到首页
+ await expect(page).toHaveURL('/');
+ });
+
+ test('未认证用户重定向', async ({ page }) => {
+ await page.goto('/converter');
+
+ // 验证被重定向到登录页
+ await expect(page).toHaveURL('/login');
+ });
+});
diff --git a/CodePlay.E2E/tests/converter.spec.ts b/CodePlay.E2E/tests/converter.spec.ts
new file mode 100644
index 0000000..f7748aa
--- /dev/null
+++ b/CodePlay.E2E/tests/converter.spec.ts
@@ -0,0 +1,55 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('CodePlay 转换功能', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/');
+ });
+
+ test('页面加载成功', async ({ page }) => {
+ await expect(page).toHaveTitle(/CodePlay/);
+ await expect(page.locator('h1')).toBeVisible();
+ });
+
+ test('C# 转 Java 转换', async ({ page }) => {
+ // 输入 C# 代码
+ const csharpCode = `
+namespace Test
+{
+ public class Calculator
+ {
+ public int Add(int a, int b)
+ {
+ return a + b;
+ }
+ }
+}`;
+
+ await page.locator('textarea').first().fill(csharpCode);
+
+ // 选择源语言和目标语言
+ await page.selectOption('select[name="sourceLanguage"]', 'CSharp');
+ await page.selectOption('select[name="targetLanguage"]', 'Java');
+
+ // 点击转换按钮
+ await page.click('button:has-text("转换")');
+
+ // 等待转换结果
+ await page.waitForTimeout(2000);
+
+ // 验证结果
+ const resultArea = page.locator('textarea').nth(1);
+ await expect(resultArea).toBeVisible();
+
+ const result = await resultArea.inputValue();
+ expect(result).toContain('public class Calculator');
+ expect(result).toContain('public int Add');
+ });
+
+ test('显示转换统计', async ({ page }) => {
+ await page.click('button:has-text("转换")');
+ await page.waitForTimeout(2000);
+
+ // 验证统计信息可见
+ await expect(page.locator('.statistics')).toBeVisible();
+ });
+});
diff --git a/CodePlay.E2E/tests/project.spec.ts b/CodePlay.E2E/tests/project.spec.ts
new file mode 100644
index 0000000..39e25e0
--- /dev/null
+++ b/CodePlay.E2E/tests/project.spec.ts
@@ -0,0 +1,29 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('CodePlay 项目管理', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/projects');
+ });
+
+ test('创建新项目', async ({ page }) => {
+ // 点击新建按钮
+ await page.click('button:has-text("新建")');
+
+ // 填写项目信息
+ await page.fill('input[name="name"]', '测试项目');
+ await page.selectOption('select[name="sourceLanguage"]', 'CSharp');
+ await page.selectOption('select[name="targetLanguage"]', 'Java');
+
+ // 提交
+ await page.click('button:has-text("保存")');
+ await page.waitForTimeout(1000);
+
+ // 验证项目创建成功
+ await expect(page.locator('.el-message')).toContainText('成功');
+ });
+
+ test('项目列表显示', async ({ page }) => {
+ // 验证项目列表可见
+ await expect(page.locator('.project-list')).toBeVisible();
+ });
+});