用户表:编号,用户名,密码,邮箱,角色,用户头像;

影片表:编号,影片名称,国外名称,演职人员,导演,电影详情,电影时长,电影类型,电影评分,票房,电影参评人数,上映时间,制片地区,电影海报地址,电影状态;

影院表:编号,影院名称,影院地址;

放映厅表:编号,放映厅名称,放映厅容量,所属影院编号;

订单表:编号,所属用户编号,所属场次编号,电影票座位信息,订单状态,订单价格,订单支付时间,所属用户对象;

订单详情表:编号,所属放映厅,放映的电影编号,电影放映时间,售价,剩余座位数,场次状态,所属放映厅对象,放映的电影;

评论表:编号,所属用户编号,评论内容,所属电影编号,评论时间,所属用户,评分;

影院票务管理系统

姓名 主要完成的工作 备注

1. 需求分析

1、用户管理模块

1.1 用户信息管理

用户表(users)用于存储用户的基础信息,包括用户 ID、用户名、密码、邮箱等字段。其中,用户 ID 作为主键,确保每个用户具有唯一标识;用户名和邮箱设置为唯一,避免重复注册;密码字段采用加密存储,保障用户信息安全。需求场景包括用户注册时新增数据、登录时验证用户名和密码、个人资料修改时更新信息,以及通过邮箱找回密码等功能。

1.2 收货地址管理

收货地址表(addresses)关联用户表,存储用户的收货地址信息。每个用户可拥有多个收货地址,通过 is_default 字段标记默认地址。需求场景包括用户添加新地址、修改已有地址、删除无效地址,以及在下单时选择默认或指定地址。

2、商品管理模块

2.1 商品类别管理

商品类别表(categories)采用树状结构,通过 parent_id 字段关联父类别,实现商品的多级分类管理。需求场景包括添加新类别、修改类别名称、删除空类别(无下级类别和商品),以及按类别层级展示商品分类目录。

2.2 商品信息管理

商品表(products)存储商品的详细信息,包括商品 ID、名称、价格、库存等,通过外键关联商品类别表和供应商表。需求场景包括商品上架(插入新商品记录)、商品信息更新(如修改价格、库存)、商品下架(逻辑删除或标记状态),以及根据类别、价格、库存等条件查询商品。

2.3 供应商管理

供应商表(suppliers)记录供应商的基本信息,如供应商 ID、名称、联系方式等。商品表通过供应商 ID 与之关联,用于明确商品的供货来源。需求场景包括添加新供应商、更新供应商信息、删除不再合作的供应商,以及查询供应商提供的商品列表。

3、购物车模块

3.1 购物车管理

购物车表(cart)为每个用户创建唯一的购物车记录,记录创建时间和最后修改时间。需求场景包括用户首次访问购物车时创建记录、清空购物车(删除关联的 cart_items 记录)、查询购物车基本信息(如创建时间)。

3.2 购物车商品管理

购物车商品表(cart_items)关联购物车表和商品表,记录用户加入购物车的商品及数量。需求场景包括用户添加商品到购物车(插入记录)、修改商品数量、删除购物车中的商品,以及统计购物车中商品的总价。

4、订单管理模块

4.1 订单管理

订单表(orders)记录订单的核心信息,包括订单 ID、用户 ID、地址 ID、订单日期、总金额、订单状态等。通过外键关联用户表和收货地址表,明确订单归属和收货信息。需求场景包括用户下单时创建订单记录、更新订单状态(如待付款、已付款、已发货等)、查询订单列表(按用户、日期、状态筛选)。

4.2 订单详情管理

订单详情表(order_details)关联订单表和商品表,记录每个订单包含的商品信息、数量和单价。subtotal 字段作为虚拟列,自动计算商品小计。需求场景包括生成订单时插入订单详情、查询订单中商品明细、统计订单商品的总数量和总金额。

2. 系统设计

2.1. 系统结构功能图

根据需求分析,确定系统结构功能如图2-1所示:

图2-1 系统功能结构图

2.2. 功能流程图

系统最主要的功能就是管理员登录之后对各种功能的使用。管理员功能如图4-2所示。

图2-2 管理员登录管理流程

2.3. 数据库设计

整体功能的E-R图如图2-4所示。

画板

图2-4 系统E-R图

本系统包含的数据表如下:

1**书架类型表** (shelf_types)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
shelf_type_id NUMBER 主键 - - 书架类型ID
shelf_type_name VARCHAR2 非空 50 - 书架类型名称

2**读者类型表** (reader_types)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
reader_type_id NUMBER 主键 - - 读者类型ID
reader_type_name VARCHAR2 非空 50 - 读者类型名称

