LearningPageComponent.play 优先播放原有音频。空缺音频或实际播放失败才调用认证接口 app_word_audio_ensure,仅传 wordId 和可选 playbackFailed。接口返回 {state:'ready',url,source} 或 {state:'pending',retryAfterMs},客户端合并同词请求并有界等待。原有只读课程预览的点读限制保持不变,历史词表的缺音频按钮可以点击生成。切换词条、阶段或离开页面后不播放旧结果。自动播放被浏览器阻止只提示再点喇叭,不生成录音。
服务端读取本帐套已发布的 Model 52 词条(既有词库访问模型),仅允许登录的学生、老师和管理员触发。不开启任意文本合成。原有音频未报错时不读取 TTS 配置、不调用供应商;报错时仅探测受信任媒体域名,禁跟随重定向,仅将 404/410 认定为失效,403/500、浏览器限制、网络超时均不重做。第三方域名失效需要管理员手动清空原地址后生成,防止 SSRF。
缓存键由帐套、NFC/空白规范化后的文本、音标、固定语音配置计算 SHA256;保留大小写与音标区别。该键是数据库主键。不同课本中相同词与音标共用,不合并不同读音。讯飞仅接收词条文本;同形异音词仍需试听验收,有误可用现有后台音频上传覆盖。
短事务认领任务、90秒租约、令牌条件写入、每租户并发限制,供应商请求在事务外执行。先保存合成字节,再上传音频、回填 VocabularyWord.yp。中间字节保留在仅服务端可读的缓存内,用于上传失败或已生成文件丢失后的恢复。回填比较旧音频、原文、音标,避免覆盖人工修改。失败冷却一分钟;成功持久化后反复复用,不重复调用讯飞。调用成功但字节尚未持久化就崩溃仍可能产生一次额外调用,不能声称供应商计费严格 exactly-once。
本机私密配置:~/.config/xiaoshu/xfyun-word-audio.json,权限 600,绝不提交。
必要字段:XFYUN_APP_ID、XFYUN_API_KEY、XFYUN_API_SECRET、XFYUN_TTS_VCN。当前验收声音为 x4_EnUs_Laura_education(Laura 教育、美式英语)。固定语速50、MP3/16k、UTF8、单词模式 reg=0,不加背景音。
控制字段:XIAOSHU_TTS_ENABLED 默认 false,XIAOSHU_TTS_DAILY_LIMIT 默认500,XIAOSHU_TTS_USER_MINUTE_LIMIT 默认30,XIAOSHU_TTS_CONCURRENCY 默认1。按北京时间每天统计实际合成尝试;失败尝试也计入限额以避免费用失控。上传重试不计入合成额度。WordAudioAttempt 保存调用、失败、恢复事件。
2026-10-29 实际服务器探测:云函数提供 fetch 和 WebCrypto,但不提供 WebSocket/require;没有配置 XIAOSHUS3*;词库表没有 isDeleted 字段(按 status 过滤)。因此实际部署需要 Node 22+ 合成适配器 scripts/word-audio-worker.mjs。云函数持有 XIAOSHU_TTS_WORKER_URL(HTTPS /synthesize)和随机32字符以上 XIAOSHU_TTS_WORKER_TOKEN,通过认证 HTTP 调用适配器,适配器负责讯飞 WebSocket。独立部署适配器时讯飞密钥只需留在适配器;云函数不需要它们。
适配器默认仅监听 127.0.0.1:4381,公网部署在 HTTPS 反向代理之后,不开放 CORS。支持 /healthz。POST 仅接受共享凭证、固定发音人/语速、最长200字符,限制并发和每分钟请求量,供应商错误只返回固定提示。不要记录 Authorization 或签名 WebSocket URL。
存储先使用现有 XIAOSHUS3* 配置(固定对象键),未配置则复用已验证支持 MP3 的现有媒体上传服务。后者可能在上传响应丢失后产生重复文件,但仍会复用数据库中保存的音频字节,不重新调用讯飞。原有通用上传的文件白名单不放宽。
node scripts/word-audio-worker.mjs 或从仓库根目录 docker build -f deployment/word-audio/Dockerfile -t xiaoshu-word-audio .;Docker 配置只读挂载到 /run/secrets/word-audio.json,文件属主须为容器 node 用户 UID1000。端口仅绑定宿主机回环地址,通过现有 HTTPS 代理转发 /synthesize。镜像中不包含配置文件。node scripts/configure-word-audio.mjs 默认只检查;--apply 使用操作者 XIAOSHU_MASTER_KEY 写入 Parse masterKeyOnly 私密配置;--gateway-only 仅传适配器参数与非讯飞密钥配置。--enable 必须与 --apply 一起才启用,禁用用不带 --enable 的 --apply。node scripts/migrate-word-audio.mjs 默认不写库;--apply 创建缓存、用量表与认领函数,Parse CLP 禁止客户端直接读写。迁移临时函数仅允许当前管理员、10分钟有效,finally 清理。应用变更前先完成迁移。本任务未自动部署适配器、迁移线上数据库或开启线上生成。授权上线时依照此顺序执行。
node --test scripts/tests/word-audio.test.mjs scripts/tests/word-audio-worker.test.mjsnpm run test:mobile:cinpm run build:mobile && npm run cloud:validatenode scripts/check-word-audio-browser.mjs(375px,隔离接口与音频样本)node scripts/check-word-audio-live.mjs 默认不调用供应商;--generate 使用一次 apple 合成,--extended 再生成 comfortable/read/NASA/Good morning,已有样音复用。输出只含声音名称、文件路径、字节数和哈希。真实样音与文件存储回读证据位于忽略目录 tmp/word-audio-qa/。MP3 结构验证不等同真人对所有词汇发音准确性的验收,也不等同手机微信实机验收。