🐛 修 code-review 指出的三处问题:车牌匹配、提交人姓名为空、分页写法
1) **车牌匹配漏了规范化兜底(最严重)** DriverWaybillServiceImpl.assertCertified 把 splitPlates 返回的**规范化**车牌 直接与 plate_no 原始列做精确 IN。库中存在带间隔符/横线/大小写差异的车牌时 会命中 0 行,导致「名下无可用车辆」→ **已认证的司机被误判为未认证而拒绝接单**, 与本次改造的核心验收「已认证的车重新提交后仍可正常接单」相悖。 DriverCertServiceImpl.findByPlateWithinBound 同样问题:先精确 IN 取候选再规范化 filter,而 filter 只能收窄不能放宽,漏掉的候选永远找不回来(等于没做规范化匹配)。 改法:把「精确 IN 或 规范化后相等」抽成 VehiclePlateSupport.applyPlateMatch, 三处调用点(含原先自己实现的 DriverAppServiceImpl.myVehicles)统一使用。 并加测试断言 SQL 表达式覆盖 Java 侧剔除的全部字符、且必须带 OR 兜底, 防止两边日后悄悄分叉。 2) **审核详情的提交人姓名恒为空**:CertAuditDetailVO.submitUserName 声明了却从未赋值, 违反「VO 展示字段须由服务端用 UserCache 填好」的规范。已补。 3) **分页未走项目统一写法**:原用 Page.of 手搓,全模块无先例。改为 Controller 收 Query + Condition.getPage(query),与 AccidentRecordController 等一致。 Query 绑定的正是 current/size,故三端契约不受影响。 顺带删除无任何调用者的 ICertAuditService.getRecordOrNull(推测性抽象 + 纯转发)。 82 个测试通过。
This commit is contained in:
+4
-3
@@ -32,6 +32,8 @@ import io.swagger.v3.oas.annotations.Parameter;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springblade.core.boot.ctrl.BladeController;
|
import org.springblade.core.boot.ctrl.BladeController;
|
||||||
|
import org.springblade.core.mp.support.Condition;
|
||||||
|
import org.springblade.core.mp.support.Query;
|
||||||
import org.springblade.core.secure.annotation.PreAuth;
|
import org.springblade.core.secure.annotation.PreAuth;
|
||||||
import org.springblade.core.tool.api.R;
|
import org.springblade.core.tool.api.R;
|
||||||
import org.springblade.transport.pojo.vo.CertAuditDetailVO;
|
import org.springblade.transport.pojo.vo.CertAuditDetailVO;
|
||||||
@@ -68,9 +70,8 @@ public class CertificationAuditController extends BladeController {
|
|||||||
@RequestParam String bizType,
|
@RequestParam String bizType,
|
||||||
@Parameter(description = "展示键模糊匹配:司机姓名 / 车牌号")
|
@Parameter(description = "展示键模糊匹配:司机姓名 / 车牌号")
|
||||||
@RequestParam(required = false) String bizKey,
|
@RequestParam(required = false) String bizKey,
|
||||||
@Parameter(description = "页码") @RequestParam(required = false) Integer current,
|
Query query) {
|
||||||
@Parameter(description = "每页条数") @RequestParam(required = false) Integer size) {
|
return R.data(certAuditService.pendingPage(bizType, bizKey, Condition.getPage(query)));
|
||||||
return R.data(certAuditService.pendingPage(bizType, bizKey, current, size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
|
|||||||
+4
-12
@@ -44,12 +44,12 @@ public interface ICertAuditService {
|
|||||||
* 待审记录分页(按认证对象类型)
|
* 待审记录分页(按认证对象类型)
|
||||||
*
|
*
|
||||||
* @param bizType 认证对象类型:driver-司机,vehicle-车辆
|
* @param bizType 认证对象类型:driver-司机,vehicle-车辆
|
||||||
* @param bizKey 展示键模糊匹配,可为空
|
* @param bizKey 展示键模糊匹配,可为空
|
||||||
* @param pageNum 页码
|
* @param page 分页参数(由 Controller 用 Condition.getPage 构造)
|
||||||
* @param pageSize 每页条数
|
|
||||||
* @return 待审记录分页
|
* @return 待审记录分页
|
||||||
*/
|
*/
|
||||||
IPage<TransportCertRecordVO> pendingPage(String bizType, String bizKey, Integer pageNum, Integer pageSize);
|
IPage<TransportCertRecordVO> pendingPage(String bizType, String bizKey,
|
||||||
|
IPage<TransportCertRecord> page);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核详情:同时返回待审快照与当前生效值,供前端做 diff
|
* 审核详情:同时返回待审快照与当前生效值,供前端做 diff
|
||||||
@@ -69,12 +69,4 @@ public interface ICertAuditService {
|
|||||||
*/
|
*/
|
||||||
boolean audit(Long recordId, boolean pass, String rejectReason);
|
boolean audit(Long recordId, boolean pass, String rejectReason);
|
||||||
|
|
||||||
/**
|
|
||||||
* 按主键取记录,供写指针时复用
|
|
||||||
*
|
|
||||||
* @param recordId 认证记录主键
|
|
||||||
* @return 认证记录;不存在返回 null
|
|
||||||
*/
|
|
||||||
TransportCertRecord getRecordOrNull(Long recordId);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-10
@@ -28,11 +28,11 @@ package org.springblade.transport.service.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.system.cache.UserCache;
|
||||||
import org.springblade.transport.pojo.entity.Driver;
|
import org.springblade.transport.pojo.entity.Driver;
|
||||||
import org.springblade.transport.pojo.entity.TransportCertRecord;
|
import org.springblade.transport.pojo.entity.TransportCertRecord;
|
||||||
import org.springblade.transport.pojo.entity.TransportVehicle;
|
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||||
@@ -71,7 +71,7 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<TransportCertRecordVO> pendingPage(String bizType, String bizKey,
|
public IPage<TransportCertRecordVO> pendingPage(String bizType, String bizKey,
|
||||||
Integer pageNum, Integer pageSize) {
|
IPage<TransportCertRecord> page) {
|
||||||
if (!CertificationRuleSupport.isValidBizType(bizType)) {
|
if (!CertificationRuleSupport.isValidBizType(bizType)) {
|
||||||
throw new ServiceException("认证对象类型不正确");
|
throw new ServiceException("认证对象类型不正确");
|
||||||
}
|
}
|
||||||
@@ -80,9 +80,6 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
|||||||
.eq(TransportCertRecord::getStatus, TransportCertRecord.STATUS_PENDING)
|
.eq(TransportCertRecord::getStatus, TransportCertRecord.STATUS_PENDING)
|
||||||
.like(Func.isNotEmpty(bizKey), TransportCertRecord::getBizKey, bizKey)
|
.like(Func.isNotEmpty(bizKey), TransportCertRecord::getBizKey, bizKey)
|
||||||
.orderByDesc(TransportCertRecord::getSubmitTime);
|
.orderByDesc(TransportCertRecord::getSubmitTime);
|
||||||
Page<TransportCertRecord> page = Page.of(
|
|
||||||
Func.isEmpty(pageNum) ? 1 : pageNum,
|
|
||||||
Func.isEmpty(pageSize) ? 10 : pageSize);
|
|
||||||
return TransportCertRecordWrapper.build().pageVO(certRecordService.page(page, queryWrapper));
|
return TransportCertRecordWrapper.build().pageVO(certRecordService.page(page, queryWrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +97,8 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
|||||||
detail.setStatusText(CertificationRuleSupport.statusText(record.getStatus()));
|
detail.setStatusText(CertificationRuleSupport.statusText(record.getStatus()));
|
||||||
detail.setSubmitTime(record.getSubmitTime());
|
detail.setSubmitTime(record.getSubmitTime());
|
||||||
detail.setSubmitUser(record.getSubmitUser());
|
detail.setSubmitUser(record.getSubmitUser());
|
||||||
|
// 面向前端的展示字段必须由服务端填好,否则审核页「提交人」一栏恒为空白
|
||||||
|
detail.setSubmitUserName(UserCache.getUserRealName(record.getSubmitUser()));
|
||||||
detail.setPendingSnapshotJson(record.getSnapshotJson());
|
detail.setPendingSnapshotJson(record.getSnapshotJson());
|
||||||
|
|
||||||
// 生效值一侧:从未认证通过时为 null,前端按「无对比基准」处理
|
// 生效值一侧:从未认证通过时为 null,前端按「无对比基准」处理
|
||||||
@@ -148,11 +147,6 @@ public class CertAuditServiceImpl implements ICertAuditService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportCertRecord getRecordOrNull(Long recordId) {
|
|
||||||
return Func.isEmpty(recordId) ? null : certRecordService.getById(recordId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新认证对象的展示缓存,并在通过时把生效快照指针切到这条记录
|
* 更新认证对象的展示缓存,并在通过时把生效快照指针切到这条记录
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
+6
-12
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.transport.service.impl;
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
@@ -73,18 +74,11 @@ public class DriverAppServiceImpl implements IDriverAppService {
|
|||||||
if (plates.isEmpty()) {
|
if (plates.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
// 精确匹配 + 规范化匹配(兼容库中带间隔符/横线的车牌)
|
// 精确 + 规范化双条件匹配,统一走共享实现(避免各调用点各自实现而漏掉写法差异)
|
||||||
List<TransportVehicle> vehicles = transportVehicleService.list(Wrappers.<TransportVehicle>lambdaQuery()
|
LambdaQueryWrapper<TransportVehicle> plateQuery = Wrappers.lambdaQuery();
|
||||||
.and(w -> {
|
VehiclePlateSupport.applyPlateMatch(plateQuery, plates);
|
||||||
w.in(TransportVehicle::getPlateNo, plates);
|
List<TransportVehicle> vehicles = transportVehicleService.list(
|
||||||
for (String plate : plates) {
|
plateQuery.orderByDesc(TransportVehicle::getUpdateTime));
|
||||||
w.or().apply(
|
|
||||||
"REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(UPPER(plate_no),'·',''),'•',''),'.',''),'-',''),' ','') = {0}",
|
|
||||||
plate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.orderByDesc(TransportVehicle::getUpdateTime));
|
|
||||||
// 去重(精确与规范化可能命中同一条)
|
// 去重(精确与规范化可能命中同一条)
|
||||||
Map<Long, TransportVehicle> uniq = new LinkedHashMap<>();
|
Map<Long, TransportVehicle> uniq = new LinkedHashMap<>();
|
||||||
for (TransportVehicle vehicle : vehicles) {
|
for (TransportVehicle vehicle : vehicles) {
|
||||||
|
|||||||
+7
-5
@@ -25,6 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.transport.service.impl;
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -263,11 +264,12 @@ public class DriverCertServiceImpl implements IDriverCertService {
|
|||||||
throw new ServiceException("车牌号不能为空");
|
throw new ServiceException("车牌号不能为空");
|
||||||
}
|
}
|
||||||
String normalized = VehiclePlateSupport.normalize(plateNo);
|
String normalized = VehiclePlateSupport.normalize(plateNo);
|
||||||
List<TransportVehicle> bound = transportVehicleService.list(
|
// 车牌软关联必须同时容忍写法差异:只精确 IN 会漏掉带间隔符/大小写不同的车牌,
|
||||||
Wrappers.<TransportVehicle>lambdaQuery()
|
// 后置 filter 无法把漏掉的候选找回来(它只能收窄,不能放宽)
|
||||||
.in(TransportVehicle::getPlateNo,
|
LambdaQueryWrapper<TransportVehicle> plateQuery = Wrappers.lambdaQuery();
|
||||||
VehiclePlateSupport.splitPlates(driver.getDrivingVehicle())));
|
VehiclePlateSupport.applyPlateMatch(plateQuery,
|
||||||
return bound.stream()
|
VehiclePlateSupport.splitPlates(driver.getDrivingVehicle()));
|
||||||
|
return transportVehicleService.list(plateQuery).stream()
|
||||||
.filter(item -> normalized.equals(VehiclePlateSupport.normalize(item.getPlateNo())))
|
.filter(item -> normalized.equals(VehiclePlateSupport.normalize(item.getPlateNo())))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|||||||
+5
-3
@@ -391,9 +391,11 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
|||||||
if (currentBoundPlates(driver).isEmpty()) {
|
if (currentBoundPlates(driver).isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<TransportVehicle> boundVehicles = transportVehicleService.list(
|
// 用共享的车牌匹配条件:只做精确 IN 会漏掉库中带间隔符/大小写差异的车牌,
|
||||||
Wrappers.<TransportVehicle>lambdaQuery()
|
// 那会把已认证的司机误判为未认证而拒绝接单
|
||||||
.in(TransportVehicle::getPlateNo, currentBoundPlates(driver)));
|
LambdaQueryWrapper<TransportVehicle> plateQuery = Wrappers.lambdaQuery();
|
||||||
|
VehiclePlateSupport.applyPlateMatch(plateQuery, currentBoundPlates(driver));
|
||||||
|
List<TransportVehicle> boundVehicles = transportVehicleService.list(plateQuery);
|
||||||
boolean anyCertified = boundVehicles.stream().anyMatch(vehicle ->
|
boolean anyCertified = boundVehicles.stream().anyMatch(vehicle ->
|
||||||
certificationGate.isVehicleAvailable(
|
certificationGate.isVehicleAvailable(
|
||||||
vehicle.getStatus() != null && vehicle.getStatus() == BladeConstant.DB_STATUS_NORMAL,
|
vehicle.getStatus() != null && vehicle.getStatus() == BladeConstant.DB_STATUS_NORMAL,
|
||||||
|
|||||||
+40
@@ -25,7 +25,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.transport.support;
|
package org.springblade.transport.support;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -110,4 +112,42 @@ public final class VehiclePlateSupport {
|
|||||||
return splitPlates(drivingVehicle).contains(target);
|
return splitPlates(drivingVehicle).contains(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按车牌匹配车辆的 SQL 条件:精确匹配 **或** 规范化后匹配
|
||||||
|
* <p>
|
||||||
|
* ⚠️ 只做精确匹配是不够的。{@link #splitPlates} 返回的已是**规范化**车牌
|
||||||
|
* (去空格/间隔符/横线并转大写),若直接拿它与 {@code plate_no} 原始列做 IN,
|
||||||
|
* 库中任何带间隔符或大小写差异的车牌都会**匹配不到**。
|
||||||
|
* 后果不是「少查一条」,而是调用方据此判定「该司机名下没有可用车辆」——
|
||||||
|
* 认证门槛会因此把**已认证的司机误判成未认证并拒绝接单**。
|
||||||
|
* <p>
|
||||||
|
* 库中已知存在这类写法差异({@code DriverAppServiceImpl.myVehicles()} 早就为此加了
|
||||||
|
* 同样的兜底),故此处把规则收敛成一处,避免各调用点各自实现、各自漏掉。
|
||||||
|
*
|
||||||
|
* @param wrapper 查询条件
|
||||||
|
* @param normalizedPlates 已规范化的车牌列表
|
||||||
|
*/
|
||||||
|
public static void applyPlateMatch(LambdaQueryWrapper<TransportVehicle> wrapper,
|
||||||
|
List<String> normalizedPlates) {
|
||||||
|
if (normalizedPlates == null || normalizedPlates.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wrapper.and(w -> {
|
||||||
|
w.in(TransportVehicle::getPlateNo, normalizedPlates);
|
||||||
|
for (String plate : normalizedPlates) {
|
||||||
|
w.or().apply(NORMALIZED_PLATE_SQL + " = {0}", plate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与 {@link #normalize} 等价的 SQL 表达式(列名固定为 {@code plate_no})
|
||||||
|
* <p>
|
||||||
|
* 两者必须保持一致:Java 侧剔除的字符集变了,这里也要跟着变。
|
||||||
|
* {@code VehiclePlateSupportTest} 会断言两边覆盖同一批字符,防止悄悄分叉。
|
||||||
|
*/
|
||||||
|
public static final String NORMALIZED_PLATE_SQL =
|
||||||
|
"REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(UPPER(plate_no)"
|
||||||
|
+ ",'·',''),'•',''),'.',''),'.',''),'-',''),' ','')";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+51
@@ -1,5 +1,12 @@
|
|||||||
package org.springblade.transport.support;
|
package org.springblade.transport.support;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||||
|
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||||
|
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -20,6 +27,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
*/
|
*/
|
||||||
class VehiclePlateSupportTest {
|
class VehiclePlateSupportTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void initTableInfo() {
|
||||||
|
// 构造 LambdaQueryWrapper 需要 MyBatis-Plus 的实体元数据缓存
|
||||||
|
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||||
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, "");
|
||||||
|
assistant.setCurrentNamespace("test");
|
||||||
|
TableInfoHelper.initTableInfo(assistant, TransportVehicle.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("单个车牌:原样返回规范化结果")
|
@DisplayName("单个车牌:原样返回规范化结果")
|
||||||
void singlePlate() {
|
void singlePlate() {
|
||||||
@@ -88,4 +104,39 @@ class VehiclePlateSupportTest {
|
|||||||
assertFalse(VehiclePlateSupport.isBound("", ""));
|
assertFalse(VehiclePlateSupport.isBound("", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SQL 规范化表达式必须覆盖 Java 侧剔除的全部字符(否则两边会悄悄分叉)")
|
||||||
|
void sqlNormalizationMirrorsJava() {
|
||||||
|
String sql = VehiclePlateSupport.NORMALIZED_PLATE_SQL;
|
||||||
|
// Java 的 normalize 会剔除这些排版字符并转大写,SQL 必须一一对应
|
||||||
|
for (String noise : List.of("·", "•", ".", ".", "-", " ")) {
|
||||||
|
assertTrue(sql.contains("'" + noise + "',''"),
|
||||||
|
"SQL 规范化漏掉了字符 [" + noise + "],会导致该写法差异的车牌匹配不到");
|
||||||
|
}
|
||||||
|
assertTrue(sql.contains("UPPER(plate_no)"), "SQL 必须先转大写再比对");
|
||||||
|
assertTrue(sql.contains("plate_no"), "表达式作用的列应为 plate_no");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("车牌匹配条件是「精确 IN 或 规范化相等」,不能只有精确 IN")
|
||||||
|
void plateMatchIncludesNormalizedFallback() {
|
||||||
|
LambdaQueryWrapper<TransportVehicle> wrapper = Wrappers.lambdaQuery();
|
||||||
|
VehiclePlateSupport.applyPlateMatch(wrapper, List.of("桂A32523"));
|
||||||
|
|
||||||
|
String sql = wrapper.getTargetSql();
|
||||||
|
assertTrue(sql.contains("plate_no IN"), "必须保留精确匹配,避免全表函数扫描");
|
||||||
|
assertTrue(sql.contains("REPLACE"), "必须带规范化兜底,否则脏车牌匹配不到");
|
||||||
|
assertTrue(sql.contains("OR"), "两者应是 OR 关系");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("空车牌列表不产生任何条件(避免 IN () 语法错误)")
|
||||||
|
void emptyPlateListAddsNoCondition() {
|
||||||
|
LambdaQueryWrapper<TransportVehicle> wrapper = Wrappers.lambdaQuery();
|
||||||
|
VehiclePlateSupport.applyPlateMatch(wrapper, List.of());
|
||||||
|
assertEquals("", wrapper.getTargetSql().trim());
|
||||||
|
VehiclePlateSupport.applyPlateMatch(wrapper, null);
|
||||||
|
assertEquals("", wrapper.getTargetSql().trim());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user