在现代城市交通管理中,停车场作为重要的基础设施,其运营效率直接影响着用户体验和商业收益。传统依赖人工记录、现金支付的模式存在效率瓶颈、财务漏洞和易拥堵等问题。为此,我们设计并实现了一套基于SSM(Spring+SpringMVC+MyBatis)框架的智能停车计费管理平台,通过数字化手段全面提升停车场运营管理水平。
该系统采用典型的三层架构设计,前端使用HTML、CSS和JavaScript构建用户交互界面,后端以Spring框架为核心容器,通过控制反转(IoC)管理业务对象生命周期,利用面向切面编程(AOP)处理事务管理和日志记录等横切关注点。SpringMVC负责Web请求的路由与控制,实现前后端分离架构。数据持久层采用MyBatis框架,通过XML映射文件实现对象关系映射(ORM),支持动态SQL查询,确保数据访问的高效性和灵活性。数据库选用MySQL,共设计11个数据表来支撑系统的核心业务逻辑。

数据库设计体现了系统的核心业务逻辑。以停车记录表(parking_record)为例,该表记录了车辆进出场的完整流水信息:
CREATE TABLE parking_record (
record_id INT PRIMARY KEY AUTO_INCREMENT,
license_plate VARCHAR(15) NOT NULL,
entry_time DATETIME NOT NULL,
exit_time DATETIME,
parking_duration INT,
payable_amount DECIMAL(10,2),
payment_status ENUM('pending', 'paid', 'free') DEFAULT 'pending',
payment_method ENUM('cash', 'card', 'mobile') DEFAULT NULL,
payment_time DATETIME,
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_plate_time (license_plate, entry_time),
INDEX idx_status_time (payment_status, entry_time)
);
该表设计具有多个技术亮点:使用DATETIME类型精确记录时间点,通过计算entry_time与exit_time的时间差得出停车时长;payment_status字段使用ENUM类型约束数据完整性;created_time和updated_time字段自动维护记录生命周期;复合索引设计优化了按车牌号查询历史记录和按状态统计的查询性能。
收费规则表(pricing_rule)采用灵活的策略模式设计,支持不同时段、不同车型的差异化定价:
CREATE TABLE pricing_rule (
rule_id INT PRIMARY KEY AUTO_INCREMENT,
rule_name VARCHAR(50) NOT NULL,
vehicle_type ENUM('car', 'suv', 'truck') DEFAULT 'car',
start_time TIME NOT NULL,
end_time TIME NOT NULL,
base_price DECIMAL(8,2) NOT NULL,
unit_price DECIMAL(8,2) NOT NULL,
unit_minutes INT NOT NULL,
daily_cap DECIMAL(10,2),
is_active BOOLEAN DEFAULT true,
effective_date DATE NOT NULL,
expiry_date DATE
);
这种设计支持复杂的计费策略,如高峰期加倍收费、24小时封顶价等业务需求,通过effective_date和expiry_date字段实现规则的时效性管理。
车辆入场处理是系统的核心功能之一。当车辆到达入口时,系统通过车牌识别技术获取车牌信息,创建停车记录:
@Controller
@RequestMapping("/parking")
public class ParkingController {
@Autowired
private ParkingRecordService parkingRecordService;
@PostMapping("/entry")
@ResponseBody
public ResponseEntity<Map<String, Object>> vehicleEntry(
@RequestParam String licensePlate,
@RequestParam String gateId) {
// 校验车牌格式
if (!LicensePlateValidator.isValid(licensePlate)) {
return ResponseEntity.badRequest()
.body(Collections.singletonMap("error", "无效车牌号"));
}
// 检查是否已有未完成记录
ParkingRecord activeRecord = parkingRecordService
.findActiveRecord(licensePlate);
if (activeRecord != null) {
return ResponseEntity.badRequest()
.body(Collections.singletonMap("error", "车辆已在场内"));
}
// 创建新记录
ParkingRecord record = new ParkingRecord();
record.setLicensePlate(licensePlate);
record.setEntryTime(new Date());
record.setGateId(gateId);
record.setPaymentStatus(PaymentStatus.PENDING);
parkingRecordService.createRecord(record);
// 触发道闸开启
gateControlService.openGate(gateId);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("recordId", record.getRecordId());
result.put("entryTime", record.getEntryTime());
return ResponseEntity.ok(result);
}
}
该接口实现了完整的业务逻辑校验,包括车牌号格式验证、重复入场检测等,确保数据一致性。通过GateControlService集成硬件控制,实现自动抬杆功能。

