feat(oa): 新增车间报表汇总功能
- 在OaProjectScheduleDelayMapper.xml中增加关联项目表的查询字段 - 扩展OaProjectScheduleDelayVo实体类以支持更多项目相关信息展示 - 创建新的服务接口IOaWorkshopReportService及其实现类OaWorkshopReportServiceImpl - 实现发货单和工艺卡数据的综合统计逻辑 - 添加用于导出车间报表汇总信息的控制器OaWorkshopReportController - 提供按时间段筛选的数据统计与Excel导出功能 - 增加OaWorkshopReportSummaryVo视图对象来封装报表统计数据
This commit is contained in:
@@ -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<OaWorkshopReportSummaryVo> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String> manufacturingLeaders;
|
||||
@ExcelProperty("作业负责人列表")
|
||||
private List<String> operationLeaders;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<OaDeliveryOrder> 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<OaDeliveryOrder> orders = deliveryOrderMapper.selectList(orderWrap);
|
||||
vo.setDeliveryOrderCount(orders.size());
|
||||
Set<Long> projFromOrders = orders.stream()
|
||||
.map(OaDeliveryOrder::getProjectId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Long> orderIds = orders.stream().map(OaDeliveryOrder::getOrderId).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(orderIds)) {
|
||||
LambdaQueryWrapper<OaDeliveryOrderDetail> dWrap = Wrappers.lambdaQuery();
|
||||
dWrap.in(OaDeliveryOrderDetail::getOrderId, orderIds).eq(OaDeliveryOrderDetail::getDelFlag, 0);
|
||||
List<OaDeliveryOrderDetail> 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<String> equipKinds = details.stream().map(OaDeliveryOrderDetail::getEquipmentName).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
Set<String> 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<OaProcessCard> 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<OaProcessCard> cards = processCardMapper.selectList(cardWrap);
|
||||
vo.setProcessCardCount(cards.size());
|
||||
Set<Long> projFromCards = cards.stream().map(OaProcessCard::getProjectId).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
|
||||
List<Long> cardIds = cards.stream().map(OaProcessCard::getCardId).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(cardIds)) {
|
||||
LambdaQueryWrapper<OaProcessCardDetail> 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<OaProcessCardDetail> cardDetails = processCardDetailMapper.selectList(cdWrap);
|
||||
vo.setProcessCardDetailCount(cardDetails.size());
|
||||
|
||||
List<BigDecimal> 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<String> manufacturingLeaders = cards.stream()
|
||||
.map(OaProcessCard::getManufacturingLeader)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(java.util.LinkedHashSet::new));
|
||||
Set<String> 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<Long> involvedProjects = new HashSet<>();
|
||||
involvedProjects.addAll(projFromOrders);
|
||||
involvedProjects.addAll(projFromCards);
|
||||
vo.setInvolvedProjectCount(involvedProjects.size());
|
||||
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user