feat(init): 添加完整的认证和业务路由模块

- 实现微信登录、手机号绑定、token验证和登出功能
- 添加打卡系统,支持正常、迟到、请假等状态记录
- 集成课程投票功能,支持多课程选择和评分评论
- 开发反馈系统,支持图文反馈提交和管理员处理
- 构建首页页面模块,动态展示学习资源和个人信息
- 设计计划管理系统,支持周/月/年计划制定和跟踪
- 实现小组计划统计和历史记录查询功能
- 完善用户认证会话管理和数据库交互逻辑
This commit is contained in:
王宏豪
2026-09-03 16:43:11 +08:00
commit f3fbd0377a
24 changed files with 1918 additions and 0 deletions

132
routes/yijing.py Normal file
View File

@@ -0,0 +1,132 @@
"""Yijing (易经) APIs, ported from the legacy Flask server to the unified FastAPI service.
All protected endpoints use the same Bearer JWT as the rest of the service, and
the user_id is sourced from the authenticated auth_users row (old cookie sessions
are handled on the H5 side by shrinking down to the token-less /api/auth/verify
fallback before reaching these APIs).
"""
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from .. import db
from .auth import get_current_user
router = APIRouter(prefix="/api/v1/yijing", tags=["yijing"])
@router.get("/catalog")
def yijing_catalog():
"""Public catalog: trigrams + hexagrams, mirroring the legacy endpoint."""
trigrams = db.fetch_all(
"SELECT id,name,symbol,nature,de,family,direction,xiantian,houtian,binary_code,class_xiang,summary "
"FROM yijing_trigrams ORDER BY xiantian"
)
hexagrams = db.fetch_all(
"SELECT id,name,full_name,symbol,upper_trigram,lower_trigram,binary_code,judgment,gua_explanation,bagong,gong_wuxing "
"FROM yijing_hexagrams ORDER BY id"
)
return {"success": True, "trigrams": trigrams, "hexagrams": hexagrams}
class ProgressUpdateRequest(BaseModel):
stage: int
item_type: str
item_id: int
status: str = "learning"
@router.get("/progress")
def yijing_progress(user=Depends(get_current_user)):
rows = db.fetch_all(
"SELECT stage,item_type,item_id,status,score,attempts,correct "
"FROM yijing_progress WHERE user_id=%s",
(user["id"],),
)
by = {}
for r in rows:
by.setdefault(r["stage"], {})[
f'{r["item_type"]}:{r["item_id"]}'
] = {
"status": r["status"],
"score": r["score"],
"attempts": r["attempts"],
"correct": r["correct"],
}
return {"success": True, "progress": by}
@router.post("/progress")
def yijing_progress_update(req: ProgressUpdateRequest, user=Depends(get_current_user)):
if not (1 <= req.stage <= 5) or not req.item_type or req.item_id <= 0:
raise HTTPException(status_code=400, detail="参数错误")
db.execute(
"INSERT INTO yijing_progress (user_id,stage,item_type,item_id,status) "
"VALUES (%s,%s,%s,%s,%s) ON DUPLICATE KEY UPDATE status=VALUES(status)",
(user["id"], req.stage, req.item_type, req.item_id, req.status),
)
return {"success": True}
class QuizRequest(BaseModel):
stage: int
item_type: str
item_id: int
correct: bool = False
answer: str = ""
@router.post("/quiz")
def yijing_quiz(req: QuizRequest, user=Depends(get_current_user)):
if not (1 <= req.stage <= 5) or not req.item_type or req.item_id <= 0:
raise HTTPException(status_code=400, detail="参数错误")
correct = 1 if req.correct else 0
answer = req.answer[:500]
db.execute(
"INSERT INTO yijing_attempts (user_id,stage,item_type,item_id,correct,answer) "
"VALUES (%s,%s,%s,%s,%s,%s)",
(user["id"], req.stage, req.item_type, req.item_id, correct, answer),
)
row = db.fetch_one(
"SELECT score,correct FROM yijing_progress "
"WHERE user_id=%s AND stage=%s AND item_type=%s AND item_id=%s",
(user["id"], req.stage, req.item_type, req.item_id),
)
if not row:
db.execute(
"INSERT INTO yijing_progress (user_id,stage,item_type,item_id,status,score,attempts,correct) "
"VALUES (%s,%s,%s,%s,'learning',%s,1,%s)",
(user["id"], req.stage, req.item_type, req.item_id, correct, correct),
)
else:
db.execute(
"UPDATE yijing_progress SET attempts=attempts+1, correct=correct+%s, "
"score=LEAST(correct+%s,2) WHERE user_id=%s AND stage=%s AND item_type=%s AND item_id=%s",
(correct, correct, user["id"], req.stage, req.item_type, req.item_id),
)
row = db.fetch_one(
"SELECT score,correct FROM yijing_progress "
"WHERE user_id=%s AND stage=%s AND item_type=%s AND item_id=%s",
(user["id"], req.stage, req.item_type, req.item_id),
)
mastered = False
if row and row["score"] >= 2:
db.execute(
"UPDATE yijing_progress SET status='mastered' "
"WHERE user_id=%s AND stage=%s AND item_type=%s AND item_id=%s",
(user["id"], req.stage, req.item_type, req.item_id),
)
mastered = True
due = datetime.now() + (timedelta(days=1) if correct else timedelta(hours=1))
db.execute(
"INSERT INTO yijing_reviews (user_id,item_type,item_id,due_at) VALUES (%s,%s,%s,%s) "
"ON DUPLICATE KEY UPDATE due_at=VALUES(due_at)",
(user["id"], req.item_type, req.item_id, due),
)
return {"success": True, "mastered": mastered, "score": row["score"] if row else 0}