fix: 消除 GROUP BY 非分组列报错并修复连锁标识脏数据
MySQL ONLY_FULL_GROUP_BY 下,MyBatis-Plus 的 list(wrapper) 默认 SELECT
实体全部字段,配合 groupBy 后非分组列既不在 GROUP BY 中也不函数依赖分组键,
导致 SQLSyntaxErrorException。即便放宽模式,这些字段也只是组内随机一行的值。
根因是借用 SQL GROUP BY 实现"按某列去重",共 7 处,统一改为一次查询 +
内存分组,返回值结构与原实现一致:
- MerchantServiceImpl.groupByChain(本次线上报错点)
- YxeProductServiceImpl.listByGroup / listByChain / listByCate(2 个重载)
- YxeTaskProductServiceImpl.productGroupByTask
- YxeShareServiceImpl.listByMerchant(2 个重载)
分组父节点用独立副本(BeanUtils.copyProperties)而非复用子列表实例,
否则 parent.setChildren(children) 会造成对象自引用,JSON 序列化报
"Direct self-reference leading to cycle"。
附带消除 N+1:groupByChain 原为 1+12 次查询、重复拉取 8105 行。
同时 groupByChain 返回稳定 chainId(连锁名,与字典 sys_dict_data.dict_id=175
的 dict_data_code 一致),从源头消除前端回退到 `chain_${index}` 位置型占位符,
避免其经审核端写入 yxe_self_report.chain_id 后按连锁等值筛选永久漏查。
SQL: fix_self_report_chain_id_placeholder_2026-10-11.sql(本地已执行 5 行)
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
-- 用途:修正 yxe_self_report.chain_id 中残留的位置型占位符(chain_0、chain_3、chain_6 …)
|
||||||
|
-- 日期:2026-10-11
|
||||||
|
-- 背景:前端 normalizeMerchantChain 在接口未返回 chainId 时会回退成 `chain_${index}`,
|
||||||
|
-- 该值经审核端 saveMerchantEdit 写入 chain_id 落库。而按连锁筛选走的是等值匹配
|
||||||
|
-- (YxeSelfReportMapper.xml: a.chain_id = #{param.chainId}),占位符导致这些记录
|
||||||
|
-- 在按连锁筛选时被永久漏掉。本系统没有连锁表,chain 名称即事实上的连锁标识
|
||||||
|
-- (连锁字典 sys_dict_data.dict_id=175 的 dict_data_code 同样是连锁名)。
|
||||||
|
-- 影响:5 行(chain_6 -> 老百姓 3 行;chain_3 -> 百姓人家 2 行)
|
||||||
|
-- 性质:幂等,可重复执行;以 chain_name 为准回填 chain_id
|
||||||
|
-- 配套代码:MerchantServiceImpl.groupByChain 返回稳定 chainId;
|
||||||
|
-- AuditSelfReportItem.vue 兜底链改用连锁名并在写入前拦截 chain_ 前缀
|
||||||
|
|
||||||
|
-- 执行前核对(应为 5 行)
|
||||||
|
SELECT id, chain_id, chain_name
|
||||||
|
FROM yxe_self_report
|
||||||
|
WHERE chain_id REGEXP '^chain_[0-9]+$'
|
||||||
|
ORDER BY id;
|
||||||
|
|
||||||
|
-- 修正
|
||||||
|
UPDATE yxe_self_report
|
||||||
|
SET chain_id = chain_name
|
||||||
|
WHERE chain_id REGEXP '^chain_[0-9]+$'
|
||||||
|
AND chain_name IS NOT NULL
|
||||||
|
AND chain_name <> '';
|
||||||
|
|
||||||
|
-- 执行后核对(remaining_placeholder 与 mismatch 均应为 0)
|
||||||
|
SELECT SUM(chain_id REGEXP '^chain_[0-9]+$') AS remaining_placeholder,
|
||||||
|
SUM(chain_id <> chain_name) AS mismatch,
|
||||||
|
COUNT(*) AS total
|
||||||
|
FROM yxe_self_report;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 执行记录
|
||||||
|
-- ============================================================
|
||||||
|
-- 2026-10-11 本地开发库(127.0.0.1:3306/yaoxiaoer,root)
|
||||||
|
-- 执行结果:受影响 5 行
|
||||||
|
-- 执行后核对:remaining_placeholder = 0,mismatch = 0
|
||||||
|
-- 功能验证:按连锁筛选命中数
|
||||||
|
-- chainId=老百姓 279 -> 282(找回 id 119、130、169)
|
||||||
|
-- chainId=百姓人家 174 -> 176(找回 id 138、139)
|
||||||
|
--
|
||||||
|
-- 生产库:尚未执行。需经确认后按同一条语句复核执行。
|
||||||
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -78,22 +79,31 @@ public class MerchantServiceImpl extends ServiceImpl<MerchantMapper, Merchant> i
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Map<String, Object>> groupByChain() {
|
public List<Map<String, Object>> groupByChain() {
|
||||||
List<Merchant> merchantList = list(
|
// 一次性查出全部有效商户,在内存中按 chain 分组。
|
||||||
|
// 这里不能用 SQL 的 GROUP BY chain:list() 会 SELECT 实体全部字段,
|
||||||
|
// 而 id/name/code 等列既不在 GROUP BY 中、也不函数依赖于 chain,
|
||||||
|
// 在 ONLY_FULL_GROUP_BY 下直接报错;即便放宽模式,取到的也只是组内随机一行的值。
|
||||||
|
List<Merchant> allMerchant = list(
|
||||||
new LambdaQueryWrapper<Merchant>()
|
new LambdaQueryWrapper<Merchant>()
|
||||||
.isNotNull(Merchant::getChain)
|
.isNotNull(Merchant::getChain)
|
||||||
.groupBy(Merchant::getChain)
|
|
||||||
);
|
);
|
||||||
List<Map<String, Object>> miniList = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Merchant merchant : merchantList) {
|
Map<String, List<Merchant>> chainGroup = new LinkedHashMap<>();
|
||||||
|
for (Merchant merchant : allMerchant) {
|
||||||
|
chainGroup.computeIfAbsent(merchant.getChain(), k -> new ArrayList<>()).add(merchant);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> miniList = new ArrayList<>();
|
||||||
|
for (Map.Entry<String, List<Merchant>> entry : chainGroup.entrySet()) {
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("chain", merchant.getChain());
|
map.put("chain", entry.getKey());
|
||||||
List<Merchant> childrenMerchant = list(
|
// 同时返回 chainId。本系统没有连锁表,chain 名称就是事实上的连锁标识
|
||||||
new LambdaQueryWrapper<Merchant>()
|
// (连锁字典 sys_dict_data.dict_id=175 的 dict_data_code 同样是连锁名)。
|
||||||
.eq(Merchant::getChain, merchant.getChain())
|
// 显式给出稳定 id,前端就不必回退到 `chain_${index}` 这种位置型占位符;
|
||||||
);
|
// 那种占位符一旦被写进 yxe_self_report.chain_id,按连锁等值筛选就查不到该记录。
|
||||||
|
map.put("chainId", entry.getKey());
|
||||||
List<Map<String, Object>> childrenList = new ArrayList<>();
|
List<Map<String, Object>> childrenList = new ArrayList<>();
|
||||||
for (Merchant merchantChild : childrenMerchant) {
|
for (Merchant merchantChild : entry.getValue()) {
|
||||||
Map<String, Object> mapChild = new HashMap<>();
|
Map<String, Object> mapChild = new HashMap<>();
|
||||||
mapChild.put("id", merchantChild.getId());
|
mapChild.put("id", merchantChild.getId());
|
||||||
mapChild.put("name", merchantChild.getName());
|
mapChild.put("name", merchantChild.getName());
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.gxwebsoft.yxe.entity.YxeProduct;
|
|||||||
import com.gxwebsoft.yxe.param.YxeProductParam;
|
import com.gxwebsoft.yxe.param.YxeProductParam;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -20,7 +21,10 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -109,15 +113,20 @@ public class YxeProductServiceImpl extends ServiceImpl<YxeProductMapper, YxeProd
|
|||||||
@Override
|
@Override
|
||||||
public List<YxeProduct> listByGroup(YxeProductParam param) {
|
public List<YxeProduct> listByGroup(YxeProductParam param) {
|
||||||
LambdaQueryWrapper<YxeProduct> queryWrapper = new LambdaQueryWrapper<YxeProduct>()
|
LambdaQueryWrapper<YxeProduct> queryWrapper = new LambdaQueryWrapper<YxeProduct>()
|
||||||
.groupBy(YxeProduct::getChain);
|
.eq(YxeProduct::getStatus, 0);
|
||||||
queryWrapper.eq(YxeProduct::getStatus, 0);
|
|
||||||
if (param != null && param.getChain() != null) {
|
if (param != null && param.getChain() != null) {
|
||||||
queryWrapper.eq(YxeProduct::getChain, param.getChain());
|
queryWrapper.eq(YxeProduct::getChain, param.getChain());
|
||||||
}
|
}
|
||||||
List<YxeProduct> list = list(queryWrapper);
|
// 按 chain 去重:查出符合条件的产品后在内存里归组。
|
||||||
|
// 不能用 SQL 的 GROUP BY chain —— list() 会 SELECT 实体全部字段,
|
||||||
|
// 这些非分组列在 ONLY_FULL_GROUP_BY 下非法,放宽模式也只是取组内随机一行的值。
|
||||||
|
// 注意:这里不加 chain IS NOT NULL,保持与原 GROUP BY 行为一致(NULL 自成一组)。
|
||||||
|
List<YxeProduct> list = distinctByKey(list(queryWrapper), YxeProduct::getChain);
|
||||||
List<YxeProduct> all = list();
|
List<YxeProduct> all = list();
|
||||||
for (YxeProduct product : list) {
|
for (YxeProduct product : list) {
|
||||||
product.setChildren(all.stream().filter(p -> p.getChain().equals(product.getChain())).collect(Collectors.toList()));
|
product.setChildren(all.stream()
|
||||||
|
.filter(p -> Objects.equals(p.getChain(), product.getChain()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -210,67 +219,77 @@ public class YxeProductServiceImpl extends ServiceImpl<YxeProductMapper, YxeProd
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<YxeProduct> listByChain() {
|
public List<YxeProduct> listByChain() {
|
||||||
List<YxeProduct> list = list(
|
// 一次查出全部有效产品,在内存中按 chain 归组,避免 SQL GROUP BY 选非分组列。
|
||||||
|
List<YxeProduct> all = list(
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
new LambdaQueryWrapper<YxeProduct>()
|
||||||
.eq(YxeProduct::getStatus, 0)
|
.eq(YxeProduct::getStatus, 0)
|
||||||
.isNotNull(YxeProduct::getChain)
|
.isNotNull(YxeProduct::getChain)
|
||||||
.groupBy(YxeProduct::getChain)
|
.orderByDesc(YxeProduct::getCreateTime)
|
||||||
);
|
);
|
||||||
for (YxeProduct product : list) {
|
Map<String, List<YxeProduct>> chainGroup = new LinkedHashMap<>();
|
||||||
product.setChildren(list(
|
for (YxeProduct product : all) {
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
chainGroup.computeIfAbsent(product.getChain(), k -> new ArrayList<>()).add(product);
|
||||||
.eq(YxeProduct::getStatus, 0)
|
}
|
||||||
.eq(YxeProduct::getChain, product.getChain())
|
List<YxeProduct> list = new ArrayList<>();
|
||||||
.orderByDesc(YxeProduct::getCreateTime)
|
for (Map.Entry<String, List<YxeProduct>> entry : chainGroup.entrySet()) {
|
||||||
));
|
List<YxeProduct> children = entry.getValue();
|
||||||
|
// 父节点必须是组内首条产品的“独立副本”。原实现中父行来自 GROUP BY 查询、
|
||||||
|
// 子列表来自另一次查询,是两个不同对象;若直接把子列表中的实例当父节点,
|
||||||
|
// parent.setChildren(children) 会让对象引用自身,JSON 序列化时报
|
||||||
|
// "Direct self-reference leading to cycle"。
|
||||||
|
YxeProduct parent = new YxeProduct();
|
||||||
|
BeanUtils.copyProperties(children.get(0), parent);
|
||||||
|
parent.setChildren(children);
|
||||||
|
list.add(parent);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<YxeProduct> listByCate() {
|
public List<YxeProduct> listByCate() {
|
||||||
List<YxeProduct> list = list(
|
return listByCate(null);
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
|
||||||
.eq(YxeProduct::getStatus, 0)
|
|
||||||
.isNotNull(YxeProduct::getCate)
|
|
||||||
.groupBy(YxeProduct::getCate)
|
|
||||||
);
|
|
||||||
for (YxeProduct product : list) {
|
|
||||||
product.setChildren(list(
|
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
|
||||||
.eq(YxeProduct::getStatus, 0)
|
|
||||||
.eq(YxeProduct::getCate, product.getCate())
|
|
||||||
.orderByDesc(YxeProduct::getCreateTime)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<YxeProduct> listByCate(YxeProductParam param) {
|
public List<YxeProduct> listByCate(YxeProductParam param) {
|
||||||
LambdaQueryWrapper<YxeProduct> wrapper =
|
// 一次查出全部有效产品,在内存中按 cate 归组,避免 SQL GROUP BY 选非分组列。
|
||||||
|
LambdaQueryWrapper<YxeProduct> allWrapper =
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
new LambdaQueryWrapper<YxeProduct>()
|
||||||
.eq(YxeProduct::getStatus, 0)
|
.eq(YxeProduct::getStatus, 0)
|
||||||
.isNotNull(YxeProduct::getCate)
|
.isNotNull(YxeProduct::getCate)
|
||||||
.groupBy(YxeProduct::getCate);
|
.orderByDesc(YxeProduct::getCreateTime);
|
||||||
if (param != null) {
|
if (param != null) {
|
||||||
if (param.getChain() != null) wrapper.eq(YxeProduct::getChain, param.getChain());
|
if (param.getChain() != null) allWrapper.eq(YxeProduct::getChain, param.getChain());
|
||||||
if (param.getType() != null) wrapper.eq(YxeProduct::getType, param.getType());
|
if (param.getType() != null) allWrapper.eq(YxeProduct::getType, param.getType());
|
||||||
}
|
}
|
||||||
List<YxeProduct> list = list(wrapper);
|
List<YxeProduct> all = list(allWrapper);
|
||||||
for (YxeProduct product : list) {
|
|
||||||
LambdaQueryWrapper<YxeProduct> childWrapper =
|
Map<String, List<YxeProduct>> cateGroup = new LinkedHashMap<>();
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
for (YxeProduct product : all) {
|
||||||
.eq(YxeProduct::getStatus, 0)
|
cateGroup.computeIfAbsent(product.getCate(), k -> new ArrayList<>()).add(product);
|
||||||
.eq(YxeProduct::getCate, product.getCate())
|
}
|
||||||
.orderByDesc(YxeProduct::getCreateTime);
|
List<YxeProduct> list = new ArrayList<>();
|
||||||
if (param != null) {
|
for (Map.Entry<String, List<YxeProduct>> entry : cateGroup.entrySet()) {
|
||||||
if (param.getChain() != null) childWrapper.eq(YxeProduct::getChain, param.getChain());
|
List<YxeProduct> children = entry.getValue();
|
||||||
if (param.getType() != null) childWrapper.eq(YxeProduct::getType, param.getType());
|
// 同上:父节点用独立副本,避免对象自引用导致 JSON 序列化成环。
|
||||||
}
|
YxeProduct parent = new YxeProduct();
|
||||||
product.setChildren(list(childWrapper));
|
BeanUtils.copyProperties(children.get(0), parent);
|
||||||
|
parent.setChildren(children);
|
||||||
|
list.add(parent);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按指定字段去重,保留每组首次出现的元素(保持原查询顺序)。
|
||||||
|
* 用于替代原来用 SQL GROUP BY 实现的“按某列取唯一值”语义。
|
||||||
|
*/
|
||||||
|
private List<YxeProduct> distinctByKey(List<YxeProduct> source, java.util.function.Function<YxeProduct, String> keyGetter) {
|
||||||
|
Map<String, YxeProduct> seen = new LinkedHashMap<>();
|
||||||
|
for (YxeProduct item : source) {
|
||||||
|
seen.putIfAbsent(keyGetter.apply(item), item);
|
||||||
|
}
|
||||||
|
return new ArrayList<>(seen.values());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,23 +204,32 @@ public class YxeShareServiceImpl extends ServiceImpl<YxeShareMapper, YxeShare> i
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<YxeShare> listByMerchant() {
|
public List<YxeShare> listByMerchant() {
|
||||||
return list(
|
return listByMerchant(null);
|
||||||
new LambdaQueryWrapper<YxeShare>()
|
|
||||||
.eq(YxeShare::getStatus, 1)
|
|
||||||
.groupBy(YxeShare::getMerchantName)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<YxeShare> listByMerchant(YxeShareParam param) {
|
public List<YxeShare> listByMerchant(YxeShareParam param) {
|
||||||
|
// 一次查出全部已通过的分享记录,在内存中按 merchantName 去重。
|
||||||
|
// 不能用 SQL GROUP BY merchant_name:list() 会 SELECT 实体全部字段,
|
||||||
|
// 这些非分组列在 ONLY_FULL_GROUP_BY 下非法,放宽模式也只是取组内随机一行的值。
|
||||||
LambdaQueryWrapper<YxeShare> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<YxeShare> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.eq(YxeShare::getStatus, 1);
|
queryWrapper.eq(YxeShare::getStatus, 1);
|
||||||
if (param.getProvince() != null && param.getCity() != null) {
|
if (param != null && param.getProvince() != null && param.getCity() != null) {
|
||||||
queryWrapper.eq(YxeShare::getProvince, param.getProvince());
|
queryWrapper.eq(YxeShare::getProvince, param.getProvince());
|
||||||
queryWrapper.eq(YxeShare::getCity, param.getCity());
|
queryWrapper.eq(YxeShare::getCity, param.getCity());
|
||||||
}
|
}
|
||||||
queryWrapper.groupBy(YxeShare::getMerchantName);
|
return distinctByMerchantName(list(queryWrapper));
|
||||||
return list(queryWrapper);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 merchantName 去重,保留每个门店首次出现的记录(保持原查询顺序)。
|
||||||
|
*/
|
||||||
|
private List<YxeShare> distinctByMerchantName(List<YxeShare> source) {
|
||||||
|
Map<String, YxeShare> seen = new LinkedHashMap<>();
|
||||||
|
for (YxeShare item : source) {
|
||||||
|
seen.putIfAbsent(item.getMerchantName(), item);
|
||||||
|
}
|
||||||
|
return new ArrayList<>(seen.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.gxwebsoft.yxe.entity.YxeTaskProduct;
|
|||||||
import com.gxwebsoft.yxe.param.YxeTaskProductParam;
|
import com.gxwebsoft.yxe.param.YxeTaskProductParam;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -130,23 +131,32 @@ public class YxeTaskProductServiceImpl extends ServiceImpl<YxeTaskProductMapper,
|
|||||||
}
|
}
|
||||||
List<YxeProduct> productList = new ArrayList<>();
|
List<YxeProduct> productList = new ArrayList<>();
|
||||||
if (!productIds.isEmpty()) {
|
if (!productIds.isEmpty()) {
|
||||||
productList = productService.list(new LambdaQueryWrapper<YxeProduct>()
|
// 取出任务下的全部产品后在内存里按 makerAbb 归组。
|
||||||
.in(YxeProduct::getId, productIds)
|
// 不能用 SQL GROUP BY:list() 会 SELECT 实体全部字段,这些非分组列在
|
||||||
.groupBy(YxeProduct::getMakerAbb)
|
// ONLY_FULL_GROUP_BY 下非法,放宽模式也只是取组内随机一行的值。
|
||||||
|
List<YxeProduct> allProducts = productService.list(
|
||||||
|
new LambdaQueryWrapper<YxeProduct>()
|
||||||
|
.in(YxeProduct::getId, productIds)
|
||||||
);
|
);
|
||||||
for (YxeProduct product : productList) {
|
Map<String, List<YxeProduct>> makerGroup = new LinkedHashMap<>();
|
||||||
List<YxeProduct> children = productService.list(
|
for (YxeProduct product : allProducts) {
|
||||||
new LambdaQueryWrapper<YxeProduct>()
|
makerGroup.computeIfAbsent(product.getMakerAbb(), k -> new ArrayList<>()).add(product);
|
||||||
.in(YxeProduct::getId, productIds)
|
}
|
||||||
.eq(YxeProduct::getMakerAbb, product.getMakerAbb())
|
for (Map.Entry<String, List<YxeProduct>> entry : makerGroup.entrySet()) {
|
||||||
);
|
List<YxeProduct> children = entry.getValue();
|
||||||
for (YxeProduct child : children) {
|
for (YxeProduct child : children) {
|
||||||
YxeTaskProduct taskProduct = list.stream().filter(item -> item.getProductId().equals(child.getId())).findFirst().orElse(null);
|
YxeTaskProduct taskProduct = list.stream().filter(item -> item.getProductId().equals(child.getId())).findFirst().orElse(null);
|
||||||
if (taskProduct != null) {
|
if (taskProduct != null) {
|
||||||
child.setTaskIncome(taskProduct.getAmount());
|
child.setTaskIncome(taskProduct.getAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
product.setChildren(children);
|
// 父节点必须是组内首条产品的“独立副本”:原实现中父行来自 GROUP BY 查询、
|
||||||
|
// 子列表来自另一次查询,是两个不同对象;直接复用子列表实例会让对象自引用,
|
||||||
|
// JSON 序列化时报 "Direct self-reference leading to cycle"。
|
||||||
|
YxeProduct parent = new YxeProduct();
|
||||||
|
BeanUtils.copyProperties(children.get(0), parent);
|
||||||
|
parent.setChildren(children);
|
||||||
|
productList.add(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return productList;
|
return productList;
|
||||||
|
|||||||
Reference in New Issue
Block a user