Ver Fonte

"气密泄露点位"菜单,图表展示

17491 há 2 dias atrás
pai
commit
c27d7dd37e

+ 39 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProductLeakagePointStat.java

@@ -0,0 +1,39 @@
+package com.jeesite.modules.mes.entity;
+
+import java.util.Date;
+
+import com.jeesite.common.utils.excel.annotation.ExcelField;
+import com.jeesite.common.utils.excel.annotation.ExcelField.Align;
+import com.jeesite.common.utils.excel.annotation.ExcelFields;
+
+/**
+ * 气密泄露点位按工件汇总的展示数据。
+ */
+public class MesProductLeakagePointStat {
+
+    private String leakagePoint;
+    private String sn;
+    private int occurrenceCount;
+    private Date firstOccurrenceTime;
+    private Date lastOccurrenceTime;
+
+    @ExcelFields({
+            @ExcelField(title = "泄露点位", attrName = "leakagePoint", align = Align.CENTER, sort = 10),
+            @ExcelField(title = "工件号", attrName = "sn", align = Align.CENTER, sort = 20),
+            @ExcelField(title = "出现次数", attrName = "occurrenceCount", align = Align.CENTER, sort = 30),
+            @ExcelField(title = "首次上报时间", attrName = "firstOccurrenceTime", align = Align.CENTER, sort = 40, dataFormat = "yyyy-MM-dd HH:mm:ss"),
+            @ExcelField(title = "最近上报时间", attrName = "lastOccurrenceTime", align = Align.CENTER, sort = 50, dataFormat = "yyyy-MM-dd HH:mm:ss")
+    })
+    public MesProductLeakagePointStat() { }
+
+    public String getLeakagePoint() { return leakagePoint; }
+    public void setLeakagePoint(String leakagePoint) { this.leakagePoint = leakagePoint; }
+    public String getSn() { return sn; }
+    public void setSn(String sn) { this.sn = sn; }
+    public int getOccurrenceCount() { return occurrenceCount; }
+    public void setOccurrenceCount(int occurrenceCount) { this.occurrenceCount = occurrenceCount; }
+    public Date getFirstOccurrenceTime() { return firstOccurrenceTime; }
+    public void setFirstOccurrenceTime(Date firstOccurrenceTime) { this.firstOccurrenceTime = firstOccurrenceTime; }
+    public Date getLastOccurrenceTime() { return lastOccurrenceTime; }
+    public void setLastOccurrenceTime(Date lastOccurrenceTime) { this.lastOccurrenceTime = lastOccurrenceTime; }
+}

+ 350 - 0
src/main/java/com/jeesite/modules/mes/web/MesProductLeakagePointStatController.java

