fix(cover): 修复AI数字人页面系统模板编辑返回403的问题
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m23s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m29s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m41s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m45s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m14s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 4m32s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m3s
AI Code Review / AI Code Review (pull_request) Successful in 6m44s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m19s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m34s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 38s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 5m25s

根因:
- 后端 PUT /cover-templates/{id} 对 is_system=true 的模板返回 403「系统模板不可修改」是正确的权限控制
- 前端 CoverSettingsModal 对系统模板(默认模板/大胆红/优雅黑等)也渲染「编辑」按钮
- useSharedCover/useStep6Cover 的 handleSaveTemplate 只要 tpl.id 存在就走 update 分支,对系统模板调用 PUT → 403
- 用户在 AI 数字人页面(无 project/task 上下文)点击系统模板编辑/保存时复现 403

修复:
1. CoverSettingsModal: 系统模板按钮文字从「编辑」改为「复制」,语义明确为另存为
2. useSharedCover.handleEditTemplate: 系统模板点击时创建新模板草稿(清空id, 名称加「副本」, is_system=false)
3. useSharedCover.handleSaveTemplate: 增加 isSystem 判断,系统模板或无id都走 create 分支
4. useStep6Cover 同步上述修复,保持 generate 页面行为一致
5. 保存成功后清空 editingTemplate,避免后续打开残留状态
This commit is contained in:
CI Bot
2026-09-25 21:57:53 +08:00
parent d6e63349cb
commit c47eb46d77
3 changed files with 49 additions and 15 deletions
@@ -104,7 +104,18 @@ export function useSharedCover(opts: UseSharedCoverOptions): UseSharedCoverRetur
}, [])
const handleEditTemplate = useCallback((tpl: CoverTemplate) => {
setEditingTemplate(tpl)
// 系统模板不可修改:复制为新模板草稿,走另存为流程
if (tpl.is_system) {
setEditingTemplate({
...tpl,
id: "",
name: tpl.name + " 副本",
is_system: false,
created_at: "",
})
} else {
setEditingTemplate(tpl)
}
setShowCoverEditor(true)
}, [])
@@ -116,15 +127,22 @@ export function useSharedCover(opts: UseSharedCoverOptions): UseSharedCoverRetur
const handleSaveTemplate = useCallback(
async (tpl: CoverTemplate) => {
try {
if (tpl.id && templates.some((t) => t.id === tpl.id)) {
const updated = await updateCoverTemplate(tpl.id, { name: tpl.name, config: tpl.config })
setTemplates((prev) => prev.map((t) => (t.id === tpl.id ? { ...t, ...updated } : t)))
} else {
const created = await createCoverTemplate({ name: tpl.name, config: tpl.config })
// 系统模板或无 id(新建/副本)→ 走创建分支;否则走更新
const isSystem = templates.find((t) => t.id === tpl.id)?.is_system === true
const shouldCreate = !tpl.id || isSystem
if (shouldCreate) {
const created = await createCoverTemplate({
name: tpl.name || "我的封面模板",
config: tpl.config,
})
setTemplates((prev) => [...prev, created])
setSelectedTemplateId(created.id || tpl.id)
} else {
const updated = await updateCoverTemplate(tpl.id, { name: tpl.name, config: tpl.config })
setTemplates((prev) => prev.map((t) => (t.id === tpl.id ? { ...t, ...updated } : t)))
}
setShowCoverEditor(false)
setEditingTemplate(null)
} catch (err) {
console.error("[SharedCover] 保存模板失败:", err)
message.error("保存模板失败,请稍后重试")
@@ -111,7 +111,7 @@ const CoverSettingsModal: React.FC<CoverSettingsModalProps> = ({
</div>
<div className="xx-cover-template-actions" onClick={(e) => e.stopPropagation()}>
<Button buttonType="ghost" buttonSize="sm" onClick={() => onEditTemplate(tpl)}>
编辑
{tpl.is_system ? "复制" : "编辑"}
</Button>
{!tpl.is_system && (
<Button
@@ -175,7 +175,18 @@ export function useStep6Cover({
}, [])
const handleEditTemplate = useCallback((tpl: CoverTemplate) => {
setEditingTemplate(tpl)
// 系统模板不可修改:复制为新模板草稿,走"另存为"流程
if (tpl.is_system) {
setEditingTemplate({
...tpl,
id: "",
name: tpl.name + " 副本",
is_system: false,
created_at: "",
})
} else {
setEditingTemplate(tpl)
}
setShowCoverEditor(true)
}, [])
@@ -183,20 +194,25 @@ export function useStep6Cover({
const handleSaveTemplate = useCallback(
async (tpl: CoverTemplate) => {
try {
if (tpl.id && coverTemplates.some((t) => t.id === tpl.id)) {
// 系统模板或无 id(新建/副本)→ 走创建分支;否则走更新
const isSystem = coverTemplates.find((t) => t.id === tpl.id)?.is_system === true
const shouldCreate = !tpl.id || isSystem
if (shouldCreate) {
const created = await createCoverTemplate({
name: tpl.name || "我的封面模板",
config: tpl.config,
})
setCoverTemplates((prev) => [...prev, created])
setSelectedTemplateId(created.id || tpl.id)
} else {
const updated = await updateCoverTemplate(tpl.id, {
name: tpl.name,
config: tpl.config,
})
setCoverTemplates((prev) => prev.map((t) => (t.id === tpl.id ? { ...t, ...updated } : t)))
} else {
const created = await createCoverTemplate({
name: tpl.name,
config: tpl.config,
})
setCoverTemplates((prev) => [...prev, created])
}
setShowCoverEditor(false)
setEditingTemplate(null)
} catch (err) {
console.error("[Step6] 保存模板失败:", err)
}