前言
在当今数字化时代,电影票预订系统已经成为影院管理的核心工具。本文将通过Python与MySQL的结合,带你从零开始开发一个功能完整的控制台版电影票预订系统。这个项目不仅涵盖了Python基础语法和MySQL数据库操作,还涉及面向对象编程、异常处理等核心概念,是提升Python实战能力的绝佳练习。
一、系统设计与功能规划
1.1 系统架构设计
我们的电影票预订系统将采用三层架构:
-
表示层:控制台界面(后期可扩展为Web或GUI)
-
业务逻辑层:处理核心业务规则
-
数据访问层:负责与MySQL数据库交互
1.2 核心功能模块
1. 用户管理
- 注册/登录/注销
- 个人信息管理
2. 电影管理
- 电影信息查询
- 电影排期查看
3. 票务管理
- 选座购票
- 订单查询
- 退票改签
4. 管理员功能
- 电影信息维护
- 场次安排
- 销售统计
二、数据库设计与实现
2.1 MySQL表结构设计
用户表(users):
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
real_name VARCHAR(50),
phone VARCHAR(20),
email VARCHAR(100),
is_admin BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
电影表(movies):
CREATE TABLE movies (
movie_id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
director VARCHAR(50),
actors VARCHAR(200),
genre VARCHAR(50),
duration INT COMMENT '分钟',
release_date DATE,
description TEXT,
poster_url VARCHAR(255)
);
放映场次表(screenings):
CREATE TABLE screenings (
screening_id INT AUTO_INCREMENT PRIMARY KEY,
movie_id INT NOT NULL,
theater_id INT NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
price DECIMAL(10,2) NOT NULL,
available_seats INT NOT NULL,
FOREIGN KEY (movie_id) REFERENCES movies(movie_id),
FOREIGN KEY (theater_id) REFERENCES theaters(theater_id)
);
订单表(orders):
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
screening_id INT NOT NULL,
seat_count INT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status ENUM('pending', 'paid', 'cancelled') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (screening_id) REFERENCES screenings(screening_id)
);
2.2 数据库连接工具类
import mysql.connector
from mysql.connector import Error
class Database:
def __init__(self, host='localhost', database='movie_booking',
user='root', password='password'):
self.host = host
self.database = database
self.user = user
self.password = password
self.connection = None
def connect(self):
try:
self.connection = mysql.connector.connect(
host=self.host,
database=self.database,
user=self.user,
password=self.password
)
return True
except Error as e:
print(f"数据库连接失败: {e}")
return False
def disconnect(self):
if self.connection and self.connection.is_connected():
self.connection.close()
def execute_query(self, query, params=None, fetch=False):
cursor = None
try:
cursor = self.connection.cursor(dictionary=True)
cursor.execute(query, params or ())
if fetch:
return cursor.fetchall()
else:
self.connection.commit()
return cursor.rowcount
except Error as e:
print(f"数据库操作失败: {e}")
return None
finally:
if cursor: cursor.close()
三、核心功能实现
3.1 用户认证模块
import hashlib
class UserService:
def __init__(self, db):
self.db = db
def register(self, username, password, **kwargs):
# 检查用户名是否存在
if self.get_user_by_username(username):
return False, "用户名已存在"
# 密码加密
hashed_pwd = self._hash_password(password)
# 插入用户数据
query = """INSERT INTO users (username, password, real_name, phone, email)
VALUES (%s, %s, %s, %s, %s)"""
params = (username, hashed_pwd, kwargs.get('real_name'),
kwargs.get('phone'), kwargs.get('email'))
if self.db.execute_query(query, params):
return True, "注册成功"
return False, "注册失败"
def login(self, username, password):
user = self.get_user_by_username(username)
if not user:
return None, "用户不存在"
if self._verify_password(password, user['password']):
return user, "登录成功"
return None, "密码错误"
def get_user_by_username(self, username):
query = "SELECT * FROM users WHERE username = %s"
result = self.db.execute_query(query, (username,), fetch=True)
return result[0] if result else None
def _hash_password(self, password):
return hashlib.sha256(password.encode()).hexdigest()
def _verify_password(self, input_pwd, hashed_pwd):
return self._hash_password(input_pwd) == hashed_pwd
3.2 电影管理模块
class MovieService:
def __init__(self, db):
self.db = db
def add_movie(self, title, director, actors, genre, duration,
release_date, description, poster_url):
query = """INSERT INTO movies
(title, director, actors, genre, duration,
release_date, description, poster_url)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)"""
params = (title, director, actors, genre, duration,
release_date, description, poster_url)
return self.db.execute_query(query, params)
def get_all_movies(self):
query = "SELECT * FROM movies ORDER BY release_date DESC"
return self.db.execute_query(query, fetch=True)
def get_movie_by_id(self, movie_id):
query = "SELECT * FROM movies WHERE movie_id = %s"
result = self.db.execute_query(query, (movie_id,), fetch=True)
return result[0] if result else None
def search_movies(self, keyword):
query = """SELECT * FROM movies
WHERE title LIKE %s OR director LIKE %s OR actors LIKE %s"""
params = (f"%{keyword}%", f"%{keyword}%", f"%{keyword}%")
return self.db.execute_query(query, params, fetch=True)
3.3 票务管理模块
from datetime import datetime
class BookingService:
def __init__(self, db):
self.db = db
def get_available_screenings(self, movie_id=None):
query = """SELECT s.*, m.title, m.duration, t.name as theater_name
FROM screenings s
JOIN movies m ON s.movie_id = m.movie_id
JOIN theaters t ON s.theater_id = t.theater_id
WHERE s.start_time > %s AND s.available_seats > 0"""
params = [datetime.now()]
if movie_id:
query += " AND s.movie_id = %s"
params.append(movie_id)
query += " ORDER BY s.start_time"
return self.db.execute_query(query, params, fetch=True)
def book_tickets(self, user_id, screening_id, seat_count):
# 检查场次和座位
screening = self.get_screening_by_id(screening_id)
if not screening or screening['available_seats'] < seat_count:
return False, "座位不足"
# 计算总价
total_amount = screening['price'] * seat_count
# 创建订单
order_query = """INSERT INTO orders
(user_id, screening_id, seat_count, total_amount)
VALUES (%s, %s, %s, %s)"""
order_params = (user_id, screening_id, seat_count, total_amount)
# 更新场次座位
screening_query = """UPDATE screenings
SET available_seats = available_seats - %s
WHERE screening_id = %s"""
screening_params = (seat_count, screening_id)
# 事务处理
try:
self.db.connection.start_transaction()
self.db.execute_query(order_query, order_params)
self.db.execute_query(screening_query, screening_params)
self.db.connection.commit()
return True, "订票成功"
except Exception as e:
self.db.connection.rollback()
return False, f"订票失败: {e}"
def get_user_orders(self, user_id):
query = """SELECT o.*, m.title, s.start_time, t.name as theater_name
FROM orders o
JOIN screenings s ON o.screening_id = s.screening_id
JOIN movies m ON s.movie_id = m.movie_id
JOIN theaters t ON s.theater_id = t.theater_id
WHERE o.user_id = %s
ORDER BY o.created_at DESC"""
return self.db.execute_query(query, (user_id,), fetch=True)
def cancel_order(self, order_id):
# 获取订单信息
order_query = """SELECT o.*, s.screening_id, o.seat_count
FROM orders o
JOIN screenings s ON o.screening_id = s.screening_id
WHERE o.order_id = %s AND o.status = 'paid'"""
order = self.db.execute_query(order_query, (order_id,), fetch=True)
if not order:
return False, "订单不存在或不可取消"
order = order[0]
# 事务处理
try:
self.db.connection.start_transaction()
# 更新订单状态
update_order = """UPDATE orders
SET status = 'cancelled'
WHERE order_id = %s"""
self.db.execute_query(update_order, (order_id,))
# 恢复座位
update_screening = """UPDATE screenings
SET available_seats = available_seats + %s
WHERE screening_id = %s"""
self.db.execute_query(update_screening,
(order['seat_count'], order['screening_id']))
self.db.connection.commit()
return True, "退票成功"
except Exception as e:
self.db.connection.rollback()
return False, f"退票失败: {e}"
四、控制台界面实现
4.1 主菜单与导航
class ConsoleUI:
def __init__(self):
self.db = Database()
if not self.db.connect():
raise Exception("无法连接数据库")
self.user_service = UserService(self.db)
self.movie_service = MovieService(self.db)
self.booking_service = BookingService(self.db)
self.current_user = None
def run(self):
while True:
if not self.current_user:
self.show_main_menu()
else:
if self.current_user['is_admin']:
self.show_admin_menu()
else:
self.show_user_menu()
def show_main_menu(self):
print("\n=== 电影票预订系统 ===")
print("1. 登录")
print("2. 注册")
print("3. 查看电影")
print("0. 退出")
choice = input("请选择: ")
if choice == "1":
self.login()
elif choice == "2":
self.register()
elif choice == "3":
self.show_movies()
elif choice == "0":
exit()
else:
print("无效选择")
def show_user_menu(self):
print(f"\n=== 欢迎, {self.current_user['username']} ===")
print("1. 查看电影")
print("2. 查询场次")
print("3. 我的订单")
print("4. 个人信息")
print("0. 注销")
choice = input("请选择: ")
if choice == "1":
self.show_movies()
elif choice == "2":
self.show_screenings()
elif choice == "3":
self.show_user_orders()
elif choice == "4":
self.show_user_profile()
elif choice == "0":
self.current_user = None
else:
print("无效选择")
def show_admin_menu(self):
print(f"\n=== 管理员面板 ===")
print("1. 电影管理")
print("2. 场次管理")
print("3. 销售统计")
print("0. 注销")
choice = input("请选择: ")
if choice == "1":
self.manage_movies()
elif choice == "2":
self.manage_screenings()
elif choice == "3":
self.view_sales_report()
elif choice == "0":
self.current_user = None
else:
print("无效选择")
4.2 用户交互功能实现
def login(self):
print("\n=== 用户登录 ===")
username = input("用户名: ")
password = input("密码: ")
user, message = self.user_service.login(username, password)
if user:
self.current_user = user
print(f"登录成功,欢迎 {user['username']}!")
else:
print(f"登录失败: {message}")
def register(self):
print("\n=== 用户注册 ===")
username = input("用户名: ")
password = input("密码: ")
real_name = input("真实姓名(可选): ")
phone = input("电话(可选): ")
email = input("邮箱(可选): ")
success, message = self.user_service.register(
username, password,
real_name=real_name,
phone=phone,
email=email
)
print(message)
def show_movies(self):
movies = self.movie_service.get_all_movies()
print("\n=== 电影列表 ===")
for idx, movie in enumerate(movies, 1):
print(f"{idx}. {movie['title']} ({movie['release_date'].year})")
print(f" 导演: {movie['director']} 类型: {movie['genre']}")
print(f" 时长: {movie['duration']}分钟")
choice = input("\n输入电影编号查看详情(0返回): ")
if choice.isdigit() and int(choice) > 0:
movie = movies[int(choice)-1]
self.show_movie_detail(movie['movie_id'])
def show_movie_detail(self, movie_id):
movie = self.movie_service.get_movie_by_id(movie_id)
if not movie:
print("电影不存在")
return
print(f"\n=== {movie['title']} ===")
print(f"导演: {movie['director']}")
print(f"主演: {movie['actors']}")
print(f"类型: {movie['genre']} 时长: {movie['duration']}分钟")
print(f"上映日期: {movie['release_date']}")
print("\n剧情简介:")
print(movie['description'])
screenings = self.booking_service.get_available_screenings(movie_id)
if screenings:
print("\n=== 放映场次 ===")
for idx, s in enumerate(screenings, 1):
print(f"{idx}. {s['start_time']} {s['theater_name']} "
f"¥{s['price']} 剩余座位: {s['available_seats']}")
if self.current_user:
choice = input("\n输入场次编号订票(0返回): ")
if choice.isdigit() and int(choice) > 0:
self.book_ticket(screenings[int(choice)-1]['screening_id'])
def book_ticket(self, screening_id):
screening = self.booking_service.get_screening_by_id(screening_id)
if not screening:
print("场次不存在")
return
print(f"\n=== 订票: {screening['title']} ===")
print(f"时间: {screening['start_time']}")
print(f"影院: {screening['theater_name']}")
print(f"价格: ¥{screening['price']}/张 剩余座位: {screening['available_seats']}")
while True:
seat_count = input("请输入购票数量: ")
if seat_count.isdigit() and 0 < int(seat_count) <= screening['available_seats']:
break
print("输入无效或座位不足")
success, message = self.booking_service.book_tickets(
self.current_user['user_id'], screening_id, int(seat_count)
)
print(message)
五、项目扩展与优化
5.1 功能扩展建议
-
座位选择:实现具体座位图选择而非简单计数
-
支付集成:模拟支付流程
-
会员积分:增加会员等级和积分系统
-
评价系统:允许用户对电影评分和评论
5.2 代码优化方向
-
使用ORM:引入SQLAlchemy替代原生SQL
-
日志记录:添加操作日志记录
-
配置管理:将数据库配置等移出代码
-
异常处理:增强异常捕获和用户友好提示
5.3 部署与打包
-
可执行文件:使用PyInstaller打包为exe
-
安装脚本:编写数据库初始化脚本
-
Docker支持:添加Dockerfile方便部署
结语
通过这个电影票预订系统项目,我们完整实践了:
-
Python控制台程序开发
-
MySQL数据库设计与操作
-
面向对象的业务逻辑实现
-
完整的用户交互流程
进一步学习建议:
-
尝试使用Tkinter或PyQt添加图形界面
-
学习使用Flask/Django转为Web应用
-
研究数据库连接池优化性能
-
添加单元测试保证代码质量
如果你在实现过程中遇到任何问题,欢迎在评论区留言讨论。觉得本文有帮助的话,请点赞收藏支持!