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
+16
View File
@@ -10,6 +10,22 @@
- v1.2.0:Linux 支持与增强功能
- v1.3.0(开发中):MCP 服务与语音控制
### v1.3.0 (2026-08-08,Host 双角色+路由+鉴权流修补)
- **修复:根路径 404 / 前端缺失引导**:`Hua.Todo.Host/Program.cs` 新增 `MinimalBootstrapPageMiddleware`(位于 Swagger 之后、UseDefaultFiles/UseStaticFiles 之前)。当前端 `wwwroot/index.html` 缺失时,访问 `/` 或 SPA 路由(如 `/settings`)不再返回 404,而是返回**中文最小引导页**(含期望路径、WebRoot、环境版本、三种修复方式:`dotnet build Host` / 手动 npm build / CI skip build;同时列出 `/swagger`、`/api/auth/bootstrap` 等仍可用的入口),解决了"根路径 404 看起来像后端挂了"的误判问题。
- **修复:没权限应该跳登录页**:`DynamicApiMiddleware` 新增 `WriteUnauthorizedResponse` / `WriteForbiddenResponse` / `IsBrowserHtmlRequest` 三个方法。判定规则:请求是 `GET`/`HEAD` 且 `Accept: text/html` 且 **非** `X-Requested-With: XMLHttpRequest` → 视为「用户在地址栏直访问接口页面」→ 返回 `302 Location: "/"`,回到 Host 前端 SPA,SPA 判断未登录 → 弹出登录/云同步设置 `CloudSyncSettingsDialog` 完成鉴权。**不破坏** axios/fetch:Accept 为 JSON 或非 GET/HEAD 时,继续返回原有的 401/403 JSON(`success=false + message`)。403 同样走 302 跳转。
- **架构文档对齐(Host 不区分模式 = 双角色统一部署)**:更新 `01-项目架构.md` 模式 B 章节:Host 部署时**同时承担 (1) Web 端前端/后端同源部署和 (2) 云同步权威服务端**两种职责,不区分"模式";端点列表扩展为 4 项(`/`、`/api/*`、`/mcp`、`/swagger + /admin/*`);明确 401/403 在浏览器页面时 302 跳 `/` 由 SPA 接管登录;新增中间件顺序硬约束链接。
- **验收测试补齐(ATDD)**:新增 `test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs`,覆盖 6 场景:① 无 index.html 时 GET `/` + AcceptHtml → 200 + 引导页含「前端尚未构建」构建命令;② `/settings`(SPA 路由)同样 200 引导页;③ 未登录浏览器 GET `/api/security/policy`(PolicyRead 权限)+ AcceptHtml → 302 `/`;④ 未登录 AcceptJson 同一接口 → 401 JSON `success=false`;⑤ PUT `/api/security/policy`(非 GET)+ AcceptHtml → 仍 401 JSON 不跳转;⑥ 匿名 `POST /api/cloud-sync/probe` 不被 redirect 或 401 影响。6 个新测试 + 原 `WebRootStaticFileTests` 4 个场景 = 10 个全部通过。
### v1.3.0 (2026-06-17)
- **架构对齐(v1.3.0 工单 05)**:新增「Hua.Todo.HttpApi.AspNetCore」中间层到项目清单和依赖图(Core → Application → HttpApi.AspNetCore → {Host,Maui,Avalonia});抽离 `AddCloudSyncServer` 到 `CloudSyncServiceCollectionExtensions.cs`;清理 `01-项目架构.md` §5 过时的扩展点路径;`00-工单总览.md` 明确模式 B 中间件顺序硬约束,`MapCloudSyncProxySettings` 已废弃(接口改由 DynamicApi 自动暴露)。
- **Web 端根路径托管前端(v1.3.0 工单 06)**:模式 B 下 Host 同时承担「动态 API + 前端静态站点 + MCP」三大职责;`Program.cs` 新增 `UseDefaultFiles`/`UseStaticFiles`/`MapFallbackToFile("index.html")` 并对齐中间件顺序;`Host.csproj` 新增 `BuildHuaTodoWebWebRoot` Target(增量 npm build + 双份 wwwroot 拷贝 + Node 缺失 Warning);开发文档新增「模式 B 双开」与「BuildHuaTodoWebWebRoot 自动构建」指南;新增 `WebRootStaticFileTests.cs` 覆盖根路径 / SPA fallback / API 不覆盖 / Swagger 不覆盖 4 场景。
- **CloudSync Swagger + 路由修复**:CloudSync 接口改查 `IsMetadataEnabled`,移除 `IsEnabled=false`,Swagger 中 `/auth/* /tasks/* /security/* /admin/* /cloud-sync/*` 可见;MCP 工具改用命名空间过滤 `Hua.Todo.Application.CloudSync.*`,避免云同步接口暴露为 MCP。
- **CloudSync 端点 DynamicApi 化**:移除 16 个手动 `MapCloudSyncEndpoints()`,新增 5 个 `IDynamicApiService` 接口 + `DynamicApiRouteAttribute` + `RequirePermissionAttribute`,中间件支持自定义前缀与权限检查;错误响应统一 401/403。
- **测试项目重构**:`src/test/` → `test/`;Host.Tests 161 个测试全部通过。
- **Host 云同步端点补齐修复**:Program.cs 新增 `AddCloudSyncServer()` + `MapCloudSyncEndpoints()`(后已改为 DynamicApi) + `app.UseAuthentication()` 顺序;前端云同步默认账号 `admin`/`123456` + 默认地址 `http://localhost:5173`;`TaskList.vue` 新增登录/登出/同步按钮。
### v1.3.0 (2026-06-16)
- **MCP 服务**:新增 MCP 协议端点(`/mcp`),DynamicMcpToolExtensions 自动扫描 IDynamicApiService 接口生成 MCP 工具;当前覆盖 ITaskService(9 个)+ IVoiceService(4 个)= 13 个 MCP 工具
+58 -8
View File
@@ -27,19 +27,60 @@ npm install
### 1.3 启动开发环境
**方式一:Windows 三件套(推荐)**
#### 方式一:模式 B「Web 端双开」(推荐,调试 Host API + 前端)
> 模式 B = Hua.Todo.Host 同时承担「动态 API + 前端静态站点 + MCP」三大职责,
> 访问 `http://localhost:5173/` 即可看到完整 Todo 前端,HTTP 同源无需再配 Vite proxy。
开发调试时分两个终端(修改前端能 HMR 热更新,不会每次都触发 npm build):
```powershell
# 终端 1:启动 Host(API 服务端)
.\start-host.ps1
# 终端 2:启动前端开发服务器
.\start-dev.ps1
# 终端 1:启动 Host(监听 :5173 HTTP + :7055 HTTPS,未登录时 HTTP 即可)
# ⚠️ 若尚未构建过前端,Host 直接启动后 GET / 返回 404。首次需要先 build 一次:
# cd src/Hua.Todo.Web && npm ci && npm run build
# 后续 build 会被 MSBuild Target 自动触发,见 1.4 节。
cd src/Hua.Todo.Host
dotnet run # 或 Visual Studio F5 调试 Hua.Todo.Host
```
**方式二:MAUI 嵌入模式**
```powershell
# 终端 2:Vite 开发服务器(:5174,负责前端 HMR;通过 .env.development 把 API 打回 :5173 Host)
cd src/Hua.Todo.Web
npm run dev
# 浏览器访问 http://localhost:5174 → 代码保存后浏览器立即刷新
```
在 Visual Studio 中直接调试运行 `Hua.Todo.Maui` 项目,MAUI 会自动启动内嵌 WebServer 并加载前端。
> 关闭 Vite 后直接访问 `http://localhost:5173/` 也能看到上次构建的前端静态页(非 HMR)。
#### 方式二:模式 A「MAUI 嵌入模式」
在 Visual Studio 中直接调试运行 `Hua.Todo.Maui` 项目,MAUI 会自动启动内嵌 WebServer(`localhost:5057`)并加载前端。
云同步链路:前端同源调用 `:5057` → CloudSyncProxy → 用户配置的 Web 端地址(默认 `http://localhost:5173`)。
#### 方式三:纯 Host 发布模式(快速验证部署产物)
```powershell
# 单次构建(前端 dist + Host 二进制 + wwwroot 复制)
dotnet build src/Hua.Todo.Host -c Release
dotnet run -c Release --project src/Hua.Todo.Host
# 浏览器访问 http://localhost:5173/ → 与部署后的体验一致
```
### 1.4 BuildHuaTodoWebWebRoot 自动构建说明
`src/Hua.Todo.Host/Hua.Todo.Host.csproj` 内置 `BuildHuaTodoWebWebRoot` MSBuild Target(触发时机:`BeforeBuild`),每次 `dotnet build/publish Hua.Todo.Host` 时执行:
| 行为 | 说明 |
|---|---|
| 增量构建 | 仅当 `src/Hua.Todo.Web/package.json` 或 `src/**/*.{ts,vue,css,html}` 变化时才 npm build,否则跳过 |
| Node 缺失保护 | 未安装 Node/npm 时仅输出 Warning,不阻塞 Host C# API 层编译(CI 可在外层单独 build 前端) |
| 跳过开关 | `dotnet build /p:SkipBuildHuaTodoWebWebRoot=true` 禁用整个 Target |
| 双份拷贝 | 产物同时写入 `src/Hua.Todo.Host/wwwroot/`(本地 `dotnet run`)和 `bin/Release/net10.0/wwwroot/`(`dotnet publish` 打包) |
常见提示排查:
- **`[BuildHuaTodoWebWebRoot] 未检测到 Node/npm`**:安装 Node.js 18+ 并重启终端;或 CI 先 `cd src/Hua.Todo.Web && npm ci && npm run build`。
- **`[BuildHuaTodoWebWebRoot] npm run build 退出码 != 0`**:去 `src/Hua.Todo.Web` 手动 `npm run build` 看 TypeScript/Vite 报错,修复后再 build Host。
## 2. 版本构建 (Release Build)
@@ -68,6 +109,15 @@ npm install
## 3. 服务端部署
### 3.0 构建流程(新增:前端 dist 自动进 wwwroot)
v1.3.0 起 `dotnet publish src/Hua.Todo.Host` 会自动执行:
1. `npm install`(仅 node_modules 不存在时)
2. `npm run build` → `src/Hua.Todo.Web/dist`
3. `dist/**/*` → `publish_output/wwwroot/`(保留目录结构)
因此部署前**不再需要**单独 build 前端,直接一条命令即可。CI 若在容器内无 Node,设置 `-p:SkipBuildHuaTodoWebWebRoot=true` 并在外层先 `npm ci && npm run build`。
### 3.1 Docker 部署(推荐)
```bash
@@ -51,3 +51,5 @@
- Linux:在基线发行版(建议 Ubuntu LTS)上可启动、可渲染前端、可调用本地 API;且有可安装/可运行的交付产物
- Search:可在主界面按 Todo 待办项标题实时过滤(含层级 Todo 的展示策略清晰)
- 云同步(基础可用):可配置服务端地址;登录后可拉取该用户的 Todo 数据;服务端可下发"是否允许落盘";客户端在禁止落盘时不产生本地持久化
@@ -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 管理后台前端)