在城市公共交通日益发展的今天,数字化出行服务已成为提升市民生活便捷度的重要一环。传统的公交信息查询方式,如纸质地图、分散的站牌信息,存在信息更新滞后、查询效率低下、无法进行动态路线规划等痛点。针对这些问题,一套整合了现代Web技术与智能算法的公交信息服务系统应运而生。
本系统采用经典的SSH(Struts2 + Spring + Hibernate)分层架构进行构建,旨在为城市居民提供高效、准确的公交路线查询与规划服务。系统通过数字化手段整合公交网络数据,利用智能路径规划算法,显著提升了公众出行的计划性与便捷性,有效降低了出行决策成本。
系统架构与技术栈
系统采用典型的三层架构,各层职责分明,通过框架支持实现松耦合和高内聚。
表现层使用Struts2框架处理用户交互。Struts2的Action类作为前端请求的入口点,负责接收表单数据、调用业务逻辑、并选择相应的视图进行渲染。其拦截器机制有效处理了通用逻辑,如参数校验、权限控制等。
业务逻辑层由Spring框架托管。Spring的IoC(控制反转)容器统一管理所有服务对象,如路线查询服务、站点管理服务、用户服务等,实现了对象之间的依赖注入,降低了组件间的耦合度。同时,利用Spring AOP(面向切面编程)实现了声明式事务管理、系统日志记录和性能监控等横切关注点,保证了业务操作的原子性和数据一致性。
数据持久层基于Hibernate框架实现对象关系映射(ORM)。Hibernate将Java实体类与数据库表进行映射,开发者可以面向对象的方式进行数据库操作,而无需编写繁琐的SQL语句。系统使用HQL(Hibernate Query Language)进行复杂的数据查询,例如多表关联查询公交线路与站点信息、根据条件动态检索换乘方案等。
前端技术采用JSP(JavaServer Pages)结合jQuery库构建用户界面。JSP负责动态生成HTML页面,而jQuery则用于处理前端的交互逻辑,如Ajax异步请求、表单验证、动态内容加载等,提升了用户体验。
数据存储选用MySQL关系型数据库,存储公交线路、站点、用户信息、实时数据等核心数据。MySQL以其稳定性、高性能和开源特性,成为此类应用的首选。
数据库设计核心剖析
数据库设计是系统稳定高效的基石。本系统共设计10张核心数据表,以下重点分析其中几个关键表的结构与设计亮点。
1. 公交线路表(bus_route)
该表是系统的核心数据表,存储了所有公交线路的静态信息。其设计不仅包含了基础信息,还考虑了线路的拓扑关系,为路径规划算法提供支持。
CREATE TABLE bus_route (
route_id INT AUTO_INCREMENT PRIMARY KEY,
route_number VARCHAR(20) NOT NULL UNIQUE COMMENT '线路编号,如\"1路\"',
route_name VARCHAR(100) NOT NULL COMMENT '线路名称',
start_station_id INT NOT NULL COMMENT '起点站ID',
end_station_id INT NOT NULL COMMENT '终点站ID',
total_stations INT NOT NULL COMMENT '总站点数',
first_bus_time TIME NOT NULL COMMENT '首班车时间',
last_bus_time TIME NOT NULL COMMENT '末班车时间',
interval_minutes INT COMMENT '发车间隔(分钟)',
is_loop TINYINT(1) DEFAULT 0 COMMENT '是否为环线',
status TINYINT(1) DEFAULT 1 COMMENT '线路状态:1-正常,0-停运',
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (start_station_id) REFERENCES bus_station(station_id),
FOREIGN KEY (end_station_id) REFERENCES bus_station(station_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公交线路表';
设计亮点:
- 唯一性约束:
route_number字段添加了唯一约束,确保线路编号不重复。 - 外键关联:通过
start_station_id和end_station_id与站点表关联,维护了数据的参照完整性。 - 业务状态管理:
status字段实现了线路的软删除或状态控制,便于线路临时调整或长期停运的管理。 - 时间戳追踪:
created_time和updated_time自动记录数据的创建和更新时间,便于数据审计。
2. 线路站点关系表(route_station_relation)
此表是实现路径规划算法的关键,它描述了线路与站点之间的顺序关系,构成了公交网络的有向图模型。
CREATE TABLE route_station_relation (
relation_id INT AUTO_INCREMENT PRIMARY KEY,
route_id INT NOT NULL,
station_id INT NOT NULL,
sequence_number INT NOT NULL COMMENT '站点在线路上的顺序',
estimated_time_to_next INT COMMENT '到下一站的预估时间(秒)',
INDEX idx_route_sequence (route_id, sequence_number),
INDEX idx_station (station_id),
FOREIGN KEY (route_id) REFERENCES bus_route(route_id),
FOREIGN KEY (station_id) REFERENCES bus_station(station_id),
UNIQUE KEY uk_route_station_sequence (route_id, station_id, sequence_number)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='线路站点关系表';
设计亮点:
- 复合唯一约束:
uk_route_station_sequence确保了同一线路、同一站点、同一顺序的组合唯一性,防止数据重复。 - 联合索引优化:
idx_route_sequence索引极大地优化了根据线路ID查询站点序列的性能,这是路径规划中最频繁的操作之一。 - 权重信息存储:
estimated_time_to_next字段存储了到下一站的预估时间,为计算最短时间路径提供了数据基础。
3. 用户查询记录表(user_search_history)
该表记录了用户的查询行为,为后续的数据分析、个性化推荐和系统优化提供了宝贵的数据来源。
CREATE TABLE user_search_history (
history_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT COMMENT '用户ID,未登录用户为NULL',
start_station_name VARCHAR(100) NOT NULL,
end_station_name VARCHAR(100) NOT NULL,
search_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
search_result_count INT COMMENT '返回的路线数量',
client_ip VARCHAR(45) COMMENT '用户IP地址',
INDEX idx_user_time (user_id, search_time),
INDEX idx_stations (start_station_name, end_station_name),
FOREIGN KEY (user_id) REFERENCES user(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户查询记录表';
设计亮点:
- 灵活的用户关联:
user_id字段允许为空,既支持已登录用户的个性化记录,也支持未登录用户的行为追踪。 - 查询效率优化:
idx_stations索引支持对热门起终点站点的分析,便于发现高频查询路径。 - 结果反馈:
search_result_count记录了当次查询返回的路线数,可用于评估查询算法的有效性和覆盖率。
核心功能实现深度解析
1. 智能公交路线查询与规划
这是系统的核心功能,用户输入起点和终点后,系统通过后台算法计算出所有可行的乘车方案,并按照最少换乘、最短时间等策略进行排序。
前端交互界面提供了清晰的输入区域和直观的结果展示。

后端核心算法基于图论中的最短路径算法进行改良。以下是路线查询服务的关键代码片段:
@Service("routePlanService")
@Transactional
public class RoutePlanServiceImpl implements RoutePlanService {
@Autowired
private RouteStationRelationDao relationDao;
@Override
public List<RoutePlanResult> findRoutes(String startStation, String endStation, SearchStrategy strategy) {
// 1. 根据站点名称模糊匹配,获取确切的站点ID
List<Integer> startStationIds = stationDao.findStationIdsByName(startStation);
List<Integer> endStationIds = stationDao.findStationIdsByName(endStation);
if (startStationIds.isEmpty() || endStationIds.isEmpty()) {
throw new BusinessException("起点或终点站点不存在");
}
// 2. 构建公交网络图
BusNetworkGraph graph = buildBusNetworkGraph();
// 3. 根据策略执行路径搜索算法
List<RoutePlan> plans = strategy.search(graph, startStationIds, endStationIds);
// 4. 将路径规划结果转换为前端展示的DTO
return convertToDisplayResult(plans);
}
private BusNetworkGraph buildBusNetworkGraph() {
BusNetworkGraph graph = new BusNetworkGraph();
// 查询所有线路站点关系数据
List<RouteStationRelation> allRelations = relationDao.findAllRelationsWithTime();
for (RouteStationRelation relation : allRelations) {
// 将每个站点作为图的顶点,线路段作为边,时间为权重
graph.addEdge(relation.getStationId(),
relation.getNextStationId(),
relation.getEstimatedTimeToNext(),
relation.getRouteId());
}
return graph;
}
}
最少换乘策略的实现基于广度优先搜索(BFS),优先寻找直达线路,再逐层搜索换乘方案:
@Component("minTransferStrategy")
public class MinTransferStrategy implements SearchStrategy {
@Override
public List<RoutePlan> search(BusNetworkGraph graph, List<Integer> startStations, List<Integer> endStations) {
Queue<SearchNode> queue = new LinkedList<>();
Map<Integer, Integer> visited = new HashMap<>();
List<RoutePlan> results = new ArrayList<>();
// 初始化:将所有起点站加入队列
for (Integer start : startStations) {
queue.offer(new SearchNode(start, null, 0, 0));
visited.put(start, 0);
}
while (!queue.isEmpty()) {
SearchNode current = queue.poll();
// 如果到达终点站,记录路径
if (endStations.contains(current.getStationId())) {
results.add(buildRoutePlan(current));
continue;
}
// 获取当前站点的所有可达邻接站点
Map<Integer, RouteSegment> neighbors = graph.getNeighbors(current.getStationId());
for (Map.Entry<Integer, RouteSegment> entry : neighbors.entrySet()) {
Integer nextStation = entry.getKey();
RouteSegment segment = entry.getValue();
int transferCount = current.getTransferCount();
// 如果换乘了线路,换乘次数加1
if (current.getLastRouteId() != null &&
!current.getLastRouteId().equals(segment.getRouteId())) {
transferCount++;
}
// 剪枝:如果到达该站点的换乘次数更优,则更新
if (!visited.containsKey(nextStation) || visited.get(nextStation) > transferCount) {
visited.put(nextStation, transferCount);
queue.offer(new SearchNode(nextStation, current, segment.getRouteId(), transferCount));
}
}
}
return results.stream()
.sorted(Comparator.comparingInt(RoutePlan::getTransferCount))
.collect(Collectors.toList());
}
}
2. 后台管理系统与数据维护
系统提供了完善的后台管理功能,管理员可以对线路、站点、新闻等信息进行维护,确保数据的准确性和时效性。
线路管理界面允许管理员对公交线路进行增删改查操作。

后台线路更新操作的Struts2 Action实现:
public class RouteManageAction extends BaseAction {
private BusRoute route;
private List<BusRoute> routeList;
private String operationResult;
@Autowired
private RouteService routeService;
// 添加或更新线路信息
public String saveOrUpdateRoute() {
try {
if (route.getRouteId() == null) {
routeService.addRoute(route);
operationResult = "线路添加成功";
} else {
routeService.updateRoute(route);
operationResult = "线路更新成功";
}
return SUCCESS;
} catch (Exception e) {
addActionError("操作失败: " + e.getMessage());
return ERROR;
}
}
// 查询所有线路
public String listAllRoutes() {
routeList = routeService.findAllRoutes();
return SUCCESS;
}
// 根据ID删除线路(软删除)
public String deleteRoute() {
try {
routeService.deleteRoute(route.getRouteId());
operationResult = "线路删除成功";
return SUCCESS;
} catch (Exception e) {
addActionError("删除失败: " + e.getMessage());
return ERROR;
}
}
// Getter和Setter方法
public BusRoute getRoute() { return route; }
public void setRoute(BusRoute route) { this.route = route; }
public List<BusRoute> getRouteList() { return routeList; }
public String getOperationResult() { return operationResult; }
}
Spring服务层的事务管理配置,确保数据操作的一致性:
<!-- Spring事务配置 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="list*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* com.bus.system.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
3. 用户社区与信息共享
系统还构建了用户社区功能,包括公交论坛、资源上传下载等,增强了用户粘性和信息交互。
公交论坛界面为用户提供了交流平台。

论坛帖子分页查询的Hibernate实现:
@Repository
public class ForumPostDaoImpl extends BaseDaoImpl<ForumPost> implements ForumPostDao {
@Override
public PageResult<ForumPost> findPostsByPage(int pageNum, int pageSize, String keyword) {
String hql = "from ForumPost where status = 1";
String countHql = "select count(*) from ForumPost where status = 1";
Map<String, Object> params = new HashMap<>();
if (StringUtils.isNotBlank(keyword)) {
hql += " and (title like :keyword or content like :keyword)";
countHql += " and (title like :keyword or content like :keyword)";
params.put("keyword", "%" + keyword + "%");
}
hql += " order by createTime desc";
// 查询总记录数
Long totalCount = (Long) getSession().createQuery(countHql)
.setProperties(params)
.uniqueResult();
// 查询当前页数据
List<ForumPost> list = getSession().createQuery(hql)
.setProperties(params)
.setFirstResult((pageNum - 1) * pageSize)
.setMaxResults(pageSize)
.list();
return new PageResult<>(list, totalCount, pageNum, pageSize);
}
}
4. 实时公交信息展示
系统整合了实时公交数据,为用户提供车辆到站时间预测等实时服务。
实时信息展示的jQuery Ajax实现:
function refreshRealTimeInfo(stationId) {
$.ajax({
url: '${pageContext.request.contextPath}/realTime/getArrivalInfo',
type: 'GET',
data: {stationId: stationId},
dataType: 'json',
success: function(response) {
if (response.success) {
updateArrivalTable(response.data);
} else {
showError('获取实时信息失败: ' + response.message);
}
},
error: function(xhr, status, error) {
showError('网络请求失败: ' + error);
}
});
}
function updateArrivalTable(arrivalInfos) {
var $table = $('#arrivalTable');
$table.empty();
$.each(arrivalInfos, function(index, info) {
var row = '<tr>' +
'<td>' + info.routeNumber + '</td>' +
'<td>' + info.direction + '</td>' +
'<td>' + formatArrivalTime(info.estimatedArrivalTime) + '</td>' +
'<td>' + info.distance + '米</td>' +
'</tr>';
$table.append(row);
});
// 5分钟后自动刷新
setTimeout(function() {
refreshRealTimeInfo(currentStationId);
}, 300000);
}
实体模型与数据映射
系统的核心实体模型通过Hibernate注解进行映射,实现了对象与关系的完美转换。
公交线路实体类的Hibernate映射配置:
@Entity
@Table(name = "bus_route")
public class BusRoute implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "route_id")
private Integer routeId;
@Column(name = "route_number", nullable = false, unique = true, length = 20)
private String routeNumber;
@Column(name = "route_name", nullable = false, length = 100)
private