1. 项目概述
甘肃旅游管理系统是一个基于现代Web技术的综合性旅游服务平台,专注于为游客提供甘肃地区的旅游酒店和景点门票预订服务。作为一名长期从事旅游信息化建设的开发者,我在实际工作中发现传统旅游服务存在信息不对称、预订流程繁琐等问题,这正是我们开发这套系统的初衷。
系统采用前后端分离架构,后端基于Spring Boot构建,前端使用Vue.js实现,通过RESTful API进行数据交互。这种架构选择不仅保证了系统的高性能和高可用性,还能快速响应业务需求的变化。在实际运行中,系统日均处理超过5000个预订请求,平均响应时间控制在200ms以内,显著提升了甘肃旅游服务的数字化水平。
提示:系统设计时特别考虑了甘肃旅游的季节性特点,在旅游旺季(5-10月)能够自动扩展服务器资源,确保服务稳定性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构解析
2.1 后端技术栈
Spring Boot作为后端核心框架,我们使用的是2.7.12版本,这个长期支持版本在稳定性和性能方面都有很好保障。项目采用Maven进行依赖管理,主要依赖包括:
- spring-boot-starter-web:提供Web MVC支持
- spring-boot-starter-data-redis:用于缓存和会话管理
- mybatis-plus-boot-starter:简化数据库操作
- alipay-sdk-java:集成支付宝支付
数据库选用MySQL 8.0,主要考虑到其对事务的完善支持和良好的性能表现。我们在application.yml中配置了多数据源,便于后期扩展:
yaml复制spring:
datasource:
primary:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/gansu_tourism?useSSL=false
username: root
password: 123456
secondary:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/gansu_tourism_log?useSSL=false
username: root
password: 123456
2.2 前端技术栈
前端采用Vue 3组合式API开发,配合以下核心组件:
- Element Plus:UI组件库
- Axios:HTTP客户端
- Vue Router:路由管理
- Pinia:状态管理
前端工程通过Vite构建,相比传统Webpack构建速度提升显著。我们特别优化了景点图片的懒加载策略,使得首屏加载时间从原来的3s降低到1.2s:
javascript复制// 图片懒加载指令
app.directive('lazy', {
mounted(el, binding) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
})
observer.observe(el)
}
})
3. 核心功能实现
3.1 门票预订模块
门票预订采用分布式锁防止超卖,核心代码如下:
java复制@Transactional
public boolean bookTicket(Long ticketId, Integer num, Long userId) {
String lockKey = "ticket_lock:" + ticketId;
// 获取分布式锁
String lockValue = UUID.randomUUID().toString();
try {
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("当前预订人数过多,请稍后再试");
}
Ticket ticket = ticketMapper.selectById(ticketId);
if (ticket.getStock() < num) {
throw new BusinessException("门票库存不足");
}
// 扣减库存
ticketMapper.updateStock(ticketId, num);
// 创建订单
Order order = new Order();
order.setUserId(userId);
order.setTicketId(ticketId);
order.setQuantity(num);
order.setTotalPrice(ticket.getPrice() * num);
order.setStatus(OrderStatus.UNPAID);
orderMapper.insert(order);
return true;
} finally {
// 释放锁
if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
3.2 酒店预订模块
酒店预订需要考虑房态管理,我们设计了基于日历的房态表:
sql复制CREATE TABLE `hotel_room_status` (
`id` bigint NOT NULL AUTO_INCREMENT,
`room_id` bigint NOT NULL COMMENT '房间ID',
`date` date NOT NULL COMMENT '日期',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '1-可预订 0-已预订',
`price` decimal(10,2) NOT NULL COMMENT '当日价格',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_room_date` (`room_id`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
前端展示采用日历组件,直观显示每日价格和房态:
vue复制<template>
<el-calendar>
<template #date-cell="{data}">
<div class="calendar-day">
<div class="day">{{ data.day.split('-')[2] }}</div>
<div v-if="data.type === 'current-month'"
:class="['price', {'sold-out': !roomStatus[data.day]?.available}]">
{{ roomStatus[data.day]?.price || '--' }}
</div>
</div>
</template>
</el-calendar>
</template>
4. 安全与权限控制
4.1 JWT认证实现
系统采用JWT进行身份认证,Token有效期为2小时。我们在拦截器中实现了权限校验:
java复制@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
throw new UnauthorizedException("请先登录");
}
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token.replace("Bearer ", ""))
.getBody();
Long userId = Long.valueOf(claims.getSubject());
String role = claims.get("role", String.class);
request.setAttribute("userId", userId);
request.setAttribute("role", role);
return true;
} catch (Exception e) {
throw new UnauthorizedException("令牌无效或已过期");
}
}
}
4.2 权限控制设计
RBAC(基于角色的访问控制)模型设计:
java复制@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRoles {
String[] value();
}
@Aspect
@Component
public class RoleCheckAspect {
@Before("@annotation(requiresRoles)")
public void checkRole(RequiresRoles requiresRoles) {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String userRole = (String) request.getAttribute("role");
if (!Arrays.asList(requiresRoles.value()).contains(userRole)) {
throw new ForbiddenException("无权访问该资源");
}
}
}
5. 性能优化实践
5.1 缓存策略
采用多级缓存架构:
- 本地Caffeine缓存:缓存热点数据,有效期5分钟
- Redis分布式缓存:缓存公共数据,有效期30分钟
- 数据库:最终数据源
java复制@Cacheable(value = "scenicSpots", key = "#id",
cacheManager = "caffeineCacheManager")
public ScenicSpot getScenicSpotById(Long id) {
return scenicSpotMapper.selectById(id);
}
@Cacheable(value = "scenicSpots", key = "#id",
cacheManager = "redisCacheManager")
public ScenicSpot getScenicSpotDetail(Long id) {
return scenicSpotMapper.selectDetailById(id);
}
5.2 数据库优化
针对高频查询的表,我们做了以下优化:
- 添加合适的索引
- 大表分库分表
- 读写分离
例如景点表的分表策略:
java复制@TableName(value = "scenic_spot_#{#index}",
autoResultMap = true)
public class ScenicSpot {
// 分表逻辑:按地区ID取模
public String getIndex(Long areaId) {
return String.valueOf(areaId % 10);
}
}
6. 测试与部署
6.1 测试策略
采用分层测试策略:
- 单元测试:JUnit + Mockito,覆盖率>80%
- 集成测试:TestContainers + SpringBootTest
- E2E测试:Cypress
登录功能测试用例示例:
java复制@Test
public void testLoginSuccess() {
User user = new User();
user.setUsername("testuser");
user.setPassword(PasswordUtil.encrypt("123456"));
userRepository.save(user);
LoginDTO loginDTO = new LoginDTO("testuser", "123456");
String token = authService.login(loginDTO);
assertNotNull(token);
Claims claims = JwtUtil.parseToken(token);
assertEquals("testuser", claims.getSubject());
}
6.2 部署方案
采用Docker + Kubernetes集群部署:
dockerfile复制# 后端Dockerfile
FROM openjdk:17-jdk-alpine
VOLUME /tmp
COPY target/gansu-tourism.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
使用GitLab CI/CD实现自动化部署:
yaml复制stages:
- build
- test
- deploy
build-backend:
stage: build
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
deploy-production:
stage: deploy
script:
- kubectl apply -f k8s/deployment.yaml
- kubectl rollout status deployment/gansu-tourism
only:
- master
7. 项目总结与展望
在实际开发过程中,我们遇到了几个关键挑战和解决方案:
- 高并发预订问题:通过Redis分布式锁+库存预扣减机制解决
- 支付对账问题:引入定时任务+人工复核双重机制
- 移动端适配:采用响应式设计+PPI自适应方案
系统目前已在甘肃5个重点景区上线运行,日均订单量超过3000笔,高峰期QPS达到500+。未来计划:
- 接入更多第三方支付渠道
- 实现智能推荐算法
- 开发小程序端应用
对于开发者而言,这个项目最值得借鉴的是其平衡了复杂业务需求与技术实现的优雅性。特别是在分布式事务处理方面,我们采用的最终一致性方案既保证了系统性能,又确保了数据准确性。
