Przeglądaj źródła

debug: PRN端口枚举+SetPort调试

lq 1 miesiąc temu
rodzic
commit
111e8eb8bd

BIN
backend-only.exe


+ 24 - 0
cmd_backend_only/main.go

@@ -0,0 +1,24 @@
+package main
+
+import (
+	"wails-app/internal/core"
+	"wails-app/internal/global"
+	"wails-app/internal/initialize"
+)
+
+func main() {
+	global.GVA_VP = core.Viper()
+	initialize.OtherInit()
+	global.GVA_LOG = core.Zap()
+	global.GVA_DB = initialize.Gorm()
+	initialize.Timer()
+	initialize.DBList()
+	if global.GVA_DB != nil {
+		initialize.RegisterTables()
+		initialize.SeedSystemData()
+	}
+	initialize.InitUHFDevices()
+
+	// 只启动 Gin 后端,不启动 Wails
+	core.RunWindowsServer()
+}

+ 118 - 0
doc/收费流程.md

@@ -0,0 +1,118 @@
+根据您补充的业务场景,这已经涉及到了**硬件交互逻辑**和**岗亭工作流**的设计。这比单纯的计费算法更贴近实际落地。
+我将针对您描述的两种主要模式(月租/VIP、临时车),梳理成详细的**进出场业务流程图**,供开发对接使用。
+---
+### 一、 月租车 / VIP车 流程梳理
+**核心逻辑:** 身份识别 -> 权限校验(有效期) -> 自动放行。
+#### 1. 进场流程
+*   **触发方式 A:车牌识别(摄像头)**
+    1.  车辆驶入入口,触发地感/红外。
+    2.  摄像头抓拍,系统识别车牌号。
+    3.  系统查询数据库:
+        *   查询 确认是否为月租车/VIP。
+        *   检查 `Expire_Date`(过期时间)。
+    4.  **判断逻辑:**
+        *   **有效:** 语音播报“欢迎光临,月租有效”,自动开闸。
+        *   **过期:** 语音播报“月租已过期”,转为临时车流程(视策略:拒绝入场 或 按临时车入场)。
+    5.  记录入场日志(车牌、时间、快照)。
+*   **触发方式 B:刷卡/RFID(非车牌识别)**
+    1.  车主在入口票机/读卡器处刷卡/RFID卡。
+    2.  系统读取卡片ID,通过ID查询绑定的车辆信息。
+    3.  校验有效期(逻辑同上)。
+    4.  **有效:** 开闸放行。
+    5.  *注意:此类进场通常需要人工辅助确认车辆身份,或配合视频录像留存。*
+#### 2. 出场流程
+*   **流程:**
+    1.  车辆驶入出口,触发识别(车牌或刷卡)。
+    2.  系统校验状态:
+        *   是否有入场记录?
+        *   月租是否在有效期内?
+    3.  **判断逻辑:**
+        *   **有效:** 语音播报“一路顺风”,自动抬杆放行。
+        *   **过期:**
+            *   策略1:拒绝出场,需去岗亭缴费续费后放行。
+            *   策略2:自动转为临时车计费,需缴纳临时停车费。
+    4.  记录出场日志,销除在场车辆记录。
+---
+### 二、 临时车 流程梳理
+**核心逻辑:** 取票进场 -> 扫码计费 -> 支付确认 -> 放行。
+#### 1. 进场流程(统一取票)
+*   **动作:**
+    1.  车辆驶入入口,驾驶员按下票机按钮(或自动感应出票)。
+    2.  **票机动作:**
+        *   打印二维码小票(包含:入场时间、流水号、二维码)。
+        *   *系统后台:* 生成一条“无车牌”或“待关联”的入场记录。
+    3.  道闸开启,车辆进场。
+    4.  *痛点提示:* 此时不识别车牌,出场时完全依赖小票。若小票丢失,需人工核对入场记录(通过入场抓拍图片)。
+#### 2. 出场流程(非人工自助缴费)
+*   **场景:** 票机自带扫码枪 + POS机连接
+*   **流程:**
+    1.  驾驶员在出口票机处,将小票二维码对准扫码窗。
+    2.  **系统计算:**
+        *   解析小票获取入场时间 ($T_{in}$)。
+        *   获取当前时间 ($T_{out}$)。
+        *   调用计费引擎,算出金额 `Amount`。
+    3.  **交互:**
+        *   屏幕/语音提示:“请缴纳费用 XX 元”。
+        *   票机连接的POS机进入待支付状态。
+    4.  **支付:**
+        *   驾驶员在POS机上刷卡(银行卡/会员卡)。
+        *   POS机返回“交易成功”信号给票机/系统。
+    5.  **结果:**
+        *   系统接收到支付成功信号 -> 下发开闸指令 -> 道闸抬杆。
+        *   票机打印/吐出收据凭证(可选)。
+#### 3. 出场流程(人工收费)
+*   **场景:** 现金支付,需岗亭保安协助
+*   **流程:**
+    1.  驾驶员到达出口,将小票交给保安,或自己在票机扫码。
+    2.  **系统计算:**
+        *   屏幕显示计费金额(保安岗亭端可见)。
+    3.  **人工交互:**
+        *   保安收取现金(纸币/硬币)。
+        *   保安在管理软件界面点击“现金收款”或“确认放行”。
+    4.  **结果:**
+        *   系统记录“现金支付”流水。
+        *   软件下发开闸指令 -> 道闸抬杆。
+---
+### 三、 系统交互逻辑图解 (开发梳理)
+为了方便您开发,我将上述逻辑转化为系统的**模块交互序列**:
+#### 场景:临时车非人工出场(票机+POS联动)
+```mermaid
+sequenceDiagram
+    participant User as 驾驶员
+    participant Machine as 出口票机(硬件)
+    participant Server as 管理服务器
+    participant POS as 刷卡POS机
+    User->>Machine: 扫描小票二维码
+    Machine->>Server: 上报票号/入场ID
+    Server->>Server: 查询入场时间,计算费用
+    Server-->>Machine: 返回金额 (例如: 15元)
+    Machine->>User: 屏幕显示/语音播报金额
+    Machine->>POS: 发送扣款指令 (金额15元)
+    User->>POS: 刷卡支付
+    POS->>POS: 银行卡扣款处理
+    POS-->>Machine: 返回扣款成功信号
+    Machine->>Server: 上报支付成功
+    Server->>Server: 更新订单状态为“已支付”
+    Server-->>Machine: 下发开闸指令
+    Machine->>Machine: 触发道闸抬杆
+```
+---
+### 四、 关键技术点与开发建议
+在实现这套逻辑时,请重点关注以下几个技术细节:
+**1. 票机与服务器的通信协议**
+*   票机通常通过 TCP/IP 或串口与服务端通信。
+*   **建议:** 采用心跳包机制。服务器需要知道票机是否“在线”。如果票机断网,应立即报警,避免车辆堵在出口。
+**2. 小票二维码的数据安全**
+*   小票上的二维码是进出的唯一凭证。
+*   **建议:** 二维码内容不要直接写“时间:20231010”,容易被伪造。建议生成一个**加密的UUID**或**动态Token**,服务器端通过Token反查入场记录,防止逃票。
+**3. POS机支付的异步回调**
+*   刷卡支付可能有延迟(网络波动)。
+*   **建议:** 票机端应设置“支付等待状态”(比如等待30秒)。如果POS机返回成功,但票机没收到信号,需要有“重试机制”或“人工干预按钮”,避免道闸不开导致堵车。
+**4. 无牌车/无票车的兜底逻辑**
+*   **进场:** 取票进场,但进场时摄像头没拍到车牌。
+*   **出场:** 用户票丢了。
+    *   **解决方案:** 保安端软件需提供“查询入场记录”功能,通过时间范围查找入场记录,并匹配摄像头抓拍的图片,确认后手动补交费放行。
+**5. 月租车“过期”的临界点处理**
+*   如果月租车在进场时有效,出场时过期了怎么办?
+*   **逻辑建议:** 通常允许出场(因为在场内过期不应惩罚),或者在进场时检查如果“距离过期少于1小时”,则语音提醒“月租即将到期,请及时续费”。
+这套逻辑梳理涵盖了您提到的硬件交互细节,您可以直接基于此流程图编写开发文档。

+ 96 - 0
doc/测试用例编写规范.md

@@ -0,0 +1,96 @@
+编写规范、高质量的测试用例是保障软件质量的关键环节。一份优秀的测试用例不仅要自己能看懂,更要让团队其他成员(如开发、产品、其他测试人员)都能轻松理解并执行。
+以下是测试用例编写的一套标准规范指南:
+---
+### 一、 测试用例的核心要素(必填项)
+一个标准的测试用例通常包含以下核心字段:
+1.  **用例编号**
+    *   **规范**:需具有唯一性,通常采用“模块_子模块_功能_序号”的格式。
+    *   *示例*:`SYS_LOGIN_001` 或 `SHOP_CART_ADD_002`。
+2.  **用例标题**
+    *   **规范**:用简练的语言概括测试点。建议采用“条件+动作+预期结果”的结构,且结尾通常不带句号。
+    *   *示例*:`输入正确的用户名和密码,登录成功`。
+3.  **前置条件**
+    *   **规范**:描述执行本用例前必须满足的状态或环境准备。
+    *   *示例*:`用户已注册账号且状态为激活;已打开登录页面`。
+4.  **测试步骤**
+    *   **规范**:步骤要清晰、具体、可执行。避免模糊词汇(如“反复操作”、“多试几次”)。步骤编号要有序(1、2、3...)。
+5.  **预期结果**
+    *   **规范**:明确描述系统应有的反馈,包括页面提示、数据库变化、接口返回值等。切忌模棱两可(如“显示正常”应改为“显示‘登录成功’弹窗”)。
+6.  **优先级**
+    *   **规范**:
+        *   P0(冒烟/核心):阻碍系统运行的核心功能。
+        *   P1(重要):主要业务流程。
+        *   P2(一般):次要功能、边界情况。
+        *   P3(次要):UI优化、体验类、极低概率场景。
+7.  **所属模块/标签**
+    *   用于分类管理,便于筛选和统计。
+---
+### 二、 编写原则(核心心法)
+在编写具体内容时,需遵循以下五大原则:
+#### 1. 单一职责原则
+*   **规范**:一个测试用例只验证一个功能点或一个业务场景。
+*   **反例**:一个用例里既测试登录成功,又测试修改密码,又测试注销。这会导致用例冗长,且出错后难以定位具体环节。
+#### 2. 可判定性/可验证性
+*   **规范**:预期结果必须是客观的、可被观察到的。不能依赖测试人员的主观感受。
+*   *错误写法*:“界面美观,用户体验好。”
+*   *正确写法*:“登录按钮圆角为4px,点击后有加载动画,时长不超过2秒。”
+#### 3. 可重复性
+*   **规范**:无论谁执行,无论执行多少次,只要环境一致,结果应该一致。步骤描述不能有歧义。
+*   *模糊写法*:“输入一些特殊字符。”
+*   *精确写法*:“在用户名输入框输入 `test@#$%`。”
+#### 4. 独立性
+*   **规范**:用例之间尽量保持独立,不要有强依赖关系。
+*   *问题*:用例B执行必须依赖用例A执行成功后的数据。如果用例A失败,用例B就无法执行,增加维护成本。
+*   *建议*:通过数据准备或公共前置条件来解耦。
+#### 5. 覆盖性
+*   **规范**:不仅要覆盖“正确路径”,更要覆盖“错误路径”和“边界值”。
+---
+### 三、 具体编写技巧
+#### 1. 步骤编写规范
+*   **量化具体**:不要说“多输入几次”,要说“输入5次错误密码”。
+*   **操作明确**:不要说“操作下拉框”,要说“点击下拉框,选择‘选项A’”。
+*   **数据明确**:不要说“输入正确的数据”,要说“输入用户名 `admin`,密码 `123456`”。
+#### 2. 预期结果编写规范
+*   **多维验证**:不仅要看界面表现,还要关注后台数据。
+    *   *UI层*:提示文案、跳转页面、按钮状态。
+    *   *数据层*:数据库字段是否更新、日志是否记录。
+    *   *接口层*:HTTP状态码、返回的JSON结构。
+#### 3. 边界值分析规范
+*   必须包含:正好等于边界、刚刚小于边界、刚刚大于边界。
+*   *示例*(限额1-100元):
+    *   0元(无效边界)
+    *   1元(有效边界)
+    *   100元(有效边界)
+    *   101元(无效边界)
+---
+### 四、 常见的分类与设计方法
+在编写用例前,建议使用以下方法设计测试点:
+1.  **等价类划分**:将输入数据分为有效等价类和无效等价类,减少测试用例数量。
+2.  **边界值分析**:重点测试边界情况(最容易出Bug的地方)。
+3.  **错误推测法**:根据经验推测可能存在的隐患(如断网、并发、超时)。
+4.  **场景法**:覆盖业务流程(基本流和备选流)。
+**按测试类型分类:**
+*   **正向用例**:符合需求定义的正常操作。
+*   **逆向用例**:异常操作、错误数据、权限不足、网络中断等。
+*   **UI/体验用例**:布局、兼容性、易用性。
+---
+### 五、 案例模板演示
+**用例编号**:`USER_LOGIN_001`
+**用例标题**:使用已注册且激活的账号,正确输入用户名和密码,登录成功
+**优先级**:P0
+**前置条件**:
+1. 数据库中存在用户 `testuser`,密码 `123456`。
+2. 用户状态为 `Activated`。
+3. 打开系统登录页面。
+| 序号 | 测试步骤                       | 预期结果                                                     |
+| :--- | :----------------------------- | :----------------------------------------------------------- |
+| 1    | 在用户名输入框输入:`testuser` | 输入框正常显示字符                                           |
+| 2    | 在密码输入框输入:`123456`     | 显示为密文(如圆点或星号)                                   |
+| 3    | 点击“登录”按钮                 | 1. 按钮置灰并显示“登录中...”加载状态<br>2. 页面跳转至首页<br>3. 右上角显示用户名 `testuser` |
+---
+### 六、 常见误区(避坑指南)
+1.  **“大概齐”步骤**:步骤写得像说明书,而不是操作指引。例如:“输入用户信息”。(错误,应具体到输入什么信息)。
+2.  **预期结果空泛**:预期结果写“登录成功”。(错误,应写明具体的页面跳转、提示弹窗或数据库记录)。
+3.  **遗漏异常场景**:只写正确流程,忘记写密码错误、账号锁定、网络波动等异常场景。
+4.  **用例不可执行**:前置条件描述为“准备好测试数据”,但没说准备什么数据,导致执行者无法开始。
+遵循以上规范,可以确保测试用例库具有良好的**可读性、可维护性和执行效率**,从而真正发挥测试用例的价值。

+ 278 - 0
doc/进出场流程图.excalidraw

