328 lines
11 KiB
Python
328 lines
11 KiB
Python
from fastapi import HTTPException
|
||
from datetime import datetime, timedelta
|
||
from decimal import Decimal
|
||
from ..db import get_connection
|
||
from ..utils.jwt_handler import verify_token
|
||
import configparser
|
||
|
||
config = configparser.ConfigParser()
|
||
config.read('backend/config.conf')
|
||
unit_price = Decimal(config.get('price', 'unit_price'))
|
||
points_rate = Decimal(config.get('price', 'points_rate'))
|
||
get_points_rate = Decimal(config.get('price', 'get_points_rate'))
|
||
|
||
def get_user_active_order(token: str):
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
print(f"Verified phone_number: {phone_number}")
|
||
except ValueError as e:
|
||
raise HTTPException(401, str(e))
|
||
"""获取用户进行中订单"""
|
||
connection = get_connection()
|
||
try:
|
||
cursor = connection.cursor(dictionary=True)
|
||
cursor.execute("""
|
||
SELECT
|
||
o.order_id,
|
||
o.start_datetime,
|
||
gt.game_table_number,
|
||
o.num_players
|
||
FROM users u
|
||
INNER JOIN orders o ON u.user_id = o.user_id
|
||
INNER JOIN game_tables gt ON o.game_table_id = gt.table_id
|
||
WHERE
|
||
u.phone_number = %s
|
||
AND o.order_status = 'in_progress'
|
||
ORDER BY o.start_datetime DESC
|
||
LIMIT 1;
|
||
""", (phone_number,))
|
||
return cursor.fetchone() or {}
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
finally:
|
||
cursor.close()
|
||
connection.close()
|
||
|
||
def create_user_order(token: str, table_id: int, num_players: int):
|
||
"""创建用户订单"""
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
except ValueError as e:
|
||
raise HTTPException(status_code=401, detail=str(e))
|
||
|
||
connection = get_connection()
|
||
try:
|
||
cursor = connection.cursor()
|
||
|
||
cursor = connection.cursor()
|
||
# 新增用户订单状态检查
|
||
cursor.execute("""
|
||
SELECT order_status
|
||
FROM orders
|
||
WHERE user_id = (SELECT user_id FROM users WHERE phone_number = %s)
|
||
AND order_status IN ('in_progress', 'pending')
|
||
LIMIT 1
|
||
""", (phone_number,))
|
||
existing_order = cursor.fetchone()
|
||
if existing_order:
|
||
status = existing_order[0]
|
||
if status == 'in_progress':
|
||
raise HTTPException(status_code=400, detail="存在进行中的订单")
|
||
raise HTTPException(status_code=400, detail="存在未付款的订单")
|
||
|
||
# 获取当前最大订单号
|
||
cursor.execute("SELECT MAX(order_id) FROM orders")
|
||
max_order_id = cursor.fetchone()[0]
|
||
|
||
# 生成订单号(从100开始)
|
||
order_id = 100 if max_order_id is None else int(max_order_id) + 1
|
||
print("order_id:", order_id)
|
||
# 验证用户存在
|
||
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (phone_number,))
|
||
user = cursor.fetchone()
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="用户不存在")
|
||
|
||
# 检查桌子占用
|
||
cursor.execute("""
|
||
SELECT EXISTS(
|
||
SELECT 1 FROM orders
|
||
WHERE game_table_id = %s
|
||
AND order_status = 'in_progress'
|
||
)""", (table_id,))
|
||
if cursor.fetchone()[0]:
|
||
raise HTTPException(status_code=400, detail="桌子已被占用")
|
||
|
||
# 创建订单
|
||
order_date = datetime.now().date()
|
||
start_datetime = datetime.now()
|
||
|
||
cursor.execute("""
|
||
INSERT INTO orders (
|
||
order_id, user_id, game_table_id,
|
||
order_date, start_datetime, num_players,
|
||
order_status
|
||
) VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||
""", (order_id, user[0], table_id, order_date, start_datetime, num_players, 'in_progress'))
|
||
|
||
connection.commit()
|
||
return {"order_id": order_id, "message": "订单创建成功"}
|
||
except Exception as e:
|
||
connection.rollback()
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
finally:
|
||
cursor.close()
|
||
connection.close()
|
||
|
||
|
||
def complete_user_order(token: str, order_id: int):
|
||
"""完成用户订单"""
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
except ValueError as e:
|
||
raise HTTPException(status_code=401, detail=str(e))
|
||
|
||
connection = get_connection()
|
||
try:
|
||
cursor = connection.cursor(dictionary=True)
|
||
# 修改后的SQL查询
|
||
cursor.execute("""
|
||
SELECT
|
||
o.user_id,
|
||
t.price AS table_price,
|
||
COALESCE(g.price, 1.0) AS game_price,
|
||
o.num_players,
|
||
o.start_datetime
|
||
FROM orders o
|
||
JOIN game_tables t ON o.game_table_id = t.table_id
|
||
LEFT JOIN games g ON o.game_id = g.game_id
|
||
WHERE o.order_id = %s
|
||
FOR UPDATE
|
||
""", (order_id,))
|
||
order = cursor.fetchone()
|
||
|
||
if not order:
|
||
raise HTTPException(status_code=404, detail="订单不存在")
|
||
|
||
# 验证用户权限
|
||
cursor.execute("SELECT user_id FROM users WHERE phone_number = %s", (phone_number,))
|
||
user = cursor.fetchone()
|
||
if order['user_id'] != user['user_id']:
|
||
raise HTTPException(status_code=403, detail="无权操作此订单")
|
||
|
||
# 计算时长(优化精度处理)
|
||
end_datetime = datetime.now()
|
||
duration = end_datetime - order['start_datetime']
|
||
duration_minutes = duration.total_seconds() / 60
|
||
duration_minutes_dec = Decimal(duration_minutes).quantize(Decimal('0.0000'))
|
||
|
||
# 直接使用Decimal类型计算
|
||
unit_price = Decimal('0.1667') # 示例单位价格(需按实际调整)
|
||
base_price = (
|
||
order['table_price'] *
|
||
order['game_price'] *
|
||
order['num_players'] *
|
||
duration_minutes_dec *
|
||
unit_price
|
||
)
|
||
total_price = round(base_price, 2)
|
||
|
||
# 使用Decimal类型更新数据库
|
||
cursor.execute("""
|
||
UPDATE orders SET
|
||
end_datetime = %s,
|
||
payable_price = %s,
|
||
game_process_time = %s,
|
||
order_status = 'pending'
|
||
WHERE order_id = %s
|
||
""", (end_datetime, total_price, duration_minutes_dec, order_id))
|
||
|
||
connection.commit()
|
||
return {"message": "订单已结束待结算", "base_price": float(base_price)}
|
||
except Exception as e:
|
||
connection.rollback()
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
finally:
|
||
cursor.close()
|
||
connection.close()
|
||
|
||
|
||
|
||
def get_earliest_pending_order(token: str):
|
||
"""获取用户最早的pending订单"""
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
except ValueError as e:
|
||
raise HTTPException(401, detail=str(e))
|
||
|
||
connection = get_connection()
|
||
try:
|
||
with connection.cursor(dictionary=True) as cursor:
|
||
# 查询最早pending订单
|
||
cursor.execute("""
|
||
SELECT o.order_id
|
||
FROM users u
|
||
JOIN orders o ON u.user_id = o.user_id
|
||
WHERE u.phone_number = %s
|
||
AND o.order_status = 'pending'
|
||
ORDER BY o.start_datetime ASC
|
||
LIMIT 1
|
||
""", (phone_number,))
|
||
result = cursor.fetchone()
|
||
return {"order_id": result["order_id"]} if result else {}
|
||
except Exception as e:
|
||
raise HTTPException(500, detail=str(e))
|
||
finally:
|
||
connection.close()
|
||
|
||
def get_order_detail_with_points(token: str, order_id: int):
|
||
"""获取订单详情及用户积分"""
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
except ValueError as e:
|
||
raise HTTPException(401, detail=str(e))
|
||
|
||
connection = get_connection()
|
||
try:
|
||
with connection.cursor(dictionary=True) as cursor:
|
||
# 验证订单归属
|
||
cursor.execute("""
|
||
SELECT
|
||
o.order_id,
|
||
gt.game_table_number,
|
||
o.order_date,
|
||
o.start_datetime AS start_time,
|
||
o.end_datetime AS end_time,
|
||
o.game_process_time,
|
||
o.num_players,
|
||
o.payable_price,
|
||
u.points
|
||
FROM orders o
|
||
JOIN game_tables gt ON o.game_table_id = gt.table_id
|
||
JOIN users u ON o.user_id = u.user_id
|
||
WHERE o.order_id = %s
|
||
AND u.phone_number = %s
|
||
""", (order_id, phone_number))
|
||
|
||
result = cursor.fetchone()
|
||
if not result:
|
||
raise HTTPException(404, detail="订单不存在或无权访问")
|
||
|
||
# 格式化日期字段
|
||
result['order_date'] = result['order_date'].isoformat()
|
||
result['start_time'] = result['start_time'].isoformat()
|
||
result['end_time'] = result['end_time'].isoformat() if result['end_time'] else None
|
||
|
||
return {
|
||
"order_id": result["order_id"],
|
||
"game_table_number": result["game_table_number"],
|
||
"order_date": result["order_date"],
|
||
"start_time": result["start_time"],
|
||
"end_time": result["end_time"],
|
||
"num_players": result["num_players"],
|
||
"payable_price": float(result["payable_price"]),
|
||
"game_process_time": result["game_process_time"],
|
||
"points": result["points"]
|
||
}
|
||
except Exception as e:
|
||
raise HTTPException(500, detail=str(e))
|
||
finally:
|
||
connection.close()
|
||
|
||
|
||
def preview_price_adjustment(token: str, order_id: int, used_points: int):
|
||
"""预览价格调整(不修改数据库)"""
|
||
try:
|
||
payload = verify_token(token)
|
||
phone_number = payload["sub"]
|
||
except ValueError as e:
|
||
raise HTTPException(401, detail=str(e))
|
||
|
||
connection = get_connection()
|
||
try:
|
||
with connection.cursor(dictionary=True) as cursor:
|
||
# 获取用户当前积分
|
||
cursor.execute("""
|
||
SELECT u.user_id, u.points
|
||
FROM users u
|
||
WHERE u.phone_number = %s
|
||
""", (phone_number,))
|
||
user = cursor.fetchone()
|
||
if not user:
|
||
raise HTTPException(404, "用户不存在")
|
||
|
||
if user['points'] < used_points:
|
||
raise HTTPException(400, "积分不足")
|
||
|
||
# 获取原始价格
|
||
cursor.execute("""
|
||
SELECT payable_price
|
||
FROM orders
|
||
WHERE order_id = %s
|
||
AND user_id = %s
|
||
AND order_status = 'pending'
|
||
""", (order_id, user['user_id']))
|
||
order = cursor.fetchone()
|
||
if not order:
|
||
raise HTTPException(404, "订单不存在或不可修改")
|
||
|
||
# 计算新价格(仅预览)
|
||
points_value = used_points * points_rate
|
||
new_price = max(order['payable_price'] - points_value, Decimal('0'))
|
||
|
||
return {
|
||
"new_price": float(new_price),
|
||
}
|
||
|
||
except HTTPException as e:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(500, f"计算失败: {str(e)}")
|
||
finally:
|
||
connection.close()
|