refactor(wms): 优化钢卷list查询速度应对大量itemId

- 引入 CollectionUtils 工具类用于集合判空
- 优化用户昵称映射逻辑,减少不必要的查询
- 使用 EXISTS 子查询替代原有的 IN 查询方式,提升性能
- 重构 item_id 筛选条件组合逻辑,支持更灵活的查询需求
- 新增 buildOrLikeClause 方法统一处理多值模糊匹配条件
- 移除冗余的 queryMatchedItemIds 方法,简化代码结构
- 增强 SQL 注入防护,使用参数化查询替代字符串拼接
This commit is contained in:
2025-12-18 15:33:00 +08:00
parent 09d0dc0991
commit 4c1dbfdd04

View File

@@ -21,6 +21,7 @@ import com.klp.domain.vo.*;
import com.klp.mapper.WmsDeliveryPlanMapper;
import com.klp.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.klp.domain.WmsMaterialCoil;
@@ -297,28 +298,31 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
Page<WmsMaterialCoilVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
List<WmsMaterialCoilVo> records = result.getRecords();
if (records == null || records.isEmpty()) {
return TableDataInfo.build(result);
}
Set<String> userNames = records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getUpdateBy()))
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
Map<String, String> nickMap = Collections.emptyMap();
if (!userNames.isEmpty()) {
Map<String, String> nickMap = userService.selectNickNameMapByUserNames(records.stream()
.flatMap(v -> java.util.stream.Stream.of(v.getCreateBy(), v.getUpdateBy()))
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList()));
records.forEach(item -> {
if (StringUtils.isNotBlank(item.getCreateBy())) {
item.setCreateByName(nickMap.getOrDefault(item.getCreateBy(), item.getCreateBy()));
}
if (StringUtils.isNotBlank(item.getUpdateBy())) {
item.setUpdateByName(nickMap.getOrDefault(item.getUpdateBy(), item.getUpdateBy()));
}
});
nickMap = userService.selectNickNameMapByUserNames(new ArrayList<>(userNames));
}
// 从联查结果中构建产品和原材料对象(避免单独查询)
for (WmsMaterialCoilVo vo : result.getRecords()) {
// 单次遍历:填充创建/更新人昵称,并构建物料/产品对象
for (WmsMaterialCoilVo vo : records) {
if (!nickMap.isEmpty()) {
if (StringUtils.isNotBlank(vo.getCreateBy())) {
vo.setCreateByName(nickMap.getOrDefault(vo.getCreateBy(), vo.getCreateBy()));
}
if (StringUtils.isNotBlank(vo.getUpdateBy())) {
vo.setUpdateByName(nickMap.getOrDefault(vo.getUpdateBy(), vo.getUpdateBy()));
}
}
// 从联查结果中构建产品和原材料对象(避免单独查询)
buildItemObjectFromJoin(vo);
}
@@ -348,28 +352,14 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
}
}
// 组合 item_id 条件:selectType 细粒度筛选 与 显式 itemIds/itemId 的并行支持
List<Long> matchedItemIds = null; // 来自 selectType + 多字段筛选
if (StringUtils.isNotBlank(bo.getSelectType())) {
boolean hasAnyItemFilter = StringUtils.isNotBlank(bo.getItemMaterial())
|| StringUtils.isNotBlank(bo.getItemManufacturer())
|| StringUtils.isNotBlank(bo.getItemSurfaceTreatmentDesc())
|| StringUtils.isNotBlank(bo.getItemZincLayer())
|| StringUtils.isNotBlank(bo.getItemName())
|| StringUtils.isNotBlank(bo.getItemSpecification());
if (hasAnyItemFilter) {
try {
matchedItemIds = queryMatchedItemIds(bo.getSelectType(), bo);
} catch (Exception e) {
Log.error("筛选产品/原材料ID失败", e);
matchedItemIds = Collections.emptyList();
}
if (matchedItemIds != null) {
matchedItemIds = matchedItemIds.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
}
}
}
// 组合 item_id 条件:改为使用 EXISTS 子查询,替代预查询 + IN
boolean hasSelectType = StringUtils.isNotBlank(bo.getSelectType());
boolean hasAnyItemFilter = StringUtils.isNotBlank(bo.getItemMaterial())
|| StringUtils.isNotBlank(bo.getItemManufacturer())
|| StringUtils.isNotBlank(bo.getItemSurfaceTreatmentDesc())
|| StringUtils.isNotBlank(bo.getItemZincLayer())
|| StringUtils.isNotBlank(bo.getItemName())
|| StringUtils.isNotBlank(bo.getItemSpecification());
// 解析显式 itemIds 或单个 itemId
List<Long> explicitItemIds = null;
@@ -390,25 +380,55 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
explicitItemIds = Collections.singletonList(bo.getItemId());
}
// 合并最终 item_id 条件:若两者都有则取交集;仅一者存在则使用那一者
List<Long> finalItemIds = null;
if (matchedItemIds != null && explicitItemIds != null) {
// 交集
Set<Long> set = new HashSet<>(explicitItemIds);
finalItemIds = matchedItemIds.stream().filter(set::contains).collect(Collectors.toList());
} else if (matchedItemIds != null) {
finalItemIds = matchedItemIds;
} else if (explicitItemIds != null) {
finalItemIds = explicitItemIds;
// 使用 EXISTS 针对 selectType 的细粒度筛选(使用参数占位符防注入)
if (hasSelectType && hasAnyItemFilter) {
StringBuilder existsSql = new StringBuilder();
List<Object> existsArgs = new ArrayList<>();
if ("product".equals(bo.getSelectType())) {
existsSql.append(" EXISTS (SELECT 1 FROM wms_product p WHERE p.del_flag = 0 AND p.product_id = mc.item_id");
String clause;
clause = buildOrLikeClause("p.product_name", bo.getItemName(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("p.material", bo.getItemMaterial(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("p.manufacturer", bo.getItemManufacturer(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("p.surface_treatment_desc", bo.getItemSurfaceTreatmentDesc(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("p.zinc_layer", bo.getItemZincLayer(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("p.specification", bo.getItemSpecification(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
existsSql.append(")");
} else if ("raw_material".equals(bo.getSelectType())) {
existsSql.append(" EXISTS (SELECT 1 FROM wms_raw_material r WHERE r.del_flag = 0 AND r.raw_material_id = mc.item_id");
String clause;
clause = buildOrLikeClause("r.raw_material_name", bo.getItemName(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("r.material", bo.getItemMaterial(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("r.manufacturer", bo.getItemManufacturer(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("r.surface_treatment_desc", bo.getItemSurfaceTreatmentDesc(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("r.zinc_layer", bo.getItemZincLayer(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
clause = buildOrLikeClause("r.specification", bo.getItemSpecification(), existsArgs);
if (!clause.isEmpty()) existsSql.append(" AND ").append(clause);
existsSql.append(")");
}
if (existsSql.length() > 0) {
qw.apply(existsSql.toString(), existsArgs.toArray());
}
}
if (finalItemIds != null) {
if (finalItemIds.isEmpty()) {
qw.apply("1 = 0");
return qw;
}
// 显式 itemId 条件:与 EXISTS 共存时,语义为交集
if (CollectionUtils.isNotEmpty(explicitItemIds)) {
qw.isNotNull("mc.item_id");
qw.in("mc.item_id", finalItemIds);
qw.in("mc.item_id", explicitItemIds);
} else if (explicitItemIds != null && explicitItemIds.isEmpty()) {
qw.apply("1 = 0");
return qw;
}
// 添加coilIds查询条件支持逗号分隔的多个coilId查询
if (StringUtils.isNotBlank(bo.getCoilIds())) {
@@ -454,236 +474,29 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
return qw;
}
public List<Long> queryMatchedItemIds(String selectType, WmsMaterialCoilBo bo) {
List<Long> result = new ArrayList<>();
if ("product".equals(selectType)) {
QueryWrapper<WmsProduct> pq = new QueryWrapper<>();
pq.eq("del_flag", 0);
if (StringUtils.isNotBlank(bo.getItemName())) {
String[] vals = bo.getItemName().split(",");
if (vals.length == 1) {
pq.like("product_name", vals[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("product_name", v);
} else {
wrapper.or().like("product_name", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemMaterial())) {
String[] vals = bo.getItemMaterial().split(",");
if (vals.length == 1) {
pq.like("material", vals[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("material", v);
} else {
wrapper.or().like("material", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemManufacturer())) {
String[] vals = bo.getItemManufacturer().split(",");
if (vals.length == 1) {
pq.like("manufacturer", vals[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("manufacturer", v);
} else {
wrapper.or().like("manufacturer", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemSurfaceTreatmentDesc())) {
String[] vals = bo.getItemSurfaceTreatmentDesc().split(",");
if (vals.length == 1) {
pq.like("surface_treatment_desc", vals[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("surface_treatment_desc", v);
} else {
wrapper.or().like("surface_treatment_desc", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemZincLayer())) {
String[] vals = bo.getItemZincLayer().split(",");
if (vals.length == 1) {
pq.like("zinc_layer", vals[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("zinc_layer", v);
} else {
wrapper.or().like("zinc_layer", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemSpecification())) {
String[] specs = bo.getItemSpecification().split(",");
if (specs.length == 1) {
pq.like("specification", specs[0].trim());
} else {
pq.and(wrapper -> {
for (int i = 0; i < specs.length; i++) {
if (i == 0) {
wrapper.like("specification", specs[i].trim());
} else {
wrapper.or().like("specification", specs[i].trim());
}
}
});
}
}
result = productMapper.selectList(pq).stream()
.map(WmsProduct::getProductId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
} else if ("raw_material".equals(selectType)) {
QueryWrapper<WmsRawMaterial> rq = new QueryWrapper<>();
rq.eq("del_flag", 0);
if (StringUtils.isNotBlank(bo.getItemName())) {
String[] vals = bo.getItemName().split(",");
if (vals.length == 1) {
rq.like("raw_material_name", vals[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("raw_material_name", v);
} else {
wrapper.or().like("raw_material_name", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemMaterial())) {
String[] vals = bo.getItemMaterial().split(",");
if (vals.length == 1) {
rq.like("material", vals[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("material", v);
} else {
wrapper.or().like("material", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemManufacturer())) {
String[] vals = bo.getItemManufacturer().split(",");
if (vals.length == 1) {
rq.like("manufacturer", vals[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("manufacturer", v);
} else {
wrapper.or().like("manufacturer", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemSurfaceTreatmentDesc())) {
String[] vals = bo.getItemSurfaceTreatmentDesc().split(",");
if (vals.length == 1) {
rq.like("surface_treatment_desc", vals[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("surface_treatment_desc", v);
} else {
wrapper.or().like("surface_treatment_desc", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemZincLayer())) {
String[] vals = bo.getItemZincLayer().split(",");
if (vals.length == 1) {
rq.like("zinc_layer", vals[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < vals.length; i++) {
String v = vals[i].trim();
if (v.isEmpty()) continue;
if (i == 0) {
wrapper.like("zinc_layer", v);
} else {
wrapper.or().like("zinc_layer", v);
}
}
});
}
}
if (StringUtils.isNotBlank(bo.getItemSpecification())) {
String[] specs = bo.getItemSpecification().split(",");
if (specs.length == 1) {
rq.like("specification", specs[0].trim());
} else {
rq.and(wrapper -> {
for (int i = 0; i < specs.length; i++) {
if (i == 0) {
wrapper.like("specification", specs[i].trim());
} else {
wrapper.or().like("specification", specs[i].trim());
}
}
});
}
}
result = rawMaterialMapper.selectList(rq).stream()
.map(WmsRawMaterial::getRawMaterialId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
/**
* 构建 OR 连接的 LIKE 子句,使用 MyBatis-Plus apply 的 {index} 占位符并将参数加入 args。
* 例如column = "p.product_name", values = "A,B" -> 返回 "(p.product_name LIKE {0} OR p.product_name LIKE {1})"
* 同时往 args 追加 "%A%", "%B%"。
*/
private String buildOrLikeClause(String column, String csvValues, List<Object> args) {
if (StringUtils.isBlank(csvValues)) {
return "";
}
return result;
String[] vals = csvValues.split(",");
List<String> parts = new ArrayList<>();
for (String raw : vals) {
if (raw == null) continue;
String v = raw.trim();
if (v.isEmpty()) continue;
int idx = args.size();
parts.add(column + " LIKE {" + idx + "}");
args.add('%' + v + '%');
}
if (parts.isEmpty()) {
return "";
}
return '(' + String.join(" OR ", parts) + ')';
}
/**