3**书架表** (shelves)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
shelf_id NUMBER 主键 - - 书架ID
shelf_number VARCHAR2 非空 20 - 书架编号
shelf_type_id NUMBER 外键且非空 - - 书架类型ID

4**工作人员表** (staff)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
staff_id NUMBER 主键 - - 工作人员ID
staff_name VARCHAR2 非空 100 - 工作人员姓名
staff_position VARCHAR2 非空 50 - 工作人员职位

5**图书表** (books)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
book_id NUMBER 主键 - - 图书ID
title VARCHAR2 非空 200 - 图书标题
author VARCHAR2 非空 100 - 图书作者
isbn VARCHAR2 唯一且非空 20 - 图书国际标准书号
publication_year NUMBER 可空 - - 出版年份
shelf_id NUMBER 外键且非空 - - 书架ID

6**读者表** (readers)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
reader_id NUMBER 主键 - - 读者ID
reader_name VARCHAR2 非空 100 - 读者姓名
reader_type_id NUMBER 外键且非空 - - 读者类型ID
contact_phone VARCHAR2 可空 15 - 联系电话

7**正借阅表** (loans)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
loan_id NUMBER 主键 - - 借阅记录ID
book_id NUMBER 外键且非空 - - 图书ID
reader_id NUMBER 外键且非空 - - 读者ID
loan_date DATE 非空 - - 借书日期
due_date DATE 非空 - - 应还日期

8**已还书籍表** (returns)

列名 数据类型 字段类型 长度 是否为空 默认值 备注
return_id NUMBER 主键 - - 还书记录ID
loan_id NUMBER 外键 - - 借阅记录ID
return_date DATE 非空 - - 还书日期

9**通知记录表(overdue_notifications)**

列名 数据类型 字段类型 长度 是否为空 默认值 备注
notification_id NUMBER 主键 - - 通知记录
loan_id NUMBER 外键 - - 借阅记录 ID
reader_id NUMBER 外键 - - 读者 ID
notification_date DATE 日期 - SYSDATE 通知日期
message VARCHAR2 字符串 500 - 通知内容

10**历史记录表(book_location_history)**

列名 数据类型 字段类型 长度 是否为空 默认值 备注
history_id NUMBER 主键 - - 历史记录
book_id NUMBER 外键 - - 图书 ID
old_shelf_id NUMBER 外键 - - 旧书架 ID
new_shelf_id NUMBER 外键 - - 新书架 ID
change_date DATE 日期 - SYSDATE 变更日期
changed_by NUMBER 外键 - - 操作人 ID

3. 3 SQL**实现**

3.1. 建表

-- 1. 用户表(基础表)
CREATE TABLE users (
user_id CHAR(10) PRIMARY KEY,
username VARCHAR2(30) NOT NULL UNIQUE,
password VARCHAR2(100) NOT NULL,
email VARCHAR2(50) NOT NULL UNIQUE,
full_name VARCHAR2(50),
phone CHAR(11),
reg_date DATE DEFAULT SYSDATE NOT NULL,
last_login DATE
);

-- 2. 商品类别表
CREATE TABLE categories (
category_id CHAR(6) PRIMARY KEY,
category_name VARCHAR2(50) NOT NULL,
parent_id CHAR(6),
CONSTRAINT fk_parent_cat FOREIGN KEY (parent_id) REFERENCES categories(category_id)
);

-- 3. 供应商表
CREATE TABLE suppliers (
supplier_id CHAR(6) PRIMARY KEY,
supplier_name VARCHAR2(50) NOT NULL,
contact_person VARCHAR2(30),
phone VARCHAR2(20),
bank_account VARCHAR2(25),
address VARCHAR2(100)
);

-- 4. 商品表
CREATE TABLE products (
product_id CHAR(8) PRIMARY KEY,
product_name VARCHAR2(100) NOT NULL,
category_id CHAR(6) NOT NULL,
description CLOB,
price NUMBER(10,2) NOT NULL CHECK(price > 0),
stock_quantity INT NOT NULL CHECK(stock_quantity >= 0),
supplier_id CHAR(6) NOT NULL,
create_date DATE DEFAULT SYSDATE NOT NULL,
CONSTRAINT fk_prod_category FOREIGN KEY (category_id) REFERENCES categories(category_id),
CONSTRAINT fk_prod_supplier FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id)
);

-- 5. 收货地址表
CREATE TABLE addresses (
address_id CHAR(8) PRIMARY KEY,
user_id CHAR(10) NOT NULL,
receiver_name VARCHAR2(30) NOT NULL,
phone CHAR(11) NOT NULL,
province VARCHAR2(20) NOT NULL,
city VARCHAR2(20) NOT NULL,
district VARCHAR2(20) NOT NULL,
detail_address VARCHAR2(100) NOT NULL,
is_default CHAR(1) DEFAULT 'N' CHECK(is_default IN ('Y','N')),
CONSTRAINT fk_addr_user FOREIGN KEY (user_id) REFERENCES users(user_id)
);

