# 智慧停车管理系统 - 自动化测试失败报告 > **测试时间**: 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=` 删除该记录 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 ✅ | --- *本报告由自动化测试工具生成*