9223ceca50
- 规则重组:全局/ 下 8 个规则合并为 6 个(01+02→01,05+06→04),序号顺延 - 新增项目规则 05-多入口功能同步规范(UI/语音入口覆盖检查) - 新增 MCP 服务基础设施:Mcp/ 目录(DI 注册、端点扩展、动态工具描述符)、单元测试 - v1.3.0 工单文档:03 系列(会议任务拆分)、04(富文本描述与附件管理) - MCP 接口与前端集成指南:docs/manual/08、09
242 lines
7.4 KiB
Markdown
242 lines
7.4 KiB
Markdown
# 研发工单 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
|
||
/// <summary>待办项类型,区分普通待办项与会议</summary>
|
||
public enum TaskType
|
||
{
|
||
/// <summary>普通待办项(默认)</summary>
|
||
Normal = 0,
|
||
/// <summary>会议:包含录音/纪要,支持 AI 任务拆分</summary>
|
||
Meeting = 1
|
||
}
|
||
```
|
||
|
||
文件位置:`src/Hua.Todo.Core/Entities/TaskType.cs`
|
||
|
||
### 4.2 TaskEntity 新增字段
|
||
|
||
```csharp
|
||
/// <summary>待办项类型</summary>
|
||
public TaskType TaskType { get; set; } = TaskType.Normal;
|
||
|
||
/// <summary>会议纪要/转写文字(仅 Meeting 类型有值)</summary>
|
||
[MaxLength(20000)]
|
||
public string? MeetingNotes { get; set; }
|
||
|
||
/// <summary>录音时长(秒),仅 Meeting 类型有值</summary>
|
||
public double? AudioDuration { get; set; }
|
||
```
|
||
|
||
### 4.3 EF Core 配置(TodoDbContext.cs)
|
||
|
||
```csharp
|
||
builder.Entity<TaskEntity>(b =>
|
||
{
|
||
// ... 现有配置 ...
|
||
|
||
b.Property(x => x.TaskType)
|
||
.HasDefaultValue(TaskType.Normal)
|
||
.HasConversion<int>(); // 枚举存为整数
|
||
|
||
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
|
||
/// <summary>待办项类型(0=Normal, 1=Meeting),默认 Normal</summary>
|
||
public TaskType TaskType { get; set; } = TaskType.Normal;
|
||
```
|
||
|
||
**TaskDto** 新增:
|
||
```csharp
|
||
/// <summary>待办项类型</summary>
|
||
public TaskType TaskType { get; set; }
|
||
/// <summary>会议纪要/转写文字</summary>
|
||
public string? MeetingNotes { get; set; }
|
||
/// <summary>录音时长(秒)</summary>
|
||
public double? AudioDuration { get; set; }
|
||
```
|
||
|
||
### 4.6 会议 DTO
|
||
|
||
```csharp
|
||
/// <summary>转写请求</summary>
|
||
public class TranscribeRequest
|
||
{
|
||
public IFormFile Audio { get; set; } = null!;
|
||
public string? Format { get; set; }
|
||
}
|
||
|
||
/// <summary>转写响应</summary>
|
||
public class TranscribeResponse
|
||
{
|
||
public int TaskId { get; set; }
|
||
public string Transcript { get; set; } = string.Empty;
|
||
public double AudioDuration { get; set; }
|
||
}
|
||
|
||
/// <summary>保存会议纪要请求</summary>
|
||
public class MeetingNotesRequest
|
||
{
|
||
public string Notes { get; set; } = string.Empty;
|
||
}
|
||
|
||
/// <summary>会议纪要响应</summary>
|
||
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
|
||
/// <summary>会议业务服务</summary>
|
||
public class MeetingService
|
||
{
|
||
private readonly ITaskRepository _taskRepo;
|
||
|
||
public MeetingService(ITaskRepository taskRepo)
|
||
{
|
||
_taskRepo = taskRepo;
|
||
}
|
||
|
||
/// <summary>验证 taskId 对应的待办项存在且为 Meeting 类型</summary>
|
||
public async Task<TaskEntity> 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;
|
||
}
|
||
|
||
/// <summary>保存会议纪要</summary>
|
||
public async Task<TaskEntity> SaveNotes(int taskId, string notes)
|
||
{
|
||
var task = await GetMeetingTaskOrThrow(taskId);
|
||
task.MeetingNotes = notes;
|
||
task.UpdatedAt = DateTime.UtcNow;
|
||
await _taskRepo.UpdateAsync(task);
|
||
return task;
|
||
}
|
||
|
||
/// <summary>保存转写结果与音频时长</summary>
|
||
public async Task<TaskEntity> 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
|