-- 6. 购物车表
CREATE TABLE cart (
cart_id CHAR(10) PRIMARY KEY,
user_id CHAR(10) NOT NULL UNIQUE,
created_date DATE DEFAULT SYSDATE NOT NULL,
modified_date DATE,
CONSTRAINT fk_cart_user FOREIGN KEY (user_id) REFERENCES users(user_id)
);

-- 7. 购物车商品表
CREATE TABLE cart_items (
cart_id CHAR(10) NOT NULL,
product_id CHAR(8) NOT NULL,
quantity INT NOT NULL CHECK(quantity > 0),
CONSTRAINT pk_cart_items PRIMARY KEY (cart_id, product_id),
CONSTRAINT fk_ci_cart FOREIGN KEY (cart_id) REFERENCES cart(cart_id),
CONSTRAINT fk_ci_product FOREIGN KEY (product_id) REFERENCES products(product_id)
);

-- 8. 订单表
CREATE TABLE orders (
order_id CHAR(12) PRIMARY KEY,
user_id CHAR(10) NOT NULL,
address_id CHAR(8) NOT NULL,
order_date DATE DEFAULT SYSDATE NOT NULL,
total_amount NUMBER(12,2) NOT NULL,
status VARCHAR2(20) DEFAULT '待付款'
CHECK(status IN ('待付款','已付款','已发货','已完成','已取消')),
payment_method VARCHAR2(20),
payment_id VARCHAR2(30),
CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES users(user_id),
CONSTRAINT fk_order_addr FOREIGN KEY (address_id) REFERENCES addresses(address_id)
);

-- 9. 订单详情表
CREATE TABLE order_details (
order_id CHAR(12) NOT NULL,
product_id CHAR(8) NOT NULL,
quantity INT NOT NULL CHECK(quantity > 0),
unit_price NUMBER(10,2) NOT NULL,
subtotal NUMBER(10,2) GENERATED ALWAYS AS (quantity * unit_price) VIRTUAL,
CONSTRAINT pk_order_details PRIMARY KEY (order_id, product_id),
CONSTRAINT fk_od_order FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
CONSTRAINT fk_od_product FOREIGN KEY (product_id) REFERENCES products(product_id)
);

3.2. 索引

-- 创建索引
CREATE INDEX idx_products_name ON products(product_name);
CREATE INDEX idx_orders_user ON orders(user_id);
CREATE INDEX idx_orders_date ON orders(order_date);
CREATE INDEX idx_address_user ON addresses(user_id);

3.3. 视图

-- 创建视图:用户订单历史
CREATE OR REPLACE VIEW user_order_history AS
SELECT
u.user_id,
u.username,
o.order_id,
o.order_date,
o.total_amount,
o.status,
COUNT(od.product_id) AS product_count
FROM users u
JOIN orders o ON u.user_id = o.user_id
JOIN order_details od ON o.order_id = od.order_id
GROUP BY u.user_id, u.username, o.order_id, o.order_date, o.total_amount, o.status;

-- 创建视图:商品库存概览
CREATE OR REPLACE VIEW product_inventory AS
SELECT
p.product_id,
p.product_name,
p.stock_quantity,
c.category_name,
s.supplier_name
FROM products p
JOIN categories c ON p.category_id = c.category_id
JOIN suppliers s ON p.supplier_id = s.supplier_id;


-- 新增视图1:用户活跃度报告
CREATE OR REPLACE VIEW user_activity_report AS
SELECT
u.user_id,
u.username,
u.email,
u.reg_date,
u.last_login,
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.user_id) AS order_count,
(SELECT SUM(total_amount) FROM orders o WHERE o.user_id = u.user_id) AS total_spent,
(SELECT MAX(order_date) FROM orders o WHERE o.user_id = u.user_id) AS last_order_date,
(SELECT COUNT(*) FROM cart_items ci
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = u.user_id) AS cart_item_count
FROM users u;

-- 新增视图2:热销商品分析
CREATE OR REPLACE VIEW top_selling_products AS
SELECT
p.product_id,
p.product_name,
p.price,
c.category_name,
s.supplier_name,
SUM(od.quantity) AS total_sold,
SUM(od.quantity * od.unit_price) AS total_revenue,
p.stock_quantity AS current_stock,
ROUND(SUM(od.quantity) / (SELECT SUM(quantity) FROM order_details), 4) * 100 AS market_share
FROM products p
JOIN order_details od ON p.product_id = od.product_id
JOIN categories c ON p.category_id = c.category_id
JOIN suppliers s ON p.supplier_id = s.supplier_id
GROUP BY p.product_id, p.product_name, p.price, c.category_name, s.supplier_name, p.stock_quantity
ORDER BY total_sold DESC;

