在传统旅游行业中,信息分散、服务流程割裂是长期存在的痛点。游客往往需要穿梭于多个平台或机构之间,才能完成行程规划、交通预订、酒店入住等一系列操作,不仅耗时耗力,还时常面临信息不对称带来的风险。旅游供应商同样面临挑战,资源管理、订单处理、营销推广等环节相互独立,导致运营效率低下,难以快速响应市场需求变化。这种碎片化的服务模式,已成为制约行业数字化升级的关键瓶颈。
针对上述问题,我们设计并实现了一套基于SpringBoot的一站式旅游服务系统——“游途无忧”平台。该系统旨在通过技术手段整合旅游产业链的上下游资源,构建一个集信息查询、产品预订、行程管理、售后服务于一体的综合性数字平台。平台采用B/S架构,前端使用Thymeleaf模板引擎结合HTML、CSS和JavaScript构建动态用户界面,后端以SpringBoot为核心框架,依托Maven进行依赖管理,数据持久化层采用MySQL数据库,并通过JPA与MyBatis混合模式实现高效的数据操作。
系统架构严格遵循MVC设计模式,将业务逻辑、数据访问和前端展示清晰分离。控制层通过RESTful API接收并处理前端请求,服务层封装核心业务规则如智能路线推荐、实时库存校验、动态定价策略等,数据访问层则负责与数据库交互,确保数据的一致性与完整性。为提升系统性能,引入了Spring Cache缓存机制,对高频访问的静态数据如景点信息、酒店列表进行缓存处理。安全方面,集成Spring Security框架实现基于角色的访问控制,并对敏感操作如支付交易、用户认证等进行加密传输与日志记录。
数据库架构设计与核心表解析
系统共设计10张核心数据表,支撑用户管理、产品管理、订单处理、内容发布等主要业务模块。以下重点分析三个具有代表性的表结构设计。
用户表(user) 的设计不仅存储基础身份信息,还集成了用户行为分析与权限控制的关键字段。
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`id_card` varchar(20) DEFAULT NULL COMMENT '身份证号',
`user_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '用户类型:0-普通用户,1-管理员',
`avatar` varchar(200) DEFAULT NULL COMMENT '头像URL',
`points` int(11) NOT NULL DEFAULT '0' COMMENT '积分',
`member_level` tinyint(4) NOT NULL DEFAULT '0' COMMENT '会员等级',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
KEY `idx_phone` (`phone`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
该表的亮点在于多维度用户画像的构建。通过user_type字段实现角色权限分离,points和member_level支持会员体系与忠诚度计划,last_login_time为活跃度分析提供数据基础。唯一索引确保账号体系的唯一性,而复合索引优化了按时间和联系方式查询的效率。avatar字段存储云存储URL,体现了现代应用对多媒体内容的支持能力。
旅游路线表(travel_route) 的设计展现了复杂产品信息的结构化存储方案。
CREATE TABLE `travel_route` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`route_name` varchar(100) NOT NULL COMMENT '路线名称',
`description` text COMMENT '路线描述',
`cover_image` varchar(200) DEFAULT NULL COMMENT '封面图片',
`days` int(11) NOT NULL COMMENT '行程天数',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`original_price` decimal(10,2) DEFAULT NULL COMMENT '原价',
`route_type` tinyint(4) NOT NULL COMMENT '路线类型:1-自由行,2-跟团游,3-定制游',
`departure_city` varchar(50) NOT NULL COMMENT '出发城市',
`destination_city` varchar(50) NOT NULL COMMENT '目的地城市',
`max_travelers` int(11) NOT NULL COMMENT '最大出行人数',
`current_booking` int(11) NOT NULL DEFAULT '0' COMMENT '当前预订人数',
`include_services` text COMMENT '包含服务',
`exclude_services` text COMMENT '不包含服务',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-下架,1-上架',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序权重',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_route_type` (`route_type`),
KEY `idx_destination` (`destination_city`),
KEY `idx_status_price` (`status`,`price`),
KEY `idx_sort` (`sort_order`,`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='旅游路线表';
此表设计充分考虑了电商场景的实际需求。价格字段采用decimal类型确保计算精度,original_price支持促销展示。current_booking与max_travelers实现库存实时监控,避免超售。include_services和exclude_services使用text类型存储HTML富文本,满足详情页展示需求。多维度索引策略优化了按目的地、价格区间、产品状态的查询效率,特别是idx_status_price复合索引大幅提升了商品列表页的加载性能。
订单表(order) 的设计体现了交易系统的复杂业务逻辑。
CREATE TABLE `order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` varchar(32) NOT NULL COMMENT '订单编号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`route_id` bigint(20) NOT NULL COMMENT '路线ID',
`adult_count` int(11) NOT NULL COMMENT '成人数',
`child_count` int(11) NOT NULL DEFAULT '0' COMMENT '儿童数',
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
`discount_amount` decimal(10,2) DEFAULT '0.00' COMMENT '优惠金额',
`pay_amount` decimal(10,2) NOT NULL COMMENT '实际支付金额',
`order_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态',
`pay_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态',
`contact_name` varchar(50) NOT NULL COMMENT '联系人姓名',
`contact_phone` varchar(20) NOT NULL COMMENT '联系人电话',
`travel_date` date NOT NULL COMMENT '出行日期',
`special_requirements` text COMMENT '特殊要求',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_route_id` (`route_id`),
KEY `idx_travel_date` (`travel_date`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
订单表通过状态字段分离业务流程与支付流程,支持灵活的状态流转。order_no采用独立生成策略而非自增ID,增强业务安全性。金额字段细分total_amount、discount_amount和pay_amount,满足财务对账与促销分析需求。travel_date索引优化了按出行日期筛选订单的查询效率,特别适用于旺季资源调度场景。
核心功能模块深度解析
智能路线推荐与预订系统
路线推荐功能基于用户历史行为、偏好设置及实时热度数据,通过算法模型生成个性化推荐列表。前端界面采用卡片式布局展示路线关键信息,用户可通过多条件筛选快速定位目标产品。

后端推荐逻辑封装在RouteRecommendationService中,采用多策略融合的推荐机制:
@Service
public class RouteRecommendationService {
@Autowired
private UserPreferenceRepository preferenceRepo;
@Autowired
private RouteRepository routeRepo;
@Autowired
private BookingStatisticsService statsService;
@Cacheable(value = "recommendedRoutes", key = "#userId + '_' + #page + '_' + #size")
public Page<RouteDTO> getRecommendedRoutes(Long userId, int page, int size) {
// 获取用户偏好
UserPreference preference = preferenceRepo.findByUserId(userId)
.orElseGet(UserPreference::getDefaultPreference);
// 多维度评分计算
List<RouteScore> scoredRoutes = routeRepo.findAll().stream()
.map(route -> calculateRouteScore(route, preference))
.sorted(Comparator.comparing(RouteScore::getScore).reversed())
.collect(Collectors.toList());
// 分页处理
int start = page * size;
int end = Math.min(start + size, scoredRoutes.size());
List<RouteDTO> result = scoredRoutes.subList(start, end).stream()
.map(RouteScore::getRoute)
.map(this::convertToDTO)
.collect(Collectors.toList());
return new PageImpl<>(result, PageRequest.of(page, size), scoredRoutes.size());
}
private RouteScore calculateRouteScore(Route route, UserPreference preference) {
double score = 0.0;
// 基于价格的偏好匹配
score += preference.getPriceWeight() * calculatePriceScore(route.getPrice());
// 基于目的地的偏好匹配
score += preference.getDestinationWeight() *
calculateDestinationScore(route.getDestinationCity(), preference.getFavoriteDestinations());
// 基于热度的加权
score += 0.3 * calculatePopularityScore(route.getId());
return new RouteScore(route, score);
}
}
预订流程采用异步库存校验机制,防止超售情况发生:
@Service
@Transactional
public class BookingService {
@Autowired
private RouteRepository routeRepo;
@Autowired
private OrderRepository orderRepo;
@Autowired
private InventoryService inventoryService;
public BookingResult createOrder(BookingRequest request) {
// 库存预占校验
InventoryLock lock = inventoryService.tryLockInventory(
request.getRouteId(),
request.getTravelDate(),
request.getTotalTravelers()
);
if (!lock.isSuccess()) {
return BookingResult.failed("库存不足");
}
try {
// 创建订单
Order order = buildOrderFromRequest(request);
order = orderRepo.save(order);
// 扣减库存
inventoryService.confirmInventoryDeduction(lock.getLockId());
// 发送订单创建事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
return BookingResult.success(order.getOrderNo());
} catch (Exception e) {
// 库存锁定回滚
inventoryService.releaseInventoryLock(lock.getLockId());
throw new BusinessException("订单创建失败", e);
}
}
}
多角色权限管理系统
平台采用基于RBAC的权限控制模型,支持管理员与普通用户的双重身份体系。管理员通过统一控制台管理用户、审核内容、配置系统参数。

权限控制通过自定义注解和Spring Security拦截器实现:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.and()
.rememberMe()
.key("uniqueAndSecret")
.tokenValiditySeconds(86400);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
// 自定义权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasPermission(#routeId, 'ROUTE', 'EDIT')")
public @interface RouteEditPermission {
}
// 业务方法中的权限应用
@Service
public class RouteManagementService {
@RouteEditPermission
public Route updateRoute(Long routeId, RouteUpdateRequest request) {
// 路由更新逻辑
Route route = routeRepo.findById(routeId)
.orElseThrow(() -> new ResourceNotFoundException("路线不存在"));
route.setRouteName(request.getRouteName());
route.setDescription(request.getDescription());
route.setPrice(request.getPrice());
return routeRepo.save(route);
}
}
旅游攻略发布与审核机制
平台构建了UGC内容生态,允许用户分享旅行经验,管理员负责内容审核与质量把控。

攻略发布采用工作流引擎管理审核流程:
@Service
public class StrategyService {
@Autowired
private StrategyRepository strategyRepo;
@Autowired
private AuditWorkflowEngine workflowEngine;
@Autowired
private ContentFilterService contentFilter;
public Strategy publishStrategy(StrategyPublishRequest request) {
// 内容安全过滤
ContentFilterResult filterResult = contentFilter.filter(
request.getTitle() + " " + request.getContent()
);
if (!filterResult.isPassed()) {
throw new ContentViolationException("内容包含违规信息");
}
// 构建攻略实体
Strategy strategy = new Strategy();
strategy.setTitle(request.getTitle());
strategy.setContent(request.getContent());
strategy.setCoverImage(request.getCoverImage());
strategy.setAuthorId(request.getAuthorId());
strategy.setStatus(StrategyStatus.PENDING_REVIEW);
strategy = strategyRepo.save(strategy);
// 启动审核工作流
workflowEngine.startAuditWorkflow(
strategy.getId(),
WorkflowType.STRATEGY_AUDIT
);
return strategy;
}
@Async
public void processAutoAudit(Long strategyId) {
Strategy strategy = strategyRepo.findById(strategyId)
.orElseThrow(() -> new ResourceNotFoundException("攻略不存在"));
// 自动化审核规则
AuditScore score = calculateAuditScore(strategy);
if (score.getTotalScore() >= AUTO_APPROVE_THRESHOLD) {
strategy.setStatus(StrategyStatus.APPROVED);
strategy.setAuditTime(LocalDateTime.now());
strategyRepo.save(strategy);
// 通知作者审核通过
notificationService.notifyStrategyApproved(strategy.getAuthorId(), strategy.getId());
} else {
// 转人工审核
workflowEngine.escalateToManualReview(strategyId);
}
}
}
酒店与景点资源管理
资源管理模块采用统一的数据模型,支持酒店、景点等旅游产品的标准化入库与动态更新。

实体关系映射采用JPA注解实现对象-关系映射:
@Entity
@Table(name = "hotel")
@Data
@EqualsAndHashCode(callSuper = true)
public class Hotel extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(length = 200)
private String address;
@ManyToOne
@JoinColumn(name = "city_id")
private City city;
@Column(precision = 10, scale = 2)
private BigDecimal price;
@Enumerated(EnumType.STRING)
@Column(length = 20)
private StarRating starRating;
@OneToMany(mappedBy = "hotel", cascade = CascadeType.ALL)
private List<RoomType> roomTypes = new ArrayList<>();
@OneToMany(mappedBy = "hotel")
private List<HotelImage> images = new ArrayList<>();
@Column(columnDefinition = "TEXT")
private String facilities;
@Column(name = "geo_lat", precision = 10, scale = 6)
private BigDecimal latitude;
@Column(name = "geo_lng", precision = 10, scale = 6)
private BigDecimal longitude;
}
@Entity
@Table(name = "room_type")
@Data
public class RoomType {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "hotel_id")
private Hotel hotel;
@Column(nullable = false, length = 50)
private String typeName;
@Column(precision = 8, scale = 2)
private BigDecimal price;
private Integer maxOccupancy;
@Column(name = "breakfast_included")
private Boolean