diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..e00f681 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306/tg01 + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py index 7acd46a..f4d0231 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -20,6 +20,7 @@ from .routers import user_coupon from .routers import admin_message from .routers import user_messages from .routers import bell +from .routers import admin_strategy @@ -61,6 +62,7 @@ app.include_router(admin_coupon.router, prefix="/admin", tags=["Admin-Coupon"]) app.include_router(admin_message.router, prefix="/admin", tags=["Admin-Message"]) +app.include_router(admin_strategy.router, prefix="/admin", tags=["Admin-Strategy"]) diff --git a/backend/app/routers/admin_strategy.py b/backend/app/routers/admin_strategy.py new file mode 100644 index 0000000..b03ff5a --- /dev/null +++ b/backend/app/routers/admin_strategy.py @@ -0,0 +1,57 @@ +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel +from typing import List +from ..services.admin_strategy_service import ( + get_table_strategy_list, + create_table_strategy, + delete_table_strategy, + bind_table_to_strategy +) + +router = APIRouter() + +class get_list(BaseModel): + token: str + +class StrategyData(BaseModel): + strategy_name: str + segment1_threshold: int + segment1_price: float + segment2_threshold: int + segment2_price: float + segment3_price: float + description: str | None = None + segment3_threshold: int | None = None + +class create_strategy(BaseModel): + token: str + strategy_data: StrategyData + +class delete_strategy(BaseModel): + token: str + strategy_id: int +class update_strategy(BaseModel): + token: str + table_ids: List[int] + strategy_id: int + + +@router.post("/table-strategies/list") +def api_get_table_strategy_list(request: get_list): + return get_table_strategy_list(request.token) + + +@router.post("/table-strategies/create") +def api_create_table_strategy(request: create_strategy): + return create_table_strategy(request.token, request.strategy_data.model_dump()) + + +@router.post("/table-strategies/delete") +def api_delete_table_strategy(request: delete_strategy): + return delete_table_strategy(request.token, request.strategy_id) + + +@router.post("/table-strategies/bind") +def api_bind_table_to_strategy(request: update_strategy): + return bind_table_to_strategy(request.token, request.strategy_id, request.table_ids) + diff --git a/backend/app/schemas/table_info.py b/backend/app/schemas/table_info.py index a63d0c9..9eeac87 100644 --- a/backend/app/schemas/table_info.py +++ b/backend/app/schemas/table_info.py @@ -3,16 +3,17 @@ from pydantic import BaseModel class TableCreate(BaseModel): game_table_number: str capacity: int - price: float + strategy_id: int + strategy_name: str class TableCreateRequest(BaseModel): token: str game_table_number: str capacity: int - price: float + strategy_id: int class TableResponse(BaseModel): table_id: int game_table_number: str capacity: int - price: float + strategy_name: str diff --git a/backend/app/services/admin_strategy_service.py b/backend/app/services/admin_strategy_service.py new file mode 100644 index 0000000..d5477ca --- /dev/null +++ b/backend/app/services/admin_strategy_service.py @@ -0,0 +1,145 @@ +from fastapi import HTTPException +from ..db import get_connection +from ..utils.jwt_handler import verify_token + +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 get_table_strategy_list(token: str): + """ + 获取 table 的策略列表 + """ + _verify_admin_permission(token) + print("get_table_strategy_list") + connection = get_connection() + cursor = None + try: + cursor = connection.cursor(dictionary=True) + cursor.execute("SELECT * FROM table_pricing_strategies") + strategies = cursor.fetchall() + return strategies + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + finally: + if cursor: + cursor.close() + connection.close() + + +def create_table_strategy(token: str, strategy_data: dict): + """ + 创建 table 的策略 + """ + _verify_admin_permission(token) + connection = get_connection() + cursor = None + + required_fields = ['strategy_name', 'segment1_threshold', 'segment1_price', + 'segment2_threshold', 'segment2_price', 'segment3_price'] + missing_fields = [field for field in required_fields if field not in strategy_data] + if missing_fields: + raise HTTPException(status_code=400, detail=f"缺少必填字段: {', '.join(missing_fields)}") + + try: + cursor = connection.cursor(dictionary=True) + cursor.execute(""" + INSERT INTO table_pricing_strategies ( + strategy_name, + description, + segment1_threshold, + segment1_price, + segment2_threshold, + segment2_price, + segment3_price, + segment3_threshold + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + """, ( + strategy_data['strategy_name'], + strategy_data.get('description'), # 可选字段 + strategy_data['segment1_threshold'], + strategy_data['segment1_price'], + strategy_data['segment2_threshold'], + strategy_data['segment2_price'], + strategy_data['segment3_price'], + strategy_data.get('segment3_threshold') # 可选字段 + )) + connection.commit() + return {"message": "Table 策略创建成功"} + except Exception as e: + connection.rollback() + raise HTTPException(status_code=500, detail=str(e)) + finally: + if cursor: + cursor.close() + connection.close() + + + +def delete_table_strategy(token: str, strategy_id: int): + """ + 删除 table 的策略 + """ + _verify_admin_permission(token) + connection = get_connection() + cursor = None + try: + cursor = connection.cursor(dictionary=True) + # 检查当前策略是否被使用 + cursor.execute("SELECT COUNT(*) as count FROM game_tables WHERE table_pricing_strategy_id = %s", (strategy_id,)) + result = cursor.fetchone() + if result['count'] > 0: + raise HTTPException(status_code=400, detail="该策略正在被使用,不可删除") + # 删除策略 + cursor.execute("DELETE FROM table_pricing_strategies WHERE strategy_id = %s", (strategy_id,)) + connection.commit() + return {"message": "Table 策略删除成功"} + except Exception as e: + connection.rollback() + raise HTTPException(status_code=500, detail=str(e)) + finally: + if cursor: + cursor.close() + connection.close() + + +def bind_table_to_strategy(token: str, strategy_id: int, table_ids: list): + """ + 绑定桌子到指定策略 + """ + _verify_admin_permission(token) + connection = get_connection() + cursor = None + try: + cursor = connection.cursor(dictionary=True) + # 批量更新桌子对应的策略 + update_query = "UPDATE game_tables SET table_pricing_strategy_id = %s WHERE table_id = %s" + params = [(strategy_id, table_id) for table_id in table_ids] + + cursor.executemany(update_query, params) + connection.commit() + return {"message": f"成功绑定{len(table_ids)}张桌子到指定策略"} + except Exception as e: + connection.rollback() + raise HTTPException(status_code=500, detail=str(e)) + finally: + if cursor: + cursor.close() + connection.close() diff --git a/backend/app/services/admin_table_service.py b/backend/app/services/admin_table_service.py index 142645b..2ee9166 100644 --- a/backend/app/services/admin_table_service.py +++ b/backend/app/services/admin_table_service.py @@ -30,6 +30,9 @@ def create_table(token: str, table_data: dict) -> dict: try: cursor = connection.cursor(dictionary=True) + # 设置默认价格倍率(原本的价格计算方法,已经弃用,防止报错) + default_price = 1.00 + # 检查桌号是否已存在 cursor.execute( "SELECT COUNT(*) AS count FROM game_tables WHERE game_table_number = %s", @@ -38,9 +41,18 @@ def create_table(token: str, table_data: dict) -> dict: if cursor.fetchone()['count'] > 0: raise HTTPException(status_code=400, detail="桌号已存在") + if 'strategy_id' in table_data and table_data['strategy_id']: + cursor.execute( + "SELECT COUNT(*) AS count FROM table_pricing_strategies WHERE strategy_id = %s", + (table_data['strategy_id'],) + ) + if cursor.fetchone()['count'] == 0: + raise HTTPException(status_code=400, detail="指定的策略不存在") + cursor.execute( - "INSERT INTO game_tables (game_table_number, capacity, price) VALUES (%s, %s, %s)", - (table_data['game_table_number'], table_data['capacity'], table_data['price']) + "INSERT INTO game_tables (game_table_number, capacity, price, table_pricing_strategy_id) VALUES (%s, %s, %s, %s)", + ( + table_data['game_table_number'], table_data['capacity'], default_price, table_data.get('strategy_id')) ) connection.commit() return {"message": "桌台创建成功"} @@ -93,13 +105,18 @@ def update_table(token: str, table_id: int, table_data: dict) -> dict: if cursor.fetchone()['count'] > 0: raise HTTPException(status_code=400, detail="桌号已存在") + # 设置默认价格倍率(原本的价格计算方法,已经弃用,防止报错) + default_price = 1.00 cursor.execute( """UPDATE game_tables SET game_table_number=%s, capacity=%s, - price=%s + price=%s, + table_pricing_strategy_id=%s WHERE table_id=%s""", - (table_data['game_table_number'], table_data['capacity'], table_data['price'], table_id) + ( + table_data['game_table_number'], table_data['capacity'], default_price, table_data.get('strategy_id'), + table_id) ) if cursor.rowcount == 0: raise HTTPException(status_code=404, detail="桌台不存在") @@ -119,10 +136,27 @@ def list_tables_service(token: str): cursor = None try: cursor = connection.cursor(dictionary=True) - cursor.execute("SELECT * FROM game_tables") + # 修改查询语句,关联 pricing_strategies 表获取 strategy_name + cursor.execute(""" + SELECT + gt.table_id, + gt.game_table_number, + gt.capacity, + gt.price, + gt.table_pricing_strategy_id, + tps.strategy_name +FROM + game_tables gt +JOIN + table_pricing_strategies tps +ON + gt.table_pricing_strategy_id = tps.strategy_id; + """) return cursor.fetchall() except Exception as e: raise HTTPException(status_code=500, detail=str(e)) finally: - if cursor: cursor.close() + if cursor: + cursor.close() connection.close() + diff --git a/backend/app/services/user_table_service.py b/backend/app/services/user_table_service.py index c67d4ae..b036967 100644 --- a/backend/app/services/user_table_service.py +++ b/backend/app/services/user_table_service.py @@ -38,7 +38,7 @@ def check_table_occupancy_service(table_id: int): ) AS is_occupied """, (table_id,)) result = cursor.fetchone() - return {"is_occupied": "false"} # 不检测桌子状态 + return {"is_occupied": 0} # 不检测桌子状态 except Exception as e: raise HTTPException(status_code=500, detail=str(e)) finally: diff --git a/backend/app/utils/calculate_price.py b/backend/app/utils/calculate_price.py index 6f00d05..07a642a 100644 --- a/backend/app/utils/calculate_price.py +++ b/backend/app/utils/calculate_price.py @@ -44,25 +44,26 @@ def calculate_segmented_price(duration, strategy): if s3 is not None and duration > s3: duration = s3 # 限制最大计费时间 price = (s1 * p1) + ((s2 - s1) * p2) + ((duration - s2) * p3) - return price def calculate_overtime_fee(start_datetime, end_datetime): - """计算超时费用(深夜时段收费)""" + """计算超时费用(深夜时段收费,按整小时计费)""" + # 读取配置文件 config = configparser.ConfigParser() config.read('backend/config.conf') stay_up_late = config['stay_up_late'] start_time = datetime.strptime(stay_up_late['start_time'], '%H:%M:%S').time() end_time = datetime.strptime(stay_up_late['end_time'], '%H:%M:%S').time() - price_per_minute = Decimal(stay_up_late['price_minutes']) + price_per_hour = Decimal(stay_up_late['price_minutes']) # 每小时收费 overtime_fee = Decimal(0) - current_time = start_datetime + current_time = start_datetime.replace(minute=0, second=0, microsecond=0) # 取整到整点 - while current_time < end_datetime: + while current_time + timedelta(hours=1) <= end_datetime: + next_hour = current_time + timedelta(hours=1) if start_time <= current_time.time() or current_time.time() < end_time: - overtime_fee += price_per_minute - current_time += timedelta(minutes=1) + overtime_fee += price_per_hour # 满一小时才计费 + current_time = next_hour # 跳到下一个整点 return overtime_fee @@ -100,22 +101,22 @@ def calculate_order_price(order_id): raise HTTPException(status_code=400, detail="价格策略未找到") # 计算价格 - unit_price = calculate_segmented_price(duration_dec, pricing_strategy) + # unit_price = calculate_segmented_price(duration_dec, pricing_strategy) + # 原本的基础价格废用,桌子价格为主要逻辑 table_price = calculate_segmented_price(duration_dec, table_strategy) - # 计算总价格 - base_price = (unit_price * order['num_players']) + table_price + # base_price = (unit_price * order['num_players']) + table_price + base_price = table_price * order['num_players'] overtime_fee = calculate_overtime_fee(start_time, end_time) total_price = base_price + overtime_fee - + # print("总价: ", unit_price * order['num_players'], " 桌费: ", table_price, " 时长: ", duration_dec, " 超时费: ", overtime_fee) # 更新订单价格 cursor.execute(""" UPDATE orders SET payable_price = %s, - game_process_time = %s, - overtime_fee = %s + game_process_time = %s WHERE order_id = %s - """, (total_price, duration_dec, overtime_fee, order_id)) + """, (total_price, duration_dec, order_id)) connection.commit() return True diff --git a/backend/app/utils/create_database.py b/backend/app/utils/create_database.py index 3ee836c..35af5ee 100644 --- a/backend/app/utils/create_database.py +++ b/backend/app/utils/create_database.py @@ -19,6 +19,23 @@ SQL_SCRIPT = """ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; +-- ---------------------------- +-- Table structure for announcements +-- ---------------------------- +DROP TABLE IF EXISTS `announcements`; +CREATE TABLE `announcements` ( + `id` int NOT NULL AUTO_INCREMENT, + `text` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `start_time` datetime NOT NULL, + `end_time` datetime NOT NULL, + `color` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `created_at` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for coupons +-- ---------------------------- DROP TABLE IF EXISTS `coupons`; CREATE TABLE `coupons` ( `coupon_id` int NOT NULL AUTO_INCREMENT, @@ -26,31 +43,24 @@ CREATE TABLE `coupons` ( `discount_type` enum('fixed','percentage') CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `discount_amount` decimal(10,2) DEFAULT NULL, `min_order_amount` decimal(10,2) DEFAULT NULL, - `valid_from` date DEFAULT NULL, - `valid_to` date DEFAULT NULL, + `valid_from` datetime DEFAULT NULL, + `valid_to` datetime DEFAULT NULL, `is_active` tinyint(1) DEFAULT '1', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `quantity` int DEFAULT NULL, PRIMARY KEY (`coupon_id`), UNIQUE KEY `coupon_code` (`coupon_code`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; - -DROP TABLE IF EXISTS `game_game_tags`; -CREATE TABLE `game_game_tags` ( - `game_tag_relation_id` int NOT NULL AUTO_INCREMENT, - `game_id` int DEFAULT NULL, - `tag_id` int DEFAULT NULL, - PRIMARY KEY (`game_tag_relation_id`), - KEY `game_id` (`game_id`), - KEY `tag_id` (`tag_id`), - CONSTRAINT `game_game_tags_ibfk_1` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`), - CONSTRAINT `game_game_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `game_tags` (`tag_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for game_groups +-- ---------------------------- +DROP TABLE IF EXISTS `game_groups`; CREATE TABLE `game_groups` ( `group_id` int NOT NULL AUTO_INCREMENT, - `user_id` int DEFAULT NULL, - `group_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `user_id` int NOT NULL, + `group_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, `start_date` date DEFAULT NULL, `start_time` datetime DEFAULT NULL, @@ -59,49 +69,43 @@ CREATE TABLE `game_groups` ( `group_status` enum('recruiting','full','completed','cancelled','pause') CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT 'recruiting', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `play_start_time` datetime DEFAULT NULL, + `play_end_time` datetime DEFAULT NULL, PRIMARY KEY (`group_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for game_tables +-- ---------------------------- +DROP TABLE IF EXISTS `game_tables`; CREATE TABLE `game_tables` ( `table_id` int NOT NULL AUTO_INCREMENT COMMENT '服务器桌号', `game_table_number` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '物理桌号', `capacity` int NOT NULL COMMENT '承载人数', `price` decimal(10,2) NOT NULL COMMENT '价格倍率', - PRIMARY KEY (`table_id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; - -CREATE TABLE `announcement` ( - `id` int NOT NULL AUTO_INCREMENT, - `text` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - `start_time` datetime NOT NULL, - `end_time` datetime NOT NULL, - `color` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; - -CREATE TABLE `user_coupons` ( - `user_coupon_id` int NOT NULL AUTO_INCREMENT, - `user_id` int NOT NULL, - `coupon_id` int NOT NULL, - `obtained_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, - `used_at` timestamp NULL DEFAULT NULL, - `is_used` tinyint(1) DEFAULT '0', - `valid_from` date DEFAULT NULL, - `valid_to` date DEFAULT NULL, - PRIMARY KEY (`user_coupon_id`), - UNIQUE KEY `unique_user_coupon` (`user_id`,`coupon_id`), - FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE, - FOREIGN KEY (`coupon_id`) REFERENCES `coupons` (`coupon_id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + `table_pricing_strategy_id` int DEFAULT NULL, + PRIMARY KEY (`table_id`), + KEY `tables_price` (`table_pricing_strategy_id`), + CONSTRAINT `tables_price` FOREIGN KEY (`table_pricing_strategy_id`) REFERENCES `table_pricing_strategies` (`strategy_id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for game_tags +-- ---------------------------- DROP TABLE IF EXISTS `game_tags`; CREATE TABLE `game_tags` ( - `tag_id` int NOT NULL AUTO_INCREMENT, - `tag_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - PRIMARY KEY (`tag_id`), - UNIQUE KEY `tag_name` (`tag_name`) + `game_id` int NOT NULL, + `tag_id` int NOT NULL, + PRIMARY KEY (`game_id`,`tag_id`), + KEY `tag_id` (`tag_id`), + CONSTRAINT `game_tags_ibfk_1` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`), + CONSTRAINT `game_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for games +-- ---------------------------- +DROP TABLE IF EXISTS `games`; CREATE TABLE `games` ( `game_id` int NOT NULL AUTO_INCREMENT, `game_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, @@ -109,17 +113,21 @@ CREATE TABLE `games` ( `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, `min_players` int DEFAULT NULL, `max_players` int DEFAULT NULL, - `duration` int DEFAULT NULL, + `duration` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, `price` decimal(10,2) DEFAULT NULL, `difficulty_level` int DEFAULT NULL, `is_available` tinyint(1) DEFAULT '1', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `quantity` int DEFAULT NULL, - `photo_url` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, + `photo_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `long_description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, PRIMARY KEY (`game_id`) -) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for group_members +-- ---------------------------- DROP TABLE IF EXISTS `group_members`; CREATE TABLE `group_members` ( `group_member_id` int NOT NULL AUTO_INCREMENT, @@ -130,8 +138,11 @@ CREATE TABLE `group_members` ( PRIMARY KEY (`group_member_id`), KEY `group_id` (`group_id`), CONSTRAINT `group_members_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `game_groups` (`group_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for order_coupons +-- ---------------------------- DROP TABLE IF EXISTS `order_coupons`; CREATE TABLE `order_coupons` ( `order_coupon_id` int NOT NULL AUTO_INCREMENT, @@ -144,6 +155,9 @@ CREATE TABLE `order_coupons` ( CONSTRAINT `order_coupons_ibfk_2` FOREIGN KEY (`coupon_id`) REFERENCES `coupons` (`coupon_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for orders +-- ---------------------------- DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `order_id` int NOT NULL COMMENT '订单 id', @@ -156,34 +170,96 @@ CREATE TABLE `orders` ( `num_players` int NOT NULL COMMENT '游玩人数', `payable_price` decimal(10,2) DEFAULT NULL COMMENT '未优惠价格', `order_status` enum('pending','paid','in_progress','completed','cancelled') CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT 'pending' COMMENT '订单状态', - `payment_method` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '优惠后价格', + `payment_method` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '支付平台', `coupon_id` int DEFAULT NULL COMMENT '优惠卷 id', `used_points` int DEFAULT NULL COMMENT '使用积分', `paid_price` decimal(10,2) DEFAULT '0.00' COMMENT '优惠后价格', `game_process_time` int DEFAULT NULL COMMENT '游戏时长', `settlement_time` datetime DEFAULT NULL, + `wx_transaction_id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, + `out_trade_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `pricing_strategy_id` int NOT NULL COMMENT '绑定的价格策略 ID', PRIMARY KEY (`order_id`), KEY `user_id` (`user_id`), KEY `game_table_id` (`game_table_id`), KEY `game_id` (`game_id`), KEY `coupon_id` (`coupon_id`), + KEY `orders_ibfk_4` (`pricing_strategy_id`), CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`), CONSTRAINT `orders_ibfk_2` FOREIGN KEY (`game_table_id`) REFERENCES `game_tables` (`table_id`), - CONSTRAINT `orders_ibfk_3` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`) + CONSTRAINT `orders_ibfk_3` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`), + CONSTRAINT `orders_ibfk_4` FOREIGN KEY (`pricing_strategy_id`) REFERENCES `pricing_strategies` (`strategy_id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for player_messages +-- ---------------------------- +DROP TABLE IF EXISTS `player_messages`; +CREATE TABLE `player_messages` ( + `message_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `message_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`message_id`), + KEY `user_id` (`user_id`), + CONSTRAINT `player_messages_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for player_reviews +-- ---------------------------- +DROP TABLE IF EXISTS `player_reviews`; +CREATE TABLE `player_reviews` ( + `review_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `game_id` int NOT NULL, + `rating` tinyint NOT NULL COMMENT '玩家的打分,范围可以根据实际情况设定,如 1 - 5 星', + `comment` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT '玩家的评论内容,可以为空', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`review_id`), + KEY `user_id` (`user_id`), + KEY `game_id` (`game_id`), + CONSTRAINT `player_reviews_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE, + CONSTRAINT `player_reviews_ibfk_2` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for points_history +-- ---------------------------- DROP TABLE IF EXISTS `points_history`; CREATE TABLE `points_history` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `change_amount` int NOT NULL, - `reason` varchar(255) COLLATE utf8mb4_bin NOT NULL, + `reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), CONSTRAINT `points_history_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for pricing_strategies +-- ---------------------------- +DROP TABLE IF EXISTS `pricing_strategies`; +CREATE TABLE `pricing_strategies` ( + `strategy_id` int NOT NULL AUTO_INCREMENT COMMENT '价格策略 ID', + `strategy_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '策略名称', + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '策略描述', + `segment1_threshold` int NOT NULL COMMENT '第一阶段时间(分钟)', + `segment1_price` decimal(10,2) NOT NULL COMMENT '第一阶段单价(元/人/分钟)', + `segment2_threshold` int NOT NULL COMMENT '第二阶段时间(分钟)', + `segment2_price` decimal(10,2) NOT NULL COMMENT '第二阶段单价(元/人/分钟)', + `segment3_price` decimal(10,2) NOT NULL COMMENT '第三阶段单价(元/人/分钟)', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `segment3_threshold` int DEFAULT NULL COMMENT '第三阶段时间上限(分钟,可选)', + PRIMARY KEY (`strategy_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Table structure for reviews +-- ---------------------------- DROP TABLE IF EXISTS `reviews`; CREATE TABLE `reviews` ( `review_id` int NOT NULL AUTO_INCREMENT, @@ -200,6 +276,77 @@ CREATE TABLE `reviews` ( CONSTRAINT `reviews_ibfk_2` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +-- ---------------------------- +-- Table structure for table_pricing_strategies +-- ---------------------------- +DROP TABLE IF EXISTS `table_pricing_strategies`; +CREATE TABLE `table_pricing_strategies` ( + `strategy_id` int NOT NULL AUTO_INCREMENT COMMENT '桌费策略 ID', + `strategy_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '策略名称', + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '策略描述', + `segment1_threshold` int NOT NULL COMMENT '第一阶段时间(分钟)', + `segment1_price` decimal(10,2) NOT NULL COMMENT '第一阶段单价(元/分钟)', + `segment2_threshold` int NOT NULL COMMENT '第二阶段时间(分钟)', + `segment2_price` decimal(10,2) NOT NULL COMMENT '第二阶段单价(元/分钟)', + `segment3_price` decimal(10,2) NOT NULL COMMENT '第三阶段单价(元/分钟)', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `segment3_threshold` int DEFAULT NULL COMMENT '第三阶段时间上限(分钟,可选)', + PRIMARY KEY (`strategy_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Table structure for tags +-- ---------------------------- +DROP TABLE IF EXISTS `tags`; +CREATE TABLE `tags` ( + `tag_id` int NOT NULL AUTO_INCREMENT, + `tag_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`tag_id`), + UNIQUE KEY `tag_name` (`tag_name`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for user_coupons +-- ---------------------------- +DROP TABLE IF EXISTS `user_coupons`; +CREATE TABLE `user_coupons` ( + `user_coupon_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `coupon_id` int NOT NULL, + `obtained_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `used_at` timestamp NULL DEFAULT NULL, + `is_used` tinyint(1) DEFAULT '0', + `valid_from` date DEFAULT NULL, + `valid_to` date DEFAULT NULL, + PRIMARY KEY (`user_coupon_id`), + KEY `coupon_id` (`coupon_id`), + KEY `idx_user_status` (`user_id`,`is_used`), + CONSTRAINT `user_coupons_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE, + CONSTRAINT `user_coupons_ibfk_2` FOREIGN KEY (`coupon_id`) REFERENCES `coupons` (`coupon_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for user_game_rating +-- ---------------------------- +DROP TABLE IF EXISTS `user_game_rating`; +CREATE TABLE `user_game_rating` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `game_id` int NOT NULL, + `rating` tinyint(1) NOT NULL COMMENT '拉为 1,踩为 0', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `game_id` (`game_id`), + CONSTRAINT `user_game_rating_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE, + CONSTRAINT `user_game_rating_ibfk_2` FOREIGN KEY (`game_id`) REFERENCES `games` (`game_id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ---------------------------- +-- Table structure for users +-- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `user_id` int NOT NULL AUTO_INCREMENT, @@ -212,11 +359,13 @@ CREATE TABLE `users` ( `points` int DEFAULT '0', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `wx_openid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, PRIMARY KEY (`user_id`), UNIQUE KEY `phone_number` (`phone_number`) -) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; SET FOREIGN_KEY_CHECKS = 1; + """ diff --git a/frontend/__init__.py b/frontend/__init__.py index c48e776..ea0a3c2 100644 --- a/frontend/__init__.py +++ b/frontend/__init__.py @@ -19,6 +19,7 @@ def create_app(): from frontend.routes.announcement import announcements_bp from frontend.routes.coupons import coupons_bp from frontend.routes.messages import messages_bp + from frontend.routes.strategies import strategies_bp app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False toolbar = DebugToolbarExtension(app) @@ -34,6 +35,7 @@ def create_app(): app.register_blueprint(announcements_bp) app.register_blueprint(coupons_bp) app.register_blueprint(messages_bp) + app.register_blueprint(strategies_bp) # 添加自定义过滤器 @app.template_filter('datetime') diff --git a/frontend/routes/strategies.py b/frontend/routes/strategies.py new file mode 100644 index 0000000..10214e2 --- /dev/null +++ b/frontend/routes/strategies.py @@ -0,0 +1,129 @@ +from flask import Blueprint, render_template, session, redirect, url_for, flash, request +import requests +from frontend.config import Config + +strategies_bp = Blueprint('strategies', __name__) + +@strategies_bp.route('/strategies') +def list_strategies(): + if not session.get('token'): + flash("请先登录", "warning") + return redirect(url_for('auth.login')) + + token = session.get('token') + try: + resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/list", + json={"token": token} + ) + if resp.status_code == 200: + return render_template('strategies/list.html', strategies=resp.json()) + else: + flash("获取策略列表失败", "danger") + except Exception as e: + flash(f"网络错误: {str(e)}", "danger") + + return redirect(url_for('dashboard.index')) + +@strategies_bp.route('/strategies/create', methods=['POST']) +def create_strategy(): + if not session.get('token'): + return redirect(url_for('auth.login')) + + try: + # 从JSON请求体获取数据 + request_data = request.get_json() + strategy_data = request_data.get('strategy_data') + print(strategy_data.get('strategy_name')) + + # 添加数据验证 + if not strategy_data: + flash("缺少策略数据", "danger") + return redirect(url_for('strategies.list_strategies')) + # 获取表单数据并转换为下划线格式 + form_data = { + "strategy_name": strategy_data.get('strategy_name'), + "segment1_threshold": int(strategy_data.get('segment1_threshold')), + "segment1_price": float(strategy_data.get('segment1_price')), + "segment2_threshold": int(strategy_data.get('segment2_threshold')), + "segment2_price": float(strategy_data.get('segment2_price')), + "segment3_price": float(strategy_data.get('segment3_price')), + "description": strategy_data.get('description'), + "segment3_threshold": int(strategy_data['segment3_threshold']) if strategy_data.get( + 'segment3_threshold') else None + } + + resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/create", + json={ + "token": session['token'], + "strategy_data": form_data + } + ) + + if resp.status_code == 200: + flash("策略创建成功", "success") + return {"status": "success"}, 200 + else: + error_msg = resp.json().get('detail', '未知错误') + flash(f"创建失败: {error_msg}", "danger") + except ValueError as e: + flash(f"数据类型错误: {str(e)}", "danger") + except Exception as e: + flash(f"网络错误: {str(e)}", "danger") + + return redirect(url_for('strategies.list_strategies')) + +@strategies_bp.route('/strategies/delete/', methods=['POST']) +def delete_strategy(strategy_id): + if not session.get('token'): + return redirect(url_for('auth.login')) + + try: + resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/delete", + json={ + "token": session['token'], + "strategy_id": strategy_id + } + ) + if resp.status_code == 200: + flash("策略删除成功", "success") + return {"status": "success"}, 200 + else: + error_msg = resp.json().get('detail', '未知错误') + flash(f"删除失败: {error_msg}", "danger") + except Exception as e: + flash(f"网络错误: {str(e)}", "danger") + + return redirect(url_for('strategies.list_strategies')) + +@strategies_bp.route('/strategies/bind_tables', methods=['POST']) +def bind_tables(): + if not session.get('token'): + return {'status': 'error', 'message': '请先登录'}, 401 + + data = request.get_json() + strategy_id = data.get('strategy_id') + table_ids = data.get('table_ids') + + if not strategy_id or not table_ids: + return {'status': 'error', 'message': '缺少必要参数'}, 400 + + try: + resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/bind", + json={ + "token": session['token'], + "strategy_id": strategy_id, + "table_ids": table_ids + } + ) + + if resp.status_code == 200: + return {'status': 'success', 'message': '绑定成功'} + else: + error_msg = resp.json().get('detail', '未知错误') + return {'status': 'error', 'message': f'绑定失败: {error_msg}'}, resp.status_code + except Exception as e: + return {'status': 'error', 'message': f'网络错误: {str(e)}'}, 500 diff --git a/frontend/routes/tables.py b/frontend/routes/tables.py index ae362c3..a3fd979 100644 --- a/frontend/routes/tables.py +++ b/frontend/routes/tables.py @@ -18,6 +18,7 @@ def list_tables(): if resp.status_code != 200: flash("获取桌台列表失败", "danger") return redirect(url_for('dashboard.index')) + # print (resp.json()) return render_template('tables/list.html', tables=resp.json()) except Exception as e: @@ -29,13 +30,24 @@ def add_table(): if not session.get('token'): return redirect(url_for('auth.login')) + strategies = [] + try: + strategy_resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/list", + json={"token": session['token']} + ) + strategies = strategy_resp.json() if strategy_resp.status_code == 200 else [] + except Exception as e: + flash(f"获取策略列表失败: {str(e)}", "warning") + + if request.method == 'POST': try: payload = { "token": session['token'], "game_table_number": str(request.form['game_table_number']), "capacity": int(request.form['capacity']), - "price": float(request.form['price']) + "strategy_id" : int(request.form['strategy_id']) } resp = requests.post( f"{Config.BASE_API_URL}/admin/tables/create", @@ -54,20 +66,33 @@ def add_table(): except Exception as e: flash(f"网络错误: {str(e)}", "danger") - return render_template('tables/add.html') + return render_template('tables/add.html', strategies = strategies ) @tables_bp.route('/tables/edit/', methods=['GET', 'POST']) def edit_table(table_id): if not session.get('token'): return redirect(url_for('auth.login')) + # 获取策略列表 + strategies = [] + try: + # 获取策略列表 + strategy_resp = requests.post( + f"{Config.BASE_API_URL}/admin/table-strategies/list", + json={"token": session['token']} + ) + strategies = strategy_resp.json() if strategy_resp.status_code == 200 else [] + + except Exception as e: + flash(f"获取策略列表失败: {str(e)}", "warning") + if request.method == 'POST': try: payload = { "token": session['token'], "game_table_number": str(request.form['game_table_number']), "capacity": int(request.form['capacity']), - "price": float(request.form['price']) + "strategy_id" : int(request.form['strategy_id']) } resp = requests.put( f"{Config.BASE_API_URL}/admin/tables/{table_id}", @@ -96,7 +121,7 @@ def edit_table(table_id): tables = resp.json() table = next((t for t in tables if t['table_id'] == table_id), None) if table: - return render_template('tables/edit.html', table=table) + return render_template('tables/edit.html', table=table, strategies=strategies) flash("桌台不存在", "danger") except Exception as e: flash(f"获取数据失败: {str(e)}", "danger") @@ -122,3 +147,18 @@ def delete_table(table_id): flash(f"网络错误: {str(e)}", "danger") return redirect(url_for('tables.list_tables')) + + +@tables_bp.route('/admin/tables/list', methods=['GET']) +def list_all_tables(): + if not session.get('token'): + return {"detail": "未认证"}, 401 + + try: + resp = requests.get( + f"{Config.BASE_API_URL}/admin/tables", + headers={"Authorization": f"Bearer {session['token']}"} + ) + return resp.json() + except Exception as e: + return {"detail": str(e)}, 500 \ No newline at end of file diff --git a/frontend/templates/base.html b/frontend/templates/base.html index f2b2d11..26ed114 100644 --- a/frontend/templates/base.html +++ b/frontend/templates/base.html @@ -4,10 +4,16 @@ {% block title %}桌游厅点单系统管理后台{% endblock %} - + - +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + {% block content %}{% endblock %} +
+ + + + - - - {% block scripts %}{% endblock %} + +{% block scripts %}{% endblock %} \ No newline at end of file diff --git a/frontend/templates/strategies/list.html b/frontend/templates/strategies/list.html new file mode 100644 index 0000000..9a577d9 --- /dev/null +++ b/frontend/templates/strategies/list.html @@ -0,0 +1,398 @@ +{% extends "base.html" %} +{% block title %}收款策略管理{% endblock %} + +{% block content %} +
+

收款策略管理

+
+
创建新策略
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
价格分段设置
+ + +
+
+ +
+ +
+ 分钟内 +
+
+
+
+ +
+ +
+ 元/分钟 +
+
+
+
+ + +
+
+ +
+ +
+ 分钟内 +
+
+
+
+ +
+ +
+ 元/分钟 +
+
+
+
+ + +
+
+ +
+ +
+ 分钟后 +
+
+ 填写后超过该部分的价格将不计算 +
+
+ +
+ +
+ 元/分钟 +
+
+
+
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + {% for strategy in strategies %} + + + + + + + + + + + + {% endfor %} + +
策略ID策略名称分段1阈值分段1价格分段2阈值分段2价格分段3阈值分段3价格操作
{{ strategy.strategy_id }}{{ strategy.strategy_name }}{{ strategy.segment1_threshold }}分钟{{ strategy.segment1_price }}元{{ strategy.segment2_threshold }}分钟{{ strategy.segment2_price }}元{{ strategy.segment3_threshold or '无' }}分钟{{ strategy.segment3_price or '无' }}元 + + +
+
+ + + + + + + + +{% endblock %} diff --git a/frontend/templates/tables/add.html b/frontend/templates/tables/add.html index 0fa31e1..3d0e000 100644 --- a/frontend/templates/tables/add.html +++ b/frontend/templates/tables/add.html @@ -7,18 +7,32 @@
- +
- - + +
取消
+ + {% endblock %} diff --git a/frontend/templates/tables/edit.html b/frontend/templates/tables/edit.html index e500c9a..92a330f 100644 --- a/frontend/templates/tables/edit.html +++ b/frontend/templates/tables/edit.html @@ -8,7 +8,7 @@
+ value="{{ table.game_table_number }}" required>
@@ -16,12 +16,26 @@ value="{{ table.capacity }}" required min="1">
- - + +
取消 +{% block extra_css %} + +{% endblock %} {% endblock %} diff --git a/frontend/templates/tables/list.html b/frontend/templates/tables/list.html index 9cdf3c0..c680117 100644 --- a/frontend/templates/tables/list.html +++ b/frontend/templates/tables/list.html @@ -1,40 +1,133 @@ {% extends "base.html" %} -{% block title %}桌台管理{% endblock %} +{% block title %}台桌管理{% endblock %} {% block content %} -
-

桌台管理

- 添加新桌台 - - - - - - - - - - - - {% for table in tables %} - - - - - - - - {% endfor %} - -
id桌号容量价格倍率操作
{{ table.table_id }}{{ table.game_table_number }}{{ table.capacity }}人{{ "%.2f"|format(table.price) }} - 编辑 -
- -
-
-
+ +
+

桌台列表

+ 添加桌台 + + + + + + + + + + + + {% for table in tables %} + + + + + + + + {% endfor %} + +
ID桌号容量收款策略操作
{{ table.table_id }}{{ table.game_table_number }}{{ table.capacity }}{{ table.strategy_name }} + 编辑 +
+ +
+
+
+ + + + + + + + {% endblock %}