|
|
@@ -0,0 +1,245 @@
|
|
|
+# 智慧停车 — 进出场逻辑流程
|
|
|
+
|
|
|
+> 日期: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)`,进出场复用现有白名单检查,零改动。
|