1. 项目概述
作为一名有着十年开发经验的Java全栈工程师,我想分享一个非常适合作为毕业设计的项目——基于SpringBoot的台球厅管理系统。这个项目不仅涵盖了企业级应用开发的完整流程,还融合了会员服务、桌台调度等实际业务场景,能够全面锻炼学生的技术实践能力。
这个系统主要解决台球厅日常运营中的三大核心问题:
- 会员管理混乱:传统手工记录方式效率低下且容易出错
- 桌台调度低效:高峰期人工安排容易引发顾客纠纷
- 经营数据分析缺失:缺乏数字化手段进行业绩统计和分析
技术栈选择上,我们采用:
- 后端:SpringBoot 2.7 + MyBatis Plus
- 前端:Vue 3 + Element Plus
- 数据库:MySQL 8.0
- 安全框架:Spring Security
2. 系统架构设计
2.1 MVC分层架构
系统采用经典的三层架构设计:
code复制表示层(View)
↓
业务逻辑层(Service)
↓
数据访问层(DAO)
这种分层设计使得各层职责明确,耦合度低,便于后期维护和扩展。在实际编码中,我特别建议遵循以下包结构:
code复制com.billiards
├── config // 配置类
├── controller // 控制器
├── service // 服务接口
├── service/impl // 服务实现
├── mapper // MyBatis映射接口
├── entity // 实体类
├── dto // 数据传输对象
├── vo // 视图对象
└── util // 工具类
2.2 技术选型考量
选择SpringBoot作为基础框架主要基于以下考虑:
- 自动配置:简化了传统SSM框架繁琐的XML配置
- 内嵌Tomcat:直接打包成可执行JAR,部署极其方便
- 丰富的Starter:快速集成各种常用组件
- 完善的生态:社区活跃,遇到问题容易找到解决方案
数据库选用MySQL 8.0而非5.7版本,主要因为:
- 性能提升:8.0版本查询速度提升明显
- JSON支持:更好的支持半结构化数据存储
- 窗口函数:方便实现复杂的统计分析
3. 核心功能实现
3.1 会员管理模块
会员管理是系统的核心功能之一,涉及以下关键表设计:
sql复制CREATE TABLE `member` (
`id` bigint NOT NULL AUTO_INCREMENT,
`card_no` varchar(20) NOT NULL COMMENT '会员卡号',
`name` varchar(50) NOT NULL COMMENT '姓名',
`phone` varchar(20) NOT NULL COMMENT '手机号',
`balance` decimal(10,2) DEFAULT '0.00' COMMENT '账户余额',
`points` int DEFAULT '0' COMMENT '积分',
`level` tinyint DEFAULT '1' COMMENT '会员等级',
`status` tinyint DEFAULT '1' COMMENT '状态(1-正常 0-冻结)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_card_no` (`card_no`),
KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
在代码实现上,我推荐使用MyBatis Plus的Active Record模式,可以大大简化CRUD操作:
java复制@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member>
implements MemberService {
@Override
public Page<MemberVO> queryPage(MemberQueryDTO queryDTO) {
return lambdaQuery()
.eq(StringUtils.isNotBlank(queryDTO.getCardNo()), Member::getCardNo, queryDTO.getCardNo())
.like(StringUtils.isNotBlank(queryDTO.getName()), Member::getName, queryDTO.getName())
.eq(StringUtils.isNotBlank(queryDTO.getPhone()), Member::getPhone, queryDTO.getPhone())
.eq(queryDTO.getLevel() != null, Member::getLevel, queryDTO.getLevel())
.page(new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()))
.convert(this::convertToVO);
}
private MemberVO convertToVO(Member member) {
// 转换逻辑...
}
}
3.2 桌台调度模块
桌台调度是系统的另一个核心功能,其状态机设计如下:
code复制[空闲] → (开台) → [使用中]
[使用中] → (结账) → [待清洁]
[待清洁] → (清洁完成) → [空闲]
实现这个状态流转时,我建议采用策略模式:
java复制public interface TableState {
void handle(Table table, Operation operation);
}
@Service
public class TableStateService {
private Map<String, TableState> stateMap;
public void changeState(Table table, Operation operation) {
TableState state = stateMap.get(table.getStatus());
state.handle(table, operation);
}
}
@Service
public class IdleState implements TableState {
@Override
public void handle(Table table, Operation operation) {
if (operation == Operation.OPEN) {
table.setStatus(TableStatus.IN_USE);
// 其他业务逻辑...
}
}
}
4. 关键技术实现
4.1 预约冲突检测
实现预约功能时,最关键的是冲突检测算法。我采用时间区间重叠检测法:
java复制public boolean checkTimeConflict(LocalDateTime start1, LocalDateTime end1,
LocalDateTime start2, LocalDateTime end2) {
return start1.isBefore(end2) && start2.isBefore(end1);
}
在实际查询中,使用MySQL的时空查询优化:
sql复制SELECT * FROM reservation
WHERE table_id = #{tableId}
AND (
(start_time < #{endTime} AND end_time > #{startTime})
OR (start_time = #{startTime} AND end_time = #{endTime})
)
AND status IN (0, 1) -- 0-待确认 1-已确认
4.2 消费结算逻辑
消费结算涉及多个业务规则:
- 会员折扣(根据等级)
- 时段定价(白天/晚上价格不同)
- 优惠券抵扣
- 积分抵扣
我采用责任链模式实现这个复杂逻辑:
java复制public abstract class PaymentHandler {
protected PaymentHandler next;
public void setNext(PaymentHandler next) {
this.next = next;
}
public abstract void handle(Order order, PaymentContext context);
}
@Service
public class MemberDiscountHandler extends PaymentHandler {
@Override
public void handle(Order order, PaymentContext context) {
Member member = memberService.getById(order.getMemberId());
double discount = memberLevelConfig.getDiscount(member.getLevel());
context.setAmount(context.getAmount() * discount);
if (next != null) {
next.handle(order, context);
}
}
}
5. 系统安全设计
5.1 认证与授权
采用Spring Security + JWT实现安全的认证体系:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/staff/**").hasAnyRole("STAFF", "ADMIN")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()));
}
}
5.2 敏感数据保护
对于会员手机号等敏感信息,采用AES对称加密存储:
java复制public class CryptoUtils {
private static final String KEY = "your-secret-key-16";
private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
public static String encrypt(String data) {
// 实现加密逻辑...
}
public static String decrypt(String encryptedData) {
// 实现解密逻辑...
}
}
6. 性能优化实践
6.1 缓存策略
采用多级缓存提升系统响应速度:
- 本地缓存(Caffeine):缓存热点数据
- Redis缓存:共享缓存,集群部署时使用
- 数据库缓存:MySQL查询缓存
java复制@Cacheable(value = "member", key = "#id", unless = "#result == null")
public Member getMemberById(Long id) {
return memberMapper.selectById(id);
}
@CacheEvict(value = "member", key = "#member.id")
public void updateMember(Member member) {
memberMapper.updateById(member);
}
6.2 SQL优化技巧
- 为常用查询字段添加索引
- 避免SELECT *,只查询需要的字段
- 使用JOIN替代子查询
- 大数据量分页使用延迟关联
sql复制-- 不好的写法
SELECT * FROM member WHERE name LIKE '%张%';
-- 优化后的写法
SELECT id, name, phone FROM member
WHERE name LIKE '张%' -- 左匹配可以使用索引
LIMIT 20;
7. 常见问题与解决方案
7.1 事务管理问题
在开发过程中,我发现很多同学容易忽略事务的传播行为。比如在会员充值时,需要同时更新余额和记录流水:
java复制@Service
@Transactional(rollbackFor = Exception.class)
public class MemberServiceImpl implements MemberService {
@Autowired
private TransactionRecordService recordService;
@Override
public void recharge(Long memberId, BigDecimal amount) {
// 更新会员余额
updateBalance(memberId, amount);
// 记录交易流水
TransactionRecord record = new TransactionRecord();
record.setMemberId(memberId);
record.setAmount(amount);
recordService.save(record); // 这里需要确保在同一个事务中
}
}
7.2 日期时间处理
处理预约时间时,推荐使用Java 8的DateTime API:
java复制public void checkReservationTime(LocalDateTime startTime, LocalDateTime endTime) {
// 检查营业时间
LocalTime start = startTime.toLocalTime();
if (start.isBefore(LocalTime.of(10, 0))
|| start.isAfter(LocalTime.of(22, 0))) {
throw new BusinessException("非营业时间");
}
// 检查时长是否合法
Duration duration = Duration.between(startTime, endTime);
if (duration.toMinutes() < 30 || duration.toHours() > 4) {
throw new BusinessException("时长不合法");
}
}
8. 项目扩展建议
这个基础版本完成后,可以考虑以下扩展方向:
- 微信小程序端:使用Uniapp开发客户自助预约小程序
- 数据分析看板:集成ECharts实现经营数据可视化
- 智能调度算法:基于历史数据预测高峰时段,优化桌台分配
- 人脸识别登录:集成百度AI实现刷脸登录
- 物联网集成:通过传感器实时监测桌台使用状态
对于想深入学习的同学,我建议重点研究:
- SpringBoot自动配置原理
- MyBatis插件开发(实现分表分库)
- Vue3组合式API
- 微服务架构改造(SpringCloud)
- 压力测试与性能调优
这个项目涵盖了企业级应用开发的完整技术栈,从需求分析到系统设计,从编码实现到测试部署,是非常好的全栈实践机会。我在实际开发中最大的体会是:良好的分层设计和清晰的代码结构比实现功能本身更重要,这直接决定了后期维护的成本。