数据库大作业 — 数据库设计与实施
沉浸式剧本杀平台的完整数据库设计——从 E-R 建模到 3NF 规范化,覆盖 10 张业务表、触发器、存储过程、RBAC 权限体系。
📋 项目概览
技术栈
SQLMySQL视图触发器存储过程RBAC
功能特性
- 沉浸式剧本平台数据库设计(10 张表)
- 用户/剧本/角色/线索/打卡点/场次/订单/排班/进度/评价
- 触发器:订单支付自动更新场次人数 + NPC 排班冲突检测
- 视图:场次概览 + 剧本评分汇总
- 存储过程:场次运营报表生成
- RBAC 四级角色权限体系(游客/NPC/运营/管理员)
还有 2 项
- 50+ 条测试数据覆盖全部业务场景
- 复杂查询:嵌套子查询 / EXISTS / GROUP BY HAVING
🚀 在线演示新窗口打开 ↗
💻 核心代码SQL · MySQL · 视图 · 触发器 · 存储过程 · RBAC
sql核心表结构(建表 DDL)
沉浸式剧本平台的核心数据模型,包含用户、剧本、角色、线索、打卡点 5 张主表。
-- =============================================
-- 沉浸式剧本平台 — 核心表结构
-- 作者: Solivagant
-- =============================================
-- 1. 用户表
CREATE TABLE User (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
phone VARCHAR(11) NOT NULL UNIQUE,
nickname VARCHAR(50) NOT NULL,
role_type TINYINT NOT NULL DEFAULT 0
COMMENT '0-游客 1-运营 2-NPC 3-管理员',
status TINYINT NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 剧本表
CREATE TABLE Script (
script_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL COMMENT '剧本名称',
description TEXT,
difficulty TINYINT NOT NULL DEFAULT 1,
duration INT NOT NULL COMMENT '时长(分)',
min_players INT NOT NULL DEFAULT 1,
max_players INT NOT NULL,
cover_url VARCHAR(255),
status TINYINT NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. 角色表
CREATE TABLE Role (
role_id INT AUTO_INCREMENT PRIMARY KEY,
script_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
description TEXT,
is_key_role TINYINT NOT NULL DEFAULT 0,
sort_order INT NOT NULL DEFAULT 0,
FOREIGN KEY (script_id) REFERENCES Script(script_id)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 4. 线索表
CREATE TABLE Clue (
clue_id INT AUTO_INCREMENT PRIMARY KEY,
script_id INT NOT NULL,
name VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
clue_type TINYINT NOT NULL DEFAULT 0,
trigger_type TINYINT NOT NULL DEFAULT 0,
sequence_num INT NOT NULL,
FOREIGN KEY (script_id) REFERENCES Script(script_id)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 5. 打卡点表
CREATE TABLE CheckPoint (
point_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
latitude DECIMAL(10,7) NOT NULL,
longitude DECIMAL(10,7) NOT NULL,
ar_content_url VARCHAR(255),
binding_clue_id INT,
is_required TINYINT NOT NULL DEFAULT 0,
FOREIGN KEY (binding_clue_id) REFERENCES Clue(clue_id)
ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;sql触发器:订单支付与排班冲突检测
两个关键触发器:支付状态变更自动更新场次报名人数,NPC 排班时间冲突检测防止重复调度。
-- 触发器1:订单支付 -> 场次人数自动更新
DELIMITER //
CREATE TRIGGER trg_order_payment_update_session
AFTER UPDATE ON `Order`
FOR EACH ROW
BEGIN
IF OLD.payment_status = 0 AND NEW.payment_status = 1 THEN
UPDATE `Session`
SET current_players = current_players + 1
WHERE session_id = NEW.session_id;
END IF;
IF OLD.payment_status = 1 AND NEW.payment_status = 2 THEN
UPDATE `Session`
SET current_players = current_players - 1
WHERE session_id = NEW.session_id;
END IF;
END//
DELIMITER ;
-- 触发器2:NPC排班时间冲突检测
DELIMITER //
CREATE TRIGGER trg_npcschedule_conflict_check
BEFORE INSERT ON NpcSchedule
FOR EACH ROW
BEGIN
DECLARE conflict_count INT;
SELECT COUNT(*) INTO conflict_count
FROM NpcSchedule ns
JOIN `Session` s ON ns.session_id = s.session_id
JOIN `Session` s_new ON s_new.session_id = NEW.session_id
WHERE ns.npc_id = NEW.npc_id
AND ns.schedule_date = NEW.schedule_date
AND (
(s.start_time <= s_new.start_time AND s.end_time > s_new.start_time)
OR
(s_new.start_time <= s.start_time AND s_new.end_time > s.start_time)
);
IF conflict_count > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '该NPC在此时间段已有排班冲突';
END IF;
END//
DELIMITER ;sql存储过程:场次运营报表
按剧本和时间范围生成运营数据,包含总订单数、总收入、逐场次明细、上座率。
DELIMITER //
CREATE PROCEDURE proc_session_report(
IN p_script_id INT,
IN p_start_date DATE,
IN p_end_date DATE
)
BEGIN
DECLARE v_total_orders INT DEFAULT 0;
DECLARE v_total_revenue DECIMAL(12,2) DEFAULT 0;
SELECT COUNT(o.order_id), COALESCE(SUM(o.amount), 0)
INTO v_total_orders, v_total_revenue
FROM `Session` se
LEFT JOIN `Order` o
ON se.session_id = o.session_id AND o.payment_status = 1
WHERE (p_script_id IS NULL OR se.script_id = p_script_id)
AND DATE(se.start_time) BETWEEN p_start_date AND p_end_date;
-- 汇总输出
SELECT p_script_id AS script_id,
p_start_date AS date_from, p_end_date AS date_to,
v_total_orders AS paid_orders,
v_total_revenue AS revenue;
-- 逐场次明细
SELECT s.name AS script_name, se.session_id,
se.start_time,
CONCAT(se.current_players, '/', se.max_players) AS occupancy,
ROUND(se.current_players / se.max_players * 100, 1) AS rate,
se.price,
COUNT(o.order_id) AS order_count,
COALESCE(SUM(o.amount), 0) AS session_revenue
FROM `Session` se
JOIN Script s ON se.script_id = s.script_id
LEFT JOIN `Order` o
ON se.session_id = o.session_id AND o.payment_status = 1
WHERE (p_script_id IS NULL OR se.script_id = p_script_id)
AND DATE(se.start_time) BETWEEN p_start_date AND p_end_date
GROUP BY se.session_id
ORDER BY se.start_time;
END//
DELIMITER ;
-- 调用:CALL proc_session_report(1, '2026-06-01', '2026-06-30');📁 源文件清单
| 路径 | 说明 | 行数 |
|---|---|---|
大作业二.sql | 完整数据库设计(建表/视图/触发器/存储过程/权限/测试数据) | 709 |