feat: 阶段2 Python后端、用户管理、统计、单词收藏与响应式适配
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from .auth import login_required
|
||||
from .db import get_db, now_iso
|
||||
|
||||
entries_bp = Blueprint("entries", __name__)
|
||||
|
||||
|
||||
def row_to_dict(row):
|
||||
return dict(row)
|
||||
|
||||
|
||||
@entries_bp.get("/api/entries")
|
||||
@login_required
|
||||
def list_entries(user):
|
||||
conn = get_db()
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT id, date, spanish, english, chinese, created_at, updated_at
|
||||
FROM entries
|
||||
WHERE user_id = ?
|
||||
ORDER BY date DESC, id DESC
|
||||
""",
|
||||
(user["id"],),
|
||||
).fetchall()
|
||||
return jsonify([row_to_dict(r) for r in rows])
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@entries_bp.post("/api/entries")
|
||||
@login_required
|
||||
def create_entry(user):
|
||||
data = request.get_json(silent=True) or {}
|
||||
spanish = (data.get("spanish") or "").strip()
|
||||
if not spanish:
|
||||
return jsonify({"error": "日记内容不能为空"}), 400
|
||||
entry_date = (data.get("date") or date.today().isoformat()).strip()
|
||||
conn = get_db()
|
||||
try:
|
||||
cur = conn.execute(
|
||||
"""
|
||||
INSERT INTO entries (user_id, date, spanish, english, chinese, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
user["id"],
|
||||
entry_date,
|
||||
spanish,
|
||||
(data.get("english") or "").strip(),
|
||||
(data.get("chinese") or "").strip(),
|
||||
now_iso(),
|
||||
now_iso(),
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
row = conn.execute(
|
||||
"SELECT id, date, spanish, english, chinese, created_at, updated_at FROM entries WHERE id = ?",
|
||||
(cur.lastrowid,),
|
||||
).fetchone()
|
||||
return jsonify(row_to_dict(row)), 201
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@entries_bp.put("/api/entries/<int:entry_id>")
|
||||
@login_required
|
||||
def update_entry(user, entry_id):
|
||||
data = request.get_json(silent=True) or {}
|
||||
spanish = (data.get("spanish") or "").strip()
|
||||
if not spanish:
|
||||
return jsonify({"error": "日记内容不能为空"}), 400
|
||||
conn = get_db()
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT id FROM entries WHERE id = ? AND user_id = ?",
|
||||
(entry_id, user["id"]),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return jsonify({"error": "日记不存在或无权访问"}), 404
|
||||
conn.execute(
|
||||
"""
|
||||
UPDATE entries
|
||||
SET date = ?, spanish = ?, english = ?, chinese = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""",
|
||||
(
|
||||
(data.get("date") or row["date"]).strip(),
|
||||
spanish,
|
||||
(data.get("english") or "").strip(),
|
||||
(data.get("chinese") or "").strip(),
|
||||
now_iso(),
|
||||
entry_id,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
updated = conn.execute(
|
||||
"SELECT id, date, spanish, english, chinese, created_at, updated_at FROM entries WHERE id = ?",
|
||||
(entry_id,),
|
||||
).fetchone()
|
||||
return jsonify(row_to_dict(updated))
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@entries_bp.delete("/api/entries/<int:entry_id>")
|
||||
@login_required
|
||||
def delete_entry(user, entry_id):
|
||||
conn = get_db()
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT id FROM entries WHERE id = ? AND user_id = ?",
|
||||
(entry_id, user["id"]),
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return jsonify({"error": "日记不存在或无权访问"}), 404
|
||||
conn.execute("DELETE FROM entries WHERE id = ?", (entry_id,))
|
||||
conn.commit()
|
||||
return jsonify({"ok": True})
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@entries_bp.get("/api/entries/stats")
|
||||
@login_required
|
||||
def entry_stats(user):
|
||||
conn = get_db()
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT date, spanish FROM entries
|
||||
WHERE user_id = ?
|
||||
""",
|
||||
(user["id"],),
|
||||
).fetchall()
|
||||
total = len(rows)
|
||||
total_chars = sum(len(r["spanish"]) for r in rows)
|
||||
dates = [r["date"] for r in rows]
|
||||
|
||||
today = date.today()
|
||||
month_start = today.replace(day=1)
|
||||
week_start = today - timedelta(days=today.weekday())
|
||||
month_count = sum(1 for d in dates if d >= month_start.isoformat())
|
||||
week_count = sum(1 for d in dates if d >= week_start.isoformat())
|
||||
|
||||
streak = 0
|
||||
cursor = today
|
||||
if cursor.isoformat() not in dates:
|
||||
cursor -= timedelta(days=1)
|
||||
while cursor.isoformat() in dates:
|
||||
streak += 1
|
||||
cursor -= timedelta(days=1)
|
||||
|
||||
daily = []
|
||||
for offset in range(6, -1, -1):
|
||||
day = today - timedelta(days=offset)
|
||||
daily.append({"date": day.isoformat(), "count": sum(1 for d in dates if d == day.isoformat())})
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"total": total,
|
||||
"month": month_count,
|
||||
"week": week_count,
|
||||
"streak": streak,
|
||||
"totalChars": total_chars,
|
||||
"daily": daily,
|
||||
}
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user