1. 项目概述与设计思路
作为一名长期从事交通信息化系统开发的工程师,我最近完成了一个基于SpringBoot的公交线路查询系统。这个系统主要解决城市居民在日常出行中遇到的公交信息获取不便、线路查询效率低下等问题。相比市面上已有的商业软件,我们的设计更注重轻量化和本地化部署,特别适合中小城市公交公司或社区自主搭建使用。
系统采用经典的B/S架构,前端使用Thymeleaf模板引擎实现基础页面渲染,后端基于SpringBoot 2.7.18构建,数据存储选用MySQL 8.0作为关系型数据库。整套技术栈的选择主要基于以下考量:
- SpringBoot的自动配置特性大幅简化了传统Spring项目的搭建过程
- 内嵌Tomcat服务器使部署变得极其简单
- MySQL在中小规模数据场景下性能表现优异
- 整套技术方案完全开源,不存在商业授权风险
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能模块实现
2.1 用户认证模块
2.1.1 注册功能实现
用户注册是系统的基础功能,我们特别强化了账号唯一性校验机制。在数据表设计上,users表采用username字段作为唯一索引:
sql复制CREATE TABLE `users` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`phone` varchar(20) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
后端处理逻辑中,我们采用Spring Validation进行参数校验:
java复制@PostMapping("/register")
public String register(@Valid UserRegisterDTO dto, BindingResult result) {
if(result.hasErrors()){
return "register";
}
if(userService.existsByUsername(dto.getUsername())){
model.addAttribute("error", "用户名已存在");
return "register";
}
userService.createUser(dto);
return "redirect:/login";
}
注意:密码存储必须使用BCryptPasswordEncoder进行加密,绝对不要明文存储
2.1.2 登录认证流程
登录功能采用Spring Security实现,配置类关键代码如下:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/lines")
.permitAll();
}
}
2.2 公交线路管理模块
2.2.1 数据模型设计
公交线路系统涉及多个实体关系,我们设计了以下核心表结构:
sql复制CREATE TABLE `bus_lines` (
`id` int NOT NULL AUTO_INCREMENT,
`line_number` varchar(20) NOT NULL,
`start_station` varchar(50) NOT NULL,
`end_station` varchar(50) NOT NULL,
`first_time` time NOT NULL,
`last_time` time NOT NULL,
`interval_minutes` int DEFAULT NULL,
`price` decimal(5,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `stations` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`longitude` decimal(10,6) NOT NULL,
`latitude` decimal(10,6) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `line_station_relation` (
`id` int NOT NULL AUTO_INCREMENT,
`line_id` int NOT NULL,
`station_id` int NOT NULL,
`sequence` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `line_station_unique` (`line_id`,`station_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2.2 线路查询实现
线路查询支持按线路编号、途经站点、首末站三种方式检索。核心查询方法如下:
java复制public List<BusLineVO> searchLines(String keyword) {
return busLineMapper.selectByCondition(keyword);
}
对应的MyBatis动态SQL:
xml复制<select id="selectByCondition" resultType="com.example.vo.BusLineVO">
SELECT bl.* FROM bus_lines bl
<where>
<if test="keyword != null and keyword != ''">
bl.line_number LIKE CONCAT('%',#{keyword},'%')
OR bl.start_station LIKE CONCAT('%',#{keyword},'%')
OR bl.end_station LIKE CONCAT('%',#{keyword},'%')
OR EXISTS (
SELECT 1 FROM line_station_relation lsr
JOIN stations s ON lsr.station_id = s.id
WHERE lsr.line_id = bl.id
AND s.name LIKE CONCAT('%',#{keyword},'%')
)
</if>
</where>
</select>
2.3 用户交互功能
2.3.1 收藏功能实现
收藏功能采用多对多关系设计:
sql复制CREATE TABLE `user_favorites` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`line_id` int NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `user_line_unique` (`user_id`,`line_id`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
FOREIGN KEY (`line_id`) REFERENCES `bus_lines` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
前端采用Ajax实现无刷新收藏:
javascript复制$('.favorite-btn').click(function(){
const lineId = $(this).data('line-id');
$.post('/favorite/toggle', {lineId: lineId}, function(res){
if(res.success){
$(this).toggleClass('active');
}
});
});
2.3.2 评论功能设计
评论系统采用树形结构存储:
sql复制CREATE TABLE `comments` (
`id` int NOT NULL AUTO_INCREMENT,
`content` text NOT NULL,
`user_id` int NOT NULL,
`line_id` int NOT NULL,
`parent_id` int DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
FOREIGN KEY (`line_id`) REFERENCES `bus_lines` (`id`),
FOREIGN KEY (`parent_id`) REFERENCES `comments` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 系统优化与性能调优
3.1 缓存策略设计
为提高线路查询性能,我们引入Redis缓存:
java复制@Cacheable(value = "lines", key = "#keyword")
public List<BusLineVO> searchLinesWithCache(String keyword) {
return searchLines(keyword);
}
配置缓存过期策略:
properties复制spring.cache.redis.time-to-live=3600000
spring.cache.redis.cache-null-values=false
3.2 数据库索引优化
针对高频查询场景添加复合索引:
sql复制ALTER TABLE `line_station_relation` ADD INDEX `idx_station_line` (`station_id`, `line_id`);
ALTER TABLE `bus_lines` ADD INDEX `idx_line_number` (`line_number`);
3.3 前端性能优化
- 使用Turbolinks实现页面局部刷新
- 对静态资源启用Gzip压缩
- 实现懒加载图片技术
4. 部署与运维方案
4.1 生产环境部署
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: java -jar /app.jar
depends_on:
- db
- redis
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: bus_system
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
4.2 监控方案
- 使用Spring Boot Actuator暴露健康检查端点
- 配置Prometheus监控JVM指标
- 使用Grafana展示监控数据
5. 常见问题排查
5.1 性能问题
问题现象:线路查询响应缓慢
排查步骤:
- 检查是否命中缓存
- 分析SQL执行计划
- 确认索引使用情况
5.2 并发问题
问题现象:重复收藏记录
解决方案:
- 数据库添加唯一约束
- 前端增加防抖处理
- 后端添加同步锁
java复制@Transactional
public synchronized void toggleFavorite(Integer userId, Integer lineId) {
// 业务逻辑
}
6. 项目扩展方向
- 实时公交功能:接入GPS数据接口,显示车辆实时位置
- 换乘推荐算法:实现基于Dijkstra算法的最优换乘方案
- 移动端适配:开发微信小程序版本
- 数据分析模块:统计线路热门程度和用户行为
在实际开发过程中,我发现公交数据的准确性直接影响用户体验。建议与当地公交公司建立数据对接机制,确保线路信息的及时更新。另外,系统的响应速度在高峰期尤为重要,需要持续监控和优化性能指标。
