From b6f8df89eda18b2766e703a05ab9b408827687ca Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Fri, 10 Apr 2026 19:13:10 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=B4=A1=E7=8C=AE=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=8E=BB=E9=87=8D=20+=20=E5=B7=B2=E5=85=B1=E4=BA=AB=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E5=8F=98=E6=9B=B4=E5=8F=AF=E9=87=8D=E6=96=B0=E5=85=B1?= =?UTF-8?q?=E4=BA=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 贡献统计: - 按配方名去重(拒绝后重新申请不重复计数) - 已采纳+待审核+被拒绝的唯一配方名总数 已共享状态: - 已共享配方修改内容后,对比公共库版本 - 内容不同时"已共享"消失,可重新共享 - 内容相同时保持"已共享" Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/main.py | 20 +++++++++++--------- frontend/src/views/RecipeManager.vue | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/backend/main.py b/backend/main.py index 9708b72..78c5666 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1522,27 +1522,29 @@ def my_contribution(user=Depends(get_current_user)): return {"adopted_count": 0, "shared_count": 0, "adopted_names": [], "pending_names": []} conn = get_db() display = user.get("display_name") or user.get("username") - # adopted: recipes adopted from this user + # adopted: unique recipe names adopted from this user adopted_rows = conn.execute( - "SELECT target_name FROM audit_log WHERE action = 'adopt_recipe' AND detail LIKE ?", + "SELECT DISTINCT target_name FROM audit_log WHERE action = 'adopt_recipe' AND detail LIKE ?", (f'%"from_user": "{display}"%',) ).fetchall() - adopted_names = [r["target_name"] for r in adopted_rows if r["target_name"]] + adopted_names = list(set(r["target_name"] for r in adopted_rows if r["target_name"])) # pending: recipes still owned by user in public library pending_rows = conn.execute( "SELECT name FROM recipes WHERE owner_id = ?", (user["id"],) ).fetchall() pending_names = [r["name"] for r in pending_rows] - # rejected: count recipes rejected from this user (they were deleted but logged) - rejected_count = conn.execute( - "SELECT COUNT(*) FROM audit_log WHERE action = 'reject_recipe' AND detail LIKE ?", + # rejected: unique recipe names rejected (not already adopted or pending) + rejected_rows = conn.execute( + "SELECT DISTINCT target_name FROM audit_log WHERE action = 'reject_recipe' AND detail LIKE ?", (f'%"from_user": "{display}"%',) - ).fetchone()[0] + ).fetchall() + rejected_names = set(r["target_name"] for r in rejected_rows if r["target_name"]) + # Unique names across all: same recipe rejected then re-submitted counts as 1 + all_names = set(adopted_names) | set(pending_names) | rejected_names conn.close() - total = len(adopted_names) + len(pending_names) + rejected_count return { "adopted_count": len(adopted_names), - "shared_count": total, + "shared_count": len(all_names), "adopted_names": adopted_names, "pending_names": pending_names, } diff --git a/frontend/src/views/RecipeManager.vue b/frontend/src/views/RecipeManager.vue index 3410f93..54ebaaf 100644 --- a/frontend/src/views/RecipeManager.vue +++ b/frontend/src/views/RecipeManager.vue @@ -1181,8 +1181,21 @@ function openRecipeDetail(recipe) { } function getDiaryShareStatus(d) { - if (sharedCount.value.adoptedNames.includes(d.name)) return 'shared' + // Check pending first (exact name match in public library owned by user) if (sharedCount.value.pendingNames.includes(d.name)) return 'pending' + // Check adopted — but if diary content was modified, allow re-sharing + if (sharedCount.value.adoptedNames.includes(d.name)) { + // Compare with the public recipe to see if content changed + const pub = recipeStore.recipes.find(r => r.name === d.name) + 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 }