1. 项目概述
剧本杀作为一种新兴的社交娱乐方式,近年来在国内迅速流行。为了满足剧本杀门店的数字化管理需求,我们基于SpringBoot+Vue技术栈开发了一套完整的剧本杀管理系统。这套系统不仅实现了剧本杀门店的日常运营管理,还为学生毕业设计提供了完整的参考案例。
作为一名有10年开发经验的Java全栈工程师,我在实际开发过程中发现,很多剧本杀门店仍在使用Excel表格或纸质文档进行管理,效率低下且容易出错。这套系统正是为了解决这些痛点而设计,涵盖了从剧本管理、场次预约到会员管理的全流程功能。
2. 系统架构设计
2.1 技术选型解析
在技术选型上,我们采用了目前主流的Java全栈技术组合:
后端技术栈:
- Spring Boot 2.7.x:简化配置,快速构建微服务
- MyBatis-Plus 3.5.x:增强型ORM框架,简化数据库操作
- Shiro 1.10.x:轻量级安全框架,处理认证授权
- Redis 6.x:缓存热点数据,提升系统响应速度
前端技术栈:
- Vue 3.x:渐进式前端框架
- Element Plus:UI组件库
- Axios:HTTP请求库
- Vue Router:前端路由管理
数据库:
- MySQL 8.0:关系型数据库
- Redis 6.x:缓存数据库
提示:选择这些技术栈主要基于以下考虑:
- Spring Boot的自动配置和起步依赖能显著提升开发效率
- Vue 3.x的组合式API更适合复杂前端应用的开发
- MyBatis-Plus在MyBatis基础上提供了更多便捷功能
- MySQL 8.0在性能和功能上都有显著提升
2.2 系统架构详解
系统采用典型的三层架构设计:
- 表现层:基于Vue的前端界面,负责用户交互和数据展示
- 业务逻辑层:Spring Boot实现的核心业务处理
- 数据访问层:MyBatis-Plus封装的数据库操作
这种分层架构使得系统各模块职责明确,便于维护和扩展。在实际开发中,我们还特别注意了以下几点:
- 前后端完全分离,通过RESTful API进行通信
- 使用Swagger生成API文档,便于前后端协作
- 采用JWT进行身份认证,保证接口安全性
- 引入Redis缓存高频访问数据,如剧本信息、用户信息等
3. 核心功能实现
3.1 用户认证模块
用户认证是系统的门户,我们实现了完整的注册、登录、权限控制流程:
java复制// 用户登录核心代码示例
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO) {
// 1. 验证验证码
String verifyCode = redisTemplate.opsForValue().get(loginDTO.getUuid());
if (StringUtils.isBlank(verifyCode)) {
return Result.fail("验证码已过期");
}
if (!loginDTO.getCode().equalsIgnoreCase(verifyCode)) {
return Result.fail("验证码错误");
}
// 2. 查询用户
User user = userService.lambdaQuery()
.eq(User::getUsername, loginDTO.getUsername())
.one();
// 3. 验证密码
if (user == null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
return Result.fail("用户名或密码错误");
}
// 4. 生成Token
String token = JwtUtil.generateToken(user.getId().toString());
// 5. 返回结果
Map<String, Object> data = new HashMap<>();
data.put("token", token);
data.put("user", user);
return Result.success(data);
}
注意事项:
- 密码必须使用BCrypt等安全算法加密存储
- JWT Token需要设置合理的过期时间
- 敏感操作需要二次验证
- 登录失败次数限制防止暴力破解
3.2 剧本管理模块
剧本是系统的核心业务对象,我们设计了完善的CRUD功能:
java复制// 剧本分页查询实现
@GetMapping("/page")
public Result page(Page<Script> page, ScriptQueryDTO queryDTO) {
return Result.success(scriptService.lambdaQuery()
.like(StringUtils.isNotBlank(queryDTO.getName()), Script::getName, queryDTO.getName())
.eq(queryDTO.getType() != null, Script::getType, queryDTO.getType())
.between(queryDTO.getMinDuration() != null && queryDTO.getMaxDuration() != null,
Script::getDuration,
queryDTO.getMinDuration(),
queryDTO.getMaxDuration())
.page(page));
}
剧本实体关键字段设计:
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | Long | 主键ID |
| name | String | 剧本名称 |
| author | String | 作者 |
| type | Integer | 剧本类型(1:情感,2:推理,...) |
| duration | Integer | 预计时长(分钟) |
| player_num | Integer | 适合玩家数量 |
| difficulty | Integer | 难度系数(1-5) |
| cover_img | String | 封面图片URL |
| description | String | 剧本描述 |
| content | Text | 剧本详细内容 |
| status | Integer | 状态(0:下架,1:上架) |
3.3 场次预约模块
场次预约是系统的核心业务流程,我们实现了完整的预约流程:
- 玩家选择剧本和时段
- 系统检查时段可用性
- 确认预约并支付定金
- 生成预约记录
- 发送通知提醒
java复制// 场次预约核心逻辑
@Transactional
public Result bookSession(BookDTO bookDTO) {
// 1. 检查场次是否可用
Session session = sessionService.getById(bookDTO.getSessionId());
if (session == null || session.getStatus() != 0) {
return Result.fail("场次不可用");
}
// 2. 检查玩家数量是否匹配
Script script = scriptService.getById(session.getScriptId());
if (script.getPlayerNum() != bookDTO.getPlayerIds().size()) {
return Result.fail("玩家数量不匹配");
}
// 3. 创建预约记录
Order order = new Order();
order.setSessionId(session.getId());
order.setUserId(bookDTO.getUserId());
order.setPlayerIds(StringUtils.join(bookDTO.getPlayerIds(), ","));
order.setAmount(script.getPrice());
order.setStatus(0); // 待支付
orderService.save(order);
// 4. 更新场次状态
session.setStatus(1); // 已预约
sessionService.updateById(session);
// 5. 发送通知
messageService.sendBookSuccessMessage(order.getId());
return Result.success(order.getId());
}
4. 数据库设计
4.1 核心表结构
用户表(user):
sql复制CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`gender` tinyint DEFAULT '0' COMMENT '性别(0:未知,1:男,2:女)',
`birthday` date DEFAULT NULL COMMENT '生日',
`status` tinyint DEFAULT '1' COMMENT '状态(0:禁用,1:正常)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`),
KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
剧本表(script):
sql复制CREATE TABLE `script` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '剧本名称',
`author` varchar(50) DEFAULT NULL COMMENT '作者',
`type` tinyint DEFAULT NULL COMMENT '类型(1:情感,2:推理,3:恐怖,4:机制,5:欢乐)',
`duration` int DEFAULT NULL COMMENT '预计时长(分钟)',
`player_num` int DEFAULT NULL COMMENT '适合玩家数量',
`difficulty` tinyint DEFAULT NULL COMMENT '难度系数(1-5)',
`cover_img` varchar(255) DEFAULT NULL COMMENT '封面图片',
`description` text COMMENT '剧本描述',
`content` longtext COMMENT '剧本内容',
`price` decimal(10,2) DEFAULT NULL COMMENT '价格',
`status` tinyint DEFAULT '1' COMMENT '状态(0:下架,1:上架)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_type` (`type`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
4.2 表关系设计
系统主要表之间的关系如下:
- 用户-角色:多对多关系,通过user_role关联表实现
- 剧本-场次:一对多关系,一个剧本可以有多个场次
- 场次-订单:一对多关系,一个场次可以生成多个订单(如改期)
- 订单-玩家:多对多关系,通过order_player关联表实现
5. 系统部署与运维
5.1 环境准备
开发环境:
- JDK 17+
- Node.js 16+
- MySQL 8.0+
- Redis 6.0+
- Maven 3.8+
生产环境推荐配置:
- 服务器:2核4G以上
- 操作系统:Linux(CentOS 7+/Ubuntu 20.04+)
- Web服务器:Nginx 1.18+
- 应用服务器:Tomcat 9+/Undertow
- 数据库:MySQL 8.0+主从配置
- 缓存:Redis 6.0+哨兵模式
5.2 部署流程
- 后端部署:
bash复制# 克隆项目
git clone https://github.com/example/murder-mystery-system.git
# 打包
mvn clean package -DskipTests
# 运行
java -jar target/murder-mystery-system.jar --spring.profiles.active=prod
- 前端部署:
bash复制# 安装依赖
npm install
# 构建生产环境代码
npm run build
# 将dist目录内容部署到Nginx
cp -r dist/* /usr/share/nginx/html/
- Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
6. 常见问题与解决方案
6.1 开发阶段问题
问题1:跨域访问错误
解决方案:
- 开发环境可以在前端配置代理:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
- 生产环境通过Nginx反向代理解决
- 后端也可以配置CORS:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
问题2:MyBatis-Plus主键ID过长
解决方案:
- 配置ID生成策略:
java复制@TableId(type = IdType.ASSIGN_ID)
private Long id;
- 或者在application.yml中全局配置:
yaml复制mybatis-plus:
global-config:
db-config:
id-type: assign_id
6.2 生产环境问题
问题1:并发预约冲突
解决方案:
- 使用数据库乐观锁:
java复制@Version
private Integer version;
- 或者使用Redis分布式锁:
java复制public boolean bookWithLock(Long sessionId) {
String lockKey = "lock:session:" + sessionId;
String lockValue = UUID.randomUUID().toString();
try {
// 尝试获取锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
// 执行业务逻辑
return doBook(sessionId);
}
return false;
} finally {
// 释放锁
if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
问题2:性能瓶颈
解决方案:
- 使用Redis缓存热点数据:
java复制@Cacheable(value = "script", key = "#id")
public Script getById(Long id) {
return getById(id);
}
- 数据库查询优化:
- 添加合适的索引
- 避免SELECT *
- 使用JOIN优化复杂查询
- 分库分表处理大数据量表
7. 项目扩展方向
在实际开发和使用过程中,可以考虑以下几个扩展方向:
- 微信小程序端:开发配套的小程序,方便玩家预约和查看信息
- 数据分析模块:增加剧本热度、玩家偏好等数据分析功能
- AI推荐系统:基于玩家历史记录推荐适合的剧本
- VR剧本杀:结合VR技术开发线上剧本杀体验
- 会员积分系统:增加会员积分和兑换功能,提升用户粘性
这个剧本杀管理系统从设计到实现,涵盖了企业级应用开发的完整流程。在开发过程中,我们特别注重了系统的实用性、稳定性和可扩展性,确保它不仅能满足毕业设计的要求,更能实际应用于剧本杀门店的日常运营管理。