427 lines
13 KiB
Python
427 lines
13 KiB
Python
from fastapi import APIRouter, HTTPException, Depends
|
|
from typing import List, Optional
|
|
from pydantic import BaseModel, Field
|
|
from ..utils.jwt_handler import verify_token
|
|
from ..services.admin_game_service import get_connection
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class GameListRequest(BaseModel):
|
|
game_type: int
|
|
|
|
|
|
class GameListResponseForUser(BaseModel):
|
|
game_id: int
|
|
game_name: str
|
|
photo_url: str
|
|
game_type: int
|
|
tags: List[str]
|
|
|
|
|
|
class GameSearchRequest(BaseModel):
|
|
search_name: str
|
|
|
|
|
|
class GameDetailResponse(BaseModel):
|
|
game_id: int
|
|
game_name: str
|
|
photo_url: str
|
|
game_type: int
|
|
description: str
|
|
duration: str
|
|
difficulty_level: int
|
|
recommended_players: str
|
|
remaining_quantity: int
|
|
tags: List[str]
|
|
long_description: str
|
|
|
|
|
|
class GameDetailRequest(BaseModel):
|
|
game_id: int
|
|
|
|
|
|
class TagResponse(BaseModel):
|
|
tag_id: int
|
|
tag_name: str
|
|
|
|
|
|
class TagGameRequest(BaseModel):
|
|
tag_ids: List[int]
|
|
|
|
|
|
class GameReviewCreate(BaseModel):
|
|
token: str
|
|
game_id: int
|
|
rating: int = Field(..., gt=0, le=10)
|
|
comment: Optional[str] = None
|
|
|
|
|
|
class GameReviewResponse(BaseModel):
|
|
user_name: str
|
|
rating: int
|
|
comment: Optional[str] = None
|
|
created_at: str
|
|
|
|
|
|
class GameReviewsResponse(BaseModel):
|
|
support_count: int
|
|
oppose_count: int
|
|
reviews: List[GameReviewResponse]
|
|
|
|
|
|
class GetUserRatingRequest(BaseModel):
|
|
token: str
|
|
game_id: int
|
|
|
|
|
|
class GetUserRatingResponse(BaseModel):
|
|
rating: int
|
|
|
|
|
|
class UpdateUserRatingRequest(BaseModel):
|
|
token: str
|
|
game_id: int
|
|
good: int
|
|
|
|
async def get_current_user(token: str):
|
|
try:
|
|
payload = verify_token(token)
|
|
return payload
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=401, detail=str(e))
|
|
|
|
|
|
|
|
@router.post("/search", response_model=List[GameListResponseForUser])
|
|
def search_games(request: GameSearchRequest):
|
|
"""搜索游戏"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
cursor.execute("""
|
|
SELECT g.*, GROUP_CONCAT(t.tag_name) as tags
|
|
FROM games g
|
|
LEFT JOIN game_tags gt ON g.game_id = gt.game_id
|
|
LEFT JOIN tags t ON gt.tag_id = t.tag_id
|
|
WHERE g.game_name LIKE %s AND is_available = 1 # 移除 game_type 条件
|
|
GROUP BY g.game_id
|
|
""", (f"%{request.search_name}%",)) # 参数减少为一个
|
|
return [parse_game_with_tags(row) for row in cursor.fetchall()]
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
@router.post("/detail", response_model=GameDetailResponse)
|
|
def get_game_detail(request: GameDetailRequest):
|
|
"""获取游戏详情"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
cursor.execute("""
|
|
SELECT g.*,
|
|
(g.quantity - COALESCE((
|
|
SELECT SUM(quantity)
|
|
FROM orders
|
|
WHERE game_id = g.game_id
|
|
AND order_status IN ('in_progress')
|
|
), 0)) as remaining_quantity,
|
|
GROUP_CONCAT(t.tag_name) as tags
|
|
FROM games g
|
|
LEFT JOIN game_tags gt ON g.game_id = gt.game_id
|
|
LEFT JOIN tags t ON gt.tag_id = t.tag_id
|
|
WHERE g.game_id = %s
|
|
GROUP BY g.game_id
|
|
""", (request.game_id,))
|
|
row = cursor.fetchone()
|
|
return parse_game_detail(row)
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
def parse_game_with_tags(row):
|
|
return {
|
|
"game_id": row["game_id"],
|
|
"game_name": row["game_name"],
|
|
"photo_url": row["photo_url"] or "",
|
|
"game_type": row["game_type"],
|
|
"tags": row["tags"].split(',') if row["tags"] else []
|
|
}
|
|
|
|
|
|
def parse_game_detail(row):
|
|
return {
|
|
"game_id": row["game_id"],
|
|
"game_name": row["game_name"],
|
|
"photo_url": row["photo_url"] or "",
|
|
"game_type": row["game_type"],
|
|
"description": row["description"],
|
|
"duration": row["duration"],
|
|
"long_description": row["long_description"] or "",
|
|
"difficulty_level": row["difficulty_level"],
|
|
"recommended_players": f"{row['min_players']}-{row['max_players']}人",
|
|
"remaining_quantity": row["remaining_quantity"],
|
|
"tags": row["tags"].split(',') if row["tags"] else []
|
|
}
|
|
|
|
|
|
@router.get("/tags", response_model=List[TagResponse])
|
|
def get_all_tags():
|
|
"""获取所有标签"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
cursor.execute("SELECT tag_id, tag_name FROM tags")
|
|
return [{"tag_id": row["tag_id"], "tag_name": row["tag_name"]} for row in cursor.fetchall()]
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
@router.post("/by_tag", response_model=List[GameListResponseForUser])
|
|
def get_games_by_tag(request: TagGameRequest):
|
|
"""根据多个标签获取游戏列表(空列表时返回所有游戏)"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
base_query = """
|
|
SELECT g.*, GROUP_CONCAT(t.tag_name) as tags
|
|
FROM games g
|
|
LEFT JOIN game_tags gt ON g.game_id = gt.game_id
|
|
LEFT JOIN tags t ON gt.tag_id = t.tag_id
|
|
WHERE is_available = 1
|
|
"""
|
|
|
|
# 动态构建查询条件
|
|
params = []
|
|
if request.tag_ids:
|
|
in_params = ', '.join(['%s'] * len(request.tag_ids))
|
|
base_query += f" AND gt.tag_id IN ({in_params})"
|
|
params.extend(request.tag_ids)
|
|
base_query += " GROUP BY g.game_id HAVING COUNT(DISTINCT gt.tag_id) = %s"
|
|
params.append(len(request.tag_ids))
|
|
else:
|
|
base_query += " GROUP BY g.game_id"
|
|
|
|
cursor.execute(base_query, tuple(params))
|
|
return [parse_game_with_tags(row) for row in cursor.fetchall()]
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
# 在路由部分添加新接口
|
|
@router.post("/review")
|
|
def create_game_review(request: GameReviewCreate):
|
|
"""提交游戏评价"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
payload = verify_token(request.token)
|
|
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (payload["sub"],))
|
|
user = cursor.fetchone()
|
|
if not user:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
user_id = user['user_id']
|
|
|
|
# 修正参数为元组格式
|
|
cursor.execute("""
|
|
INSERT INTO player_reviews
|
|
(user_id, game_id, rating, comment)
|
|
VALUES (%s, %s, %s, %s)
|
|
""",(user_id, request.game_id, request.rating, request.comment)) # 用单个元组包裹参数
|
|
|
|
connection.commit()
|
|
return {"message": "评价提交成功"}
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
@router.post("/reviews", response_model=GameReviewsResponse)
|
|
def get_game_reviews(request: GameDetailRequest):
|
|
"""获取游戏评价"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
# 获取拉数和踩数
|
|
cursor.execute("""
|
|
SELECT
|
|
SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) as support_count,
|
|
SUM(CASE WHEN rating = 0 THEN 1 ELSE 0 END) as oppose_count
|
|
FROM user_game_rating
|
|
WHERE game_id = %s
|
|
""", (request.game_id,))
|
|
count_result = cursor.fetchone()
|
|
support_count = int(count_result['support_count']) if count_result and count_result['support_count'] else 0
|
|
oppose_count = int(count_result['oppose_count']) if count_result and count_result['oppose_count'] else 0
|
|
|
|
# 获取有评论的评价列表
|
|
cursor.execute("""
|
|
SELECT
|
|
u.username,
|
|
pr.rating,
|
|
pr.comment,
|
|
pr.created_at
|
|
FROM player_reviews pr
|
|
JOIN users u ON pr.user_id = u.user_id
|
|
WHERE pr.game_id = %s AND pr.comment IS NOT NULL
|
|
GROUP BY pr.review_id
|
|
""", (request.game_id,))
|
|
results = cursor.fetchall()
|
|
|
|
# 处理返回结果
|
|
reviews = [{
|
|
"user_name": row['username'],
|
|
"rating": row['rating'],
|
|
"comment": row['comment'],
|
|
"created_at": str(row['created_at'])
|
|
} for row in results]
|
|
|
|
return {
|
|
"support_count": support_count,
|
|
"oppose_count": oppose_count,
|
|
"reviews": reviews
|
|
}
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
@router.post("/user-rating", response_model=GetUserRatingResponse)
|
|
def get_user_rating(request: GetUserRatingRequest):
|
|
"""获取用户对游戏的拉踩状态"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
payload = verify_token(request.token)
|
|
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (payload["sub"],))
|
|
user = cursor.fetchone()
|
|
if not user:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
user_id = user['user_id']
|
|
|
|
# 查询用户对游戏的拉踩状态
|
|
cursor.execute("""
|
|
SELECT rating
|
|
FROM user_game_rating
|
|
WHERE user_id = %s AND game_id = %s
|
|
""", (user_id, request.game_id))
|
|
rating_result = cursor.fetchone()
|
|
if rating_result:
|
|
rating = rating_result['rating']
|
|
else:
|
|
rating = 3
|
|
return {"rating": rating}
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
|
|
@router.post("/update-user-rating")
|
|
def update_user_rating(request: UpdateUserRatingRequest):
|
|
"""更新用户对游戏的拉踩记录"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
payload = verify_token(request.token)
|
|
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (payload["sub"],))
|
|
user = cursor.fetchone()
|
|
if not user:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
user_id = user['user_id']
|
|
|
|
# 检查是否已有记录
|
|
cursor.execute("""
|
|
SELECT id
|
|
FROM user_game_rating
|
|
WHERE user_id = %s AND game_id = %s
|
|
""", (user_id, request.game_id))
|
|
record = cursor.fetchone()
|
|
|
|
if record:
|
|
if request.good == 3:
|
|
# 删除记录
|
|
cursor.execute("""
|
|
DELETE FROM user_game_rating
|
|
WHERE user_id = %s AND game_id = %s
|
|
""", (user['user_id'], request.game_id))
|
|
else:
|
|
# 更新记录
|
|
cursor.execute("""
|
|
UPDATE user_game_rating
|
|
SET rating = %s
|
|
WHERE user_id = %s AND game_id = %s
|
|
""", (request.good, user['user_id'], request.game_id))
|
|
else:
|
|
if request.good != 3:
|
|
# 插入新记录
|
|
cursor.execute("""
|
|
INSERT INTO user_game_rating (user_id, game_id, rating)
|
|
VALUES (%s, %s, %s)
|
|
""", (user['user_id'], request.game_id, request.good))
|
|
|
|
connection.commit()
|
|
return {"message": "拉踩记录更新成功"}
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
class MessageCreate(BaseModel):
|
|
token: str
|
|
message: str
|
|
|
|
class MessageResponse(BaseModel):
|
|
message_id: int
|
|
username: str
|
|
message_content: str
|
|
created_at: str
|
|
|
|
|
|
|
|
@router.post("/messages")
|
|
def create_message(request: MessageCreate):
|
|
"""创建用户留言"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
payload = verify_token(request.token)
|
|
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (payload["sub"],))
|
|
user = cursor.fetchone()
|
|
if not user:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
user_id = user['user_id']
|
|
|
|
# 插入留言
|
|
cursor.execute(
|
|
"""INSERT INTO player_messages
|
|
(user_id, message_content)
|
|
VALUES (%s, %s)""",
|
|
(user_id, request.message)
|
|
)
|
|
connection.commit()
|
|
return {"message": "留言提交成功"}
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
@router.get("/messages", response_model=list[MessageResponse])
|
|
def get_messages():
|
|
"""获取最新10条留言"""
|
|
connection = get_connection()
|
|
cursor = connection.cursor(dictionary=True)
|
|
try:
|
|
cursor.execute("""
|
|
SELECT m.message_id, u.username, m.message_content, m.created_at
|
|
FROM player_messages m
|
|
JOIN users u ON m.user_id = u.user_id
|
|
ORDER BY m.created_at DESC
|
|
LIMIT 10
|
|
""")
|
|
return cursor.fetchall()
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|