table_game/backend/app/services/user_order_service.py

319 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
from ..utils import calculate_price
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'))
strategy_id = int(config.get('now_price', 'strategy_id'))
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, pricing_strategy_id
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
""", (order_id, user[0], table_id, order_date, start_datetime, num_players, 'in_progress', strategy_id))
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:
with connection.cursor(dictionary=True) as cursor:
# 获取订单信息
cursor.execute("""
SELECT o.user_id, o.start_datetime
FROM orders o
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 not user or order['user_id'] != user['user_id']:
raise HTTPException(status_code=403, detail="无权操作此订单")
# 计算订单时长
end_datetime = datetime.now()
duration_minutes = (end_datetime - order['start_datetime']).total_seconds() / 60
duration_minutes_dec = Decimal(str(duration_minutes))
# 先更新订单状态
cursor.execute("""
UPDATE orders
SET end_datetime = %s,
game_process_time = %s,
order_status = 'pending'
WHERE order_id = %s
""", (end_datetime, duration_minutes_dec, order_id))
# 提交事务,确保订单结束
connection.commit()
# 调用价格计算逻辑(会自动写入数据库)
success = calculate_price.calculate_order_price(order_id)
if not success:
raise HTTPException(status_code=500, detail="订单价格计算失败")
return {"message": "订单已结束待结算"}
except Exception as e:
connection.rollback()
raise HTTPException(status_code=500, detail=str(e))
finally:
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()