This commit is contained in:
2026-03-29 23:17:20 +08:00
commit 7dc5e73af7
36 changed files with 1480 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
namespace OrpaonVision.ConfigApp.Annotation.Options;
/// <summary>
/// CVAT 连接配置。
/// </summary>
public sealed class CvatOptions
{
/// <summary>
/// CVAT 服务地址。
/// </summary>
public string ServerEndpoint { get; set; } = "http://127.0.0.1:8080";
/// <summary>
/// API Token预留
/// </summary>
public string? ApiToken { get; set; }
/// <summary>
/// 同步超时时间(秒)。
/// </summary>
public int SyncTimeoutSeconds { get; set; } = 60;
}

View File

@@ -0,0 +1,82 @@
using OrpaonVision.ConfigApp.Annotation.Options;
using OrpaonVision.Core.Annotation;
using OrpaonVision.Core.Annotation.Contracts;
using OrpaonVision.Core.Enums;
using OrpaonVision.Core.Results;
namespace OrpaonVision.ConfigApp.Annotation.Services;
/// <summary>
/// CVAT 标注同步应用服务(骨架实现)。
///
/// 说明:
/// - 当前阶段先提供参数校验与返回契约,便于前后链路先跑通;
/// - 实际 HTTP 调用、认证、重试策略将在后续迭代补齐。
/// </summary>
public sealed class CvatAnnotationSyncAppService : IAnnotationSyncAppService
{
private readonly CvatOptions _options;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="options">CVAT 配置。</param>
public CvatAnnotationSyncAppService(CvatOptions options)
{
_options = options;
}
/// <inheritdoc />
public Task<Result> SyncProjectAsync(SyncAnnotationProjectCommand command, CancellationToken cancellationToken = default)
{
if (command.Platform != AnnotationPlatformEnum.Cvat)
{
return Task.FromResult(Result.Fail("ANNOTATION_PLATFORM_NOT_SUPPORTED", "当前版本仅支持 CVAT 平台同步。"));
}
if (command.ProjectId == Guid.Empty || command.AnnotationTaskId == Guid.Empty)
{
return Task.FromResult(Result.Fail("ANNOTATION_ARGUMENT_INVALID", "项目标识或标注任务标识无效。"));
}
if (string.IsNullOrWhiteSpace(command.CvatServerEndpoint))
{
return Task.FromResult(Result.Fail("ANNOTATION_ENDPOINT_REQUIRED", "CVAT 服务地址不能为空。"));
}
if (!Uri.TryCreate(command.CvatServerEndpoint, UriKind.Absolute, out _))
{
return Task.FromResult(Result.Fail("ANNOTATION_ENDPOINT_INVALID", "CVAT 服务地址格式不合法。"));
}
if (command.CvatTaskId <= 0 || command.CvatProjectId <= 0)
{
return Task.FromResult(Result.Fail("ANNOTATION_CVAT_ID_INVALID", "CVAT 项目 ID 或任务 ID 必须大于 0。"));
}
// 当前为骨架实现:预留后续与 CVAT API 的双向同步逻辑。
return Task.FromResult(Result.Success(message: "CVAT 同步请求已受理。"));
}
/// <inheritdoc />
public Task<Result<AnnotationSyncStatusDto>> GetSyncStatusAsync(Guid projectId, CancellationToken cancellationToken = default)
{
if (projectId == Guid.Empty)
{
return Task.FromResult(Result<AnnotationSyncStatusDto>.Fail("ANNOTATION_PROJECT_ID_INVALID", "项目标识不能为空。"));
}
var status = new AnnotationSyncStatusDto
{
ProjectId = projectId,
AnnotationTaskId = Guid.Empty,
Platform = AnnotationPlatformEnum.Cvat,
SyncStatus = AnnotationSyncStatusEnum.None,
ProgressPercent = 0,
LastSyncedAtUtc = null,
LastErrorMessage = null
};
return Task.FromResult(Result<AnnotationSyncStatusDto>.Success(status, message: $"CVAT 状态查询完成({_options.ServerEndpoint})。"));
}
}

View File

@@ -0,0 +1,8 @@
<Application x:Class="OrpaonVision.ConfigApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:OrpaonVision.ConfigApp">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,37 @@
using Microsoft.Extensions.DependencyInjection;
using OrpaonVision.ConfigApp.DependencyInjection;
using System.Windows;
namespace OrpaonVision.ConfigApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private ServiceProvider? _serviceProvider;
/// <inheritdoc />
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var services = new ServiceCollection();
services.AddConfigAppServices();
services.AddSingleton<MainWindow>();
_serviceProvider = services.BuildServiceProvider();
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
}
/// <inheritdoc />
protected override void OnExit(ExitEventArgs e)
{
_serviceProvider?.Dispose();
base.OnExit(e);
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using OrpaonVision.ConfigApp.Annotation.Options;
using OrpaonVision.ConfigApp.Annotation.Services;
using OrpaonVision.Core.Annotation;
namespace OrpaonVision.ConfigApp.DependencyInjection;
/// <summary>
/// ConfigApp 服务注册扩展。
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 注册 ConfigApp 应用服务。
/// </summary>
/// <param name="services">服务集合。</param>
/// <returns>服务集合。</returns>
public static IServiceCollection AddConfigAppServices(this IServiceCollection services)
{
services.AddSingleton(new CvatOptions());
services.AddSingleton<IAnnotationSyncAppService, CvatAnnotationSyncAppService>();
return services;
}
}

View File

@@ -0,0 +1,12 @@
<Window x:Class="OrpaonVision.ConfigApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OrpaonVision.ConfigApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,25 @@
using OrpaonVision.Core.Annotation;
using System.Windows;
namespace OrpaonVision.ConfigApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly IAnnotationSyncAppService _annotationSyncAppService;
/// <summary>
/// 主窗口构造函数。
/// </summary>
/// <param name="annotationSyncAppService">标注同步应用服务。</param>
public MainWindow(IAnnotationSyncAppService annotationSyncAppService)
{
_annotationSyncAppService = annotationSyncAppService;
InitializeComponent();
Title = "OrpaonVision ConfigApp (CVAT)";
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\OrpaonVision.Core\OrpaonVision.Core.csproj" />
<ProjectReference Include="..\OrpaonVision.Model\OrpaonVision.Model.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
</Project>