222 lines
8.3 KiB
Python
222 lines
8.3 KiB
Python
from fastapi import HTTPException
|
||
from datetime import datetime
|
||
from decimal import Decimal, ROUND_HALF_UP
|
||
from ..db import get_connection
|
||
from ..utils.jwt_handler import verify_token
|
||
import configparser
|
||
from datetime import datetime
|
||
import pytz
|
||
|
||
# 读取配置文件
|
||
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 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 o.start_datetime, t.price AS table_price,
|
||
COALESCE(g.price, 1) AS game_price, o.num_players -- 处理空值情况
|
||
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()
|
||
|
||
# 将数据库中的时间和输入的时间转换为 naive(移除时区信息)
|
||
db_start = order['start_datetime'].replace(tzinfo=None)
|
||
input_end = end_datetime.replace(tzinfo=None)
|
||
|
||
# 计算游戏时长(分钟),duration_minutes 是 float 类型
|
||
duration_minutes = (input_end - db_start).total_seconds() / 60
|
||
# 将时长转换为 Decimal 类型,避免与 Decimal 相乘时报错
|
||
duration_minutes_dec = Decimal(str(duration_minutes))
|
||
|
||
# 计算原始价格(假设 table_price 单位为每分钟价格)
|
||
base_price = (
|
||
order['table_price'] *
|
||
order['game_price'] *
|
||
order['num_players'] *
|
||
duration_minutes_dec *
|
||
unit_price
|
||
)
|
||
print(f"Base Price: {base_price}")
|
||
# 更新订单信息
|
||
cursor.execute("""
|
||
UPDATE orders
|
||
SET end_datetime = %s,
|
||
payable_price = %s,
|
||
game_process_time = %s,
|
||
order_status = 'pending'
|
||
WHERE order_id = %s
|
||
""", (end_datetime, base_price, duration_minutes_dec, order_id))
|
||
|
||
connection.commit()
|
||
return {"message": "订单已结束待结算", "base_price": base_price}
|
||
|
||
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()
|