feat: 完成云同步、语音控制与多平台扩展基础架构搭建

本次提交完成了项目核心基础架构升级:
1. 新增动态API中间件与权限控制系统,支持匿名/鉴权接口分离
2. 搭建云同步服务体系,包含认证、任务同步、安全策略等核心模块
3. 实现语音控制全链路,从STT/意图解析到命令执行
4. 新增任务类型、附件实体与相关仓储接口
5. 重构前端配置与代理规则,统一后端端口为5057
6. 新增多平台测试项目与CI脚本优化
7. 完善项目文档与代码注释规范

移除了旧版迁移文件与冗余代理配置,调整项目结构适配跨平台部署需求。
This commit is contained in:
ShaoHua
2026-06-21 03:26:04 +08:00
parent 65cee20006
commit 4fe0b5a963
200 changed files with 12728 additions and 3550 deletions
@@ -0,0 +1,94 @@
using System.Net;
using System.Net.Http.Json;
using Hua.Todo.HttpApi.AspNetCore.CloudSync.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Hua.Todo.Avalonia.Tests.CloudSync;
/// <summary>
/// CloudSyncProxyMiddleware 行为集成测试(Avalonia 端)。
/// 验证云同步路径代理转发、无 URL 时返回 503。
/// </summary>
public class CloudSyncProxyMiddlewareTests : IDisposable
{
private readonly WebApplication _app;
private readonly HttpClient _client;
public CloudSyncProxyMiddlewareTests()
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseUrls("http://127.0.0.1:0");
builder.Services.AddCloudSyncProxy();
var app = builder.Build();
app.UseCloudSyncProxy();
_app = app;
_app.StartAsync().GetAwaiter().GetResult();
_client = new HttpClient { BaseAddress = new Uri(_app.Urls.First()) };
}
[Fact]
public async Task Probe_NoCloudSyncUrl_Returns503()
{
var response = await _client.PostAsJsonAsync("/api/cloud-sync/probe",
new { targetUrl = "http://localhost:5173" });
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("cloud sync server URL not configured", content);
}
[Fact]
public async Task AuthLogin_NoCloudSyncUrl_Returns503()
{
var response = await _client.PostAsJsonAsync("/api/auth/login",
new { username = "admin", password = "123456" });
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
[Fact]
public async Task Tasks_NoCloudSyncUrl_Returns503()
{
var response = await _client.GetAsync("/api/tasks/");
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
[Fact]
public async Task Tasks_WithoutTrailingSlash_NoCloudSyncUrl_Returns503()
{
var response = await _client.PostAsJsonAsync("/api/tasks", new { });
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
[Fact]
public async Task SecurityPolicy_NoCloudSyncUrl_Returns503()
{
var response = await _client.GetAsync("/api/security/policy");
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
[Fact]
public async Task Sync_NoCloudSyncUrl_Returns503()
{
var response = await _client.PostAsJsonAsync("/api/sync/", new { });
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
}
public void Dispose()
{
_client.Dispose();
_app.StopAsync().GetAwaiter().GetResult();
_app.DisposeAsync().GetAwaiter().GetResult();
}
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Hua.Todo.Application\Hua.Todo.Application.csproj" />
<ProjectReference Include="..\..\src\Hua.Todo.Core\Hua.Todo.Core.csproj" />
</ItemGroup>
</Project>