# 取票打印 + 扫码出场 — 设计文档

> 日期：2026-07-30 | 优先级：P0-4

## 1. 目标

实现临时车取票入场、扫码出场。入场时票机按钮触发打印二维码小票，出场时扫码枪扫描小票二维码查询数字票并结算。

## 2. 硬件架构

```
入场：打印机按钮 → 串口 → PC → 后端 → ESC/POS 打印
出场：扫码枪(USB键盘) → PC → entryExit.vue → 后端查询 ticket
```

## 3. 数据模型

### printer 设备配置表

| 字段 | 类型 | 说明 |
|------|------|------|
| id | uint | 主键 |
| name | string | 打印机名称 |
| port_name | string | COM端口名（如 COM3） |
| baud_rate | int | 波特率（默认 9600） |
| status | string | online / offline |
| location | string | 安装位置（入口/出口） |

复用现有 `vehicle_record` + `digital_ticket`（无车牌，trigger_mode=ticket_machine）。

## 4. 入场流程

```
驾驶员按票机按钮 → 串口接收 → POST /ticket-machine/button
  ├─ 创建 vehicle_record（无车牌）
  ├─ 创建 digital_ticket (trigger_mode=ticket_machine)
  ├─ ESC/POS 打印小票
  │    内容：停车场名称 / 入场时间 / 票号 / ticket_no 二维码
  └─ GateOpener 抬杆
```

## 5. 出场流程

```
扫码枪扫小票 → entryExit.vue 监听输入 → 自动填入 ticket_no
  ├─ GET /digital-ticket/{ticketNo}
  ├─ ExitPreview → 计费
  ├─ 支付（现金/POS/扫码）
  └─ ExitConfirm → 抬杆
```

## 6. 后端新增

| 文件 | 说明 |
|------|------|
| `internal/dao/printer.go` | 打印机设备 DAO |
| `internal/modules/printer/api.go` | 打印机管理 + 按钮事件 API |
| `internal/modules/printer/service/service.go` | ESC/POS 指令封装 + 串口通信 |
| `internal/modules/printer/service/escpos.go` | ESC/POS 协议常量与指令构建 |

## 7. 小票模板

```
┌──────────────────────────────┐
│    PARKING TICKET            │
│    停车票                     │
│                              │
│    {parking_lot.lot_name}    │
│    {parking_lot.type_text}   │
│                              │
│    PARK AT YOUR OWN RISK     │
│──────────────────────────────│
│    In-Time::{entry_time}     │
│    InGate::{channel.code}    │
│    Veh No::{plate_number}    │  ← 识别到则印，未识别留空
│──────────────────────────────│
│    Slip No::{ticket_id}      │  ← digital_ticket.id 短流水号
│──────────────────────────────│
│      [二维码]                 │  ← 编码 ticket_no（full UUID）
│──────────────────────────────│
│  {payment_hint}              │  ← 配置项，如 "SCAN AND PAY..."
└──────────────────────────────┘
```

数据来源：
| 字段 | 来源 |
|------|------|
| parking_lot | `parking_lot.lot_name` |
| type_text | `parking_lot.remark` 或写死 "A-General Car" |
| entry_time | `time.Now().Format("02-01-2006 15:04:05")` |
| in_gate | `channel.channel_code`（按钮事件时指定通道） |
| veh_no | 车牌识别结果，无则空 |
| slip_no | `digital_ticket.id`（自增整数） |
| 二维码 | `digital_ticket.ticket_no`（32位UUID） |
| payment_hint | 系统配置表或写死 |

## 8. 文件变更

| 文件 | 操作 |
|------|------|
| `internal/dao/printer.go` | 新建 |
| `internal/modules/printer/api.go` | 新建 |
| `internal/modules/printer/service/service.go` | 新建 |
| `internal/modules/printer/service/escpos.go` | 新建 |
| `internal/initialize/gorm.go` | 修改 |
| `internal/initialize/router.go` | 修改 |
| `frontend/src/view/parking/entryExit.vue` | 修改（出场监听扫码枪输入） |
