from fastapi import APIRouter, HTTPException from pydantic import BaseModel from datetime import datetime, timedelta from ..db import get_connection from ..utils.jwt_handler import verify_token from apscheduler.schedulers.background import BackgroundScheduler from datetime import timezone, timedelta from fastapi import FastAPI scheduler = BackgroundScheduler() router = APIRouter() class CreateGroupRequest(BaseModel): token: str group_name: str description: str max_members: int start_time: datetime end_time: datetime play_start_time: datetime play_end_time: datetime class JoinGroupRequest(BaseModel): group_id: int token: str class GroupOperationRequest(BaseModel): group_id: int class GroupDetailsRequest(GroupOperationRequest): token: str class BaseTokenRequest(BaseModel): token: str class GroupListResponse(BaseModel): group_id: int group_name: str start_date: datetime start_time: datetime end_time: datetime play_start_time: datetime play_end_time: datetime group_status: str max_members: int current_members: int class LeaveGroupRequest(GroupOperationRequest): token: str class DeleteGroupRequest(GroupOperationRequest): token: str class UpdateDescriptionRequest(GroupOperationRequest): new_description: str token: str def _get_user_id(token: str): try: payload = verify_token(token) phone_number = payload["sub"] conn = get_connection() cursor = conn.cursor() cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (phone_number,)) result = cursor.fetchone() if not result: raise HTTPException(401, "用户不存在") return result[0] finally: cursor.close() conn.close() def _check_group_timing(group_id: int): conn = get_connection() try: cursor = conn.cursor() cursor.execute(""" SELECT end_time, group_status FROM game_groups WHERE group_id = %s """, (group_id,)) result = cursor.fetchone() if not result: raise HTTPException(404, "群组不存在") end_time, status = result return status finally: cursor.close() conn.close() @router.post("/create") def create_group(request: CreateGroupRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor() start_time = request.start_time.astimezone(timezone(timedelta(hours=8))) end_time = request.end_time.astimezone(timezone(timedelta(hours=8))) play_start_time = request.play_start_time.astimezone(timezone(timedelta(hours=8))) play_end_time = request.play_end_time.astimezone(timezone(timedelta(hours=8))) cursor.execute(""" INSERT INTO game_groups (user_id, group_name, description, max_members, start_date, start_time, end_time, group_status, play_start_time, play_end_time) VALUES (%s, %s, %s, %s, %s, %s, %s, 'recruiting', %s, %s) """, (user_id, request.group_name, request.description, request.max_members, start_time.date(), start_time, end_time, play_start_time, play_end_time)) # 获取新创建的群组ID group_id = cursor.lastrowid # 加入群组 cursor.execute(""" INSERT INTO group_members (group_id, user_id) VALUES (%s, %s) """, (group_id, user_id)) conn.commit() return {"message": "群组创建成功"} finally: cursor.close() conn.close() @router.post("/join") def join_group(request: JoinGroupRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor() # 检查是否已加入 cursor.execute("SELECT 1 FROM group_members WHERE group_id = %s AND user_id = %s", (request.group_id, user_id)) if cursor.fetchone(): raise HTTPException(400, "已加入该群组") # 检查群组状态和人数 cursor.execute(""" SELECT max_members, play_start_time FROM game_groups WHERE group_id = %s """, (request.group_id,)) group_info = cursor.fetchone() # 获取当前实际人数 cursor.execute(""" SELECT COUNT(*) FROM group_members WHERE group_id = %s """, (request.group_id,)) current_members = cursor.fetchone()[0] if not group_info: raise HTTPException(404, "群组不存在") # 已移除的时间限制检查 ↓ if current_members >= group_info[0]: raise HTTPException(400, "群组已满员") # 加入群组 cursor.execute(""" INSERT INTO group_members (group_id, user_id) VALUES (%s, %s) """, (request.group_id, user_id)) # 自动更新群组状态 if current_members + 1 == group_info[0]: cursor.execute(""" UPDATE game_groups SET group_status = 'full' WHERE group_id = %s """, (request.group_id,)) conn.commit() return {"message": "加入成功"} finally: cursor.close() conn.close() @router.post("/members") def get_group_members(request: GroupOperationRequest): conn = get_connection() try: cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT u.user_id, u.username FROM group_members gm JOIN users u ON gm.user_id = u.user_id WHERE gm.group_id = %s """, (request.group_id,)) return cursor.fetchall() finally: cursor.close() conn.close() @router.post("/managed") def my_managed_groups(request: BaseTokenRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT g.group_id, g.group_name, g.start_date, g.start_time, g.end_time, g.group_status, g.max_members, g.play_start_time, g.play_end_time, (SELECT COUNT(*) FROM group_members WHERE group_id = g.group_id) AS current_members FROM game_groups g WHERE user_id = %s AND group_status IN ('recruiting', 'full', 'pause') """, (user_id,)) return cursor.fetchall() finally: cursor.close() conn.close() # 退出群组接口 @router.post("/leave") def leave_group(request: LeaveGroupRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor() # 获取群组信息 cursor.execute(""" SELECT g.end_time, g.group_status, g.max_members FROM game_groups g WHERE g.group_id = %s """, (request.group_id,)) group_info = cursor.fetchone() if not group_info: raise HTTPException(404, "群组不存在") end_time, status, max_members = group_info # 检查退出时间限制 current_time = datetime.now(timezone(timedelta(hours=8))).replace(tzinfo=None) if current_time >= end_time - timedelta(hours=2): raise HTTPException(400, "距离招募结束不足2小时,无法退出") # 删除成员记录 cursor.execute(""" DELETE FROM group_members WHERE group_id = %s AND user_id = %s """, (request.group_id, user_id)) if cursor.rowcount == 0: raise HTTPException(404, "未加入该群组") # 如果原状态是满员,检查并更新状态 if status == 'full': cursor.execute(""" SELECT COUNT(*) FROM group_members WHERE group_id = %s """, (request.group_id,)) current_members = cursor.fetchone()[0] if current_members < max_members: cursor.execute(""" UPDATE game_groups SET group_status = 'recruiting' WHERE group_id = %s """, (request.group_id,)) conn.commit() return {"message": "已成功退出群组"} finally: cursor.close() conn.close() # 删除群组接口 @router.post("/delete") def delete_group(request: DeleteGroupRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor() # 验证管理员权限 cursor.execute(""" SELECT 1 FROM game_groups WHERE group_id = %s AND user_id = %s """, (request.group_id, user_id)) if not cursor.fetchone(): raise HTTPException(403, "只有群主可以删除群组") # 获取结束时间并检查时间限制 cursor.execute(""" SELECT end_time, group_status FROM game_groups WHERE group_id = %s """, (request.group_id,)) result = cursor.fetchone() if not result: raise HTTPException(404, "群组不存在") end_time, group_status = result current_time = datetime.now(timezone(timedelta(hours=8))).replace(tzinfo=None) if group_status != 'pause' and current_time >= end_time - timedelta(hours=1): raise HTTPException(400, "距离招募结束不足1小时,无法解散群组") # 删除关联成员 cursor.execute("DELETE FROM group_members WHERE group_id = %s", (request.group_id,)) # 删除群组 cursor.execute("DELETE FROM game_groups WHERE group_id = %s", (request.group_id,)) conn.commit() return {"message": "群组已删除"} finally: cursor.close() conn.close() # 获取我加入的群组 @router.post("/joined") def my_joined_groups(request: BaseTokenRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor(dictionary=True) cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT g.group_id, g.group_name, g.start_date, g.start_time, g.end_time, g.group_status, g.max_members, g.play_start_time, g.play_end_time, (SELECT COUNT(*) FROM group_members WHERE group_id = g.group_id) AS current_members FROM game_groups g JOIN group_members m ON g.group_id = m.group_id WHERE m.user_id = %s AND g.user_id != %s -- 新增排除创建者的条件 AND g.group_status IN ('recruiting', 'full', 'pause') """, (user_id, user_id)) # 第二个user_id参数用于排除创建者 return cursor.fetchall() finally: cursor.close() conn.close() # 获取所有活跃群组 @router.get("/active") def get_all_active_groups(): conn = get_connection() try: cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT group_id, group_name, start_date, start_time, end_time, group_status, max_members, play_start_time, play_end_time, (SELECT COUNT(*) FROM group_members WHERE group_id = g.group_id) AS current_members FROM game_groups g WHERE group_status IN ('recruiting') ORDER BY start_time ASC """) return cursor.fetchall() finally: cursor.close() conn.close() @router.post("/description") def get_group_description(request: GroupOperationRequest): conn = get_connection() try: cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT description FROM game_groups WHERE group_id = %s """, (request.group_id,)) result = cursor.fetchone() if not result: raise HTTPException(404, "群组不存在") return {"description": result['description']} finally: cursor.close() conn.close() @router.post("/update_description") def update_group_description(request: UpdateDescriptionRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor() # 验证管理员权限 cursor.execute(""" SELECT 1 FROM game_groups WHERE group_id = %s AND user_id = %s """, (request.group_id, user_id)) if not cursor.fetchone(): raise HTTPException(403, "无权限修改该群组") # 执行更新 cursor.execute(""" UPDATE game_groups SET description = %s WHERE group_id = %s """, (request.new_description, request.group_id)) conn.commit() return {"message": "群组简介已更新"} finally: cursor.close() conn.close() def _update_group_status(): conn = get_connection() try: cursor = conn.cursor() now = datetime.now() cursor.execute(""" UPDATE game_groups SET group_status = 'pause' WHERE group_status IN ('recruiting', 'full') AND end_time < %s """, (now,)) conn.commit() finally: cursor.close() conn.close() # 在已有路由后添加新接口 @router.get("/check_expired_groups") def check_expired_groups(): conn = get_connection() try: cursor = conn.cursor() now = datetime.now() # 更新已满员的群组状态 cursor.execute(""" UPDATE game_groups SET group_status = 'full' WHERE group_status = 'recruiting' AND (SELECT COUNT(*) FROM group_members WHERE group_id = game_groups.group_id) >= max_members AND end_time > %s -- 仅处理未过期的群组 """, (now,)) full_count = cursor.rowcount # 更新已过期的群组状态 cursor.execute(""" UPDATE game_groups SET group_status = 'pause' WHERE group_status IN ('recruiting', 'full') AND end_time < %s """, (now,)) expired_count = cursor.rowcount conn.commit() return { "updated_to_full": full_count, "updated_to_pause": expired_count } finally: cursor.close() conn.close() # 在已有路由后添加新接口 @router.post("/group_details") def get_group_details(request: GroupDetailsRequest): user_id = _get_user_id(request.token) conn = get_connection() try: cursor = conn.cursor(dictionary=True) # 获取群组详情及创建者ID cursor.execute(""" SELECT g.*, u.username as creator_name, (SELECT COUNT(*) FROM group_members WHERE group_id = g.group_id) AS current_members FROM game_groups g JOIN users u ON g.user_id = u.user_id WHERE g.group_id = %s """, (request.group_id,)) group_info = cursor.fetchone() if not group_info: raise HTTPException(404, "群组不存在") # 判断是否为创建者 is_creator = group_info['user_id'] == user_id is_joined = False if not is_creator: cursor.execute(""" SELECT 1 FROM group_members WHERE group_id = %s AND user_id = %s """, (request.group_id, user_id)) is_joined = cursor.fetchone() is not None # 构建返回数据 return { "group_name": group_info['group_name'], "description": group_info['description'], "start_date": group_info['start_date'], "start_time": group_info['start_time'], "end_time": group_info['end_time'], "play_start_time": group_info['play_start_time'], "play_end_time": group_info['play_end_time'], "group_status": group_info['group_status'], "max_members": group_info['max_members'], "current_members": group_info['current_members'], "creator_name": group_info['creator_name'], "is_creator": is_creator, "is_joined": is_joined if not is_creator else None # 创建者不返回加入状态 } finally: cursor.close() conn.close()