日期:2026-07-24
车辆到达/离开
│
├─ RFID 自动识别 → PassageService.HandlePassage() → 自动进出场
├─ 手动操作 → entryExit.vue → 手动进出场
└─ 摄像头车牌识别 → (待实现 P0-5)
车辆到达入口
│
├── 手动录入 ──────────────────────────────┐
│ 前端 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) │
│ 手动 → 仅记录,需手动命令抬杆 │
└─────────────────────────────────────────────────┘
RFID 扫描 → PassageService.HandlePassage()
│
├── 防抖(3秒内重复扫描忽略)
├── 查询设备 → 获取通道 → 确定方向/direction
├── 查询通道 → allowTemporary(是否允许临停)
├── 查询车辆 → 判断是否为临时车(VehicleType.IsSystem)
├── 黑名单检查
├── 临时车 && 通道不允许临停 → 拦截
└── 入场状态检查(epcStatus 防重入)
车辆到达出口
│
├── 手动录入 ─────────────────────────────────┐
│ 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 → 开闸 │
│ 手动 → 仅记录 │
└─────────────────────────────────────────────────────┘
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
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 ✅
VehicleEntry()
│
└─ digital_ticket.Create()
│
状态: pending_payment
event_log: [{"event":"created","trigger":"manual|rfid"}]
│
▼ ExitConfirm()
│ └─ 创建 payment_record
│
状态: paid (手动调用)
event_log: [...,{"event":"paid","payment_method":"cash"}]
| 触发方式 | 入口函数 | 文件 |
|---|---|---|
| 手动进场 | 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 |
车辆进场 / 出场
│
├─ CheckVehicleShortlist(plate, rfid)
│
└─ shortlist.ListType == "白名单" && ExpirationTime > now
├─ 未过期 → 白名单放行(后续按免密处理)
└─ 已过期 → 按临时车计费
包月车办理时自动写入 shortlist(白名单, ExpirationTime=end_date),进出场复用现有白名单检查,零改动。