feat: 修复清除数据、语音缓存、排行榜限制,新增项目配置文件
- 修复 cloud.clearAll 静默失败导致重启后数据恢复 - 清除数据弹窗改用自定义 ui-modal 替代 wx.showModal - TTS 语音合成增加 fileID 持久化缓存,重启不再重新合成 - 排行榜限制前15名,maxRank 参数化到 config.js - 新增 config.js 统一管理版本号/更新日期/开发者/排行榜限制 - progress-ring 消除 getSystemInfoSync 弃用警告 - voice.js 增加 InnerAudioContext 错误监听 - storage/util 完善用户资料、打卡日期精度、记录日志
This commit is contained in:
+23
-15
@@ -1,18 +1,11 @@
|
||||
// Cloud function: synthesize the 4 fixed training-voice prompts.
|
||||
//
|
||||
// We do NOT cache on the server. Two reasons:
|
||||
// 1. Caching via cloud storage requires self-constructing a fileID of
|
||||
// the form `cloud://<full-env-id>/...`, but `getWXContext().ENV`
|
||||
// only returns the simple env id. Mismatched → unopenable fileID.
|
||||
// 2. Caching via the cloud database requires `cloud.database()` at
|
||||
// top level, which on this WeChat cloud runtime crashes the SCF
|
||||
// framework's `writeRuntimeFile` step (it can't toString() the
|
||||
// error object the SDK throws).
|
||||
//
|
||||
// The client (`utils/voice.js`) caches audioUrls in memory per session,
|
||||
// so each of the 4 prompts is synthesized at most once per app launch.
|
||||
// Re-synthesizing on app restart is fine — 800万 chars/month free tier
|
||||
// covers 4 × ~30 chars × thousands of restarts per month.
|
||||
// Caching strategy: the client stores the permanent `fileID` returned by
|
||||
// the first successful synthesis in local storage. On subsequent calls it
|
||||
// passes `fileID` back to us; we call `getTempFileURL` with it to get a
|
||||
// fresh temporary URL instantly (no synthesis, no upload). If the cached
|
||||
// fileID is stale (file deleted from cloud storage), we fall through and
|
||||
// re-synthesize, returning the new fileID so the client can update its cache.
|
||||
//
|
||||
// Configuration (云开发控制台 → 云函数 → tts → 函数配置 → 环境变量):
|
||||
// TTS_SECRET_ID — Tencent Cloud API key id
|
||||
@@ -76,12 +69,27 @@ const _upload = async (key, buffer) => {
|
||||
}
|
||||
|
||||
exports.main = async (event) => {
|
||||
const { promptKey } = event || {}
|
||||
const { promptKey, fileID } = event || {}
|
||||
const text = PROMPTS[promptKey]
|
||||
if (!text) {
|
||||
return { success: false, error: `Unknown prompt key: ${promptKey}` }
|
||||
}
|
||||
|
||||
// If the client passed a cached fileID, try to get a fresh temp URL
|
||||
// from it — this is the fast path (no synthesis, no upload).
|
||||
if (fileID) {
|
||||
try {
|
||||
const urlRes = await cloud.getTempFileURL({ fileList: [fileID] })
|
||||
const url = urlRes.fileList[0].tempFileURL
|
||||
if (url) {
|
||||
return { success: true, audioUrl: url, fileID, text, cached: true }
|
||||
}
|
||||
} catch (e) {
|
||||
// fileID stale or file deleted — fall through to re-synthesize
|
||||
}
|
||||
}
|
||||
|
||||
// Slow path: synthesize + upload + return fresh fileID for caching
|
||||
try {
|
||||
const buffer = await _synthesize(text, promptKey)
|
||||
const newFileID = await _upload(promptKey, buffer)
|
||||
@@ -90,7 +98,7 @@ exports.main = async (event) => {
|
||||
if (!url) {
|
||||
return { success: false, error: 'getTempFileURL returned empty URL', text }
|
||||
}
|
||||
return { success: true, audioUrl: url, text }
|
||||
return { success: true, audioUrl: url, fileID: newFileID, text, cached: false }
|
||||
} catch (e) {
|
||||
return { success: false, error: e.message, text }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user