版本:1.0 | 日期:2026-07-23
数字票(Digital Ticket)是停车业务中的标准化抽象模型,以电子化的"票"作为核心对象,遵循 创建 → 支付 → 验证 → 放行 的线性状态机模式。不绑定特定硬件或支付方式,具有高度的通用性和可扩展性。
任何进场方式(手动/RFID/车牌)
│
▼
VehicleEntry() ──→ 创建 vehicle_record
│ │
│ ▼
│ 创建 digital_ticket
│ (状态: pending_payment)
│
▼
ExitPreview / ExitConfirm
│
▼
digital_ticket 状态 → paid → exited
数字票是 vehicle_record 的业务状态层,不替代它。报表和监控继续用 vehicle_record,业务流转看 digital_ticket 状态。
┌──────────┐
┌──→ │ expired │ ←──┐
│ │ 已过期 │ │
│ └──────────┘ │
│ 超时未支付 │ 超时未离场
│ │
┌─────────┐ │ ┌───────────────┐ │ ┌──────┐ ┌────────┐
│ created │─→│ │pending_payment│─→│ paid │─→│ exited │
│ 已创建 │ │ 待支付 │ │ 已支付│ │ 已离场 │
└─────────┘ └───────────────┘ └──────┘ └────────┘
车辆入场 自动转换 支付成功 验证放行
| 状态 | 说明 | 触发条件 |
|---|---|---|
created |
车辆入场,数字票生成 | 入场事件触发 |
pending_payment |
待支付 | 创建完成后自动进入 |
paid |
已支付,费用结清 | 支付成功回调 |
exited |
已离场,流程结束 | 验证通过后开闸 |
expired |
超时未支付或未离场 | 定时任务或超时逻辑 |
| 当前状态 | 允许转换到 |
|---|---|
created |
pending_payment |
pending_payment |
paid, expired |
paid |
exited, expired |
exited |
无(终态) |
expired |
无(终态) |
状态转换是原子操作,代码层做了合法性校验,不允许跳跃式变更。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PK | 主键 |
| ticket_no | TEXT(64) | UNIQUE | 票号(32位十六进制UUID) |
| plate_number | TEXT(20) | 车牌号 | |
| trigger_mode | TEXT(20) | 触发方式:rfid / plate_recognition / manual | |
| state | TEXT(20) | default:created | 状态 |
| vehicle_record_id | INTEGER | FK | 关联 vehicle_record.id |
| entry_time | DATETIME | 入场时间 | |
| exit_time | DATETIME | 出场时间 | |
| fee | REAL | 计费金额 | |
| payment_method | TEXT(20) | 支付方式:cash / free / wechat / alipay | |
| payment_order_no | TEXT(64) | 支付单号 | |
| paid_at | DATETIME | 支付时间 | |
| expire_at | DATETIME | 过期时间 | |
| event_log | TEXT | 事件日志(JSON数组) | |
| created_at | DATETIME | 创建时间 | |
| updated_at | DATETIME | 更新时间 |
[
{"at": "2026-07-23T15:27:08+08:00", "event": "created", "trigger": "manual"},
{"at": "2026-07-23T15:35:12+08:00", "event": "pending_payment"},
{"at": "2026-07-23T15:36:01+08:00", "event": "paid", "payment_method": "cash", "paid_amount": 20, "payment_order_no": "PO20260723001"},
{"at": "2026-07-23T15:36:05+08:00", "event": "exited"}
]
| 方法 | 路径 | 说明 | 鉴权 |
|---|---|---|---|
| GET | /digital-ticket/list |
分页列表(按车牌/状态筛选) | JWT |
| GET | /digital-ticket/:ticketNo |
根据票号查询详情 | JWT |
| POST | /digital-ticket/:ticketNo/pay |
支付(→ paid) | JWT |
| POST | /digital-ticket/:ticketNo/exit |
离场验证(→ exited) | JWT |
{
"payment_method": "cash",
"payment_order_no": "PO20260723001",
"paid_amount": 20.0
}
| 参数 | 说明 |
|---|---|
| plate_number | 车牌号(模糊匹配) |
| state | 状态筛选 |
| page | 页码 |
| page_size | 每页条数 |
internal/modules/digital-ticket/
├── api.go # HTTP Handler + 路由注册
├── service/
│ └── service.go # 状态机 + 事件日志 + Create/Transition
└── repository/
└── repo.go # CRUD
internal/dao/digital_ticket.go # GORM 实体
| 函数 | 说明 |
|---|---|
TicketService.Create() |
创建数字票,生成 UUID ticket_no,写入 event_log |
TicketService.Transition() |
状态转换,校验合法性,追加事件日志 |
TicketService.GetByTicketNo() |
扫码查询(二维码凭证验证入口) |
数字票作为统一抽象,后续可扩展:
PayTicket/ticketNo 查询费用 → POS 支付 → 回调 /ticketNo/paypending_payment 超时票 → Transition 到 expiredWindows: %APPDATA%\smart-parking\lc_garage.db
→ C:\Users\<用户名>\AppData\Roaming\smart-parking\lc_garage.db
查询数字票数据:
sqlite3 "%APPDATA%/smart-parking/lc_garage.db" "SELECT id, ticket_no, plate_number, state FROM digital_ticket;"