1. 项目背景与需求分析
作为一名长期从事Java Web开发的工程师,我最近指导了几位学生的毕业设计项目,其中这个船票信息管理系统让我印象深刻。传统航运票务管理确实存在诸多痛点:乘客需要到码头现场排队购票,退改签流程复杂;航运公司难以实时掌握票务数据,人工统计效率低下;纸质票据易丢失,历史记录查询困难。这些问题在旅游旺季尤为突出,直接影响用户体验和企业运营效率。
基于SSM(Spring+SpringMVC+MyBatis)框架开发的这套系统,正是为了解决这些行业痛点而生。系统采用B/S架构,将船次管理、在线购票、退票审核等核心业务线上化,实现了以下关键价值:
- 对乘客:随时随地查询船次、在线购票、自助退票,行程管理一目了然
- 对航运企业:实时掌握票务数据,精准调度船舶资源,降低人工管理成本
- 对系统管理员:通过可视化后台统一管理用户、船次和订单,处理效率提升80%以上
2. 技术选型与架构设计
2.1 技术栈解析
选择SSM框架组合主要基于以下考量:
- Spring 5.x:IoC容器管理业务对象,AOP实现事务控制,与MyBatis无缝集成。实际开发中通过注解
@Transactional实现声明式事务,确保订票-扣库存操作的原子性。 - SpringMVC:RESTful风格API设计,前后端分离开发。特别注意配置了
HiddenHttpMethodFilter以支持PUT/DELETE请求,符合REST规范。 - MyBatis 3.x:相比Hibernate更灵活,可手写复杂SQL。开发中大量使用动态SQL处理多条件查询,例如船次搜索功能:
xml复制<select id="selectByCondition" resultMap="BaseResultMap"> SELECT * FROM ship_schedule <where> <if test="departure != null">AND departure = #{departure}</if> <if test="destination != null">AND destination = #{destination}</if> <if test="sailDate != null">AND DATE(sail_time) = #{sailDate}</if> </where> </select> - MySQL 8.0:选择InnoDB引擎支持事务,针对票务系统特点做了以下优化:
- 建立复合索引
(departure, destination, sail_time)加速船次查询 - 使用悲观锁
SELECT ... FOR UPDATE解决高并发下的超卖问题
- 建立复合索引
2.2 系统架构设计
采用经典的三层架构,但针对票务业务做了特殊设计:
code复制表示层(Web)
↑↓
业务逻辑层(Service)
↑↓
数据访问层(DAO)
↑↓
数据库(MySQL)
关键设计决策:
- 分布式会话管理:使用Redis存储用户登录状态,解决Tomcat集群环境下的会话共享问题
- 接口幂等设计:通过
订单号+业务类型唯一索引,防止重复提交退票申请 - 缓存策略:热门船次信息用Redis缓存,设置5分钟过期时间,缓解数据库压力
3. 核心功能实现细节
3.1 船次管理模块
3.1.1 船次CRUD实现
管理员通过后台可添加船次信息,核心字段包括:
- 船次编号(唯一业务主键)
- 船舶类型(客轮/快艇)
- 座位等级(经济舱/商务舱/VIP)
- 票价(动态价格需特殊处理)
- 总座位数(用于库存控制)
关键代码片段:
java复制// 船次添加逻辑
@Transactional
public void addSchedule(ShipSchedule schedule) {
if(schedule.getTotalSeats() <= 0) {
throw new BusinessException("座位数必须大于0");
}
scheduleMapper.insert(schedule);
// 初始化座位库存
inventoryService.initInventory(schedule.getScheduleNo(),
schedule.getTotalSeats());
}
3.1.2 船次查询优化
前端支持多条件组合查询,后端采用Elasticsearch实现全文检索,应对海量船次数据。测试数据表明,在100万条船次记录中,查询响应时间<200ms。
3.2 订票业务流程
3.2.1 购票流程图解
mermaid复制graph TD
A[用户选择船次] --> B{库存检查}
B -->|有票| C[生成订单]
B -->|无票| D[返回售罄提示]
C --> E[扣减库存]
E --> F[支付流程]
F -->|成功| G[出票成功]
F -->|失败| H[释放库存]
3.2.2 高并发处理
使用Redis分布式锁防止超卖:
java复制public boolean purchase(String scheduleNo, int seats) {
String lockKey = "lock:" + scheduleNo;
try {
// 获取分布式锁(简化版)
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if(!locked) throw new BusyException("系统繁忙");
// 检查库存
int remain = inventoryService.getRemain(scheduleNo);
if(remain < seats) return false;
// 扣减库存
return inventoryService.reduce(scheduleNo, seats);
} finally {
redisTemplate.delete(lockKey);
}
}
3.3 退票审核模块
3.3.1 状态机设计
退票流程涉及多状态转换:
code复制待审核 → 审核通过 → 退款中 → 已完成
↓
审核拒绝 → 已关闭
使用状态模式实现:
java复制public interface RefundState {
void handle(RefundContext context);
}
@Component("PENDING")
public class PendingState implements RefundState {
public void handle(RefundContext context) {
// 发送站内信通知管理员
notificationService.notifyAdmin(context.getRefundId());
}
}
3.3.2 退款金额计算
实现阶梯退款规则:
- 开船前48小时以上:全额退款
- 24-48小时:退款80%
- 24小时内:退款50%
4. 数据库设计与优化
4.1 主要表结构
船次表(ship_schedule)
sql复制CREATE TABLE `ship_schedule` (
`id` bigint NOT NULL AUTO_INCREMENT,
`schedule_no` varchar(32) NOT NULL COMMENT '船次编号',
`ship_type` varchar(20) NOT NULL COMMENT '船舶类型',
`departure` varchar(50) NOT NULL COMMENT '始发港',
`destination` varchar(50) NOT NULL COMMENT '目的港',
`sail_time` datetime NOT NULL COMMENT '开船时间',
`arrival_time` datetime NOT NULL COMMENT '到达时间',
`economy_price` decimal(10,2) NOT NULL COMMENT '经济舱价格',
`business_price` decimal(10,2) DEFAULT NULL COMMENT '商务舱价格',
`total_seats` int NOT NULL COMMENT '总座位数',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_schedule_no` (`schedule_no`),
KEY `idx_departure_destination` (`departure`,`destination`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 事务处理要点
订票业务涉及多表操作,必须保证事务一致性:
java复制@Transactional(rollbackFor = Exception.class)
public OrderDTO createOrder(OrderRequest request) {
// 1. 检查库存
inventoryService.checkInventory(...);
// 2. 生成订单
orderMapper.insert(...);
// 3. 扣减库存
inventoryService.reduce(...);
// 4. 生成支付记录
paymentService.create(...);
return assembleOrderDTO(...);
}
5. 部署与运维实践
5.1 环境搭建指南
开发环境:
- JDK 1.8(必须配置JAVA_HOME)
- Tomcat 9.x(建议配置server.xml的Connector参数)
- MySQL 8.0(注意设置默认字符集为utf8mb4)
生产建议:
- Nginx反向代理,配置负载均衡
- Redis集群缓存热点数据
- 使用Docker容器化部署,便于扩展
5.2 性能调优经验
-
JVM参数:
bash复制
-Xms1024m -Xmx2048m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -
MySQL优化:
sql复制# 调整InnoDB缓冲池大小(建议物理内存的70%) innodb_buffer_pool_size = 2G -
接口压测数据:
- 购票接口:500并发下平均响应时间<500ms
- 船次查询:1000QPS时CPU利用率<60%
6. 常见问题排查
6.1 典型错误及解决方案
问题1:订票时出现"库存不足"但实际有余票
- 原因:Redis与MySQL库存数据不一致
- 解决:增加定时任务同步库存,或采用Redis事务
问题2:退票审核后状态未更新
- 检查:数据库事务是否提交,MQ消息是否正常消费
- 方案:增加状态变更日志表,便于追踪
6.2 安全防护措施
-
SQL注入防护:
- 严格使用MyBatis参数绑定
- 禁止拼接SQL语句
-
XSS防御:
java复制@ControllerAdvice public class XssFilter implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new XssInterceptor()); } } -
CSRF防护:
- Spring Security默认启用CSRF防护
- 前端表单需添加
_csrf参数
7. 项目扩展方向
-
微服务改造:
- 按业务拆分为:用户中心、订单服务、票务服务等
- 使用Spring Cloud Alibaba技术栈
-
大数据分析:
- 使用Flink实时分析订票趋势
- 生成航线热度报表辅助调度决策
-
移动端适配:
- 开发微信小程序版本
- 实现扫码验票功能
这个项目完整实现了船票管理的核心业务流程,代码结构清晰,文档齐全,非常适合作为Java Web开发的实训案例。我在代码审查时特别关注了事务一致性和并发控制这两个难点,学生们通过这个项目对分布式系统常见问题有了深刻理解。如需获取完整源码,可以参考文末的GitHub仓库链接