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()