计费引擎采用策略模式实现,支持多种计费规则灵活组合:
@Service
public class PricingEngine {
@Autowired
private PricingRuleService pricingRuleService;
public ParkingFee calculateFee(ParkingRecord record) {
List<PricingRule> applicableRules = pricingRuleService
.getApplicableRules(record.getEntryTime(),
record.getVehicleType());
ParkingFee fee = new ParkingFee();
BigDecimal totalAmount = BigDecimal.ZERO;
long durationMinutes = calculateDurationMinutes(
record.getEntryTime(), record.getExitTime());
for (PricingRule rule : applicableRules) {
BigDecimal ruleAmount = calculateRuleAmount(rule, durationMinutes);
totalAmount = totalAmount.add(ruleAmount);
}
// 应用每日封顶限制
BigDecimal dailyCap = getDailyCap(applicableRules);
if (dailyCap != null && totalAmount.compareTo(dailyCap) > 0) {
totalAmount = dailyCap;
}
fee.setTotalAmount(totalAmount);
fee.setDurationMinutes(durationMinutes);
fee.setCalculationDetails(buildCalculationDetails(applicableRules));
return fee;
}
private BigDecimal calculateRuleAmount(PricingRule rule, long durationMinutes) {
// 实现分段计费逻辑
long ruleMinutes = Math.min(durationMinutes,
rule.getEndMinutes() - rule.getStartMinutes());
if (ruleMinutes <= 0) return BigDecimal.ZERO;
BigDecimal amount = rule.getBasePrice();
if (ruleMinutes > rule.getFreeMinutes()) {
long chargeableMinutes = ruleMinutes - rule.getFreeMinutes();
long chargeableUnits = (long) Math.ceil((double) chargeableMinutes
/ rule.getUnitMinutes());
amount = amount.add(rule.getUnitPrice()
.multiply(BigDecimal.valueOf(chargeableUnits)));
}
return amount;
}
}
计费引擎考虑了免费时长、分段计费、单位时长价格、每日封顶等多种业务场景,通过可配置的规则实现灵活的定价策略。
车辆出场处理涉及费用计算、支付验证和道闸控制等复杂流程:
@Transactional
public PaymentResult processExit(String licensePlate, String paymentMethod) {
// 查询未完成的停车记录
ParkingRecord record = parkingRecordService
.findActiveRecord(licensePlate);
if (record == null) {
throw new BusinessException("未找到有效的停车记录");
}
// 更新出场时间
record.setExitTime(new Date());
// 计算停车费用
ParkingFee fee = pricingEngine.calculateFee(record);
record.setPayableAmount(fee.getTotalAmount());
record.setParkingDuration(fee.getDurationMinutes());
// 处理支付
PaymentService paymentService = paymentFactory
.getService(paymentMethod);
PaymentResult paymentResult = paymentService.processPayment(
fee.getTotalAmount(), record.getRecordId());
if (paymentResult.isSuccess()) {
record.setPaymentStatus(PaymentStatus.PAID);
record.setPaymentMethod(paymentMethod);
record.setPaymentTime(new Date());
// 更新停车场空位数量
parkingLotService.incrementAvailableSpaces();
// 开启出口道闸
gateControlService.openGate(record.getExitGateId());
}
parkingRecordService.updateRecord(record);
return paymentResult;
}
该方法使用@Transactional注解确保数据一致性,通过策略工厂模式支持多种支付方式,实现了完整的出场业务闭环。

数据统计模块为管理人员提供决策支持,通过MyBatis的动态SQL实现多维度数据分析:
<!-- 收入统计查询 -->
<select id="selectIncomeStatistics" parameterType="IncomeQuery"
resultType="IncomeStatistic">
SELECT
DATE(payment_time) as statDate,
payment_method as paymentMethod,
COUNT(*) as transactionCount,
SUM(payable_amount) as totalAmount
FROM parking_record
WHERE payment_status = 'paid'
AND payment_time BETWEEN #{startDate} AND #{endDate}
<if test="paymentMethod != null and paymentMethod != ''">
AND payment_method = #{paymentMethod}
</if>
GROUP BY DATE(payment_time), payment_method
ORDER BY statDate DESC, payment_method
</select>
<!-- 车位使用率统计 -->
<select id="selectUsageStatistics" parameterType="UsageQuery"
resultType="UsageStatistic">
SELECT
HOUR(entry_time) as hourOfDay,
DAYNAME(entry_time) as dayOfWeek,
COUNT(*) as entryCount,
AVG(TIMESTAMPDIFF(MINUTE, entry_time, exit_time)) as avgDuration
FROM parking_record
WHERE entry_time BETWEEN #{startTime} AND #{endTime}
GROUP BY HOUR(entry_time), DAYNAME(entry_time)
ORDER BY dayOfWeek, hourOfDay
</select>
这些统计查询为运营分析提供了数据支撑,帮助管理人员优化定价策略和资源分配。

