feat: 共享配方审核完整流程
- 新增 /api/recipes/{id}/reject 端点:拒绝配方并通知提交者(含原因)
- 采纳配方时通知提交者"配方已采纳"
- 管理员拒绝配方时输入原因
- 贡献统计改为统计被采纳的配方数(含 audit_log 记录)
- 完整流程测试:共享→通知→拒绝(带原因)→通知→重新共享→采纳→通知
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -863,11 +863,48 @@ def adopt_recipe(recipe_id: int, user=Depends(require_role("admin"))):
|
||||
if row["owner_id"] == user["id"]:
|
||||
conn.close()
|
||||
return {"ok": True, "msg": "already owned"}
|
||||
old_owner = conn.execute("SELECT display_name, username FROM users WHERE id = ?", (row["owner_id"],)).fetchone()
|
||||
old_owner = conn.execute("SELECT id, role, display_name, username FROM users WHERE id = ?", (row["owner_id"],)).fetchone()
|
||||
old_name = (old_owner["display_name"] or old_owner["username"]) if old_owner else "unknown"
|
||||
conn.execute("UPDATE recipes SET owner_id = ?, updated_by = ? WHERE id = ?", (user["id"], user["id"], recipe_id))
|
||||
log_audit(conn, user["id"], "adopt_recipe", "recipe", recipe_id, row["name"],
|
||||
json.dumps({"from_user": old_name}))
|
||||
# Notify submitter that recipe was approved
|
||||
if old_owner and old_owner["id"] != user["id"]:
|
||||
conn.execute(
|
||||
"INSERT INTO notifications (target_role, title, body, target_user_id) VALUES (?, ?, ?, ?)",
|
||||
(old_owner["role"], "🎉 配方已采纳",
|
||||
f"你共享的配方「{row['name']}」已被采纳到公共配方库!", old_owner["id"])
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.post("/api/recipes/{recipe_id}/reject")
|
||||
def reject_recipe(recipe_id: int, body: dict = None, user=Depends(require_role("admin"))):
|
||||
conn = get_db()
|
||||
row = conn.execute("SELECT id, name, owner_id FROM recipes WHERE id = ?", (recipe_id,)).fetchone()
|
||||
if not row:
|
||||
conn.close()
|
||||
raise HTTPException(404, "Recipe not found")
|
||||
reason = (body or {}).get("reason", "").strip()
|
||||
# Notify submitter
|
||||
old_owner = conn.execute("SELECT id, role, display_name, username FROM users WHERE id = ?", (row["owner_id"],)).fetchone()
|
||||
if old_owner and old_owner["id"] != user["id"]:
|
||||
msg = f"你共享的配方「{row['name']}」未被采纳。"
|
||||
if reason:
|
||||
msg += f"\n原因:{reason}"
|
||||
msg += "\n你可以修改后重新共享。"
|
||||
conn.execute(
|
||||
"INSERT INTO notifications (target_role, title, body, target_user_id) VALUES (?, ?, ?, ?)",
|
||||
(old_owner["role"], "配方未被采纳", msg, old_owner["id"])
|
||||
)
|
||||
# Delete the recipe
|
||||
conn.execute("DELETE FROM recipe_ingredients WHERE recipe_id = ?", (recipe_id,))
|
||||
conn.execute("DELETE FROM recipe_tags WHERE recipe_id = ?", (recipe_id,))
|
||||
conn.execute("DELETE FROM recipes WHERE id = ?", (recipe_id,))
|
||||
log_audit(conn, user["id"], "reject_recipe", "recipe", recipe_id, row["name"],
|
||||
json.dumps({"reason": reason}))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"ok": True}
|
||||
@@ -1407,11 +1444,17 @@ def my_contribution(user=Depends(get_current_user)):
|
||||
if not user.get("id"):
|
||||
return {"shared_count": 0}
|
||||
conn = get_db()
|
||||
# Count recipes adopted from this user (tracked in audit_log)
|
||||
count = conn.execute(
|
||||
"SELECT COUNT(*) FROM audit_log WHERE action = 'adopt_recipe' AND detail LIKE ?",
|
||||
(f'%"from_user": "{user.get("display_name") or user.get("username")}"%',)
|
||||
).fetchone()[0]
|
||||
# Also count recipes still owned by user in public library
|
||||
own_count = conn.execute(
|
||||
"SELECT COUNT(*) FROM recipes WHERE owner_id = ?", (user["id"],)
|
||||
).fetchone()[0]
|
||||
conn.close()
|
||||
return {"shared_count": count}
|
||||
return {"shared_count": count + own_count}
|
||||
|
||||
|
||||
# ── Notifications ──────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user