3.4. 存储过程、函数

-- 存储过程和函数
-- 1. 添加商品到购物车
CREATE OR REPLACE PROCEDURE add_to_cart(
p_user_id IN CHAR,
p_product_id IN CHAR,
p_quantity IN INT
)
IS
v_cart_id CHAR(10);
BEGIN
-- 获取用户购物车
BEGIN
SELECT cart_id INTO v_cart_id FROM cart WHERE user_id = p_user_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- 创建新购物车
INSERT INTO cart (cart_id, user_id)
VALUES ('C' || LPAD(seq_cart_id.NEXTVAL, 9, '0'), p_user_id)
RETURNING cart_id INTO v_cart_id;
END;

-- 添加或更新商品
BEGIN
UPDATE cart_items
SET quantity = quantity + p_quantity
WHERE cart_id = v_cart_id AND product_id = p_product_id;

IF SQL%ROWCOUNT = 0 THEN
INSERT INTO cart_items (cart_id, product_id, quantity)
VALUES (v_cart_id, p_product_id, p_quantity);
END IF;
END;

-- 更新购物车修改时间
UPDATE cart SET modified_date = SYSDATE WHERE cart_id = v_cart_id;

COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

-- 2. 从购物车移除商品
CREATE OR REPLACE PROCEDURE remove_from_cart(
p_user_id IN CHAR,
p_product_id IN CHAR
)
IS
v_cart_id CHAR(10);
BEGIN
SELECT cart_id INTO v_cart_id FROM cart WHERE user_id = p_user_id;

DELETE FROM cart_items
WHERE cart_id = v_cart_id AND product_id = p_product_id;

-- 更新购物车修改时间
UPDATE cart SET modified_date = SYSDATE WHERE cart_id = v_cart_id;

COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20002, '购物车不存在');
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

-- 3. 更新购物车商品数量
CREATE OR REPLACE PROCEDURE update_cart_item(
p_user_id IN CHAR,
p_product_id IN CHAR,
p_quantity IN INT
)
IS
v_cart_id CHAR(10);
BEGIN
IF p_quantity <= 0 THEN
RAISE_APPLICATION_ERROR(-20003, '数量必须大于0');
END IF;

SELECT cart_id INTO v_cart_id FROM cart WHERE user_id = p_user_id;

UPDATE cart_items
SET quantity = p_quantity
WHERE cart_id = v_cart_id AND product_id = p_product_id;

IF SQL%ROWCOUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20004, '商品不在购物车中');
END IF;

-- 更新购物车修改时间
UPDATE cart SET modified_date = SYSDATE WHERE cart_id = v_cart_id;

COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20002, '购物车不存在');
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

-- 4. 创建订单(从购物车)
CREATE OR REPLACE PROCEDURE create_order_from_cart(
p_user_id IN CHAR,
p_address_id IN CHAR,
p_payment_method IN VARCHAR2,
p_payment_id IN VARCHAR2,
p_order_id OUT CHAR
)
IS
v_cart_id CHAR(10);
v_total NUMBER(12,2);
BEGIN
-- 获取购物车
SELECT cart_id INTO v_cart_id FROM cart WHERE user_id = p_user_id;

-- 计算总金额
SELECT SUM(ci.quantity * p.price)
INTO v_total
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
WHERE ci.cart_id = v_cart_id;

IF v_total IS NULL THEN
RAISE_APPLICATION_ERROR(-20005, '购物车为空');
END IF;

-- 生成订单ID
p_order_id := 'O' || LPAD(seq_order_id.NEXTVAL, 11, '0');

-- 创建订单
INSERT INTO orders (order_id, user_id, address_id, total_amount, payment_method, payment_id)
VALUES (p_order_id, p_user_id, p_address_id, v_total, p_payment_method, p_payment_id);

-- 添加订单详情
INSERT INTO order_details (order_id, product_id, quantity, unit_price)
SELECT p_order_id, ci.product_id, ci.quantity, p.price
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
WHERE ci.cart_id = v_cart_id;

-- 清空购物车
DELETE FROM cart_items WHERE cart_id = v_cart_id;

COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

-- 5. 更新订单状态
CREATE OR REPLACE PROCEDURE update_order_status(
p_order_id IN CHAR,
p_new_status IN VARCHAR2
)
IS
v_current_status VARCHAR2(20);
BEGIN
SELECT status INTO v_current_status FROM orders WHERE order_id = p_order_id;