@@ -0,0 +1,278 @@
+{
+  "type": "excalidraw",
+  "version": 2,
+  "source": "hermes-agent",
+  "elements": [
+    {"type":"text","id":"title","x":300,"y":10,"text":"停车场车辆进出场完整流程图","fontSize":28,"fontFamily":1,"strokeColor":"#1e1e1e","originalText":"停车场车辆进出场完整流程图","autoResize":true},
+
+    {"type":"text","id":"leg_entry","x":50,"y":55,"text":"蓝=入场","fontSize":14,"fontFamily":1,"strokeColor":"#1971c2","originalText":"蓝=入场","autoResize":true},
+    {"type":"text","id":"leg_exit","x":170,"y":55,"text":"绿=出场","fontSize":14,"fontFamily":1,"strokeColor":"#2f9e44","originalText":"绿=出场","autoResize":true},
+    {"type":"text","id":"leg_dec","x":290,"y":55,"text":"黄=决策","fontSize":14,"fontFamily":1,"strokeColor":"#e67700","originalText":"黄=决策","autoResize":true},
+    {"type":"text","id":"leg_err","x":400,"y":55,"text":"红=拦截","fontSize":14,"fontFamily":1,"strokeColor":"#e03131","originalText":"红=拦截","autoResize":true},
+    {"type":"text","id":"leg_db","x":500,"y":55,"text":"青=数据","fontSize":14,"fontFamily":1,"strokeColor":"#0c8599","originalText":"青=数据","autoResize":true},
+
+    {"type":"line","id":"div_top","x":30,"y":80,"width":1100,"height":0,"strokeStyle":"dashed","strokeColor":"#adb5bd","opacity":50,"points":[[0,0],[1100,0]]},
+    {"type":"line","id":"div_mid","x":580,"y":95,"width":0,"height":900,"strokeStyle":"dashed","strokeColor":"#adb5bd","opacity":25,"points":[[0,0],[0,900]]},
+
+    {"type":"text","id":"sec_entry_t","x":180,"y":95,"text":"-- 入 场 流 程 --","fontSize":18,"fontFamily":1,"strokeColor":"#1971c2","originalText":"-- 入 场 流 程 --","autoResize":true},
+    {"type":"text","id":"sec_exit_t","x":780,"y":95,"text":"-- 出 场 流 程 --","fontSize":18,"fontFamily":1,"strokeColor":"#2f9e44","originalText":"-- 出 场 流 程 --","autoResize":true},
+
+    {"type":"rectangle","id":"e_arrive","x":60,"y":130,"width":220,"height":48,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_arrive","type":"text"}]},
+    {"type":"text","id":"te_arrive","x":95,"y":143,"text":"车辆到达入口","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_arrive","originalText":"车辆到达入口","autoResize":true},
+
+    {"type":"diamond","id":"e_trig","x":70,"y":210,"width":200,"height":65,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"te_trig","type":"text"}]},
+    {"type":"text","id":"te_trig","x":120,"y":233,"text":"触发方式?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_trig","originalText":"触发方式?","autoResize":true},
+
+    {"type":"arrow","id":"ae1","x":170,"y":178,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow","startBinding":{"elementId":"e_arrive","fixedPoint":[0.5,1]},"endBinding":{"elementId":"e_trig","fixedPoint":[0.5,0]}},
+
+    {"type":"rectangle","id":"e_rfid","x":30,"y":310,"width":170,"height":42,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"te_rfid","type":"text"}]},
+    {"type":"text","id":"te_rfid","x":50,"y":323,"text":"RFID 自动识别","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_rfid","originalText":"RFID 自动识别","autoResize":true},
+
+    {"type":"rectangle","id":"e_manual","x":240,"y":310,"width":170,"height":42,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"te_manual","type":"text"}]},
+    {"type":"text","id":"te_manual","x":260,"y":323,"text":"手动录入(前端)","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_manual","originalText":"手动录入(前端)","autoResize":true},
+
+    {"type":"arrow","id":"ae2","x":140,"y":273,"width":0,"height":37,"points":[[0,0],[-40,37]],"endArrowhead":"arrow","startBinding":{"elementId":"e_trig","fixedPoint":[0.3,1]},"endBinding":{"elementId":"e_rfid","fixedPoint":[0.5,0]}},
+    {"type":"text","id":"le2","x":70,"y":280,"text":"RFID","fontSize":13,"fontFamily":1,"strokeColor":"#e67700","containerId":"ae2","originalText":"RFID","autoResize":true},
+
+    {"type":"arrow","id":"ae3","x":220,"y":273,"width":0,"height":37,"points":[[0,0],[40,37]],"endArrowhead":"arrow","startBinding":{"elementId":"e_trig","fixedPoint":[0.7,1]},"endBinding":{"elementId":"e_manual","fixedPoint":[0.5,0]}},
+    {"type":"text","id":"le3","x":260,"y":280,"text":"手动","fontSize":13,"fontFamily":1,"strokeColor":"#e67700","containerId":"ae3","originalText":"手动","autoResize":true},
+
+    {"type":"rectangle","id":"e_dbn","x":30,"y":385,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_dbn","type":"text"}]},
+    {"type":"text","id":"te_dbn","x":50,"y":395,"text":"防抖(3s)","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_dbn","originalText":"防抖(3s)","autoResize":true},
+    {"type":"arrow","id":"ae4","x":115,"y":352,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_dev","x":30,"y":455,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_dev","type":"text"}]},
+    {"type":"text","id":"te_dev","x":35,"y":465,"text":"查询设备/通道方向","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_dev","originalText":"查询设备/通道方向","autoResize":true},
+    {"type":"arrow","id":"ae5","x":115,"y":423,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_veh","x":30,"y":525,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_veh","type":"text"}]},
+    {"type":"text","id":"te_veh","x":35,"y":535,"text":"查询车辆(isTemp?)","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_veh","originalText":"查询车辆(isTemp?)","autoResize":true},
+    {"type":"arrow","id":"ae6","x":115,"y":493,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"diamond","id":"e_blk","x":30,"y":600,"width":170,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"te_blk","type":"text"}]},
+    {"type":"text","id":"te_blk","x":65,"y":618,"text":"黑名单?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_blk","originalText":"黑名单?","autoResize":true},
+    {"type":"arrow","id":"ae7","x":115,"y":563,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_blk_block","x":220,"y":608,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#ffc9c9","fillStyle":"solid","boundElements":[{"id":"te_blk_block","type":"text"}]},
+    {"type":"text","id":"te_blk_block","x":235,"y":618,"text":"禁止入场","fontSize":14,"fontFamily":1,"strokeColor":"#e03131","containerId":"e_blk_block","originalText":"禁止入场","autoResize":true},
+    {"type":"arrow","id":"ae8","x":170,"y":626,"width":50,"height":0,"points":[[0,0],[48,0]],"endArrowhead":"arrow","startBinding":{"elementId":"e_blk","fixedPoint":[0.85,0.5]},"endBinding":{"elementId":"e_blk_block","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"le8","x":180,"y":613,"text":"是","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","containerId":"ae8","originalText":"是","autoResize":true},
+
+    {"type":"diamond","id":"e_temp","x":30,"y":685,"width":170,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"te_temp","type":"text"}]},
+    {"type":"text","id":"te_temp","x":40,"y":700,"text":"临时车+不允许?","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_temp","originalText":"临时车+不允许?","autoResize":true},
+    {"type":"arrow","id":"ae9","x":100,"y":652,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow","startBinding":{"elementId":"e_blk","fixedPoint":[0.35,1]},"endBinding":{"elementId":"e_temp","fixedPoint":[0.35,0]}},
+    {"type":"text","id":"le9","x":70,"y":660,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ae9","originalText":"否","autoResize":true},
+
+    {"type":"rectangle","id":"e_temp_block","x":220,"y":693,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#ffc9c9","fillStyle":"solid","boundElements":[{"id":"te_temp_block","type":"text"}]},
+    {"type":"text","id":"te_temp_block","x":235,"y":703,"text":"禁止通行","fontSize":14,"fontFamily":1,"strokeColor":"#e03131","containerId":"e_temp_block","originalText":"禁止通行","autoResize":true},
+    {"type":"arrow","id":"ae10","x":170,"y":711,"width":50,"height":0,"points":[[0,0],[48,0]],"endArrowhead":"arrow","startBinding":{"elementId":"e_temp","fixedPoint":[0.85,0.5]},"endBinding":{"elementId":"e_temp_block","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"le10","x":180,"y":698,"text":"是","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","containerId":"ae10","originalText":"是","autoResize":true},
+
+    {"type":"diamond","id":"e_dup","x":30,"y":770,"width":170,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"te_dup","type":"text"}]},
+    {"type":"text","id":"te_dup","x":55,"y":788,"text":"已在场内?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_dup","originalText":"已在场内?","autoResize":true},
+    {"type":"arrow","id":"ae11","x":95,"y":737,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow","startBinding":{"elementId":"e_temp","fixedPoint":[0.35,1]},"endBinding":{"elementId":"e_dup","fixedPoint":[0.35,0]}},
+    {"type":"text","id":"le11","x":60,"y":745,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ae11","originalText":"否","autoResize":true},
+
+    {"type":"rectangle","id":"e_dup_block","x":220,"y":778,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#ffc9c9","fillStyle":"solid","boundElements":[{"id":"te_dup_block","type":"text"}]},
+    {"type":"text","id":"te_dup_block","x":235,"y":788,"text":"重复入场拦截","fontSize":14,"fontFamily":1,"strokeColor":"#e03131","containerId":"e_dup_block","originalText":"重复入场拦截","autoResize":true},
+    {"type":"arrow","id":"ae12","x":170,"y":796,"width":50,"height":0,"points":[[0,0],[48,0]],"endArrowhead":"arrow","startBinding":{"elementId":"e_dup","fixedPoint":[0.85,0.5]},"endBinding":{"elementId":"e_dup_block","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"le12","x":180,"y":783,"text":"是","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","containerId":"ae12","originalText":"是","autoResize":true},
+
+    {"type":"rectangle","id":"e_gate","x":30,"y":860,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"te_gate","type":"text"}]},
+    {"type":"text","id":"te_gate","x":35,"y":870,"text":"GateOpener 开闸","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_gate","originalText":"GateOpener 开闸","autoResize":true},
+    {"type":"arrow","id":"ae13","x":80,"y":822,"width":0,"height":38,"points":[[0,0],[0,38]],"endArrowhead":"arrow","startBinding":{"elementId":"e_dup","fixedPoint":[0.25,1]},"endBinding":{"elementId":"e_gate","fixedPoint":[0.25,0]}},
+    {"type":"text","id":"le13","x":40,"y":830,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ae13","originalText":"否","autoResize":true},
+
+    {"type":"rectangle","id":"e_api","x":240,"y":385,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_api","type":"text"}]},
+    {"type":"text","id":"te_api","x":250,"y":395,"text":"POST /vehicle/entry","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_api","originalText":"POST /vehicle/entry","autoResize":true},
+    {"type":"arrow","id":"ae14","x":325,"y":352,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_ve_entry","x":240,"y":455,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","boundElements":[{"id":"te_ve_entry","type":"text"}]},
+    {"type":"text","id":"te_ve_entry","x":250,"y":465,"text":"VehicleEntry()","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_ve_entry","originalText":"VehicleEntry()","autoResize":true},
+    {"type":"arrow","id":"ae15","x":325,"y":423,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"arrow","id":"ae16","x":200,"y":879,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"e_gate","fixedPoint":[1,0.5]},"endBinding":{"elementId":"e_ve_entry","fixedPoint":[0,0.5]}},
+
+    {"type":"rectangle","id":"e_cr_veh","x":240,"y":525,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"te_cr_veh","type":"text"}]},
+    {"type":"text","id":"te_cr_veh","x":245,"y":535,"text":"查/创建车辆(临时车)","fontSize":12,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_cr_veh","originalText":"查/创建车辆(临时车)","autoResize":true},
+    {"type":"arrow","id":"ae17","x":325,"y":493,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_ins_rec","x":240,"y":600,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"te_ins_rec","type":"text"}]},
+    {"type":"text","id":"te_ins_rec","x":245,"y":610,"text":"INSERT vehicle_record","fontSize":12,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_ins_rec","originalText":"INSERT vehicle_record","autoResize":true},
+    {"type":"arrow","id":"ae18","x":325,"y":563,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_ticket","x":240,"y":670,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"te_ticket","type":"text"}]},
+    {"type":"text","id":"te_ticket","x":245,"y":680,"text":"CREATE digital_ticket","fontSize":12,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_ticket","originalText":"CREATE digital_ticket","autoResize":true},
+    {"type":"arrow","id":"ae19","x":325,"y":638,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_upd_v","x":240,"y":740,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"te_upd_v","type":"text"}]},
+    {"type":"text","id":"te_upd_v","x":245,"y":750,"text":"UPDATE last_entry_time","fontSize":12,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"e_upd_v","originalText":"UPDATE last_entry_time","autoResize":true},
+    {"type":"arrow","id":"ae20","x":325,"y":708,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"e_done","x":240,"y":820,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"te_done","type":"text"}]},
+    {"type":"text","id":"te_done","x":270,"y":830,"text":"入场成功","fontSize":16,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"e_done","originalText":"入场成功","autoResize":true},
+    {"type":"arrow","id":"ae21","x":325,"y":778,"width":0,"height":42,"points":[[0,0],[0,42]],"endArrowhead":"arrow"},
+
+    {"type":"text","id":"e_note1","x":30,"y":910,"text":"* RFID: HandlePassage 内部自动完成所有步骤","fontSize":12,"fontFamily":1,"strokeColor":"#868e96","originalText":"* RFID: HandlePassage 内部自动完成所有步骤","autoResize":true},
+    {"type":"text","id":"e_note2","x":30,"y":927,"text":"* 手动: 前端 entryExit.vue 调用 API,不控制道闸","fontSize":12,"fontFamily":1,"strokeColor":"#868e96","originalText":"* 手动: 前端 entryExit.vue 调用 API,不控制道闸","autoResize":true},
+
+    {"type":"rectangle","id":"x_arrive","x":680,"y":130,"width":220,"height":48,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_arrive","type":"text"}]},
+    {"type":"text","id":"tx_arrive","x":715,"y":143,"text":"车辆到达出口","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_arrive","originalText":"车辆到达出口","autoResize":true},
+
+    {"type":"diamond","id":"x_trig","x":690,"y":210,"width":200,"height":65,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tx_trig","type":"text"}]},
+    {"type":"text","id":"tx_trig","x":740,"y":233,"text":"触发方式?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_trig","originalText":"触发方式?","autoResize":true},
+    {"type":"arrow","id":"ax1","x":790,"y":178,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_rfid","x":620,"y":310,"width":170,"height":42,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"tx_rfid","type":"text"}]},
+    {"type":"text","id":"tx_rfid","x":640,"y":323,"text":"RFID 自动识别","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_rfid","originalText":"RFID 自动识别","autoResize":true},
+
+    {"type":"rectangle","id":"x_manual","x":830,"y":310,"width":170,"height":42,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"tx_manual","type":"text"}]},
+    {"type":"text","id":"tx_manual","x":850,"y":323,"text":"手动录入(前端)","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_manual","originalText":"手动录入(前端)","autoResize":true},
+
+    {"type":"arrow","id":"ax2","x":760,"y":273,"width":0,"height":37,"points":[[0,0],[-40,37]],"endArrowhead":"arrow","startBinding":{"elementId":"x_trig","fixedPoint":[0.3,1]},"endBinding":{"elementId":"x_rfid","fixedPoint":[0.5,0]}},
+    {"type":"text","id":"lx2","x":690,"y":280,"text":"RFID","fontSize":13,"fontFamily":1,"strokeColor":"#e67700","containerId":"ax2","originalText":"RFID","autoResize":true},
+
+    {"type":"arrow","id":"ax3","x":840,"y":273,"width":0,"height":37,"points":[[0,0],[40,37]],"endArrowhead":"arrow","startBinding":{"elementId":"x_trig","fixedPoint":[0.7,1]},"endBinding":{"elementId":"x_manual","fixedPoint":[0.5,0]}},
+    {"type":"text","id":"lx3","x":880,"y":280,"text":"手动","fontSize":13,"fontFamily":1,"strokeColor":"#e67700","containerId":"ax3","originalText":"手动","autoResize":true},
+
+    {"type":"rectangle","id":"x_dbn","x":620,"y":385,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_dbn","type":"text"}]},
+    {"type":"text","id":"tx_dbn","x":640,"y":395,"text":"防抖(3s)","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_dbn","originalText":"防抖(3s)","autoResize":true},
+    {"type":"arrow","id":"ax4","x":705,"y":352,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_dev","x":620,"y":455,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_dev","type":"text"}]},
+    {"type":"text","id":"tx_dev","x":625,"y":465,"text":"查询设备/通道方向","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_dev","originalText":"查询设备/通道方向","autoResize":true},
+    {"type":"arrow","id":"ax5","x":705,"y":423,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_hex","x":620,"y":525,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_hex","type":"text"}]},
+    {"type":"text","id":"tx_hex","x":630,"y":535,"text":"handleExit()","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_hex","originalText":"handleExit()","autoResize":true},
+    {"type":"arrow","id":"ax6","x":705,"y":493,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_gate","x":620,"y":600,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_gate","type":"text"}]},
+    {"type":"text","id":"tx_gate","x":625,"y":610,"text":"GateOpener 开闸","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_gate","originalText":"GateOpener 开闸","autoResize":true},
+    {"type":"text","id":"tx_gate_note","x":795,"y":608,"text":"(先开闸后扣费)","fontSize":11,"fontFamily":1,"strokeColor":"#e03131","originalText":"(先开闸后扣费)","autoResize":true},
+    {"type":"arrow","id":"ax7","x":705,"y":563,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_pv","x":830,"y":385,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_pv","type":"text"}]},
+    {"type":"text","id":"tx_pv","x":840,"y":395,"text":"ExitPreview 费用预览","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_pv","originalText":"ExitPreview 费用预览","autoResize":true},
+    {"type":"arrow","id":"ax8","x":915,"y":352,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow"},
+
+    {"type":"diamond","id":"x_is_temp","x":840,"y":460,"width":150,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tx_is_temp","type":"text"}]},
+    {"type":"text","id":"tx_is_temp","x":875,"y":478,"text":"临时车?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_is_temp","originalText":"临时车?","autoResize":true},
+    {"type":"arrow","id":"ax9","x":915,"y":423,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_cash","x":840,"y":545,"width":150,"height":38,"roundness":{"type":3},"backgroundColor":"#ffd8a8","fillStyle":"solid","boundElements":[{"id":"tx_cash","type":"text"}]},
+    {"type":"text","id":"tx_cash","x":850,"y":555,"text":"现金收款+找零","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_cash","originalText":"现金收款+找零","autoResize":true},
+    {"type":"arrow","id":"ax10","x":915,"y":513,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+    {"type":"text","id":"lx10","x":890,"y":518,"text":"是","fontSize":12,"fontFamily":1,"strokeColor":"#e67700","containerId":"ax10","originalText":"是","autoResize":true},
+
+    {"type":"rectangle","id":"x_free","x":1020,"y":470,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_free","type":"text"}]},
+    {"type":"text","id":"tx_free","x":1030,"y":480,"text":"免密(free)","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_free","originalText":"免密(free)","autoResize":true},
+    {"type":"arrow","id":"ax11","x":990,"y":486,"width":30,"height":0,"points":[[0,0],[28,0]],"endArrowhead":"arrow","startBinding":{"elementId":"x_is_temp","fixedPoint":[1,0.5]},"endBinding":{"elementId":"x_free","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lx11","x":1000,"y":473,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ax11","originalText":"否","autoResize":true},
+
+    {"type":"rectangle","id":"x_ec","x":830,"y":620,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_ec","type":"text"}]},
+    {"type":"text","id":"tx_ec","x":840,"y":630,"text":"ExitConfirm()","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_ec","originalText":"ExitConfirm()","autoResize":true},
+
+    {"type":"arrow","id":"ax12","x":790,"y":619,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"x_gate","fixedPoint":[1,0.5]},"endBinding":{"elementId":"x_ec","fixedPoint":[0,0.5]}},
+    {"type":"arrow","id":"ax13","x":915,"y":583,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow","startBinding":{"elementId":"x_cash","fixedPoint":[0.5,1]},"endBinding":{"elementId":"x_ec","fixedPoint":[0.5,0]}},
+
+    {"type":"diamond","id":"x_blk","x":840,"y":690,"width":150,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tx_blk","type":"text"}]},
+    {"type":"text","id":"tx_blk","x":875,"y":708,"text":"黑名单?","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_blk","originalText":"黑名单?","autoResize":true},
+    {"type":"arrow","id":"ax14","x":915,"y":658,"width":0,"height":32,"points":[[0,0],[0,32]],"endArrowhead":"arrow"},
+
+    {"type":"rectangle","id":"x_blk_block","x":1000,"y":698,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#ffc9c9","fillStyle":"solid","boundElements":[{"id":"tx_blk_block","type":"text"}]},
+    {"type":"text","id":"tx_blk_block","x":1015,"y":708,"text":"禁止出场","fontSize":14,"fontFamily":1,"strokeColor":"#e03131","containerId":"x_blk_block","originalText":"禁止出场","autoResize":true},
+    {"type":"arrow","id":"ax15","x":960,"y":716,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"x_blk","fixedPoint":[0.85,0.5]},"endBinding":{"elementId":"x_blk_block","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lx15","x":970,"y":703,"text":"是","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","containerId":"ax15","originalText":"是","autoResize":true},
+
+    {"type":"diamond","id":"x_rec","x":840,"y":775,"width":150,"height":55,"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tx_rec","type":"text"}]},
+    {"type":"text","id":"tx_rec","x":860,"y":793,"text":"未出场记录?","fontSize":15,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"x_rec","originalText":"未出场记录?","autoResize":true},
+    {"type":"arrow","id":"ax16","x":900,"y":742,"width":0,"height":33,"points":[[0,0],[0,33]],"endArrowhead":"arrow","startBinding":{"elementId":"x_blk","fixedPoint":[0.35,1]},"endBinding":{"elementId":"x_rec","fixedPoint":[0.35,0]}},
+    {"type":"text","id":"lx16","x":870,"y":750,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ax16","originalText":"否","autoResize":true},
+
+    {"type":"rectangle","id":"x_no_rec_block","x":1000,"y":783,"width":120,"height":38,"roundness":{"type":3},"backgroundColor":"#ffc9c9","fillStyle":"solid","boundElements":[{"id":"tx_no_rec_block","type":"text"}]},
+    {"type":"text","id":"tx_no_rec_block","x":1015,"y":793,"text":"未入场/已出场","fontSize":13,"fontFamily":1,"strokeColor":"#e03131","containerId":"x_no_rec_block","originalText":"未入场/已出场","autoResize":true},
+    {"type":"arrow","id":"ax17","x":960,"y":801,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"x_rec","fixedPoint":[0.85,0.5]},"endBinding":{"elementId":"x_no_rec_block","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lx17","x":970,"y":788,"text":"否","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","containerId":"ax17","originalText":"否","autoResize":true},
+
+    {"type":"text","id":"sec_fee","x":380,"y":960,"text":"-- 计 费 引 擎 calculateFee --","fontSize":16,"fontFamily":1,"strokeColor":"#e67700","originalText":"-- 计 费 引 擎 calculateFee --","autoResize":true},
+
+    {"type":"line","id":"div_fee","x":30,"y":985,"width":1100,"height":0,"strokeStyle":"dashed","strokeColor":"#adb5bd","opacity":25,"points":[[0,0],[1100,0]]},
+
+    {"type":"rectangle","id":"f_cfg","x":50,"y":1005,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tf_cfg","type":"text"}]},
+    {"type":"text","id":"tf_cfg","x":60,"y":1015,"text":"查 fee_config(按车型)","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_cfg","originalText":"查 fee_config(按车型)","autoResize":true},
+
+    {"type":"rectangle","id":"f_def","x":300,"y":1005,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#ffd8a8","fillStyle":"solid","boundElements":[{"id":"tf_def","type":"text"}]},
+    {"type":"text","id":"tf_def","x":310,"y":1015,"text":"未配置: 默认0.1元/min","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_def","originalText":"未配置: 默认0.1元/min","autoResize":true},
+    {"type":"arrow","id":"af1","x":250,"y":1024,"width":50,"height":0,"points":[[0,0],[48,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_cfg","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_def","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lf1","x":260,"y":1012,"text":"无","fontSize":12,"fontFamily":1,"strokeColor":"#e67700","containerId":"af1","originalText":"无","autoResize":true},
+
+    {"type":"rectangle","id":"f_vip","x":550,"y":1005,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#fff3bf","fillStyle":"solid","boundElements":[{"id":"tf_vip","type":"text"}]},
+    {"type":"text","id":"tf_vip","x":560,"y":1015,"text":"VIP 判断+折扣/免费","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_vip","originalText":"VIP 判断+折扣/免费","autoResize":true},
+    {"type":"arrow","id":"af2","x":250,"y":1024,"width":300,"height":0,"points":[[0,0],[298,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_cfg","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_vip","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lf2","x":400,"y":1012,"text":"有配置","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"af2","originalText":"有配置","autoResize":true},
+
+    {"type":"rectangle","id":"f_free","x":50,"y":1080,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tf_free","type":"text"}]},
+    {"type":"text","id":"tf_free","x":60,"y":1090,"text":"免费时长内 -> 0元","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_free","originalText":"免费时长内 -> 0元","autoResize":true},
+
+    {"type":"rectangle","id":"f_calc","x":300,"y":1080,"width":220,"height":38,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"tf_calc","type":"text"}]},
+    {"type":"text","id":"tf_calc","x":310,"y":1090,"text":"超时: 起步+单位x数量","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_calc","originalText":"超时: 起步+单位x数量","autoResize":true},
+    {"type":"arrow","id":"af3","x":250,"y":1099,"width":50,"height":0,"points":[[0,0],[48,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_free","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_calc","fixedPoint":[0,0.5]}},
+    {"type":"text","id":"lf3","x":260,"y":"1087","text":"超时","fontSize":12,"fontFamily":1,"strokeColor":"#e67700","containerId":"af3","originalText":"超时","autoResize":true},
+
+    {"type":"rectangle","id":"f_cap","x":"560","y":1080,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"tf_cap","type":"text"}]},
+    {"type":"text","id":"tf_cap","x":570,"y":1090,"text":"每日封顶检查","fontSize":14,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_cap","originalText":"每日封顶检查","autoResize":true},
+    {"type":"arrow","id":"af4","x":520,"y":1099,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_calc","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_cap","fixedPoint":[0,0.5]}},
+
+    {"type":"rectangle","id":"f_dsc","x":770,"y":1080,"width":160,"height":38,"roundness":{"type":3},"backgroundColor":"#d0bfff","fillStyle":"solid","boundElements":[{"id":"tf_dsc","type":"text"}]},
+    {"type":"text","id":"tf_dsc","x":780,"y":1090,"text":"VIP 折扣","fontSize":16,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"f_dsc","originalText":"VIP 折扣","autoResize":true},
+    {"type":"arrow","id":"af5","x":730,"y":1099,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_cap","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_dsc","fixedPoint":[0,0.5]}},
+
+    {"type":"rectangle","id":"f_res","x":970,"y":1080,"width":170,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tf_res","type":"text"}]},
+    {"type":"text","id":"tf_res","x":1000,"y":1090,"text":"返回费用","fontSize":16,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"f_res","originalText":"返回费用","autoResize":true},
+    {"type":"arrow","id":"af6","x":930,"y":1099,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"f_dsc","fixedPoint":[1,0.5]},"endBinding":{"elementId":"f_res","fixedPoint":[0,0.5]}},
+
+    {"type":"arrow","id":"af7","x":550,"y":1043,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow"},
+    {"type":"arrow","id":"af8","x":550,"y":1043,"width":0,"height":37,"points":[[0,0],[-300,37]],"endArrowhead":"arrow","startBinding":{"elementId":"f_vip","fixedPoint":[0,1]},"endBinding":{"elementId":"f_free","fixedPoint":[1,0]}},
+    {"type":"text","id":"lf8","x":350,"y":1050,"text":"VIP免费?","fontSize":12,"fontFamily":1,"strokeColor":"#e67700","containerId":"af8","originalText":"VIP免费?","autoResize":true},
+
+    {"type":"text","id":"sec_db","x":380,"y":1145,"text":"-- 出 场 数 据 操 作 --","fontSize":16,"fontFamily":1,"strokeColor":"#0c8599","originalText":"-- 出 场 数 据 操 作 --","autoResize":true},
+    {"type":"line","id":"div_db","x":30,"y":1170,"width":1100,"height":0,"strokeStyle":"dashed","strokeColor":"#adb5bd","opacity":25,"points":[[0,0],[1100,0]]},
+
+    {"type":"rectangle","id":"db_upd_rec","x":50,"y":1190,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"tdb_upd_rec","type":"text"}]},
+    {"type":"text","id":"tdb_upd_rec","x":60,"y":1200,"text":"UPDATE vehicle_record","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"db_upd_rec","originalText":"UPDATE vehicle_record","autoResize":true},
+
+    {"type":"rectangle","id":"db_pay","x":290,"y":1190,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"tdb_pay","type":"text"}]},
+    {"type":"text","id":"tdb_pay","x":300,"y":1200,"text":"INSERT payment_record","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"db_pay","originalText":"INSERT payment_record","autoResize":true},
+
+    {"type":"rectangle","id":"db_upd_v","x":530,"y":1190,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"tdb_upd_v","type":"text"}]},
+    {"type":"text","id":"tdb_upd_v","x":540,"y":1200,"text":"UPDATE vehicle统计","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"db_upd_v","originalText":"UPDATE vehicle统计","autoResize":true},
+
+    {"type":"rectangle","id":"db_tk","x":770,"y":1190,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#c3fae8","fillStyle":"solid","boundElements":[{"id":"tdb_tk","type":"text"}]},
+    {"type":"text","id":"tdb_tk","x":780,"y":1200,"text":"UPDATE digital_ticket","fontSize":13,"fontFamily":1,"strokeColor":"#1e1e1e","containerId":"db_tk","originalText":"UPDATE digital_ticket","autoResize":true},
+
+    {"type":"arrow","id":"ad1","x":250,"y":1209,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"db_upd_rec","fixedPoint":[1,0.5]},"endBinding":{"elementId":"db_pay","fixedPoint":[0,0.5]}},
+    {"type":"arrow","id":"ad2","x":490,"y":1209,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"db_pay","fixedPoint":[1,0.5]},"endBinding":{"elementId":"db_upd_v","fixedPoint":[0,0.5]}},
+    {"type":"arrow","id":"ad3","x":730,"y":1209,"width":40,"height":0,"points":[[0,0],[38,0]],"endArrowhead":"arrow","startBinding":{"elementId":"db_upd_v","fixedPoint":[1,0.5]},"endBinding":{"elementId":"db_tk","fixedPoint":[0,0.5]}},
+
+    {"type":"rectangle","id":"x_final","x":770,"y":1265,"width":200,"height":38,"roundness":{"type":3},"backgroundColor":"#b2f2bb","fillStyle":"solid","boundElements":[{"id":"tx_final","type":"text"}]},
+    {"type":"text","id":"tx_final","x":810,"y":1275,"text":"出场成功","fontSize":16,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"x_final","originalText":"出场成功","autoResize":true},
+    {"type":"arrow","id":"ad4","x":870,"y":1228,"width":0,"height":37,"points":[[0,0],[0,37]],"endArrowhead":"arrow","startBinding":{"elementId":"db_tk","fixedPoint":[0.5,1]},"endBinding":{"elementId":"x_final","fixedPoint":[0.5,0]}},
+
+    {"type":"arrow","id":"ad5","x":900,"y":827,"width":0,"height":363,"points":[[0,0],[-850,363]],"endArrowhead":"arrow","startBinding":{"elementId":"x_rec","fixedPoint":[0.35,1]},"endBinding":{"elementId":"db_upd_rec","fixedPoint":[0.5,0]}},
+    {"type":"text","id":"ld5","x":860,"y":870,"text":"有记录","fontSize":12,"fontFamily":1,"strokeColor":"#2f9e44","containerId":"ad5","originalText":"有记录","autoResize":true},
+
+    {"type":"text","id":"x_note1","x":620,"y":910,"text":"* RFID: handleExit 调 ExitConfirm(free) 自动扣费","fontSize":12,"fontFamily":1,"strokeColor":"#868e96","originalText":"* RFID: handleExit 调 ExitConfirm(free) 自动扣费","autoResize":true},
+    {"type":"text","id":"x_note2","x":620,"y":927,"text":"* 手动: entryExit.vue -> ExitPreview -> ExitConfirm(选支付)","fontSize":12,"fontFamily":1,"strokeColor":"#868e96","originalText":"* 手动: entryExit.vue -> ExitPreview -> ExitConfirm(选支付)","autoResize":true},
+
+    {"type":"text","id":"leg_v","x":50,"y":1280,"text":"车辆类型行为:","fontSize":15,"fontFamily":1,"strokeColor":"#1e1e1e","originalText":"车辆类型行为:","autoResize":true},
+    {"type":"text","id":"leg_v1","x":50,"y":1305,"text":"o 临时车: 入场自动创建 | 出场现金收款 | RFID免密","fontSize":12,"fontFamily":1,"strokeColor":"#495057","originalText":"o 临时车: 入场自动创建 | 出场现金收款 | RFID免密","autoResize":true},
+    {"type":"text","id":"leg_v2","x":50,"y":1325,"text":"o 固定车: 预先注册 | 出场免密(free)","fontSize":12,"fontFamily":1,"strokeColor":"#495057","originalText":"o 固定车: 预先注册 | 出场免密(free)","autoResize":true},
+    {"type":"text","id":"leg_v3","x":50,"y":1345,"text":"o VIP: 出场按 fee_config 免费/折扣","fontSize":12,"fontFamily":1,"strokeColor":"#495057","originalText":"o VIP: 出场按 fee_config 免费/折扣","autoResize":true},
+    {"type":"text","id":"leg_v4","x":50,"y":1365,"text":"o 白名单: 放行 | 出场正常计费","fontSize":12,"fontFamily":1,"strokeColor":"#495057","originalText":"o 白名单: 放行 | 出场正常计费","autoResize":true},
+    {"type":"text","id":"leg_v5","x":50,"y":1385,"text":"o 黑名单: 入场拦截 | 出场也拦截(需先解除)","fontSize":12,"fontFamily":1,"strokeColor":"#e03131","originalText":"o 黑名单: 入场拦截 | 出场也拦截(需先解除)","autoResize":true}
+  ],
+  "appState": {
+    "viewBackgroundColor": "#ffffff"
+  }
+}

+ 3 - 6
internal/modules/printer/api.go

@@ -75,12 +75,9 @@ func SetupPrinterPublic(router *gin.RouterGroup) {
 	router.GET("/ticket-machine/test", TicketMachineButtonTest)
 	router.GET("/ticket-machine/usb-list", func(c *gin.Context) {
 		printService.InitCSN(`D:\lq\资料\停车场 道闸 车牌识别 收费机\票机自制\打印机\EMD807嵌入式热敏票据打印模组\5、SDK指令集\Win_SDK_Demo_2023_08_14\Win_SDK_Demo_2023_08_14\lib\x64\CsnPrinterLibs.dll`)
-		name := printService.Csn_EnumUSB()
-		if name == "" {
-			response.FailWithMessage("未发现 USB 打印机", c)
-		} else {
-			response.OkWithData(gin.H{"usb_printer": name}, c)
-		}
+		usb := printService.Csn_EnumUSB()
+		prn := printService.Csn_EnumPRN()
+		response.OkWithData(gin.H{"usb": usb, "prn": prn}, c)
 	})
 }
 

+ 11 - 8
internal/modules/printer/service/csn_dll.go

@@ -22,15 +22,18 @@ func InitCSN(dllPath string) error {
 // === Port functions ===
 
 func Csn_EnumUSB() string {
-	if csnDll == nil {
-		return ""
-	}
+	if csnDll == nil { return "" }
 	buf := make([]byte, 256)
-	ret, _, _ := csnDll.NewProc("Port_EnumUSB").Call(
-		uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
-	if ret == 0 {
-		return ""
-	}
+	ret, _, _ := csnDll.NewProc("Port_EnumUSB").Call(uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
+	if ret == 0 { return "" }
+	return string(buf[:ret])
+}
+
+func Csn_EnumPRN() string {
+	if csnDll == nil { return "" }
+	buf := make([]byte, 256)
+	ret, _, _ := csnDll.NewProc("Port_EnumPRN").Call(uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
+	if ret == 0 { return "" }
 	return string(buf[:ret])
 }
 

BIN
server_job.txt


BIN
smart-parking-test.exe


+ 30 - 0
test/testutil/_fix_casbin.py

@@ -0,0 +1,30 @@
+import os
+
+# Fix testenv.go: add missing Casbin rules
+testenv_path = r"test\testutil\testenv.go"
+with open(testenv_path, 'r', encoding='utf-8') as f:
+    content = f.read()
+
+# Find the rules block and insert missing rules before the closing brace
+missing_rules = """\t\t// 黑白名单查询/更新/删除
+\t\t{"888", "/shortlist/queryShortlistList", "POST"},
+\t\t{"888", "/shortlist/updateShortlist", "PUT"},
+\t\t{"888", "/shortlist/deleteShortlist", "DELETE"},
+\t\t// 车辆类型管理
+\t\t{"888", "/vehicle/type/create", "POST"},
+\t\t{"888", "/vehicle/type/all", "GET"},
+"""
+
+# Insert after the shortlist queryAllShortlists line
+old = '\t\t{"888", "/shortlist/queryAllShortlists", "GET"},\n'
+new = old + missing_rules
+if old in content:
+    content = content.replace(new if False else old, new)
+    print("Inserted missing Casbin rules")
+else:
+    print("ERROR: Could not find insertion point in testenv.go")
+    exit(1)
+
+with open(testenv_path, 'w', encoding='utf-8') as f:
+    f.write(content)
+print(f"Updated testenv.go ({len(content)} bytes)")

+ 2 - 0
test/testutil/_g.py

@@ -0,0 +1,2 @@
+BT=chr(96)
+print(BT)

+ 41 - 0
test/testutil/_shortlist_delete.txt

@@ -0,0 +1,41 @@
+=== RUN   TestShortlistDelete
+您正在使用func Viper()传递的值,config的路径为D:\lq\Smart Parking\lc_garage\config.yaml
+
+2026/07/21 16:07:44 C:/Users/longc/go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274 SLOW SQL >= 200ms
+[251.373ms] [rows:0] CREATE INDEX `idx_payment_record_record_id` ON `payment_record`(`record_id`)
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:07:46.005	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+
+2026/07/21 16:08:01 D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:436 SLOW SQL >= 200ms
+[241.078ms] [rows:1] INSERT INTO casbin_rule (ptype, v0, v1, v2, v3, v4, v5) VALUES ("p", "9527", "/sysDictionaryDetail/deleteSysDictionaryDetail", "DELETE", "", "", "")
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:04.786	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+
+2026/07/21 16:08:09 D:/lq/Smart Parking/lc_garage/test/testutil/testenv.go:158 SLOW SQL >= 200ms
+[200.149ms] [rows:1] INSERT INTO `uhf_reader` (`created_at`,`updated_at`,`deleted_at`,`device_code`,`device_name`,`device_type`,`connect_type`,`ip_address`,`port`,`com_port`,`baud_rate`,`status`,`is_active`,`description`,`parking_lot_id`,`channel_id`) VALUES ("2026-07-21 16:08:09.294","2026-07-21 16:08:09.294",NULL,"UHF001","测试UHF读卡器","TCP","tcp","127.0.0.1",8000,"",9600,"offline",true,"",1,1) RETURNING `id`,`last_online_time`
+
+2026/07/21 16:08:09 D:/lq/Smart Parking/lc_garage/test/testutil/testenv.go:175 record not found
+[0.504ms] [rows:0] SELECT * FROM `vehicle_type` WHERE (is_system = true AND name != "临时车") AND `vehicle_type`.`deleted_at` IS NULL ORDER BY `vehicle_type`.`id` LIMIT 1
+无鉴权插件安装==》 &{[0x7ff6a3896980] / 0xc0016f1860 false}
+鉴权插件安装==》 &{[0x7ff6a3896980] / 0xc0016f1860 false}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:09.986	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:09.987	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+
+2026/07/21 16:08:09 D:/lq/Smart Parking/lc_garage/internal/service/vehicle/vehicle.go:30 record not found
+[0.000ms] [rows:0] SELECT * FROM `vehicle` WHERE (plate_number = "JingDel001" OR rfid_tag = "RFID_DELETE") AND `vehicle`.`deleted_at` IS NULL ORDER BY `vehicle`.`id` LIMIT 1
+--- FAIL: TestShortlistDelete (31.33s)
+panic: interface conversion: interface {} is nil, not float64 [recovered, repanicked]
+
+goroutine 50 [running]:
+testing.tRunner.func1.2({0x7ff6a4518000, 0xc001c3cd20})
+	C:/Users/longc/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.windows-amd64/src/testing/testing.go:1872 +0x239
+testing.tRunner.func1()
+	C:/Users/longc/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.windows-amd64/src/testing/testing.go:1875 +0x35b
+panic({0x7ff6a4518000?, 0xc001c3cd20?})
+	C:/Users/longc/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.windows-amd64/src/runtime/panic.go:783 +0x132
+wails-app/test/testutil.TestShortlistDelete(0xc000106fc0)
+	D:/lq/Smart Parking/lc_garage/test/testutil/e2e_edge_cases_test.go:242 +0x329
+testing.tRunner(0xc000106fc0, 0x7ff6a4d66bf0)
+	C:/Users/longc/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.windows-amd64/src/testing/testing.go:1934 +0xc3
+created by testing.(*T).Run in goroutine 1
+	C:/Users/longc/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.windows-amd64/src/testing/testing.go:1997 +0x44b
+FAIL	wails-app/test/testutil	31.635s
+FAIL

+ 397 - 0
test/testutil/_write_edge.py

@@ -0,0 +1,397 @@
+import os
+
+path = r"test\testutil\e2e_edge_cases_test.go"
+BT = chr(96)
+
+L = []
+def a(s=""):
+    L.append(s)
+
+a("package testutil")
+a()
+a("import (")
+a('\t"encoding/json"')
+a('\t"fmt"')
+a('\t"net/http"')
+a('\t"testing"')
+a('\t"time"')
+a()
+a('\t"wails-app/internal/dao"')
+a('\t"wails-app/internal/global"')
+a('\t"wails-app/internal/model/common/response"')
+a(")")
+a()
+
+a("// TestVehicleReEntry 同一车辆重复入场应被拦截")
+a("func TestVehicleReEntry(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingR00001", "RFID_REENTRY", 1)')
+a('\tentryResp, _ := env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tAssertResponse(t, entryResp, nil, http.StatusOK, response.SUCCESS)')
+a()
+a('\t_, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 2, "entry_image": "test2.jpg",')
+a('\t})')
+a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
+a('\tjson.Unmarshal(entryBody2, &result)')
+a('\tif result.Code == response.SUCCESS {')
+a('\t\tt.Fatalf("BUG: Vehicle re-entry was NOT blocked! code=%d msg=%s", result.Code, result.Msg)')
+a('\t}')
+a('\tfmt.Println("PASS: Re-entry attempt correctly rejected")')
+a("}")
+a()
+
+a("// TestExitWithoutEntry 未入场车辆尝试出场应返回错误")
+a("func TestExitWithoutEntry(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\t_, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
+a('\t\t"plate_number": "JingZ99999", "rfid_tag": "RFID_NOENTRY",')
+a('\t})')
+a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
+a('\tjson.Unmarshal(exitPreviewBody, &result)')
+a('\tif result.Code == response.SUCCESS {')
+a('\t\tt.Fatalf("BUG: Exit preview succeeded for non-entered vehicle!")')
+a('\t}')
+a('\tfmt.Println("PASS: Exit without entry correctly rejected")')
+a("}")
+a()
+
+a("// TestPaymentInsufficientAmount 支付金额不足时的行为")
+a("func TestPaymentInsufficientAmount(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingP00001", "RFID_PAY", 1)')
+a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tentryTime := time.Now().Add(-3 * time.Hour)')
+a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingP00001").Update("entry_time", entryTime)')
+a('\t_, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{')
+a('\t\t"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",')
+a('\t\t"payment_method": "cash", "paid_amount": 0.0,')
+a('\t})')
+a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
+a('\tjson.Unmarshal(exitConfirmBody, &result)')
+a('\tfmt.Printf("INFO: Insufficient payment response: code=%d msg=%s\\n", result.Code, result.Msg)')
+a("}")
+a()
+
+a("// TestVIPExpired 过期 VIP 应恢复普通计费")
+a("func TestVIPExpired(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{')
+a('\t\t"owner_name": "VIP Expired Test", "owner_surname": "VIP",')
+a('\t\t"owner_phone": "13900000001", "is_vip": true,')
+a('\t\t"vip_expire_time": time.Now().Add(-24*time.Hour).Format(time.RFC3339),')
+a('\t})')
+a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)')
+a()
+a('\tvar owner dao.Owner')
+a('\tenv.DB.Where("owner_phone = ?", "13900000001").First(&owner)')
+a('\tcreateVehicleWithOwner(t, env, "JingV00001", "RFID_VIP_EXP", owner.ID)')
+a()
+a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tentryTime := time.Now().Add(-3 * time.Hour)')
+a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingV00001").Update("entry_time", entryTime)')
+a()
+a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
+a('\t\t"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",')
+a('\t})')
+a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
+a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
+a('\tjson.Unmarshal(exitBody, &preview)')
+a('\tif preview.Fee == 0 {')
+a('\t\tt.Fatalf("BUG: Expired VIP still getting free parking! Fee should be > 0")')
+a('\t}')
+a('\tfmt.Printf("PASS: Expired VIP charged correctly, fee=%.2f\\n", preview.Fee)')
+a("}")
+a()
+
+a("// TestFreeDuration 免费时长内不应收费")
+a("func TestFreeDuration(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingF00001", "RFID_FREE", 1)')
+a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tentryTime := time.Now().Add(-10 * time.Minute)')
+a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingF00001").Update("entry_time", entryTime)')
+a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
+a('\t\t"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",')
+a('\t})')
+a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
+a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
+a('\tjson.Unmarshal(exitBody, &preview)')
+a('\tif preview.Fee != 0 {')
+a('\t\tt.Fatalf("BUG: Vehicle within free duration should have zero fee, got %.2f", preview.Fee)')
+a('\t}')
+a('\tfmt.Println("PASS: Free duration correctly applied")')
+a("}")
+a()
+
+a("// TestDailyCap 超过每日封顶费用应封顶")
+a("func TestDailyCap(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingD00001", "RFID_CAP", 1)')
+a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tentryTime := time.Now().Add(-24 * time.Hour)')
+a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingD00001").Update("entry_time", entryTime)')
+a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
+a('\t\t"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",')
+a('\t})')
+a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
+a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
+a('\tjson.Unmarshal(exitBody, &preview)')
+a('\tif preview.Fee > 50.0 {')
+a('\t\tt.Fatalf("BUG: Fee exceeds daily cap! Expected <= 50, got %.2f", preview.Fee)')
+a('\t}')
+a('\tfmt.Printf("PASS: Daily cap applied correctly, fee=%.2f\\n", preview.Fee)')
+a("}")
+a()
+
+a("// TestNoFeeConfig 无收费配置时默认计费")
+a("func TestNoFeeConfig(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tvar normalType dao.VehicleType')
+a('\tenv.DB.Where("is_system = ? AND name != ?", true, "临时车").First(&normalType)')
+a('\tif normalType.ID == 0 {')
+a('\t\tnormalType = dao.VehicleType{Name: "特殊车", Remarks: "无收费配置"}')
+a('\t\tenv.DB.Create(&normalType)')
+a('\t}')
+a('\tenv.DB.Where("vehicle_type_id = ?", normalType.ID).Delete(&dao.FeeConfig{})')
+a('\tcreateVehicleWithOwner(t, env, "JingN00001", "RFID_NOFEE", normalType.ID)')
+a('\t_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tentryTime := time.Now().Add(-1 * time.Hour)')
+a('\tglobal.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingN00001").Update("entry_time", entryTime)')
+a('\texitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{')
+a('\t\t"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",')
+a('\t})')
+a('\tAssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)')
+a('\tvar preview struct { Fee float64 '+BT+'json:"fee"'+BT+' }')
+a('\tjson.Unmarshal(exitBody, &preview)')
+a('\tif preview.Fee <= 0 {')
+a('\t\tt.Fatalf("BUG: No fee config should use default rate, got fee=%.2f", preview.Fee)')
+a('\t}')
+a('\tfmt.Printf("PASS: Default fee applied correctly, fee=%.2f\\n", preview.Fee)')
+a("}")
+a()
+
+a("// TestShortlistByPlateOrRFID 黑白名单按车牌/RFID查询")
+a("func TestShortlistByPlateOrRFID(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingS00001", "RFID_FILTER", 1)')
+a('\tvar vehicle dao.Vehicle')
+a('\tenv.DB.Where("plate_number = ?", "JingS00001").First(&vehicle)')
+a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
+a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
+a('\t})')
+a()
+a('\tresp1, body1 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{')
+a('\t\t"page": 1, "pageSize": 10, "list_type": "", "plate_number": "JingS00001",')
+a('\t})')
+a('\tAssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)')
+a('\tlist1 := ParsePageList(body1)')
+a('\tif len(list1) < 1 { t.Fatalf("Expected at least 1 shortlist record by plate number") }')
+a('\tfmt.Printf("PASS: Shortlist query by plate number returned %d records\\n", len(list1))')
+a()
+a('\tresp2, body2 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{')
+a('\t\t"page": 1, "pageSize": 10, "list_type": "", "rfid_tag": "RFID_FILTER",')
+a('\t})')
+a('\tAssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)')
+a('\tlist2 := ParsePageList(body2)')
+a('\tif len(list2) < 1 { t.Fatalf("Expected at least 1 shortlist record by RFID") }')
+a('\tfmt.Printf("PASS: Shortlist query by RFID returned %d records\\n", len(list2))')
+a("}")
+a()
+
+a("// TestShortlistUpdate 黑白名单更新")
+a("func TestShortlistUpdate(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingU00001", "RFID_UPDATE", 1)')
+a('\tvar vehicle dao.Vehicle')
+a('\tenv.DB.Where("plate_number = ?", "JingU00001").First(&vehicle)')
+a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
+a('\t\tListType: "白名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
+a('\t})')
+a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
+a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)')
+a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }')
+a('\tjson.Unmarshal(queryBody, &result)')
+a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }')
+a('\tid := int(result.Data[0]["ID"].(float64))')
+a('\tupdateResp, updateBody := env.DoPut("/shortlist/updateShortlist", map[string]interface{}{')
+a('\t\t"ID": id, "list_type": "黑名单", "vehicle_id": vehicle.ID,')
+a('\t\t"expiration_time": time.Now().Add(30*24*time.Hour).Format(time.RFC3339),')
+a('\t})')
+a('\tAssertResponse(t, updateResp, updateBody, http.StatusOK, response.SUCCESS)')
+a('\tupdatedResp, updatedBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
+a('\tAssertResponse(t, updatedResp, updatedBody, http.StatusOK, response.SUCCESS)')
+a('\tfmt.Println("PASS: Shortlist update successful")')
+a("}")
+a()
+
+a("// TestShortlistDelete 黑白名单删除")
+a("func TestShortlistDelete(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tcreateVehicle(t, env, "JingDel001", "RFID_DELETE", 1)')
+a('\tvar vehicle dao.Vehicle')
+a('\tenv.DB.Where("plate_number = ?", "JingDel001").First(&vehicle)')
+a('\t_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{')
+a('\t\tListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,')
+a('\t})')
+a('\tqueryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)')
+a('\tAssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)')
+a('\tvar result struct { Data []map[string]interface{} '+BT+'json:"data"'+BT+' }')
+a('\tjson.Unmarshal(queryBody, &result)')
+a('\tif len(result.Data) == 0 { t.Fatalf("No shortlist found") }')
+a('\tid := int(result.Data[0]["ID"].(float64))')
+a('\tdeleteResp, deleteBody := env.DoDelete(fmt.Sprintf("/shortlist/deleteShortlist?id=%d", id))')
+a('\tAssertResponse(t, deleteResp, deleteBody, http.StatusOK, response.SUCCESS)')
+a('\tfmt.Println("PASS: Shortlist delete successful")')
+a("}")
+a()
+
+a("// TestOwnerVehicleRelationship 车主关联车辆查询")
+a("func TestOwnerVehicleRelationship(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\townerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{')
+a('\t\t"owner_name": "Rel Test Owner", "owner_surname": "REL",')
+a('\t\t"owner_phone": "13911111111", "is_vip": false, "vip_expire_time": nil,')
+a('\t})')
+a('\tAssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)')
+a('\tvar owner dao.Owner')
+a('\tenv.DB.Where("owner_phone = ?", "13911111111").First(&owner)')
+a('\tcreateVehicleWithOwner(t, env, "JingO00001", "RFID_OWNER", owner.ID)')
+a('\townerListResp, ownerListBody := env.DoGet("/owner/list", map[string]string{"page": "1", "page_size": "10"})')
+a('\tAssertResponse(t, ownerListResp, ownerListBody, http.StatusOK, response.SUCCESS)')
+a('\tlist := ParsePageList(ownerListBody)')
+a('\tfound := false')
+a('\tfor _, item := range list {')
+a('\t\tm, ok := item.(map[string]interface{})')
+a('\t\tif !ok { continue }')
+a('\t\tif m["owner_phone"] == "13911111111" { found = true; break }')
+a('\t}')
+a('\tif !found { t.Fatalf("Owner not found in list") }')
+a('\tfmt.Println("PASS: Owner-vehicle relationship verified")')
+a("}")
+a()
+
+a("// TestParkingLotCapacity 停车场容量检查")
+a("func TestParkingLotCapacity(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tlotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{')
+a('\t\t"lot_code": "TEST_EMPTY", "lot_name": "Empty Lot",')
+a('\t\t"capacity": 0, "description": "Zero capacity test",')
+a('\t})')
+a('\tAssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)')
+a('\tcreateVehicle(t, env, "JingC00001", "RFID_FULL", 1)')
+a('\t_, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{')
+a('\t\t"plate_number": "JingC00001", "rfid_tag": "RFID_FULL",')
+a('\t\t"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",')
+a('\t})')
+a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
+a('\tjson.Unmarshal(entryBody, &result)')
+a('\tfmt.Printf("INFO: Entry into zero-capacity lot: code=%d msg=%s\\n", result.Code, result.Msg)')
+a("}")
+a()
+
+a("// TestBoothCRUD 岗亭CRUD")
+a("func TestBoothCRUD(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tboothResp, boothBody := env.DoPost("/parking/booth/create", map[string]interface{}{')
+a('\t\t"booth_code": "BT001", "booth_name": "Test Booth",')
+a('\t\t"parking_lot_id": 1, "description": "Automated test booth",')
+a('\t})')
+a('\tAssertResponse(t, boothResp, boothBody, http.StatusOK, response.SUCCESS)')
+a('\tlistResp, listBody := env.DoGet("/parking/booth/list", map[string]string{"page": "1", "page_size": "10"})')
+a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
+a('\tlist := ParsePageList(listBody)')
+a('\tif len(list) < 1 { t.Fatalf("Booth list is empty") }')
+a('\tfmt.Printf("PASS: Booth CRUD successful, total %d booths\\n", len(list))')
+a("}")
+a()
+
+a("// TestChannelCRUD 通道CRUD")
+a("func TestChannelCRUD(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tchannelResp, channelBody := env.DoPost("/parking/channel/create", map[string]interface{}{')
+a('\t\t"channel_code": "CH-IN-TEST", "channel_name": "Test Entrance",')
+a('\t\t"direction": "in", "parking_lot_id": 1, "booth_id": 1,')
+a('\t\t"allow_temporary": true, "description": "Automated test channel",')
+a('\t})')
+a('\tAssertResponse(t, channelResp, channelBody, http.StatusOK, response.SUCCESS)')
+a('\tlistResp, listBody := env.DoGet("/parking/channel/list", map[string]string{"page": "1", "page_size": "10"})')
+a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
+a('\tlist := ParsePageList(listBody)')
+a('\tif len(list) < 1 { t.Fatalf("Channel list is empty") }')
+a('\tfmt.Printf("PASS: Channel CRUD successful, total %d channels\\n", len(list))')
+a("}")
+a()
+
+a("// TestDeviceCRUD 设备CRUD")
+a("func TestDeviceCRUD(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\tdeviceResp, deviceBody := env.DoPost("/parking/device/create", map[string]interface{}{')
+a('\t\t"device_code": "DEV001", "device_name": "Test Device",')
+a('\t\t"device_type": "TCP", "connect_type": "tcp", "ip_address": "127.0.0.1",')
+a('\t\t"port": 9000, "is_active": true, "parking_lot_id": 1, "channel_id": 1,')
+a('\t\t"description": "Automated test device",')
+a('\t})')
+a('\tAssertResponse(t, deviceResp, deviceBody, http.StatusOK, response.SUCCESS)')
+a('\tlistResp, listBody := env.DoGet("/parking/device/list", map[string]string{"page": "1", "page_size": "10"})')
+a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
+a('\tlist := ParsePageList(listBody)')
+a('\tif len(list) < 1 { t.Fatalf("Device list is empty") }')
+a('\tfmt.Printf("PASS: Device CRUD successful, total %d devices\\n", len(list))')
+a("}")
+a()
+
+a("// TestVehicleTypeCRUD 自定义车辆类型CRUD")
+a("func TestVehicleTypeCRUD(t *testing.T) {")
+a('\tenv := InitTestEnv(t)')
+a('\ttypeResp, typeBody := env.DoPost("/vehicle/type/create", map[string]interface{}{')
+a('\t\t"name": "新能源车", "remarks": "测试新能源车辆类型", "is_system": false,')
+a('\t})')
+a('\tAssertResponse(t, typeResp, typeBody, http.StatusOK, response.SUCCESS)')
+a('\tlistResp, listBody := env.DoGet("/vehicle/type/all", nil)')
+a('\tAssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)')
+a('\tlist := ParsePageList(listBody)')
+a('\tif len(list) < 1 { t.Fatalf("Vehicle type list is empty") }')
+a('\tfmt.Printf("PASS: Vehicle type CRUD successful, total %d types\\n", len(list))')
+a("}")
+a()
+
+a("// ===================== Helper functions =====================")
+a()
+a("func createVehicleWithOwner(t *testing.T, env *TestEnv, plateNumber, rfidTag string, ownerID uint) {")
+a('\tt.Helper()')
+a('\t_, body := env.DoPost("/vehicle/create", map[string]interface{}{')
+a('\t\t"plate_number": plateNumber, "vehicle_type_id": 1,')
+a('\t\t"vehicle_brand": "TestBrand", "vehicle_color": "TestColor",')
+a('\t\t"rfid_tag": rfidTag, "owner_id": ownerID,')
+a('\t})')
+a('\tvar result struct { Code int '+BT+'json:"code"'+BT+'; Msg string '+BT+'json:"msg"'+BT+' }')
+a('\tjson.Unmarshal(body, &result)')
+a('\tif result.Code != response.SUCCESS {')
+a('\t\tt.Fatalf("Failed to create vehicle with owner: %s", result.Msg)')
+a('\t}')
+a("}")
+
+content = '\n'.join(L) + '\n'
+with open(path, 'w', encoding='utf-8') as f:
+    f.write(content)
+print(f"Written {len(content)} bytes to {path}")

+ 353 - 0
test/testutil/e2e_test.go

@@ -0,0 +1,353 @@
+package testutil
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"testing"
+	"time"
+
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/utils"
+)
+
+// ===================== 端到端测试用例 =====================
+
+func TestLoginFlow(t *testing.T) {
+	env := InitTestEnv(t)
+
+	if env.Token == "" {
+		t.Fatalf("Token is empty")
+	}
+
+	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)}
+	claims, err := j.ParseToken(env.Token)
+	if err != nil {
+		t.Fatalf("Token parse failed: %v", err)
+	}
+	if claims.Username != "admin" || claims.AuthorityId != 888 {
+		t.Fatalf("Token claims mismatch: username=%s, authorityId=%d", claims.Username, claims.AuthorityId)
+	}
+
+	fmt.Printf("PASS: Login flow verified, Token: %s...\n", env.Token[:min(20, len(env.Token))])
+}
+
+func TestSystemManagement(t *testing.T) {
+	env := InitTestEnv(t)
+
+	resp, body := env.DoPost("/user/getUserList", map[string]interface{}{
+		"page":     1,
+		"pageSize": 10,
+	})
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	var result response.Response
+	json.Unmarshal(body, &result)
+	dataMap, ok := result.Data.(map[string]interface{})
+	if !ok {
+		t.Fatalf("data is not map type: %T", result.Data)
+	}
+
+	listRaw, exists := dataMap["list"]
+	if !exists {
+		t.Fatalf("data.list missing, keys: %v", mapKeys(dataMap))
+	}
+
+	list, ok := listRaw.([]interface{})
+	if !ok {
+		t.Fatalf("data.list is not array: %T", listRaw)
+	}
+
+	if len(list) == 0 {
+		t.Fatalf("user list is empty")
+	}
+	fmt.Printf("PASS: User list query successful, total %d records\n", len(list))
+}
+
+func TestParkingInfrastructure(t *testing.T) {
+	env := InitTestEnv(t)
+
+	lotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{
+		"lot_code":    "TEST002",
+		"lot_name":    "Test Parking Lot B Zone",
+		"capacity":    200,
+		"description": "Automated test parking lot B zone",
+	})
+	AssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)
+
+	resp, body := env.DoGet("/parking/lot/list", map[string]string{
+		"page":      "1",
+		"page_size": "10",
+	})
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	list := ParsePageList(body)
+	if len(list) < 2 {
+		t.Fatalf("Parking lot list should contain at least 2 items, actual %d", len(list))
+	}
+	fmt.Printf("PASS: Parking lot create and query successful, total %d lots\n", len(list))
+}
+
+func TestVehicleCRUD(t *testing.T) {
+	env := InitTestEnv(t)
+
+	createVehicle(t, env, "JingA12345", "RFID001", 1)
+
+	resp, body := env.DoGet("/vehicle/get-by-plate", map[string]string{
+		"plate_number": "JingA12345",
+	})
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	dataMap := ParseDataMap(body)
+	if dataMap["plate_number"] != "JingA12345" {
+		t.Fatalf("Vehicle info mismatch")
+	}
+	fmt.Println("PASS: Vehicle create and query successful")
+}
+
+func TestFeeConfig(t *testing.T) {
+	env := InitTestEnv(t)
+
+	resp, body := env.DoGet("/vehicle/fee-config/list", map[string]string{
+		"page":      "1",
+		"page_size": "10",
+	})
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	list := ParsePageList(body)
+	if len(list) == 0 {
+		t.Fatalf("Fee config list is empty")
+	}
+	fmt.Printf("PASS: Fee config query successful, total %d records\n", len(list))
+}
+
+func TestVehicleEntryAndExit(t *testing.T) {
+	env := InitTestEnv(t)
+
+	createVehicle(t, env, "JingB67890", "RFID002", 1)
+
+	entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
+		"plate_number":     "JingB67890",
+		"rfid_tag":         "RFID002",
+		"parking_lot_id":   1,
+		"parking_space_id": 10,
+		"entry_image":      "test_entry.jpg",
+	})
+	AssertResponse(t, entryResp, entryBody, http.StatusOK, response.SUCCESS)
+	fmt.Println("PASS: Vehicle entry successful")
+
+	// Set entry time to 2 hours ago to skip real waiting
+	entryTime := time.Now().Add(-2 * time.Hour)
+	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingB67890").Update("entry_time", entryTime)
+
+	exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
+		"plate_number": "JingB67890",
+		"rfid_tag":     "RFID002",
+	})
+	AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS)
+
+	exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
+		"plate_number":   "JingB67890",
+		"rfid_tag":       "RFID002",
+		"payment_method": "cash",
+		"paid_amount":    10.0,
+	})
+	AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS)
+	fmt.Println("PASS: Vehicle exit and payment successful")
+
+	paymentResp, paymentBody := env.DoGet("/payment/list", map[string]string{
+		"page":         "1",
+		"page_size":    "10",
+		"plate_number": "JingB67890",
+	})
+	AssertResponse(t, paymentResp, paymentBody, http.StatusOK, response.SUCCESS)
+
+	list := ParsePageList(paymentBody)
+	if len(list) == 0 {
+		t.Fatalf("Payment record query returned empty")
+	}
+	fmt.Printf("PASS: Payment record query successful, total %d records\n", len(list))
+}
+
+func TestBlackWhiteList(t *testing.T) {
+	env := InitTestEnv(t)
+
+	createVehicle(t, env, "JingC11111", "RFID_WHITE", 1)
+
+	var vehicle dao.Vehicle
+	env.DB.Where("plate_number = ?", "JingC11111").First(&vehicle)
+
+	shortlistResp, shortlistBody := env.DoPost("/shortlist/createShortlist", dao.Shortlist{
+		ListType:       "白名单",
+		VehicleId:      int(vehicle.ID),
+		ExpirationTime: nil,
+	})
+	AssertResponse(t, shortlistResp, shortlistBody, http.StatusOK, response.SUCCESS)
+
+	resp, body := env.DoGet("/shortlist/queryAllShortlists", nil)
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	fmt.Println("PASS: Black/white list operation successful")
+}
+
+// TestBlackWhiteListEntryExitBehavior 测试黑白名单对进出场的影响
+// 注意:当前后端 VehicleEntry 尚未集成 CheckVehicleShortlist,
+// 此测试记录当前行为,待业务逻辑接入后断言应反转。
+func TestBlackWhiteListEntryExitBehavior(t *testing.T) {
+	env := InitTestEnv(t)
+
+	// --- 场景1:黑名单车辆尝试进场 ---
+	createVehicle(t, env, "JingB99999", "RFID_BLACK", 1)
+
+	var blackVehicle dao.Vehicle
+	env.DB.Where("plate_number = ?", "JingB99999").First(&blackVehicle)
+
+	_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
+		ListType:       "黑名单",
+		VehicleId:      int(blackVehicle.ID),
+		ExpirationTime: nil,
+	})
+
+	entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
+		"plate_number":     "JingB99999",
+		"rfid_tag":         "RFID_BLACK",
+		"parking_lot_id":   1,
+		"parking_space_id": 99,
+		"entry_image":      "test_black_entry.jpg",
+	})
+
+	// 黑名单车辆应该被拦截,进场请求应返回失败
+	if entryResp.StatusCode == http.StatusOK {
+		var result struct {
+			Code int    `json:"code"`
+			Msg  string `json:"msg"`
+		}
+		json.Unmarshal(entryBody, &result)
+		if result.Code == response.SUCCESS {
+			t.Fatalf("BUG: Blacklisted vehicle entry was NOT blocked! Response: code=%d msg=%s", result.Code, result.Msg)
+		}
+	}
+	AssertResponse(t, entryResp, entryBody, http.StatusOK, response.ERROR)
+
+	// --- 场景2:白名单车辆进场 ---
+	createVehicle(t, env, "JingW00001", "RFID_WHITELIST", 1)
+
+	var whiteVehicle dao.Vehicle
+	env.DB.Where("plate_number = ?", "JingW00001").First(&whiteVehicle)
+
+	_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
+		ListType:       "白名单",
+		VehicleId:      int(whiteVehicle.ID),
+		ExpirationTime: nil,
+	})
+
+	entryResp2, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{
+		"plate_number":     "JingW00001",
+		"rfid_tag":         "RFID_WHITELIST",
+		"parking_lot_id":   1,
+		"parking_space_id": 88,
+		"entry_image":      "test_white_entry.jpg",
+	})
+	AssertResponse(t, entryResp2, entryBody2, http.StatusOK, response.SUCCESS)
+	fmt.Println("PASS: Whitelisted vehicle entry successful")
+
+	// --- 场景3:查询黑白名单列表确认两条记录存在 ---
+	resp3, body3 := env.DoGet("/shortlist/queryAllShortlists", nil)
+	AssertResponse(t, resp3, body3, http.StatusOK, response.SUCCESS)
+
+	// queryAllShortlists 返回 data=[] 数组格式,不是分页结构
+	var result3 response.Response
+	json.Unmarshal(body3, &result3)
+	listRaw, ok := result3.Data.([]interface{})
+	if !ok {
+		t.Fatalf("shortlist data is not array: %T", result3.Data)
+	}
+	if len(listRaw) < 2 {
+		t.Fatalf("Expected at least 2 shortlist records, got %d", len(listRaw))
+	}
+	fmt.Printf("PASS: Shortlist query returned %d records\n", len(listRaw))
+
+	// --- 场景4:白名单车辆出场,验证费用计算(VIP/白名单免单逻辑)---
+	exitPreviewResp, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
+		"plate_number": "JingW00001",
+		"rfid_tag":     "RFID_WHITELIST",
+	})
+	AssertResponse(t, exitPreviewResp, exitPreviewBody, http.StatusOK, response.SUCCESS)
+
+	// 修改入场时间为 1 小时前
+	entryTime := time.Now().Add(-1 * time.Hour)
+	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingW00001").Update("entry_time", entryTime)
+
+	exitConfirmResp, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
+		"plate_number":   "JingW00001",
+		"rfid_tag":       "RFID_WHITELIST",
+		"payment_method": "cash",
+		"paid_amount":    0.0,
+	})
+	AssertResponse(t, exitConfirmResp, exitConfirmBody, http.StatusOK, response.SUCCESS)
+	fmt.Println("PASS: Whitelisted vehicle exit and payment successful")
+}
+
+func TestOwnerManagement(t *testing.T) {
+	env := InitTestEnv(t)
+
+	ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
+		"owner_name":      "Zhang San",
+		"owner_surname":   "Zhang",
+		"owner_phone":     "13800138000",
+		"is_vip":          true,
+		"vip_expire_time": time.Now().Add(365 * 24 * time.Hour).Format(time.RFC3339),
+	})
+	AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)
+
+	resp, body := env.DoGet("/owner/list", map[string]string{
+		"page":      "1",
+		"page_size": "10",
+	})
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	list := ParsePageList(body)
+	if len(list) == 0 {
+		t.Fatalf("Owner list is empty after creation")
+	}
+	fmt.Printf("PASS: Owner management successful, total %d owners\n", len(list))
+}
+
+func TestDashboardMonitoring(t *testing.T) {
+	env := InitTestEnv(t)
+
+	resp, body := env.DoGetRaw("/dashboard/monitor", nil)
+	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
+
+	dataMap := ParseDataMap(body)
+	if dataMap == nil {
+		t.Fatalf("Dashboard data is empty")
+	}
+
+	fmt.Println("PASS: Dashboard monitoring data query successful")
+}
+
+// ===================== Helper functions =====================
+
+func createVehicle(t *testing.T, env *TestEnv, plateNumber, rfidTag string, vehicleTypeID uint) {
+	t.Helper()
+
+	_, body := env.DoPost("/vehicle/create", map[string]interface{}{
+		"plate_number":    plateNumber,
+		"vehicle_type_id": vehicleTypeID,
+		"vehicle_brand":   "TestBrand",
+		"vehicle_color":   "TestColor",
+		"rfid_tag":        rfidTag,
+	})
+
+	var result struct {
+		Code int    `json:"code"`
+		Msg  string `json:"msg"`
+	}
+	json.Unmarshal(body, &result)
+	if result.Code != response.SUCCESS {
+		t.Fatalf("Failed to create vehicle: %s", result.Msg)
+	}
+}

+ 69 - 0
test/testutil/log/2026-07-20/info.log

@@ -0,0 +1,69 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:16:55.866	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:17:15.964	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:17:17.517	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:17:17.517	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:26:42.947	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:01.987	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:03.304	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:03.305	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:12.346	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:28.820	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:29.971	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:29.971	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:37.708	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:55.644	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:56.519	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:27:56.519	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:28:04.727	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:28:23.143	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:28:23.927	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:28:23.927	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 18:28:31.601	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 20:50:07.580	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 20:50:25.420	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 20:50:26.861	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 20:50:26.862	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:18:16.800	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:18:35.556	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:18:40.006	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:18:40.007	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:28:19.818	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:28:36.400	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:28:40.233	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:28:40.233	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:29:13.138	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:29:30.470	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:29:34.544	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:29:34.545	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:32:37.699	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:32:56.289	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:33:00.172	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 21:33:00.173	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:08:33.074	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:08:51.313	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:08:55.564	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:08:55.564	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:12:34.130	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:12:53.662	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:12:58.154	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:12:58.154	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:15:00.199	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:15:17.546	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:15:21.480	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:15:21.481	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:16:17.890	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:16:36.596	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:16:40.597	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:16:40.598	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:19:34.701	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:19:54.241	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:19:57.875	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:19:57.875	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:24:31.910	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:24:49.695	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:24:53.555	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:24:53.556	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:26:34.539	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:26:51.023	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:26:55.008	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/20 - 22:26:55.008	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success

+ 8 - 0
test/testutil/log/2026-07-21/error.log

@@ -0,0 +1,8 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:46:05.975	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:05:59.473	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:23:20.535	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:24:22.429	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:50:59.370	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:20:56.066	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:33:31.174	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:02:31.608	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}

+ 193 - 0
test/testutil/log/2026-07-21/info.log

@@ -0,0 +1,193 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:14:48.510	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:15:25.559	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:15:33.562	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:15:33.562	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:34:45.320	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:40:10.159	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:40:43.831	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:40:49.588	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:40:49.588	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:48:43.236	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:49:36.556	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:49:43.814	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:49:43.815	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:51:11.077	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:51:50.958	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:51:56.282	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 08:51:56.282	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:01:17.264	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:01:36.410	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:01:39.835	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:01:39.835	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:02:11.063	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:02:32.861	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:02:37.386	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:02:37.386	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:04:56.845	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:05:14.868	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:05:18.368	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:05:18.368	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:45:34.785	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:45:53.175	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:45:57.984	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 10:45:57.984	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:05:27.355	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:05:46.686	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:05:52.127	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:05:52.127	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:09:47.926	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:10:04.824	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:10:09.708	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:10:09.708	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:13:14.970	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:13:33.693	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:13:39.535	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:13:39.535	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:22:48.278	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:23:06.717	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:23:11.917	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:23:11.920	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:23:51.354	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:24:10.735	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:24:15.568	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:24:15.569	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:26:11.313	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:26:30.220	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:26:34.786	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:26:34.786	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:26:47.884	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:06.566	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:11.057	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:11.057	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:22.964	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:45.278	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:50.403	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 11:27:50.404	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:50:26.106	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:50:46.812	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:50:51.271	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:50:51.271	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:58:46.776	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:05.674	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:11.641	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:11.641	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:23.855	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:42.903	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:47.802	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 14:59:47.803	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:00:01.684	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:00:20.515	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:00:25.749	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:00:25.751	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:00:40.697	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:01:03.352	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:01:08.421	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:01:08.422	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:02:47.950	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:07.256	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:12.800	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:12.800	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:26.804	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:48.034	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:52.268	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:03:52.268	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:08:59.768	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:09:16.932	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:09:22.583	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:09:22.583	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:09:36.505	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:09:56.461	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:10:01.661	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 15:10:01.662	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:07:46.005	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:04.786	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:09.986	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:08:09.987	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:10:20.096	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:10:39.086	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:10:44.635	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:10:44.636	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:11:00.974	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:11:36.596	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:11:43.079	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:11:43.079	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:12:17.058	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:12:38.381	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:12:43.407	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:12:43.408	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:15:12.815	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:15:30.498	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:15:35.704	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:15:35.704	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:15:48.111	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:16:07.366	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:16:11.625	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:16:11.625	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:32:28.300	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:33:02.105	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:33:08.663	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 16:33:08.664	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:02:07.947	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:02:30.386	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:02:34.878	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:02:34.879	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:02:56.226	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:03:23.539	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:03:28.222	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:03:28.222	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:03:42.044	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:04:06.484	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:04:12.125	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:04:12.125	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:04:30.090	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:04:52.887	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:05:00.771	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:05:00.771	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:14:54.319	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:15:19.651	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:15:27.617	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:15:27.618	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:17:32.979	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:17:52.268	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:17:57.189	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:17:57.189	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:19:33.732	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:19:53.637	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:19:59.164	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:19:59.165	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:20:21.282	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:20:42.391	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:20:47.691	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:20:47.692	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:28:58.345	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:29:19.918	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:29:26.288	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:29:26.289	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:31:02.466	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:31:23.314	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:31:28.788	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:31:28.788	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:32:56.919	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:33:16.958	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:33:22.716	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:33:22.716	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:57:37.494	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:58:05.563	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:58:12.787	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:58:12.787	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:59:15.279	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:59:35.269	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:59:43.585	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 17:59:43.585	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:00:11.981	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:00:34.529	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:00:42.229	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:00:42.229	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:01:06.201	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:01:26.465	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:01:34.931	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:01:34.932	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:01:50.845	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:67	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:02:15.145	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:498	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:02:24.301	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:68	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/21 - 18:02:24.302	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:116	router register success

+ 2 - 0
test/testutil/log/2026-07-22/error.log

@@ -0,0 +1,2 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:14:12.953	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:15:16.194	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}

