Эх сурвалжийг харах

Excel导出所有相关功能

wukai 3 сар өмнө
parent
commit
3b37838093
18 өөрчлөгдсөн 1555 нэмэгдсэн , 130 устгасан
  1. 237 0
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiEnergyExportController.java
  2. 1 16
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiExportController.java
  3. 97 0
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiProdExportController.java
  4. 280 0
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiProductivityExportController.java
  5. 1 1
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiStopExportController.java
  6. 223 0
      jjt-biz/src/main/java/com/jjt/biz/controller/ApiYarnExportController.java
  7. 20 33
      jjt-biz/src/main/java/com/jjt/biz/controller/TwinDeviceController.java
  8. 478 0
      jjt-biz/src/main/java/com/jjt/biz/service/impl/BrokenYarnExportServiceImpl.java
  9. 9 0
      jjt-biz/src/main/java/com/jjt/ws/mapper/TwinWorkshopCalcMapper.java
  10. 20 12
      jjt-biz/src/main/java/com/jjt/ws/mapper/TwinWorkshopMapper.java
  11. 17 0
      jjt-biz/src/main/java/com/jjt/ws/service/ITwinWorkshopCalcService.java
  12. 7 0
      jjt-biz/src/main/java/com/jjt/ws/service/ITwinWorkshopService.java
  13. 27 0
      jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopCalcServiceImpl.java
  14. 11 0
      jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopServiceImpl.java
  15. 104 68
      jjt-biz/src/main/resources/mapper/calc/TwinCalcDayMapper.xml
  16. 5 0
      jjt-biz/src/main/resources/mapper/ws/TwinWorkshopCalcMapper.xml
  17. 5 0
      jjt-biz/src/main/resources/mapper/ws/TwinWorkshopMapper.xml
  18. 13 0
      jjt-common/src/main/java/com/jjt/common/utils/DateUtils.java

+ 237 - 0
jjt-biz/src/main/java/com/jjt/biz/controller/ApiEnergyExportController.java

