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
@@ -40,6 +40,16 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
当前版本暂无串行工单依赖。
### 2.4 补充工单(架构对齐,2026-08-08 新增)
> 按 [04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md) §2.3「新增工单约束」:已有条目不可改写,本章节为本次**新增**。触发原因:本次 307 修复后重写 [01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md) §3/§5,对照代码发现 2 处架构缺口(文档遗漏 HttpApi.AspNetCore 层、Web 端根路径无前端托管)。
| 工单编号 | 标题 | 负责人 | 状态 | 并行性 |
|---|---|---|---|---|
| 05 | 架构文档与代码实际路径对齐 | - | 待开始 | 与 03/06 并行 |
| 06 | Web 端根路径托管前端 | - | 待开始 | 与 03/05 并行(建议 05 缺口 D 先落,再推 06 Program.cs 集成) |
| 07 - 数据库初始化默认管理员 | 待实现 | 待验证 | Host 服务端 DB Migrate 后自动创建默认管理员账号;仅 Host 模式;配置驱动 + 幂等 + 强制首登改密;作为 06.1 Bootstrap 的自动前置项。详见 [10-数据库初始化默认管理员.md](10-数据库初始化默认管理员.md) |
---
## 三、各子工单摘要
@@ -123,6 +133,33 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
- 本地附件可通过系统关联程序打开
- 附件数量(20个)和大小(50MB)限制生效
### 3.5 工单 05 - 架构文档与代码实际路径对齐
**目标**:修正 01-项目架构.md §1/§2/§5 共 5 处与实际代码结构不一致的描述,使规则文档成为代码的单一事实来源。
**核心需求**:
- **文档侧**:§1 项目清单补 `Hua.Todo.HttpApi.AspNetCore` 条目;§2 ASCII 依赖图加该中间层;§5 路径修正 Mcp 描述
- **代码侧**:`CloudSyncServerService.cs` 抽离 `AddCloudSyncServer()` 到新文件 `CloudSyncServiceCollectionExtensions.cs`;新建 `CloudSyncEndpointExtensions.cs` 还原 `MapCloudSyncProxySettings()`
- **回归**:Host 编译 + 161 个既有测试全部通过
**不包含**:业务逻辑变更、公共 API 签名修改、v1.2.0 工单 10(DataSeeder)
**验收标准**:详细 5 条 Given-When-Then 见 [05-架构文档与代码实际路径对齐.md](./05-架构文档与代码实际路径对齐.md) §五。
### 3.6 工单 06 - Web 端根路径托管前端
**目标**:补齐模式 B(Hua.Todo.Host Web 端)的前端静态页托管链路,使 `dotnet publish Hua.Todo.Host` 产出一个「页面 + API + Admin 占位」完整可部署的服务。
**核心需求**:
- Host `Program.cs`:`UseDefaultFiles` + `UseStaticFiles` + `MapFallbackToFile("index.html")`(支持 SPA 路由)
- Host `csproj`:新增 `BuildHuaTodoWebWebRoot` Target(`npm install`/`npm run build`/`copy dist → wwwroot`,Inputs/Outputs 支持增量构建)
- 文档:`docs/manual/08-构建与开发指南.md` 追加模式 B 开发双开步骤 + 生产发布步骤
- 测试:`WebRootStaticFileTests.cs` 验证根路径 `/` 与 SPA fallback 不覆盖 `/api/*`
**不包含**:Admin 管理后台前端实现(v1.2.0 工单 06.1)、嵌入式模式前端托管(`EmbeddedWebServerService` 各自管理)
**验收标准**:详细 6 条 Given-When-Then 见 [06-Web端根路径托管前端.md](./06-Web端根路径托管前端.md) §四。
---
## 四、待验证表
@@ -149,6 +186,14 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
| 04 | 附件数量/大小限制 | 待验证 | 20 个 / 50MB |
| 04 | 待办项删除时附件级联清理 | 待验证 | - |
| 04 | 跨平台编辑安全(移动端不覆盖描述/附件) | 待验证 | 桌面设值 → 移动端改标题 → 桌面验证不丢失 |
| 05 | 规则文档 §1/§2 补 HttpApi.AspNetCore 条目与依赖图 | 待验证 | Glob 到项目 + 规则描述一致 |
| 05 | §5 扩展点 AddCloudSyncServer / MapCloudSyncProxySettings / Mcp 3 个路径修复 | 待验证 | 规则路径 = Grep 实际路径;Host 编译 0 错误 |
| 05 | 既有 AddCloudSyncServer 回归测试 | 待验证 | `CloudSyncEndpointRegistrationTests` 5 个用例全部通过 |
| 06 | Host `/` 返回 Hua.Todo.Web index.html(200 + div#app) | 待验证 | `WebRootStaticFileTests.Root_Get_ReturnsIndexHtml` 通过 |
| 06 | SPA 路由 `/settings` fallback 到 index.html | 待验证 | 不 404;不覆盖 `/api/*` 动态 API |
| 06 | `dotnet publish Hua.Todo.Host` 产出包含前端静态资源 | 待验证 | publish/wwwroot/index.html 存在 + 资源齐全 |
| 06 | 增量构建前端未变时跳过 npm build | 待验证 | MSBuild Inputs/Outputs 检测 Skipping target 提示 |
| 06 | 模式 B 开发双开文档可执行性 | 待验证 | 从零 clone → 文档 step by step 跑通页面 + API |
---
@@ -172,6 +217,9 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
| 附件存储策略 | 附件存储在应用数据目录 `Attachments/` 子目录;外部链接不复制文件仅存 URL;单文件 50MB / 每待办项 20 个上限 |
| 外部程序启动 | 通过 `IPlatformAttachmentOpener` 接口实现平台差异:Windows 用 `Process.Start`,Linux 用 `xdg-open` |
| 工单 04 与 03 共享文件 | `TaskEntity.cs`、`TodoDbContext.cs`、`task.ts`、`TaskEditDialog.vue` 为共享文件,工单 03 先写入,04 后续追加 |
| **架构文档 ↔ 代码一致性强制策略** | 后续凡改 01-项目架构.md 任何一节,改后必须立即对 §1/§2/§5 所有提到的文件路径做一次 Glob/Grep 校验;不一致 → 先开对齐工单,不直接碰业务代码(本次 05 工单即为策略首次落地案例) |
| **Host 中间件顺序固定模板** | Swagger → UseDefaultFiles/UseStaticFiles → CORS → Authentication → Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile → Run(顺序错误直接判作编译/验收失败) |
| **MSBuild 前端构建 Target 命名与增量** | Host 前端构建 Target 固定名 `BuildHuaTodoWebWebRoot`;必须显式写 `Inputs="$(HuaTodoWebSrc);$(HuaTodoWebPkgJson)" Outputs="$(WebRoot)/index.html"` 支持增量;Node/npm 不存在时只 Warning 不阻塞 API 层 build |
---
@@ -185,6 +233,9 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
| LLM API(AI 拆分) | 待确认 | Host 端调用 |
| 浏览器 MediaRecorder API | 已就绪 | 前端录音 |
| 工单 02 LlmClientService | 待实现 | 会议拆分复用 |
| 工单 05 前置(缺口 D) | 待完成 | 建议先还原 `CloudSyncEndpointExtensions.cs`(MapCloudSyncProxySettings),再推 06 Program.cs 集成 |
| Node.js + npm(构建机器) | 外部依赖 | 构建 Hua.Todo.Web 必备;缺失时 Host csproj Target 仅 Warning |
| 01-项目架构.md §3 模式 B 端点划分表 | 已固化 | 本批次工单 05/06 的对齐基准 |
---
@@ -199,9 +250,25 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级:
| 会议录音文件过大 | 上传超时/存储压力 | 前端限制最长 2 小时;压缩音频格式 |
| 浏览器 MediaRecorder 兼容性 | 部分平台录音不可用 | 降级提示使用文字输入 |
| STT 转写准确率不足 | 会议纪要质量差 | 转写后支持用户编辑修正 |
| **工单 05 风险:架构文档与代码路径反复漂移** | 后续智能体又按旧路径改错文件 | 05 验收完成后,01-项目架构.md §2 ASCII 图顶部加"最后一次代码对齐时间戳";后续改架构章节必须更新时间戳 + 走 Glob 校验 |
| **工单 05 风险:抽离 AddCloudSyncServer 时丢 using/命名空间** | Host 编译失败 | 改动范围严格控制在 CloudSync 两个文件;Program.cs 仅补 using;单步 build 验证后再提交 |
| **工单 06 风险:MapFallbackToFile 覆盖 /api 或 /swagger** | API/Swagger 全 404 | 中间件顺序严格按 §5 固定模板;集成测试同时验证 `/`、`/settings`(fallback)、`/api/cloud-sync/probe`(DynamicApi)、`/swagger`(Swagger UI)四个端点 |
| **工单 06 风险:CI 环境 npm 网络差/无 Node** | publish 超时或失败 | MSBuild Target 加 Node 缺失检测 → Warning + 跳过;生产部署可在外层 CI 先 build 前端再 publish Host |
| **工单 06 风险:增量构建 Inputs/Outputs 粗粒度** | 前端代码改了不重 build 或每次都重 build | Inputs 精确到 `**/*.{ts,vue,css,html}` + `package.json`;Outputs 精确到 `wwwroot/index.html`,首次过 CI 后人工改一行 .vue 验证是否触发 rebuild |
---
## 补充工单(追加于 2026-08-08)
### 工单 10 - 数据库初始化默认管理员
- **目标**:Host 服务端 DB Migrate 完成后自动创建默认管理员账号,免去手动调用 Bootstrap API
- **范围**:仅 `Hua.Todo.Host`;嵌入式模式(MAUI/Avalonia)不涉及
- **关联**:作为 06.1 服务端安全设计 Bootstrap 的自动前置项;与现有 `BootstrapAdmin` API 共存
- **详见**:[10-数据库初始化默认管理员.md](10-数据库初始化默认管理员.md)
---
**创建日期**:2026-06-15
**修订日期**:2026-06-16
**修订日期**:2026-08-08
**版本**:v1.3.0
**修订说明(2026-08-08)**:新增架构对齐补充工单 05/06(01-项目架构.md §3 模式 B 端点划分表与代码实际不一致触发);所有新增条目均以"追加末尾"形式写入,未改动 2026-06-16 版本已有表格行和章节结构(符合 04-研发工单全流程规范 §2.1 已有文件不可触碰)。
@@ -0,0 +1,105 @@
# 研发工单 v1.3.0 - 05:架构文档与代码实际路径对齐
> 术语澄清:本文件中"研发工单"指智能体/开发者执行的**编码工作项**,与业务侧的 Todo 待办项无关。详见 [.trae/rules/全局/04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md)。
---
## 一、背景与动机
在修正「云同步探测 307 → HTTPS 重定向规则」时,重写了 `.trae/rules/项目/01-项目架构.md` §3「两种运行模式」与 §5「关键扩展点」以固化正确链路。但对照代码后发现:**规则文档中的两处描述已与当前代码结构不一致**,会导致后续智能体/开发者按规则路径搜索文件时直接落空,进而引发与本次 307 同类的"链路理解错位 → 改错文件"问题。
本工单是纯**工程化对齐工单**(无用户可见的业务功能变更),目标是让 01-项目架构.md 成为"代码的单一事实来源":要么文档改到与代码一致,要么代码重构到与文档一致,**不允许中间的灰色状态**。
## 二、缺口清单(代码 vs 01-项目架构.md 规则)
| # | 缺口 | 规则写的内容 | 代码实际情况 | 本工单处理方式 |
|---|---|---|---|---|
| A | **§1 项目清单漏列 HttpApi.AspNetCore 层** | Core / Application / Host / Maui / Avalonia / Web 共 6 行 | 存在 `src/Hua.Todo.HttpApi.AspNetCore/`(CloudSync/DynamicApi/Mcp 三层 ASP.NET Core 专属实现),Host 直接引用它 | **规则加表项**:补齐项目条目,注明"承载 ASP.NET Core 专属的 HTTP 层代码(Application 保持 netstandard 兼容)" |
| B | **§2 依赖方向图缺失 HttpApi.AspNetCore 中间层** | `Application ← { Host, Maui, Avalonia }` 直接三层 | 实际是 `Application ← HttpApi.AspNetCore ← Host`,MAUI/Avalonia 只按需引用 HttpApi.AspNetCore 的部分子模块 | **规则改 ASCII 图**:新增中间层 + 注释跨层引用的边界 |
| C | **§5 扩展点路径:AddCloudSyncServer 所在文件名错误** | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs` | 定义在 `CloudSync/Services/CloudSyncServerService.cs` 内(非 *Extensions 命名,与 Mcp / DynamicApi 的命名习惯不一致) | **代码重命名/抽离**:新建 `CloudSyncServiceCollectionExtensions.cs`,把 `AddCloudSyncServer()` 移入;原 `CloudSyncServerService.cs` 仅保留服务实现类 |
| D | **§5 扩展点路径:MapCloudSyncProxySettings 所在文件缺失** | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncEndpointExtensions.cs`(仅保留 `MapCloudSyncProxySettings`) | Grep 显示该方法**仅被 Host Program.cs 调用,定义文件不存在**(即时状态记忆 2026-06-17 条目说"仅保留 MapCloudSyncProxySettings",但文件未落地) | **代码补齐**:新建 `CloudSyncEndpointExtensions.cs`,写入 `MapCloudSyncProxySettings` 扩展方法(将目前分散的或在旧文件被删除的内容还原),并更新引用命名空间 |
| E | **§5 扩展点:McpServerMiddleware 实际不存在** | `DynamicMcpToolExtensions.cs + McpServerMiddleware` | 只有 `McpEndpointExtensions.cs`(`MapMcpServer`)+ `McpServiceCollectionExtensions.cs`(`AddMcpServerServices`),无 Middleware 类型 | **规则改路径**:改为 `McpEndpointExtensions.cs` + `McpServiceCollectionExtensions.cs`;不强求新增 Middleware 类型(EndpointRouting 扩展符合 ASP.NET Core 主流模式),只改描述 |
## 三、目标 / 范围
### 3.1 做什么
- 对齐 01-项目架构.md §1/§2/§5 三章共 5 处缺口(A–E,见上表)
- 代码改动:C(文件抽离+重命名)、D(新建+还原)
- 文档改动:A、B、E(仅改规则文档,不改代码)
### 3.2 不做什么
- 不改业务逻辑(不触碰 CloudSyncServerService 内的方法体、不改动 DynamicApiMiddleware 行为)
- 不改公共 API 契约(AddCloudSyncServer / MapCloudSyncProxySettings 的签名保持不变,仅迁移到新文件)
- 不做 v1.2.0 工单 10 范围内的工作(DataSeeder)
## 四、前置条件 / 依赖
- 无;可与 v1.3.0 工单 06、03 并行执行
- 共享文件:`.trae/rules/项目/01-项目架构.md`(只读,只做局部追加替换;不重排原有章节结构)
- 共享文件:`Hua.Todo.HttpApi.AspNetCore.csproj`(新增 2 个源文件,C# 编译级冲突风险极低)
## 五、验收标准(Given-When-Then)
| # | 场景 | GIVEN | WHEN | THEN |
|---|---|---|---|---|
| 1 | 缺口 A/B(规则补层) | - | 打开规则文档 §1/§2 | §1 出现 `Hua.Todo.HttpApi.AspNetCore` 表项,描述包含"ASP.NET Core 专属 HTTP 层";§2 ASCII 图新增中间层,箭头方向正确(单向 Application → HttpApi.AspNetCore → Host) |
| 2 | 缺口 C(DI 扩展抽离新文件) | `CloudSyncServerService.cs` 当前包含 `AddCloudSyncServer(this IServiceCollection)` | 抽离为 `CloudSyncServiceCollectionExtensions.cs` | 新文件存在且命名空间为 `Hua.Todo.HttpApi.AspNetCore.CloudSync`;`Program.cs` 编译通过无需改 using;旧文件仅保留服务实现(非 static / 非扩展方法)|
| 3 | 缺口 D(MapCloudSyncProxySettings 新建文件) | `MapCloudSyncProxySettings(this IEndpointRouteBuilder)` 在 Program.cs 被调用但无定义文件 | 创建 `CloudSyncEndpointExtensions.cs` 并补全方法体 | 新文件存在;`Program.cs` 加对应 using 后编译通过;该端点在 Host 启动后响应 `/api/cloud-sync/proxy-settings`(与既有集成测试一致) |
| 4 | 缺口 E(规则改 Middleware 描述) | §5 写 "McpServerMiddleware" | 将 §5 对应行改为 "McpEndpointExtensions.cs(MapMcpServer 端点) + McpServiceCollectionExtensions.cs(DI 注册)" | 规则路径描述与实际文件一一对应,Grep 两个路径都能找到 |
| 5 | 回归(全部编译通过) | - | `dotnet build src/Hua.Todo.Host/Hua.Todo.Host.csproj` + `dotnet test test/Hua.Todo.Host.Tests/` 全量 | 0 编译错误;既有测试(含 AddCloudSyncServer DI 注册测试)全部通过,不允许新增失败用例 |
## 六、并行冲突规避
| Touch List | 共享文件策略 | 编译绿线策略 |
|---|---|---|
| `.trae/rules/项目/01-项目架构.md` | 追加/局部替换,**不改 §3 模式 A/B 的正文段落和 §6 版本统一/§7 测试架构**;本工单只动 §1 表、§2 ASCII 图 + 说明、§5 表 | 纯 Markdown,无编译;提交前 diff 确认未改动他人段落 |
| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServerService.cs` | 只移除 `AddCloudSyncServer` 静态方法块,**不改动类的其他成员**;新文件独立写入,无双向合并 | `dotnet build Hua.Todo.HttpApi.AspNetCore.csproj` 必须 0 错误 |
| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs` | 新建;其他并行工单不改同名文件 | 同上 |
| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncEndpointExtensions.cs` | 新建;其他并行工单不改同名文件 | 同上 |
| `Hua.Todo.Host/Program.cs` | 可能需要追加 `using Hua.Todo.HttpApi.AspNetCore.CloudSync;`(如果原 using 不完整);不碰其他语句 | `dotnet build Hua.Todo.Host.csproj` 0 错误 |
## 七、风险与回滚
| 风险 | 影响 | 回滚策略 |
|---|---|---|
| C/D 移动方法体时遗漏了 using 或扩展方法的 `this` 参数 | Host 编译失败 | git 单文件 revert;不要手改多个地方 |
| §2 依赖方向图改动后,后续智能体仍按旧图改代码 | 跨工单改错 | 规则文档 §2 顶部加一句"本图为 2026-08-08 v1.3.0 工单 05 更新版"作为时间戳标记 |
| D 还原的 MapCloudSyncProxySettings 方法体与"即时状态记忆 2026-06-17"实际实现不一致 | 端点行为变化 | 先从 git 历史(CloudSyncEndpointExtensions.cs 被删前的版本)原样恢复,不做任何逻辑修改 |
## 八、与其他工单的语音/UI 入口衔接
> 按 [05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md) 第 3.2 条:纯基础设施调整无需检查双入口。
| 入口 | 已规划 | 方案 |
|---|---|---|
| UI | ❌(无需) | 纯工程化,用户无感知 |
| 语音 | ❌(无需) | 不涉及业务操作 |
---
**创建日期**:2026-08-08
**关联规则**:[01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md)
**关联工单**:v1.2.0 工单 08 / 工单 10;v1.3.0 工单 01 / 06
---
## 九、执行期设计修正(2026-08-08,不违反 §2.1:本段落为本次执行前新增,未改动 §二 缺口表原有行)
### 9.1 缺口 D 重判:`MapCloudSyncProxySettings` 实际上不应作为手动 Map 存在
**执行前代码核对结果**:`ICloudSyncProxySettingsService` 已继承 `IDynamicApiService`(见 `src/Hua.Todo.Application/Services/CloudSync/Services/ICloudSyncProxySettingsService.cs`),因此代理设置端点(GET/POST `/api/cloudSyncProxySettings`)**由 DynamicApiMiddleware 自动暴露**。即时状态记忆 2026-06-17 条目记录的「CloudSyncEndpointExtensions 仅保留 `MapCloudSyncProxySettings`」属于"CloudSync 端点 DynamicApi 化"过程中的中间状态快照:移除 16 个手动 Map 时,旧 MapCloudSyncProxySettings 本应连同删除却留下了文档引用,造成规则文档 §5 出现了「云同步代理设置端点 → CloudSyncEndpointExtensions.cs」这个错误路径。
**修正后缺口 D**:从「代码侧新建 CloudSyncEndpointExtensions.cs 还原方法」**改为「规则侧清理过时引用」**:
- 01-项目架构.md §5:把「云同步代理设置端点 → CloudSyncEndpointExtensions.cs」改为 `ICloudSyncProxySettingsService`(DynamicApi 自动暴露,仅在嵌入式 AddCloudSyncProxy 时注册)
- v1.3.0 00-工单总览 §5 关键决策「Host 中间件顺序固定模板」:末尾移除 `→ MapCloudSyncProxySettings`(Host 上代理设置端点本来就不注册)
- 代码侧:不新建 CloudSyncEndpointExtensions.cs,也不在 Program.cs 追加 `MapCloudSyncProxySettings()` 调用
**验收影响**:
- §五 验收 #3 对应取消;新增验收 #3b:01-项目架构.md §5 中不再出现 `CloudSyncEndpointExtensions.cs` 和 `MapCloudSyncProxySettings` 两个字符串;`ICloudSyncProxySettingsService` 行出现且描述正确
- §五 验收 #5 回归编译仍然保留(仍然是 0 错误 + 既有测试全过)
### 9.2 其余缺口 A/B/C/E 不变
A(§1 补条目)、B(§2 ASCII 补中间层)、C(抽离 AddCloudSyncServer 到 *Extensions 文件)、E(Mcp 描述改 EndpointExtensions + ServiceCollectionExtensions)与代码实际核对一致,按原计划执行。
@@ -0,0 +1,89 @@
# 研发工单 v1.3.0 - 06:Web 端根路径托管前端
> 术语澄清:本文件中"研发工单"指智能体/开发者执行的**编码工作项**,与业务侧的 Todo 待办项无关。详见 [.trae/rules/全局/04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md)。
---
## 一、背景与动机
根据 [01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md) §3「模式 B:Web 端」端点划分表的定义:
| URL 前缀 | 内容 | 来源 |
|---|---|---|
| `/`(根路径) | **Hua.Todo.Web 用户前端**(登录后 Todo 列表) | `src/Hua.Todo.Web` 的 `dist/` 构建产物,复制到 `Hua.Todo.Host/wwwroot/` |
| `/api/*` | 动态 API | DynamicApi |
| `/admin/*` | 管理后台前端 | `Hua.Todo.Host/wwwroot/admin/`(占位) |
**当前代码不符合**:
- Host `Program.cs` 没有 `UseStaticFiles()` / `UseDefaultFiles()`;访问 `http://localhost:5173/` 返回 **404**
- `Hua.Todo.Host.csproj` 没有任何构建 Target 把 `Hua.Todo.Web` 的 `dist/*` 产物拷到 `Hua.Todo.Host/wwwroot/`
- 开发联调文档缺失:开发时 Vite(:5174)+ Host(:5173)双开怎么跑、端口怎么代理、生产部署怎么构建单文件,都没有书面说明
结果:**用户按规则部署"Web 端(Hua.Todo.Host)",会拿到一个只有 API 没有前端页面的半成品服务**。本工单补全模式 B 的生产/开发闭环。
## 二、目标 / 范围
### 2.1 做什么
1. **Host 侧 ASP.NET Core 中间件补齐**:在 `Program.cs` DynamicApi 之前插入 `UseDefaultFiles()` + `UseStaticFiles()`,并保证 SPA 路由(前端内部路由,如 `/settings`)fallback 到 `index.html`(用 `MapFallbackToFile("index.html")`)
2. **Host csproj 构建 Target**:新增 `npm install`(若 `node_modules` 不存在)+ `npm run build`(Hua.Todo.Web)+ 复制 `dist/**` → `$(OutputPath)/wwwroot/` 的 MSBuild Target,只在构建 Host 时触发(确保 `dotnet publish Hua.Todo.Host` 产出含前端的完整包)
3. **开发环境联调说明**:在 `docs/manual/08-构建与开发指南.md`(或现有的开发指南对应章节)追加 "模式 B(Web 端)开发双开步骤" 与 "生产发布步骤" 小节
4. **验收测试**:新增 1 个 Host 集成测试(`WebRootStaticFileTests`),验证 HTTP GET `/` 返回 200 且 body 含 `<title>`/`<div id="app">` 特征
### 2.2 不做什么
- **不实现 `/admin/*` 管理后台前端**(只有 index.html 占位,v1.2.0 工单 06.1 规划)
- **不改嵌入式模式 MAUI/Avalonia 的前端托管逻辑**(那边是 `EmbeddedWebServerService` 内自己 UseWebRoot + 复制 wwwroot,与 Host 部署模式解耦)
- **不改 Hua.Todo.Web 构建逻辑本身**(只调用现成 `npm run build`;不修改 vite.config.ts build.outDir)
## 三、前置条件 / 依赖
- v1.3.0 工单 05 缺口 D:`CloudSyncEndpointExtensions.cs` 的还原落地(因为本工单需要在 Program.cs 末尾追加 `MapFallbackToFile`,和 `MapCloudSyncProxySettings()` 同时存在于 `Program.cs` 尾部,改动顺序不敏感但建议先落 05 的 D)
- Node.js + npm 在构建机器可用(作为前置条件,Host csproj Target 不负责安装 Node)
- 共享文件:`Hua.Todo.Host/Program.cs`、`Hua.Todo.Host/Hua.Todo.Host.csproj`、`docs/manual/08-构建与开发指南.md`
- 可与 v1.3.0 工单 03、05 并行
## 四、验收标准(Given-When-Then)
| # | 场景 | GIVEN | WHEN | THEN |
|---|---|---|---|---|
| 1 | 根路径返回前端 HTML(模式 B) | Host 已 `dotnet build`,且 `wwwroot/index.html` 有内容 | HTTP GET `http://localhost:5173/` | 返回 200;`Content-Type: text/html`;body 含 `<div id="app">`(来自 Hua.Todo.Web 模板/index.html) |
| 2 | SPA 路由 fallback | 同 1 | HTTP GET `http://localhost:5173/settings`(前端内部路由) | 返回 200;body 是 `index.html` 全文,**不是** 404 页 |
| 3 | API 路由不被 fallback 覆盖 | 同 1 | HTTP POST `/api/cloud-sync/probe` | DynamicApi 正常处理(200 / 503 都行,但不是 index.html) |
| 4 | MSBuild Target:`dotnet publish Hua.Todo.Host` | 干净克隆(删 node_modules 和 dist),Node 已装 | 执行 `dotnet publish src/Hua.Todo.Host -c Release -o ./publish-host` | `publish-host/wwwroot/` 下有 `index.html`、`assets/` 等 Hua.Todo.Web build 产物;且 publish-host 启动后 `GET /` 满足验收 1 |
| 5 | MSBuild Target 增量构建(不重复 npm run build) | Hua.Todo.Web src 文件未变动,已执行过一次 build | 再次执行 `dotnet build src/Hua.Todo.Host` | npm build 阶段跳过后直接 copy(Inputs/Outputs 正确);日志出现 "Skipping target BuildHuaTodoWeb"(或等价跳过提示) |
| 6 | 开发联调(文档可执行) | 新环境 clone 后 Node 已装、.NET 10 SDK 已装 | 按 docs/manual 新增章节的步骤执行(两条命令:1. Vite dev;2. Host run) | Vite :5174 页面打开、API 请求能到 Host :5173;云同步探测能返回 `isReachable` JSON(与 307 修复后的链路一致) |
## 五、并行冲突规避
| Touch List | 共享文件策略 | 编译绿线策略 |
|---|---|---|
| `Hua.Todo.Host/Program.cs` | 只在 `app.UseDynamicApi();` **之前**插入 `UseDefaultFiles/UseStaticFiles`;在 `app.Run()` **之前**追加 `MapFallbackToFile("index.html")`(若已存在 `/api/cloud-sync/proxy-settings` endpoint 则不改动) | `dotnet build Hua.Todo.Host.csproj` 0 错误 |
| `Hua.Todo.Host/Hua.Todo.Host.csproj` | 追加 `<Target Name="BuildHuaTodoWebWebRoot" BeforeTargets="BeforeBuild" ...>` 节点;**不修改任何现有 PropertyGroup/ItemGroup/Target** | 同编译 + `dotnet publish` 产出验证 |
| `docs/manual/08-构建与开发指南.md` | 末尾追加两个新小节标题,不改原有章节顺序 | 纯 Markdown |
| `test/Hua.Todo.Host.Tests/...` | 新建 `WebRootStaticFileTests.cs`(独立文件),不修改既有测试类 | `dotnet test Hua.Todo.Host.Tests` 新增 + 既有全部通过 |
## 六、风险与回滚
| 风险 | 影响 | 应对策略 |
|---|---|---|
| `npm install` 时长慢(首次)+ 网络差导致构建失败 | CI publish 超时 | MSBuild Target 加 `ContinueOnError="WarnAndContinue"` + 清晰错误日志:"Hua.Todo.Web build skipped,deploy wwwroot 是空的——请手动构建 Hua.Todo.Web 后重试" |
| `MapFallbackToFile` 顺序放错,覆盖掉 `/api/*` 或 `/swagger/*` | API / Swagger 404 | 在 Program.cs 写明确顺序:`UseSwagger → UseStaticFiles → UseAuthentication/Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile`;并在集成测试里同时验证 fallback 和 API 都正常 |
| 增量构建 Inputs/Outputs 写粗了导致漏 build(或反复 build) | 前端代码变更后不生效 / 构建变慢 | Inputs 精确到 `Hua.Todo.Web/package.json` + `Hua.Todo.Web/src/**/*.{ts,vue,css,html}`;Outputs 精确到 `wwwroot/index.html` |
| CI 环境 Node 不存在 | 构建报错 | Target 检测不到 `node`/`npm` 时仅 Warning,不阻塞 Host API 层 build(生产环境可由外部 CI 先 build 前端再打包) |
## 七、与其他工单的语音/UI 入口衔接
> 按 [05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md) 第 3.2 条:纯基础设施调整无需检查双入口。
| 入口 | 已规划 | 方案 |
|---|---|---|
| UI | ✅(本次就是 UI 托管入口补齐) | 模式 B `http://localhost:5173/` 直接返回 Hua.Todo.Web |
| 语音 | ❌(无需) | 不涉及业务操作 |
---
**创建日期**:2026-08-08
**关联规则**:[01-项目架构.md §3 模式 B](../../../.trae/rules/项目/01-项目架构.md)
**关联工单**:v1.3.0 工单 05;v1.2.0 工单 06.1(Admin 管理后台前端)