feat: 完成v1.3.0架构对齐与Web端根路径托管全量开发

- 重构架构:抽离AddCloudSyncServer到扩展类,补全中间层文档对齐
- 修复根路径404与鉴权跳转:新增中间件处理浏览器请求302跳转
- 实现种子数据初始化:添加默认管理员账号自动创建逻辑
- 优化前端代理与构建:新增Vite代理配置与自动构建Target
- 补全测试与文档:新增集成测试,更新开发与部署文档
This commit is contained in:
ShaoHua
2026-08-08 07:33:44 +08:00
parent 4fe0b5a963
commit 3213d4c718
32 changed files with 1937 additions and 86 deletions
+88 -28
View File
@@ -10,45 +10,101 @@
| 项目 | 类型 | 职责 | 平台 |
|---|---|---|---|
| [Hua.Todo.Core](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Core) | 类库 | 领域实体、仓储接口(`TaskEntity`、`UserEntity`、`SecurityPolicyEntity`、`AuditLogEntity`、`ITaskRepository` 等) | netstandard / net |
| [Hua.Todo.Application](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application) | 类库(共享层) | EF Core `TodoDbContext`、`TaskService`/`TaskRepository`、动态 API(`DynamicApi/`)、云同步(`CloudSync/`)、迁移 | net |
| [Hua.Todo.Host](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Host) | ASP.NET 服务端 | 独立服务端宿主,承载本地动态 API + 云同步端点 + Admin 管理后台静态资源 | Windows/Linux 等服务器 |
| [Hua.Todo.Application](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application) | 类库(共享层) | EF Core `TodoDbContext`、`TaskService`/`TaskRepository`、CloudSync 业务服务(`Application/Services/CloudSync/`)、动态 API 属性层、迁移 | net |
| [Hua.Todo.HttpApi.AspNetCore](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.HttpApi.AspNetCore) | 类库(ASP.NET Core 专属 HTTP 层) | 承载依赖 ASP.NET Core 的实现:DynamicApiMiddleware、Mcp Server 端点、云同步服务端 DI(SessionAuthenticationHandler、CloudSyncServerService) | net(依赖 `Microsoft.AspNetCore.App` 共享框架) |
| [Hua.Todo.Host](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Host) | ASP.NET 服务端 | **Host 双角色统一部署**:(1) Web 端前端/后端同源部署(承载前端静态资源 + Dynamic API + Swagger + MCP);(2) 云同步权威服务端(供 MAUI/Avalonia 的 CloudSyncProxy 转发);不区分模式 | Windows/Linux 等服务器 |
| [Hua.Todo.Maui](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Maui) | MAUI 客户端 | Windows / macOS / iOS / Android 入口;内嵌 WebServer + WebView 承载 Vue 前端 | 多端 |
| [Hua.Todo.Avalonia](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Avalonia) | Avalonia 客户端 | Linux / Windows 桌面入口;内嵌 WebServer + `WebView.Avalonia` 承载 Vue 前端 | 桌面(含 Linux) |
| [Hua.Todo.Web](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Web) | Vite + Vue 3 前端 | 同一份前端构建产物,被多个宿主以静态资源形式承载 | 浏览器/WebView |
## 二、依赖方向(必须保持单向)
> 本图为 2026-08-08 v1.3.0 工单 05 更新版(代码对齐时间戳)。后续修改架构章节时,必须更新本标记并走 Glob/Grep 校验(详见 v1.3.0 00-工单总览 §5「架构文档 ↔ 代码一致性强制策略」)。
```
Hua.Todo.Core
↑
Hua.Todo.Application
↑
├── Hua.Todo.Host (服务端注册 AddCloudSyncServer + 动态 API)
├── Hua.Todo.Maui (客户端,仅注册 AddApplicationServices)
└── Hua.Todo.Avalonia (客户端,仅注册 AddApplicationServices)
Hua.Todo.Core (无依赖)
↑ 单向引用
Hua.Todo.Application (依赖 Core)
↑ 单向引用
Hua.Todo.HttpApi.AspNetCore (依赖 Application;依赖 Microsoft.AspNetCore.App 共享框架)
↑ 单向引用
├── Hua.Todo.Host (注册 AddApplicationServices + AddCloudSyncServer + AddMcpServerServices)
├── Hua.Todo.Maui (仅引用 Application + HttpApi.AspNetCore 的 CloudSyncProxy 子模块;不引用 AddCloudSyncServer)
└── Hua.Todo.Avalonia (同上,仅嵌入式 + CloudSyncProxy)
Hua.Todo.Web (无 .NET 依赖;通过 HTTP/同源调用上述任一宿主)
```
- **禁止反向依赖**:Core 不得引用 Application;Application 不得引用任何宿主项目。
- **客户端不暴露云同步端点**:MAUI / Avalonia 只调用 `AddApplicationServices()`,不调用 `AddCloudSyncServer()`,详见 [docs/project/研发工单-v1.2.0/08-cloud_sync_refactor_plan.md](../../../docs/project/研发工单-v1.2.0/08-cloud_sync_refactor_plan.md)。
- **禁止反向依赖**:Core 不得引用 Application;Application 不得引用 HttpApi.AspNetCore / 任何宿主;HttpApi.AspNetCore 不得引用宿主项目。
- **MAUI / Avalonia 不得注册 AddCloudSyncServer**:只注册 `AddApplicationServices()` + `AddCloudSyncProxy()`,权威云同步端点只在 Host 暴露。
- **MAUI / Avalonia 跨层引用边界**:只引用 HttpApi.AspNetCore 中与嵌入式相关的类型(CloudSyncProxyMiddleware、CloudSyncProxySettings 等),不引用 CloudSyncServerService 等 Host-only DI 扩展(实际通过命名空间自然隔离)。
## 三、两种运行模式
### 模式 A:嵌入式(MAUI / Avalonia + WebView)
- 启动内嵌 Kestrel WebServer(默认端口 5057),托管 Vue 前端的静态产物(`wwwroot/`)+ 本地 `/api/*`
- WebView 加载 `HostUrl`(生产)或 `ForEndUrl`(开发,例如 Vite dev server)
- 注入:`window.__API_BASE_URL__ = "${HostUrl}/api"`、`window.mauiInterop`
- 默认 SQLite 路径:`LocalApplicationData/Hua.Todo/Hua.Todo.db`(避免安装目录无写权限)
- 不暴露云同步端点;登录/同步走外部 Host
### 端口对照表(全局统一,禁止硬编码歧义值)
### 模式 B:独立服务端(Hua.Todo.Host)
- 独立部署的 ASP.NET 应用,端口 `:5173`(Vite proxy 目标)
- 同时注册 `AddApplicationServices()` + `AddCloudSyncServer()`,对外提供:
- `/api/*`:本地任务 API(动态 API)
- `/auth/*`、`/tasks/*`、`/sync`、`/security/policy`、`/cloud-sync/probe`:云同步端点
- `/admin/*`:管理后台前端静态资源
- 数据库:`src/Hua.Todo.Host/Hua.Todo.db`(开发/测试用)
| 端口 | 用途 | 所属模式 | 说明 |
|---|---|---|---|
| `:5057` | 嵌入式 Kestrel WebServer(HTTP) | 嵌入式 | MAUI / Avalonia 内嵌;Vite proxy 默认 target |
| `:5173` | Web 端 ASP.NET(HTTP) | Web 端 | 前端云同步默认填写的服务器地址;Host `Program.cs.UseUrls` 显式绑定 |
| `:7055` | Web 端 ASP.NET(HTTPS) | Web 端 | 开发环境可选;生产由反向代理(Nginx/Caddy)统一 TLS 终止 |
| `:5174` | Vite dev server(开发) | 两者均可 | WebView / 浏览器开发调试入口;`.env.development` 使用 |
### 模式 A:嵌入式(MAUI / Avalonia + WebView)
> 桌面/移动原生客户端,**数据先存本地 SQLite(本地缓存)**,再按需与 Web 端同步。
**请求链路(前端 → 本地 → 远端)**:
```
WebView(Hua.Todo.Web)
├─ 本地 Todo 操作(增删改查) → 同源 /api/task* 等动态 API
│ → EmbeddedWebServerService(:5057)
│ → UseDynamicApi
│ → ITaskService / TaskRepository
│ → 本地 SQLite(Hua.Todo.db)
└─ 云同步操作(探测 / 登录 / 拉取 / 推送 / 策略)
→ 同源 /api/auth、/api/tasks、/api/cloud-sync/*、/api/security/*
→ EmbeddedWebServerService(:5057)
→ UseCloudSyncProxy(CloudSyncProxyMiddleware)
→ 后端 HttpClient 转发到「用户配置的 Web 端地址」(例 http://localhost:5173)
→ Web 端 Hua.Todo.Host 的动态 API 做鉴权与同步
```
**关键规则**:
- 启动内嵌 Kestrel WebServer(端口 `:5057`),生产托管 Vue 前端静态产物(`wwwroot/`),开发加载 Vite dev server(`ForEndUrl=http://localhost:5174`)
- 注入:`window.__API_BASE_URL__ = "${HostUrl}/api"`(HostUrl=`http://localhost:5057`)、`window.mauiInterop`
- DI 只注册 `AddApplicationServices()` + `AddCloudSyncProxy()`,**不注册 `AddCloudSyncServer()`**:嵌入式端不做用户管理、不暴露云同步的权威端点
- 默认 SQLite 路径:`LocalApplicationData/Hua.Todo/Hua.Todo.db`(避免安装目录无写权限)
- 本地用户 ID 固定为 `TodoUserIds.LocalUserId = "local"`,与云端用户隔离
### 模式 B:Web 端(Hua.Todo.Host,部署入口)= **Host 双角色统一部署**
> ⚠️ Host 部署时不区分模式:
> - **对外角色**:用户浏览器直接访问 Host(`http://host-address:5173/`)→ 即"Web 端前端 + 后端同源部署"
> - **对内角色**:同时承担"云同步权威服务端"职责,接受 MAUI/Avalonia 嵌入式客户端通过 CloudSyncProxy 转发 `/api/*` 过来的请求
>
> 因此 `Hua.Todo.Host/Program.cs` 必须同时注册:`AddApplicationServices` + `AddCloudSyncServer` + `AddMcpServerServices`。
>
> 独立部署的 ASP.NET 服务端,既是**后端接口服务**,也是**前端静态站点托管**,部署产物是单个 `Hua.Todo.Host` 进程。
**端点划分(四个逻辑入口,同一进程)**:
| URL 前缀 | 内容 | 来源 |
|---|---|---|
| `/`(根路径及所有 SPA 路由) | **Hua.Todo.Web 用户前端**(登录后 Todo 列表) | `src/Hua.Todo.Web` 的 `dist/` 构建产物复制到 `Hua.Todo.Host/wwwroot/`;若缺失则由 `MinimalBootstrapPageMiddleware` 返回最小引导页(中文错误提示 + 构建命令,不 404) |
| `/api/*` | **动态 API**(本地+云同步共用的一套接口) | `Hua.Todo.Application/DynamicApi` 扫描所有 `IDynamicApiService` 自动暴露(task / auth / tasks / security / cloud-sync / admin / voice)。权限检查时,浏览器 HTML 直访 Accept:text/html → 302 跳 `/`(前端接管登录弹窗);其他 API 请求(JSON)→ 401/403 JSON 错误 |
| `/mcp` | **MCP Server 端点**(Streamable HTTP) | `McpEndpointExtensions.cs` 映射,供 MCP 客户端调用 |
| `/swagger` + `/admin/*` | **Swagger UI** + **管理后台前端**(用户管理、审计、策略) | Swagger:开发环境启用;Admin:`Hua.Todo.Host/wwwroot/admin/`(当前占位,后续 Vue 3 构建产物填入) |
**关键规则**:
- 同时注册 `AddApplicationServices()` + `AddCloudSyncServer()`(后者额外注册 `IPasswordHasher<UserEntity>`、云同步动态 API、安全策略校验器等)
- 中间件顺序必须严格遵守(见 `00-工单总览.md` §5):Swagger → MinimalBootstrapPageMiddleware → UseDefaultFiles/UseStaticFiles → CORS → Authentication → Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile → Run
- 端口:HTTP `:5173` + HTTPS `:7055`,`Program.cs` 显式 `UseUrls` 绑定
- 开发环境**不启用 `UseHttpsRedirection`**(由反向代理统一做 TLS 终止),避免前端 axios 把 307 当错误
- 数据库:开发/测试使用 `src/Hua.Todo.Host/Hua.Todo.db`(SQLite);生产可切换其他后端
## 四、跨平台原则
@@ -60,11 +116,15 @@ Hua.Todo.Web (无 .NET 依赖;通过 HTTP/同源调用上述任一宿主)
| 扩展点 | 位置 |
|---|---|
| DI 注册总入口 | [Hua.Todo.Application/ServiceCollectionExtensions.cs](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application/ServiceCollectionExtensions.cs) |
| 云同步 DI | `Hua.Todo.Application/CloudSync/CloudSyncServiceCollectionExtensions.cs` |
| 云同步端点 | `Hua.Todo.Application/CloudSync/CloudSyncEndpointExtensions.cs` |
| 动态 API 中间件 | `Hua.Todo.Application/DynamicApi/DynamicApiMiddleware.cs` |
| DI 注册总入口 | `Hua.Todo.Application/Common/ServiceCollectionExtensions.cs`(`AddApplicationServices`) |
| 云同步服务端 DI(Host 专用) | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs`(`AddCloudSyncServer`) |
| 云同步代理 DI / 中间件(嵌入式) | `Hua.Todo.Application/Services/CloudSync/Services/CloudSyncProxyService.cs`(`AddCloudSyncProxy` / `UseCloudSyncProxy`) |
| 云同步代理设置端点(嵌入式) | `Hua.Todo.Application/Services/CloudSync/Services/ICloudSyncProxySettingsService`(继承 `IDynamicApiService`,由 DynamicApi 自动暴露;仅在 AddCloudSyncProxy 时注册 DI) |
| 云同步接口(auth/tasks/security/admin/cloud-sync probe) | `Hua.Todo.Application/Services/CloudSync/Services/ICloud*Service` 实现 `IDynamicApiService`,由 DynamicApi 自动暴露 |
| 动态 API 中间件 + 路由/权限 | `Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs`(含 `DynamicApiRouteAttribute` / `RequirePermissionAttribute`) |
| MCP Server(工具描述符自动生成 + 端点) | `Hua.Todo.HttpApi.AspNetCore/Mcp/DynamicMcpToolExtensions.cs` + `McpServiceCollectionExtensions.cs`(DI 注册 `AddMcpServerServices`) + `McpEndpointExtensions.cs`(端点 `MapMcpServer`) |
| 嵌入式 WebServer | `Hua.Todo.{Maui,Avalonia}/Services/EmbeddedWebServerService.cs` |
| 种子数据初始化器(Web 端 DB Migrate 后) | `Hua.Todo.Application/Services/DataSeeder/IDataSeeder`(仅在 `AddCloudSyncServer` 时注册) |
| 全局快捷键平台实现 | `Hua.Todo.{Maui,Avalonia}/Services/Platforms/*GlobalHotKeyService.cs` |
## 六、版本统一策略
+14 -2
View File
@@ -9,9 +9,9 @@
---
## 一、当前活跃版本
## 一、当前活跃版本(2026-08-08 快照)
- **进行中版本**:v1.2.0(收尾中)、v1.3.0(规划中)
- **进行中版本**:v1.2.0(收尾中)、v1.3.0(开发中)
- **v1.2.0 研发工单总览**:[docs/project/研发工单-v1.2.0/00-工单总览.md](../../../docs/project/研发工单-v1.2.0/00-工单总览.md)
- **v1.3.0 研发工单总览**:[docs/project/研发工单-v1.3.0/00-工单总览.md](../../../docs/project/研发工单-v1.3.0/00-工单总览.md)
- **PRD**:[docs/project/产品需求文档-1.2.0.md](../../../docs/project/产品需求文档-1.2.0.md)
@@ -31,6 +31,7 @@
| 07 - 文档同步与验收 | 进行中 | 进行中 | README/docs 已基本对齐;术语对照刚完成 |
| 08 - cloud_sync 重构 | 已设计 | 待实现 | "同源 Host"方案,Vite proxy 补 `/auth` `/tasks` `/sync` `/security` `/cloud-sync` |
| 09 - CloudSync 同步策略改进 | 已实现 | 待验证 | TaskEntity 继承 ABP 基类;软删除修复(SaveChangesAsync);前端类型和 cloudSync.ts 已更新;新增 guid.ts |
| 10 - 数据库初始化默认管理员 | 待实现 | 待验证 | Host 服务端 DB Migrate 后自动创建默认管理员;仅 Host 模式;配置驱动 + 幂等 + 强制首登改密;作为 06.1 Bootstrap 自动前置项。详见 [10-数据库初始化默认管理员.md](../../../docs/project/研发工单-v1.2.0/10-数据库初始化默认管理员.md) |
## 三、v1.3.0 工单状态快照
@@ -44,6 +45,8 @@
| 03-03 - AI 任务拆分服务 | 待开始 | 待验证 | 会议专用 LLM prompt + 批量创建子任务 |
| 03-04 | 任务建议与确认 UI | 已完成 | 待验证 | MeetingBreakdownDialog.vue + meeting.ts 拆分/确认 API;待集成到 TaskItem |
| 04 | 富文本描述、附件与外部链接 | 已完成 | 待验证 | Description 字段 + AttachmentEntity 模型 + 附件 CRUD API + 外部链接 + 平台文件打开器(Maui/Avalonia)+ 前端类型和 API 模块完善 + EF 迁移 + 19 个单元测试通过 |
| 05 | 架构文档与代码实际路径对齐 | 已完成 | 已验证 | 01-项目架构.md §1 补 HttpApi.AspNetCore 条目、§2 ASCII 依赖图加中间层、§5 修复 3 个扩展点路径;代码侧:抽离 AddCloudSyncServer 到 CloudSyncServiceCollectionExtensions.cs;设计修正:MapCloudSyncProxySettings 手动端点已废弃,由 ICloudSyncProxySettingsService(IDynamicApiService)自动暴露,清理 v1.3.0 00-工单总览 Host 中间件顺序末尾的 MapCloudSyncProxySettings;模式 B 中间件顺序硬约束文档化为 Swagger→StaticFiles→CORS→Authentication→Authorization→UseDynamicApi→MapMcpServer→MapFallbackToFile→Run |
| 06 | Web 端根路径托管前端 | 已完成 | 已验证(4 场景集成测试) | Host Program.cs 加 UseDefaultFiles/UseStaticFiles/MapFallbackToFile("index.html"),顺序严格对齐硬约束;Host.csproj 加 BuildHuaTodoWebWebRoot Target(增量 npm build + 双 wwwroot 拷贝 + Node 缺失 Warning + SkipBuildHuaTodoWebWebRoot 开关);docs/manual/06-开发环境与构建.md 新增「模式 B 双开」「1.4 BuildHuaTodoWebWebRoot 自动构建说明」「3.0 部署构建流程」三节;v1.3.0 00-工单总览补充 6 条 Given-When-Then 验收表;WebRootStaticFileTests.cs 4 个集成测试覆盖根路径、SPA fallback、/api 不覆盖、Swagger 不覆盖 |
## 四、关键临时决策
@@ -54,7 +57,10 @@
## 五、已知未完结事项 / 待办
- [x] v1.3.0 工单 05 - 架构文档与代码实际路径对齐:已完成(文档 + 代码抽离 + 中间件顺序约束文档化)
- [x] v1.3.0 工单 06 - Web 端根路径托管前端:已完成(Program.cs 三中间件 + Host.csproj BuildHuaTodoWebWebRoot Target + docs 双开指南 + 4 场景集成测试)
- [ ] 06 客户端"内存模式"在 `allowPersist=false` 时的端到端落盘清理(含 token、同步队列)尚未充分验证
- [ ] 工单 10 - 数据库初始化默认管理员:待实现 `IDataSeeder`/`DataSeeder` + Host Program.cs 集成 + `AdminBootstrapOptions` 配置 + 测试先行(详见 [10-数据库初始化默认管理员.md](../../../docs/project/研发工单-v1.2.0/10-数据库初始化默认管理员.md))
- [ ] 06.1 设计中的 Admin 管理后台前端(位于 `Hua.Todo.Host/wwwroot/admin/`)当前仅有 `index.html` 占位,需 Vue 3 + Vite 实现
- [ ] 08 同源 Host 改造:`cloudClient.ts` 的 baseURL 解耦、Vite proxy 端点补全
- [ ] Linux Flatpak/AppImage 自包含产物在干净环境的实测验证(v1.2.0 验收 Linux 部分仍为"待验证")
@@ -89,6 +95,12 @@
7. 清理测试项目中的 SkipCloudSync 排除
- **验证**:Application(net10.0 + net10.0-android) + Host + MAUI(Windows + Android) 全部 0 错误;152 个测试全部通过
- [x] Host 双角色统一部署路由与鉴权流修补(2026-08-08,对应用户反馈三条问题):
- **问题 1:根路径 404** → 修复:`Hua.Todo.Host/Program.cs` 新增 `MinimalBootstrapPageMiddleware`,前端 index.html 缺失时,访问 `/` 与 SPA 路由返回中文引导页(含三种修复方式 + 可继续使用的 API/Swagger/MCP 入口),不再 404
- **问题 2:没权限应跳登录页** → 修复:`DynamicApiMiddleware` 新增 `WriteUnauthorizedResponse/WriteForbiddenResponse/IsBrowserHtmlRequest`,GET/HEAD + Accept:text/html 且非 XMLHttpRequest → 302 Location="/" 跳回 Host 前端 SPA,SPA 判断未登录 → 弹 `CloudSyncSettingsDialog`;Accept:json 或非 GET/HEAD → 保持原 401/403 JSON(不破坏 axios/fetch 拦截器)
- **问题 3:Host 部署不区分模式** → 修复:`01-项目架构.md` 模式 B 章节重写为"Host 双角色统一部署 = (1) Web 端前端/后端同源 + (2) 云同步权威服务端",端点扩展为 4 项(/、/api、/mcp、/swagger+/admin),新增中间件顺序硬约束说明
- **验证**:`HostBootstrapAndAuthRedirectTests.cs` 6 个 ATDD 测试全部通过 + 原 `WebRootStaticFileTests.cs` 4 场景全部通过;`GetDiagnostics` 0 编译错误
## 六、最近一次重大重构(如有)
- **术语统一与目录中文化**(2026-06):