+ 12 - 0
test/testutil/log/2026-07-22/info.log

@@ -0,0 +1,12 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:12:37.401	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:68	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:12:58.291	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:13:07.206	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:69	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:13:07.206	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:120	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:13:31.677	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:68	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:13:57.751	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:14:06.434	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:69	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:14:06.434	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:120	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:14:36.421	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:68	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:14:57.645	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:15:06.229	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:69	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/22 - 10:15:06.229	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:120	router register success

+ 6 - 0
test/testutil/log/2026-07-23/error.log

@@ -0,0 +1,6 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:36:22.640	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:57:40.275	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:09:05.933	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:22:39.736	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:30:39.049	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 17:27:57.039	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}

+ 60 - 0
test/testutil/log/2026-07-23/info.log

@@ -0,0 +1,60 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:35:41.494	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:36:04.481	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:36:10.914	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:36:10.915	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:50:05.905	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:50:24.979	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:50:33.212	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:50:33.212	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:51:20.263	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:51:40.143	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:51:46.668	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:51:46.668	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:52:39.154	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:53:24.384	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:53:37.183	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:53:37.184	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:55:55.010	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:56:16.002	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:56:25.523	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:56:25.524	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:56:52.103	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:57:15.292	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:57:24.792	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 15:57:24.793	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:07:26.684	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:07:48.276	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:07:57.322	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:07:57.323	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:08:19.433	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:08:44.501	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:08:52.103	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:08:52.104	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:21:59.287	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:22:18.105	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:22:25.680	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:22:25.681	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:29:51.395	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:30:15.841	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:30:25.683	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:30:25.684	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:31:36.133	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:31:58.841	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:32:09.360	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:32:09.361	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:33:29.467	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:34:03.600	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:34:12.262	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:34:12.262	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:34:41.813	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:35:04.680	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:35:13.285	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:35:13.285	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:35:50.138	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:36:15.978	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:36:26.195	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 16:36:26.196	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 17:27:15.159	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:69	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 17:27:35.981	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:510	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 17:27:43.536	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:70	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/23 - 17:27:43.536	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:124	router register success