优惠券管理功能支持营销活动的开展,通过状态机和有效期验证确保业务规则:
@Service
public class CouponService {
public CouponValidationResult validateCoupon(String couponCode,
String licensePlate,
BigDecimal orderAmount) {
Coupon coupon = couponMapper.selectByCode(couponCode);
if (coupon == null) {
return CouponValidationResult.failure("优惠券不存在");
}
if (coupon.getStatus() != CouponStatus.ACTIVE) {
return CouponValidationResult.failure("优惠券不可用");
}
if (coupon.getExpiryTime().before(new Date())) {
coupon.setStatus(CouponStatus.EXPIRED);
couponMapper.updateStatus(coupon);
return CouponValidationResult.failure("优惠券已过期");
}
if (orderAmount.compareTo(coupon.getMinOrderAmount()) < 0) {
return CouponValidationResult.failure(
"订单金额不满足使用条件");
}
// 检查使用次数限制
int usedCount = couponUsageMapper.countUsage(coupon.getCouponId());
if (usedCount >= coupon.getUsageLimit()) {
return CouponValidationResult.failure("优惠券已达使用上限");
}
BigDecimal discountAmount = calculateDiscountAmount(coupon, orderAmount);
return CouponValidationResult.success(discountAmount, coupon);
}
@Transactional
public void useCoupon(String couponCode, String licensePlate,
Long recordId, BigDecimal discountAmount) {
Coupon coupon = couponMapper.selectByCode(couponCode);
CouponUsage usage = new CouponUsage();
usage.setCouponId(coupon.getCouponId());
usage.setLicensePlate(licensePlate);
usage.setRecordId(recordId);
usage.setDiscountAmount(discountAmount);
usage.setUsedTime(new Date());
couponUsageMapper.insert(usage);
// 更新优惠券使用次数
coupon.setUsedCount(coupon.getUsedCount() + 1);
if (coupon.getUsedCount() >= coupon.getUsageLimit()) {
coupon.setStatus(CouponStatus.USED_UP);
}
couponMapper.updateUsageInfo(coupon);
}
}
系统在实体模型设计上充分考虑了扩展性和业务连续性。ParkingRecord实体通过状态字段管理生命周期,支持各种异常情况的处理。PricingRule实体采用组合模式,未来可扩展支持更复杂的计费策略。User实体通过角色权限分离,支持多租户架构的演进。

针对未来发展方向,系统可在以下几个层面进行优化增强:
无感支付技术的集成将进一步提升用户体验。通过对接车牌识别技术和第三方支付平台,实现车辆出场时自动扣费,减少人工干预。技术实现上需要建立支付授权协议管理,设计异步对账机制,确保交易数据的一致性。
大数据分析能力的引入将为运营决策提供更深入的洞察。通过整合历史停车数据、天气信息、周边事件等外部数据,建立预测模型来预估车位需求变化。这需要扩展数据仓库架构,采用Apache Spark等计算框架处理海量数据。
物联网设备的深度集成将提升系统智能化水平。通过部署车位传感器、智能灯控等设备,实时监控车位状态,引导车辆快速泊车。技术实现需要设计设备通信协议,建立设备管理平台,处理高并发实时数据流。
移动端应用的扩展将增强用户服务的便捷性。开发专属移动应用,支持车位预约、室内导航、反向寻车等功能。需要采用React Native或Flutter跨平台框架,设计RESTful API接口规范。
多停车场联网管理将支持规模化运营。通过建立停车场联盟,实现车位资源共享和统一支付结算。技术架构上需要设计分布式系统,解决数据分区、事务一致性等复杂问题。
系统的数据库设计为这些扩展预留了良好的基础。parking_record表的扩展字段支持无感支付场景,pricing_rule表的灵活结构适应多种计费模式,user表的角色权限体系为多租户提供支持。通过持续的技术迭代,智能停车管理平台将不断提升服务能力和商业价值。