535 lines
17 KiB
Python
535 lines
17 KiB
Python
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()
|