项目概览
SeatFlow 是一个影院在线选座票务系统。观众浏览影片和场次,选择座位、提交订单,在有效期内完成模拟支付;运营人员维护影片、影厅和排期,查询订单及统计。后端采用 Spring Boot 单体架构,重点是同一座位被多人争抢时,怎样让订单、座位状态和支付结果最终一致。
业务角色与范围
观众与运营人员共用同一套影片、影厅、场次和订单数据,但操作权限不同。观众只能管理自己的订单;运营人员负责准备可售库存和查询经营状态。项目采用单影院范围,集中处理一条交易链路,没有把影院管理、会员营销、退款改签等能力混进当前实现。
| 对象 | 业务含义 | 与交易的关系 |
|---|---|---|
| 影片 | 片名、时长、简介与上架状态 | 决定可排哪些电影 |
| 影厅与座位模板 | 行列、座位标识与类型 | 描述物理空间,不随每次购票改变 |
| 场次 | 某影片在某影厅的一次放映 | 把座位模板变成该场次独立的可售库存 |
| 订单与有效占用 | 用户购买哪些座位、总价、截止时间与状态 | 是交易记录和数据库防重复占用的依据 |
| 支付单与回调记录 | 支付尝试、成功或失败、通知身份 | 将支付过程与订单生命周期分开 |
同一个影厅的同一个座位可以在不同场次出售,因此防重复占用的维度是“场次 + 座位”,不是只有座位 ID。订单价格由后端根据场次单价和座位数计算,前端选中的金额不是结算权威。
从选座到出票
flowchart TD
A[浏览影片与场次] --> B[选择空闲座位]
B --> C[原子预占与限购]
C --> D[写入待支付订单]
D --> E{支付或取消}
E -->|成功| F[确认订单与已售座位]
E -->|主动取消或过期| G[释放座位和限购额度]
G --> B
座位有可选、锁定、已售三种业务状态。“选中”只是前端临时状态,提交订单才会预占库存。支付失败不等于取消订单:在截止时间之前可以重试,锁座继续保留。
系统如何分工
flowchart TD
U[Vue 购票与运营界面] --> W[HTTP 接口与身份校验]
W --> S[订单、支付、场次与缓存服务]
S --> D[(MySQL 订单与有效占用)]
S --> R[(Redis 座位、限购与缓存)]
S --> Q[RocketMQ 超时消息]
Q --> C[取消与释放]
T[定时对账] --> C
T --> D
T --> R
Redis 承担高频座位查询和原子预占,MySQL 保存业务记录并用唯一约束守住重复占用,RocketMQ 唤醒到期检查。三者各自解决一部分问题:Redis 成功不能代表订单提交成功,消息到达不能代表订单一定过期,支付通知成功也不能代表座位副作用已经完成。
项目用本地事务处理数据库内的一致性,把跨存储失败交给幂等操作和定时对账收敛。保留单体部署,是为了让这些边界在一套可运行工程内清楚呈现,而不是用服务数量增加复杂度。
演示中的操作
演示直接运行项目当前的 Vue 前端,默认以体验观众登录。选择影片和在售场次后,可以一次购买最多四个座位;先模拟支付失败,再模拟成功,可以看到待支付到已支付的状态变化。“我的订单”提供订单详情、取消与支付。
顶部身份选择可切换到运营管理,浏览影片列表、创建或上下架影片、创建影厅座位模板、编排并开售场次,以及按状态查询订单。数据保存在当前浏览器,刷新后仍保留;“重置演示”恢复初始数据。演示账户为 demo、admin,密码均为 demo123,注册也只发生在本地,请勿填写真实账户密码。
核心实现
| 问题 | 实现与阅读入口 |
|---|---|
| 多人同时选择同一座位 | 并发锁座与限流:Lua 原子预占,数据库唯一约束兜底 |
| 付款、取消和过期同时发生 | 订单与支付状态机:条件更新竞争状态,重复回调继续修复 |
| Redis、数据库和消息不一致 | 缓存与一致性补偿:五类对账及逻辑过期 |
| 怎样证明链路可靠 | 验证与性能口径:并发测试、回归范围与历史压测 |
演示与真实后端的边界
浏览器内的适配器模拟接口、身份、座位占用和订单状态,使用同步的本地状态变更表达“一批座位全部成功或全部失败”。它没有启动 Redis、MySQL 或 RocketMQ,也不具备真实的分布式互斥、JWT 鉴权、密码加密或支付签名能力;多浏览器窗口间不保证并发一致性。
订单保留十五分钟支付时间,过期在后续接口请求时结算,不是后台消息消费。模拟网关只演示成功、失败和重试,不连接支付渠道,不验证 HMAC,也不实现退款。真实后端中“订单取消后才收到支付成功”的差异处理,见状态机章节。
技术栈与工程组织
Java 21 / Spring Boot 3、MyBatis-Plus、Flyway、MySQL、Redis、RocketMQ、Caffeine、Redisson;前端是 Vue 3、Element Plus 和 Axios。工程按 web → service → dao → common 分层,保持单应用部署,让测试集中覆盖交易边界。
web 层接收请求、校验身份和转换业务错误;service 层组织事务、状态机、缓存与消息;dao 层保存实体、Mapper 和迁移脚本;common 层统一返回体、异常和链路标识。多模块约束编译依赖,运行时仍是同一个 Spring Boot 应用。
真实应用使用 JWT 和 BCrypt 处理登录身份,回调入口独立验证支付签名。本站演示只保留角色和业务归属判断,没有把本地模拟登录等同于真实鉴权系统。
