feat(wms/material-coil): 优化钢卷囤积统计查询性能
1. 重构囤积统计方法:将原Java循环计算逻辑替换为单SQL聚合查询,通过JSON_EXTRACT解析二维码步骤创建时间,一次性计算平均囤积周期与成本 2. 移除原低效实现:删除getHoardingStatistics方法中的批量查询与循环解析代码,消除N+1性能问题 3. 新增Mapper方法与XML映射:添加selectHoardingStatistics接口及对应SQL,支持与分页查询相同的条件筛选 调整前,统计需先查询钢卷列表再批量获取二维码并循环解析,存在性能瓶颈;调整后,通过单SQL完成所有聚合计算,大幅提升查询效率,支持大规模数据统计。
This commit is contained in:
@@ -484,6 +484,34 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
return selectMaterialCoilStatistics(qw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已发货钢卷的平均囤积周期和平均囤积成本
|
||||
* 一次SQL完成聚合,高性能
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getHoardingStatistics(WmsMaterialCoilBo bo) {
|
||||
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
|
||||
qw.isNotNull("mc.export_time");
|
||||
|
||||
Map<String, Object> stats = baseMapper.selectHoardingStatistics(qw);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (stats != null) {
|
||||
Object count = stats.get("total_count");
|
||||
Object avgDays = stats.get("avg_hoarding_days");
|
||||
Object avgCost = stats.get("avg_hoarding_cost");
|
||||
result.put("totalCount", count != null ? new BigDecimal(count.toString()) : BigDecimal.ZERO);
|
||||
result.put("avgHoardingDays", avgDays != null ? new BigDecimal(avgDays.toString()).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
result.put("avgHoardingCost", avgCost != null ? new BigDecimal(avgCost.toString()).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
} else {
|
||||
result.put("totalCount", BigDecimal.ZERO);
|
||||
result.put("avgHoardingDays", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
result.put("avgHoardingCost", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Page<WmsMaterialCoilVo> queryMaterialCoilPage(WmsMaterialCoilBo bo, PageQuery pageQuery) {
|
||||
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
|
||||
Page<WmsMaterialCoilVo> result;
|
||||
@@ -5862,123 +5890,6 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
* - totalCount: 符合条件的钢卷总数
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getHoardingStatistics(WmsMaterialCoilBo bo) {
|
||||
// 构建查询条件
|
||||
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
|
||||
|
||||
// 查询符合条件的钢卷列表
|
||||
List<WmsMaterialCoilVo> list = baseMapper.selectVoListWithDynamicJoin(qw);
|
||||
|
||||
// 初始化返回结果,设置默认值
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("avgHoardingDays", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
result.put("avgHoardingCost", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||
result.put("totalCount", 0);
|
||||
|
||||
// 如果没有钢卷数据,直接返回默认值
|
||||
if (list == null || list.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 收集所有钢卷的二维码记录ID,用于批量查询二维码信息
|
||||
Set<Long> qrcodeIds = list.stream()
|
||||
.map(WmsMaterialCoilVo::getQrcodeRecordId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 批量查询二维码记录,建立ID到二维码对象的映射
|
||||
Map<Long, WmsGenerateRecordVo> qrcodeMap = new HashMap<>();
|
||||
if (!qrcodeIds.isEmpty()) {
|
||||
List<WmsGenerateRecordVo> qrcodeList = generateRecordMapper.selectVoBatchIds(new ArrayList<>(qrcodeIds));
|
||||
if (qrcodeList != null) {
|
||||
for (WmsGenerateRecordVo qrcode : qrcodeList) {
|
||||
if (qrcode != null && qrcode.getRecordId() != null) {
|
||||
qrcodeMap.put(qrcode.getRecordId(), qrcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化统计变量
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
BigDecimal totalHoardingDays = BigDecimal.ZERO;
|
||||
BigDecimal totalHoardingCost = BigDecimal.ZERO;
|
||||
int validCount = 0;
|
||||
|
||||
// 遍历每个钢卷,计算囤积天数和成本
|
||||
for (WmsMaterialCoilVo vo : list) {
|
||||
Date exportTime = vo.getExportTime();
|
||||
if (exportTime == null) {
|
||||
exportTime = vo.getUpdateTime(); // 没有发货时间则用更新时间
|
||||
}
|
||||
Long qrcodeRecordId = vo.getQrcodeRecordId();
|
||||
if (qrcodeRecordId == null) {
|
||||
continue; // 没有二维码记录,跳过
|
||||
}
|
||||
|
||||
WmsGenerateRecordVo qrcode = qrcodeMap.get(qrcodeRecordId);
|
||||
if (qrcode == null) {
|
||||
continue; // 二维码记录不存在,跳过
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析二维码内容,获取操作步骤
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> contentMap = objectMapper.readValue(qrcode.getContent(), Map.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
|
||||
if (steps == null || steps.isEmpty()) {
|
||||
continue; // 没有操作步骤,跳过
|
||||
}
|
||||
|
||||
// 获取第一步操作的时间(钢卷创建时间)
|
||||
Object createTimeObj = steps.get(0).get("create_time");
|
||||
if (createTimeObj == null) {
|
||||
continue; // 没有创建时间,跳过
|
||||
}
|
||||
|
||||
Date firstCreateTime = parseDateFromObject(createTimeObj);
|
||||
if (firstCreateTime == null) {
|
||||
continue; // 时间解析失败,跳过
|
||||
}
|
||||
|
||||
// 计算囤积天数:发货时间 - 创建时间
|
||||
long diffMs = exportTime.getTime() - firstCreateTime.getTime();
|
||||
BigDecimal days = BigDecimal.valueOf(diffMs)
|
||||
.divide(BigDecimal.valueOf(1000L * 60 * 60 * 24), 2, RoundingMode.HALF_UP);
|
||||
|
||||
totalHoardingDays = totalHoardingDays.add(days);
|
||||
|
||||
// 计算囤积成本:囤积天数 × 钢卷净重
|
||||
BigDecimal netWeight = vo.getNetWeight() != null ? vo.getNetWeight() : BigDecimal.ZERO;
|
||||
BigDecimal cost = days.multiply(netWeight);
|
||||
totalHoardingCost = totalHoardingCost.add(cost);
|
||||
|
||||
validCount++; // 有效数据计数
|
||||
} catch (Exception e) {
|
||||
log.warn("计算囤积统计时解析二维码失败, coilId={}", vo.getCoilId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算平均值
|
||||
if (validCount > 0) {
|
||||
result.put("avgHoardingDays", totalHoardingDays.divide(BigDecimal.valueOf(validCount), 2, RoundingMode.HALF_UP));
|
||||
result.put("avgHoardingCost", totalHoardingCost.divide(BigDecimal.valueOf(validCount), 2, RoundingMode.HALF_UP));
|
||||
result.put("totalCount", validCount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询分页列表并填充二维码记录
|
||||
* 在普通分页查询的基础上,额外填充每个钢卷的二维码详细信息
|
||||
*
|
||||
* @param bo 查询条件对象
|
||||
* @param pageQuery 分页查询参数
|
||||
* @return 包含二维码信息的分页结果
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<WmsMaterialCoilVo> queryPageListWithQrcode(WmsMaterialCoilBo bo, PageQuery pageQuery) {
|
||||
// 先执行普通分页查询
|
||||
TableDataInfo<WmsMaterialCoilVo> result = queryPageList(bo, pageQuery);
|
||||
|
||||
Reference in New Issue
Block a user