+ 5 - 0
test/testutil/log/2026-07-29/error.log

@@ -0,0 +1,5 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:06:46.361	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:09:53.410	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:12:35.432	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:28:00.572	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:31:43.248	error	D:/lq/Smart Parking/lc_garage/internal/service/uhf/uhf_service.go:50	启动设备失败	{"code": "DEV001", "error": "dial tcp 127.0.0.1:9000: connectex: No connection could be made because the target machine actively refused it."}

+ 44 - 0
test/testutil/log/2026-07-29/info.log

@@ -0,0 +1,44 @@
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:02:24.748	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:02:56.120	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:03:07.477	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:03:07.477	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:04:16.804	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:04:36.366	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:04:45.444	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:04:45.444	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:05:08.510	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:05:32.110	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:05:39.826	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:05:39.827	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:05:56.375	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:06:20.380	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:06:29.427	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:06:29.427	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:07:46.259	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:08:08.790	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:08:18.589	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:08:18.590	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:09:03.615	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:09:28.437	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:09:37.617	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:09:37.617	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:10:46.567	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:11:12.040	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:11:19.214	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:11:19.215	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:11:48.811	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:12:11.825	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:12:19.474	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:12:19.475	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:27:18.860	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:27:39.007	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:27:47.698	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:27:47.699	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:30:13.050	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:30:32.707	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:30:39.163	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:30:39.164	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:30:56.027	info	D:/lq/Smart Parking/lc_garage/internal/initialize/gorm.go:70	register table success
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:31:20.974	info	D:/lq/Smart Parking/lc_garage/internal/initialize/seed.go:527	system data seeded successfully
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:31:27.667	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:72	register swagger handler
+[github.com/flipped-aurora/gin-vue-admin/server]2026/07/29 - 18:31:27.668	info	D:/lq/Smart Parking/lc_garage/internal/initialize/router.go:132	router register success

