feat(init): 添加完整的认证和业务路由模块
- 实现微信登录、手机号绑定、token验证和登出功能 - 添加打卡系统,支持正常、迟到、请假等状态记录 - 集成课程投票功能,支持多课程选择和评分评论 - 开发反馈系统,支持图文反馈提交和管理员处理 - 构建首页页面模块,动态展示学习资源和个人信息 - 设计计划管理系统,支持周/月/年计划制定和跟踪 - 实现小组计划统计和历史记录查询功能 - 完善用户认证会话管理和数据库交互逻辑
This commit is contained in:
132
routes/yijing.py
Normal file
132
routes/yijing.py
Normal 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}
|
||||
Reference in New Issue
Block a user