@@ -0,0 +1,237 @@
+package com.jjt.biz.controller;
+
+import com.jjt.calc.domain.TwinCalcDay;
+import com.jjt.calc.service.ITwinCalcDayService;
+import com.jjt.common.core.controller.BaseController;
+import com.jjt.common.utils.DateUtils;
+import com.jjt.ws.domain.TwinWorkshop;
+import com.jjt.ws.domain.TwinWorkshopCalc;
+import com.jjt.ws.service.ITwinWorkshopCalcService;
+import com.jjt.ws.service.ITwinWorkshopService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * Excel导出控制类
+ *
+ * @author ruoyi
+ */
+@Api("数据导出接口--能耗")
+@RestController
+@Slf4j
+public class ApiEnergyExportController extends BaseController {
+    @Resource
+    private ITwinWorkshopService wsService;
+    @Resource
+    private ITwinWorkshopCalcService calcService;
+    @Resource
+    private ITwinCalcDayService twinCalcDayService;
+
+    @ApiOperation("导出能耗日报")
+    @GetMapping("/api/export/energy-day")
+    @CrossOrigin(origins = "*")
+    public void energyDayExport(String date, HttpServletResponse response) {
+        LocalDate localDate = LocalDate.parse(date);
+        TwinWorkshop workshop = wsService.selectTwinWorkshopByWsCode("WS_01");
+        List<TwinWorkshopCalc> calcs = calcService.selectTwinWorkshopCalcListByDate(workshop.getWsId(), DateUtils.parseDate(date));
+        if (calcs.size() == 0) {
+            return;
+        }
+        TwinWorkshopCalc calc = calcs.get(0);
+        List<TwinCalcDay> calcDayList = twinCalcDayService.selectTwinCalcDayListByTime(DateUtils.toDate(localDate), DateUtils.toDate(localDate));
+
+        double kwhA = calcDayList.stream().mapToDouble(d -> d.getKwhA().doubleValue()).sum();
+        double kwhB = calcDayList.stream().mapToDouble(d -> d.getKwhB().doubleValue()).sum();
+        double kwh = kwhA + kwhB;
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/energy-day.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            CreationHelper creationHelper = wb.getCreationHelper();
+            CellStyle percentStyle = wb.createCellStyle();
+            percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            percentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            CellStyle p2 = wb.createCellStyle();
+            p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+            XSSFCellStyle bk = wb.createCellStyle();
+            bk.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
+            bk.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            Sheet sheet = wb.getSheetAt(0);
+            Cell title = sheet.getRow(0).getCell(0);
+            title.setCellValue("经编车间能耗日报(" + date + ")");
+            Cell cell2 = sheet.getRow(1).getCell(1);
+            cell2.setCellValue(calc.getAValue().add(calc.getBValue()).doubleValue());
+            Cell cell3 = sheet.getRow(2).getCell(1);
+            cell3.setCellValue(calc.getAValue().doubleValue());
+            Cell cell4 = sheet.getRow(3).getCell(1);
+            cell4.setCellValue(calc.getBValue().doubleValue());
+            Cell cell5 = sheet.getRow(4).getCell(1);
+            cell5.setCellValue(kwh);
+            Cell cell6 = sheet.getRow(5).getCell(1);
+            cell6.setCellValue(kwhA);
+            Cell cell7 = sheet.getRow(6).getCell(1);
+            cell7.setCellValue(kwhB);
+            Sheet bSheet = wb.getSheetAt(1);
+            AtomicInteger rowNum = new AtomicInteger(2);
+            calcDayList.stream().sorted(Comparator.comparing(TwinCalcDay::getKwh).reversed()).forEach(d -> {
+                Row row = bSheet.createRow(rowNum.get());
+                Cell[] cells = new Cell[4];
+                for (int i = 0; i < cells.length; i++) {
+                    cells[i] = row.createCell(i);
+                }
+
+                cells[0].setCellValue(d.getDeviceId());
+                cells[1].setCellValue(d.getKwh().doubleValue());
+                cells[2].setCellValue(d.getKwhA().doubleValue());
+                cells[3].setCellValue(d.getKwhB().doubleValue());
+
+                rowNum.getAndIncrement();
+            });
+
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("车间能耗日报" + localDate + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @ApiOperation("导出能耗月报")
+    @GetMapping("/api/export/energy-month")
+    @CrossOrigin(origins = "*")
+    public void energyMonthExport(String date, HttpServletResponse response) {
+        date = date.substring(0, 7);
+        TwinWorkshop workshop = wsService.selectTwinWorkshopByWsCode("WS_01");
+        List<TwinWorkshopCalc> shopList = calcService.selectTwinWorkshopCalcListByMonth(workshop.getWsId(), date);
+        Map<Date, TwinWorkshopCalc> shopMap = shopList.stream().collect(Collectors.toMap(TwinWorkshopCalc::getDataDate, o -> o));
+        List<TwinCalcDay> dayList = twinCalcDayService.selectTwinCalcDayListByMonth(date);
+        Map<Date, List<TwinCalcDay>> dayMap = dayList.stream().collect(Collectors.groupingBy(o -> o.getTime(), LinkedHashMap::new, Collectors.toList()));
+        Set<Date> allSet = new TreeSet<>();
+        allSet.addAll(shopMap.keySet());
+        allSet.addAll(dayMap.keySet());
+        double allA = shopList.stream().mapToDouble(d -> d.getAValue().doubleValue()).sum();
+        double allB = shopList.stream().mapToDouble(d -> d.getBValue().doubleValue()).sum();
+        double all = allA + allB;
+        double kwhA = dayList.stream().mapToDouble(d -> d.getKwhA().doubleValue()).sum();
+        double kwhB = dayList.stream().mapToDouble(d -> d.getKwhB().doubleValue()).sum();
+        double kwh = kwhA + kwhB;
+
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/energy-month.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            CreationHelper creationHelper = wb.getCreationHelper();
+            CellStyle percentStyle = wb.createCellStyle();
+            percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            percentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            CellStyle p2 = wb.createCellStyle();
+            p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+            XSSFCellStyle bk = wb.createCellStyle();
+            bk.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
+            bk.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            Sheet sheet = wb.getSheetAt(0);
+            Cell title = sheet.getRow(0).getCell(0);
+            title.setCellValue("经编车间能耗月报(" + date + ")");
+            Cell cell2 = sheet.getRow(1).getCell(1);
+            cell2.setCellValue(all);
+            Cell cell3 = sheet.getRow(2).getCell(1);
+            cell3.setCellValue(allA);
+            Cell cell4 = sheet.getRow(3).getCell(1);
+            cell4.setCellValue(allB);
+            Cell cell5 = sheet.getRow(4).getCell(1);
+            cell5.setCellValue(kwh);
+            Cell cell6 = sheet.getRow(5).getCell(1);
+            cell6.setCellValue(kwhA);
+            Cell cell7 = sheet.getRow(6).getCell(1);
+            cell7.setCellValue(kwhB);
+            Sheet bSheet = wb.getSheetAt(1);
+            Cell bTitle = sheet.getRow(0).getCell(0);
+            bTitle.setCellValue("月度能耗趋势(" + date + ")");
+            AtomicInteger rowNum = new AtomicInteger(3);
+
+            for (Date day : allSet) {
+                TwinWorkshopCalc shopCalc = shopMap.get(day);
+                List<TwinCalcDay> calcDayList = dayMap.get(day);
+                Row row = bSheet.createRow(rowNum.get());
+                Cell[] cells = new Cell[7];
+                for (int i = 0; i < cells.length; i++) {
+                    cells[i] = row.createCell(i);
+                }
+                double aaa = calcDayList.stream().mapToDouble(d -> d.getKwhA().doubleValue()).sum();
+                double bbb = calcDayList.stream().mapToDouble(d -> d.getKwhB().doubleValue()).sum();
+                double ttt = aaa + bbb;
+                cells[0].setCellValue(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, day));
+                if (shopCalc != null) {
+                    cells[1].setCellValue(shopCalc.getAValue().add(shopCalc.getBValue()).doubleValue());
+                    cells[2].setCellValue(shopCalc.getAValue().doubleValue());
+                    cells[3].setCellValue(shopCalc.getBValue().doubleValue());
+                }
+                cells[4].setCellValue(ttt);
+                cells[5].setCellValue(aaa);
+                cells[6].setCellValue(bbb);
+
+                rowNum.getAndIncrement();
+            }
+            dayList.stream().sorted(Comparator.comparing(TwinCalcDay::getKwh).reversed()).forEach(d -> {
+            });
+
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("车间能耗月报" + date + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @ApiOperation("能耗统计报表")
+    @GetMapping("/api/export/tmp")
+    @CrossOrigin(origins = "*")
+    public void tmp(HttpServletResponse response) {
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/tmp.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("能耗统计报表" + DateUtils.dateTimeNow() + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+}

+ 1 - 16
jjt-biz/src/main/java/com/jjt/biz/controller/ApiExportController.java

@@ -1,18 +1,13 @@
 package com.jjt.biz.controller;
 
-import com.jjt.biz.service.ITwinDeviceService;
 import com.jjt.biz.vo.FormulaDetail;
 import com.jjt.biz.vo.GramMass;
 import com.jjt.biz.vo.GramMassDetail;
 import com.jjt.biz.vo.WarpRunIn;
-import com.jjt.calc.service.ITwinCalcAlarmsService;
-import com.jjt.calc.service.ITwinCalcDayService;
-import com.jjt.calc.service.ITwinCalcStopService;
 import com.jjt.common.constant.CacheConstants;
 import com.jjt.common.core.controller.BaseController;
 import com.jjt.common.core.redis.RedisCache;
 import com.jjt.common.utils.DateUtils;
-import com.jjt.utils.AsyncService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -39,21 +34,11 @@ import java.util.stream.Collectors;
  *
  * @author ruoyi
  */
-@Api("数据接口")
+@Api("数据导出接口--实时")
 @RestController
 @Slf4j
 public class ApiExportController extends BaseController {
     @Resource
-    private ITwinCalcDayService twinCalcDayService;
-    @Resource
-    private ITwinCalcStopService stopService;
-    @Resource
-    private ITwinCalcAlarmsService alarmsService;
-    @Resource
-    private ITwinDeviceService deviceService;
-    @Resource
-    private AsyncService asyncService;
-    @Resource
     private RedisCache redisCache;
 
     @ApiOperation("送经量")

+ 97 - 0
jjt-biz/src/main/java/com/jjt/biz/controller/ApiProdExportController.java

@@ -0,0 +1,97 @@
+package com.jjt.biz.controller;
+
+import com.jjt.biz.service.ITwinDeviceService;
+import com.jjt.calc.domain.TwinCalcDay;
+import com.jjt.calc.service.ITwinCalcDayService;
+import com.jjt.calc.service.ITwinCalcStopService;
+import com.jjt.common.core.controller.BaseController;
+import com.jjt.common.core.redis.RedisCache;
+import com.jjt.common.utils.DateUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * Excel导出控制类
+ *
+ * @author ruoyi
+ */
+@Api("数据导出接口--产量")
+@RestController
+@Slf4j
+public class ApiProdExportController extends BaseController {
+    @Resource
+    private ITwinCalcDayService twinCalcDayService;
+
+    @ApiOperation("导出产量数据")
+    @GetMapping("/api/export/production")
+    @CrossOrigin(origins = "*")
+    public void productionExport(String start, String end, HttpServletResponse response) {
+        LocalDate localDate = LocalDate.parse(start);
+        Date sd = Date.from(localDate.atStartOfDay(ZoneOffset.of("+8")).toInstant());
+        localDate = LocalDate.parse(end);
+        Date ed = Date.from(localDate.atStartOfDay(ZoneOffset.of("+8")).toInstant());
+        List<TwinCalcDay> list = twinCalcDayService.selectTwinCalcDayListByTime(sd, ed);
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/total.xlsx"); Workbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            CreationHelper creationHelper = wb.getCreationHelper();
+            CellStyle percentStyle = wb.createCellStyle();
+            percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            CellStyle p2 = wb.createCellStyle();
+            p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+
+            Map<Date, List<TwinCalcDay>> dayGroup = list.stream().collect(Collectors.groupingBy(TwinCalcDay::getTime, LinkedHashMap::new, Collectors.toList()));
+            for (Map.Entry<Date, List<TwinCalcDay>> entry : dayGroup.entrySet()) {
+                Sheet sheet = wb.cloneSheet(0);
+                wb.setSheetName(wb.getSheetIndex(sheet), DateUtils.parseDateToStr("yyyy-MM-dd", entry.getKey()));
+                List<TwinCalcDay> days = entry.getValue();
+                AtomicInteger rowNum = new AtomicInteger(2);
+                days.forEach(day -> {
+                    Row row = sheet.createRow(rowNum.get());
+                    Cell[] cells = new Cell[25];
+                    for (int i = 0; i < cells.length; i++) {
+                        cells[i] = row.createCell(i);
+                    }
+                    day.setCells(cells);
+                    rowNum.getAndIncrement();
+                });
+            }
+
+            wb.removeSheetAt(0);
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("最近7天产量统计" + DateUtils.dateTimeNow() + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+}

+ 280 - 0
jjt-biz/src/main/java/com/jjt/biz/controller/ApiProductivityExportController.java

@@ -0,0 +1,280 @@
+package com.jjt.biz.controller;
+
+import com.jjt.common.core.controller.BaseController;
+import com.jjt.common.utils.DateUtils;
+import com.jjt.emp.domain.TwinEmpCalc;
+import com.jjt.emp.service.ITwinEmpCalcService;
+import com.jjt.utils.Tools;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Excel导出控制类
+ *
+ * @author ruoyi
+ */
+@Api("数据导出接口--绩效")
+@RestController
+@Slf4j
+public class ApiProductivityExportController extends BaseController {
+    @Resource
+    private ITwinEmpCalcService empCalcService;
+
+    @ApiOperation("导出绩效日报")
+    @GetMapping("/api/export/productivity-day")
+    @CrossOrigin(origins = "*")
+    public void productivityDayExport(String date, HttpServletResponse response) {
+        LocalDate localDate = LocalDate.parse(date);
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/productivity-day.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            CreationHelper creationHelper = wb.getCreationHelper();
+            CellStyle percentStyle = wb.createCellStyle();
+            percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            percentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            CellStyle p2 = wb.createCellStyle();
+            p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+            XSSFCellStyle bk = wb.createCellStyle();
+            bk.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
+            bk.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+            XSSFCellStyle bkp = wb.createCellStyle();
+            bkp.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
+            bkp.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+            bkp.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            bkp.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            List<TwinEmpCalc> list = empCalcService.selectTwinEmpCalcListByDate(DateUtils.parseDate(date));
+            Sheet sheet = wb.getSheetAt(0);
+            Sheet bSheet = wb.cloneSheet(0);
+            Cell title = sheet.getRow(0).getCell(0);
+            title.setCellValue("A班经编生产绩效日报(" + date + ")");
+            Cell bTitle = bSheet.getRow(0).getCell(0);
+            bTitle.setCellValue("B班经编生产绩效日报(" + date + ")");
+            //按姓名分组
+            Map<String, List<TwinEmpCalc>> mapA = list.stream().filter(o -> "A".equals(o.getEmpTeam())).collect(Collectors.groupingBy(TwinEmpCalc::getEmpName, LinkedHashMap::new, Collectors.toList()));
+            Map<String, List<TwinEmpCalc>> mapB = list.stream().filter(o -> "B".equals(o.getEmpTeam())).collect(Collectors.groupingBy(TwinEmpCalc::getEmpName, LinkedHashMap::new, Collectors.toList()));
+            processJx(sheet, mapA, percentStyle, p2, bk, bkp);
+
+            processJx(bSheet, mapB, percentStyle, p2, bk, bkp);
+            wb.setSheetName(0, "A班经编生产绩效");
+            wb.setSheetName(1, "B班经编生产绩效");
+
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("经编生产绩效日报" + localDate + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @ApiOperation("导出绩效月报")
+    @GetMapping("/api/export/productivity-month")
+    @CrossOrigin(origins = "*")
+    public void productivityMonthExport(String date, HttpServletResponse response)  {
+        date = date.substring(0, 7);
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/productivity-month.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            CreationHelper creationHelper = wb.getCreationHelper();
+            CellStyle percentStyle = wb.createCellStyle();
+            percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+            percentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+            CellStyle p2 = wb.createCellStyle();
+            p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+            XSSFCellStyle bk = wb.createCellStyle();
+            bk.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
+            bk.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+            List<TwinEmpCalc> list = empCalcService.selectTwinEmpCalcListByMonth(date);
+            Sheet sheet = wb.getSheetAt(0);
+            Cell title = sheet.getRow(0).getCell(0);
+            title.setCellValue("经编生产绩效月报(" + date + ")");
+            //1.按姓名分组
+            Map<String, List<TwinEmpCalc>> map = list.stream().collect(Collectors.groupingBy(TwinEmpCalc::getEmpName, LinkedHashMap::new, Collectors.toList()));
+            int rowNum = 3;
+            for (String name : map.keySet()) {
+                int sr = rowNum;
+                List<TwinEmpCalc> calcList = map.get(name);
+                //密度字段合并
+                calcList.forEach(calc -> calc.setDensity(Tools.density(calc.getDensity())));
+                //2.按密度分组
+                Map<BigDecimal, List<TwinEmpCalc>> densityMap = calcList.stream().collect(Collectors.groupingBy(TwinEmpCalc::getDensity, LinkedHashMap::new, Collectors.toList()));
+                BigDecimal[] dayCalc = new BigDecimal[31];
+                BigDecimal totalP = BigDecimal.ZERO;
+                Arrays.fill(dayCalc, BigDecimal.ZERO);
+                for (BigDecimal density : densityMap.keySet()) {
+                    BigDecimal totalLength = BigDecimal.ZERO;
+                    List<TwinEmpCalc> tempList = densityMap.get(density);
+                    Row row = sheet.createRow(rowNum);
+                    Cell[] cells = new Cell[36];
+                    for (int i = 0; i < cells.length; i++) {
+                        cells[i] = row.createCell(i);
+                    }
+                    cells[0].setCellValue(name);
+                    cells[1].setCellValue(density.doubleValue());
+                    cells[1].setCellStyle(bk);
+                    BigDecimal[] lengths = new BigDecimal[31];
+                    Arrays.fill(lengths, BigDecimal.ZERO);
+                    BigDecimal price = BigDecimal.ZERO;
+                    for (TwinEmpCalc calc : tempList) {
+                        int day = DateUtils.toLocalDate(calc.getEmpDate()).getDayOfMonth();
+                        lengths[day - 1] = lengths[day - 1].add(calc.getLength());
+                        dayCalc[day - 1] = dayCalc[day - 1].add(calc.getLength());
+                        totalLength = totalLength.add(calc.getLength());
+                        if (calc.getPrice() != null) {
+                            price = calc.getPrice();
+                        }
+                    }
+                    for (int i = 0; i < lengths.length; i++) {
+                        if (lengths[i].compareTo(BigDecimal.ZERO) != 0) {
+                            cells[i + 2].setCellValue(lengths[i].doubleValue());
+                        }
+                    }
+                    cells[33].setCellValue(totalLength.doubleValue());
+                    BigDecimal totalPrice = totalLength.multiply(price);
+                    totalP = totalP.add(totalPrice);
+                    cells[34].setCellValue(price.doubleValue());
+                    cells[35].setCellValue(totalPrice.doubleValue());
+                    rowNum++;
+                }
+
+                Row row = sheet.createRow(rowNum);
+                Cell[] cells = new Cell[36];
+                for (int i = 0; i < cells.length; i++) {
+                    cells[i] = row.createCell(i);
+                }
+                cells[0].setCellValue(name);
+                cells[1].setCellValue("合计");
+                cells[1].setCellStyle(bk);
+                BigDecimal total = BigDecimal.ZERO;
+                for (int i = 0; i < dayCalc.length; i++) {
+                    if (dayCalc[i].compareTo(BigDecimal.ZERO) != 0) {
+                        cells[i + 2].setCellValue(dayCalc[i].doubleValue());
+                        total = total.add(dayCalc[i]);
+                    }
+                    cells[i + 2].setCellStyle(bk);
+                }
+                cells[33].setCellValue(total.doubleValue());
+                cells[33].setCellStyle(bk);
+                cells[34].setCellValue("合计:");
+                cells[34].setCellStyle(bk);
+                cells[35].setCellValue(totalP.doubleValue());
+                cells[35].setCellStyle(bk);
+                int er = rowNum;
+                sheet.addMergedRegion(new CellRangeAddress(sr, er, 0, 0));
+                rowNum++;
+            }
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("经编生产绩效月报" + date + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+
+    private void processJx(Sheet sheet, Map<String, List<TwinEmpCalc>> map, CellStyle percentStyle, CellStyle p2, XSSFCellStyle bk, XSSFCellStyle bkp) {
+        int rowNum = 2;
+        for (String name : map.keySet()) {
+            List<TwinEmpCalc> calcList = map.get(name);
+            Map<Long, List<TwinEmpCalc>> calcMap = calcList.stream().collect(Collectors.groupingBy(TwinEmpCalc::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+            int sr = rowNum;
+            BigDecimal len = BigDecimal.ZERO;
+            BigDecimal total = BigDecimal.ZERO;
+            BigDecimal ef = BigDecimal.ZERO;
+            for (Long deviceId : calcMap.keySet()) {
+                List<TwinEmpCalc> list = calcMap.get(deviceId);
+                int ssr = rowNum;
+                for (TwinEmpCalc calc : list) {
+                    Row row = sheet.createRow(rowNum);
+                    Cell[] cells = new Cell[10];
+                    for (int i = 0; i < cells.length; i++) {
+                        cells[i] = row.createCell(i);
+                    }
+                    cells[0].setCellValue(name);
+                    cells[1].setCellValue(calc.getDeviceId());
+                    if (ssr == rowNum) {
+                        cells[2].setCellValue(calc.getEfficiency().setScale(2, RoundingMode.HALF_UP).doubleValue());
+                        cells[2].setCellStyle(percentStyle);
+                        ef = ef.add(calc.getEfficiency());
+                    }
+//                    cells[3].setCellValue();
+                    if (calc.getHeight() != null) {
+                        cells[4].setCellValue(calc.getHeight().setScale(2, RoundingMode.HALF_UP).doubleValue());
+                    }
+                    cells[5].setCellValue(calc.getMick());
+                    cells[6].setCellValue(calc.getDensity().setScale(2, RoundingMode.HALF_UP).doubleValue());
+                    cells[6].setCellStyle(p2);
+                    cells[7].setCellValue(calc.getLength().doubleValue());
+                    len = len.add(calc.getLength());
+                    if (calc.getPrice() != null) {
+                        BigDecimal totalPrice = calc.getTotalPrice().setScale(2, RoundingMode.HALF_UP);
+                        total = total.add(totalPrice);
+                        cells[8].setCellValue(calc.getPrice().doubleValue());
+                        cells[9].setCellValue(totalPrice.doubleValue());
+                    }
+                    rowNum++;
+                }
+                int eer = rowNum - 1;
+                if (list.size() > 1) {
+                    sheet.addMergedRegion(new CellRangeAddress(ssr, eer, 1, 1));
+                    sheet.addMergedRegion(new CellRangeAddress(ssr, eer, 2, 2));
+                }
+            }
+            Cell[] cells = new Cell[10];
+            Row row = sheet.createRow(rowNum);
+            for (int i = 0; i < cells.length; i++) {
+                cells[i] = row.createCell(i);
+                cells[i].setCellStyle(bk);
+            }
+            BigDecimal avgEf = ef.divide(BigDecimal.valueOf(calcMap.size()), 4, RoundingMode.HALF_UP);
+            cells[0].setCellValue(name);
+            cells[1].setCellValue("合计");
+            cells[2].setCellValue(avgEf.doubleValue());
+            cells[2].setCellStyle(bkp);
+            cells[7].setCellValue(len.doubleValue());
+            cells[9].setCellValue(total.doubleValue());
+            rowNum++;
+
+            int er = rowNum - 1;
+            sheet.addMergedRegion(new CellRangeAddress(sr, er, 0, 0));
+        }
+    }
+}

+ 1 - 1
jjt-biz/src/main/java/com/jjt/biz/controller/ApiStopExportController.java

@@ -42,7 +42,7 @@ import java.util.stream.Collectors;
  *
  * @author ruoyi
  */
-@Api("数据接口")
+@Api("数据导出接口--停机")
 @RestController
 @Slf4j
 public class ApiStopExportController extends BaseController {

+ 223 - 0
jjt-biz/src/main/java/com/jjt/biz/controller/ApiYarnExportController.java

@@ -0,0 +1,223 @@
+package com.jjt.biz.controller;
+
+import com.jjt.biz.service.impl.BrokenYarnExportServiceImpl;
+import com.jjt.biz.vo.FormulaDetail;
+import com.jjt.calc.domain.TwinCalcDay;
+import com.jjt.calc.service.ITwinCalcDayService;
+import com.jjt.common.constant.CacheConstants;
+import com.jjt.common.core.controller.BaseController;
+import com.jjt.common.core.redis.RedisCache;
+import com.jjt.common.utils.DateUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xddf.usermodel.chart.BarDirection;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * Excel导出控制类
+ *
+ * @author ruoyi
+ */
+@Api("数据导出接口--纱线")
+@RestController
+@Slf4j
+public class ApiYarnExportController extends BaseController {
+    @Resource
+    private ITwinCalcDayService twinCalcDayService;
+    @Resource
+    private BrokenYarnExportServiceImpl brokenYarnExportService;
+
+    @Resource
+    private RedisCache redisCache;
+
+    @ApiOperation("纱线规格")
+    @GetMapping("/api/export/yarn-specifications")
+    @CrossOrigin(origins = "*")
+    public void yarnSpecificationsExport(Integer day, HttpServletResponse response) {
+        List<FormulaDetail> list = redisCache.getCacheObject(CacheConstants.INDEX_FORMULA_DETAIL);
+        if (list != null) {
+            list.sort(Comparator.comparing(FormulaDetail::getFormula_data_4).thenComparing(FormulaDetail::getFormula_data_5).thenComparing(FormulaDetail::getFormula_data_6)
+                    .thenComparing(FormulaDetail::getFormula_data_7).thenComparing(FormulaDetail::getFormula_data_8).thenComparing(FormulaDetail::getFormula_data_9)
+                    .thenComparing(FormulaDetail::getFormula_data_15));
+            Map<String, List<FormulaDetail>> map = list.stream().collect(Collectors.groupingBy(FormulaDetail::getYarnD, LinkedHashMap::new, Collectors.toList()));
+            LocalDateTime localDateTime = LocalDateTime.now();
+            if (localDateTime.getHour() < 7) {
+                //当天7点前,则是前天数据
+                localDateTime = localDateTime.minusDays(2);
+            } else {
+                //7点后,就是昨日数据
+                localDateTime = localDateTime.minusDays(1);
+            }
+            LocalDate localDate = localDateTime.toLocalDate();
+            Date sd = Date.from(localDate.atStartOfDay(ZoneOffset.of("+8")).toInstant());
+            List<TwinCalcDay> calcDayList = twinCalcDayService.selectTwinCalcDayListByTime(sd, sd);
+            Map<Long, BigDecimal> decimalMap = calcDayList.stream().collect(Collectors.toMap(TwinCalcDay::getDeviceId, TwinCalcDay::getLength));
+            try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/yarn.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+                Sheet sheet = wb.getSheetAt(0);
+                AtomicInteger rowNum = new AtomicInteger(2);
+                CellStyle percentStyle = wb.createCellStyle();
+                percentStyle.setDataFormat(wb.createDataFormat().getFormat("0.00%"));
+                // 垂直居中
+                percentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+                CellStyle p2 = wb.createCellStyle();
+                p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+                for (String s : map.keySet()) {
+                    int sr = rowNum.get();
+                    List<FormulaDetail> fList = map.get(s);
+                    for (FormulaDetail detail : fList) {
+                        Row row = sheet.createRow(rowNum.get());
+                        Cell[] cells = new Cell[39];
+                        for (int i = 0; i < cells.length; i++) {
+                            cells[i] = row.createCell(i);
+                        }
+                        detail.setYarnCells(cells, p2);
+                        rowNum.getAndIncrement();
+                    }
+                    if (fList.size() > 1) {
+                        int er = sr + fList.size() - 1;
+                        sheet.addMergedRegion(new CellRangeAddress(sr, er, 0, 0));
+                        sheet.addMergedRegion(new CellRangeAddress(sr, er, 1, 1));
+                        sheet.addMergedRegion(new CellRangeAddress(sr, er, 2, 2));
+                    }
+                }
+                XSSFSheet sheet2 = wb.getSheetAt(1);
+                wb.setSheetName(1, "未来产能预期(" + day + "天)");
+                AtomicInteger rn = new AtomicInteger(2);
+                //1.先按底纱分组
+                Map<String, List<FormulaDetail>> map2 = list.stream().collect(Collectors.groupingBy(FormulaDetail::getYarnDd, LinkedHashMap::new, Collectors.toList()));
+                for (String s : map2.keySet()) {
+                    int sr = rn.get();
+                    List<FormulaDetail> l12list = map2.get(s);
+                    //2.再按规格分组
+                    Map<Integer, List<FormulaDetail>> d1map = l12list.stream().collect(Collectors.groupingBy(FormulaDetail::getFormula_data_6, LinkedHashMap::new, Collectors.toList()));
+                    for (Integer l3d : d1map.keySet()) {
+                        int ssr = rn.get();
+                        List<FormulaDetail> l3list = d1map.get(l3d);
+                        //3.再按盘头根数和毛高分组
+                        Map<String, List<FormulaDetail>> d2map = l3list.stream().collect(Collectors.groupingBy(FormulaDetail::getYarnPt, LinkedHashMap::new, Collectors.toList()));
+                        for (String pt : d2map.keySet()) {
+                            List<FormulaDetail> ptList = d2map.get(pt);
+                            FormulaDetail detail = ptList.get(0);
+                            String device = "";
+                            BigDecimal length = BigDecimal.ZERO;
+                            for (FormulaDetail dd : ptList) {
+                                BigDecimal temp = decimalMap.get(dd.getDeviceId());
+                                if (temp != null) {
+                                    length = length.add(temp);
+                                }
+//                                device += dd.getDevice() + "/";
+                            }
+                            BigDecimal yLength = length.multiply(BigDecimal.valueOf(day));
+                            Row row = sheet2.createRow(rn.get());
+                            Cell[] cells = new Cell[12];
+                            for (int i = 0; i < cells.length; i++) {
+                                cells[i] = row.createCell(i);
+                            }
+                            cells[0].setCellValue(rn.get() - 1);
+                            cells[1].setCellValue(detail.getFormula_data_4());
+                            cells[2].setCellValue(detail.getFormula_data_5());
+                            cells[3].setCellValue(detail.getFormula_data_6());
+                            cells[4].setCellValue(detail.getFormula_data_10());
+                            cells[5].setCellValue(detail.getFormula_data_11());
+                            cells[6].setCellValue(detail.getFormula_data_12());
+                            cells[7].setCellValue(detail.getFormula_data_15());
+                            cells[8].setCellValue(length.doubleValue());
+                            cells[8].setCellStyle(p2);
+                            cells[9].setCellValue(yLength.doubleValue());
+                            cells[9].setCellStyle(p2);
+                            //规格占比
+                            BigDecimal percent1 = BigDecimal.valueOf(ptList.size()).divide(BigDecimal.valueOf(list.size()), 4, RoundingMode.HALF_UP);
+                            cells[10].setCellValue(percent1.doubleValue());
+                            cells[10].setCellStyle(percentStyle);
+                            //底纱占比
+                            BigDecimal percent2 = BigDecimal.valueOf(l12list.size()).divide(BigDecimal.valueOf(list.size()), 4, RoundingMode.HALF_UP);
+                            cells[11].setCellValue(percent2.doubleValue());
+                            cells[11].setCellStyle(percentStyle);
+                            rn.getAndIncrement();
+                        }
+                        if (d2map.size() > 1) {
+                            int er = rn.get() - 1;
+                            sheet2.addMergedRegion(new CellRangeAddress(ssr, er, 1, 1));
+                            sheet2.addMergedRegion(new CellRangeAddress(ssr, er, 2, 2));
+                            sheet2.addMergedRegion(new CellRangeAddress(ssr, er, 3, 3));
+                        }
+                    }
+                    if (d1map.size() > 1) {
+                        int er = rn.get() - 1;
+                        sheet2.addMergedRegion(new CellRangeAddress(sr, er, 11, 11));
+                    }
+                }
+                int endRn = rn.get() - 1;
+                int[] pos = {13, 1, 20, endRn + 1};
+                int[] xdata = {2, endRn, 0, 0};
+                int[] data = {2, endRn, 10, 10};
+                brokenYarnExportService.drawBar(sheet2, "规格占比", "规格序号", pos, xdata, data, BarDirection.COL);
+                // 清空response
+                response.reset();
+                // 设置response的Header
+                response.setCharacterEncoding("UTF-8");
+                //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+                //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+                // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("纱线规格导出" + DateUtils.dateTimeNow() + ".xlsx", "UTF-8"));
+                response.setContentType("application/octet-stream");
+                wb.write(outputStream);
+                outputStream.flush();
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    @ApiOperation("导出断纱分析")
+    @GetMapping("/api/export/broken-yarn")
+    @CrossOrigin(origins = "*")
+    public void brokenYarnExport(String date, HttpServletResponse response) throws ParseException {
+        LocalDate localDate = LocalDate.parse(date);
+        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("tpl/brokenYarn.xlsx"); XSSFWorkbook wb = new XSSFWorkbook(inputStream); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
+            //1.基本信息表
+            brokenYarnExportService.base(wb, localDate);
+            //2.停机原因分析
+            brokenYarnExportService.stop(wb, localDate);
+            //4.断纱停机TOP排名
+            brokenYarnExportService.top(wb, localDate);
+            // 清空response
+            response.reset();
+            // 设置response的Header
+            response.setCharacterEncoding("UTF-8");
+            //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
+            //attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
+            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
+            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("断纱分析日报" + localDate + ".xlsx", "UTF-8"));
+            response.setContentType("application/octet-stream");
+            wb.write(outputStream);
+            outputStream.flush();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+}

+ 20 - 33
jjt-biz/src/main/java/com/jjt/biz/controller/TwinDeviceController.java

@@ -1,28 +1,21 @@
 package com.jjt.biz.controller;
 
-import java.util.List;
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
-
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import com.jjt.biz.domain.TwinDevice;
+import com.jjt.biz.service.ITwinDeviceService;
 import com.jjt.common.annotation.Log;
 import com.jjt.common.core.controller.BaseController;
 import com.jjt.common.core.domain.AjaxResult;
+import com.jjt.common.core.page.TableDataInfo;
 import com.jjt.common.enums.BusinessType;
-import com.jjt.biz.domain.TwinDevice;
-import com.jjt.biz.service.ITwinDeviceService;
 import com.jjt.common.utils.poi.ExcelUtil;
-import com.jjt.common.core.page.TableDataInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 
 /**
  * 设备管理Controller
@@ -30,10 +23,10 @@ import com.jjt.common.core.page.TableDataInfo;
  * @author wukai
  * @date 2025-01-15
  */
-@Api(tags="设备管理")
+@Api(tags = "设备管理")
 @RestController
 @RequestMapping("/biz/device")
-public class TwinDeviceController extends BaseController{
+public class TwinDeviceController extends BaseController {
     @Resource
     private ITwinDeviceService twinDeviceService;
 
@@ -43,8 +36,7 @@ public class TwinDeviceController extends BaseController{
     @ApiOperation("查询设备管理列表")
     @PreAuthorize("@ss.hasPermi('biz:device:list')")
     @GetMapping("/list")
-    public TableDataInfo list(TwinDevice twinDevice)
-    {
+    public TableDataInfo list(TwinDevice twinDevice) {
         startPage();
         List<TwinDevice> list = twinDeviceService.selectTwinDeviceList(twinDevice);
         return getDataTable(list);
@@ -57,8 +49,7 @@ public class TwinDeviceController extends BaseController{
     @PreAuthorize("@ss.hasPermi('biz:device:export')")
     @Log(title = "设备管理", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
-    public void export(HttpServletResponse response, TwinDevice twinDevice)
-    {
+    public void export(HttpServletResponse response, TwinDevice twinDevice) {
         List<TwinDevice> list = twinDeviceService.selectTwinDeviceList(twinDevice);
         ExcelUtil<TwinDevice> util = new ExcelUtil<TwinDevice>(TwinDevice.class);
         util.exportExcel(response, list, "设备管理数据");
@@ -70,8 +61,7 @@ public class TwinDeviceController extends BaseController{
     @ApiOperation("获取设备管理详细信息")
     @PreAuthorize("@ss.hasPermi('biz:device:query')")
     @GetMapping(value = "/{deviceId}")
-    public AjaxResult getInfo(@PathVariable("deviceId") Long deviceId)
-    {
+    public AjaxResult getInfo(@PathVariable("deviceId") Long deviceId) {
         return success(twinDeviceService.selectTwinDeviceByDeviceId(deviceId));
     }
 
@@ -82,8 +72,7 @@ public class TwinDeviceController extends BaseController{
     @PreAuthorize("@ss.hasPermi('biz:device:add')")
     @Log(title = "设备管理", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody TwinDevice twinDevice)
-    {
+    public AjaxResult add(@RequestBody TwinDevice twinDevice) {
         return toAjax(twinDeviceService.insertTwinDevice(twinDevice));
     }
 
@@ -94,8 +83,7 @@ public class TwinDeviceController extends BaseController{
     @PreAuthorize("@ss.hasPermi('biz:device:edit')")
     @Log(title = "设备管理", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody TwinDevice twinDevice)
-    {
+    public AjaxResult edit(@RequestBody TwinDevice twinDevice) {
         return toAjax(twinDeviceService.updateTwinDevice(twinDevice));
     }
 
@@ -105,9 +93,8 @@ public class TwinDeviceController extends BaseController{
     @ApiOperation("删除设备管理")
     @PreAuthorize("@ss.hasPermi('biz:device:remove')")
     @Log(title = "设备管理", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{deviceIds}")
-    public AjaxResult remove(@PathVariable Long[] deviceIds)
-    {
+    @DeleteMapping("/{deviceIds}")
+    public AjaxResult remove(@PathVariable Long[] deviceIds) {
         return toAjax(twinDeviceService.deleteTwinDeviceByDeviceIds(deviceIds));
     }
 }

+ 478 - 0
jjt-biz/src/main/java/com/jjt/biz/service/impl/BrokenYarnExportServiceImpl.java

@@ -0,0 +1,478 @@
+package com.jjt.biz.service.impl;
+
+import com.jjt.biz.domain.TwinDevice;
+import com.jjt.biz.service.ITwinDeviceService;
+import com.jjt.calc.domain.TwinCalcDay;
+import com.jjt.calc.domain.TwinCalcStop;
+import com.jjt.calc.service.ITwinCalcDayService;
+import com.jjt.calc.service.ITwinCalcStopService;
+import com.jjt.common.utils.DateUtils;
+import com.jjt.utils.Tools;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xddf.usermodel.chart.*;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * 断纱分析服务
+ *
+ * @author wukai
+ * @date 2024/5/4 20:35
+ */
+@Service
+@Slf4j
+public class BrokenYarnExportServiceImpl {
+    @Resource
+    private ITwinCalcDayService twinCalcDayService;
+    @Resource
+    private ITwinCalcStopService stopService;
+    @Resource
+    private ITwinDeviceService deviceService;
+
+    /**
+     * 基本信息表
+     *
+     * @param wb
+     * @param localDate
+     */
+    public void base(XSSFWorkbook wb, LocalDate localDate) {
+        LocalDate st = localDate.minusDays(7);
+        Date sd = Date.from(st.atStartOfDay(ZoneOffset.of("+8")).toInstant());
+        Date ed = Date.from(localDate.atStartOfDay(ZoneOffset.of("+8")).toInstant());
+        //7天数据
+        List<TwinCalcDay> dayList = twinCalcDayService.selectTwinCalcDayListByTime(sd, ed);
+        //当天数据
+        List<TwinCalcDay> curr = dayList.stream().filter(day -> day.getTime().equals(ed)).collect(Collectors.toList());
+        //1.基本信息
+        XSSFSheet sheet = wb.getSheetAt(0);
+        Cell cell = sheet.getRow(1).getCell(1);
+        cell.setCellValue(localDate);
+
+        //计算开机设备数量,A班开机时间+B班开机时间大于0的。
+        //按设备分组
+        Map<Long, List<TwinCalcDay>> deviceGr = curr.stream().collect(Collectors.groupingBy(TwinCalcDay::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+        int openDevices = 0;
+        BigDecimal totOpenTimes = BigDecimal.ZERO;
+        for (Map.Entry<Long, List<TwinCalcDay>> entry1 : deviceGr.entrySet()) {
+            TwinCalcDay tempDay = new TwinCalcDay(ed);
+            tempDay.calcDays(entry1.getValue());
+            BigDecimal tot = tempDay.getOpenTimeA().add(tempDay.getOpenTimeB());
+            if (tot.intValue() > 0) {
+                openDevices++;
+            }
+            totOpenTimes = totOpenTimes.add(tot);
+        }
+
+        cell = sheet.getRow(2).getCell(1);
+        cell.setCellValue(openDevices);
+        BigDecimal avgOpenTimes = totOpenTimes.divide(BigDecimal.valueOf(openDevices), 0, RoundingMode.HALF_UP);
+
+        cell = sheet.getRow(3).getCell(1);
+        cell.setCellValue(Tools.convertHMS(totOpenTimes.longValue()));
+        cell = sheet.getRow(4).getCell(1);
+        cell.setCellValue(Tools.convertHMS(avgOpenTimes.longValue()));
+
+        drawLine(sheet, dayList);
+    }
+
+    private final String[] stopStr = {"停经片停机", "CCD停机(相机号+断纱/故障)", "人工停机", "断电停机", "设备故障停机", "落布米数达到停机", "盘头剩余圈数达到停机"};
+
+    public void stop(XSSFWorkbook wb, LocalDate localDate) {
+        CreationHelper creationHelper = wb.getCreationHelper();
+        CellStyle percentStyle = wb.createCellStyle();
+        percentStyle.setDataFormat(creationHelper.createDataFormat().getFormat("0.00%"));
+        CellStyle p2 = wb.createCellStyle();
+        p2.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+        CellStyle dateStyle = wb.createCellStyle();
+        dateStyle.setDataFormat(creationHelper.createDataFormat().getFormat(DateUtils.YYYY_MM_DD));
+        CellStyle timeStyle = wb.createCellStyle();
+        timeStyle.setDataFormat(creationHelper.createDataFormat().getFormat(DateUtils.YYYY_MM_DD_HH_MM_SS));
+        CellStyle rightStyle = wb.createCellStyle();
+        rightStyle.setAlignment(HorizontalAlignment.RIGHT);
+        LocalDateTime sdt = LocalDateTime.of(localDate, LocalTime.of(7, 0));
+        LocalDateTime edt = LocalDateTime.of(localDate.plusDays(1), LocalTime.of(6, 59, 59));
+        Date sTime = Date.from(sdt.atZone(ZoneId.systemDefault()).toInstant());
+        Date eTime = Date.from(edt.atZone(ZoneId.systemDefault()).toInstant());
+        List<TwinCalcStop> stopList = stopService.selectTwinCalcStopListByDate(sTime, eTime);
+        Map<Integer, List<TwinCalcStop>> stopDeviceGroup = stopList.stream().collect(Collectors.groupingBy(TwinCalcStop::getStopType, LinkedHashMap::new, Collectors.toList()));
+
+        List<TwinCalcStop> yarnStopList = stopList.stream().filter(stop -> stop.getStopType().equals(2)).collect(Collectors.toList());
+        //1停经片停机,2-CCD停机(相机号+断纱/故障),3-人工停机,4-断电停机,5-设备故障停机,6-落布米数达到停机,7-盘头剩余圈数达到停机
+        //停机次数
+        Integer[] stopNum = new Integer[7];
+        Integer totalNum = 0;
+        //停机时间
+        Long[] stopTime = new Long[7];
+        Arrays.fill(stopTime, 0L);
+        AtomicReference<Long> totalTime = new AtomicReference<>(0L);
+        for (Map.Entry<Integer, List<TwinCalcStop>> entry : stopDeviceGroup.entrySet()) {
+            Integer stopType = entry.getKey();
+            int pos = stopType - 1;
+            List<TwinCalcStop> stops = entry.getValue();
+            stopNum[pos] = stops.size();
+            totalNum += stops.size();
+            stops.forEach(stop -> {
+                Date stopEndTime = stop.getEndTime();
+                // 调整停机时间
+                if (stop.getEndTime().compareTo(eTime) > 0) {
+                    stopEndTime = eTime;
+                }
+                Date stopStartTime = stop.getStartTime();
+                //调整开机时间
+                if (stop.getStartTime().compareTo(sTime) < 0) {
+                    stopStartTime = sTime;
+                }
+                long t = (stopEndTime.getTime() - stopStartTime.getTime()) / 1000;
+                stopTime[pos] += t;
+                totalTime.updateAndGet(v -> v + t);
+            });
+        }
+        XSSFSheet sheet = wb.getSheetAt(1);
+        for (int i = 0; i < stopStr.length; i++) {
+            Row row = sheet.createRow(i + 2);
+            Cell[] cells = new Cell[5];
+            for (int j = 0; j < cells.length; j++) {
+                cells[j] = row.createCell(j);
+            }
+            cells[0].setCellValue(stopStr[i]);
+            cells[1].setCellValue(stopNum[i]);
+            cells[2].setCellValue(Tools.convertHMS(stopTime[i]));
+            cells[2].setCellStyle(rightStyle);
+            float percentNum = BigDecimal.valueOf(stopNum[i]).divide(BigDecimal.valueOf(totalNum), 4, RoundingMode.HALF_UP).floatValue();
+            float percentTimes = BigDecimal.valueOf(stopTime[i]).divide(BigDecimal.valueOf(totalTime.get()), 4, RoundingMode.HALF_UP).floatValue();
+            cells[3].setCellValue(percentNum);
+            cells[3].setCellStyle(percentStyle);
+            cells[4].setCellValue(percentTimes);
+            cells[4].setCellStyle(percentStyle);
+        }
+        int[] pos = {0, 10, 5, 30};
+        int[] data = {2, 8, 3, 3};
+        int[] xdata = {2, 8, 0, 0};
+        drawBar(sheet, "停机次数占比", "停机原因", pos, xdata, data,BarDirection.BAR);
+        int[] pos1 = {6, 10, 16, 30};
+        int[] data1 = {2, 8, 4, 4};
+        drawBar(sheet, "停机时长占比", "停机原因", pos1, xdata, data1,BarDirection.BAR);
+
+        brokenYarn(wb, yarnStopList, stopTime[1], stopList.size());
+        yarn(wb, yarnStopList, dateStyle, timeStyle, p2);
+
+    }
+
+    public void top(XSSFWorkbook wb, LocalDate localDate) {
+        LocalDateTime sdt = LocalDateTime.of(localDate.minusDays(6), LocalTime.of(7, 0));
+        LocalDateTime edt = LocalDateTime.of(localDate.plusDays(1), LocalTime.of(6, 59, 59));
+        Date sTime = Date.from(sdt.atZone(ZoneId.systemDefault()).toInstant());
+        Date eTime = Date.from(edt.atZone(ZoneId.systemDefault()).toInstant());
+        List<TwinCalcStop> yarnStopList = stopService.selectTwinCalcStopListByDate(sTime, eTime, 2);
+
+        yarnStopList.forEach(stop -> {
+            if (stop.getHour() < 7) {
+                Date d = stop.getDataDate();
+                stop.setDataDate(DateUtils.addDays(d, -1));
+            }
+        });
+        Map<Long, Map<Date, Long>> weekData = yarnStopList.stream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, Collectors.groupingBy(TwinCalcStop::getDataDate, Collectors.counting())));
+        List<Date> dateList = yarnStopList.stream().map(TwinCalcStop::getDataDate).distinct().sorted().collect(Collectors.toList());
+        Map<Long, Long> collect = yarnStopList.parallelStream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, Collectors.counting()));
+        Map<Long, Long> sortedMap = collect.entrySet()
+                .stream()
+                .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        Map.Entry::getValue,
+                        (e1, e2) -> e1,
+                        LinkedHashMap::new));
+        Map<Long, TwinDevice> deviceMap = deviceService.deviceMap();
+        XSSFSheet sheet = wb.getSheetAt(3);
+        Cell cell = sheet.getRow(0).getCell(0);
+        for (int i = 0; i < dateList.size(); i++) {
+            Cell dateCell = sheet.getRow(1).getCell(i + 2);
+            dateCell.setCellValue(dateList.get(i));
+        }
+
+        String title = "断纱停机周TOP排名\n(" + DateUtils.parseTimeToStr(sdt) + "至" + DateUtils.parseTimeToStr(edt) + ")";
+        cell.setCellValue(title);
+        int rn = 2;
+        for (Map.Entry<Long, Long> entry : sortedMap.entrySet()) {
+            Long deviceId = entry.getKey();
+            Row r1 = sheet.createRow(rn);
+            Cell[] cells = new Cell[9];
+            for (int j = 0; j < cells.length; j++) {
+                cells[j] = r1.createCell(j);
+            }
+            cells[0].setCellValue(deviceMap.get(deviceId).getDeviceName());
+            cells[1].setCellValue(entry.getValue());
+            Map<Date, Long> xx = weekData.get(deviceId);
+            for (int i = 0; i < dateList.size(); i++) {
+                Long v = xx.get(dateList.get(i));
+                if (v == null) {
+                    v = 0L;
+                }
+                cells[i + 2].setCellValue(v);
+            }
+            rn++;
+        }
+
+        int[] pos = {10, 2, 16, rn};
+        int[] data = {2, rn, 1, 1};
+        int[] xdata = {2, rn, 0, 0};
+        drawBar(sheet, "断纱停机周TOP", "设备名称", pos, xdata, data,BarDirection.BAR);
+        drawTopLine(sheet, rn);
+    }
+
+    private void drawTopLine(XSSFSheet sheet, int endRn) {
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        // 2 左侧距离单元格个数, 4 顶部距离单元格个数, 7 左侧距离单元格个数, 26 顶部距离单元格个数
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, endRn + 1, 15, endRn + 26);
+        XSSFChart chart = drawing.createChart(anchor);
+        // 图表标题
+        chart.setTitleText("断纱停机趋势");
+        // 图例是否覆盖标题
+        chart.setTitleOverlay(false);
+        XDDFChartLegend legend = chart.getOrAddLegend();
+        // 图例位置:上下左右
+        legend.setPosition(LegendPosition.BOTTOM);
+        // 创建x轴
+        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.LEFT);
+        // X轴标题
+        bottomAxis.setTitle("日期");
+        // y轴标题
+        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
+        leftAxis.setTitle("单位:次");
+        // y轴数据
+        // 分类轴标(X轴)数据,单元格范围位置[0, 0]到[0, 6]
+        XDDFDataSource<String> xData = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(1, 1, 2, 8));
+        // 创建y轴
+        XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
+        for (int i = 2; i < endRn; i++) {
+            // 创建y轴
+            XDDFNumericalDataSource<Double> yData = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(i, i, 2, 8));
+            XDDFLineChartData.Series series = (XDDFLineChartData.Series) data.addSeries(xData, yData);
+            // 图例标题
+            series.setTitle(sheet.getRow(i).getCell(0).getStringCellValue(), null);
+            // 线条样式:true平滑曲线,false折线
+            series.setSmooth(true);
+            // 点的样式
+//            series.setMarkerStyle(MarkerStyle.CIRCLE);
+        }
+        chart.plot(data);
+    }
+
+    private void drawLine(XSSFSheet sheet, List<TwinCalcDay> dayList) {
+        Map<Date, List<TwinCalcDay>> dayGroup = dayList.stream().collect(Collectors.groupingBy(TwinCalcDay::getTime, LinkedHashMap::new, Collectors.toList()));
+        List<TwinCalcDay> jdl = new ArrayList<>();
+        for (Map.Entry<Date, List<TwinCalcDay>> entry : dayGroup.entrySet()) {
+            TwinCalcDay day = new TwinCalcDay(entry.getKey());
+            List<TwinCalcDay> days = entry.getValue();
+            day.calcDays(days);
+            jdl.add(day);
+        }
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        // 2 左侧距离单元格个数, 4 顶部距离单元格个数, 7 左侧距离单元格个数, 26 顶部距离单元格个数
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 7, 12, 25);
+        XSSFChart chart = drawing.createChart(anchor);
+        // 图表标题
+        chart.setTitleText("稼动率周曲线");
+        // 图例是否覆盖标题
+        chart.setTitleOverlay(false);
+        XDDFChartLegend legend = chart.getOrAddLegend();
+        // 图例位置:上下左右
+        legend.setPosition(LegendPosition.TOP);
+        // 创建x轴
+        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
+        // X轴标题
+        bottomAxis.setTitle("日期");
+        // y轴标题
+        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
+        leftAxis.setTitle("单位:%");
+
+//            // y轴数据
+        List<String> xtd = new ArrayList<>();
+        List<Double> tef = new ArrayList<>();
+        List<Double> aef = new ArrayList<>();
+        List<Double> bef = new ArrayList<>();
+        jdl.forEach(day -> {
+            aef.add(day.getEfficiencyA().multiply(BigDecimal.valueOf(100)).doubleValue());
+            bef.add(day.getEfficiencyB().multiply(BigDecimal.valueOf(100)).doubleValue());
+            tef.add(day.getEfficiency().multiply(BigDecimal.valueOf(100)).doubleValue());
+            xtd.add(DateUtils.parseDateToStr("MM-dd", day.getTime()));
+        });
+        XDDFDataSource<String> xdate = XDDFDataSourcesFactory.fromArray(xtd.toArray(new String[0]));
+        XDDFNumericalDataSource<Double> tt = XDDFDataSourcesFactory.fromArray(tef.toArray(new Double[0]));
+        XDDFNumericalDataSource<Double> aa = XDDFDataSourcesFactory.fromArray(aef.toArray(new Double[0]));
+        XDDFNumericalDataSource<Double> bb = XDDFDataSourcesFactory.fromArray(bef.toArray(new Double[0]));
+        // 创建y轴
+        XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
+        XDDFLineChartData.Series series = (XDDFLineChartData.Series) data.addSeries(xdate, tt);
+        // 图例标题
+        series.setTitle("总稼动率", null);
+        // 线条样式:true平滑曲线,false折线
+        series.setSmooth(true);
+        // 点的样式
+        series.setMarkerStyle(MarkerStyle.CIRCLE);
+
+        XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(xdate, aa);
+        series1.setTitle("A班稼动率", null);
+        series1.setSmooth(true);
+        series1.setMarkerStyle(MarkerStyle.CIRCLE);
+        XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) data.addSeries(xdate, bb);
+        series2.setTitle("B班稼动率", null);
+        series2.setSmooth(true);
+        series2.setMarkerStyle(MarkerStyle.CIRCLE);
+        chart.plot(data);
+    }
+
+    public void drawBar(XSSFSheet sheet, String title, String xTitle, int[] pos, int[] xdata, int[] data,BarDirection barDirection) {
+        // 创建一个画布
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        // 前四个默认0,[0,5]:从0列5行开始;[7,26]:到7列26行结束
+        // 默认宽度(14-8)*12
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, pos[0], pos[1], pos[2], pos[3]);
+        // 创建一个chart对象
+        XSSFChart chart = drawing.createChart(anchor);
+        // 标题
+        chart.setTitleText(title);
+        // 标题覆盖
+        chart.setTitleOverlay(false);
+
+        // 图例位置
+//        XDDFChartLegend legend = chart.getOrAddLegend();
+//        legend.setPosition(LegendPosition.TOP);
+
+        // 分类轴标(X轴),标题位置
+        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
+        bottomAxis.setTitle(xTitle);
+        // 值(Y轴)轴,标题位置
+        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
+//        leftAxis.setTitle("停机次数占比");
+
+        // CellRangeAddress(起始行号,终止行号, 起始列号,终止列号)
+        // 分类轴标(X轴)数据,单元格范围位置[0, 0]到[0, 6]
+        XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(xdata[0], xdata[1], xdata[2], xdata[3]));
+        // XDDFCategoryDataSource countries = XDDFDataSourcesFactory.fromArray(new String[] {"俄罗斯","加拿大","美国","中国","巴西","澳大利亚","印度"});
+        // 数据1,单元格范围位置[1, 0]到[1, 6]
+        XDDFNumericalDataSource<Double> area = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(data[0], data[1], data[2], data[3]));
+        // XDDFNumericalDataSource<Integer> area = XDDFDataSourcesFactory.fromArray(new Integer[] {17098242,9984670,9826675,9596961,8514877,7741220,3287263});
+
+        // bar:条形图,
+        XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
+
+        leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
+        // 设置为可变颜色
+        bar.setVaryColors(true);// 如果需要设置成自己想要的颜色,这里可变颜色要设置成false
+        // 条形图方向,纵向/横向:纵向
+        bar.setBarDirection(barDirection);
+
+        // 图表加载数据,条形图1
+        XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(countries, area);
+//        // 条形图例标题
+        series1.setTitle("百分比", null);
+//			XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(PresetColor.RED));
+//			// 条形图,填充颜色
+//			series1.setFillProperties(fill);
+
+        // 绘制
+        chart.plot(bar);
+    }
+
+    public void brokenYarn(XSSFWorkbook wb, List<TwinCalcStop> yarnStopList, long time, int size) {
+        //3.断纱分析
+        Sheet sheet = wb.getSheetAt(2);
+        int yst = yarnStopList.size();
+        Cell cell = sheet.getRow(1).getCell(1);
+        cell.setCellValue(yst);
+        Map<Long, List<TwinCalcStop>> deviceGroup = yarnStopList.stream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+        cell = sheet.getRow(2).getCell(1);
+        cell.setCellValue(deviceGroup.size());
+        float bl = 0f;
+        long aavg = 0;
+        if (yst > 0) {
+            bl = BigDecimal.valueOf(yst).divide(BigDecimal.valueOf(size), 4, RoundingMode.HALF_UP).floatValue();
+            aavg = time / yst;
+        }
+        cell = sheet.getRow(3).getCell(1);
+        cell.setCellValue(bl);
+
+        cell = sheet.getRow(4).getCell(1);
+        cell.setCellValue(Tools.convertHMS(time));
+        cell = sheet.getRow(5).getCell(1);
+        cell.setCellValue(Tools.convertHMS(aavg));
+    }
+
+    public void yarn(XSSFWorkbook wb, List<TwinCalcStop> yarnStopList, CellStyle dateStyle, CellStyle timeStyle, CellStyle p2) {
+        Map<Long, TwinDevice> deviceMap = deviceService.deviceMap();
+        //5.并发断纱分析
+        Sheet sheet = wb.getSheetAt(4);
+        int rowNum = 2;
+        int rn = 2;
+        yarnStopList.sort(Comparator.comparing(TwinCalcStop::getDataDate).thenComparing(TwinCalcStop::getHour));
+        Map<Date, Map<Integer, List<TwinCalcStop>>> stopHourGroup = yarnStopList.stream().collect(
+                Collectors.groupingBy(TwinCalcStop::getDataDate, LinkedHashMap::new,
+                        Collectors.groupingBy(TwinCalcStop::getHour, LinkedHashMap::new, Collectors.toList())));
+        for (Map.Entry<Date, Map<Integer, List<TwinCalcStop>>> entry : stopHourGroup.entrySet()) {
+            Map<Integer, List<TwinCalcStop>> map = entry.getValue();
+            for (Map.Entry<Integer, List<TwinCalcStop>> entry1 : map.entrySet()) {
+                List<TwinCalcStop> stops = entry1.getValue();
+                Row row = sheet.createRow(rowNum);
+                Cell[] cells = new Cell[7];
+                for (int j = 0; j < cells.length; j++) {
+                    cells[j] = row.createCell(j);
+                }
+                cells[0].setCellValue(entry.getKey());
+                cells[0].setCellStyle(dateStyle);
+                cells[1].setCellValue(entry1.getKey());
+                Map<Long, List<TwinCalcStop>> yarnDeviceGroup = stops.stream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+                cells[2].setCellValue(yarnDeviceGroup.size());
+                int num = stops.size();
+                cells[3].setCellValue(num);
+                //0.最小,1.最大,2.总时间
+                final long[] time = {999999L, 0, 0};
+                //6.设备断纱停机详情
+                Sheet sheet6 = wb.getSheetAt(5);
+                for (TwinCalcStop stop : stops) {
+                    long t = (stop.getEndTime().getTime() - stop.getStartTime().getTime()) / 1000;
+                    if (t < time[0]) {
+                        time[0] = t;
+                    }
+                    if (t > time[1]) {
+                        time[1] = t;
+                    }
+                    time[2] += t;
+
+                    Row r1 = sheet6.createRow(rn);
+                    Cell[] cs = new Cell[6];
+                    for (int j = 0; j < cs.length; j++) {
+                        cs[j] = r1.createCell(j);
+                    }
+                    cs[0].setCellValue(stop.getDataDate());
+                    cs[0].setCellStyle(dateStyle);
+                    cs[1].setCellValue(stop.getHour());
+                    cs[2].setCellValue(deviceMap.get(stop.getDeviceId()).getDeviceName());
+                    cs[3].setCellValue(stopStr[stop.getStopType() - 1]);
+                    cs[4].setCellValue(stop.getStartTime());
+                    cs[4].setCellStyle(timeStyle);
+                    cs[5].setCellValue(stop.getEndTime());
+                    cs[5].setCellStyle(timeStyle);
+                    rn++;
+                }
+
+                cells[4].setCellValue(time[1]);
+                cells[5].setCellValue(time[0]);
+                float avg = BigDecimal.valueOf(time[2]).divide(BigDecimal.valueOf(num), 2, RoundingMode.HALF_UP).floatValue();
+                cells[6].setCellValue(avg);
+                cells[6].setCellStyle(p2);
+                rowNum++;
+            }
+        }
+    }
+}

+ 9 - 0
jjt-biz/src/main/java/com/jjt/ws/mapper/TwinWorkshopCalcMapper.java

@@ -4,6 +4,7 @@ import java.util.Date;
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.jjt.ws.domain.TwinWorkshopCalc;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 能源统计Mapper接口
@@ -67,4 +68,12 @@ public interface TwinWorkshopCalcMapper extends BaseMapper<TwinWorkshopCalc>
      * @param date 日期
      */
     void deleteTwinWorkshopCalcByDate(Date date);
+    /**
+     * 按月查询
+     *
+     * @param wsId  wsid
+     * @param month 月份
+     * @return 结果
+     */
+    List<TwinWorkshopCalc> selectTwinWorkshopCalcListByMonth(@Param("wsId") Long wsId, @Param("month") String month);
 }

+ 20 - 12
jjt-biz/src/main/java/com/jjt/ws/mapper/TwinWorkshopMapper.java

@@ -7,7 +7,7 @@ import com.jjt.ws.domain.TwinWorkshopEnergy;
 
 /**
  * 车间管理Mapper接口
- * 
+ *
  * @author wukai
  * @date 2025-01-17
  */
@@ -15,7 +15,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 {
     /**
      * 查询车间管理
-     * 
+     *
      * @param wsId 车间管理主键
      * @return 车间管理
      */
@@ -23,7 +23,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 查询车间管理列表
-     * 
+     *
      * @param twinWorkshop 车间管理
      * @return 车间管理集合
      */
@@ -31,7 +31,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 新增车间管理
-     * 
+     *
      * @param twinWorkshop 车间管理
      * @return 结果
      */
@@ -39,7 +39,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 修改车间管理
-     * 
+     *
      * @param twinWorkshop 车间管理
      * @return 结果
      */
@@ -47,7 +47,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 删除车间管理
-     * 
+     *
      * @param wsId 车间管理主键
      * @return 结果
      */
@@ -55,7 +55,7 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 批量删除车间管理
-     * 
+     *
      * @param wsIds 需要删除的数据主键集合
      * @return 结果
      */
@@ -63,26 +63,34 @@ public interface TwinWorkshopMapper extends BaseMapper<TwinWorkshop>
 
     /**
      * 批量删除车间能源管理
-     * 
+     *
      * @param wsIds 需要删除的数据主键集合
      * @return 结果
      */
     public int deleteTwinWorkshopEnergyByWsIds(Long[] wsIds);
-    
+
     /**
      * 批量新增车间能源管理
-     * 
+     *
      * @param twinWorkshopEnergyList 车间能源管理列表
      * @return 结果
      */
     public int batchTwinWorkshopEnergy(List<TwinWorkshopEnergy> twinWorkshopEnergyList);
-    
+
 
     /**
      * 通过车间管理主键删除车间能源管理信息
-     * 
+     *
      * @param wsId 车间管理ID
      * @return 结果
      */
     public int deleteTwinWorkshopEnergyByWsId(Long wsId);
+
+    /**
+     * 查询车间管理
+     *
+     * @param wsCode 车间编码
+     * @return 车间管理
+     */
+    TwinWorkshop selectTwinWorkshopByWsCode(String wsCode);
 }

+ 17 - 0
jjt-biz/src/main/java/com/jjt/ws/service/ITwinWorkshopCalcService.java

@@ -67,4 +67,21 @@ public interface ITwinWorkshopCalcService {
      * @return 结果
      */
     int calc(Date date);
+    /**
+     * 根据日期查询
+     *
+     * @param wsId wsid
+     * @param date 日期
+     * @return 结果
+     */
+    List<TwinWorkshopCalc> selectTwinWorkshopCalcListByDate(Long wsId, Date date);
+
+    /**
+     * 按月查询
+     *
+     * @param wsId wsid
+     * @param date 月份
+     * @return 结果
+     */
+    List<TwinWorkshopCalc> selectTwinWorkshopCalcListByMonth(Long wsId, String date);
 }

+ 7 - 0
jjt-biz/src/main/java/com/jjt/ws/service/ITwinWorkshopService.java

@@ -57,4 +57,11 @@ public interface ITwinWorkshopService {
      * @return 结果
      */
     public int deleteTwinWorkshopByWsId(Long wsId);
+    /**
+     * 查询车间管理
+     *
+     * @param wsCode 车间编码
+     * @return 车间管理
+     */
+    public TwinWorkshop selectTwinWorkshopByWsCode(String wsCode);
 }

+ 27 - 0
jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopCalcServiceImpl.java

@@ -146,4 +146,31 @@ public class TwinWorkshopCalcServiceImpl implements ITwinWorkshopCalcService {
         }
         return 1;
     }
+
+    /**
+     * 根据日期查询
+     *
+     * @param wsId wsid
+     * @param date 日期
+     * @return 结果
+     */
+    @Override
+    public List<TwinWorkshopCalc> selectTwinWorkshopCalcListByDate(Long wsId, Date date) {
+        TwinWorkshopCalc search = new TwinWorkshopCalc();
+        search.setWsId(wsId);
+        search.setDataDate(date);
+        return selectTwinWorkshopCalcList(search);
+    }
+
+    /**
+     * 按月查询
+     *
+     * @param wsId  wsid
+     * @param month 月份
+     * @return 结果
+     */
+    @Override
+    public List<TwinWorkshopCalc> selectTwinWorkshopCalcListByMonth(Long wsId, String month) {
+        return twinWorkshopCalcMapper.selectTwinWorkshopCalcListByMonth(wsId, month);
+    }
 }

+ 11 - 0
jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopServiceImpl.java

@@ -118,4 +118,15 @@ public class TwinWorkshopServiceImpl implements ITwinWorkshopService {
             }
         }
     }
+
+    /**
+     * 查询车间管理
+     *
+     * @param wsCode 车间编码
+     * @return 车间管理
+     */
+    @Override
+    public TwinWorkshop selectTwinWorkshopByWsCode(String wsCode) {
+        return twinWorkshopMapper.selectTwinWorkshopByWsCode(wsCode);
+    }
 }

+ 104 - 68
jjt-biz/src/main/resources/mapper/calc/TwinCalcDayMapper.xml

@@ -1,80 +1,114 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper
-PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.jjt.calc.mapper.TwinCalcDayMapper">
 
     <resultMap type="TwinCalcDay" id="TwinCalcDayResult">
-        <result property="id"    column="ID"    />
-        <result property="time"    column="TIME"    />
-        <result property="deviceId"    column="DEVICE_ID"    />
-        <result property="length"    column="LENGTH"    />
-        <result property="weight"    column="WEIGHT"    />
-        <result property="efficiency"    column="EFFICIENCY"    />
-        <result property="kwh"    column="KWH"    />
-        <result property="alarm"    column="ALARM"    />
-        <result property="lengthA"    column="LENGTH_A"    />
-        <result property="weightA"    column="WEIGHT_A"    />
-        <result property="efficiencyA"    column="EFFICIENCY_A"    />
-        <result property="openTimeA"    column="OPEN_TIME_A"    />
-        <result property="closeTimeA"    column="CLOSE_TIME_A"    />
-        <result property="kwhA"    column="KWH_A"    />
-        <result property="stop1A"    column="STOP1_A"    />
-        <result property="stop2A"    column="STOP2_A"    />
-        <result property="stop3A"    column="STOP3_A"    />
-        <result property="lengthB"    column="LENGTH_B"    />
-        <result property="weightB"    column="WEIGHT_B"    />
-        <result property="efficiencyB"    column="EFFICIENCY_B"    />
-        <result property="openTimeB"    column="OPEN_TIME_B"    />
-        <result property="closeTimeB"    column="CLOSE_TIME_B"    />
-        <result property="kwhB"    column="KWH_B"    />
-        <result property="stop1B"    column="STOP1_B"    />
-        <result property="stop2B"    column="STOP2_B"    />
-        <result property="stop3B"    column="STOP3_B"    />
-        <result property="createdBy"    column="CREATED_BY"    />
-        <result property="createdTime"    column="CREATED_TIME"    />
-        <result property="updatedBy"    column="UPDATED_BY"    />
-        <result property="updatedTime"    column="UPDATED_TIME"    />
-        <result property="remark"    column="REMARK"    />
+        <result property="id" column="ID"/>
+        <result property="time" column="TIME"/>
+        <result property="deviceId" column="DEVICE_ID"/>
+        <result property="length" column="LENGTH"/>
+        <result property="weight" column="WEIGHT"/>
+        <result property="efficiency" column="EFFICIENCY"/>
+        <result property="kwh" column="KWH"/>
+        <result property="alarm" column="ALARM"/>
+        <result property="lengthA" column="LENGTH_A"/>
+        <result property="weightA" column="WEIGHT_A"/>
+        <result property="efficiencyA" column="EFFICIENCY_A"/>
+        <result property="openTimeA" column="OPEN_TIME_A"/>
+        <result property="closeTimeA" column="CLOSE_TIME_A"/>
+        <result property="kwhA" column="KWH_A"/>
+        <result property="stop1A" column="STOP1_A"/>
+        <result property="stop2A" column="STOP2_A"/>
+        <result property="stop3A" column="STOP3_A"/>
+        <result property="lengthB" column="LENGTH_B"/>
+        <result property="weightB" column="WEIGHT_B"/>
+        <result property="efficiencyB" column="EFFICIENCY_B"/>
+        <result property="openTimeB" column="OPEN_TIME_B"/>
+        <result property="closeTimeB" column="CLOSE_TIME_B"/>
+        <result property="kwhB" column="KWH_B"/>
+        <result property="stop1B" column="STOP1_B"/>
+        <result property="stop2B" column="STOP2_B"/>
+        <result property="stop3B" column="STOP3_B"/>
+        <result property="createdBy" column="CREATED_BY"/>
+        <result property="createdTime" column="CREATED_TIME"/>
+        <result property="updatedBy" column="UPDATED_BY"/>
+        <result property="updatedTime" column="UPDATED_TIME"/>
+        <result property="remark" column="REMARK"/>
     </resultMap>
 
     <sql id="selectTwinCalcDayVo">
-        select ID, TIME, DEVICE_ID, LENGTH, WEIGHT, EFFICIENCY, KWH, ALARM, LENGTH_A, WEIGHT_A, EFFICIENCY_A, OPEN_TIME_A, CLOSE_TIME_A, KWH_A, STOP1_A, STOP2_A, STOP3_A, LENGTH_B, WEIGHT_B, EFFICIENCY_B, OPEN_TIME_B, CLOSE_TIME_B, KWH_B, STOP1_B, STOP2_B, STOP3_B, CREATED_BY, CREATED_TIME, UPDATED_BY, UPDATED_TIME, REMARK from TWIN_CALC_DAY
+        select *
+        from (select A.ID,
+                     A.TIME,
+                     A.DEVICE_ID,
+                     A.LENGTH,
+                     A.WEIGHT,
+                     A.EFFICIENCY,
+                     A.KWH,
+                     A.ALARM,
+                     A.LENGTH_A,
+                     A.WEIGHT_A,
+                     A.EFFICIENCY_A,
+                     A.OPEN_TIME_A,
+                     A.CLOSE_TIME_A,
+                     A.KWH_A,
+                     A.STOP1_A,
+                     A.STOP2_A,
+                     A.STOP3_A,
+                     A.LENGTH_B,
+                     A.WEIGHT_B,
+                     A.EFFICIENCY_B,
+                     A.OPEN_TIME_B,
+                     A.CLOSE_TIME_B,
+                     A.KWH_B,
+                     A.STOP1_B,
+                     A.STOP2_B,
+                     A.STOP3_B,
+                     A.CREATED_BY,
+                     A.CREATED_TIME,
+                     A.UPDATED_BY,
+                     A.UPDATED_TIME,
+                     B.device_name REMARK
+              from twin_calc_day a,
+                   twin_device b
+              where a.device_id = b.device_id) a
     </sql>
 
     <select id="selectTwinCalcDayList" parameterType="TwinCalcDay" resultMap="TwinCalcDayResult">
         <include refid="selectTwinCalcDayVo"/>
         <where>
-            <if test="time != null "> and TIME = #{time}</if>
-            <if test="deviceId != null "> and DEVICE_ID = #{deviceId}</if>
-            <if test="length != null "> and LENGTH = #{length}</if>
-            <if test="weight != null "> and WEIGHT = #{weight}</if>
-            <if test="efficiency != null "> and EFFICIENCY = #{efficiency}</if>
-            <if test="kwh != null "> and KWH = #{kwh}</if>
-            <if test="alarm != null "> and ALARM = #{alarm}</if>
-            <if test="lengthA != null "> and LENGTH_A = #{lengthA}</if>
-            <if test="weightA != null "> and WEIGHT_A = #{weightA}</if>
-            <if test="efficiencyA != null "> and EFFICIENCY_A = #{efficiencyA}</if>
-            <if test="openTimeA != null "> and OPEN_TIME_A = #{openTimeA}</if>
-            <if test="closeTimeA != null "> and CLOSE_TIME_A = #{closeTimeA}</if>
-            <if test="kwhA != null "> and KWH_A = #{kwhA}</if>
-            <if test="stop1A != null "> and STOP1_A = #{stop1A}</if>
-            <if test="stop2A != null "> and STOP2_A = #{stop2A}</if>
-            <if test="stop3A != null "> and STOP3_A = #{stop3A}</if>
-            <if test="lengthB != null "> and LENGTH_B = #{lengthB}</if>
-            <if test="weightB != null "> and WEIGHT_B = #{weightB}</if>
-            <if test="efficiencyB != null "> and EFFICIENCY_B = #{efficiencyB}</if>
-            <if test="openTimeB != null "> and OPEN_TIME_B = #{openTimeB}</if>
-            <if test="closeTimeB != null "> and CLOSE_TIME_B = #{closeTimeB}</if>
-            <if test="kwhB != null "> and KWH_B = #{kwhB}</if>
-            <if test="stop1B != null "> and STOP1_B = #{stop1B}</if>
-            <if test="stop2B != null "> and STOP2_B = #{stop2B}</if>
-            <if test="stop3B != null "> and STOP3_B = #{stop3B}</if>
-            <if test="createdBy != null  and createdBy != ''"> and CREATED_BY = #{createdBy}</if>
-            <if test="createdTime != null "> and CREATED_TIME = #{createdTime}</if>
-            <if test="updatedBy != null  and updatedBy != ''"> and UPDATED_BY = #{updatedBy}</if>
-            <if test="updatedTime != null "> and UPDATED_TIME = #{updatedTime}</if>
-            <if test="remark != null  and remark != ''"> and REMARK = #{remark}</if>
+            <if test="time != null ">and TIME = #{time}</if>
+            <if test="deviceId != null ">and DEVICE_ID = #{deviceId}</if>
+            <if test="length != null ">and LENGTH = #{length}</if>
+            <if test="weight != null ">and WEIGHT = #{weight}</if>
+            <if test="efficiency != null ">and EFFICIENCY = #{efficiency}</if>
+            <if test="kwh != null ">and KWH = #{kwh}</if>
+            <if test="alarm != null ">and ALARM = #{alarm}</if>
+            <if test="lengthA != null ">and LENGTH_A = #{lengthA}</if>
+            <if test="weightA != null ">and WEIGHT_A = #{weightA}</if>
+            <if test="efficiencyA != null ">and EFFICIENCY_A = #{efficiencyA}</if>
+            <if test="openTimeA != null ">and OPEN_TIME_A = #{openTimeA}</if>
+            <if test="closeTimeA != null ">and CLOSE_TIME_A = #{closeTimeA}</if>
+            <if test="kwhA != null ">and KWH_A = #{kwhA}</if>
+            <if test="stop1A != null ">and STOP1_A = #{stop1A}</if>
+            <if test="stop2A != null ">and STOP2_A = #{stop2A}</if>
+            <if test="stop3A != null ">and STOP3_A = #{stop3A}</if>
+            <if test="lengthB != null ">and LENGTH_B = #{lengthB}</if>
+            <if test="weightB != null ">and WEIGHT_B = #{weightB}</if>
+            <if test="efficiencyB != null ">and EFFICIENCY_B = #{efficiencyB}</if>
+            <if test="openTimeB != null ">and OPEN_TIME_B = #{openTimeB}</if>
+            <if test="closeTimeB != null ">and CLOSE_TIME_B = #{closeTimeB}</if>
+            <if test="kwhB != null ">and KWH_B = #{kwhB}</if>
+            <if test="stop1B != null ">and STOP1_B = #{stop1B}</if>
+            <if test="stop2B != null ">and STOP2_B = #{stop2B}</if>
+            <if test="stop3B != null ">and STOP3_B = #{stop3B}</if>
+            <if test="createdBy != null  and createdBy != ''">and CREATED_BY = #{createdBy}</if>
+            <if test="createdTime != null ">and CREATED_TIME = #{createdTime}</if>
+            <if test="updatedBy != null  and updatedBy != ''">and UPDATED_BY = #{updatedBy}</if>
+            <if test="updatedTime != null ">and UPDATED_TIME = #{updatedTime}</if>
+            <if test="remark != null  and remark != ''">and REMARK = #{remark}</if>
         </where>
     </select>
 
@@ -116,7 +150,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updatedBy != null">UPDATED_BY,</if>
             <if test="updatedTime != null">UPDATED_TIME,</if>
             <if test="remark != null">REMARK,</if>
-         </trim>
+        </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="time != null">#{time},</if>
             <if test="deviceId != null">#{deviceId},</if>
@@ -148,7 +182,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updatedBy != null">#{updatedBy},</if>
             <if test="updatedTime != null">#{updatedTime},</if>
             <if test="remark != null">#{remark},</if>
-         </trim>
+        </trim>
     </insert>
 
     <update id="updateTwinCalcDay" parameterType="TwinCalcDay">
@@ -189,7 +223,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </update>
 
     <delete id="deleteTwinCalcDayById" parameterType="Long">
-        delete from TWIN_CALC_DAY where ID = #{id}
+        delete
+        from TWIN_CALC_DAY
+        where ID = #{id}
     </delete>
 
     <delete id="deleteTwinCalcDayByIds" parameterType="String">
@@ -201,7 +237,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <delete id="delete4date" parameterType="Date">
         DELETE
         FROM TWIN_CALC_DAY
-        WHERE TIME =#{date}
+        WHERE TIME = #{date}
     </delete>
     <select id="selectTwinCalcDayListByMonth" resultMap="TwinCalcDayResult">
         <include refid="selectTwinCalcDayVo"/>

+ 5 - 0
jjt-biz/src/main/resources/mapper/ws/TwinWorkshopCalcMapper.xml

@@ -31,6 +31,11 @@
         <include refid="selectTwinWorkshopCalcVo"/>
         where CALC_ID = #{calcId}
     </select>
+    <select id="selectTwinWorkshopCalcListByMonth" resultType="com.jjt.ws.domain.TwinWorkshopCalc">
+        <include refid="selectTwinWorkshopCalcVo"/>
+        where ws_id=#{wsId} and FORMAT(DATA_DATE, 'yyyy-MM') = #{month}
+        order by DATA_DATE
+    </select>
 
     <insert id="insertTwinWorkshopCalc" parameterType="TwinWorkshopCalc" useGeneratedKeys="true" keyProperty="calcId">
         insert into TWIN_WORKSHOP_CALC

+ 5 - 0
jjt-biz/src/main/resources/mapper/ws/TwinWorkshopMapper.xml

@@ -50,6 +50,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         from TWIN_WORKSHOP_ENERGY
         where WS_ID = #{wsId}
     </select>
+    <select id="selectTwinWorkshopByWsCode" resultMap="TwinWorkshopTwinWorkshopEnergyResult">
+        select WS_ID, WS_CODE, WS_NAME, REMARK
+        from TWIN_WORKSHOP
+        where WS_CODE = #{wsCode}
+    </select>
 
     <insert id="insertTwinWorkshop" parameterType="TwinWorkshop" useGeneratedKeys="true" keyProperty="wsId">
         insert into TWIN_WORKSHOP

+ 13 - 0
jjt-common/src/main/java/com/jjt/common/utils/DateUtils.java

@@ -6,6 +6,7 @@ import java.lang.management.ManagementFactory;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.*;
+import java.time.format.DateTimeFormatter;
 import java.util.Date;
 
 /**
@@ -173,4 +174,16 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
         LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
         return localDate;
     }
+
+    /**
+     * LocalDateTime转字符串
+     *
+     * @param time 转换前的时间
+     * @return 字符串
+     */
+    public static String parseTimeToStr(LocalDateTime time) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS);
+        String str = time.format(formatter);
+        return str;
+    }
 }