# 智慧停车 — 进出场逻辑流程 > 日期:2026-07-24 ## 1. 触发方式总览 ``` 车辆到达/离开 │ ├─ RFID 自动识别 → PassageService.HandlePassage() → 自动进出场 ├─ 手动操作 → entryExit.vue → 手动进出场 └─ 摄像头车牌识别 → (待实现 P0-5) ``` --- ## 2. 入场流程 ### 2.1 流程图 ``` 车辆到达入口 │ ├── 手动录入 ──────────────────────────────┐ │ 前端 entryExit.vue │ │ 输入车牌/RFID → POST /vehicle/entry │ │ │ ├── RFID 自动识别 ───────────────────────── │ │ reader.go → ParseBuffer() │ │ → PassageService.HandlePassage() │ │ → 防抖检查(3秒去重) │ │ → handleEntry() │ │ │ ▼ ▼ ┌─────────────────────────────────────────────────┐ │ VehicleEntry() │ │ internal/service/vehicle/vehicle.go │ │ │ │ ① 黑名单检查 │ │ CheckVehicleShortlist(plate, rfid) │ │ ├─ 在黑名单 → 拦截,返回错误 │ │ └─ 不在 → 继续 │ │ │ │ ② 车辆查询 │ │ GetVehicleByPlateNumber(plate, rfid) │ │ ├─ 找到 → 使用现有车辆 │ │ └─ 未找到 → 新建车辆(默认 is_system类型=临时车) │ │ │ │ ③ 重复入场检查 │ │ ┌── 已有未出场记录 → 拦截 │ │ └── 无 → 继续 │ │ │ │ ④ 创建入场记录 │ │ INSERT vehicle_record │ │ (plate_number, rfid_tag, entry_time, │ │ parking_lot_id, payment_status=unpaid) │ │ │ │ ⑤ 创建数字票 │ │ digital_ticket.Create() │ │ 状态: pending_payment │ │ 事件日志: [{"event":"created", ...}] │ │ │ │ ⑥ 更新车辆统计 │ │ vehicle.last_entry_time = now │ │ │ │ ⑦ RFID → 开闸(GateOpener) │ │ 手动 → 仅记录,需手动命令抬杆 │ └─────────────────────────────────────────────────┘ ``` ### 2.2 RFID 入场特有检查 ``` RFID 扫描 → PassageService.HandlePassage() │ ├── 防抖(3秒内重复扫描忽略) ├── 查询设备 → 获取通道 → 确定方向/direction ├── 查询通道 → allowTemporary(是否允许临停) ├── 查询车辆 → 判断是否为临时车(VehicleType.IsSystem) ├── 黑名单检查 ├── 临时车 && 通道不允许临停 → 拦截 └── 入场状态检查(epcStatus 防重入) ``` --- ## 3. 出场流程 ### 3.1 流程图 ``` 车辆到达出口 │ ├── 手动录入 ─────────────────────────────────┐ │ entryExit.vue 出场Tab │ │ ┌─ 输入车牌/RFID → 查询车辆 │ │ ├─ POST /vehicle/exit/preview → 费用预览 │ │ ├─ 显示费用 + 支付方式选择 │ │ ├─ 临时车: 现金收款 → 输入实收 → 计算找零 │ │ ├─ 非临时车: 免密(payment_method=free) │ │ └─ POST /vehicle/exit/confirm → 确认 │ │ │ ├── RFID 自动识别 ──────────────────────────── │ │ PassageService.handleExit() │ │ → 直接调 ExitConfirm(payment_method=free) │ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────┐ │ ExitConfirm() │ │ internal/service/vehicle/vehicle.go │ │ │ │ ① 黑名单检查 │ │ CheckVehicleShortlist(plate, rfid) │ │ └─ 在黑名单 → 拦截 │ │ │ │ ② 查询未出场记录 │ │ WHERE (plate=? OR rfid=?) AND exit_time IS NULL │ │ └─ 未找到 → "车辆未入场或已出场" │ │ │ │ ③ 计算费用 │ │ calculateFee(vehicle, stayTime) │ │ ├─ 查 fee_config(按车型) │ │ ├─ VIP检查(IsVip + 过期检查 + 非零值) │ │ ├─ 免费时长内 → 0 元 │ │ ├─ 超时计费(StartFee + units × UnitFee) │ │ └─ 每日封顶 / VIP折扣 │ │ │ │ ④ 更新出场记录 │ │ vehicle_record.exit_time = now │ │ vehicle_record.fee = calculated │ │ vehicle_record.payment_method = req.method │ │ │ │ ⑤ 更新车辆统计 │ │ vehicle.last_exit_time / total_stay / total_fee │ │ │ │ ⑥ 创建支付记录 │ │ payment_record (amount, method, operator, change) │ │ vehicle_record.payment_status = "paid" │ │ │ │ ⑦ RFID → 开闸 │ │ 手动 → 仅记录 │ └─────────────────────────────────────────────────────┘ ``` ### 3.2 计费算法(calculateFee) ``` calculateFee(vehicle, stayTime) │ ├─ 1. 查 fee_config WHERE vehicle_type_id = ? │ ├─ 未找到 → 默认费率 0.1元/分钟 │ └─ 全零配置 → 同样回退默认费率 │ ├─ 2. VIP 判断 │ owner.IsVip && VipExpireTime.After(now) && !VipExpireTime.IsZero() │ └─ 过期 / 零值 → 非 VIP │ ├─ 3. VIP 免费(isVIP && feeConfig.IsVIPFree) │ └─ return 0 │ ├─ 4. 免费时长内 │ stayTime ≤ StartTime → return 0 │ ├─ 5. 超时计费 │ extraTime = stayTime - StartTime │ units = ceil(extraTime / UnitTime) │ fee = StartFee + units × UnitFee │ ├─ 6. 每日封顶 │ fee > DailyMaxFee → fee = DailyMaxFee │ └─ 7. VIP 折扣 fee ×= VIPDiscount ``` --- ## 4. 名单检查逻辑 ``` CheckVehicleShortlist(plateNumber, rfidTag) │ ├─ 按车牌或 RFID 查 vehicle 表 │ └─ 未找到 → 返回 false, false │ ├─ 按 vehicle_id 查 shortlist 表 │ └─ 未找到 → 返回 false, false │ └─ shortlist.ListType == "黑名单" → isBlack=true shortlist.ListType == "白名单" → isWhite=true 名单检查位置: ├─ VehicleEntry (手动+RIFD进场) ✅ ├─ ExitConfirm (手动+RIFD出场) ✅ └─ PassageService.HandlePassage ✅ ``` --- ## 5. 数字票状态跟踪 ``` VehicleEntry() │ └─ digital_ticket.Create() │ 状态: pending_payment event_log: [{"event":"created","trigger":"manual|rfid"}] │ ▼ ExitConfirm() │ └─ 创建 payment_record │ 状态: paid (手动调用) event_log: [...,{"event":"paid","payment_method":"cash"}] ``` --- ## 6. 代码入口汇总 | 触发方式 | 入口函数 | 文件 | |---------|---------|------| | 手动进场 | `VehicleEntry()` | `internal/service/vehicle/vehicle.go:177` | | RFID 进场 | `PassageService.handleEntry()` | `internal/service/parking/passage.go:103` | | 手动出场(预览) | `ExitPreview()` | `internal/service/vehicle/vehicle.go:296` | | 手动出场(确认) | `ExitConfirm()` | `internal/service/vehicle/vehicle.go:334` | | RFID 出场 | `PassageService.handleExit()` | `internal/service/parking/passage.go:150` | | 计费引擎 | `calculateFee()` | `internal/service/vehicle/vehicle.go:384` | | 名单检查 | `CheckVehicleShortlist()` | `internal/service/vehicle/shortlist.go:30` | | 数字票创建 | `TicketService.Create()` | `internal/modules/digital-ticket/service/service.go:52` | | 道闸控制 | `GateOpener()` | `internal/service/uhf/reader.go:88` | ## 7. 包月车进出场识别 ``` 车辆进场 / 出场 │ ├─ CheckVehicleShortlist(plate, rfid) │ └─ shortlist.ListType == "白名单" && ExpirationTime > now ├─ 未过期 → 白名单放行(后续按免密处理) └─ 已过期 → 按临时车计费 ``` 包月车办理时自动写入 `shortlist(白名单, ExpirationTime=end_date)`,进出场复用现有白名单检查,零改动。