digital-ticket.md 6.2 KB

数字票(Digital Ticket)说明文档

版本:1.0 | 日期:2026-07-23

1. 概述

数字票(Digital Ticket)是停车业务中的标准化抽象模型,以电子化的"票"作为核心对象,遵循 创建 → 支付 → 验证 → 放行 的线性状态机模式。不绑定特定硬件或支付方式,具有高度的通用性和可扩展性。

2. 核心概念

设计原则

  • 统一抽象:所有进场方式(RFID、车牌识别、手动录入)最终都创建数字票
  • 状态驱动:票的状态决定下一步操作,不允许跳跃式状态变更
  • 事件溯源:关键节点记录事件日志,便于审计和异常追踪
  • 二维码凭证:以 ticket_no(UUID)作为唯一标识和验证依据

与现有流程的关系

任何进场方式(手动/RFID/车牌)
        │
        ▼
  VehicleEntry() ──→ 创建 vehicle_record
        │                │
        │                ▼
        │         创建 digital_ticket
        │         (状态: pending_payment)
        │
        ▼
  ExitPreview / ExitConfirm
        │
        ▼
  digital_ticket 状态 → paid → exited

数字票是 vehicle_record 的业务状态层,不替代它。报表和监控继续用 vehicle_record,业务流转看 digital_ticket 状态。

3. 状态机

                    ┌──────────┐
               ┌──→ │  expired │ ←──┐
               │    │  已过期   │    │
               │    └──────────┘    │
               │     超时未支付      │ 超时未离场
               │                    │
  ┌─────────┐ │  ┌───────────────┐ │  ┌──────┐  ┌────────┐
  │ created │─→│  │pending_payment│─→│ paid │─→│ exited │
  │  已创建  │   │    待支付      │  │ 已支付│  │ 已离场  │
  └─────────┘   └───────────────┘  └──────┘  └────────┘
   车辆入场        自动转换         支付成功    验证放行
状态 说明 触发条件
created 车辆入场,数字票生成 入场事件触发
pending_payment 待支付 创建完成后自动进入
paid 已支付,费用结清 支付成功回调
exited 已离场,流程结束 验证通过后开闸
expired 超时未支付或未离场 定时任务或超时逻辑

状态转换规则

当前状态 允许转换到
created pending_payment
pending_payment paid, expired
paid exited, expired
exited 无(终态)
expired 无(终态)

状态转换是原子操作,代码层做了合法性校验,不允许跳跃式变更。

4. 数据模型

digital_ticket 表

字段 类型 约束 说明
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 更新时间

event_log 格式

[
  {"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"}
]

5. API 接口

方法 路径 说明 鉴权
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 每页条数

6. 代码结构

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() 扫码查询(二维码凭证验证入口)

7. 扩展性

数字票作为统一抽象,后续可扩展:

  • 线上支付:扫码 → 跳转支付页 → 回调 PayTicket
  • 自助缴费机:票机扫码 → 调 /ticketNo 查询费用 → POS 支付 → 回调 /ticketNo/pay
  • 无牌车处理:进场出纸质二维码小票(ticket_no 打印在票上)
  • 定时过期:后台任务扫描 pending_payment 超时票 → Transition 到 expired

8. 数据库位置

Windows: %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;"