From e326b53e1834af289294c3dc0687b7eac2b384db Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Thu, 11 Dec 2025 16:46:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(oa):=20=E6=96=B0=E5=A2=9E=E8=BD=A6?= =?UTF-8?q?=E9=97=B4=E6=8A=A5=E8=A1=A8=E6=B1=87=E6=80=BB=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在OaProjectScheduleDelayMapper.xml中增加关联项目表的查询字段 - 扩展OaProjectScheduleDelayVo实体类以支持更多项目相关信息展示 - 创建新的服务接口IOaWorkshopReportService及其实现类OaWorkshopReportServiceImpl - 实现发货单和工艺卡数据的综合统计逻辑 - 添加用于导出车间报表汇总信息的控制器OaWorkshopReportController - 提供按时间段筛选的数据统计与Excel导出功能 - 增加OaWorkshopReportSummaryVo视图对象来封装报表统计数据 --- .../dashboard/OaWorkshopReportController.java | 40 +++++ .../dashboard/OaWorkshopReportSummaryVo.java | 52 ++++++ .../oa/service/IOaWorkshopReportService.java | 9 + .../OaWorkshopReportServiceImpl.java | 165 ++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/controller/dashboard/OaWorkshopReportController.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/dashboard/OaWorkshopReportSummaryVo.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaWorkshopReportService.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/dashboard/OaWorkshopReportServiceImpl.java diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/dashboard/OaWorkshopReportController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/dashboard/OaWorkshopReportController.java new file mode 100644 index 0000000..432c3a6 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/dashboard/OaWorkshopReportController.java @@ -0,0 +1,40 @@ +package com.ruoyi.oa.controller.dashboard; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.oa.domain.vo.dashboard.OaWorkshopReportSummaryVo; +import com.ruoyi.oa.service.IOaWorkshopReportService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.util.Collections; +import java.util.Date; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/oa/workshopReport") +public class OaWorkshopReportController extends BaseController { + + private final IOaWorkshopReportService workshopReportService; + + @GetMapping("/summary") + public R summary(@RequestParam(required = false) Date startTime, + @RequestParam(required = false) Date endTime) { + return R.ok(workshopReportService.summary(startTime, endTime)); + } + + @GetMapping("/export") + public void export(@RequestParam(required = false) Date startTime, + @RequestParam(required = false) Date endTime, + HttpServletResponse response) { + OaWorkshopReportSummaryVo vo = workshopReportService.summary(startTime, endTime); + ExcelUtil.exportExcel(Collections.singletonList(vo), "车间报表汇总", OaWorkshopReportSummaryVo.class, response); + } +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/dashboard/OaWorkshopReportSummaryVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/dashboard/OaWorkshopReportSummaryVo.java new file mode 100644 index 0000000..2d5ab79 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/dashboard/OaWorkshopReportSummaryVo.java @@ -0,0 +1,52 @@ +package com.ruoyi.oa.domain.vo.dashboard; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@ExcelIgnoreUnannotated +public class OaWorkshopReportSummaryVo { + @ExcelProperty("涉及项目数") + private Integer involvedProjectCount; + + @ExcelProperty("发货单数") + private Integer deliveryOrderCount; + @ExcelProperty("发货明细数") + private Integer deliveryDetailCount; + @ExcelProperty("合计数量") + private BigDecimal totalQuantity; + @ExcelProperty("合计净重kg") + private BigDecimal totalNetWeight; + @ExcelProperty("合计毛重kg") + private BigDecimal totalGrossWeight; + @ExcelProperty("合计体积m3") + private BigDecimal totalVolume; + @ExcelProperty("设备种类数") + private Integer equipmentKinds; + @ExcelProperty("型号种类数") + private Integer modelKinds; + + @ExcelProperty("工艺卡数") + private Integer processCardCount; + @ExcelProperty("工艺卡明细数") + private Integer processCardDetailCount; + @ExcelProperty("平均工时h") + private BigDecimal avgWorkHours; + @ExcelProperty("总工时h") + private BigDecimal totalWorkHours; + @ExcelProperty("最小工时h") + private BigDecimal minWorkHours; + @ExcelProperty("最大工时h") + private BigDecimal maxWorkHours; + @ExcelProperty("参与统计工序数") + private Integer processWithTimeCount; + + @ExcelProperty("制造负责人列表") + private List manufacturingLeaders; + @ExcelProperty("作业负责人列表") + private List operationLeaders; +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaWorkshopReportService.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaWorkshopReportService.java new file mode 100644 index 0000000..10f9597 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaWorkshopReportService.java @@ -0,0 +1,9 @@ +package com.ruoyi.oa.service; + +import com.ruoyi.oa.domain.vo.dashboard.OaWorkshopReportSummaryVo; + +import java.util.Date; + +public interface IOaWorkshopReportService { + OaWorkshopReportSummaryVo summary(Date startTime, Date endTime); +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/dashboard/OaWorkshopReportServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/dashboard/OaWorkshopReportServiceImpl.java new file mode 100644 index 0000000..fb98f1b --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/dashboard/OaWorkshopReportServiceImpl.java @@ -0,0 +1,165 @@ +package com.ruoyi.oa.service.impl.dashboard; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.oa.domain.OaDeliveryOrder; +import com.ruoyi.oa.domain.OaDeliveryOrderDetail; +import com.ruoyi.oa.domain.OaProcessCard; +import com.ruoyi.oa.domain.OaProcessCardDetail; +import com.ruoyi.oa.domain.vo.dashboard.OaWorkshopReportSummaryVo; +import com.ruoyi.oa.mapper.OaDeliveryOrderDetailMapper; +import com.ruoyi.oa.mapper.OaDeliveryOrderMapper; +import com.ruoyi.oa.mapper.OaProcessCardDetailMapper; +import com.ruoyi.oa.mapper.OaProcessCardMapper; +import com.ruoyi.oa.service.IOaWorkshopReportService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class OaWorkshopReportServiceImpl implements IOaWorkshopReportService { + + private final OaDeliveryOrderMapper deliveryOrderMapper; + private final OaDeliveryOrderDetailMapper deliveryOrderDetailMapper; + private final OaProcessCardMapper processCardMapper; + private final OaProcessCardDetailMapper processCardDetailMapper; + + @Override + public OaWorkshopReportSummaryVo summary(Date startTime, Date endTime) { + OaWorkshopReportSummaryVo vo = new OaWorkshopReportSummaryVo(); + + // 1) 发货单全局统计(按创建时间可选过滤) + LambdaQueryWrapper orderWrap = Wrappers.lambdaQuery(); + orderWrap.eq(OaDeliveryOrder::getDelFlag, 0); + if (startTime != null) { + orderWrap.ge(OaDeliveryOrder::getCreateTime, startTime); + } + if (endTime != null) { + orderWrap.le(OaDeliveryOrder::getCreateTime, endTime); + } + List orders = deliveryOrderMapper.selectList(orderWrap); + vo.setDeliveryOrderCount(orders.size()); + Set projFromOrders = orders.stream() + .map(OaDeliveryOrder::getProjectId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + List orderIds = orders.stream().map(OaDeliveryOrder::getOrderId).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(orderIds)) { + LambdaQueryWrapper dWrap = Wrappers.lambdaQuery(); + dWrap.in(OaDeliveryOrderDetail::getOrderId, orderIds).eq(OaDeliveryOrderDetail::getDelFlag, 0); + List details = deliveryOrderDetailMapper.selectList(dWrap); + vo.setDeliveryDetailCount(details.size()); + + BigDecimal qty = details.stream() + .map(d -> d.getQuantity() == null ? BigDecimal.ZERO : new BigDecimal(d.getQuantity())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal net = details.stream() + .map(d -> d.getNetWeight() != null ? d.getNetWeight() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal gross = details.stream() + .map(d -> d.getGrossWeight() != null ? d.getGrossWeight() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal vol = details.stream() + .map(d -> d.getVolume() != null ? d.getVolume() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + vo.setTotalQuantity(qty); + vo.setTotalNetWeight(net); + vo.setTotalGrossWeight(gross); + vo.setTotalVolume(vol); + Set equipKinds = details.stream().map(OaDeliveryOrderDetail::getEquipmentName).filter(Objects::nonNull).collect(Collectors.toSet()); + Set modelKinds = details.stream().map(OaDeliveryOrderDetail::getModelSpec).filter(Objects::nonNull).collect(Collectors.toSet()); + vo.setEquipmentKinds(equipKinds.size()); + vo.setModelKinds(modelKinds.size()); + } else { + vo.setDeliveryDetailCount(0); + vo.setTotalQuantity(BigDecimal.ZERO); + vo.setTotalNetWeight(BigDecimal.ZERO); + vo.setTotalGrossWeight(BigDecimal.ZERO); + vo.setTotalVolume(BigDecimal.ZERO); + vo.setEquipmentKinds(0); + vo.setModelKinds(0); + } + + // 2) 工艺卡全局统计(可选创建时间过滤,明细可选工序时间过滤) + LambdaQueryWrapper cardWrap = Wrappers.lambdaQuery(); + cardWrap.eq(OaProcessCard::getDelFlag, 0); + if (startTime != null) { + cardWrap.ge(OaProcessCard::getCreateTime, startTime); + } + if (endTime != null) { + cardWrap.le(OaProcessCard::getCreateTime, endTime); + } + List cards = processCardMapper.selectList(cardWrap); + vo.setProcessCardCount(cards.size()); + Set projFromCards = cards.stream().map(OaProcessCard::getProjectId).filter(Objects::nonNull).collect(Collectors.toSet()); + + List cardIds = cards.stream().map(OaProcessCard::getCardId).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(cardIds)) { + LambdaQueryWrapper cdWrap = Wrappers.lambdaQuery(); + cdWrap.in(OaProcessCardDetail::getCardId, cardIds).eq(OaProcessCardDetail::getDelFlag, 0); + if (startTime != null) { + cdWrap.ge(OaProcessCardDetail::getProcessStartTime, startTime); + } + if (endTime != null) { + cdWrap.le(OaProcessCardDetail::getProcessEndTime, endTime); + } + List cardDetails = processCardDetailMapper.selectList(cdWrap); + vo.setProcessCardDetailCount(cardDetails.size()); + + List hoursList = cardDetails.stream() + .filter(d -> d.getProcessStartTime() != null && d.getProcessEndTime() != null) + .map(d -> { + long minutes = Math.max(0, Duration.between(d.getProcessStartTime().toInstant(), d.getProcessEndTime().toInstant()).toMinutes()); + return new BigDecimal(minutes).divide(new BigDecimal(60), 2, java.math.RoundingMode.HALF_UP); + }) + .collect(Collectors.toList()); + vo.setProcessWithTimeCount(hoursList.size()); + BigDecimal totalHours = hoursList.stream().reduce(BigDecimal.ZERO, BigDecimal::add); + vo.setTotalWorkHours(totalHours); + if (!hoursList.isEmpty()) { + vo.setAvgWorkHours(totalHours.divide(new BigDecimal(hoursList.size()), 2, java.math.RoundingMode.HALF_UP)); + vo.setMinWorkHours(hoursList.stream().min(Comparator.naturalOrder()).orElse(BigDecimal.ZERO)); + vo.setMaxWorkHours(hoursList.stream().max(Comparator.naturalOrder()).orElse(BigDecimal.ZERO)); + } else { + vo.setAvgWorkHours(BigDecimal.ZERO); + vo.setMinWorkHours(BigDecimal.ZERO); + vo.setMaxWorkHours(BigDecimal.ZERO); + } + + Set manufacturingLeaders = cards.stream() + .map(OaProcessCard::getManufacturingLeader) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(java.util.LinkedHashSet::new)); + Set operationLeaders = cards.stream() + .map(OaProcessCard::getOperationLeader) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(java.util.LinkedHashSet::new)); + vo.setManufacturingLeaders(new java.util.ArrayList<>(manufacturingLeaders)); + vo.setOperationLeaders(new java.util.ArrayList<>(operationLeaders)); + } else { + vo.setProcessCardDetailCount(0); + vo.setAvgWorkHours(BigDecimal.ZERO); + vo.setTotalWorkHours(BigDecimal.ZERO); + vo.setMinWorkHours(BigDecimal.ZERO); + vo.setMaxWorkHours(BigDecimal.ZERO); + vo.setProcessWithTimeCount(0); + vo.setManufacturingLeaders(java.util.Collections.emptyList()); + vo.setOperationLeaders(java.util.Collections.emptyList()); + } + + // 3) 涉及项目数(发货+工艺卡并集) + Set involvedProjects = new HashSet<>(); + involvedProjects.addAll(projFromOrders); + involvedProjects.addAll(projFromCards); + vo.setInvolvedProjectCount(involvedProjects.size()); + + return vo; + } +}