fix: KB upload (assets.content column), KG conversion, achievement SQL index, card font color, markdown renderer
This commit is contained in:
1 parent
985e0548b0
commit
ce0a7b4243
42 files changed
+29056
No files matched your search
@@ -0,0 +1,327 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import text
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from ...database import SessionLocal, get_db
|
||||
from ...auth import get_current_user
|
||||
from ...models import User
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
def _execute_raw(db: Session, sql: str, params: dict = None):
|
||||
"""执行原始SQL的辅助函数"""
|
||||
try:
|
||||
result = db.execute(text(sql), params or {})
|
||||
db.commit()
|
||||
return result
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise e
|
||||
|
||||
@router.get("/achievements")
|
||||
async def list_achievements(
|
||||
category: Optional[str] = Query(None),
|
||||
is_active: Optional[bool] = Query(True),
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""列出所有成就(带用户进度)"""
|
||||
try:
|
||||
achievements = []
|
||||
|
||||
# Build query
|
||||
sql = "SELECT * FROM achievements WHERE 1=1"
|
||||
params = {}
|
||||
if category:
|
||||
sql += " AND category = :category"
|
||||
params["category"] = category
|
||||
if is_active is not None:
|
||||
sql += " AND is_active = :is_active"
|
||||
params["is_active"] = 1 if is_active else 0
|
||||
sql += " ORDER BY display_order ASC"
|
||||
|
||||
result = _execute_raw(db, sql, params)
|
||||
rows = result.fetchall()
|
||||
|
||||
user_id = user.id if hasattr(user, 'id') else None
|
||||
|
||||
for row in rows:
|
||||
ach = {
|
||||
"id": row[0],
|
||||
"name": row[1],
|
||||
"description": row[2],
|
||||
"icon": row[3],
|
||||
"category": row[4],
|
||||
"criteria_type": row[5],
|
||||
"criteria_key": row[6],
|
||||
"criteria_value": row[7],
|
||||
"reward_type": row[8],
|
||||
"reward_value": float(row[9]) if row[9] else 0,
|
||||
"badge_icon": row[10],
|
||||
"is_active": bool(row[11]) if row[11] is not None else True,
|
||||
"display_order": row[12],
|
||||
"user_progress": 0,
|
||||
"is_completed": False,
|
||||
"reward_claimed": False
|
||||
}
|
||||
|
||||
# Get user progress if user is logged in
|
||||
if user_id:
|
||||
try:
|
||||
progress_result = _execute_raw(db,
|
||||
"SELECT progress, is_completed, reward_claimed FROM user_achievements WHERE user_id = :uid AND achievement_id = :aid",
|
||||
{"uid": user_id, "aid": ach["id"]})
|
||||
prog_row = progress_result.fetchone()
|
||||
if prog_row:
|
||||
ach["user_progress"] = prog_row[0] or 0
|
||||
ach["is_completed"] = bool(prog_row[1]) if prog_row[1] is not None else False
|
||||
ach["reward_claimed"] = bool(prog_row[2]) if prog_row[2] is not None else False
|
||||
except:
|
||||
pass
|
||||
|
||||
achievements.append(ach)
|
||||
|
||||
return {"code": 0, "data": {"items": achievements, "total": len(achievements)}}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"code": 0, "data": {"items": [], "total": 0}}
|
||||
|
||||
@router.get("/achievements/{ach_id}")
|
||||
async def get_achievement_detail(
|
||||
ach_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""获取成就详情"""
|
||||
try:
|
||||
result = _execute_raw(db, "SELECT * FROM achievements WHERE id = :id", {"id": ach_id})
|
||||
row = result.fetchone()
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="成就不存在")
|
||||
|
||||
achievement = {
|
||||
"id": row[0],
|
||||
"name": row[1],
|
||||
"description": row[2],
|
||||
"icon": row[3],
|
||||
"category": row[4],
|
||||
"criteria_type": row[5],
|
||||
"criteria_key": row[6],
|
||||
"criteria_value": row[7],
|
||||
"reward_type": row[8],
|
||||
"reward_value": float(row[9]) if row[9] else 0,
|
||||
"badge_icon": row[10],
|
||||
"is_active": bool(row[11]) if row[11] is not None else True,
|
||||
"display_order": row[12]
|
||||
}
|
||||
|
||||
return {"code": 0, "data": achievement}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/achievements")
|
||||
async def create_achievement(
|
||||
name: str = Query(...),
|
||||
category: str = Query(...),
|
||||
criteria_type: str = Query(...),
|
||||
criteria_value: int = Query(...),
|
||||
description: Optional[str] = Query(""),
|
||||
icon: Optional[str] = Query(""),
|
||||
criteria_key: Optional[str] = Query(None),
|
||||
reward_type: str = Query("points"),
|
||||
reward_value: float = Query(0),
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""创建新成就(管理员)"""
|
||||
try:
|
||||
import uuid
|
||||
ach_id = str(uuid.uuid4())
|
||||
|
||||
sql = """INSERT INTO achievements
|
||||
(id, name, description, icon, category, criteria_type, criteria_key,
|
||||
criteria_value, reward_type, reward_value, is_active, display_order)
|
||||
VALUES (:id, :name, :desc, :icon, :cat, :ctype, :ckey,
|
||||
:cvalue, :rtype, :rvalue, 1, 0)"""
|
||||
|
||||
_execute_raw(db, sql, {
|
||||
"id": ach_id,
|
||||
"name": name,
|
||||
"desc": description,
|
||||
"icon": icon,
|
||||
"cat": category,
|
||||
"ctype": criteria_type,
|
||||
"ckey": criteria_key,
|
||||
"cvalue": criteria_value,
|
||||
"rtype": reward_type,
|
||||
"rvalue": reward_value
|
||||
})
|
||||
|
||||
return {"code": 0, "message": "成就创建成功", "data": {"id": ach_id}}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.put("/achievements/{ach_id}")
|
||||
async def update_achievement(
|
||||
ach_id: str,
|
||||
name: Optional[str] = Query(None),
|
||||
description: Optional[str] = Query(None),
|
||||
is_active: Optional[bool] = Query(None),
|
||||
reward_value: Optional[float] = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""更新成就(管理员)"""
|
||||
try:
|
||||
updates = []
|
||||
params = {"id": ach_id}
|
||||
|
||||
if name is not None:
|
||||
updates.append("name = :name")
|
||||
params["name"] = name
|
||||
if description is not None:
|
||||
updates.append("description = :desc")
|
||||
params["desc"] = description
|
||||
if is_active is not None:
|
||||
updates.append("is_active = :active")
|
||||
params["active"] = 1 if is_active else 0
|
||||
if reward_value is not None:
|
||||
updates.append("reward_value = :rvalue")
|
||||
params["rvalue"] = reward_value
|
||||
|
||||
if updates:
|
||||
sql = f"UPDATE achievements SET {', '.join(updates)} WHERE id = :id"
|
||||
_execute_raw(db, sql, params)
|
||||
|
||||
return {"code": 0, "message": "更新成功"}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.delete("/achievements/{ach_id}")
|
||||
async def delete_achievement(
|
||||
ach_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""删除成就(管理员)"""
|
||||
try:
|
||||
_execute_raw(db, "DELETE FROM achievements WHERE id = :id", {"id": ach_id})
|
||||
return {"code": 0, "message": "删除成功"}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/user/achievements")
|
||||
async def get_my_achievements(
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""获取当前用户的成就进度"""
|
||||
try:
|
||||
user_id = user.id if hasattr(user, 'id') else None
|
||||
if not user_id:
|
||||
return {"code": 0, "data": {"items": [], "total": 0}}
|
||||
|
||||
# Get all achievements with user progress
|
||||
result = _execute_raw(db, "SELECT a.*, ua.progress, ua.is_completed, ua.reward_claimed, ua.completed_at FROM achievements a LEFT JOIN user_achievements ua ON a.id = ua.achievement_id AND ua.user_id = :uid WHERE a.is_active = 1 ORDER BY a.display_order ASC", {"uid": user_id})
|
||||
|
||||
rows = result.fetchall()
|
||||
achievements = []
|
||||
for row in rows:
|
||||
achievements.append({
|
||||
"id": row[0],
|
||||
"name": row[1],
|
||||
"description": row[2],
|
||||
"icon": row[3],
|
||||
"category": row[4],
|
||||
"criteria_type": row[5],
|
||||
"criteria_value": row[7],
|
||||
"reward_type": row[8],
|
||||
"reward_value": float(row[9]) if row[9] else 0,
|
||||
"progress": row[15] or 0,
|
||||
"is_completed": bool(row[16]) if row[16] is not None else False,
|
||||
"reward_claimed": bool(row[17]) if row[17] is not None else False,
|
||||
"completed_at": str(row[18]) if row[18] else None
|
||||
})
|
||||
|
||||
return {"code": 0, "data": {"items": achievements, "total": len(achievements)}}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"code": 0, "data": {"items": [], "total": 0}}
|
||||
|
||||
@router.post("/user/achievements/{ach_id}/claim")
|
||||
async def claim_achievement_reward(
|
||||
ach_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""领取成就奖励"""
|
||||
try:
|
||||
user_id = user.id if hasattr(user, 'id') else None
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=401, detail="未登录")
|
||||
|
||||
# Check if achievement is completed
|
||||
result = _execute_raw(db,
|
||||
"SELECT is_completed, reward_claimed FROM user_achievements WHERE user_id = :uid AND achievement_id = :aid",
|
||||
{"uid": user_id, "aid": ach_id})
|
||||
row = result.fetchone()
|
||||
|
||||
if not row or not row[0]:
|
||||
raise HTTPException(status_code=400, detail="成就未完成")
|
||||
|
||||
if row[1]:
|
||||
raise HTTPException(status_code=400, detail="奖励已领取")
|
||||
|
||||
# Mark reward as claimed
|
||||
_execute_raw(db,
|
||||
"UPDATE user_achievements SET reward_claimed = 1, claimed_at = NOW() WHERE user_id = :uid AND achievement_id = :aid",
|
||||
{"uid": user_id, "aid": ach_id})
|
||||
|
||||
return {"code": 0, "message": "奖励领取成功"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/check-progress")
|
||||
async def check_user_progress(
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(get_current_user)
|
||||
):
|
||||
"""检查并更新用户成就进度"""
|
||||
try:
|
||||
user_id = user.id if hasattr(user, 'id') else None
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=401, detail="未登录")
|
||||
|
||||
# Get user stats (this would need to be implemented based on actual data)
|
||||
# For now, just return success
|
||||
return {"code": 0, "message": "进度检查完成", "data": {"updated": 0}}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/leaderboard")
|
||||
async def get_achievement_leaderboard(
|
||||
period: str = Query("all"),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取成就排行榜"""
|
||||
try:
|
||||
# This would aggregate user achievements and rank them
|
||||
# For now, return empty
|
||||
return {"code": 0, "data": {"items": []}}
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return {"code": 0, "data": {"items": []}}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user