1. 项目背景与核心需求
旅游行业数字化进程的加速让景区门票预订系统成为刚需。去年帮杭州某4A景区做系统升级时,我深刻体会到传统窗口售票的痛点:旺季排队2小时起、黄牛票泛滥、财务对账困难。这正是我们开发这套SSM5架构的旅游景点门票预订网站的初衷。
从技术角度看,这类系统需要解决三个核心问题:
- 高并发场景下的票务库存精准控制(避免超卖)
- 多终端适配的购票流程(微信/H5/PC全覆盖)
- 实时动态的票价策略管理(节假日/时段差异化定价)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择SSM5组合
SSM(Spring+SpringMVC+MyBatis)框架的轻量级特性特别适合中小型旅游系统。最近帮学生调试项目时发现,相比SpringBoot的自动配置,SSM5的显式配置更利于理解底层机制。具体版本选择:
- Spring 5.3.18(控制反转+事务管理)
- SpringMVC 5.3.18(RESTful接口设计)
- MyBatis 3.5.9(SQL优化空间大)
实际开发中发现,MyBatis的二级缓存会引发库存同步问题,建议关闭并改用Redis分布式锁
2.2 分层架构实现
典型的三层架构在门票系统中要特别注意边界:
code复制表现层:JSP+Thymeleaf(兼容老浏览器)
业务层:门票状态机设计(待支付/已预订/已使用)
持久层:MyBatis动态SQL+乐观锁
数据库表设计有个易错点:ticket表需要包含version字段实现乐观锁,避免超卖:
sql复制CREATE TABLE ticket (
id BIGINT PRIMARY KEY,
scenic_id BIGINT,
date DATE,
time_slot VARCHAR(20),
price DECIMAL(10,2),
stock INT,
version INT DEFAULT 0 -- 关键字段
);
3. 核心功能实现细节
3.1 高并发售票控制
景区旺季瞬时并发可达5000+,我们采用分级限流策略:
- Nginx层限流5000QPS
- 业务层Redis分布式锁(Redisson实现)
- 数据库乐观锁兜底
关键代码示例:
java复制public boolean purchase(Long ticketId, Integer quantity) {
// 获取Redisson分布式锁
RLock lock = redissonClient.getLock("ticket:" + ticketId);
try {
lock.lock(5, TimeUnit.SECONDS); // 最长锁定5秒
Ticket ticket = ticketMapper.selectForUpdate(ticketId);
if (ticket.getStock() >= quantity) {
ticket.setStock(ticket.getStock() - quantity);
ticket.setVersion(ticket.getVersion() + 1);
return ticketMapper.updateWithVersion(ticket) > 0;
}
return false;
} finally {
lock.unlock();
}
}
3.2 动态票价策略
通过策略模式实现不同场景定价:
java复制public interface PricingStrategy {
BigDecimal calculate(BigDecimal basePrice);
}
// 节假日策略实现
public class HolidayStrategy implements PricingStrategy {
@Override
public BigDecimal calculate(BigDecimal basePrice) {
return basePrice.multiply(new BigDecimal("1.5"));
}
}
在管理后台通过责任链模式实现策略组合:
code复制基础价格 → 时段加成 → 节假日加成 → 促销折扣
4. 典型问题排查实录
4.1 库存超卖问题排查
某景区上线首日出现库存负数,排查过程:
- 检查Redis监控发现锁过期时间设置过短(原2秒改为5秒)
- 日志显示部分请求走到数据库乐观锁环节
- 最终定位到@Transactional注解未正确传播锁
解决方案:
- 增加本地缓存预热
- 调整事务隔离级别为REPEATABLE_READ
- 添加库存预警机制
4.2 支付超时处理
支付回调超时会导致"幽灵订单",我们设计的状态机:
code复制[待支付] --30min--> [自动取消]
--支付成功--> [已预订]
--退款申请--> [退款中]
关键是要配置Spring的@Scheduled定时任务扫描超时订单:
java复制@Scheduled(cron = "0 */5 * * * ?")
public void cancelTimeoutOrders() {
List<Order> orders = orderMapper.selectTimeoutOrders(30);
orders.forEach(order -> {
order.setStatus(OrderStatus.CANCELLED);
orderMapper.update(order);
ticketMapper.rollbackStock(order.getTicketId(), order.getQuantity());
});
}
5. 性能优化实践
5.1 缓存设计技巧
多级缓存策略实测效果:
code复制Redis集群(热点数据) → Caffeine本地缓存(用户维度的) → 数据库
特别注意缓存穿透防护:
java复制public Ticket getTicket(Long id) {
String key = "ticket:" + id;
// 布隆过滤器前置校验
if (!bloomFilter.mightContain(key)) {
return null;
}
// 缓存查询逻辑...
}
5.2 SQL优化案例
景区列表页的N+1查询问题优化:
xml复制<!-- 改造前 -->
<select id="selectScenicList" resultType="Scenic">
SELECT * FROM scenic
</select>
<!-- 改造后 -->
<select id="selectScenicListWithTickets" resultMap="scenicResultMap">
SELECT s.*, t.id as ticket_id, t.date, t.time_slot
FROM scenic s LEFT JOIN ticket t ON s.id = t.scenic_id
WHERE t.date >= CURDATE()
</select>
优化后QPS从120提升到2100,响应时间从450ms降到28ms。
6. 安全防护要点
6.1 防黄牛策略
结合业务特征的多维度防控:
- 设备指纹识别(通过js收集浏览器特征)
- 购票频率限制(同一身份证1天最多5单)
- 人机验证升级(滑动拼图+行为分析)
6.2 敏感数据保护
门票二维码生成要特别注意:
java复制public String generateQrCode(Order order) {
String raw = order.getId() + "|" + order.getTicketId();
return AESUtil.encrypt(raw, secretKey); // AES-256加密
}
密钥管理建议采用HSM硬件加密机,避免硬编码在代码中。
7. 部署与监控方案
7.1 容器化部署
Docker Compose编排示例:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
redis:
image: redis:6
volumes:
- redis_data:/data
7.2 监控指标配置
Prometheus需要监控的关键指标:
- 门票库存余量(grafana设置阈值告警)
- 订单创建成功率(低于99%触发预警)
- 支付回调平均耗时(超过800ms需优化)
我在生产环境发现,订单支付成功率与CDN节点分布强相关,建议做地域化监控。
8. 扩展方向建议
现有系统可以进一步扩展:
- 智能推荐系统(基于用户历史游览记录)
- 虚拟排队功能(实时展示景区人流热力图)
- 旅行社API对接(开放库存接口)
最近实施的某景区项目中,通过接入人脸识别闸机,将验票效率提升了6倍。这需要与硬件厂商深度对接,注意协议兼容性问题。
