feat: 完成第一批高优先级任务 (Task 4.5, 4.7, 7.3)

Task 4.5 - 转换界面完善:
- ConverterView.vue: 完整转换界面
- 集成 Monaco Editor 代码编辑器
- 语言选择器 (C#/Java)
- 验证轮次选择 (1-3 轮)
- 转换结果显示
- 转换报告弹窗 (TODO 和问题)
- 光标位置显示
- 复制结果功能
- 状态栏统计

Task 4.7 - 项目管理界面:
- ProjectView.vue: 项目管理页面
- 项目列表展示
- 新建项目对话框
- 项目详情和转换历史
- 项目删除功能
- 路由配置更新

Task 7.3 - 数据库持久化:
- CodePlay.Persistence 项目创建
- AppDbContext: SQLite DbContext
- ConversionReport 和 ProjectInfo 数据模型
- DatabaseStorageService: IReportStorageService 实现
- 支持报告存储、查询、删除
- 统计信息聚合
- AddPersistence 扩展方法

测试:42 个测试(41 通过,1 跳过)

新增文件:
- CodePlay.Web/src/views/ConverterView.vue
- CodePlay.Web/src/views/ProjectView.vue
- CodePlay.Persistence/AppDbContext.cs
- CodePlay.Persistence/DatabaseStorageService.cs
- CodePlay.Web/src/router/index.ts (更新)
Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
This commit is contained in:
monkeycode-ai
2026-06-04 00:38:53 +00:00
parent 4c94bdf666
commit 6745fba6ba
7 changed files with 846 additions and 1 deletions
+31
View File
@@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
using CodePlay.Core.Models;
namespace CodePlay.Persistence;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<ConversionReport> ConversionReports { get; set; }
public DbSet<ProjectInfo> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ConversionReport>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(50);
entity.Property(e => e.ProjectId).HasMaxLength(50);
entity.HasIndex(e => e.ProjectId);
entity.HasIndex(e => e.CreatedAt);
});
modelBuilder.Entity<ProjectInfo>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(50);
entity.Property(e => e.Name).HasMaxLength(200);
});
}
}
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodePlay.Core\CodePlay.Core.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,102 @@
using Microsoft.EntityFrameworkCore;
using CodePlay.Core.Models;
using CodePlay.Core.Services;
using CodePlay.Core.Common;
namespace CodePlay.Persistence;
public class DatabaseStorageService : IReportStorageService
{
private readonly AppDbContext _context;
public DatabaseStorageService(AppDbContext context)
{
_context = context;
}
public async Task<ConversionReport> SaveReportAsync(ConversionReport report, string? projectId = null)
{
if (string.IsNullOrEmpty(report.Id))
report.Id = Guid.NewGuid().ToString("N")[..20];
report.ProjectId = projectId ?? "default";
report.CreatedAt = DateTime.UtcNow;
_context.ConversionReports.Add(report);
await _context.SaveChangesAsync();
// 更新项目统计
if (projectId != null)
{
var project = await _context.Projects.FindAsync(projectId);
if (project != null)
{
project.TotalConversions++;
project.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
}
return report;
}
public async Task<ConversionReport?> GetReportAsync(string reportId)
{
return await _context.ConversionReports.FindAsync(reportId);
}
public async Task<List<ConversionReport>> GetReportsByProjectAsync(string projectId)
{
return await _context.ConversionReports
.Where(r => r.ProjectId == projectId)
.OrderByDescending(r => r.CreatedAt)
.ToListAsync();
}
public async Task<List<ConversionReport>> GetAllReportsAsync()
{
return await _context.ConversionReports
.OrderByDescending(r => r.CreatedAt)
.ToListAsync();
}
public async Task DeleteReportAsync(string reportId)
{
var report = await _context.ConversionReports.FindAsync(reportId);
if (report != null)
{
_context.ConversionReports.Remove(report);
await _context.SaveChangesAsync();
}
}
public async Task<ConversionStatistics> GetStatisticsAsync()
{
var reports = await _context.ConversionReports.ToListAsync();
var projects = await _context.Projects.ToListAsync();
return new ConversionStatistics
{
TotalConversions = reports.Count,
TotalProjects = projects.Count,
ConversionsByLanguage = reports.GroupBy(r => r.TargetLanguage)
.ToDictionary(g => g.Key, g => g.Count()),
AverageLinesConverted = reports.Any() ? reports.Average(r => r.LinesConverted) : 0,
TotalIssuesDetected = reports.Sum(r => r.IssueCount),
TotalTODOs = reports.Sum(r => r.TodoCount)
};
}
}
public static class PersistenceServiceExtensions
{
public static IServiceCollection AddPersistence(this IServiceCollection services, string connectionString)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(connectionString));
services.AddScoped<IReportStorageService, DatabaseStorageService>();
return services;
}
}