BIN
test/testutil/test_1785320831425542100_17616.db


BIN
test/testutil/test_1785321002184716000_25356.db


BIN
test/testutil/test_1785321046720680900_25392.db


+ 467 - 0
test/testutil/testenv.go

@@ -0,0 +1,467 @@
+package testutil
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"sync"
+	"testing"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/glebarez/sqlite"
+	"gorm.io/gorm"
+
+	"wails-app/internal/core"
+	"wails-app/internal/dao"
+	"wails-app/internal/global"
+	"wails-app/internal/initialize"
+	"wails-app/internal/model/common/response"
+	"wails-app/internal/model/system/request"
+	"wails-app/internal/service/system"
+	"wails-app/internal/utils"
+)
+
+// TestEnv 测试环境:DB + Router + HTTP Client + Token
+type TestEnv struct {
+	DB      *gorm.DB
+	Router  *gin.Engine
+	Client  *http.Client
+	Token   string
+	UserID  uint
+	BaseURL string
+}
+
+var (
+	mu      sync.Mutex
+	testEnv *TestEnv
+)
+
+// InitTestEnv 初始化测试环境(所有测试共享一个环境)
+func InitTestEnv(t *testing.T) *TestEnv {
+	t.Helper()
+
+	mu.Lock()
+	defer mu.Unlock()
+
+	if testEnv != nil {
+		return testEnv
+	}
+
+	// 1. 初始化 Viper 配置和 Zap Logger
+	configPath := "D:\\lq\\Smart Parking\\lc_garage\\config.yaml"
+	core.Viper(configPath)
+	global.GVA_LOG = core.Zap()
+
+	// 2. 初始化 BlackCache(JWT 中间件依赖)
+	initialize.OtherInit()
+
+	// 3. 创建独立 SQLite 数据库
+	tmpDB := fmt.Sprintf("test_%d_%d.db", time.Now().UnixNano(), os.Getpid())
+	db, err := gorm.Open(sqlite.Open(tmpDB), &gorm.Config{})
+	if err != nil {
+		t.Fatalf("打开测试数据库失败: %v", err)
+	}
+	global.GVA_DB = db
+
+	// 4. 建表 + 种子数据
+	initialize.RegisterTables()
+	initialize.SeedSystemData()
+	system.LoadAll()
+
+	// 5. 添加 Casbin 权限规则(必须在 RegisterTables + SeedSystemData 之后)
+	addTestCasbinRules(t, db)
+
+	// 6. 创建测试基础设施
+	createTestInfrastructure(t, db)
+
+	// 7. 注册路由
+	gin.SetMode(gin.TestMode)
+	router := initialize.Routers()
+
+	// 8. 生成 admin JWT Token
+	token, userID := generateAdminToken(t, db)
+
+	testEnv = &TestEnv{
+		DB:      db,
+		Router:  router,
+		Client:  &http.Client{Timeout: 15 * time.Second},
+		BaseURL: "http://127.0.0.1:8888",
+		Token:   token,
+		UserID:  userID,
+	}
+
+	return testEnv
+}
+
+// createTestInfrastructure 创建测试基础设施
+func createTestInfrastructure(t *testing.T, db *gorm.DB) {
+	t.Helper()
+
+	var lotCount int64
+	db.Model(&dao.ParkingLot{}).Count(&lotCount)
+	if lotCount > 0 {
+		return
+	}
+
+	lot := dao.ParkingLot{
+		LotCode:     "TEST001",
+		LotName:     "测试停车场-A区",
+		Capacity:    100,
+		Available:   100,
+		Description: "自动化测试停车场",
+	}
+	db.Create(&lot)
+
+	booth := dao.Booth{
+		BoothCode:    "B001",
+		BoothName:    "测试岗亭1号",
+		ParkingLotID: lot.ID,
+	}
+	db.Create(&booth)
+
+	channelIn := dao.Channel{
+		ChannelCode:    "CH-IN-001",
+		ChannelName:    "入口通道1",
+		Direction:      "in",
+		ParkingLotID:   lot.ID,
+		BoothID:        booth.ID,
+		AllowTemporary: true,
+	}
+	db.Create(&channelIn)
+
+	channelOut := dao.Channel{
+		ChannelCode:    "CH-OUT-001",
+		ChannelName:    "出口通道1",
+		Direction:      "out",
+		ParkingLotID:   lot.ID,
+		BoothID:        booth.ID,
+		AllowTemporary: true,
+	}
+	db.Create(&channelOut)
+
+	reader := dao.UHFReader{
+		DeviceCode:   "UHF001",
+		DeviceName:   "测试UHF读卡器",
+		DeviceType:   "TCP",
+		ConnectType:  "tcp",
+		IPAddress:    "127.0.0.1",
+		Port:         8000,
+		IsActive:     true,
+		ParkingLotID: lot.ID,
+		ChannelID:    channelIn.ID,
+	}
+	db.Create(&reader)
+
+	var tempType dao.VehicleType
+	db.Where("is_system = ?", true).First(&tempType)
+	feeConfig := dao.FeeConfig{
+		VehicleTypeID: tempType.ID,
+		StartFee:      0,
+		StartTime:     30,
+		UnitTime:      60,
+		UnitFee:       5,
+		DailyMaxFee:   50,
+		IsVIPFree:     false,
+		VIPDiscount:   0.8,
+	}
+	db.Create(&feeConfig)
+
+	var normalType dao.VehicleType
+	db.Where("is_system = ? AND name != ?", true, "临时车").First(&normalType)
+	if normalType.ID == 0 {
+		normalType = dao.VehicleType{Name: "月租车", Remarks: "测试月租车类型"}
+		db.Create(&normalType)
+	}
+	feeConfig2 := dao.FeeConfig{
+		VehicleTypeID: normalType.ID,
+		StartFee:      0,
+		StartTime:     0,
+		UnitTime:      60,
+		UnitFee:       3,
+		DailyMaxFee:   30,
+		IsVIPFree:     true,
+		VIPDiscount:   0.5,
+	}
+	db.Create(&feeConfig2)
+}
+
+// addTestCasbinRules 为 admin(888) 添加测试所需的 Casbin 路由权限
+// 注意:实际注册的路由不带 /v1 前缀(config.yaml 中 router-prefix 为空)
+func addTestCasbinRules(t *testing.T, db *gorm.DB) {
+	t.Helper()
+
+	rules := []struct {
+		v0, v1, v2 string
+	}{
+		// 系统管理
+		{"888", "/user/getUserList", "POST"},
+		// 停车场管理
+		{"888", "/parking/lot/create", "POST"},
+		{"888", "/parking/lot/list", "GET"},
+		{"888", "/parking/lot/get", "GET"},
+		{"888", "/parking/lot/update", "PUT"},
+		{"888", "/parking/lot/delete", "DELETE"},
+		// 岗亭管理
+		{"888", "/parking/booth/create", "POST"},
+		{"888", "/parking/booth/list", "GET"},
+		// 通道管理
+		{"888", "/parking/channel/create", "POST"},
+		{"888", "/parking/channel/list", "GET"},
+		// 设备管理
+		{"888", "/parking/device/create", "POST"},
+		{"888", "/parking/device/list", "GET"},
+		// 车辆管理
+		{"888", "/vehicle/create", "POST"},
+		{"888", "/vehicle/get-by-plate", "GET"},
+		{"888", "/vehicle/entry", "POST"},
+		{"888", "/vehicle/exit/preview", "POST"},
+		{"888", "/vehicle/exit/confirm", "POST"},
+		// 收费配置
+		{"888", "/vehicle/fee-config/list", "GET"},
+		// 收费记录
+		{"888", "/payment/list", "GET"},
+		// 黑白名单
+		{"888", "/shortlist/createShortlist", "POST"},
+		{"888", "/shortlist/queryAllShortlists", "GET"},
+		// 黑白名单查询/更新/删除
+		{"888", "/shortlist/queryShortlistList", "POST"},
+		{"888", "/shortlist/updateShortlist", "PUT"},
+		{"888", "/shortlist/deleteShortlist", "DELETE"},
+		// 车辆类型管理
+		{"888", "/vehicle/type/create", "POST"},
+		{"888", "/vehicle/type/all", "GET"},
+		// 车主管理
+		{"888", "/owner/create", "POST"},
+		{"888", "/owner/list", "GET"},
+		{"888", "/vehicle/update", "PUT"},
+		{"888", "/vehicle/delete", "DELETE"},
+		{"888", "/vehicle/list", "GET"},
+		{"888", "/vehicle/all", "GET"},
+		{"888", "/vehicle/type/update", "PUT"},
+		{"888", "/vehicle/type/delete", "DELETE"},
+		{"888", "/vehicle/type/list", "POST"},
+		{"888", "/vehicle/fee-config/create", "POST"},
+		{"888", "/vehicle/fee-config/update", "PUT"},
+		{"888", "/vehicle/fee-config/delete", "DELETE"},
+		{"888", "/vehicle/fee-config/get", "GET"},
+		{"888", "/vehicle/record/get", "GET"},
+		{"888", "/vehicle/record/list-by-plate", "GET"},
+		{"888", "/vehicle/record/list", "POST"},
+		{"888", "/owner/update", "PUT"},
+		{"888", "/owner/delete", "DELETE"},
+		{"888", "/owner/get-by-phone", "GET"},
+		{"888", "/owner/get", "GET"},
+		{"888", "/parking/lot/all", "GET"},
+		{"888", "/parking/booth/update", "PUT"},
+		{"888", "/parking/channel/update", "PUT"},
+		{"888", "/parking/device/update", "PUT"},
+		{"888", "/monthly-card/create", "POST"},
+		{"888", "/monthly-card/renew", "POST"},
+		{"888", "/monthly-card/refund", "DELETE"},
+		{"888", "/monthly-card/list", "GET"},
+		// 数字票
+		{"888", "/digital-ticket/list", "GET"},
+		{"888", "/digital-ticket/*", "GET"},
+		{"888", "/digital-ticket/*/pay", "POST"},
+		{"888", "/digital-ticket/*/exit", "POST"},
+
+
+	}
+
+	for _, r := range rules {
+		var count int64
+		db.Raw("SELECT COUNT(*) FROM casbin_rule WHERE ptype=? AND v0=? AND v1=? AND v2=?",
+			"p", r.v0, r.v1, r.v2).Scan(&count)
+		if count == 0 {
+			db.Exec("INSERT INTO casbin_rule (ptype, v0, v1, v2, v3, v4, v5) VALUES (?, ?, ?, ?, '', '', '')",
+				"p", r.v0, r.v1, r.v2)
+		}
+	}
+
+	// 重新加载 Casbin 策略缓存(因为刚插入了新规则)
+	system.CasbinServiceApp.FreshCasbin()
+}
+
+// generateAdminToken 直接生成 JWT Token(绕过验证码)
+func generateAdminToken(t *testing.T, db *gorm.DB) (string, uint) {
+	t.Helper()
+
+	var user dao.SysUser
+	if err := db.Where("username = ?", "admin").First(&user).Error; err != nil {
+		t.Fatalf("查询 admin 用户失败: %v", err)
+	}
+
+	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)}
+	claims := j.CreateClaims(request.BaseClaims{
+		UUID:        user.UUID,
+		ID:          user.ID,
+		NickName:    user.NickName,
+		Username:    user.Username,
+		AuthorityId: user.AuthorityId,
+	})
+	token, err := j.CreateToken(claims)
+	if err != nil {
+		t.Fatalf("生成 Token 失败: %v", err)
+	}
+
+	return token, user.ID
+}
+
+// doRaw 发送原始 HTTP 请求
+func (e *TestEnv) doRaw(method, path string, body interface{}) (*http.Response, []byte) {
+	var reqBody io.Reader = bytes.NewReader([]byte{})
+	if body != nil {
+		data, _ := json.Marshal(body)
+		reqBody = bytes.NewReader(data)
+	}
+
+	req, err := http.NewRequest(method, e.BaseURL+path, reqBody)
+	if err != nil {
+		panic(fmt.Sprintf("创建请求失败: %v", err))
+	}
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("x-token", e.Token)
+
+	w := httptest.NewRecorder()
+	e.Router.ServeHTTP(w, req)
+
+	resp := &http.Response{
+		StatusCode: w.Code,
+		Header:     w.Header(),
+		Body:       io.NopCloser(bytes.NewReader(w.Body.Bytes())),
+	}
+	return resp, w.Body.Bytes()
+}
+
+// DoGet GET 请求
+func (e *TestEnv) DoGet(path string, params map[string]string) (*http.Response, []byte) {
+	url := path
+	if len(params) > 0 {
+		query := ""
+		for k, v := range params {
+			if query != "" {
+				query += "&"
+			}
+			query += fmt.Sprintf("%s=%s", k, v)
+		}
+		url += "?" + query
+	}
+	return e.doRaw("GET", url, nil)
+}
+
+// DoPost POST 请求
+func (e *TestEnv) DoPost(path string, body interface{}) (*http.Response, []byte) {
+	return e.doRaw("POST", path, body)
+}
+
+// DoPut PUT 请求
+func (e *TestEnv) DoPut(path string, body interface{}) (*http.Response, []byte) {
+	return e.doRaw("PUT", path, body)
+}
+
+// DoDelete DELETE 请求
+func (e *TestEnv) DoDelete(path string) (*http.Response, []byte) {
+	return e.doRaw("DELETE", path, nil)
+}
+
+// DoGetRaw GET 请求(不带 Token,用于公共接口)
+func (e *TestEnv) DoGetRaw(path string, params map[string]string) (*http.Response, []byte) {
+	url := e.BaseURL + path
+	if len(params) > 0 {
+		query := ""
+		for k, v := range params {
+			if query != "" {
+				query += "&"
+			}
+			query += fmt.Sprintf("%s=%s", k, v)
+		}
+		url += "?" + query
+	}
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		panic(fmt.Sprintf("创建请求失败: %v", err))
+	}
+	w := httptest.NewRecorder()
+	e.Router.ServeHTTP(w, req)
+	resp := &http.Response{
+		StatusCode: w.Code,
+		Header:     w.Header(),
+		Body:       io.NopCloser(bytes.NewReader(w.Body.Bytes())),
+	}
+	return resp, w.Body.Bytes()
+}
+
+// AssertResponse 断言响应状态码和 code
+func AssertResponse(t *testing.T, resp *http.Response, body []byte, expectedStatus, expectCode int) {
+	t.Helper()
+
+	if resp.StatusCode != expectedStatus {
+		t.Errorf("HTTP 状态码不匹配: 期望 %d, 实际 %d | Body: %s", expectedStatus, resp.StatusCode, truncate(string(body), 300))
+	}
+	var result response.Response
+	if err := json.Unmarshal(body, &result); err != nil {
+		t.Fatalf("JSON 解析失败: %v | Body: %s", err, truncate(string(body), 300))
+	}
+	if result.Code != expectCode {
+		t.Errorf("code 不匹配: 期望 %d, 实际 %d | Msg: %s", expectCode, result.Code, result.Msg)
+	}
+}
+
+// ParseDataMap 解析响应 data 为 map
+func ParseDataMap(body []byte) map[string]interface{} {
+	var result response.Response
+	json.Unmarshal(body, &result)
+	m, _ := result.Data.(map[string]interface{})
+	return m
+}
+
+// ParseDataList 解析响应 data 为列表
+func ParseDataList(body []byte) []interface{} {
+	var result response.Response
+	json.Unmarshal(body, &result)
+	list, _ := result.Data.([]interface{})
+	return list
+}
+
+// ParsePageList 解析分页响应 data.list(如 {"total":N,"list":[...]})
+func ParsePageList(body []byte) []interface{} {
+	var result response.Response
+	json.Unmarshal(body, &result)
+	m, ok := result.Data.(map[string]interface{})
+	if !ok {
+		return nil
+	}
+	rawList, exists := m["list"]
+	if !exists {
+		return nil
+	}
+	list, ok := rawList.([]interface{})
+	if !ok {
+		return nil
+	}
+	return list
+}
+
+func truncate(s string, n int) string {
+	if len(s) <= n {
+		return s
+	}
+	return s[:n] + "..."
+}
+
+// mapKeys returns the keys of a map for diagnostic purposes
+func mapKeys(m map[string]interface{}) []string {
+	keys := make([]string, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+
+			}
+	return keys
+}
+
+// ParsePageList 解析分页响应 data.list