-- 状态转换验证
IF v_current_status = '已完成' OR v_current_status = '已取消' THEN
RAISE_APPLICATION_ERROR(-20006, '已完成或已取消的订单不能修改状态');
END IF;

IF v_current_status = '待付款' AND p_new_status NOT IN ('已付款', '已取消') THEN
RAISE_APPLICATION_ERROR(-20007, '待付款订单只能转为已付款或已取消');
END IF;

IF v_current_status = '已付款' AND p_new_status NOT IN ('已发货', '已取消') THEN
RAISE_APPLICATION_ERROR(-20008, '已付款订单只能转为已发货或已取消');
END IF;

IF v_current_status = '已发货' AND p_new_status NOT IN ('已完成') THEN
RAISE_APPLICATION_ERROR(-20009, '已发货订单只能转为已完成');
END IF;

UPDATE orders SET status = p_new_status WHERE order_id = p_order_id;

COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20010, '订单不存在');
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

-- 6. 计算订单总金额(函数)
CREATE OR REPLACE FUNCTION calculate_order_total(
p_order_id IN CHAR
) RETURN NUMBER
IS
v_total NUMBER(12,2);
BEGIN
SELECT SUM(quantity * unit_price)
INTO v_total
FROM order_details
WHERE order_id = p_order_id;

RETURN v_total;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 0;
END;
/

-- 7. 检查库存可用性(函数)
CREATE OR REPLACE FUNCTION check_stock_availability(
p_product_id IN CHAR,
p_quantity IN INT
) RETURN BOOLEAN
IS
v_stock INT;
BEGIN
SELECT stock_quantity INTO v_stock
FROM products
WHERE product_id = p_product_id;

RETURN (v_stock >= p_quantity);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
/

-- 8. 获取用户购物车详情(函数)
CREATE OR REPLACE FUNCTION get_user_cart_details(
p_user_id IN CHAR
) RETURN SYS_REFCURSOR
IS
v_cursor SYS_REFCURSOR;
BEGIN
OPEN v_cursor FOR
SELECT
p.product_id,
p.product_name,
ci.quantity,
p.price,
(ci.quantity * p.price) AS subtotal
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = p_user_id;

RETURN v_cursor;
END;
/

-- 9. 搜索商品(函数)
CREATE OR REPLACE FUNCTION search_products(
p_keyword IN VARCHAR2,
p_category_id IN CHAR DEFAULT NULL,
p_min_price IN NUMBER DEFAULT 0,
p_max_price IN NUMBER DEFAULT 9999999,
p_sort_by IN VARCHAR2 DEFAULT 'price',
p_sort_order IN VARCHAR2 DEFAULT 'ASC'
) RETURN SYS_REFCURSOR
IS
v_cursor SYS_REFCURSOR;
v_sql VARCHAR2(4000);
BEGIN
v_sql := 'SELECT
product_id,
product_name,
price,
stock_quantity,
(SELECT category_name FROM categories WHERE category_id = p.category_id) AS category_name
FROM products p
WHERE (LOWER(product_name) LIKE ''%'' || LOWER(:keyword) || ''%''
OR LOWER(description) LIKE ''%'' || LOWER(:keyword) || ''%'')
AND (:category_id IS NULL OR category_id = :category_id)
AND price BETWEEN :min_price AND :max_price';

-- 添加排序
IF p_sort_by = 'price' THEN
v_sql := v_sql || ' ORDER BY price ';
ELSIF p_sort_by = 'name' THEN
v_sql := v_sql || ' ORDER BY product_name ';
ELSIF p_sort_by = 'stock' THEN
v_sql := v_sql || ' ORDER BY stock_quantity ';
ELSE
v_sql := v_sql || ' ORDER BY product_id ';
END IF;

v_sql := v_sql || p_sort_order;

OPEN v_cursor FOR v_sql
USING p_keyword, p_keyword, p_category_id, p_min_price, p_max_price;

RETURN v_sql;
END;
/

