package cn.iocoder.yudao.module.mes.controller.admin.dashboard;
|
|
import cn.idev.excel.ExcelWriter;
|
import cn.idev.excel.FastExcelFactory;
|
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardDailyCountRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardGenericExcelVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardIndicatorAvgRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardInventoryRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardNameCountRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOutboundRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOverviewExcelVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionExcelVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionTrendRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardQualityRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardSummaryRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardTraceRespVO;
|
import cn.iocoder.yudao.module.mes.service.dashboard.MesDashboardService;
|
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
import jakarta.annotation.Resource;
|
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.validation.constraints.Max;
|
import jakarta.validation.constraints.Min;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
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 java.io.IOException;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|
/**
|
* 管理后台 - 管理驾驶舱与统计分析
|
* <p>
|
* 整合生产、质检、库存、出库、追溯五大维度核心指标,提供可视化图表数据与 Excel 报表导出。
|
*
|
* @author 超级管理员
|
*/
|
@Tag(name = "管理后台 - MES 管理驾驶舱")
|
@RestController
|
@RequestMapping("/mes/dashboard")
|
@Validated
|
public class MesDashboardController {
|
|
@Resource
|
private MesDashboardService dashboardService;
|
|
@GetMapping("/summary")
|
@Operation(summary = "获得管理驾驶舱五维度汇总统计")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<MesDashboardSummaryRespVO> getSummary() {
|
return success(dashboardService.getSummary());
|
}
|
|
@GetMapping("/production-trend")
|
@Operation(summary = "获得生产趋势")
|
@Parameter(name = "days", description = "天数", example = "30")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<List<MesDashboardProductionTrendRespVO>> getProductionTrend(
|
@RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) {
|
return success(dashboardService.getProductionTrend(days));
|
}
|
|
@GetMapping("/quality-statistics")
|
@Operation(summary = "获得质检维度统计")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<MesDashboardQualityRespVO> getQualityStatistics() {
|
return success(dashboardService.getQualityStatistics());
|
}
|
|
@GetMapping("/inventory-statistics")
|
@Operation(summary = "获得库存维度统计")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<MesDashboardInventoryRespVO> getInventoryStatistics() {
|
return success(dashboardService.getInventoryStatistics());
|
}
|
|
@GetMapping("/outbound-statistics")
|
@Operation(summary = "获得出库维度统计")
|
@Parameter(name = "days", description = "天数", example = "30")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<MesDashboardOutboundRespVO> getOutboundStatistics(
|
@RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) {
|
return success(dashboardService.getOutboundStatistics(days));
|
}
|
|
@GetMapping("/trace-statistics")
|
@Operation(summary = "获得追溯维度统计")
|
@Parameter(name = "days", description = "天数", example = "30")
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:query')")
|
public CommonResult<MesDashboardTraceRespVO> getTraceStatistics(
|
@RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) {
|
return success(dashboardService.getTraceStatistics(days));
|
}
|
|
@GetMapping("/export-excel")
|
@Operation(summary = "导出管理驾驶舱 Excel 报表")
|
@ApiAccessLog(operateType = EXPORT)
|
@PreAuthorize("@ss.hasPermission('mes:dashboard:export')")
|
public void exportExcel(HttpServletResponse response) throws IOException {
|
// 1. 组装各维度报表数据
|
MesDashboardSummaryRespVO summary = dashboardService.getSummary();
|
List<MesDashboardProductionTrendRespVO> productionTrend = dashboardService.getProductionTrend(30);
|
MesDashboardQualityRespVO quality = dashboardService.getQualityStatistics();
|
MesDashboardInventoryRespVO inventory = dashboardService.getInventoryStatistics();
|
MesDashboardOutboundRespVO outbound = dashboardService.getOutboundStatistics(30);
|
MesDashboardTraceRespVO trace = dashboardService.getTraceStatistics(30);
|
|
// 2. 多 sheet 写入
|
ExcelWriter excelWriter = FastExcelFactory.write(response.getOutputStream())
|
.autoCloseStream(false)
|
.build();
|
try {
|
excelWriter.write(buildOverviewRows(summary), FastExcelFactory.writerSheet("驾驶舱总览").build());
|
excelWriter.write(buildProductionRows(summary, productionTrend), FastExcelFactory.writerSheet("生产统计").build());
|
excelWriter.write(buildQualityRows(quality), FastExcelFactory.writerSheet("质检统计").build());
|
excelWriter.write(buildInventoryRows(inventory), FastExcelFactory.writerSheet("库存统计").build());
|
excelWriter.write(buildOutboundRows(outbound), FastExcelFactory.writerSheet("出库统计").build());
|
excelWriter.write(buildTraceRows(trace), FastExcelFactory.writerSheet("追溯统计").build());
|
} finally {
|
excelWriter.finish();
|
excelWriter.close();
|
}
|
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8("管理驾驶舱统计报表.xls"));
|
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
}
|
|
// ========== Excel 行组装 ==========
|
|
private List<MesDashboardOverviewExcelVO> buildOverviewRows(MesDashboardSummaryRespVO summary) {
|
List<MesDashboardOverviewExcelVO> rows = new ArrayList<>();
|
addOverview(rows, "批次总数", summary.getBatchTotal());
|
addOverview(rows, "生产中批次", summary.getBatchProducing());
|
addOverview(rows, "已完成批次", summary.getBatchFinished());
|
addOverview(rows, "已作废批次", summary.getBatchCanceled());
|
addOverview(rows, "袋码总数", summary.getBagCodeTotal());
|
addOverview(rows, "已导出印刷袋码", summary.getBagCodePrinted());
|
addOverview(rows, "已作废袋码", summary.getBagCodeCanceled());
|
addOverview(rows, "托盘码总数", summary.getPalletTotal());
|
addOverview(rows, "今日扫码入库袋数", summary.getTodayInboundBagCount());
|
addOverview(rows, "检验记录总数", summary.getQualityTotal());
|
addOverview(rows, "合格检验记录", summary.getQualityPass());
|
addOverview(rows, "不合格检验记录", summary.getQualityFail());
|
addOverview(rows, "检验合格率(%)", summary.getQualityPassRate());
|
addOverview(rows, "当前库存总量", summary.getStockTotal());
|
addOverview(rows, "暂存库存量", summary.getStockTemporary());
|
addOverview(rows, "合格库存量", summary.getStockQualified());
|
addOverview(rows, "低库存预警数", summary.getLowStockCount());
|
addOverview(rows, "出库单总数", summary.getOutboundOrderTotal());
|
addOverview(rows, "出库总量", summary.getOutboundQuantityTotal());
|
addOverview(rows, "今日出库量", summary.getTodayOutboundQuantity());
|
addOverview(rows, "扫码总数", summary.getScanTotal());
|
addOverview(rows, "今日扫码数", summary.getTodayScanCount());
|
addOverview(rows, "窜货预警总数", summary.getCrossRegionWarningTotal());
|
addOverview(rows, "待处理窜货预警", summary.getCrossRegionPendingCount());
|
return rows;
|
}
|
|
private List<MesDashboardProductionExcelVO> buildProductionRows(MesDashboardSummaryRespVO summary,
|
List<MesDashboardProductionTrendRespVO> trend) {
|
List<MesDashboardProductionExcelVO> rows = new ArrayList<>();
|
addProduction(rows, "批次", "草稿", summary.getBatchDraft());
|
addProduction(rows, "批次", "生产中", summary.getBatchProducing());
|
addProduction(rows, "批次", "已完成", summary.getBatchFinished());
|
addProduction(rows, "批次", "已作废", summary.getBatchCanceled());
|
addProduction(rows, "袋码", "已生成", summary.getBagCodeGenerated());
|
addProduction(rows, "袋码", "已导出印刷", summary.getBagCodePrinted());
|
addProduction(rows, "袋码", "已作废", summary.getBagCodeCanceled());
|
addProduction(rows, "托盘", "总数", summary.getPalletTotal());
|
addProduction(rows, "入库", "今日扫码入库袋数", summary.getTodayInboundBagCount());
|
for (MesDashboardProductionTrendRespVO item : trend) {
|
addProduction(rows, "趋势-" + item.getDate(), "新增批次", item.getBatchCount());
|
addProduction(rows, "趋势-" + item.getDate(), "新增袋码", item.getBagCodeCount());
|
addProduction(rows, "趋势-" + item.getDate(), "新增托盘", item.getPalletCount());
|
addProduction(rows, "趋势-" + item.getDate(), "入库袋数", item.getInboundBagQuantity() == null ? 0L
|
: item.getInboundBagQuantity().longValue());
|
}
|
return rows;
|
}
|
|
private List<MesDashboardGenericExcelVO> buildQualityRows(MesDashboardQualityRespVO quality) {
|
List<MesDashboardGenericExcelVO> rows = new ArrayList<>();
|
for (MesDashboardNameCountRespVO item : quality.getResultDistribution()) {
|
addGeneric(rows, "检验结果", item.getName(), item.getCount());
|
}
|
for (MesDashboardIndicatorAvgRespVO item : quality.getIndicatorList()) {
|
addGeneric(rows, "指标均值", item.getIndicatorName() + "(平均值)", item.getAverageValue());
|
addGeneric(rows, "指标均值", item.getIndicatorName() + "(合格率%)", item.getPassRate());
|
}
|
addEmptyGuard(rows);
|
return rows;
|
}
|
|
private List<MesDashboardGenericExcelVO> buildInventoryRows(MesDashboardInventoryRespVO inventory) {
|
List<MesDashboardGenericExcelVO> rows = new ArrayList<>();
|
for (MesDashboardNameCountRespVO item : inventory.getWarehouseDistribution()) {
|
addGeneric(rows, "按仓库", item.getName(), item.getCount());
|
}
|
for (MesDashboardNameCountRespVO item : inventory.getItemDistribution()) {
|
addGeneric(rows, "按品种", item.getName(), item.getCount());
|
}
|
for (MesDashboardNameCountRespVO item : inventory.getStateDistribution()) {
|
addGeneric(rows, "按状态", item.getName(), item.getCount());
|
}
|
addEmptyGuard(rows);
|
return rows;
|
}
|
|
private List<MesDashboardGenericExcelVO> buildOutboundRows(MesDashboardOutboundRespVO outbound) {
|
List<MesDashboardGenericExcelVO> rows = new ArrayList<>();
|
for (MesDashboardDailyCountRespVO item : outbound.getTrend()) {
|
addGeneric(rows, "出库趋势", item.getDate(), item.getCount());
|
}
|
for (MesDashboardNameCountRespVO item : outbound.getDealerDistribution()) {
|
addGeneric(rows, "按经销商", item.getName(), item.getCount());
|
}
|
addEmptyGuard(rows);
|
return rows;
|
}
|
|
private List<MesDashboardGenericExcelVO> buildTraceRows(MesDashboardTraceRespVO trace) {
|
List<MesDashboardGenericExcelVO> rows = new ArrayList<>();
|
for (MesDashboardDailyCountRespVO item : trace.getScanTrend()) {
|
addGeneric(rows, "扫码趋势", item.getDate(), item.getCount());
|
}
|
for (MesDashboardNameCountRespVO item : trace.getProvinceDistribution()) {
|
addGeneric(rows, "扫码省份", item.getName(), item.getCount());
|
}
|
for (MesDashboardNameCountRespVO item : trace.getChannelDistribution()) {
|
addGeneric(rows, "扫码渠道", item.getName(), item.getCount());
|
}
|
if (trace.getCrossRegionSummary() != null) {
|
addGeneric(rows, "窜货预警", "总数", trace.getCrossRegionSummary().getTotal());
|
addGeneric(rows, "窜货预警", "待处理", trace.getCrossRegionSummary().getPending());
|
addGeneric(rows, "窜货预警", "已处理", trace.getCrossRegionSummary().getHandled());
|
addGeneric(rows, "窜货预警", "误报", trace.getCrossRegionSummary().getFalseAlarm());
|
addGeneric(rows, "窜货预警", "跨省", trace.getCrossRegionSummary().getCrossProvince());
|
addGeneric(rows, "窜货预警", "跨市", trace.getCrossRegionSummary().getCrossCity());
|
}
|
addEmptyGuard(rows);
|
return rows;
|
}
|
|
/**
|
* 空数据兜底:保证每个 sheet 至少有一行,避免 FastExcel 无法推断表头
|
*/
|
private void addEmptyGuard(List<MesDashboardGenericExcelVO> rows) {
|
if (rows.isEmpty()) {
|
rows.add(new MesDashboardGenericExcelVO("提示", "暂无数据", "0"));
|
}
|
}
|
|
private void addOverview(List<MesDashboardOverviewExcelVO> rows, String indicator, Object value) {
|
rows.add(new MesDashboardOverviewExcelVO(indicator, value == null ? "0" : value.toString()));
|
}
|
|
private void addProduction(List<MesDashboardProductionExcelVO> rows, String category, String status, Object count) {
|
rows.add(new MesDashboardProductionExcelVO(category, status, count == null ? 0L : ((Number) count).longValue()));
|
}
|
|
private void addGeneric(List<MesDashboardGenericExcelVO> rows, String category, String name, Object value) {
|
rows.add(new MesDashboardGenericExcelVO(category, name, value == null ? "0" : value.toString()));
|
}
|
|
}
|