+ 332 - 0
test/testutil/测试失败报告.md

@@ -0,0 +1,332 @@
+# 智慧停车管理系统 - 自动化测试失败报告
+
+> **测试时间**: 2026-07-21 15:10 CST
+> **测试环境**: Windows 11 / Go 后端 + SQLite / Wails v2
+> **工作目录**: `D:\lq\Smart Parking\lc_garage`
+> **测试套件**: `e2e_test.go` (11个) + `e2e_edge_cases_test.go` (16个)
+> **总计**: 27 个用例,**19 PASS / 8 FAIL**
+
+---
+
+## 一、总体统计
+
+| 分类 | 通过 | 失败 | 通过率 |
+|------|------|------|--------|
+| 核心 E2E 测试 (`e2e_test.go`) | 10 | 0 | 100% |
+| 边界场景测试 (`e2e_edge_cases_test.go`) | 9 | 8 | 53% |
+| **合计** | **19** | **8** | **70%** |
+
+---
+
+## 二、失败用例汇总
+
+| # | 测试用例 | Bug 类型 | 严重度 | HTTP 状态码 |
+|---|----------|----------|--------|-------------|
+| 1 | TestVehicleReEntry | 测试框架 / 响应体解析 | 🟡 低 | 200 |
+| 2 | TestBlackWhiteListEntryExitBehavior | 黑名单未拦截进场 | 🔴 严重 | 200 |
+| 3 | TestVIPExpired | VIP 过期仍免费停车 | 🔴 严重 | 200 |
+| 4 | TestNoFeeConfig | 无收费配置默认费率为 0 | 🟡 中等 | 200 |
+| 5 | TestShortlistDelete | 后端 panic (nil pointer) | 🔴 严重 | 500 |
+| 6 | TestVehicleTypeCRUD | 列表查询返回空 | 🟡 中等 | 200 |
+
+---
+
+## 三、详细失败分析
+
+### BUG #1: 黑名单车辆未拦截进场
+
+**测试用例**: `TestBlackWhiteListEntryExitBehavior`
+**严重度**: 🔴 严重(安全漏洞)
+**HTTP 状态码**: 200 OK
+
+**测试步骤**:
+1. 初始化测试环境(独立 SQLite + Casbin 权限注入)
+2. 创建车辆 `JingB99999` / RFID `RFID_BLACK`,关联停车场 ID=1
+3. 创建黑名单记录,将该车加入黑名单
+4. 调用 `POST /vehicle/entry`,传入车牌 `JingB99999`、RFID `RFID_BLACK`、停车场 ID=1、车位 ID=1
+5. 断言:接口应拒绝该车辆进场,返回非成功状态码
+
+**实际结果**:
+- HTTP 状态码: **200 OK**
+- 响应内容: `code=0, msg=查询成功`
+- 车辆 **成功进入停车场**,入场记录已写入数据库
+- 日志显示 SQL: `INSERT INTO vehicle_record ... plate_number="JingB99999"`
+
+**预期结果**:
+- 黑名单车辆尝试进场时,系统应拒绝并返回错误信息
+- 不应创建 `vehicle_record` 入场记录
+
+**根因分析**:
+- `internal/service/vehicle/vehicle.go` → `VehicleEntry()` 方法中 **未调用** `CheckVehicleShortlist()`
+- 进场流程直接创建入场记录,完全绕过了黑白名单检查逻辑
+- `shortlist.go` 中的 `CheckVehicleShortlist(rfid string)` 方法存在但未被集成到进出场流程中
+
+**修复建议**:
+```go
+// internal/service/vehicle/vehicle.go -> VehicleEntry()
+func (vs *VehicleService) VehicleEntry(ctx context.Context, req *dao.VehicleRecord) (*dao.VehicleRecord, error) {
+    // TODO: 在创建入场记录前调用黑名单检查
+    // shortlistResp := shortlist.CheckVehicleShortlist(req.RfidTag)
+    // if shortlistResp.Code == response.FAIL {
+    //     return nil, errors.New(shortlistResp.Msg)
+    // }
+    ...
+}
+```
+
+---
+
+### BUG #2: 过期 VIP 仍免费停车
+
+**测试用例**: `TestVIPExpired`
+**严重度**: 🔴 严重(计费漏洞)
+**HTTP 状态码**: 200 OK
+
+**测试步骤**:
+1. 初始化测试环境
+2. 调用 `POST /owner/create` 创建 VIP 车主:
+   - 姓名: VIP Expired Test
+   - 电话: 13900000001
+   - is_vip: true
+   - vip_expire_time: **当前时间往前推 24 小时**(已过期)
+3. 根据电话查询车主 ID,创建关联车辆 `JingV00001` / RFID `RFID_VIP_EXP`
+4. 调用 `POST /vehicle/entry` 让车辆进场
+5. 通过数据库直接修改入场时间为 **3 小时前**(模拟长时间停放)
+6. 调用 `POST /vehicle/exit/preview` 预览出场费用
+7. 断言:费用应大于 0(过期 VIP 应按普通车辆计费)
+
+**实际结果**:
+- HTTP 状态码: **200 OK**
+- 出场预览费用: **fee=0.00**
+- 测试断言失败: `BUG: Expired VIP still getting free parking! Fee should be > 0`
+
+**预期结果**:
+- 过期 VIP 应恢复为普通车辆计费
+- 停放 3 小时应产生相应停车费用
+
+**根因分析**:
+- 计费服务在判断 VIP 免单时,**未正确校验 VIP 有效期是否已过期**
+- `is_vip` 字段为 true 即触发免单逻辑,忽略了 `vip_expire_time` 的时间判断
+- 需要在 `CalculateFee()` 中增加 VIP 过期检查:`if owner.IsVip && owner.VipExpireTime > now`
+
+**修复建议**:
+```go
+// internal/modules/payment/service/service.go -> CalculateFee()
+func (ps *PaymentService) CalculateFee(vehicleID uint, entryTime time.Time) (float64, error) {
+    // 获取车主信息
+    var owner dao.Owner
+    db.Where("id = ?", vehicle.OwnerId).First(&owner)
+    
+    // TODO: 增加 VIP 过期检查
+    // if owner.IsVip && owner.VipExpireTime != nil && owner.VipExpireTime.After(time.Now()) {
+    //     return 0, nil
+    // }
+    ...
+}
+```
+
+---
+
+### BUG #3: 无收费配置时默认费率为 0
+
+**测试用例**: `TestNoFeeConfig`
+**严重度**: 🟡 中等(计费风险)
+**HTTP 状态码**: 200 OK
+
+**测试步骤**:
+1. 初始化测试环境
+2. 查询非系统内置车辆类型(排除"临时车"),若无则创建 "特殊车"
+3. 删除该车辆类型的所有收费配置(`DELETE FROM fee_config WHERE vehicle_type_id = ?`)
+4. 创建关联车辆 `JingN00001` / RFID `RFID_NOFEE`,使用上述无配置的车型
+5. 调用 `POST /vehicle/entry` 进场
+6. 通过数据库直接修改入场时间为 **1 小时前**
+7. 调用 `POST /vehicle/exit/preview` 预览出场费用
+8. 断言:费用应大于 0(应使用系统默认费率)
+
+**实际结果**:
+- HTTP 状态码: **200 OK**
+- 出场预览费用: **fee=0.00**
+- 测试断言失败: `BUG: No fee config should use default rate, got fee=0.00`
+
+**预期结果**:
+- 当特定车型无收费配置时,系统应回退到默认费率进行计费
+- 停放 1 小时应产生相应停车费用
+
+**根因分析**:
+- `internal/modules/payment/service/service.go` → `CalculateFee()` 方法中,当查不到对应车型的收费配置时,**直接返回 0**
+- 缺少默认费率回退逻辑
+
+**修复建议**:
+```go
+// internal/modules/payment/service/service.go -> CalculateFee()
+func (ps *PaymentService) CalculateFee(vehicleID uint, entryTime time.Time) (float64, error) {
+    feeConfig, err := ps.GetFeeConfigByVehicleType(vehicleTypeID)
+    if err != nil || feeConfig.ID == 0 {
+        // TODO: 应使用默认费率,而非返回 0
+        // defaultConfig := ps.GetDefaultFeeConfig()
+        // feeConfig = defaultConfig
+        return 0, nil  // BUG: 这里应该用默认费率
+    }
+    ...
+}
+```
+
+---
+
+### BUG #4: 重复入场首次 DoPost 响应体为空
+
+**测试用例**: `TestVehicleReEntry`
+**严重度**: 🟡 低(测试框架兼容性问题)
+**HTTP 状态码**: 200 OK
+
+**测试步骤**:
+1. 初始化测试环境
+2. 创建车辆 `JingR00001` / RFID `RFID_REENTRY`
+3. 第一次调用 `POST /vehicle/entry` 进场
+4. 第二次调用 `POST /vehicle/entry` 再次进场(重复入场)
+5. 解析第二次响应 JSON,断言:重复入场应被拦截
+
+**实际结果**:
+- 第一次进场响应体为 **空字符串**
+- JSON 解析失败: `unexpected end of JSON input`
+- 测试在第 23 行断言处 panic: `JSON 解析失败: unexpected end of JSON input | Body: `
+- 日志显示: 车辆已更新 `last_entry_time`,说明第一次进场实际成功
+
+**预期结果**:
+- 第一次进场应返回正常 JSON 响应
+- 第二次重复进场应返回错误信息(如"车辆已在场内")
+
+**根因分析**:
+- `DoPost` 方法在处理首次进场请求时,可能因为后端返回了空 body 或 Content-Type 不匹配导致读取失败
+- 需要检查 `internal/api/v1/vehicle/enter.go` 的响应写入逻辑
+
+---
+
+### BUG #5: 删除黑白名单后端 Panic
+
+**测试用例**: `TestShortlistDelete`
+**严重度**: 🔴 严重(系统稳定性)
+**HTTP 状态码**: 500 Internal Server Error
+
+**测试步骤**:
+1. 初始化测试环境
+2. 创建车辆 `JingDel001` / RFID `RFID_DELETE`
+3. 调用 `POST /shortlist/createShortlist` 创建黑名单记录
+4. 调用 `GET /shortlist/queryAllShortlists` 查询所有名单,获取刚创建的记录 ID
+5. 调用 `DELETE /shortlist/deleteShortlist?id=<ID>` 删除该记录
+6. 断言:删除应返回 200 OK
+
+**实际结果**:
+- HTTP 状态码: **500 Internal Server Error**
+- 响应体: **空**
+- 测试断言失败: `HTTP 状态码不匹配: 期望 200, 实际 500`
+- 堆栈跟踪显示: `runtime error: invalid memory address or nil pointer dereference`
+- Panic 发生在 JWT 认证中间件之后、路由处理之前
+
+**预期结果**:
+- 删除操作应返回 200 OK 及成功消息
+
+**根因分析**:
+- 后端 `deleteShortlist` 接口存在 **空指针引用** 问题
+- 可能是删除参数解析、短名单记录查找或数据库操作返回 nil 后未做空值检查
+- 需要查看 `internal/service/vehicle/shortlist.go` 的删除实现
+
+---
+
+### BUG #6: 自定义车辆类型创建后列表查询为空
+
+**测试用例**: `TestVehicleTypeCRUD`
+**严重度**: 🟡 中等(功能缺陷)
+**HTTP 状态码**: 200 OK
+
+**测试步骤**:
+1. 初始化测试环境
+2. 调用 `POST /vehicle/type/create` 创建自定义车辆类型:
+   - name: 新能源车
+   - remarks: 测试新能源车辆类型
+   - is_system: false
+3. 调用 `GET /vehicle/type/all` 查询所有车辆类型
+4. 断言:列表中应包含刚创建的 "新能源车" 类型
+
+**实际结果**:
+- HTTP 状态码: **200 OK**
+- 响应列表: **空数组**
+- 测试断言失败: `Vehicle type list is empty`
+- 日志显示: 创建接口返回成功,但查询接口返回空列表
+
+**预期结果**:
+- 查询接口应返回包含新创建车辆类型的列表
+
+**根因分析**:
+- 创建接口和查询接口的数据一致性存在问题
+- 可能原因:
+  - 创建成功后未正确提交事务
+  - 查询接口使用了不同的数据源或缓存
+  - `is_system=false` 的记录在查询时被过滤掉了
+  - 分页参数导致第一条记录未被返回
+
+---
+
+## 四、Bug 优先级排序
+
+| 优先级 | Bug | 影响范围 | 建议处理 |
+|--------|-----|----------|----------|
+| P0 | 黑名单未拦截 | 安全漏洞,任何黑名单车辆可自由进出 | 立即修复 |
+| P0 | 删除接口 Panic | 系统稳定性,可能导致服务崩溃 | 立即修复 |
+| P1 | 过期 VIP 免费停车 | 计费漏洞,造成经济损失 | 尽快修复 |
+| P1 | 无配置默认费率为 0 | 计费漏洞,无配置车型免费停车 | 尽快修复 |
+| P2 | 车辆类型列表查询为空 | 功能缺陷,新增类型无法显示 | 安排修复 |
+| P3 | 重复入场响应体为空 | 测试框架兼容性问题 | 低优先级 |
+
+---
+
+## 五、修复验证计划
+
+每个 Bug 修复后,应运行以下命令验证:
+
+```powershell
+cd "D:\lq\Smart Parking\lc_garage"
+go test ./test/testutil/ -v -timeout 180s
+```
+
+重点关注以下测试用例:
+1. `TestBlackWhiteListEntryExitBehavior` — 黑名单拦截
+2. `TestShortlistDelete` — 删除接口稳定性
+3. `TestVIPExpired` — VIP 过期计费
+4. `TestNoFeeConfig` — 默认费率回退
+5. `TestVehicleTypeCRUD` — 车辆类型 CRUD
+6. `TestVehicleReEntry` — 重复入场检测
+
+---
+
+## 六、附录:通过测试清单
+
+以下 19 个测试用例全部通过,核心业务流程正常:
+
+| 序号 | 测试用例 | 说明 |
+|------|----------|------|
+| 1 | TestLoginFlow | 登录流程 |
+| 2 | TestSystemManagement | 系统用户管理 |
+| 3 | TestParkingInfrastructure | 停车场基础设施 |
+| 4 | TestVehicleCRUD | 车辆增删改查 |
+| 5 | TestFeeConfig | 收费配置查询 |
+| 6 | TestVehicleEntryAndExit | 车辆进出场闭环 |
+| 7 | TestBlackWhiteList | 黑白名单基本操作 |
+| 8 | TestBlackWhiteListEntryExitBehavior | 白名单进场/出场(黑名单部分失败) |
+| 9 | TestOwnerManagement | 车主管理 |
+| 10 | TestDashboardMonitoring | 仪表盘数据 |
+| 11 | TestExitWithoutEntry | 未入场车辆出场拦截 ✅ |
+| 12 | TestPaymentInsufficientAmount | 支付金额不足处理 ✅ |
+| 13 | TestFreeDuration | 免费时长内不收费 ✅ |
+| 14 | TestDailyCap | 每日封顶费用 ✅ |
+| 15 | TestShortlistByPlateOrRFID | 按车牌/RFID 查询名单 ✅ |
+| 16 | TestShortlistUpdate | 名单更新 ✅ |
+| 17 | TestOwnerVehicleRelationship | 车主关联车辆 ✅ |
+| 18 | TestParkingLotCapacity | 停车场容量检查 ✅ |
+| 19 | TestBoothCRUD | 岗亭 CRUD ✅ |
+| 20 | TestChannelCRUD | 通道 CRUD ✅ |
+| 21 | TestDeviceCRUD | 设备 CRUD ✅ |
+
+---
+
+*本报告由自动化测试工具生成*

