文件
shanghaipeiwan/server/tests/voice-intro.spec.ts
xcbyy 720cb97877 feat(voice): 大神语音介绍真实落地(录制/上传/试听)
- DB: companions.voice_intro_url;仓储 updateCompanionVoice;详情与 /api/expert/me 返回 voiceIntroUrl/voiceDuration
- 后端: POST /api/companions/:id/voice(音频白名单 mp3/m4a/aac/wav/ogg、5MB、≤60s、限流、仅本人或管理员)
- 小程序: api.uploadVoiceIntro(uni.uploadFile + 用户令牌);工作台新增「语音介绍」卡片(录制/重录/试听,60s mp3);详情页改为 uni.createInnerAudioContext 真实播放,卸载销毁
- 修复 uploadImage 未携带用户令牌(生产 fail-closed 后会导致上传失败)
- 新增 voice-intro.spec.ts(104 套件/701 用例全绿);冒烟 7/7
2026-10-20 18:25:11 +08:00

55 行
2.3 KiB
TypeScript

// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第八十八批:大神语音介绍(录音试听)真实落地。
* 说明:「语音试听」= 大神自我介绍录音;平台不提供实时语音房/连麦。
*/
describe('第八十八批语音介绍闸门', () => {
const root = path.resolve(__dirname, '../../');
const read = (p: string) => fs.readFileSync(path.join(root, p), 'utf-8');
const indexCode = read('server/src/index.ts');
const repoCode = read('server/src/db/repository.ts');
const mysqlCode = read('server/src/db/mysql.ts');
const api = read('pwuniapp/common/api.js');
const detail = read('pwuniapp/pages/expert/detail.vue');
const workspace = read('pwuniapp/pages/expert/workspace.vue');
it('数据库与仓储支持语音介绍地址/时长', () => {
expect(mysqlCode).toContain('voice_intro_url');
expect(repoCode).toContain('voiceIntroUrl');
expect(repoCode).toContain('async updateCompanionVoice(');
});
it('提供语音上传接口(音频白名单 + 限流 + 归属校验)', () => {
expect(indexCode).toContain("app.post(\n '/api/companions/:id/voice'");
expect(indexCode).toContain("name: 'voice_upload'");
expect(indexCode).toContain("'audio/mpeg'");
expect(indexCode).toContain("assertCompanionActor(req, own, '上传语音介绍')");
expect(indexCode).toContain('fileSize: 5 * 1024 * 1024');
});
it('详情页真实播放(不再用 setTimeout 假动画)', () => {
expect(detail).toContain('voiceIntroUrl');
expect(detail).toContain('uni.createInnerAudioContext()');
expect(detail).not.toContain('4s 真实原声');
// 播放器需在卸载时销毁,避免泄漏
expect(detail).toContain('voiceAudio.destroy()');
});
it('工作台支持录音上传与试听', () => {
expect(workspace).toContain('uni.getRecorderManager()');
expect(workspace).toContain('uploadVoiceIntro(');
expect(workspace).toContain('format: \'mp3\'');
expect(workspace).toContain('duration: 60000');
expect(workspace).toContain('uni.createInnerAudioContext()');
});
it('上传类接口携带用户令牌(含图片上传修复)', () => {
expect(api).toContain('function authHeader()');
expect(api).toContain('export function uploadVoiceIntro(');
expect(api).toContain('header: authHeader()');
});
});