feat(wms): 添加钢卷报表汇总功能

- 新增 WmsMaterialCoilReportSummaryBo 请求参数类
- 新增 WmsMaterialCoilReportSummaryVo 响应结果类
- 在 IWmsMaterialCoilService 中添加 reportSummary 方法定义
- 在 WmsMaterialCoilController 中添加 /reportSummary 接口
- 实现 reportSummary 业务逻辑,支持待操作条件和钢卷条件组合筛选
- 实现钢卷ID合并、异常库汇总、班组汇总等统计功能
- 提供空数据情况下的默认汇总结果处理
This commit is contained in:
2026-03-24 18:01:46 +08:00
parent 45f58a7d3e
commit 6cb78bd3bf
5 changed files with 299 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType; import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil; import com.klp.common.utils.poi.ExcelUtil;
import com.klp.domain.bo.WmsMaterialCoilBo; import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.bo.WmsMaterialCoilReportSummaryBo;
import com.klp.domain.vo.dashboard.CoilTrimStatisticsVo; import com.klp.domain.vo.dashboard.CoilTrimStatisticsVo;
import com.klp.domain.vo.dashboard.CategoryWidthStatisticsVo; import com.klp.domain.vo.dashboard.CategoryWidthStatisticsVo;
import com.klp.service.IWmsMaterialCoilService; import com.klp.service.IWmsMaterialCoilService;
@@ -162,6 +163,15 @@ public class WmsMaterialCoilController extends BaseController {
return iWmsMaterialCoilService.queryPageList(bo, pageQuery); return iWmsMaterialCoilService.queryPageList(bo, pageQuery);
} }
/**
* 报表汇总(待操作筛选 + 钢卷筛选)
* 仅返回统计结果,不返回钢卷明细
*/
@PostMapping("/reportSummary")
public R<WmsMaterialCoilReportSummaryVo> reportSummary(@RequestBody WmsMaterialCoilReportSummaryBo bo) {
return R.ok(iWmsMaterialCoilService.reportSummary(bo));
}
/** /**
* 钢卷发货,将钢卷状态更新为已发货,且更新发货时间 * 钢卷发货,将钢卷状态更新为已发货,且更新发货时间
* *

View File

@@ -0,0 +1,21 @@
package com.klp.domain.bo;
import lombok.Data;
/**
* 报表汇总请求参数
* 同时支持待操作条件 + 钢卷条件组合筛选
*/
@Data
public class WmsMaterialCoilReportSummaryBo {
/**
* 待操作筛选条件(可为空)
*/
private WmsCoilPendingActionBo pendingActionFilter;
/**
* 钢卷筛选条件(可为空,内部会按空条件处理)
*/
private WmsMaterialCoilBo materialCoilFilter;
}

View File

@@ -0,0 +1,36 @@
package com.klp.domain.vo;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 报表汇总结果(不返回钢卷明细)
*/
@Data
public class WmsMaterialCoilReportSummaryVo {
/**
* 简单汇总(总数量、总重、均重)
*/
private Integer totalCount;
private String totalWeight;
private String avgWeight;
/**
* 异常库汇总明细(与前端 calcAbSummary 结构兼容)
*/
private List<Map<String, String>> abnormalSummary;
/**
* 班组汇总team -> count/weight
*/
private Map<String, TeamSummaryItem> teamSummary;
@Data
public static class TeamSummaryItem {
private Integer count;
private String weight;
}
}

View File

@@ -2,6 +2,7 @@ package com.klp.service;
import com.klp.domain.vo.*; import com.klp.domain.vo.*;
import com.klp.domain.bo.WmsMaterialCoilBo; import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.bo.WmsMaterialCoilReportSummaryBo;
import com.klp.common.core.page.TableDataInfo; import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery; import com.klp.common.core.domain.PageQuery;
import com.klp.domain.vo.dashboard.CoilTrimStatisticsVo; import com.klp.domain.vo.dashboard.CoilTrimStatisticsVo;
@@ -247,5 +248,10 @@ public interface IWmsMaterialCoilService {
String manufacturer); String manufacturer);
List<WmsMaterialCoilAllExportVo> queryExportListAll(WmsMaterialCoilBo bo); List<WmsMaterialCoilAllExportVo> queryExportListAll(WmsMaterialCoilBo bo);
/**
* 报表汇总(待操作条件 + 钢卷条件)
*/
WmsMaterialCoilReportSummaryVo reportSummary(WmsMaterialCoilReportSummaryBo bo);
} }

