SpringBoot+Vue3公交查询系统开发实战

dax eursir

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的