feat(calls): voice_clip 手动匹配(voice_clip↔customer 本地关联写操作)

- VoiceClip: 新增 matched_friend_id 列 (String(128), nullable) + alembic 迁移 b3c4d5e6f7a8
- PATCH /api/voice-clips/{id}/match: friend_id 关联 / null 取消 (本地写, 不回写云客)
- GET /api/voice-clips: 批量解析 matched_friend_name (remark>nickname>real>id)
- VoiceClipModal: 已匹配客户列 + 匹配/取消匹配按钮 + 嵌套客户选择 picker (默认按员工列客户, 名搜索走 /search)
- 测试: 17 passed (新增 7 个 match 用例覆盖 set/clear/name 解析/404)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-11 10:43:34 +08:00
parent f1b5861b35
commit 9da272b992
9 changed files with 533 additions and 21 deletions
+22 -1
View File
@@ -3,7 +3,7 @@
// Mirrors the FastAPI routes in yunke-server/app/api/routers/. Each wrapper
// drops `undefined`/empty params so the query string only carries set filters
// (the backend treats missing as "no filter").
import { getJson } from './client'
import { getJson, requestJson } from './client'
import type {
Account,
CallChannel,
@@ -228,6 +228,27 @@ export function getVoiceClips(query: VoiceClipQuery = {}): Promise<Page<VoiceCli
)
}
/** PATCH /voice-clips/{id}/match 响应(镜像 backend payload)。 */
export interface VoiceClipMatchResult {
voice_clip_id: number
matched_friend_id: string | null
}
/**
* 手动匹配 voice_clip ↔ customer (LOCAL 写)。`friendId=null` 取消匹配。
* 该写操作只落本地镜像库,不回写云客。
*/
export function matchVoiceClip(
voiceClipId: number,
friendId: string | null,
): Promise<VoiceClipMatchResult> {
return requestJson<VoiceClipMatchResult>(
'patch',
`/voice-clips/${voiceClipId}/match`,
{ friend_id: friendId },
)
}
// ---------------------------------------------------------------------------
// SMS
// ---------------------------------------------------------------------------