View File

@@ -4636,5 +4636,231 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
return result; return result;
} }
@Override
public WmsMaterialCoilReportSummaryVo reportSummary(WmsMaterialCoilReportSummaryBo req) {
WmsMaterialCoilBo coilFilter = req != null && req.getMaterialCoilFilter() != null
? req.getMaterialCoilFilter() : new WmsMaterialCoilBo();
WmsCoilPendingActionBo pendingFilter = req == null ? null : req.getPendingActionFilter();
if (hasAnyPendingFilter(pendingFilter)) {
List<Long> pendingCoilIds = queryCoilIdsByPendingFilter(pendingFilter);
if (CollectionUtils.isEmpty(pendingCoilIds)) {
return buildEmptySummary();
}
coilFilter.setCoilIds(mergeCoilIds(coilFilter.getCoilIds(), pendingCoilIds));
}
List<WmsMaterialCoilVo> list = queryList(coilFilter);
return buildSummaryFromList(list);
}
private boolean hasAnyPendingFilter(WmsCoilPendingActionBo bo) {
if (bo == null) {
return false;
}
return bo.getCoilId() != null
|| StringUtils.isNotBlank(bo.getCurrentCoilNo())
|| bo.getActionType() != null
|| CollectionUtils.isNotEmpty(bo.getActionTypes())
|| bo.getActionStatus() != null
|| bo.getWarehouseId() != null
|| bo.getPriority() != null
|| StringUtils.isNotBlank(bo.getSourceType())
|| bo.getStartTime() != null
|| bo.getEndTime() != null
|| StringUtils.isNotBlank(bo.getCreateBy())
|| StringUtils.isNotBlank(bo.getUpdateBy())
|| StringUtils.isNotBlank(bo.getProcessedCoilIds())
|| bo.getIncludeDeleted() != null;
}
private List<Long> queryCoilIdsByPendingFilter(WmsCoilPendingActionBo bo) {
LambdaQueryWrapper<WmsCoilPendingAction> qw = Wrappers.lambdaQuery();
qw.select(WmsCoilPendingAction::getCoilId);
qw.eq(bo.getCoilId() != null, WmsCoilPendingAction::getCoilId, bo.getCoilId());
qw.like(StringUtils.isNotBlank(bo.getCurrentCoilNo()), WmsCoilPendingAction::getCurrentCoilNo, bo.getCurrentCoilNo());
if (CollectionUtils.isNotEmpty(bo.getActionTypes())) {
qw.in(WmsCoilPendingAction::getActionType, bo.getActionTypes());
} else {
qw.eq(bo.getActionType() != null, WmsCoilPendingAction::getActionType, bo.getActionType());
}
if (bo.getActionStatus() != null) {
if (bo.getActionStatus() == -1) {
qw.ne(WmsCoilPendingAction::getActionStatus, 2);
} else {
qw.eq(WmsCoilPendingAction::getActionStatus, bo.getActionStatus());
}
}
qw.eq(bo.getWarehouseId() != null, WmsCoilPendingAction::getWarehouseId, bo.getWarehouseId());
qw.eq(bo.getPriority() != null, WmsCoilPendingAction::getPriority, bo.getPriority());
qw.like(StringUtils.isNotBlank(bo.getSourceType()), WmsCoilPendingAction::getSourceType, bo.getSourceType());
qw.ge(bo.getStartTime() != null, WmsCoilPendingAction::getCompleteTime, bo.getStartTime());
qw.le(bo.getEndTime() != null, WmsCoilPendingAction::getCompleteTime, bo.getEndTime());
qw.eq(StringUtils.isNotBlank(bo.getCreateBy()), WmsCoilPendingAction::getCreateBy, bo.getCreateBy());
qw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), WmsCoilPendingAction::getUpdateBy, bo.getUpdateBy());
qw.like(StringUtils.isNotBlank(bo.getProcessedCoilIds()), WmsCoilPendingAction::getProcessedCoilIds, bo.getProcessedCoilIds());
if (bo.getIncludeDeleted() != null) {
if (bo.getIncludeDeleted() == 2) {
qw.eq(WmsCoilPendingAction::getDelFlag, 2);
} else if (bo.getIncludeDeleted() == 0) {
qw.eq(WmsCoilPendingAction::getDelFlag, 0);
}
} else {
qw.eq(WmsCoilPendingAction::getDelFlag, 0);
}
List<WmsCoilPendingAction> actions = coilPendingActionMapper.selectList(qw);
if (CollectionUtils.isEmpty(actions)) {
return Collections.emptyList();
}
return actions.stream()
.map(WmsCoilPendingAction::getCoilId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
private String mergeCoilIds(String coilIdsCsv, List<Long> pendingCoilIds) {
Set<Long> pendingSet = new LinkedHashSet<>(pendingCoilIds);
if (StringUtils.isBlank(coilIdsCsv)) {
return pendingSet.stream().map(String::valueOf).collect(Collectors.joining(","));
}
List<Long> existing = parseCsvLongs(coilIdsCsv);
if (CollectionUtils.isEmpty(existing)) {
return pendingSet.stream().map(String::valueOf).collect(Collectors.joining(","));
}
Set<Long> existingSet = new LinkedHashSet<>(existing);
existingSet.retainAll(pendingSet);
if (existingSet.isEmpty()) {
return "-1";
}
return existingSet.stream().map(String::valueOf).collect(Collectors.joining(","));
}
private WmsMaterialCoilReportSummaryVo buildEmptySummary() {
WmsMaterialCoilReportSummaryVo vo = new WmsMaterialCoilReportSummaryVo();
vo.setTotalCount(0);
vo.setTotalWeight("0.00");
vo.setAvgWeight("0");
vo.setAbnormalSummary(buildAbnormalSummary(Collections.emptyList()));
vo.setTeamSummary(Collections.emptyMap());
return vo;
}
private WmsMaterialCoilReportSummaryVo buildSummaryFromList(List<WmsMaterialCoilVo> list) {
List<WmsMaterialCoilVo> safeList = list == null ? Collections.emptyList() : list;
int totalCount = safeList.size();
BigDecimal totalWeight = safeList.stream()
.map(WmsMaterialCoilVo::getNetWeight)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
String avgWeight = totalCount > 0
? totalWeight.divide(BigDecimal.valueOf(totalCount), 2, java.math.RoundingMode.HALF_UP).toPlainString()
: "0";
WmsMaterialCoilReportSummaryVo vo = new WmsMaterialCoilReportSummaryVo();
vo.setTotalCount(totalCount);
vo.setTotalWeight(totalWeight.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString());
vo.setAvgWeight(avgWeight);
vo.setAbnormalSummary(buildAbnormalSummary(safeList));
vo.setTeamSummary(buildTeamSummary(safeList));
return vo;
}
private List<Map<String, String>> buildAbnormalSummary(List<WmsMaterialCoilVo> list) {
int totalCount = list.size();
int jishuCount = 0;
int miniCount = 0;
int rubbishCount = 0;
int returnCount = 0;
BigDecimal jishuWeight = BigDecimal.ZERO;
BigDecimal miniWeight = BigDecimal.ZERO;
BigDecimal rubbishWeight = BigDecimal.ZERO;
BigDecimal returnWeight = BigDecimal.ZERO;
for (WmsMaterialCoilVo coil : list) {
String warehouseId = coil.getWarehouseId() == null ? null : String.valueOf(coil.getWarehouseId());
String qualityStatus = coil.getQualityStatus();
BigDecimal netWeight = coil.getNetWeight() == null ? BigDecimal.ZERO : coil.getNetWeight();
if ("2019583656787259393".equals(warehouseId) || "O".equals(qualityStatus)) {
jishuCount++;
jishuWeight = jishuWeight.add(netWeight);
} else if ("2019583325311414274".equals(warehouseId)) {
miniCount++;
miniWeight = miniWeight.add(netWeight);
} else if ("2019583429955104769".equals(warehouseId)
|| "D-".equals(qualityStatus)
|| "D".equals(qualityStatus)
|| "D+".equals(qualityStatus)
|| "C-".equals(qualityStatus)
|| "C".equals(qualityStatus)
|| "C+".equals(qualityStatus)) {
rubbishCount++;
rubbishWeight = rubbishWeight.add(netWeight);
} else if ("2019583137616310273".equals(warehouseId)) {
returnCount++;
returnWeight = returnWeight.add(netWeight);
}
}
List<Map<String, String>> result = new ArrayList<>();
result.add(buildLabelValue("技术部钢卷数", String.valueOf(jishuCount)));
result.add(buildLabelValue("小钢卷库钢卷数", String.valueOf(miniCount)));
result.add(buildLabelValue("废品库钢卷数", String.valueOf(rubbishCount)));
result.add(buildLabelValue("退货库钢卷数", String.valueOf(returnCount)));
result.add(buildLabelValue("技术部钢卷重量", jishuWeight.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString()));
result.add(buildLabelValue("小钢卷库钢卷重量", miniWeight.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString()));
result.add(buildLabelValue("废品库钢卷重量", rubbishWeight.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString()));
result.add(buildLabelValue("退货库钢卷重量", returnWeight.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString()));
result.add(buildLabelValue("技术部占比", percent(jishuCount, totalCount)));
result.add(buildLabelValue("小钢卷库占比", percent(miniCount, totalCount)));
result.add(buildLabelValue("废品库占比", percent(rubbishCount, totalCount)));
result.add(buildLabelValue("退货库占比", percent(returnCount, totalCount)));
return result;
}
private Map<String, WmsMaterialCoilReportSummaryVo.TeamSummaryItem> buildTeamSummary(List<WmsMaterialCoilVo> list) {
Map<String, WmsMaterialCoilReportSummaryVo.TeamSummaryItem> teamSummary = new LinkedHashMap<>();
for (WmsMaterialCoilVo coil : list) {
String team = StringUtils.isNotBlank(coil.getTeam()) ? coil.getTeam() : "未分配";
WmsMaterialCoilReportSummaryVo.TeamSummaryItem item = teamSummary.computeIfAbsent(team, k -> {
WmsMaterialCoilReportSummaryVo.TeamSummaryItem summaryItem = new WmsMaterialCoilReportSummaryVo.TeamSummaryItem();
summaryItem.setCount(0);
summaryItem.setWeight("0.00");
return summaryItem;
});
int nextCount = item.getCount() == null ? 1 : item.getCount() + 1;
BigDecimal currWeight = StringUtils.isBlank(item.getWeight()) ? BigDecimal.ZERO : new BigDecimal(item.getWeight());
BigDecimal netWeight = coil.getNetWeight() == null ? BigDecimal.ZERO : coil.getNetWeight();
item.setCount(nextCount);
item.setWeight(currWeight.add(netWeight).setScale(2, java.math.RoundingMode.HALF_UP).toPlainString());
}
return teamSummary;
}
private Map<String, String> buildLabelValue(String label, String value) {
Map<String, String> map = new LinkedHashMap<>(2);
map.put("label", label);
map.put("value", value);
return map;
}
private String percent(int part, int total) {
if (total <= 0) {
return "0.00%";
}
return BigDecimal.valueOf(part)
.multiply(BigDecimal.valueOf(100))
.divide(BigDecimal.valueOf(total), 2, java.math.RoundingMode.HALF_UP)
.toPlainString() + "%";
}
} }