影城管理系统是影院日常运营的核心支撑平台,传统的人工管理模式存在诸多痛点:纸质票务易出错、排片效率低下、会员信息分散、数据统计滞后。我曾参与过三家影院的系统改造项目,发现这些共性问题直接影响着影院30%以上的运营效率。
小徐影城管理系统正是为解决这些问题而设计。核心需求可归纳为三个维度:
选择SpringBoot+Vue的组合主要基于以下考量:
code复制[前端层]
Vue.js + ElementUI
Axios通信
Vuex状态管理
[网关层]
Nginx反向代理
JWT鉴权过滤
[业务层]
SpringBoot 2.7
MyBatis-Plus 3.5
Redis缓存
[数据层]
MySQL 8.0主从
ElasticSearch影片检索
关键设计要点:影厅座位状态使用Redis Bitmap存储,单个影厅(200座位)的锁座操作仅需2ms
sql复制CREATE TABLE `movie` (
`movie_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`movie_name` VARCHAR(50) COLLATE utf8mb4_bin NOT NULL,
`cover_url` VARCHAR(255) COMMENT '海报URL',
`duration` INT UNSIGNED CHECK (duration>0),
`status` TINYINT DEFAULT 0 COMMENT '0-待映 1-热映 2-下架',
FULLTEXT INDEX `ft_search` (`movie_name`,`director`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
实现技巧:
排片算法核心逻辑:
java复制public List<Schedule> generateSchedules(Movie movie, LocalDate date) {
// 获取影厅开放时段
List<TimeSlot> slots = hallService.getAvailableSlots(date);
return slots.stream()
.filter(slot -> slot.getDuration() >= movie.getDuration() + 30) // 预留清洁时间
.map(slot -> new Schedule()
.setStartTime(slot.getStart())
.setEndTime(slot.getStart().plusMinutes(movie.getDuration()))
.setPrice(calculateDynamicPrice(slot)))
.collect(Collectors.toList());
}
支付流程状态机设计:
mermaid复制stateDiagram-v2
[*] --> PENDING
PENDING --> PAID: 支付成功
PENDING --> CANCELLED: 超时未支付
PAID --> REFUNDED: 申请退票
PAID --> COMPLETED: 观影结束
采用分布式锁+乐观锁双重保障:
java复制@Transactional
public boolean lockSeats(Long scheduleId, List<Integer> seats) {
String lockKey = "lock:" + scheduleId;
try {
// Redis原子锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (locked) {
// 数据库乐观锁
Schedule schedule = scheduleMapper.selectForUpdate(scheduleId);
String seatStatus = updateSeatStatus(schedule.getSeatStatus(), seats);
return scheduleMapper.updateSeats(scheduleId, seatStatus, schedule.getVersion()) > 0;
}
return false;
} finally {
redisTemplate.delete(lockKey);
}
}
使用MySQL物化视图+定时任务:
sql复制CREATE MATERIALIZED VIEW daily_stats AS
SELECT
DATE(create_time) AS day,
COUNT(*) AS order_count,
SUM(total_price) AS revenue
FROM `order`
WHERE order_status = 1
GROUP BY DATE(create_time);
Nginx关键参数:
nginx复制upstream backend {
server 192.168.1.10:8080 weight=5;
server 192.168.1.11:8080 weight=5;
keepalive 32;
}
server {
listen 443 ssl;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location /api {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
bash复制java -jar -Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:ParallelGCThreads=4 -XX:ConcGCThreads=2 \
-Dspring.profiles.active=prod cinema.jar
智能推荐系统:
动态定价策略:
java复制public BigDecimal calculateDynamicPrice(TimeSlot slot) {
float basePrice = 45.0f;
float timeFactor = getTimeFactor(slot.getStart());
float seatFactor = getSeatOccupationRate(slot);
return BigDecimal.valueOf(basePrice * timeFactor * seatFactor)
.setScale(2, RoundingMode.HALF_UP);
}
移动端适配:
MyBatis批量插入:
<foreach>标签+rewriteBatchedStatements=truexml复制<insert id="batchInsert">
INSERT INTO seat (schedule_id, seat_no) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.scheduleId}, #{item.seatNo})
</foreach>
</insert>
Vue性能优化:
时间处理陷阱:
javascript复制dayjs.extend(utc)
dayjs.extend(timezone)
const showTime = dayjs.utc(dbTime).tz('Asia/Shanghai')
项目源码已做企业级代码优化,包含: