1. 项目背景与技术选型解析
公交线路查询系统作为城市公共交通信息化的重要组成部分,其技术实现方案需要兼顾前后端开发效率与系统稳定性。本项目采用SpringBoot+Vue3+MyBatis的技术组合,是当前企业级应用开发的黄金搭配方案。
SpringBoot 2.7.x版本作为后端框架,提供了以下核心优势:
- 自动配置机制大幅减少了XML配置工作量
- 内嵌Tomcat服务器简化了部署流程
- Starter依赖管理使MyBatis等组件集成变得极其简单
- Actuator端点为系统监控提供了开箱即用的支持
前端选用Vue3组合式API开发,相比Vue2具有:
- Composition API使代码组织更符合逻辑关注点
- 更好的TypeScript支持
- 更小的打包体积和更高的运行时性能
- 更灵活的逻辑复用方式
数据库层采用MySQL 8.0,主要考虑因素包括:
- 完善的GIS空间数据支持(虽然本项目暂未使用)
- JSON字段类型便于存储线路的扩展属性
- 良好的事务支持保证数据一致性
- 与MyBatis的完美兼容性
技术选型心得:在实际项目中,SpringBoot+Vue3的组合已经验证过多次,特别适合中小型信息系统的快速开发。Vue3的setup语法糖可以显著提升开发效率,而SpringBoot的自动配置则让开发者能更专注于业务逻辑。
2. 系统架构设计与核心模块
2.1 前后端分离架构
系统采用经典的前后端分离架构:
code复制前端(Vue3) ←HTTP→ 后端(SpringBoot) ←JDBC→ MySQL
前端通过axios发送RESTful请求,后端返回统一格式的JSON数据。这种架构的优势在于:
- 前后端可以并行开发
- 前端可以使用任何支持HTTP的客户端(Web/App/小程序)
- 后端服务可以独立扩展和部署
2.2 数据库设计要点
公交系统的核心表结构设计如下:
线路表(bus_route)
sql复制CREATE TABLE `bus_route` (
`id` int NOT NULL AUTO_INCREMENT,
`route_name` varchar(50) NOT NULL COMMENT '线路名称',
`start_station` varchar(50) NOT NULL COMMENT '始发站',
`end_station` varchar(50) NOT NULL COMMENT '终点站',
`first_bus_time` time NOT NULL COMMENT '首班车时间',
`last_bus_time` time NOT NULL COMMENT '末班车时间',
`interval_minutes` int DEFAULT NULL COMMENT '发车间隔(分钟)',
`is_loop` tinyint(1) DEFAULT '0' COMMENT '是否环线',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
站点表(bus_station)
sql复制CREATE TABLE `bus_station` (
`id` int NOT NULL AUTO_INCREMENT,
`station_name` varchar(50) NOT NULL COMMENT '站点名称',
`longitude` decimal(10,7) NOT NULL COMMENT '经度',
`latitude` decimal(10,7) NOT NULL COMMENT '纬度',
`address` varchar(100) DEFAULT NULL COMMENT '详细地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
线路站点关联表(route_station)
sql复制CREATE TABLE `route_station` (
`id` int NOT NULL AUTO_INCREMENT,
`route_id` int NOT NULL,
`station_id` int NOT NULL,
`sequence` int NOT NULL COMMENT '站点顺序',
`arrival_time` varchar(20) DEFAULT NULL COMMENT '预计到达时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_route_station` (`route_id`,`station_id`),
KEY `idx_station` (`station_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
数据库设计经验:公交系统的关键是要处理好线路与站点的多对多关系。在实际项目中,我们还需要考虑站点间的距离计算、首末班车时间校验等业务约束。
3. 后端核心实现细节
3.1 SpringBoot应用结构
标准的三层架构组织:
code复制src/main/java
├── com.example.busquery
│ ├── config # 配置类
│ ├── controller # 控制器层
│ ├── service # 业务逻辑层
│ ├── dao # 数据访问层
│ ├── entity # 实体类
│ └── dto # 数据传输对象
src/main/resources
├── application.yml # 应用配置
└── mapper # MyBatis映射文件
3.2 MyBatis动态SQL应用
在查询复杂线路信息时,我们大量使用MyBatis的动态SQL功能。例如根据条件查询线路:
xml复制<!-- BusRouteMapper.xml -->
<select id="selectByCondition" resultMap="BaseResultMap">
SELECT * FROM bus_route
<where>
<if test="routeName != null and routeName != ''">
AND route_name LIKE CONCAT('%',#{routeName},'%')
</if>
<if test="startStation != null and startStation != ''">
AND start_station = #{startStation}
</if>
<if test="endStation != null and endStation != ''">
AND end_station = #{endStation}
</if>
</where>
ORDER BY id DESC
</select>
对应的Java接口:
java复制public interface BusRouteMapper {
List<BusRoute> selectByCondition(@Param("routeName") String routeName,
@Param("startStation") String startStation,
@Param("endStation") String endStation);
}
3.3 事务管理示例
添加新线路时需要同时维护线路表和线路站点关联表,必须保证事务一致性:
java复制@Service
@RequiredArgsConstructor
public class BusRouteServiceImpl implements BusRouteService {
private final BusRouteMapper busRouteMapper;
private final RouteStationMapper routeStationMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void addRouteWithStations(BusRouteDTO routeDTO) {
// 插入线路基本信息
BusRoute route = convertToEntity(routeDTO);
busRouteMapper.insert(route);
// 批量插入站点关联信息
List<RouteStation> stations = routeDTO.getStations().stream()
.map(s -> new RouteStation(route.getId(), s.getStationId(), s.getSequence()))
.collect(Collectors.toList());
routeStationMapper.batchInsert(stations);
}
}
开发技巧:MyBatis的批量插入性能远高于单条插入。当需要处理大量关联数据时,务必使用批量操作方式。同时要注意Spring事务的传播行为设置,确保关键业务操作在同一个事务中执行。
4. 前端Vue3实现要点
4.1 组合式API的使用
线路查询页面的核心逻辑封装:
vue复制<script setup>
import { ref, onMounted } from 'vue'
import { searchRoutes } from '@/api/bus'
const loading = ref(false)
const routeList = ref([])
const queryParams = ref({
routeName: '',
startStation: '',
endStation: ''
})
const fetchData = async () => {
try {
loading.value = true
const { data } = await searchRoutes(queryParams.value)
routeList.value = data
} finally {
loading.value = false
}
}
onMounted(() => {
fetchData()
})
</script>
4.2 使用Element Plus构建UI
线路表格展示组件:
vue复制<template>
<el-table :data="routeList" v-loading="loading" border>
<el-table-column prop="routeName" label="线路名称" width="180" />
<el-table-column prop="startStation" label="始发站" width="120" />
<el-table-column prop="endStation" label="终点站" width="120" />
<el-table-column label="运营时间">
<template #default="{row}">
{{ row.firstBusTime }} - {{ row.lastBusTime }}
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="{row}">
<el-button type="text" @click="showDetail(row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
</template>
4.3 前端路由配置
Vue Router的模块化配置:
javascript复制// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
},
{
path: '/routes',
name: 'RouteList',
component: () => import('@/views/route/List.vue')
},
{
path: '/routes/:id',
name: 'RouteDetail',
component: () => import('@/views/route/Detail.vue'),
props: true
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
前端优化建议:在真实项目中,应该对表格数据进行分页加载,并使用keep-alive缓存路由组件状态。Vue3的
