Files
mini_tp/docs/note_api.md
T
nepiedg 36c506f4bf feat(note): add audio upload and sharing functionality
- 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.
2026-04-17 10:33:33 +00:00

685 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. 分享读取接口为公开接口,建议前端仅用于只读展示