From 9e1ebb3c867063380bc3eea4696dcbd0bea31dda Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 11:51:52 +0000 Subject: [PATCH] =?UTF-8?q?revert:=20=E5=88=A0=E9=99=A4=E8=B4=AD=E6=B2=B9?= =?UTF-8?q?=E6=96=B9=E6=A1=88=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?MyDiary=E6=A8=A1=E6=9D=BF=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完全移除oil_plans相关代码: - 后端: 7个API端点、2个数据库表 - 前端: plans store、Inventory方案UI、UserManagement方案编辑器 - UserMenu: 方案通知按钮 - 修复MyDiary.vue多余的section-card div导致的构建失败 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/database.py | 18 -- backend/main.py | 194 +------------------- frontend/src/components/UserMenu.vue | 14 -- frontend/src/stores/plans.js | 65 ------- frontend/src/views/Inventory.vue | 199 +------------------- frontend/src/views/MyDiary.vue | 1 - frontend/src/views/UserManagement.vue | 253 -------------------------- 7 files changed, 4 insertions(+), 740 deletions(-) delete mode 100644 frontend/src/stores/plans.js diff --git a/backend/database.py b/backend/database.py index 88c2045..137ec68 100644 --- a/backend/database.py +++ b/backend/database.py @@ -247,24 +247,6 @@ def init_db(): if "en_name" not in cols: c.execute("ALTER TABLE recipes ADD COLUMN en_name TEXT DEFAULT ''") - # Oil plans - c.execute("""CREATE TABLE IF NOT EXISTS oil_plans ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, - teacher_id INTEGER, - title TEXT DEFAULT '', - health_desc TEXT DEFAULT '', - status TEXT NOT NULL DEFAULT 'pending', - created_at TEXT DEFAULT (datetime('now')) - )""") - c.execute("""CREATE TABLE IF NOT EXISTS oil_plan_recipes ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - plan_id INTEGER NOT NULL REFERENCES oil_plans(id) ON DELETE CASCADE, - recipe_name TEXT NOT NULL, - ingredients TEXT NOT NULL DEFAULT '[]', - times_per_month INTEGER NOT NULL DEFAULT 1 - )""") - # Seed admin user if no users exist count = c.execute("SELECT COUNT(*) FROM users").fetchone()[0] if count == 0: diff --git a/backend/main.py b/backend/main.py index 9149e74..55a5729 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1611,200 +1611,8 @@ def recipes_by_inventory(user=Depends(get_current_user)): return result -# ── Oil Plans (purchase plans) ───────────────────────── -@app.get("/api/teachers") -def list_teachers(user=Depends(get_current_user)): - conn = get_db() - rows = conn.execute( - "SELECT id, display_name, username FROM users WHERE role IN ('senior_editor', 'admin') ORDER BY id" - ).fetchall() - conn.close() - return [{"id": r["id"], "display_name": r["display_name"] or r["username"], "username": r["username"]} for r in rows] - - -@app.get("/api/oil-plans") -def list_oil_plans(user=Depends(get_current_user)): - if not user.get("id"): - return [] - conn = get_db() - if user["role"] in ("senior_editor", "admin"): - plans = conn.execute( - "SELECT p.*, u.display_name as user_name, u.username " - "FROM oil_plans p LEFT JOIN users u ON p.user_id = u.id " - "WHERE p.teacher_id = ? OR p.user_id = ? ORDER BY p.created_at DESC", - (user["id"], user["id"]) - ).fetchall() - else: - plans = conn.execute( - "SELECT p.*, u.display_name as teacher_name " - "FROM oil_plans p LEFT JOIN users u ON p.teacher_id = u.id " - "WHERE p.user_id = ? ORDER BY p.created_at DESC", (user["id"],) - ).fetchall() - result = [] - for p in plans: - d = dict(p) - recipes = conn.execute( - "SELECT id, recipe_name, ingredients, times_per_month FROM oil_plan_recipes WHERE plan_id = ?", - (p["id"],) - ).fetchall() - d["recipes"] = [{"id": r["id"], "recipe_name": r["recipe_name"], - "ingredients": json.loads(r["ingredients"]), "times_per_month": r["times_per_month"]} for r in recipes] - result.append(d) - conn.close() - return result - - -@app.post("/api/oil-plans", status_code=201) -def create_oil_plan(body: dict, user=Depends(get_current_user)): - if not user.get("id"): - raise HTTPException(403, "请先登录") - health_desc = body.get("health_desc", "").strip() - teacher_id = body.get("teacher_id") - # Teacher/admin can create plan for a user directly - target_user_id = body.get("user_id") if user["role"] in ("senior_editor", "admin") else None - if not teacher_id: - raise HTTPException(400, "请选择老师") - conn = get_db() - c = conn.cursor() - plan_user_id = target_user_id or user["id"] - c.execute("INSERT INTO oil_plans (user_id, teacher_id, health_desc) VALUES (?, ?, ?)", - (plan_user_id, teacher_id, health_desc)) - plan_id = c.lastrowid - who = user.get("display_name") or user["username"] - conn.execute( - "INSERT INTO notifications (target_role, title, body, target_user_id) VALUES (?, ?, ?, ?)", - ("admin", "📋 新方案请求", f"{who}:{health_desc[:50]}", teacher_id) - ) - conn.commit() - conn.close() - return {"id": plan_id} - - -@app.put("/api/oil-plans/{plan_id}") -def update_oil_plan(plan_id: int, body: dict, user=Depends(get_current_user)): - conn = get_db() - plan = conn.execute("SELECT * FROM oil_plans WHERE id = ?", (plan_id,)).fetchone() - if not plan: - conn.close() - raise HTTPException(404, "方案不存在") - is_teacher = plan["teacher_id"] == user["id"] or user["role"] == "admin" - is_owner = plan["user_id"] == user["id"] - if not is_teacher and not is_owner: - conn.close() - raise HTTPException(403, "无权操作") - if "health_desc" in body and is_owner: - conn.execute("UPDATE oil_plans SET health_desc = ? WHERE id = ?", (body["health_desc"], plan_id)) - who = user.get("display_name") or user["username"] - conn.execute( - "INSERT INTO notifications (target_role, title, body, target_user_id) VALUES (?, ?, ?, ?)", - ("admin", "📋 方案需求已更新", f"{who} 更新了健康需求:{body['health_desc'][:50]}", plan["teacher_id"]) - ) - if "title" in body and is_teacher: - conn.execute("UPDATE oil_plans SET title = ? WHERE id = ?", (body["title"], plan_id)) - if "status" in body and is_teacher: - old_status = plan["status"] - conn.execute("UPDATE oil_plans SET status = ? WHERE id = ?", (body["status"], plan_id)) - if body["status"] == "active" and old_status != "active": - teacher_name = user.get("display_name") or user["username"] - conn.execute( - "INSERT INTO notifications (target_role, title, body, target_user_id) VALUES (?, ?, ?, ?)", - ("viewer", "🎉 你的购油方案已就绪", f"{teacher_name}老师已为你定制好方案,去库存页查看!", plan["user_id"]) - ) - conn.commit() - conn.close() - return {"ok": True} - - -@app.post("/api/oil-plans/{plan_id}/recipes", status_code=201) -def add_plan_recipe(plan_id: int, body: dict, user=Depends(get_current_user)): - conn = get_db() - plan = conn.execute("SELECT * FROM oil_plans WHERE id = ?", (plan_id,)).fetchone() - if not plan: - conn.close() - raise HTTPException(404, "方案不存在") - if plan["teacher_id"] != user["id"] and user["role"] != "admin": - conn.close() - raise HTTPException(403, "无权操作") - recipe_name = body.get("recipe_name", "").strip() - ingredients = body.get("ingredients", []) - times = body.get("times_per_month", 1) - if not recipe_name or not ingredients: - conn.close() - raise HTTPException(400, "配方名和成分不能为空") - c = conn.cursor() - c.execute("INSERT INTO oil_plan_recipes (plan_id, recipe_name, ingredients, times_per_month) VALUES (?, ?, ?, ?)", - (plan_id, recipe_name, json.dumps(ingredients, ensure_ascii=False), times)) - conn.commit() - rid = c.lastrowid - conn.close() - return {"id": rid} - - -@app.delete("/api/oil-plans/{plan_id}/recipes/{recipe_id}") -def remove_plan_recipe(plan_id: int, recipe_id: int, user=Depends(get_current_user)): - conn = get_db() - plan = conn.execute("SELECT * FROM oil_plans WHERE id = ?", (plan_id,)).fetchone() - if not plan: - conn.close() - raise HTTPException(404) - if plan["teacher_id"] != user["id"] and user["role"] != "admin": - conn.close() - raise HTTPException(403) - conn.execute("DELETE FROM oil_plan_recipes WHERE id = ? AND plan_id = ?", (recipe_id, plan_id)) - conn.commit() - conn.close() - return {"ok": True} - - -@app.get("/api/oil-plans/{plan_id}/shopping-list") -def plan_shopping_list(plan_id: int, user=Depends(get_current_user)): - if not user.get("id"): - raise HTTPException(403, "请先登录") - conn = get_db() - plan = conn.execute("SELECT * FROM oil_plans WHERE id = ?", (plan_id,)).fetchone() - if not plan: - conn.close() - raise HTTPException(404) - if plan["user_id"] != user["id"] and plan["teacher_id"] != user["id"] and user["role"] != "admin": - conn.close() - raise HTTPException(403) - recipes = conn.execute("SELECT * FROM oil_plan_recipes WHERE plan_id = ?", (plan_id,)).fetchall() - # Calculate monthly drops per oil - oil_drops = {} - for r in recipes: - ings = json.loads(r["ingredients"]) - for ing in ings: - name = ing.get("oil_name") or ing.get("oil", "") - drops = ing.get("drops", 0) - if name and name != "椰子油": - oil_drops[name] = oil_drops.get(name, 0) + drops * r["times_per_month"] - # Get oil metadata - oils_meta = {} - for row in conn.execute("SELECT name, bottle_price, drop_count FROM oils").fetchall(): - oils_meta[row["name"]] = {"bottle_price": row["bottle_price"], "drop_count": row["drop_count"]} - # Get user inventory - inv = set(r["oil_name"] for r in conn.execute( - "SELECT oil_name FROM user_inventory WHERE user_id = ?", (plan["user_id"],)).fetchall()) - conn.close() - import math - result = [] - for oil, drops in sorted(oil_drops.items()): - meta = oils_meta.get(oil, {}) - drop_count = meta.get("drop_count", 250) - bottle_price = meta.get("bottle_price", 0) - bottles = math.ceil(drops / drop_count) if drop_count else 1 - result.append({ - "oil_name": oil, - "monthly_drops": round(drops, 1), - "bottles_needed": bottles, - "bottle_price": bottle_price, - "total_cost": round(bottles * bottle_price, 2), - "in_inventory": oil in inv, - }) - result.sort(key=lambda x: (x["in_inventory"], x["oil_name"])) - return result - +# ── Search Logging ───────────────────────────────────── # ── Search Logging ───────────────────────────────────── @app.post("/api/search-log") def log_search(body: dict, user=Depends(get_current_user)): diff --git a/frontend/src/components/UserMenu.vue b/frontend/src/components/UserMenu.vue index f55268b..dc39e9b 100644 --- a/frontend/src/components/UserMenu.vue +++ b/frontend/src/components/UserMenu.vue @@ -44,8 +44,6 @@ - - @@ -167,16 +165,6 @@ function goRename(n) { changeUsername() } -function isPlanRequest(n) { - return n.title && (n.title.includes('方案请求') || n.title.includes('方案需求')) -} - -function goPlanDesign(n) { - markOneRead(n) - emit('close') - window.location.hash = '#/users' -} - function isReviewable(n) { if (!n.title) return false // Admin: review recipe/business/applications @@ -319,8 +307,6 @@ onMounted(loadNotifications) } .notif-btn-added { color: #4a9d7e; border-color: #7ec6a4; } .notif-btn-added:hover { background: #e8f5e9; } -.notif-btn-plan { color: #1565c0; border-color: #90caf9; } -.notif-btn-plan:hover { background: #e3f2fd; } .notif-btn-review { color: #e65100; border-color: #ffb74d; } .notif-btn-review:hover { background: #fff3e0; } .notif-body { color: #888; font-size: 12px; margin-top: 2px; white-space: pre-line; } diff --git a/frontend/src/stores/plans.js b/frontend/src/stores/plans.js deleted file mode 100644 index 8a7b9fa..0000000 --- a/frontend/src/stores/plans.js +++ /dev/null @@ -1,65 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import { api } from '../composables/useApi' - -export const usePlansStore = defineStore('plans', () => { - const plans = ref([]) - const teachers = ref([]) - const shoppingList = ref([]) - - const activePlan = computed(() => plans.value.find(p => p.status === 'active')) - const pendingPlans = computed(() => plans.value.filter(p => p.status === 'pending')) - - async function loadPlans() { - const res = await api('/api/oil-plans') - if (res.ok) plans.value = await res.json() - } - - async function loadTeachers() { - const res = await api('/api/teachers') - if (res.ok) teachers.value = await res.json() - } - - async function createPlan(healthDesc, teacherId) { - const res = await api('/api/oil-plans', { - method: 'POST', - body: JSON.stringify({ health_desc: healthDesc, teacher_id: teacherId }), - }) - if (!res.ok) throw new Error('创建失败') - await loadPlans() - return res.json() - } - - async function updatePlan(planId, data) { - await api(`/api/oil-plans/${planId}`, { - method: 'PUT', - body: JSON.stringify(data), - }) - await loadPlans() - } - - async function addRecipe(planId, recipeName, ingredients, timesPerMonth) { - await api(`/api/oil-plans/${planId}/recipes`, { - method: 'POST', - body: JSON.stringify({ recipe_name: recipeName, ingredients, times_per_month: timesPerMonth }), - }) - await loadPlans() - } - - async function removeRecipe(planId, recipeId) { - await api(`/api/oil-plans/${planId}/recipes/${recipeId}`, { method: 'DELETE' }) - await loadPlans() - } - - async function loadShoppingList(planId) { - const res = await api(`/api/oil-plans/${planId}/shopping-list`) - if (res.ok) shoppingList.value = await res.json() - } - - return { - plans, teachers, shoppingList, - activePlan, pendingPlans, - loadPlans, loadTeachers, createPlan, updatePlan, - addRecipe, removeRecipe, loadShoppingList, - } -}) diff --git a/frontend/src/views/Inventory.vue b/frontend/src/views/Inventory.vue index 1e481cc..6cdecc3 100644 --- a/frontend/src/views/Inventory.vue +++ b/frontend/src/views/Inventory.vue @@ -2,86 +2,10 @@