-- 10. 获取用户订单历史(存储过程)
CREATE OR REPLACE PROCEDURE get_user_orders(
p_user_id IN CHAR,
p_start_date IN DATE DEFAULT NULL,
p_end_date IN DATE DEFAULT NULL,
p_status IN VARCHAR2 DEFAULT NULL,
p_result OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN p_result FOR
SELECT
order_id,
order_date,
total_amount,
status,
(SELECT COUNT(*) FROM order_details WHERE order_id = o.order_id) AS item_count
FROM orders o
WHERE user_id = p_user_id
AND (p_start_date IS NULL OR order_date >= p_start_date)
AND (p_end_date IS NULL OR order_date <= p_end_date)
AND (p_status IS NULL OR status = p_status)
ORDER BY order_date DESC;
END;
/

3.5. 触发器

-- 创建触发器(自动生成ID)
-- 用户ID触发器
CREATE OR REPLACE TRIGGER trg_users_id
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF :NEW.user_id IS NULL THEN
SELECT 'U' || LPAD(seq_user_id.NEXTVAL, 9, '0') INTO :NEW.user_id FROM DUAL;
END IF;
END;
/

-- 商品ID触发器
CREATE OR REPLACE TRIGGER trg_products_id
BEFORE INSERT ON products
FOR EACH ROW
BEGIN
IF :NEW.product_id IS NULL THEN
SELECT 'P' || LPAD(seq_product_id.NEXTVAL, 7, '0') INTO :NEW.product_id FROM DUAL;
END IF;
END;
/

-- 订单ID触发器
CREATE OR REPLACE TRIGGER trg_orders_id
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF :NEW.order_id IS NULL THEN
SELECT 'O' || LPAD(seq_order_id.NEXTVAL, 11, '0') INTO :NEW.order_id FROM DUAL;
END IF;
END;
/

-- 地址ID触发器
CREATE OR REPLACE TRIGGER trg_addresses_id
BEFORE INSERT ON addresses
FOR EACH ROW
BEGIN
IF :NEW.address_id IS NULL THEN
SELECT 'A' || LPAD(seq_address_id.NEXTVAL, 7, '0') INTO :NEW.address_id FROM DUAL;
END IF;
END;
/

-- 购物车ID触发器
CREATE OR REPLACE TRIGGER trg_cart_id
BEFORE INSERT ON cart
FOR EACH ROW
BEGIN
IF :NEW.cart_id IS NULL THEN
SELECT 'C' || LPAD(seq_cart_id.NEXTVAL, 9, '0') INTO :NEW.cart_id FROM DUAL;
END IF;
END;
/

-- 库存更新触发器
CREATE OR REPLACE TRIGGER trg_update_stock
AFTER INSERT ON order_details
FOR EACH ROW
BEGIN
UPDATE products
SET stock_quantity = stock_quantity - :NEW.quantity
WHERE product_id = :NEW.product_id;
END;
/

3.6. 数据操纵

----------------------------------------------
-- 用户管理操作
----------------------------------------------

-- 1. 用户注册
INSERT INTO users (username, password, email, full_name, phone)
VALUES ('alice_wong', 'p@ssw0rd!', 'alice@example.com', 'Alice Wong', '13900139000');

-- 2. 更新用户信息
UPDATE users
SET phone = '13912345678', last_login = SYSDATE
WHERE username = 'alice_wong';

-- 3. 删除非活跃用户(超过1年未登录)
DELETE FROM users
WHERE last_login < ADD_MONTHS(SYSDATE, -12)
AND user_id NOT IN (SELECT user_id FROM orders);

----------------------------------------------
-- 用户注册
DECLARE
v_user_id CHAR(10);
BEGIN
-- 生成用户ID
SELECT 'U' || LPAD(seq_user_id.NEXTVAL, 9, '0') INTO v_user_id FROM DUAL;

-- 插入用户
INSERT INTO users (user_id, username, password, email, full_name, phone)
VALUES (v_user_id, 'alice_wong', 'secure_pass123', 'alice@example.com', 'Alice Wong', '13900139000');

COMMIT;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('用户名或邮箱已存在');
ROLLBACK;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('注册失败: ' || SQLERRM);
ROLLBACK;
RAISE;
END;
/

-- 添加商品到购物车
DECLARE
v_user_id CHAR(10);
v_product1_id CHAR(8);
v_product2_id CHAR(8);
v_cart_exists NUMBER;
BEGIN
-- 获取用户ID
SELECT user_id INTO v_user_id
FROM users
WHERE username = 'alice_wong';

-- 检查购物车是否存在
SELECT COUNT(*) INTO v_cart_exists
FROM cart
WHERE user_id = v_user_id;

IF v_cart_exists = 0 THEN
-- 创建购物车
INSERT INTO cart (cart_id, user_id)
VALUES ('C' || LPAD(seq_cart_id.NEXTVAL, 9, '0'), v_user_id);
END IF;

-- 获取商品ID
SELECT product_id INTO v_product1_id
FROM products
WHERE product_name = '平板电脑 Pro';

SELECT product_id INTO v_product2_id
FROM products
WHERE product_name = '无线耳机';

-- 添加商品到购物车
add_to_cart(v_user_id, v_product1_id, 1);
add_to_cart(v_user_id, v_product2_id, 2);

COMMIT;
DBMS_OUTPUT.PUT_LINE('商品已成功添加到购物车');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('错误: 用户或商品不存在');
ROLLBACK;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('添加失败: ' || SQLERRM);
ROLLBACK;
RAISE;
END;
/

-- 创建订单
DECLARE
v_user_id CHAR(10);
v_address_id CHAR(8);
v_order_id CHAR(12);
BEGIN
-- 获取用户ID
SELECT user_id INTO v_user_id
FROM users
WHERE username = 'alice_wong';

-- 获取默认地址ID
SELECT address_id INTO v_address_id
FROM addresses
WHERE user_id = v_user_id AND is_default = 'Y';

-- 创建订单
create_order_from_cart(
p_user_id => v_user_id,
p_address_id => v_address_id,
p_payment_method => '微信支付',
p_payment_id => 'WXPAY20231003123456',
p_order_id => v_order_id
);

DBMS_OUTPUT.PUT_LINE('订单创建成功: ' || v_order_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('错误: 用户不存在或未设置默认地址');
ROLLBACK;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('订单创建失败: ' || SQLERRM);
ROLLBACK;
RAISE;
END;
/



-- 2.2 价格调整(基于类别的批量更新)
UPDATE products p
SET price = price *
CASE
WHEN (SELECT category_name FROM categories c WHERE c.category_id = p.category_id) = '智能手机' THEN 0.95
WHEN price > 3000 THEN 0.90
ELSE 0.98
END
WHERE create_date < ADD_MONTHS(SYSDATE, -6);

-- 2.3 商品下架(转移历史数据)
CREATE TABLE archived_products AS
SELECT * FROM products
WHERE stock_quantity = 0 AND create_date < ADD_MONTHS(SYSDATE, -12);

DELETE FROM products
WHERE product_id IN (SELECT product_id FROM archived_products);



----------------------------------------------
-- 4. 订单处理
----------------------------------------------

-- 4.1 批量创建测试订单
DECLARE
CURSOR user_cursor IS
SELECT u.user_id, a.address_id
FROM users u
JOIN addresses a ON u.user_id = a.user_id
WHERE a.is_default = 'Y'
FETCH FIRST 20 ROWS ONLY;

v_order_id CHAR(12);
BEGIN
FOR user_rec IN user_cursor LOOP
BEGIN
-- 为每个用户创建1-3个订单
FOR i IN 1..ROUND(DBMS_RANDOM.VALUE(1, 3)) LOOP
create_order_from_cart(
p_user_id => user_rec.user_id,
p_address_id => user_rec.address_id,
p_payment_method => CASE ROUND(DBMS_RANDOM.VALUE(1,3))
WHEN 1 THEN '支付宝'
WHEN 2 THEN '微信支付'
ELSE '银行卡' END,
p_payment_id => 'PAY' || TO_CHAR(SYSDATE, 'YYYYMMDD') || LPAD(DBMS_RANDOM.VALUE(1,99999), 5, '0'),
p_order_id => v_order_id
);

-- 随机更新订单状态
IF DBMS_RANDOM.VALUE > 0.3 THEN
update_order_status(v_order_id, '已付款');
IF DBMS_RANDOM.VALUE > 0.5 THEN
update_order_status(v_order_id, '已发货');
IF DBMS_RANDOM.VALUE > 0.7 THEN
update_order_status(v_order_id, '已完成');
END IF;
END IF;
ELSE
update_order_status(v_order_id, '已取消');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('用户 ' || user_rec.user_id || ' 创建订单失败: ' || SQLERRM);
END;
END LOOP;
COMMIT;
END;
/

-- 4.2 处理退货
DECLARE
v_order_id orders.order_id%TYPE;
v_product_id products.product_id%TYPE;
v_quantity INT;
BEGIN
-- 随机选择一个已完成订单
SELECT o.order_id, od.product_id, od.quantity
INTO v_order_id, v_product_id, v_quantity
FROM orders o
JOIN order_details od ON o.order_id = od.order_id
WHERE o.status = '已完成'
ORDER BY DBMS_RANDOM.VALUE
FETCH FIRST 1 ROW ONLY;

-- 退货部分商品
v_quantity := ROUND(v_quantity * 0.5);

-- 开始退货流程
UPDATE orders SET status = '退货处理中' WHERE order_id = v_order_id;

-- 增加库存
UPDATE products
SET stock_quantity = stock_quantity + v_quantity
WHERE product_id = v_product_id;

-- 添加退货记录
INSERT INTO order_details (order_id, product_id, quantity, unit_price)
SELECT v_order_id, v_product_id, -v_quantity, unit_price
FROM order_details
WHERE order_id = v_order_id AND product_id = v_product_id;

-- 更新订单总金额
UPDATE orders
SET total_amount = total_amount - (v_quantity * (
SELECT unit_price
FROM order_details
WHERE order_id = v_order_id AND product_id = v_product_id
))
WHERE order_id = v_order_id;

-- 完成退货
UPDATE orders SET status = '部分退货' WHERE order_id = v_order_id;

COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
----------------------------------------------
-- 6. 数据分析与报表
----------------------------------------------

-- 6.1 用户购买行为分析
SELECT
u.user_id,
u.username,
COUNT(DISTINCT o.order_id) AS total_orders,
SUM(o.total_amount) AS total_spent,
ROUND(AVG(o.total_amount), 2) AS avg_order_value,
MAX(o.order_date) AS last_order_date,
(SELECT COUNT(*) FROM cart_items ci JOIN cart c ON ci.cart_id = c.cart_id WHERE c.user_id = u.user_id) AS cart_items,
(SELECT COUNT(*) FROM addresses a WHERE a.user_id = u.user_id) AS address_count,
CASE
WHEN SUM(o.total_amount) > 10000 THEN 'VIP'
WHEN SUM(o.total_amount) > 5000 THEN '忠诚'
WHEN SUM(o.total_amount) > 1000 THEN '活跃'
ELSE '普通'
END AS user_level
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.username
ORDER BY total_spent DESC;

-- 6.2 商品销售漏斗分析
WITH cart_data AS (
SELECT
p.product_id,
COUNT(DISTINCT ci.cart_id) AS in_carts
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
GROUP BY p.product_id
),
order_data AS (
SELECT
od.product_id,
COUNT(DISTINCT o.order_id) AS ordered,
SUM(od.quantity) AS total_ordered
FROM order_details od
JOIN orders o ON od.order_id = o.order_id
GROUP BY od.product_id
)
SELECT
p.product_id,
p.product_name,
NVL(c.in_carts, 0) AS in_carts,
NVL(o.ordered, 0) AS ordered,
NVL(o.total_ordered, 0) AS total_ordered,
ROUND(NVL(o.ordered, 0) / NULLIF(NVL(c.in_carts, 0), 0) * 100 AS conversion_rate
FROM products p
LEFT JOIN cart_data c ON p.product_id = c.product_id
LEFT JOIN order_data o ON p.product_id = o.product_id
ORDER BY conversion_rate DESC NULLS LAST;

-- 6.3 月度销售报告(带同比环比)
SELECT
TO_CHAR(order_date, 'YYYY-MM') AS sales_month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_revenue,
LAG(COUNT(*), 1) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM')) AS prev_month_orders,
ROUND((COUNT(*) - LAG(COUNT(*), 1) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM'))) /
NULLIF(LAG(COUNT(*), 1) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM')), 0) * 100, 2) AS order_growth,
LAG(SUM(total_amount), 12) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM')) AS prev_year_revenue,
ROUND((SUM(total_amount) - LAG(SUM(total_amount), 12) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM'))) /
NULLIF(LAG(SUM(total_amount), 12) OVER (ORDER BY TO_CHAR(order_date, 'YYYY-MM')), 0) * 100, 2) AS yoy_growth
FROM orders
WHERE order_date > ADD_MONTHS(SYSDATE, -24)
GROUP BY TO_CHAR(order_date, 'YYYY-MM')
ORDER BY sales_month DESC;

----------------------------------------------
-- 7. 系统维护操作
----------------------------------------------

-- 7.1 数据归档
CREATE TABLE archived_orders AS
SELECT * FROM orders
WHERE order_date < ADD_MONTHS(SYSDATE, -24);

CREATE TABLE archived_order_details AS
SELECT od.*
FROM order_details od
JOIN archived_orders ao ON od.order_id = ao.order_id;

DELETE FROM order_details
WHERE order_id IN (SELECT order_id FROM archived_orders);

DELETE FROM orders
WHERE order_date < ADD_MONTHS(SYSDATE, -24);

-- 7.2 重建索引
ALTER INDEX idx_orders_date REBUILD;
ALTER INDEX idx_products_name REBUILD;

-- 7.3 收集统计信息
BEGIN
DBMS_STATS.GATHER_SCHEMA_STATS(
ownname => USER,
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
degree => DBMS_STATS.AUTO_DEGREE
);
END;
/

-- 7.4 数据库备份(模拟)
CREATE DATABASE LINK backup_link
CONNECT TO backup_user IDENTIFIED BY "backup_pass"
USING 'backup_db';

INSERT INTO orders@backup_link
SELECT * FROM orders
WHERE order_date > SYSDATE - 7;

INSERT INTO order_details@backup_link
SELECT * FROM order_details
WHERE order_id IN (SELECT order_id FROM orders WHERE order_date > SYSDATE - 7);

学生宿舍管理系统.zip