220 lines
7.8 KiB
Python
220 lines
7.8 KiB
Python
from fastapi import HTTPException
|
|
from decimal import Decimal
|
|
from ..db import get_connection
|
|
from ..utils.jwt_handler import verify_token
|
|
import configparser
|
|
from datetime import datetime
|
|
import pytz
|
|
from ..utils import calculate_price
|
|
|
|
# 读取配置文件
|
|
config = configparser.ConfigParser()
|
|
config.read('backend/config.conf')
|
|
points_rate = Decimal(config.get('price', 'points_rate'))
|
|
get_points_rate = Decimal(config.get('price', 'get_points_rate'))
|
|
|
|
def verify_admin_permission(token: str):
|
|
"""公共权限验证方法"""
|
|
try:
|
|
payload = verify_token(token)
|
|
username = payload["sub"]
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=401, detail=str(e))
|
|
|
|
connection = get_connection()
|
|
try:
|
|
cursor = connection.cursor(dictionary=True)
|
|
cursor.execute("SELECT user_type FROM users WHERE username = %s;", (username,))
|
|
admin_user = cursor.fetchone()
|
|
|
|
if not admin_user or admin_user["user_type"] != "admin":
|
|
raise HTTPException(status_code=403, detail="Permission denied")
|
|
return username
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
def query_orders_by_status_and_range(token: str, start: int, end: int, order_status: str):
|
|
verify_admin_permission(token)
|
|
|
|
connection = get_connection()
|
|
try:
|
|
cursor = connection.cursor(dictionary=True)
|
|
cursor.execute("""
|
|
SELECT
|
|
o.order_id,
|
|
o.user_id,
|
|
u.username AS user_name, -- 添加用户名
|
|
g.game_table_number AS game_table_number, -- 添加游戏桌编号
|
|
o.order_date,
|
|
o.num_players,
|
|
o.order_status,
|
|
o.payable_price,
|
|
o.paid_price,
|
|
o.payment_method,
|
|
o.start_datetime,
|
|
o.end_datetime,
|
|
o.used_points, -- 使用的积分
|
|
o.coupon_id, -- 使用的优惠券
|
|
o.game_process_time, -- 总游戏时间
|
|
o.settlement_time
|
|
FROM orders o
|
|
LEFT JOIN users u ON o.user_id = u.user_id
|
|
LEFT JOIN game_tables g ON o.game_table_id = g.table_id
|
|
WHERE order_status = %s
|
|
ORDER BY o.order_id ASC
|
|
LIMIT %s OFFSET %s
|
|
""", (order_status, end - start + 1, start - 1))
|
|
result = cursor.fetchall()
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM orders WHERE order_status = %s", (order_status,))
|
|
total = cursor.fetchone()["COUNT(*)"]
|
|
|
|
return {"orders": result, "total": total}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Error during query: {str(e)}")
|
|
finally:
|
|
cursor.close()
|
|
connection.close()
|
|
|
|
def complete_order(token: str, order_id: int, end_datetime: datetime):
|
|
"""结束订单并计算最终价格"""
|
|
verify_admin_permission(token)
|
|
|
|
connection = get_connection()
|
|
try:
|
|
with connection.cursor(dictionary=True) as cursor:
|
|
# 查询订单基础信息(确保订单存在并锁定)
|
|
cursor.execute("""
|
|
SELECT start_datetime
|
|
FROM orders
|
|
WHERE order_id = %s
|
|
FOR UPDATE
|
|
""", (order_id,))
|
|
order = cursor.fetchone()
|
|
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="订单不存在")
|
|
|
|
# 处理时间
|
|
db_start = order['start_datetime'].replace(tzinfo=None)
|
|
input_end = end_datetime.replace(tzinfo=None)
|
|
|
|
# 计算游戏时长(分钟)
|
|
duration_minutes = (input_end - db_start).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 settle_order(token: str, order_id: int, used_points: int = 0, coupon_id: int = None):
|
|
"""结算最终订单"""
|
|
verify_admin_permission(token)
|
|
|
|
connection = get_connection()
|
|
try:
|
|
with connection.cursor(dictionary=True) as cursor:
|
|
cursor.execute("""
|
|
SELECT payable_price, user_id
|
|
FROM orders
|
|
WHERE order_id = %s
|
|
AND order_status = 'pending'
|
|
FOR UPDATE
|
|
""", (order_id,))
|
|
order = cursor.fetchone()
|
|
|
|
|
|
points_value = used_points * points_rate
|
|
final_price = max(order['payable_price'] - points_value, Decimal('0'))
|
|
settlement_time = datetime.now(pytz.timezone('Asia/Shanghai'))
|
|
|
|
# 更新订单状态
|
|
cursor.execute("""
|
|
UPDATE orders
|
|
SET paid_price = %s,
|
|
used_points = %s,
|
|
coupon_id = %s,
|
|
order_status = 'completed',
|
|
payment_method = 'offline',
|
|
settlement_time = %s
|
|
WHERE order_id = %s
|
|
""", (final_price, used_points, coupon_id, settlement_time.strftime("%Y-%m-%d %H:%M:%S"), order_id))
|
|
|
|
if used_points > 0:
|
|
earned_points = 0
|
|
else:
|
|
earned_points = int(final_price * get_points_rate)
|
|
|
|
# 更新用户积分
|
|
cursor.execute("""
|
|
UPDATE users
|
|
SET points = points - %s + %s
|
|
WHERE user_id = %s
|
|
""", (used_points, earned_points, order['user_id']))
|
|
|
|
# 插入积分变动记录
|
|
change_amount = earned_points - used_points
|
|
reason = f"订单结算,订单号:{order_id}"
|
|
cursor.execute("""
|
|
INSERT INTO points_history (user_id, change_amount, reason)
|
|
VALUES (%s, %s, %s)
|
|
""", (order['user_id'], change_amount, reason))
|
|
|
|
connection.commit()
|
|
return {"message": "订单结算成功", "final_price": final_price}
|
|
|
|
except Exception as e:
|
|
connection.rollback()
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
finally:
|
|
connection.close()
|
|
|
|
# 在适当位置新增服务层方法
|
|
def get_order_details_service(token: str, order_id: int):
|
|
"""服务层:获取订单详情"""
|
|
verify_admin_permission(token) # 复用已有的权限验证方法
|
|
|
|
connection = get_connection()
|
|
if not connection:
|
|
raise HTTPException(status_code=500, detail="Database connection failed!")
|
|
|
|
try:
|
|
with connection.cursor(dictionary=True) as cursor:
|
|
cursor.execute("""
|
|
SELECT o.*, t.price AS table_price, g.price AS game_price
|
|
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
|
|
""", (order_id,))
|
|
result = cursor.fetchone()
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Order not found")
|
|
return result
|
|
finally:
|
|
connection.close()
|