@@ -0,0 +1,350 @@
+package com.jeesite.modules.mes.web;
+
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.mes.entity.MesProductHf;
+import com.jeesite.modules.mes.entity.MesProductQm;
+import com.jeesite.modules.mes.entity.MesProductLeakagePointStat;
+import com.jeesite.modules.mes.req.MesSearchDateReq;
+import com.jeesite.modules.mes.service.MesProductHfService;
+import com.jeesite.modules.mes.service.MesProductQmService;
+import com.jeesite.modules.mes.util.CommonUitl;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletOutputStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.net.URLEncoder;
+
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/** 气密泄露点位统计 Controller。 */
+@Controller
+@RequestMapping(value = "${adminPath}/mes/mesProductLeakagePointStat")
+public class MesProductLeakagePointStatController extends BaseController {
+
+    @Autowired
+    private MesProductHfService mesProductHfService;
+
+    @Autowired
+    private MesProductQmService mesProductQmService;
+
+    @RequiresPermissions("mes:mesProductQm:view")
+    @RequestMapping(value = {"list", ""})
+    public String list(Model model) {
+        return "redirect:" + adminPath + "/mes/mesProductLeakagePointStat/chart";
+    }
+
+    @RequiresPermissions("mes:mesProductQm:view")
+    @RequestMapping(value = "listData")
+    @ResponseBody
+    public Page<MesProductLeakagePointStat> listData(String leakagePoint, String sn, Date startDate,
+            Date endDate, HttpServletRequest request, HttpServletResponse response) {
+        List<MesProductLeakagePointStat> rows = buildStatistics(leakagePoint, sn, startDate, endDate);
+        Page<MesProductLeakagePointStat> page = new Page<>(request, response);
+        page.setCount(rows.size());
+        int from = Math.max(0, (page.getPageNo() - 1) * page.getPageSize());
+        int to = Math.min(rows.size(), from + page.getPageSize());
+        page.setList(from < to ? rows.subList(from, to) : new ArrayList<MesProductLeakagePointStat>());
+        return page;
+    }
+
+    @RequiresPermissions("mes:mesProductQm:view")
+    @RequestMapping(value = "chart")
+    public String chart(Model model) {
+        MesSearchDateReq dateRange = CommonUitl.getSearchDate();
+        model.addAttribute("startDate", dateRange.getStartDate());
+        model.addAttribute("endDate", dateRange.getEndtDate());
+        return "modules/mes/mesProductLeakagePointStatChart";
+    }
+
+    @RequiresPermissions("mes:mesProductQm:view")
+    @RequestMapping(value = "chartData")
+    @ResponseBody
+    public Map<String, Object> chartData(String leakagePoint, String sn, Date startDate, Date endDate) {
+        List<MesProductHf> records = findRecords(sn, startDate, endDate);
+        Map<String, Integer> pointCounts = new HashMap<>();
+        for (MesProductHf record : records) {
+            if (StringUtils.isBlank(record.getHfFlag())) continue;
+            for (String item : record.getHfFlag().split(",")) {
+                String point = normalizeLeakPoint(item);
+                if (StringUtils.isNotBlank(point)) pointCounts.put(point, pointCounts.getOrDefault(point, 0) + 1);
+            }
+        }
+
+        List<Map<String, Object>> points = new ArrayList<>();
+        for (Map.Entry<String, Integer> entry : pointCounts.entrySet()) {
+            Map<String, Object> row = new LinkedHashMap<>();
+            row.put("point", entry.getKey());
+            row.put("count", entry.getValue());
+            points.add(row);
+        }
+        points.sort((a, b) -> Integer.compare((Integer) b.get("count"), (Integer) a.get("count")));
+        String selectedPoint = StringUtils.isNotBlank(leakagePoint) ? leakagePoint.trim()
+                : (points.isEmpty() ? "" : (String) points.get(0).get("point"));
+
+        Map<String, MesProductLeakagePointStat> detailsBySn = new LinkedHashMap<>();
+        Map<String, Integer> dailyCounts = new LinkedHashMap<>();
+        SimpleDateFormat dayFormat = new SimpleDateFormat("MM-dd");
+        for (MesProductHf record : records) {
+            if (StringUtils.isBlank(record.getSn()) || StringUtils.isBlank(record.getHfFlag())) continue;
+            boolean containsSelectedPoint = false;
+            for (String item : record.getHfFlag().split(",")) {
+                if (selectedPoint.equals(normalizeLeakPoint(item))) {
+                    containsSelectedPoint = true;
+                    break;
+                }
+            }
+            if (!containsSelectedPoint) continue;
+            MesProductLeakagePointStat detail = detailsBySn.get(record.getSn());
+            if (detail == null) {
+                detail = new MesProductLeakagePointStat();
+                detail.setLeakagePoint(selectedPoint);
+                detail.setSn(record.getSn());
+                detail.setFirstOccurrenceTime(record.getCreateDate());
+                detailsBySn.put(record.getSn(), detail);
+            }
+            detail.setOccurrenceCount(detail.getOccurrenceCount() + 1);
+            if (record.getCreateDate() != null && (detail.getFirstOccurrenceTime() == null || record.getCreateDate().before(detail.getFirstOccurrenceTime()))) {
+                detail.setFirstOccurrenceTime(record.getCreateDate());
+            }
+            if (record.getCreateDate() != null && (detail.getLastOccurrenceTime() == null || record.getCreateDate().after(detail.getLastOccurrenceTime()))) {
+                detail.setLastOccurrenceTime(record.getCreateDate());
+            }
+            if (record.getCreateDate() != null) {
+                String day = dayFormat.format(record.getCreateDate());
+                dailyCounts.put(day, dailyCounts.getOrDefault(day, 0) + 1);
+            }
+        }
+
+        List<MesProductLeakagePointStat> details = new ArrayList<>(detailsBySn.values());
+        details.sort(Comparator.comparing(MesProductLeakagePointStat::getOccurrenceCount, Comparator.reverseOrder())
+                .thenComparing(MesProductLeakagePointStat::getLastOccurrenceTime, Comparator.nullsLast(Comparator.reverseOrder())));
+        List<Map<String, Object>> trend = new ArrayList<>();
+        for (Map.Entry<String, Integer> entry : dailyCounts.entrySet()) {
+            Map<String, Object> row = new LinkedHashMap<>();
+            row.put("day", entry.getKey());
+            row.put("count", entry.getValue());
+            trend.add(row);
+        }
+        trend.sort(Comparator.comparing(row -> (String) row.get("day")));
+
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("points", points);
+        result.put("selectedPoint", selectedPoint);
+        result.put("details", details);
+        result.put("trend", trend);
+        result.put("totalCount", pointCounts.getOrDefault(selectedPoint, 0));
+        result.put("workpieceCount", details.size());
+        result.put("rawRecordCount", records.size());
+        result.putAll(buildQualityData(records, startDate, endDate));
+        return result;
+    }
+
+    private Map<String, Object> buildQualityData(List<MesProductHf> hfRecords, Date startDate, Date endDate) {
+        MesProductQm query = new MesProductQm();
+        if (startDate != null) query.setTestTime_gte(startDate);
+        if (endDate != null) query.setTestTime_lte(endOfDay(endDate));
+        int qualified = 0;
+        int defective = 0;
+        int noPoint = 0;
+        Map<String, Integer> pointCounts = new LinkedHashMap<>();
+        for (MesProductQm record : mesProductQmService.findList(query)) {
+            if ("OK".equalsIgnoreCase(record.getTestResult())) qualified++;
+            else { defective++; noPoint++; }
+        }
+        for (MesProductHf record : hfRecords) {
+            if ("OK".equalsIgnoreCase(record.getResult())) { qualified++; continue; }
+            defective++;
+            boolean found = false;
+            for (String item : (record.getHfFlag() == null ? "" : record.getHfFlag()).split(",")) {
+                String point = normalizeLeakPoint(item);
+                if (StringUtils.isNotBlank(point)) {
+                    found = true;
+                    pointCounts.put(point, pointCounts.getOrDefault(point, 0) + 1);
+                }
+            }
+            if (!found) noPoint++;
+        }
+        List<Map<String, Object>> quality = new ArrayList<>();
+        Map<String, Object> ok = new LinkedHashMap<>(); ok.put("label", "合格数量"); ok.put("count", qualified); ok.put("color", "#2b8c70"); quality.add(ok);
+        Map<String, Object> ng = new LinkedHashMap<>(); ng.put("label", "不良数量"); ng.put("count", defective); ng.put("color", "#d65b43"); quality.add(ng);
+        List<Map<String, Object>> defects = new ArrayList<>();
+        String[] colors = {"#c94f3b", "#df8a43", "#3b94a1", "#6f8d4e", "#7d6aa3", "#5682b8"};
+        int colorIndex = 0;
+        for (Map.Entry<String, Integer> entry : pointCounts.entrySet()) {
+            Map<String, Object> item = new LinkedHashMap<>(); item.put("label", entry.getKey()); item.put("count", entry.getValue());
+            item.put("color", colors[colorIndex++ % colors.length]); defects.add(item);
+        }
+        Map<String, Object> missing = new LinkedHashMap<>(); missing.put("label", "找不到漏点"); missing.put("count", noPoint); missing.put("color", "#8998a3"); defects.add(missing);
+        defects.sort((a, b) -> Integer.compare((Integer) b.get("count"), (Integer) a.get("count")));
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("qualifiedCount", qualified); result.put("defectiveCount", defective); result.put("noPointCount", noPoint);
+        result.put("qualityCategories", quality); result.put("defectCategories", defects); result.put("qualityTotal", qualified + defective);
+        return result;
+    }
+
+    @RequiresPermissions("mes:mesProductQm:view")
+    @RequestMapping(value = "chartExport")
+    public void chartExport(String sn, Date startDate, Date endDate, HttpServletResponse response) throws Exception {
+        List<MesProductLeakagePointStat> rows = buildStatistics("", sn, startDate, endDate);
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String start = startDate == null ? "全部" : dateFormat.format(startDate);
+        String end = endDate == null ? "全部" : dateFormat.format(endDate);
+        String fileName = "气密泄露点位汇总_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".xlsx";
+
+        response.reset();
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''"
+                + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20"));
+        try (Workbook workbook = new XSSFWorkbook(); ServletOutputStream output = response.getOutputStream()) {
+            Sheet sheet = workbook.createSheet("泄露点位汇总");
+            int[] widths = {14, 34, 12, 22, 22};
+            for (int i = 0; i < widths.length; i++) sheet.setColumnWidth(i, widths[i] * 256);
+
+            CellStyle titleStyle = workbook.createCellStyle();
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+            CellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setAlignment(HorizontalAlignment.CENTER);
+            headerStyle.setBorderBottom(BorderStyle.THIN);
+            headerStyle.setBorderTop(BorderStyle.THIN);
+            headerStyle.setBorderLeft(BorderStyle.THIN);
+            headerStyle.setBorderRight(BorderStyle.THIN);
+            CellStyle dataStyle = workbook.createCellStyle();
+            dataStyle.setBorderBottom(BorderStyle.THIN);
+            dataStyle.setBorderTop(BorderStyle.THIN);
+            dataStyle.setBorderLeft(BorderStyle.THIN);
+            dataStyle.setBorderRight(BorderStyle.THIN);
+
+            Row titleRow = sheet.createRow(0);
+            titleRow.setHeightInPoints(24);
+            Cell titleCell = titleRow.createCell(0);
+            titleCell.setCellValue("气密泄露点位汇总(统计范围:" + start + " 至 " + end + ")");
+            titleCell.setCellStyle(titleStyle);
+            sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, widths.length - 1));
+
+            String[] headers = {"泄露点位", "工件号", "出现次数", "首次上报时间", "最近上报时间"};
+            Row headerRow = sheet.createRow(2);
+            headerRow.setHeightInPoints(21);
+            for (int i = 0; i < headers.length; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerStyle);
+            }
+            SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            int rowIndex = 3;
+            for (MesProductLeakagePointStat stat : rows) {
+                Row row = sheet.createRow(rowIndex++);
+                String[] values = {stat.getLeakagePoint(), stat.getSn(), String.valueOf(stat.getOccurrenceCount()),
+                        formatDateTime(stat.getFirstOccurrenceTime(), dateTimeFormat), formatDateTime(stat.getLastOccurrenceTime(), dateTimeFormat)};
+                for (int i = 0; i < values.length; i++) {
+                    Cell cell = row.createCell(i);
+                    cell.setCellValue(values[i]);
+                    cell.setCellStyle(dataStyle);
+                }
+            }
+            sheet.createFreezePane(0, 3);
+            workbook.write(output);
+        } catch (Exception e) {
+            throw new RuntimeException("导出气密泄露点位失败", e);
+        }
+    }
+
+    private String formatDateTime(Date date, SimpleDateFormat format) {
+        return date == null ? "" : format.format(date);
+    }
+
+    private List<MesProductLeakagePointStat> buildStatistics(String leakagePoint, String sn, Date startDate, Date endDate) {
+        Map<String, MesProductLeakagePointStat> statistics = new LinkedHashMap<>();
+        for (MesProductHf record : findRecords(sn, startDate, endDate)) {
+            if (StringUtils.isBlank(record.getSn()) || StringUtils.isBlank(record.getHfFlag())) continue;
+            for (String item : record.getHfFlag().split(",")) {
+                String point = normalizeLeakPoint(item);
+                if (StringUtils.isBlank(point)
+                        || (StringUtils.isNotBlank(leakagePoint) && !point.contains(leakagePoint.trim()))) continue;
+                String key = point + "\u0000" + record.getSn();
+                MesProductLeakagePointStat stat = statistics.get(key);
+                if (stat == null) {
+                    stat = new MesProductLeakagePointStat();
+                    stat.setLeakagePoint(point);
+                    stat.setSn(record.getSn());
+                    stat.setFirstOccurrenceTime(record.getCreateDate());
+                    statistics.put(key, stat);
+                }
+                stat.setOccurrenceCount(stat.getOccurrenceCount() + 1);
+                if (record.getCreateDate() != null && (stat.getFirstOccurrenceTime() == null
+                        || record.getCreateDate().before(stat.getFirstOccurrenceTime()))) {
+                    stat.setFirstOccurrenceTime(record.getCreateDate());
+                }
+                if (record.getCreateDate() != null && (stat.getLastOccurrenceTime() == null
+                        || record.getCreateDate().after(stat.getLastOccurrenceTime()))) {
+                    stat.setLastOccurrenceTime(record.getCreateDate());
+                }
+            }
+        }
+
+        List<MesProductLeakagePointStat> rows = new ArrayList<>(statistics.values());
+        rows.sort(Comparator.comparing(MesProductLeakagePointStat::getLeakagePoint)
+                .thenComparing(MesProductLeakagePointStat::getOccurrenceCount, Comparator.reverseOrder())
+                .thenComparing(MesProductLeakagePointStat::getSn));
+        return rows;
+    }
+
+    private List<MesProductHf> findRecords(String sn, Date startDate, Date endDate) {
+        MesProductHf query = new MesProductHf();
+        query.setSn(sn);
+        if (startDate != null) query.setCreateDate_gte(startDate);
+        if (endDate != null) query.setCreateDate_lte(endOfDay(endDate));
+        return mesProductHfService.findList(query);
+    }
+
+    /** Date-only filters are inclusive of the selected end date, matching the detection-record page. */
+    private Date endOfDay(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        return calendar.getTime();
+    }
+
+    private String normalizeLeakPoint(String value) {
+        String point = value == null ? "" : value.trim();
+        if (!point.matches("\\d+")) return point;
+        int index;
+        try {
+            index = Integer.parseInt(point);
+        } catch (NumberFormatException ignored) {
+            return point;
+        }
+        if (index < 1 || index > 39) return point;
+        if (index <= 9) return "A" + index;
+        if (index <= 25) return "B" + (index - 9);
+        if (index <= 27) return "C" + (index - 25);
+        return "D" + (index - 27);
+    }
+}