36c506f4bf
- Introduced `note_audio` table for storing audio attachments related to notes. - Implemented audio upload endpoint in `Note` controller to handle audio file uploads. - Added sharing functionality with `note_share` table to manage share tokens and view counts. - Updated API routes to include endpoints for audio uploads and share creation. - Enhanced documentation to reflect new audio and sharing features.
685 lines
13 KiB
Markdown
685 lines
13 KiB
Markdown
# Note 模块接口文档
|
||
|
||
## 1. 模块说明
|
||
|
||
`note` 是独立于现有 `app/api` 业务的笔记小程序模块,代码位置如下:
|
||
|
||
- [app/note/controller](/root/work/tp/app/note/controller)
|
||
- [app/note/service](/root/work/tp/app/note/service)
|
||
- [app/note/model](/root/work/tp/app/note/model)
|
||
|
||
当前已实现能力:
|
||
|
||
1. 微信小程序登录
|
||
2. 笔记创建、列表、详情、更新、删除
|
||
3. 实时转写文本保存
|
||
4. 录音文件上传与播放
|
||
5. AI 总结生成与查询
|
||
6. 笔记分享与分享只读访问
|
||
|
||
路由注册位置:
|
||
|
||
- [route/app.php](/root/work/tp/route/app.php)
|
||
|
||
---
|
||
|
||
## 2. 鉴权说明
|
||
|
||
除登录接口外,其余 `note` 接口都需要携带 JWT:
|
||
|
||
- Header: `Authorization: Bearer {token}`
|
||
|
||
`note` 模块复用现有 JWT 机制,但要求 token 载荷中必须包含:
|
||
|
||
```json
|
||
{
|
||
"userid": 123,
|
||
"guard": "note"
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- `userid` 对应 `note_user.id`
|
||
- `guard=note` 用于和旧 `api` 模块登录态隔离
|
||
|
||
---
|
||
|
||
## 3. 环境变量
|
||
|
||
微信登录依赖以下配置:
|
||
|
||
- `WECHAT_MINI_APPID`
|
||
- `WECHAT_MINI_SECRET`
|
||
- `DB_NOTE_HOSTNAME`
|
||
- `DB_NOTE_DATABASE`
|
||
- `DB_NOTE_USERNAME`
|
||
- `DB_NOTE_PASSWORD`
|
||
- `DB_NOTE_HOSTPORT`
|
||
|
||
示例位置:
|
||
|
||
- [\.example.env](/root/work/tp/.example.env)
|
||
|
||
---
|
||
|
||
## 4. 数据表
|
||
|
||
表结构定义位置:
|
||
|
||
- [database.sql](/root/work/tp/database.sql#L26)
|
||
|
||
当前 `note` 模块使用以下 6 张表:
|
||
|
||
1. `note_user`
|
||
用于小程序用户登录和用户资料
|
||
|
||
2. `note_item`
|
||
笔记主表,包含正文、转写累计文本、状态、录音时长等
|
||
|
||
3. `note_transcript`
|
||
实时转写分片记录表
|
||
|
||
4. `note_ai_summary`
|
||
AI 总结结果表
|
||
|
||
5. `note_audio`
|
||
录音附件表,保存上传后的音频文件地址与时长
|
||
|
||
6. `note_share`
|
||
分享记录表,保存分享 token 与查看次数
|
||
|
||
---
|
||
|
||
## 5. 接口列表
|
||
|
||
### 5.1 获取模块概览
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/meta/interfaces`
|
||
- 是否鉴权:否
|
||
|
||
用途:
|
||
|
||
- 返回 note 模块接口规划概览
|
||
|
||
---
|
||
|
||
### 5.2 微信小程序登录
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/auth/wechat-login`
|
||
- 是否鉴权:否
|
||
|
||
请求参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `code` | string | 是 | `wx.login` 获取的临时 code |
|
||
| `nickname` | string | 否 | 用户昵称 |
|
||
| `avatar_url` | string | 否 | 用户头像地址 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"code": "021xxx",
|
||
"nickname": "张三",
|
||
"avatar_url": "https://example.com/avatar.png"
|
||
}
|
||
```
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "登录成功",
|
||
"data": {
|
||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.xxx",
|
||
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.xxx",
|
||
"expires_in": 604800,
|
||
"user": {
|
||
"id": 1,
|
||
"member_id": 0,
|
||
"openid": "oxxx",
|
||
"nickname": "张三",
|
||
"avatar_url": "https://example.com/avatar.png",
|
||
"mobile": "",
|
||
"is_new_user": true
|
||
}
|
||
},
|
||
"time": 1710000000
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
1. 首次登录时自动创建 `note_user`
|
||
2. 后续登录时按 `openid` 更新资料与最后登录时间
|
||
3. 若用户被禁用,返回 403
|
||
|
||
---
|
||
|
||
### 5.3 获取当前用户信息
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/auth/me`
|
||
- 是否鉴权:是
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"id": 1,
|
||
"member_id": 0,
|
||
"openid": "oxxx",
|
||
"nickname": "张三",
|
||
"avatar_url": "https://example.com/avatar.png",
|
||
"mobile": "",
|
||
"status": 1,
|
||
"last_login_time": 1710000000,
|
||
"created_at": 1710000000
|
||
},
|
||
"time": 1710000001
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.4 创建笔记
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/item/create`
|
||
- 是否鉴权:是
|
||
|
||
请求参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `source_type` | string | 是 | `text` / `audio` / `mix` |
|
||
| `title` | string | 否 | 标题 |
|
||
| `content` | string | 否 | 正文 |
|
||
| `audio_duration_ms` | int | 否 | 录音时长 |
|
||
| `status` | string | 否 | 默认 `draft` |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"source_type": "text",
|
||
"title": "会议纪要",
|
||
"content": "今天确认了下周需求排期",
|
||
"status": "draft"
|
||
}
|
||
```
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "创建成功",
|
||
"data": {
|
||
"id": 10,
|
||
"note_user_id": 1,
|
||
"title": "会议纪要",
|
||
"content": "今天确认了下周需求排期",
|
||
"transcript_text": "",
|
||
"source_type": "text",
|
||
"status": "draft",
|
||
"audio_duration_ms": 0,
|
||
"summary_status": "none",
|
||
"last_transcript_time": 0,
|
||
"created_at": 1710000000,
|
||
"updated_at": 1710000000
|
||
},
|
||
"time": 1710000000
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 如果不传 `title`,后端会根据 `content` 自动生成标题
|
||
- 如果 `title` 和 `content` 都为空,标题会默认为 `未命名笔记`
|
||
|
||
---
|
||
|
||
### 5.5 笔记列表
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/item/list`
|
||
- 是否鉴权:是
|
||
|
||
查询参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `page` | int | 否 | 默认 1 |
|
||
| `page_size` | int | 否 | 默认 10,最大 100 |
|
||
| `keyword` | string | 否 | 搜索标题、正文、转写文本 |
|
||
| `status` | string | 否 | 按状态筛选 |
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"list": [
|
||
{
|
||
"id": 10,
|
||
"note_user_id": 1,
|
||
"title": "会议纪要",
|
||
"content": "今天确认了下周需求排期",
|
||
"transcript_text": "",
|
||
"source_type": "text",
|
||
"status": "draft",
|
||
"audio_duration_ms": 0,
|
||
"summary_status": "none",
|
||
"last_transcript_time": 0,
|
||
"created_at": 1710000000,
|
||
"updated_at": 1710000000
|
||
}
|
||
],
|
||
"total": 1,
|
||
"page": 1,
|
||
"page_size": 10
|
||
},
|
||
"time": 1710000001
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.6 笔记详情
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/item/:id`
|
||
- 是否鉴权:是
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"id": 10,
|
||
"note_user_id": 1,
|
||
"title": "会议纪要",
|
||
"content": "今天确认了下周需求排期",
|
||
"transcript_text": "需要跟进接口联调",
|
||
"source_type": "mix",
|
||
"status": "draft",
|
||
"audio_duration_ms": 120000,
|
||
"summary_status": "success",
|
||
"last_transcript_time": 1710000100,
|
||
"created_at": 1710000000,
|
||
"updated_at": 1710000200,
|
||
"summary": {
|
||
"summary_id": 3,
|
||
"summary_type": "brief",
|
||
"summary_text": "今天确认了下周需求排期\n需要跟进接口联调",
|
||
"todo_list": [
|
||
"需要跟进接口联调"
|
||
],
|
||
"keywords": [
|
||
"需求",
|
||
"排期",
|
||
"接口联调"
|
||
],
|
||
"status": "success"
|
||
}
|
||
},
|
||
"time": 1710000201
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.7 更新笔记
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/item/update/:id`
|
||
- 是否鉴权:是
|
||
|
||
请求参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `title` | string | 否 | 标题 |
|
||
| `content` | string | 否 | 正文 |
|
||
| `status` | string | 否 | 状态 |
|
||
| `audio_duration_ms` | int | 否 | 录音时长 |
|
||
|
||
说明:
|
||
|
||
- 只更新传入字段
|
||
- 只能更新自己的笔记
|
||
|
||
---
|
||
|
||
### 5.8 删除笔记
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/item/delete/:id`
|
||
- 是否鉴权:是
|
||
|
||
说明:
|
||
|
||
- 当前为软删除,写入 `deleted_at`
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "删除成功",
|
||
"data": {
|
||
"deleted": true,
|
||
"id": 10
|
||
},
|
||
"time": 1710000300
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.9 保存实时转写内容
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/item/transcript/:id`
|
||
- 是否鉴权:是
|
||
|
||
请求参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `full_text` | string | 是 | 当前累计完整转写文本 |
|
||
| `segment_no` | int | 否 | 分片序号,默认 0 |
|
||
| `segment_text` | string | 否 | 当前分片文本 |
|
||
| `is_final` | int/bool | 否 | 是否最终片段 |
|
||
| `audio_duration_ms` | int | 否 | 当前累计录音时长 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"segment_no": 3,
|
||
"segment_text": "需要跟进接口联调",
|
||
"full_text": "今天确认了下周需求排期,需要跟进接口联调",
|
||
"is_final": 1,
|
||
"audio_duration_ms": 120000
|
||
}
|
||
```
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "转写保存成功",
|
||
"data": {
|
||
"note_id": 10,
|
||
"segment_no": 3,
|
||
"is_final": 1,
|
||
"transcript_text": "今天确认了下周需求排期,需要跟进接口联调",
|
||
"audio_duration_ms": 120000,
|
||
"updated_at": 1710000400
|
||
},
|
||
"time": 1710000400
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 后端会同步更新 `note_item.transcript_text`
|
||
- 同一个 `note_id + segment_no` 会覆盖写入
|
||
|
||
---
|
||
|
||
### 5.10 上传录音文件
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/item/audio/:id`
|
||
- 是否鉴权:是
|
||
- Content-Type:`multipart/form-data`
|
||
|
||
表单参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `audio` | file | 是 | 录音文件 |
|
||
| `audio_duration_ms` | int | 否 | 录音时长 |
|
||
|
||
说明:
|
||
|
||
- 上传成功后会返回可直接播放的 `audio_url`
|
||
- 后端会同步写入 `note_audio`,并更新笔记的录音时长
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "上传成功",
|
||
"data": {
|
||
"audio_id": 2,
|
||
"disk": "public",
|
||
"file_path": "note/audio/20260417/test.m4a",
|
||
"audio_url": "http://127.0.0.1:8000/storage/note/audio/20260417/test.m4a",
|
||
"file_size": 20480,
|
||
"mime_type": "audio/mp4",
|
||
"duration_ms": 18500,
|
||
"updated_at": 1710000400
|
||
},
|
||
"time": 1710000400
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.11 生成 AI 总结
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/ai/summary/:id`
|
||
- 是否鉴权:是
|
||
|
||
请求参数:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `summary_type` | string | 否 | `brief` / `outline` / `todo` |
|
||
| `force_refresh` | int/bool | 否 | 是否强制重新生成 |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"summary_type": "brief",
|
||
"force_refresh": 1
|
||
}
|
||
```
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "总结生成成功",
|
||
"data": {
|
||
"summary_id": 3,
|
||
"note_id": 10,
|
||
"summary_type": "brief",
|
||
"summary_text": "今天确认了下周需求排期\n需要跟进接口联调",
|
||
"todo_list": [
|
||
"需要跟进接口联调"
|
||
],
|
||
"keywords": [
|
||
"需求",
|
||
"排期",
|
||
"接口联调"
|
||
],
|
||
"status": "success",
|
||
"error_message": "",
|
||
"created_at": 1710000500,
|
||
"updated_at": 1710000500
|
||
},
|
||
"time": 1710000500
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 当前实现为规则版总结,不依赖外部大模型
|
||
- 若已存在成功总结且 `force_refresh=0`,会直接返回已有结果
|
||
|
||
---
|
||
|
||
### 5.12 查看 AI 总结
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/ai/summary/:id`
|
||
- 是否鉴权:是
|
||
|
||
说明:
|
||
|
||
- 返回指定笔记最新的一条总结记录
|
||
|
||
---
|
||
|
||
### 5.13 创建分享
|
||
|
||
- 方法:`POST`
|
||
- 路径:`/note/v1/share/create/:id`
|
||
- 是否鉴权:是
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "分享已生成",
|
||
"data": {
|
||
"note_id": 10,
|
||
"share_token": "7a4d2f2d4a5f1b20f6f9670bb1f4d123",
|
||
"share_path": "/pages/note/edit?share_token=7a4d2f2d4a5f1b20f6f9670bb1f4d123",
|
||
"title": "会议纪要"
|
||
},
|
||
"time": 1710000600
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 返回 `share_token` 和小程序页面路径
|
||
- 小程序可用该路径做转发
|
||
|
||
---
|
||
|
||
### 5.14 读取分享内容
|
||
|
||
- 方法:`GET`
|
||
- 路径:`/note/v1/share/read/:token`
|
||
- 是否鉴权:否
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"id": 10,
|
||
"note_user_id": 1,
|
||
"title": "会议纪要",
|
||
"content": "今天确认了下周需求排期",
|
||
"transcript_text": "今天确认了下周需求排期",
|
||
"source_type": "mix",
|
||
"status": "draft",
|
||
"audio_duration_ms": 18500,
|
||
"summary_status": "success",
|
||
"last_transcript_time": 1710000300,
|
||
"created_at": 1710000000,
|
||
"updated_at": 1710000600,
|
||
"audio": {
|
||
"audio_id": 2,
|
||
"disk": "public",
|
||
"file_path": "note/audio/20260417/test.m4a",
|
||
"audio_url": "http://127.0.0.1:8000/storage/note/audio/20260417/test.m4a",
|
||
"file_size": 20480,
|
||
"mime_type": "audio/mp4",
|
||
"duration_ms": 18500,
|
||
"updated_at": 1710000400
|
||
},
|
||
"summary": {
|
||
"summary_text": "今天确认了下周需求排期,需要继续跟进接口联调。",
|
||
"status": "success"
|
||
},
|
||
"share": {
|
||
"share_token": "7a4d2f2d4a5f1b20f6f9670bb1f4d123",
|
||
"title": "会议纪要",
|
||
"view_count": 3
|
||
}
|
||
},
|
||
"time": 1710000601
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 用于收件人直接查看分享的笔记内容、录音和 AI 摘要
|
||
|
||
---
|
||
|
||
## 6. 错误码约定
|
||
|
||
当前模块沿用项目统一返回结构:
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"msg": "错误信息",
|
||
"data": [],
|
||
"time": 1710000000
|
||
}
|
||
```
|
||
|
||
常见情况:
|
||
|
||
- `400`:参数错误
|
||
- `401`:未登录或登录态无效
|
||
- `403`:用户被禁用
|
||
- `404`:资源不存在
|
||
- `500`:服务端错误
|
||
- `502`:调用微信接口失败
|
||
|
||
---
|
||
|
||
## 7. 调用顺序建议
|
||
|
||
小程序端推荐按以下顺序接入:
|
||
|
||
1. 调 `wx.login`
|
||
2. 把 `code` 发给 `POST /note/v1/auth/wechat-login`
|
||
3. 保存返回的 `token`
|
||
4. 创建笔记 `POST /note/v1/item/create`
|
||
5. 录音转写过程中持续调用 `POST /note/v1/item/transcript/:id`
|
||
6. 录音停止后上传录音文件 `POST /note/v1/item/audio/:id`
|
||
7. 需要生成总结时调用 `POST /note/v1/ai/summary/:id`
|
||
8. 分享前调用 `POST /note/v1/share/create/:id`
|
||
9. 打开详情页时调用 `GET /note/v1/item/:id`
|
||
|
||
---
|
||
|
||
## 8. 当前限制
|
||
|
||
1. AI 总结目前是规则版,不是大模型版
|
||
2. 微信登录依赖服务端已正确配置 `WECHAT_MINI_APPID` 和 `WECHAT_MINI_SECRET`
|
||
3. 分享读取接口为公开接口,建议前端仅用于只读展示
|