feat: 高级编辑共享跳过审核 + 去重 + 通知 + 已分享状态
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 4s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 15s
Test / e2e-test (push) Failing after 53s
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 4s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 15s
Test / e2e-test (push) Failing after 53s
共享流程: - 高级编辑/管理员共享直接进公共库(跳过审核) - 普通用户共享仍需管理员审核 - 高级编辑共享后通知管理员"已添加"(非待审核) 去重检测: - 同名同内容:提示"已有一模一样的" - 同名不同内容:提示改名后共享 状态显示: - 共享后对比公共库内容,相同则显示"已共享" - 修改内容后"已共享"消失,可重新共享 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -799,9 +799,16 @@ def create_recipe(recipe: RecipeIn, user=Depends(get_current_user)):
|
|||||||
c.execute("INSERT OR IGNORE INTO tags (name) VALUES (?)", (tag,))
|
c.execute("INSERT OR IGNORE INTO tags (name) VALUES (?)", (tag,))
|
||||||
c.execute("INSERT OR IGNORE INTO recipe_tags (recipe_id, tag_name) VALUES (?, ?)", (rid, tag))
|
c.execute("INSERT OR IGNORE INTO recipe_tags (recipe_id, tag_name) VALUES (?, ?)", (rid, tag))
|
||||||
log_audit(conn, user["id"], "create_recipe", "recipe", rid, recipe.name)
|
log_audit(conn, user["id"], "create_recipe", "recipe", rid, recipe.name)
|
||||||
# Notify admin when non-admin/non-senior_editor creates a recipe (needs review)
|
who = user.get("display_name") or user["username"]
|
||||||
if user["role"] not in ("admin", "senior_editor"):
|
if user["role"] == "senior_editor":
|
||||||
who = user.get("display_name") or user["username"]
|
# Senior editor adds directly — just inform admin
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO notifications (target_role, title, body) VALUES (?, ?, ?)",
|
||||||
|
("admin", "📋 新配方已添加",
|
||||||
|
f"{who} 将配方「{recipe.name}」添加到了公共配方库。\n[recipe_id:{rid}]")
|
||||||
|
)
|
||||||
|
elif user["role"] not in ("admin",):
|
||||||
|
# Other users need review
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO notifications (target_role, title, body) VALUES (?, ?, ?)",
|
"INSERT INTO notifications (target_role, title, body) VALUES (?, ?, ?)",
|
||||||
("admin", "📝 新配方待审核",
|
("admin", "📝 新配方待审核",
|
||||||
|
|||||||
@@ -1199,22 +1199,21 @@ function openRecipeDetail(recipe) {
|
|||||||
if (idx >= 0) previewRecipeIndex.value = idx
|
if (idx >= 0) previewRecipeIndex.value = idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function diaryMatchesPublic(d) {
|
||||||
|
const pub = recipeStore.recipes.find(r => r.name === d.name)
|
||||||
|
if (!pub) return false
|
||||||
|
const dIngs = (d.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
const pIngs = (pub.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
return dIngs.length === pIngs.length && dIngs.every((ing, i) => ing.oil === pIngs[i].oil && ing.drops === pIngs[i].drops)
|
||||||
|
}
|
||||||
|
|
||||||
function getDiaryShareStatus(d) {
|
function getDiaryShareStatus(d) {
|
||||||
// Check pending first (exact name match in public library owned by user)
|
// Check pending (owned by user in public library, not yet adopted)
|
||||||
if (sharedCount.value.pendingNames.includes(d.name)) return 'pending'
|
if (sharedCount.value.pendingNames.includes(d.name)) return 'pending'
|
||||||
// Check adopted — but if diary content was modified, allow re-sharing
|
// Check if public library has same recipe with same content
|
||||||
if (sharedCount.value.adoptedNames.includes(d.name)) {
|
if (diaryMatchesPublic(d)) return 'shared'
|
||||||
// Compare with the public recipe to see if content changed
|
// Check adopted names from audit log
|
||||||
const pub = recipeStore.recipes.find(r => r.name === d.name)
|
if (sharedCount.value.adoptedNames.includes(d.name) && diaryMatchesPublic(d)) return 'shared'
|
||||||
if (pub) {
|
|
||||||
const dIngs = (d.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
|
||||||
const pIngs = (pub.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
|
||||||
const same = dIngs.length === pIngs.length && dIngs.every((ing, i) => ing.oil === pIngs[i].oil && ing.drops === pIngs[i].drops)
|
|
||||||
if (same) return 'shared'
|
|
||||||
}
|
|
||||||
// Content changed or public recipe not found — can re-share
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1244,6 +1243,27 @@ async function recommendApprove(recipe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function shareDiaryToPublic(diary) {
|
async function shareDiaryToPublic(diary) {
|
||||||
|
// Check for duplicates in public library
|
||||||
|
const dup = recipeStore.recipes.find(r => r.name === diary.name)
|
||||||
|
if (dup) {
|
||||||
|
const dIngs = (diary.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
const pIngs = (dup.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
const identical = dIngs.length === pIngs.length && dIngs.every((ing, i) => ing.oil === pIngs[i].oil && ing.drops === pIngs[i].drops)
|
||||||
|
if (identical) {
|
||||||
|
ui.showToast('公共配方库中已有一模一样的配方「' + diary.name + '」')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Same name, different content
|
||||||
|
const action = await showConfirm(
|
||||||
|
`公共配方库中已有同名配方「${diary.name}」但内容不同,是否改名后共享?`,
|
||||||
|
{ okText: '改名', cancelText: '取消' }
|
||||||
|
)
|
||||||
|
if (!action) return
|
||||||
|
const newName = await showPrompt('请输入新名称:', diary.name)
|
||||||
|
if (!newName || !newName.trim()) return
|
||||||
|
diary = { ...diary, name: newName.trim() }
|
||||||
|
}
|
||||||
|
|
||||||
const ok = await showConfirm(`将「${diary.name}」共享到公共配方库?`)
|
const ok = await showConfirm(`将「${diary.name}」共享到公共配方库?`)
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
try {
|
try {
|
||||||
@@ -1257,7 +1277,7 @@ async function shareDiaryToPublic(diary) {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
if (auth.isAdmin) {
|
if (auth.isAdmin || auth.canManage) {
|
||||||
ui.showToast('已共享到公共配方库')
|
ui.showToast('已共享到公共配方库')
|
||||||
} else {
|
} else {
|
||||||
ui.showToast('已提交,等待管理员审核')
|
ui.showToast('已提交,等待管理员审核')
|
||||||
|
|||||||
Reference in New Issue
Block a user