From 65247b095a57db78fc6698fe48c6d338340d401d Mon Sep 17 00:00:00 2001 From: "3371392206@qq.com" <3371392206@qq.com> Date: Sun, 30 Aug 2026 14:07:03 +0800 Subject: [PATCH] =?UTF-8?q?ux(setup):=20voice.resource=5Fid=20=E7=95=99?= =?UTF-8?q?=E7=A9=BA=E6=97=B6=E8=87=AA=E5=8A=A8=E7=94=A8=20settings.defaul?= =?UTF-8?q?t=5Fresource=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: step 2 有 "默认资源 ID" 字段,但 step 3 的每个 voice 行 也有 "资源 ID" 字段,要求用户填 2 次。用户经常只在 step 2 填, 留空 step 3 的 resource_id,导致 voice 行拿到 form placeholder "volc.megatts.icl" 或空串。结果 settings 和 voice 不一致, 运行时火山 500(mismatch / not granted)。 修法 (后端兜底,无需前端改动): 1. 装时校验放宽: resource_id 留空是允许的(原来是必填) 2. 装时遍历 voices, 如果 voice.resource_id == "" 就用 settings.default_resource_id 兜底, 并记 log 用户收益: step 3 voice 行的 resource_id 字段可以不动, step 2 填一次就行,始终保持一致。 可选前端优化 (待前端做): step 2 的 "默认资源 ID" 改变时, 自动同步所有 voice 行的 resource_id (placeholder 提示已自动填充)。 e2e 验证 (本机): - 提交 install body 中 voice[0].resource_id="" - 启动 log: '[setup] voice "chun" resource_id 留空,自动用 default_resource_id="seed-icl-2.0"' - dumpdb 看 voice chun.resource_id = seed-icl-2.0 (从 settings 兜底) ✓ 二进制: ~18MB → ~18MB (代码量几乎不变) 未 push (待用户确认) --- controller/setup.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/controller/setup.go b/controller/setup.go index 765c53a..f89335e 100644 --- a/controller/setup.go +++ b/controller/setup.go @@ -190,11 +190,21 @@ func SetupSubmitHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[setup] 检测到 %d 条已存在 voices,本次将跳过清空(name 冲突由 ErrDuplicate 处理)", len(existing)) } inserted := 0 + // 【UX 改进】voice 行的 resource_id 留空时,自动用 settings.default_resource_id 兜底。 + // 用户在 step 2 填了 default_resource_id 后,step 3 的 voice 行 resource_id + // 可以不填 — 保持一致。否则会出现 "settings 里 seed-icl-2.0,voice 里 volc.megatts.icl" + // 这种 mismatch,运行时 500。 + defaultResourceID := settingsKV["default_resource_id"] for _, v := range body.Voices { + voiceResourceID := v.ResourceID + if voiceResourceID == "" { + voiceResourceID = defaultResourceID + log.Printf("[setup] voice %q resource_id 留空,自动用 default_resource_id=%q", v.Name, defaultResourceID) + } _, err := s.VoiceInsert(store.Voice{ Name: v.Name, Speaker: v.Speaker, - ResourceID: v.ResourceID, + ResourceID: voiceResourceID, Model: v.Model, Language: v.Language, Enabled: true, @@ -267,9 +277,9 @@ func validateSetupVoices(vs []SetupVoice) error { if strings.TrimSpace(v.Speaker) == "" { return fmt.Errorf("voices[%d] (%s): speaker is required", i, v.Name) } - if strings.TrimSpace(v.ResourceID) == "" { - return fmt.Errorf("voices[%d] (%s): resource_id is required", i, v.Name) - } + // 【UX 改进】resource_id 留空是允许的 — 装时(下面那个循环)会自动用 + // settings.default_resource_id 兜底。用户只填 step 2 一处即可。 + _ = v.ResourceID // (保留以便以后加更细的校验) if _, dup := names[v.Name]; dup { return fmt.Errorf("voices[%d]: duplicate name %q", i, v.Name) }