+ 69 - 0
test_server.ps1

@@ -0,0 +1,69 @@
+$root = "D:\lq\Smart Parking\lc_garage"
+$exe = Join-Path $root "backend-only.exe"
+
+# 清理老进程
+Get-Process -Name "backend-only" -ErrorAction SilentlyContinue | Stop-Process -Force
+Get-Process -Name "smart-parking-test" -ErrorAction SilentlyContinue | Stop-Process -Force
+
+# 编译
+Write-Host "=== 编译后端 ==="
+cd $root
+go build -o backend-only.exe .\cmd_backend_only\ 2>&1
+if ($LASTEXITCODE -ne 0) {
+    Write-Host "编译失败!"
+    exit 1
+}
+Write-Host "编译成功"
+
+# 启动服务器(后台作业)
+$job = Start-Job -Name "server" -ScriptBlock {
+    param($exe, $root)
+    Set-Location $root
+    & $exe 2>&1 | Out-File (Join-Path $root "server_job.txt")
+} -ArgumentList $exe, $root
+
+# 等服务器启动
+Write-Host "等待服务启动..."
+$ready = $false
+for ($i = 0; $i -lt 30; $i++) {
+    Start-Sleep -Seconds 1
+    try {
+        $r = Invoke-WebRequest -Uri "http://127.0.0.1:8888/health" -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop
+        if ($r.StatusCode -eq 200) {
+            $ready = $true
+            Write-Host "服务已就绪!"
+            break
+        }
+    } catch {
+        # 还没好
+    }
+}
+
+if (-not $ready) {
+    Write-Host "服务启动超时"
+    Get-Content (Join-Path $root "server_job.txt") -ErrorAction SilentlyContinue
+    Stop-Job $job -ErrorAction SilentlyContinue
+    Remove-Job $job -ErrorAction SilentlyContinue
+    exit 1
+}
+
+# 测试接口
+Write-Host "`n=== 测试健康检查 ==="
+$health = Invoke-WebRequest -Uri "http://127.0.0.1:8888/health" -UseBasicParsing
+Write-Host ("状态码: " + $health.StatusCode)
+Write-Host ("响应: " + $health.Content)
+
+Write-Host "`n=== 测试验证码接口 ==="
+try {
+    $captcha = Invoke-WebRequest -Uri "http://127.0.0.1:8888/base/captcha" -UseBasicParsing -Method POST
+    Write-Host ("状态码: " + $captcha.StatusCode)
+    Write-Host ("响应: " + $captcha.Content.Substring(0, [Math]::Min(200, $captcha.Content.Length)))
+} catch {
+    Write-Host ("验证码接口: " + $_.Exception.Message)
+}
+
+Write-Host "`n=== 服务验证通过 ✅ ==="
+
+# 清理
+Stop-Job $job -ErrorAction SilentlyContinue
+Remove-Job $job -ErrorAction SilentlyContinue