revert: 删除购油方案功能,修复MyDiary模板错误
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 6s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 14s
Test / e2e-test (push) Successful in 53s
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 6s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 14s
Test / e2e-test (push) Successful in 53s
完全移除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) <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
194
backend/main.py
194
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)):
|
||||
|
||||
Reference in New Issue
Block a user