소스 검색

docs: 进出场逻辑流程文档

lq 1 개월 전
부모
커밋
483ec3d79c
1개의 변경된 파일245개의 추가작업 그리고 0개의 파일을 삭제
  1. 245 0
      doc/进出场逻辑流程.md

+ 245 - 0
doc/进出场逻辑流程.md

@@ -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)`,进出场复用现有白名单检查,零改动。