1. 项目背景与需求分析
美术馆作为城市文化地标,每年接待大量参观者。传统的人工预约和现场排队方式存在诸多痛点:高峰时段排队时间长、预约信息管理混乱、参观体验差。2023年国内主要美术馆的统计数据显示,周末平均排队时间超过90分钟,30%的参观者因预约流程繁琐而放弃参观。
微信小程序预约系统能有效解决这些问题。小程序无需下载安装、即用即走的特点,使其成为轻量化解决方案的理想载体。通过线上预约分流,可实现:
- 参观时段精准控制(每30分钟为一个时段)
- 人流量均衡分布(系统自动推荐非高峰时段)
- 电子票务无接触核验(扫码入场)
- 参观数据统计分析(为运营决策提供支持)
2. 技术架构设计
2.1 整体技术栈选型
采用前后端分离架构,主要技术组件包括:
code复制前端:微信小程序 + Vant Weapp组件库
后端:Spring Boot 2.7 + MyBatis-Plus
数据库:MySQL 8.0 + Redis缓存
部署:Docker + Nginx
选型依据:
- 微信小程序覆盖12亿月活用户,开发成本低
- Spring Boot简化后端开发,内置Tomcat支持快速部署
- MySQL关系型数据库保证事务一致性
- Redis缓存高频访问的余票数据
2.2 系统模块划分
code复制用户端功能模块:
- 注册登录(微信授权+手机号绑定)
- 展览信息浏览
- 预约时段选择
- 电子票生成与核销
- 个人中心(历史预约记录)
管理端功能模块:
- 展览排期管理
- 预约规则配置
- 实时监控看板
- 数据统计分析
- 系统权限管理
3. 核心功能实现细节
3.1 高并发预约处理
采用分布式锁解决超卖问题:
java复制// 基于Redis的分布式锁实现
public boolean tryLock(String lockKey, String requestId, int expireTime) {
return redisTemplate.opsForValue().setIfAbsent(
lockKey,
requestId,
expireTime,
TimeUnit.SECONDS
);
}
// 预约业务逻辑
public Result makeReservation(Long exhibitionId, LocalDateTime timeSlot) {
String lockKey = "reserve:" + exhibitionId + ":" + timeSlot;
try {
if (!tryLock(lockKey, uuid, 10)) {
return Result.fail("当前时段预约人数过多");
}
// 检查余票
// 扣减库存
// 生成订单
} finally {
releaseLock(lockKey, uuid);
}
}
3.2 微信支付集成
支付流程关键代码:
java复制@PostMapping("/pay")
public WxPayJSAPIResponse pay(@RequestBody Order order) {
// 1. 校验订单
Order dbOrder = orderService.validate(order.getId());
// 2. 调用微信支付API
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setBody("美术馆门票");
request.setOutTradeNo(dbOrder.getOrderNo());
request.setTotalFee(dbOrder.getAmount());
request.setOpenid(dbOrder.getOpenid());
// 3. 返回支付参数
return wxPayService.createOrder(request);
}
注意事项:
- 支付结果异步通知需做好幂等处理
- 建议设置15分钟未支付自动取消
- 金额单位需转换为分(微信API要求)
4. 数据库设计优化
4.1 主要表结构
sql复制-- 展览表
CREATE TABLE `exhibition` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL COMMENT '展览名称',
`cover_img` varchar(255) NOT NULL COMMENT '封面图',
`start_date` date NOT NULL,
`end_date` date NOT NULL,
`max_daily` int DEFAULT 1000 COMMENT '每日最大接待量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 预约时段表
CREATE TABLE `time_slot` (
`id` bigint NOT NULL AUTO_INCREMENT,
`exhibition_id` bigint NOT NULL,
`slot_time` datetime NOT NULL COMMENT '具体时段',
`total` int NOT NULL COMMENT '总名额',
`remain` int NOT NULL COMMENT '剩余名额',
PRIMARY KEY (`id`),
KEY `idx_exhibition_time` (`exhibition_id`,`slot_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 预约记录表
CREATE TABLE `reservation` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`time_slot_id` bigint NOT NULL,
`order_no` varchar(32) NOT NULL COMMENT '订单编号',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0待支付 1已预约 2已取消',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user` (`user_id`),
KEY `idx_time_slot` (`time_slot_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 性能优化措施
-
余票缓存:使用Redis Hash存储各时段余票
code复制HSET exhibition:1 2024-09-20T10:00:00 50 HINCRBY exhibition:1 2024-09-20T10:00:00 -1 -
读写分离:查询走从库,写入走主库
-
历史数据归档:三个月前的记录迁移到历史表
5. 安全防护方案
5.1 接口安全
-
防刷策略:
- 预约接口限流(Guava RateLimiter)
- 关键操作验证码校验
- 同一账号15分钟内最多3次预约
-
数据加密:
java复制// 敏感信息加密存储 @Column(columnDefinition = "varchar(255) COMMENT '手机号'") @FieldEncrypt private String mobile;
5.2 小程序安全
- 配置内容安全域名
- 开启HTTPS传输
- 定期更新SSL证书
- 敏感权限动态获取(如位置信息)
6. 运营数据分析
通过Elasticsearch+Kibana构建看板,关键指标:
- 实时参观人数
- 各时段预约热度
- 用户来源地域分布
- 取消率分析
示例统计SQL:
sql复制SELECT
DATE_FORMAT(slot_time, '%H:%i') AS time,
AVG(remain/total) AS vacancy_rate
FROM time_slot
WHERE exhibition_id = 1
GROUP BY time
ORDER BY time
7. 项目部署方案
7.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- ./data/mysql:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
app:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
7.2 监控告警
-
Prometheus采集指标:
- JVM内存使用
- 接口响应时间
- 数据库连接数
-
关键阈值告警:
- 余票不足10%时通知运营
- 500错误率超过1%时通知开发
8. 实际开发中的经验总结
-
微信小程序审核注意:
- 类目必须选择"文娱-展览/艺术"
- 需提供《网络文化经营许可证》
- 支付功能需企业主体
-
性能优化实践:
- 时段查询接口添加二级缓存(Redis → 本地缓存)
- 批量生成未来30天的时段数据(避免实时计算)
-
异常处理要点:
java复制// 微信支付回调处理 @Transactional(rollbackFor = Exception.class) public void handlePayNotify(String xmlData) { try { WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlData); if ("SUCCESS".equals(result.getResultCode())) { orderService.paySuccess(result.getOutTradeNo()); } } catch (WxPayException e) { log.error("支付通知处理失败", e); // 记录异常通知,人工介入处理 notifyAlertService.sendAlert(e); } } -
用户体验优化:
- 预约成功后发送服务通知
- 开场前1小时推送提醒
- 支持随时退票(开场前2小时截止)
