9223ceca50
- 规则重组:全局/ 下 8 个规则合并为 6 个(01+02→01,05+06→04),序号顺延 - 新增项目规则 05-多入口功能同步规范(UI/语音入口覆盖检查) - 新增 MCP 服务基础设施:Mcp/ 目录(DI 注册、端点扩展、动态工具描述符)、单元测试 - v1.3.0 工单文档:03 系列(会议任务拆分)、04(富文本描述与附件管理) - MCP 接口与前端集成指南:docs/manual/08、09
555 lines
22 KiB
Markdown
555 lines
22 KiB
Markdown
# 研发工单 v1.3.0 - 04 富文本描述、附件与外部链接
|
||
|
||
> 术语澄清:本文件中"研发工单"指智能体/开发者执行的**编码工作项**;"任务/待办项/Todo"指 Hua.Todo 业务领域的 **Todo 待办项**(Task 实体),二者请勿混淆。
|
||
|
||
---
|
||
|
||
## 一、目标与范围
|
||
|
||
### 1.1 目标
|
||
|
||
当前 Todo 待办项仅包含单行标题(`Title`),无法满足"一个待办项关联更多上下文信息"的需求。本工单为 Todo 待办项新增三项能力:
|
||
|
||
| 能力 | 描述 |
|
||
|---|---|
|
||
| **多行描述(Description)** | 每个待办项可附带一段多行文字描述,用于记录详细说明、步骤、备注等 |
|
||
| **文件附件(Attachments)** | 可为一个待办项关联多个本地文件,支持上传、查看、下载、删除 |
|
||
| **外部链接与程序启动** | 附件可以是外部 URL(点击在系统默认浏览器打开)或本地文件路径/可执行文件(点击通过系统关联程序打开) |
|
||
|
||
### 1.2 平台范围
|
||
|
||
| 平台 | 是否支持 | 说明 |
|
||
|---|---|---|
|
||
| Windows(MAUI) | ✅ 支持 | 全功能:描述编辑、附件管理、外部程序/链接打开 |
|
||
| Linux(Avalonia) | ✅ 支持 | 全功能;`xdg-open` 打开外部资源 |
|
||
| macOS(MAUI) | 待定 | 视为 Windows 同级,但本期不单独投入 |
|
||
| iOS / Android | ❌ 不支持 | 移动端不提供附件/描述编辑入口,但**移动端编辑待办项时不得覆盖/清空已有描述和附件数据**(详见 1.4 节) |
|
||
|
||
### 1.3 范围
|
||
|
||
**包含**:
|
||
|
||
- `TaskEntity` 新增 `Description` 字段(多行文本)
|
||
- 新增 `AttachmentEntity` 数据模型(附件元数据)
|
||
- 附件 CRUD API(上传、列表、下载、删除、打开)
|
||
- 前端编辑对话框扩展(描述 textarea + 附件管理区域)
|
||
- 前端待办项列表/详情展示(描述预览、附件数量角标)
|
||
- 桌面端通过系统关联程序打开附件(URL → 浏览器,文件路径 → 关联应用)
|
||
|
||
**不包含**:
|
||
|
||
- 附件云同步(本期不涉及,后续版本规划)
|
||
- 附件预览(如图片缩略图、PDF 内嵌预览,本期不做)
|
||
- 移动端附件管理(本期仅桌面端)
|
||
- 富文本编辑器(如 Markdown 渲染,本期仅纯文本多行)
|
||
|
||
### 1.4 跨平台数据安全:移动端编辑不得破坏已有数据(强制)
|
||
|
||
**背景**:移动端(iOS/Android)不提供描述/附件的编辑 UI,但用户仍可在移动端修改待办项标题、优先级、完成状态等基础字段。如果没有防护,移动端发起更新请求时会用"空值"覆盖掉桌面端已设置的 `Description` 和 `Attachments`,导致数据丢失。
|
||
|
||
**约束**:
|
||
|
||
| 约束项 | 要求 |
|
||
|---|---|
|
||
| **后端 API 必须支持真正的部分更新** | `PUT /api/task/{id}` 的请求体中,**未传递的字段保持原值不变**,不得将缺失字段视为"置空"。即:`description` 不在 JSON 中 → 不修改数据库中的 `Description`;`description` 为 `null` → 清空 |
|
||
| **UpdateTaskDto 所有扩展字段均为 optional** | `description`、`attachments` 相关字段在 DTO 中均标记为可选(`string?` / `null`),与必填字段(`id`)区分 |
|
||
| **移动端前端不传递未知字段** | 移动端构建 `UpdateTaskDto` 时只传 `id`、`title`、`priority`、`isCompleted`,不传 `description`、`attachments`。后端对这些字段视为"不修改" |
|
||
| **Attachment 实体独立于 Task 更新** | 附件 CRUD 走独立端点(`/api/task/{id}/attachments`),不通过 `PUT /api/task/{id}` 携带。移动端不调附件端点,自然不会破坏附件数据 |
|
||
|
||
**验证方式**:
|
||
1. 桌面端创建待办项,添加描述 + 上传附件
|
||
2. 移动端编辑同一待办项(只改标题),保存
|
||
3. 回到桌面端查看 → 描述和附件完整保留,未被覆盖
|
||
|
||
---
|
||
|
||
## 二、前置条件
|
||
|
||
| 条件 | 说明 | 状态 |
|
||
|---|---|---|
|
||
| Hua.Todo v1.2.0 | 基础业务能力(Task CRUD、父子任务) | 已完成 |
|
||
| 工单 03 TaskType 枚举 | `TaskEntity` 已有字段扩展模式可参考 | 待实现(03 先于 04 或并行) |
|
||
| 桌面 WebView 文件选择 | 前端 `<input type="file">` 能力 | 已就绪 |
|
||
| 桌面程序启动 | 通过后端 `Process.Start` / `xdg-open` 实现 | 需新增 |
|
||
|
||
---
|
||
|
||
## 三、功能入口覆盖检查
|
||
|
||
> 依据 [.trae/rules/项目/05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md),新功能必须检查两个入口覆盖情况。
|
||
|
||
| 入口 | 已规划 | 方案 |
|
||
|---|---|---|
|
||
| UI | ✅ | 编辑对话框新增描述 textarea + 附件列表区域;TaskItem 卡片显示描述预览与附件数量角标 |
|
||
| 语音 | ⚠️ 部分 | 语音可追加/编辑描述文本(通过"给「标题」添加描述"意图);附件上传与外部程序启动不适合语音入口 |
|
||
|
||
> 语音入口覆盖说明:
|
||
> - **支持**:通过语音添加/修改待办项描述("给「XXX」添加备注:...")
|
||
> - **不支持**:语音上传附件、语音打开外部程序 —— 两者本质上是可视化交互,语音不是合适的入口。本期不覆盖。
|
||
> - TODO:工单 02 的 LLM intent parser prompt 需预留 `SET_DESCRIPTION` 意图,参数为 `targetTask` + `description`
|
||
|
||
---
|
||
|
||
## 四、数据模型设计
|
||
|
||
### 4.1 TaskEntity 扩展
|
||
|
||
在现有 `TaskEntity` 上新增一个字段:
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 多行描述文本(可为空)。
|
||
/// 用于记录待办项相关的详细说明、步骤或备注。
|
||
/// </summary>
|
||
public string? Description { get; set; }
|
||
```
|
||
|
||
| 字段 | 类型 | 默认值 | 约束 | 说明 |
|
||
|---|---|---|---|---|
|
||
| `Description` | `string?` | null | 最大 5000 字符 | 多行描述文本 |
|
||
|
||
### 4.2 AttachmentEntity(新增)
|
||
|
||
在 `Hua.Todo.Core/Entities/` 下新增 `AttachmentEntity.cs`:
|
||
|
||
```csharp
|
||
/// <summary>附件类型枚举</summary>
|
||
public enum AttachmentType
|
||
{
|
||
/// <summary>本地文件(已复制到应用数据目录)</summary>
|
||
LocalFile = 0,
|
||
/// <summary>外部链接(URL)</summary>
|
||
ExternalLink = 1
|
||
}
|
||
|
||
/// <summary>附件实体,表示待办项关联的文件或外部链接</summary>
|
||
public class AttachmentEntity
|
||
{
|
||
/// <summary>附件唯一标识符</summary>
|
||
public int Id { get; set; }
|
||
|
||
/// <summary>所属待办项ID</summary>
|
||
public int TaskId { get; set; }
|
||
|
||
/// <summary>显示名称(用户可见的文件名或链接标题)</summary>
|
||
public string FileName { get; set; } = string.Empty;
|
||
|
||
/// <summary>存储路径(本地文件的相对路径)或外部URL</summary>
|
||
public string FilePath { get; set; } = string.Empty;
|
||
|
||
/// <summary>文件大小(字节),外部链接为 0</summary>
|
||
public long FileSize { get; set; }
|
||
|
||
/// <summary>MIME 类型(如 text/plain、application/pdf),外部链接为空字符串</summary>
|
||
public string ContentType { get; set; } = string.Empty;
|
||
|
||
/// <summary>附件类型</summary>
|
||
public AttachmentType AttachmentType { get; set; } = AttachmentType.LocalFile;
|
||
|
||
/// <summary>创建时间(UTC)</summary>
|
||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||
|
||
// 导航属性
|
||
public TaskEntity Task { get; set; } = null!;
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 默认值 | 说明 |
|
||
|---|---|---|---|
|
||
| `Id` | `int` | 自增 | 主键 |
|
||
| `TaskId` | `int` | - | FK → Tasks.Id |
|
||
| `FileName` | `string` | - | 显示名称,最大 256 字符 |
|
||
| `FilePath` | `string` | - | 存储路径或外部 URL,最大 1024 字符 |
|
||
| `FileSize` | `long` | 0 | 字节数 |
|
||
| `ContentType` | `string` | "" | MIME 类型 |
|
||
| `AttachmentType` | `AttachmentType` | `LocalFile` | 本地文件 vs 外部链接 |
|
||
| `CreatedAt` | `DateTime` | `UtcNow` | 创建时间 |
|
||
|
||
`TaskEntity` 新增导航属性:
|
||
```csharp
|
||
public List<AttachmentEntity> Attachments { get; set; } = new();
|
||
```
|
||
|
||
### 4.3 数据库表
|
||
|
||
| 表名 | 说明 |
|
||
|---|---|
|
||
| `T_Attachments` | 附件表 |
|
||
|
||
> 注:表名沿用 ABP 模板规范 `T_{实体名}s`(见 [.trae/rules/项目/03-数据模型与迁移约束.md](../../../.trae/rules/项目/03-数据模型与迁移约束.md))。
|
||
|
||
### 4.4 附件存储策略
|
||
|
||
| 策略项 | 决定 |
|
||
|---|---|
|
||
| 存储位置 | 应用数据目录下 `Attachments/` 子目录(与数据库 `.db` 同级) |
|
||
| 文件命名 | `{attachmentId}_{originalFileName}`,避免重名冲突 |
|
||
| 文件大小上限 | 单文件 50MB(`appsettings.json` 可配置) |
|
||
| 总附件数上限 | 每个待办项最多 20 个附件 |
|
||
| 外部链接 | 不复制文件,仅存储 URL 字符串;打开时调用系统默认浏览器 |
|
||
|
||
---
|
||
|
||
## 五、API 设计
|
||
|
||
### 5.1 已修改:创建/更新待办项(扩展 Description)
|
||
|
||
```
|
||
POST /api/task
|
||
请求体新增字段:
|
||
{
|
||
"title": "...",
|
||
"priority": 1,
|
||
"description": "多行描述文本(可选)" // ← 新增
|
||
}
|
||
|
||
PUT /api/task/{id}
|
||
请求体新增字段(所有扩展字段均为可选):
|
||
{
|
||
"id": 42,
|
||
"title": "...",
|
||
"priority": 1,
|
||
"description": "..." // ← 可选;不传=保持原值,传 null=清空
|
||
}
|
||
```
|
||
|
||
> **部分更新语义(强制)**:`PUT` 端点必须实现"仅更新已传递字段"的逻辑。
|
||
> - `description` 字段**不在 JSON 中** → 数据库 `Description` 保持原值
|
||
> - `description` 字段**为 `null`** → 数据库 `Description` 清空为 `null`
|
||
> - 此设计确保移动端不传递 `description` 时不会意外清空已有描述
|
||
|
||
### 5.2 附件上传
|
||
|
||
```
|
||
POST /api/task/{taskId}/attachments
|
||
Content-Type: multipart/form-data
|
||
字段:
|
||
- file: 文件内容(必填,最大 50MB)
|
||
|
||
响应体:
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 1,
|
||
"taskId": 42,
|
||
"fileName": "需求文档.pdf",
|
||
"fileSize": 204800,
|
||
"contentType": "application/pdf",
|
||
"attachmentType": 0,
|
||
"createdAt": "2026-06-16T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 添加外部链接
|
||
|
||
```
|
||
POST /api/task/{taskId}/attachments/link
|
||
请求体:
|
||
{
|
||
"url": "https://example.com/doc",
|
||
"fileName": "参考文档" // 可选,不传则使用 URL 作为显示名
|
||
}
|
||
|
||
响应体:
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 2,
|
||
"taskId": 42,
|
||
"fileName": "参考文档",
|
||
"filePath": "https://example.com/doc",
|
||
"fileSize": 0,
|
||
"contentType": "",
|
||
"attachmentType": 1,
|
||
"createdAt": "2026-06-16T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.4 获取附件列表
|
||
|
||
```
|
||
GET /api/task/{taskId}/attachments
|
||
|
||
响应体:
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"fileName": "需求文档.pdf",
|
||
"fileSize": 204800,
|
||
"contentType": "application/pdf",
|
||
"attachmentType": 0,
|
||
"createdAt": "2026-06-16T10:00:00Z"
|
||
},
|
||
{
|
||
"id": 2,
|
||
"fileName": "参考文档",
|
||
"filePath": "https://example.com/doc",
|
||
"attachmentType": 1,
|
||
"createdAt": "2026-06-16T10:05:00Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 5.5 下载附件
|
||
|
||
```
|
||
GET /api/attachments/{attachmentId}/download
|
||
|
||
响应:文件流(Content-Disposition: attachment; filename="需求文档.pdf")
|
||
```
|
||
|
||
### 5.6 打开附件(桌面端)
|
||
|
||
```
|
||
POST /api/attachments/{attachmentId}/open
|
||
|
||
响应体:
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"opened": true
|
||
}
|
||
}
|
||
|
||
// 后端行为:
|
||
// - AttachmentType.LocalFile → Process.Start(filePath)(Windows)或 xdg-open(Linux)
|
||
// - AttachmentType.ExternalLink → Process.Start(url)(在默认浏览器打开)
|
||
```
|
||
|
||
### 5.7 删除附件
|
||
|
||
```
|
||
DELETE /api/task/{taskId}/attachments/{attachmentId}
|
||
|
||
响应体:
|
||
{
|
||
"success": true,
|
||
"message": "附件已删除"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、前端设计
|
||
|
||
### 6.1 类型扩展
|
||
|
||
`src/Hua.Todo.Web/src/types/task.ts` 新增 / 修改:
|
||
|
||
```typescript
|
||
// Task 接口新增字段
|
||
export interface Task {
|
||
// ... 既有字段
|
||
description?: string; // 多行描述
|
||
attachments?: AttachmentItem[]; // 附件列表
|
||
attachmentCount?: number; // 附件数量(列表视图用,不传完整列表)
|
||
}
|
||
|
||
export interface AttachmentItem {
|
||
id: number;
|
||
taskId: number;
|
||
fileName: string;
|
||
filePath: string;
|
||
fileSize: number;
|
||
contentType: string;
|
||
attachmentType: AttachmentType; // 0=本地文件, 1=外部链接
|
||
createdAt: string;
|
||
}
|
||
|
||
export type AttachmentType = 0 | 1;
|
||
```
|
||
|
||
### 6.2 组件变更
|
||
|
||
| 组件 | 变更类型 | 说明 |
|
||
|---|---|---|
|
||
| `TaskEditDialog.vue` | 修改 | 新增描述 textarea + 附件管理区域(上传按钮、附件列表) |
|
||
| `TaskItem.vue` | 修改 | 卡片底部显示描述预览(单行截断)+ 附件数量角标 |
|
||
| `AttachmentList.vue` | 新增 | 附件列表组件:文件名、大小、类型图标、删除按钮、打开按钮 |
|
||
| `LinkInputDialog.vue` | 新增 | 外部链接输入弹出框(URL + 显示名称) |
|
||
|
||
### 6.3 新增 API 模块
|
||
|
||
`src/Hua.Todo.Web/src/api/attachments.ts`:
|
||
|
||
```typescript
|
||
// uploadAttachment(taskId, file) — POST multipart/form-data
|
||
// addLink(taskId, url, fileName?) — POST /api/task/{id}/attachments/link
|
||
// getAttachments(taskId) — GET
|
||
// deleteAttachment(taskId, attId) — DELETE
|
||
// openAttachment(attId) — POST /api/attachments/{id}/open
|
||
// downloadAttachment(attId) — GET /api/attachments/{id}/download
|
||
```
|
||
|
||
### 6.4 交互流程
|
||
|
||
```
|
||
编辑待办项对话框
|
||
├─ 标题输入框(现有)
|
||
├─ 优先级选择(现有)
|
||
├─ 描述 textarea(新增:多行文本,placeholder "添加备注、步骤说明...")
|
||
├─ 附件区域(新增)
|
||
│ ├─ [+ 上传文件] 按钮 → 触发文件选择器 → 上传至后端
|
||
│ ├─ [+ 添加链接] 按钮 → 弹出 URL 输入框 → 保存为外部链接附件
|
||
│ └─ 附件列表(每项显示:图标 + 文件名 + 大小 + [打开] [删除])
|
||
│ ├─ 本地文件:[打开] → 调用后端 open API → 系统关联程序打开
|
||
│ ├─ 外部链接:[打开] → 调用后端 open API → 浏览器打开
|
||
│ └─ [删除] → 确认 → 删除附件
|
||
└─ [取消] [保存]
|
||
```
|
||
|
||
### 6.5 列表卡片展示
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ ☐ 整理产品需求文档 │
|
||
│ 高优先级 │
|
||
│ 需要在本周五前完成需求文档的整理... │ ← 描述预览(单行,超长截断)
|
||
│ 📎 2 个附件 │ ← 附件数量角标
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 七、后端架构
|
||
|
||
### 7.1 新增服务
|
||
|
||
| 服务/类 | 位置 | 说明 |
|
||
|---|---|---|
|
||
| `AttachmentEntity` | `Hua.Todo.Core/Entities/` | 附件实体 |
|
||
| `AttachmentType` | `Hua.Todo.Core/Entities/` | 附件类型枚举 |
|
||
| `IAttachmentRepository` | `Hua.Todo.Core/Repositories/` | 仓储接口 |
|
||
| `AttachmentRepository` | `Hua.Todo.Application/Data/` | EF Core 仓储实现 |
|
||
| `AttachmentService` | `Hua.Todo.Application/Services/` | 附件业务逻辑(上传、下载、打开、清理) |
|
||
| `IAttachmentService` | `Hua.Todo.Application/Services/` | 服务接口 |
|
||
| `AttachmentController` | `Hua.Todo.Application/DynamicApi/` 或独立 Controller | 附件动态 API 端点(或手动 Controller) |
|
||
| `PlatformAttachmentOpener` | `Hua.Todo.{Maui,Avalonia}/Services/` | 平台特定文件/链接打开实现(`Process.Start` / `xdg-open`) |
|
||
|
||
### 7.2 平台差异:外部程序启动
|
||
|
||
| 平台 | 实现方式 |
|
||
|---|---|
|
||
| Windows(MAUI) | `System.Diagnostics.Process.Start(new ProcessStartInfo { FileName = path, UseShellExecute = true })` |
|
||
| Linux(Avalonia) | `System.Diagnostics.Process.Start("xdg-open", path)` |
|
||
|
||
通过 `IPlatformAttachmentOpener` 接口在 Core 定义,MAUI / Avalonia 宿主各自实现并注册。
|
||
|
||
### 7.3 附件文件管理
|
||
|
||
- 上传的本地文件复制到 `<AppData>/Hua.Todo/Attachments/{attachmentId}_{originalFileName}`
|
||
- 删除附件时同步删除磁盘文件
|
||
- 删除待办项时级联删除其所有附件(实体 + 文件)
|
||
- 启动时验证附件文件完整性(数据库有记录但文件缺失 → 标记为失效,UI 提示)
|
||
|
||
### 7.4 安全约束
|
||
|
||
| 约束 | 说明 |
|
||
|---|---|
|
||
| 文件类型白名单 | 不限制(本地工具场景,用户自行管理) |
|
||
| 文件大小上限 | 50MB(可配置) |
|
||
| 路径遍历防护 | 文件名去除 `../`、`..\` 等危险路径片段 |
|
||
| 外部链接验证 | 仅允许 `http://`、`https://` 协议 |
|
||
|
||
---
|
||
|
||
## 八、子工单拆分
|
||
|
||
本工单体积适中,不拆分子工单,单文件完整实现。
|
||
|
||
如需与工单 03 并行推进,注意以下共享文件:
|
||
|
||
| 共享文件 | Writer | 说明 |
|
||
|---|---|---|
|
||
| `TaskEntity.cs` | **工单 03** 先写入 | 03 新增 TaskType/MeetingNotes/AudioDuration;04 后续追加 Description/Attachments 导航属性 |
|
||
| `todoDbContext.cs` | **工单 03** 先写入 | 03 新增 TaskType 映射;04 后续追加 Attachments DbSet + 关系映射 |
|
||
| `task.ts` | **工单 03** 先写入 | 03 新增 taskType;04 后追加 description、attachments |
|
||
| `TaskEditDialog.vue` | 协调 | 03 新增 MeetingType 条件渲染;04 追加描述 + 附件区域 |
|
||
|
||
> 执行顺序:优先完成 03-01(数据模型),再开始 04 的后端模型部分。前端组件部分可独立并行。
|
||
|
||
---
|
||
|
||
## 九、验收标准
|
||
|
||
| 验收项 | 验证方法 | 预期结果 |
|
||
|---|---|---|
|
||
| 描述编辑 | 编辑待办项,在描述 textarea 输入多行文字,保存 | 再次打开编辑框,描述内容完整保留 |
|
||
| 描述显示 | 查看待办项列表/详情 | 列表卡片显示描述预览(单行截断),详情显示完整描述 |
|
||
| 附件上传 | 编辑待办项,点击上传文件,选择一个本地文件 | 文件上传成功,附件列表显示文件名和大小 |
|
||
| 附件下载 | 在附件列表点击下载按钮 | 文件以原始文件名下载到本地 |
|
||
| 附件删除 | 点击附件删除按钮 | 附件从列表移除,磁盘文件同步清理 |
|
||
| 外部链接添加 | 点击添加链接,输入 URL | 链接保存为附件,类型标记为"外部链接" |
|
||
| 打开本地文件 | 点击本地附件"打开" | 系统关联程序打开该文件(如 PDF → PDF 阅读器) |
|
||
| 打开外部链接 | 点击外部链接附件"打开" | 系统默认浏览器打开该 URL |
|
||
| 打开可执行文件 | 附件为 `.exe`(Windows)或可执行脚本(Linux) | 系统正常运行该程序 |
|
||
| 附件数量限制 | 超过 20 个附件后继续上传 | 提示"每个待办项最多 20 个附件" |
|
||
| 文件大小限制 | 上传超过 50MB 的文件 | 提示"文件大小不能超过 50MB" |
|
||
| 待办项删除级联 | 删除有附件的待办项 | 附件记录和磁盘文件同步清理 |
|
||
| 空描述 | 创建待办项时不填描述 | 正常创建,列表中不显示描述行 |
|
||
| 跨平台安全 | 桌面端设置描述+附件 → 移动端只改标题并保存 → 桌面端查看 | 描述和附件完整保留,未被覆盖(详见 1.4 节) |
|
||
|
||
---
|
||
|
||
## 十、风险与回滚
|
||
|
||
| 风险 | 影响 | 应对策略 |
|
||
|---|---|---|
|
||
| `Process.Start` 在不同 Linux 发行版行为差异 | 部分 Linux 文件打开失败 | 用 `xdg-open` 兜底;提供错误提示"无法打开文件:{原因}" |
|
||
| 附件文件被用户手动删除 | 数据库有记录但文件不存在 | 启动时校验,缺失文件标记为"失效",UI 用灰色 + 感叹号提示 |
|
||
| 大文件上传耗尽磁盘空间 | 应用数据目录磁盘满 | 单文件 50MB + 每待办项 20 个上限 = 最多 1GB;检查和提示剩余空间 |
|
||
| 并发编辑附件冲突 | 多窗口同时删除/添加附件 | 附件操作为即时保存(非批量提交),利用数据库事务隔离 |
|
||
|
||
---
|
||
|
||
## 十一、Touch List
|
||
|
||
| 文件路径 | 修改类型 | 是否共享 | 说明 |
|
||
|---|---|---|---|
|
||
| `src/Hua.Todo.Core/Entities/TaskEntity.cs` | 修改 | 是(与 03 共享) | 新增 Description 字段 + Attachments 导航属性 |
|
||
| `src/Hua.Todo.Core/Entities/AttachmentEntity.cs` | 新增 | 否 | 附件实体 |
|
||
| `src/Hua.Todo.Core/Entities/AttachmentType.cs` | 新增 | 否 | 附件类型枚举 |
|
||
| `src/Hua.Todo.Core/Repositories/IAttachmentRepository.cs` | 新增 | 否 | 附件仓储接口 |
|
||
| `src/Hua.Todo.Application/Data/TodoDbContext.cs` | 修改 | 是(与 03 共享) | 新增 Attachments DbSet + 关系映射 |
|
||
| `src/Hua.Todo.Application/Services/IAttachmentService.cs` | 新增 | 否 | 附件服务接口 |
|
||
| `src/Hua.Todo.Application/Services/AttachmentService.cs` | 新增 | 否 | 附件业务逻辑 |
|
||
| `src/Hua.Todo.Application/Controllers/AttachmentController.cs` | 新增 | 否 | 附件 API 端点 |
|
||
| `src/Hua.Todo.Application/Models/AttachmentModels.cs` | 新增 | 否 | 附件 DTO |
|
||
| `src/Hua.Todo.Core/Services/IPlatformAttachmentOpener.cs` | 新增 | 否 | 平台文件打开接口 |
|
||
| `src/Hua.Todo.Maui/Services/Platforms/MauiAttachmentOpener.cs` | 新增 | 否 | Windows MAUI 文件打开实现 |
|
||
| `src/Hua.Todo.Avalonia/Services/Platforms/AvaloniaAttachmentOpener.cs` | 新增 | 否 | Linux Avalonia 文件打开实现 |
|
||
| `src/Hua.Todo.Web/src/api/attachments.ts` | 新增 | 否 | 前端附件 API 模块 |
|
||
| `src/Hua.Todo.Web/src/types/task.ts` | 修改 | 是(与 03 共享) | 新增 description、AttachmentItem 等类型 |
|
||
| `src/Hua.Todo.Web/src/components/TaskEditDialog.vue` | 修改 | 是(与 03 共享) | 新增描述 textarea + 附件管理区域 |
|
||
| `src/Hua.Todo.Web/src/components/TaskItem.vue` | 修改 | 否 | 描述预览 + 附件角标 |
|
||
| `src/Hua.Todo.Web/src/components/AttachmentList.vue` | 新增 | 否 | 附件列表组件 |
|
||
| `src/Hua.Todo.Web/src/composables/useAttachments.ts` | 新增 | 否 | 附件管理组合式函数 |
|
||
|
||
---
|
||
|
||
## 十二、与工单 03 的交互
|
||
|
||
| 维度 | 工单 03 | 工单 04 |
|
||
|---|---|---|
|
||
| 描述文本 | `MeetingNotes`(会议纪要,仅会议类型) | `Description`(通用多行描述,所有类型) |
|
||
| 附件 | 不涉及 | 通用附件管理 |
|
||
| 外部链接 | 不涉及 | 通用外部链接 |
|
||
|
||
> `MeetingNotes` 与 `Description` 是两个独立字段:
|
||
> - `MeetingNotes`:会议转写/文字纪要,可能很长(数千字),仅在会议类型下有值
|
||
> - `Description`:通用简短备注(上限 5000 字符),所有类型的待办项都可以有
|
||
>
|
||
> 两者可共存:会议类型的待办项可以同时有 `MeetingNotes`(AI 拆分的输入)和 `Description`(用户的简短备注)。
|
||
|
||
---
|
||
|
||
**工单编号**:04
|
||
**标题**:富文本描述、附件与外部链接
|
||
**版本**:v1.3.0
|
||
**创建日期**:2026-06-16
|