# 研发工单 v1.3.0 - 03-01 会议数据模型与 API > 父工单:[03-会议任务拆分](./03-会议任务拆分.md) --- ## 一、目标 为"会议任务拆分"功能建立数据模型:扩展 `TaskEntity` 新增 `TaskType` 枚举和 `MeetingNotes`/`AudioDuration` 字段,定义完整的会议相关 API 端点与 DTO,完成数据库迁移。 ## 二、范围 **包含**: - 新增 `TaskType` 枚举(Normal / Meeting) - `TaskEntity` 新增字段:`TaskType`、`MeetingNotes`、`AudioDuration` - EF Core 数据库迁移 - DTO 扩展:`CreateTaskDto`、`UpdateTaskDto`、`TaskDto` 新增对应字段 - 会议 API 端点骨架:`MeetingController` + `MeetingService` - API 路径:`/api/meeting/{taskId}/transcribe`、`/api/meeting/{taskId}/notes` - 会议相关 DTO 定义:`TranscribeRequest`、`TranscribeResponse`、`MeetingNotesRequest` **不包含**: - AI 拆分逻辑(由 03-03 完成) - STT 转写实现(由 03-02 完成) - 前端组件(由 03-04 完成) ## 三、前置条件 | 条件 | 说明 | |---|---| | Hua.Todo v1.2.0 | 基础 Task CRUD 能力、父子任务、DynamicApi 中间件 | | ABP 基类迁移(可选) | 若工单 09(v1.2.0)已完成,TaskEntity 已转为 ABP 基类,字段新增方式不同 | ## 四、详细规格 ### 4.1 TaskType 枚举 ```csharp /// 待办项类型,区分普通待办项与会议 public enum TaskType { /// 普通待办项(默认) Normal = 0, /// 会议:包含录音/纪要,支持 AI 任务拆分 Meeting = 1 } ``` 文件位置:`src/Hua.Todo.Core/Entities/TaskType.cs` ### 4.2 TaskEntity 新增字段 ```csharp /// 待办项类型 public TaskType TaskType { get; set; } = TaskType.Normal; /// 会议纪要/转写文字(仅 Meeting 类型有值) [MaxLength(20000)] public string? MeetingNotes { get; set; } /// 录音时长(秒),仅 Meeting 类型有值 public double? AudioDuration { get; set; } ``` ### 4.3 EF Core 配置(TodoDbContext.cs) ```csharp builder.Entity(b => { // ... 现有配置 ... b.Property(x => x.TaskType) .HasDefaultValue(TaskType.Normal) .HasConversion(); // 枚举存为整数 b.Property(x => x.MeetingNotes) .HasMaxLength(20000); // 最多约 20000 字符 b.Property(x => x.AudioDuration) .IsRequired(false); }); ``` ### 4.4 数据库迁移 ``` dotnet ef migrations add AddMeetingFieldsToTasks ``` 迁移应在 `Hua.Todo.Application/Migrations/` 目录生成。 ### 4.5 DTO 扩展 **CreateTaskDto** 新增: ```csharp /// 待办项类型(0=Normal, 1=Meeting),默认 Normal public TaskType TaskType { get; set; } = TaskType.Normal; ``` **TaskDto** 新增: ```csharp /// 待办项类型 public TaskType TaskType { get; set; } /// 会议纪要/转写文字 public string? MeetingNotes { get; set; } /// 录音时长(秒) public double? AudioDuration { get; set; } ``` ### 4.6 会议 DTO ```csharp /// 转写请求 public class TranscribeRequest { public IFormFile Audio { get; set; } = null!; public string? Format { get; set; } } /// 转写响应 public class TranscribeResponse { public int TaskId { get; set; } public string Transcript { get; set; } = string.Empty; public double AudioDuration { get; set; } } /// 保存会议纪要请求 public class MeetingNotesRequest { public string Notes { get; set; } = string.Empty; } /// 会议纪要响应 public class MeetingNotesResponse { public int TaskId { get; set; } public string MeetingNotes { get; set; } = string.Empty; } ``` ### 4.7 API 端点 #### POST /api/meeting/{taskId}/transcribe - 接收:multipart/form-data(音频文件) - 返回:`TranscribeResponse` - 业务:音频上传后异步转写,结果存回 `TaskEntity.MeetingNotes` - 当前骨架:返回占位文字,具体转写逻辑由 03-02 实现 #### POST /api/meeting/{taskId}/notes - 接收:`MeetingNotesRequest` - 返回:`MeetingNotesResponse` - 业务:保存/更新会议纪要 ### 4.8 MeetingService 骨架 ```csharp /// 会议业务服务 public class MeetingService { private readonly ITaskRepository _taskRepo; public MeetingService(ITaskRepository taskRepo) { _taskRepo = taskRepo; } /// 验证 taskId 对应的待办项存在且为 Meeting 类型 public async Task GetMeetingTaskOrThrow(int taskId) { var task = await _taskRepo.GetAsync(taskId); if (task == null) throw new NotFoundException($"任务 {taskId} 不存在"); if (task.TaskType != TaskType.Meeting) throw new BusinessException($"任务 {taskId} 不是会议类型"); return task; } /// 保存会议纪要 public async Task SaveNotes(int taskId, string notes) { var task = await GetMeetingTaskOrThrow(taskId); task.MeetingNotes = notes; task.UpdatedAt = DateTime.UtcNow; await _taskRepo.UpdateAsync(task); return task; } /// 保存转写结果与音频时长 public async Task SaveTranscript(int taskId, string transcript, double audioDuration) { var task = await GetMeetingTaskOrThrow(taskId); task.MeetingNotes = transcript; task.AudioDuration = audioDuration; task.UpdatedAt = DateTime.UtcNow; await _taskRepo.UpdateAsync(task); return task; } } ``` ## 五、验收标准 | 验收项 | 验证方法 | 预期结果 | |---|---|---| | TaskType 枚举可用 | 编译通过 | `TaskType.Normal` / `TaskType.Meeting` 可正常赋值 | | DB 迁移可执行 | `dotnet ef database update` | `Tasks` 表新增 `TaskType`/`MeetingNotes`/`AudioDuration` 列 | | 创建 Meeting 类型任务 | `POST /api/task` 带 `taskType:1` | 返回的 TaskDto 中 `taskType=1` | | 保存会议纪要 | `POST /api/meeting/{id}/notes` | `meetingNotes` 字段更新成功 | | 非 Meeting 类型调用会议 API | 普通任务调 `/api/meeting/{id}/notes` | 返回业务异常 | | 现有代码兼容 | 运行所有已有测试 | 不破坏现有业务 | ## 六、风险 | 风险 | 影响 | 应对 | |---|---|---| | TaskEntity 已重构为 ABP 基类 | 字段新增方式不同 | 通过 ABP 的 `ExtraProperties` 或标准字段新增,迁移方式略有调整 | | 字段长度限制 | 超长会议纪要截断 | `MeetingNotes` 设 `MaxLength(20000)`,前端也做长度限制 | ## 七、Touch List | 文件路径 | 修改类型 | 是否共享 | |---|---|---| | `src/Hua.Todo.Core/Entities/TaskType.cs` | 新增 | 否 | | `src/Hua.Todo.Core/Entities/TaskEntity.cs` | 修改 | 是 | | `src/Hua.Todo.Application/Data/TodoDbContext.cs` | 修改 | 是 | | `src/Hua.Todo.Application/Models/TaskModels.cs` | 修改 | 是 | | `src/Hua.Todo.Application/Meeting/MeetingController.cs` | 新增 | 否 | | `src/Hua.Todo.Application/Meeting/MeetingService.cs` | 新增 | 否 | | `src/Hua.Todo.Application/Meeting/Models/MeetingDtos.cs` | 新增 | 否 | | `src/Hua.Todo.Web/src/types/task.ts` | 修改 | 是 | | `Migrations/AddMeetingFieldsToTasks.cs` | 新增 | 是 | --- **工单编号**:03-01 **标题**:会议数据模型与 API **版本**:v1.3.0 **创建日期**:2026-06-16