table_game/backend/app/services/admin_order_service.py
2025-03-10 08:35:19 +08:00

222 lines
8.3 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
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()