From 5236500f043539c3869f1ec409a62fc0197cc63d Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Thu, 4 Jun 2026 15:45:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(wms/material-coil):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=92=A2=E5=8D=B7=E5=9B=A4=E7=A7=AF=E7=BB=9F=E8=AE=A1=E4=B8=8E?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E8=AF=A6=E6=83=85=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增囤积统计接口:计算已发货钢卷的平均囤积周期和平均囤积成本 - 囤积周期 = 发货时间 - 二维码第一步创建时间 - 囤积成本 = 囤积天数 × 钢卷净重 × 1元/吨/天 - 支持与分页列表相同的查询条件筛选 2. 新增二维码详情查询接口:在分页查询基础上填充完整的二维码记录信息 - 前端可通过 qrcodeRecord.content 获取二维码JSON内容 - 采用批量查询避免N+1性能问题 调整前,钢卷查询无法获取二维码详细内容,也无法统计囤积相关指标;调整后,支持二维码内容查看和囤积成本分析,为仓储成本核算提供数据支持。 --- .../controller/WmsMaterialCoilController.java | 21 ++ .../klp/service/IWmsMaterialCoilService.java | 21 ++ .../impl/WmsMaterialCoilServiceImpl.java | 200 ++++++++++++++++++ 3 files changed, 242 insertions(+) diff --git a/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java b/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java index beeeef8e..469e2a2a 100644 --- a/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java +++ b/klp-wms/src/main/java/com/klp/controller/WmsMaterialCoilController.java @@ -99,6 +99,27 @@ public class WmsMaterialCoilController extends BaseController { return iWmsMaterialCoilService.queryPageListWithRejudge(bo, pageQuery); } + /** + * 查询钢卷物料表列表(包含二维码记录内容) + * 与list接口查询条件完全一致,区别是会填充二维码content到qrcodeRecord字段 + * 前端可通过qrcodeRecord.content获取二维码JSON内容 + */ + @GetMapping("/listWithQrcode") + public TableDataInfo listWithQrcode(WmsMaterialCoilBo bo, PageQuery pageQuery) { + return iWmsMaterialCoilService.queryPageListWithQrcode(bo, pageQuery); + } + + /** + * 统计已发货钢卷的平均囤积周期和平均囤积成本 + * 使用与分页列表相同的查询条件,按发货时间筛选 + * 囤积周期:发货时间 - 二维码中第一步的创建时间 + * 囤积成本:囤积天数 * 净重(吨) * 1元/吨/天 + */ + @PostMapping("/hoardingStatistics") + public R> getHoardingStatistics(@RequestBody WmsMaterialCoilBo bo) { + return R.ok(iWmsMaterialCoilService.getHoardingStatistics(bo)); + } + /** * 统计筛选条件下的全量汇总数据 * 独立的统计接口,使用与分页列表相同的查询条件 diff --git a/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java b/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java index 7a2c076c..c702dda7 100644 --- a/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java +++ b/klp-wms/src/main/java/com/klp/service/IWmsMaterialCoilService.java @@ -387,5 +387,26 @@ public interface IWmsMaterialCoilService { * 根据入场钢卷号或当前钢卷号查询钢卷,供双机架计划绑定使用 */ com.klp.domain.vo.WmsMaterialCoilVo queryByCoilNo(String coilNo); + + /** + * 统计已发货钢卷的平均囤积周期和平均囤积成本 + * 使用与分页列表相同的查询条件,按发货时间筛选已发货钢卷 + * 囤积周期 = 发货时间 - 二维码中第一个步骤的创建时间 + * 囤积成本 = 囤积天数 * 净重(吨) * 1元/吨/天 + * + * @param bo 查询条件 + * @return avgHoardingDays(平均囤积天数), avgHoardingCost(平均囤积成本), totalCount(已发货钢卷数量) + */ + Map getHoardingStatistics(WmsMaterialCoilBo bo); + + /** + * 查询钢卷物料表列表(包含二维码记录信息) + * 与queryPageList查询条件完全一致,区别是会填充qrcodeRecord字段(WmsGenerateRecordVo) + * 前端可通过qrcodeRecord.content获取二维码的JSON内容 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + */ + TableDataInfo queryPageListWithQrcode(WmsMaterialCoilBo bo, PageQuery pageQuery); } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java index 0c1955be..03347c6d 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java @@ -5849,5 +5849,205 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { return baseMapper.selectVoOne(lqw); } + /** + * 获取材料卷的囤积统计信息 + * 根据查询条件计算钢卷的平均囤积天数、平均囤积成本和符合条件的钢卷总数 + * 囤积天数 = 发货时间 - 二维码中第一步操作时间 + * 囤积成本 = 囤积天数 × 钢卷净重 + * 注意: 此处是统计当前钢卷的重量,但是钢卷在加工的过程中重量会发生变化,如果想要更精确的数据需要对二维码进行一个更细致的拆分才行 + * @param bo 查询条件对象,包含筛选钢卷的各种条件 + * @return 包含平均囤积天数、平均囤积成本和总数的Map + * - avgHoardingDays: 平均囤积天数(保留2位小数) + * - avgHoardingCost: 平均囤积成本(保留2位小数) + * - totalCount: 符合条件的钢卷总数 + */ + @Override + public Map getHoardingStatistics(WmsMaterialCoilBo bo) { + // 构建查询条件 + QueryWrapper qw = buildQueryWrapperPlus(bo); + + // 查询符合条件的钢卷列表 + List list = baseMapper.selectVoListWithDynamicJoin(qw); + + // 初始化返回结果,设置默认值 + Map 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 qrcodeIds = list.stream() + .map(WmsMaterialCoilVo::getQrcodeRecordId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 批量查询二维码记录,建立ID到二维码对象的映射 + Map qrcodeMap = new HashMap<>(); + if (!qrcodeIds.isEmpty()) { + List 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 contentMap = objectMapper.readValue(qrcode.getContent(), Map.class); + @SuppressWarnings("unchecked") + List> steps = (List>) 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 queryPageListWithQrcode(WmsMaterialCoilBo bo, PageQuery pageQuery) { + // 先执行普通分页查询 + TableDataInfo result = queryPageList(bo, pageQuery); + List records = result.getRows(); + // 如果有数据,填充二维码记录 + if (records != null && !records.isEmpty()) { + fillQrcodeRecords(records); + } + return result; + } + + /** + * 批量填充钢卷列表的二维码记录 + * 通过批量查询二维码记录,避免N+1查询问题 + * + * @param voList 钢卷列表 + */ + private void fillQrcodeRecords(List voList) { + // 收集所有二维码记录ID + Set qrcodeRecordIds = voList.stream() + .map(WmsMaterialCoilVo::getQrcodeRecordId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (qrcodeRecordIds.isEmpty()) { + return; // 没有二维码记录ID,直接返回 + } + + // 批量查询二维码记录 + Map qrcodeMap = new HashMap<>(); + List qrcodeList = generateRecordMapper.selectVoBatchIds(new ArrayList<>(qrcodeRecordIds)); + if (qrcodeList != null) { + for (WmsGenerateRecordVo qrcode : qrcodeList) { + if (qrcode != null && qrcode.getRecordId() != null) { + qrcodeMap.put(qrcode.getRecordId(), qrcode); + } + } + } + + // 将二维码记录设置到对应的钢卷对象中 + for (WmsMaterialCoilVo vo : voList) { + if (vo.getQrcodeRecordId() != null && qrcodeMap.containsKey(vo.getQrcodeRecordId())) { + vo.setQrcodeRecord(qrcodeMap.get(vo.getQrcodeRecordId())); + } + } + } + + /** + * 将对象解析为Date类型 + * 支持Number、Date和字符串格式的时间对象 + * + * @param obj 时间对象,可以是Number、Date或String + * @return 解析后的Date对象,解析失败返回null + */ + private Date parseDateFromObject(Object obj) { + if (obj == null) { + return null; + } + if (obj instanceof Number) { + // 处理时间戳格式 + return new Date(((Number) obj).longValue()); + } + if (obj instanceof Date) { + // 已经是Date类型,直接返回 + return (Date) obj; + } + // 字符串格式,使用工具类解析 + return DateUtils.parseDate(obj.toString()); + } + }