- 实现微信登录、手机号绑定、token验证和登出功能 - 添加打卡系统,支持正常、迟到、请假等状态记录 - 集成课程投票功能,支持多课程选择和评分评论 - 开发反馈系统,支持图文反馈提交和管理员处理 - 构建首页页面模块,动态展示学习资源和个人信息 - 设计计划管理系统,支持周/月/年计划制定和跟踪 - 实现小组计划统计和历史记录查询功能 - 完善用户认证会话管理和数据库交互逻辑
133 lines
4.8 KiB
Python
133 lines
4.8 KiB
Python
"""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}
|