From ca9892af12e094eccc5fabdf2314ff17f7b415c8 Mon Sep 17 00:00:00 2001 From: yang_shj <1069818635@QQ.com> Date: Wed, 17 Apr 2024 20:21:34 +0800 Subject: [PATCH] =?UTF-8?q?#=E6=A3=80=E4=BF=AE=E6=97=A5=E5=BF=97=E5=AF=BC?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/ConstructionServiceImpl.java | 32 +- .../service/impl/OperAccessLibraryServiceImpl.java | 2 +- .../hzims/operational/access/utils/BaseUtil.java | 364 ++++++++++++++++++ .../access/utils/EasyExcelWriterFactory.java | 55 +++ .../access/utils/ExcelToolListener.java | 33 ++ .../hzims/operational/access/utils/ExcelUtil.java | 411 +++++++++++++++++++++ .../hzims/operational/access/utils/HtmlModule.java | 119 ++++++ .../operational/access/utils/TableJsonUtils.java | 230 ++++++++++++ .../hzims/operational/access/utils/TimeUtils.java | 194 ++++++++++ .../operational/access/utils/ZXingGenCode.java | 63 ++++ .../template/word/constructio_template.docx | Bin 22490 -> 22579 bytes 11 files changed, 1474 insertions(+), 29 deletions(-) create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/BaseUtil.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/EasyExcelWriterFactory.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelToolListener.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelUtil.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/HtmlModule.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TableJsonUtils.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TimeUtils.java create mode 100644 hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ZXingGenCode.java diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/ConstructionServiceImpl.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/ConstructionServiceImpl.java index b16da12..cbd3fa6 100644 --- a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/ConstructionServiceImpl.java +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/ConstructionServiceImpl.java @@ -1,7 +1,6 @@ package com.hnac.hzims.operational.access.service.impl; import cn.afterturn.easypoi.entity.ImageEntity; -import cn.afterturn.easypoi.word.WordExportUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -10,12 +9,12 @@ import com.hnac.hzims.operational.access.entity.OperConstructionEntity; import com.hnac.hzims.operational.access.mapper.ConstructionMapper; import com.hnac.hzims.operational.access.service.ConstructionService; import com.hnac.hzims.operational.access.service.IOperAccessTaskService; +import com.hnac.hzims.operational.access.utils.BaseUtil; import com.hnac.hzims.operational.access.vo.ConstructionVo; import com.hnac.hzims.operational.util.PdfUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springblade.core.log.exception.ServiceException; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.mp.support.Condition; @@ -28,14 +27,11 @@ import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLEncoder; import java.util.Date; -import java.util.HashMap; import java.util.Map; /** @@ -81,35 +77,15 @@ public class ConstructionServiceImpl extends BaseServiceImpl params = new HashMap<>(); + Map params = BaseUtil.obj2Map(construction); try { params = PdfUtils.objectToMap(construction); }catch (Exception e) { log.error("转换对象失败!"); } String fileName = construction.getCode() + "_" + construction.getOverhaulName() + "_施工日志" + PdfUtils.DOCX_SUFFIX; - XWPFDocument document; - try { - document = WordExportUtil.exportWord07("template/word/constructio_template.docx",params); - } catch (Exception e) { - throw new ServiceException("文件创建失败!"); - } - response.setCharacterEncoding("UTF-8"); - response.setContentType("application/vnd.ms-word"); - ServletOutputStream outputStream; - try { - //设置docx格式 - response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); - //创建一个输出流 - outputStream = response.getOutputStream(); - //写入数据 - document.write(outputStream); - // 关闭 - outputStream.close(); - document.close(); - } catch (IOException e) { - e.printStackTrace(); - } + String templateFile = "template/word/constructio_template.docx"; + BaseUtil.exportDocument(templateFile, params, fileName, "constructionAttachment", response); } @Override diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/OperAccessLibraryServiceImpl.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/OperAccessLibraryServiceImpl.java index 9033366..57daf5f 100644 --- a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/OperAccessLibraryServiceImpl.java +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/service/impl/OperAccessLibraryServiceImpl.java @@ -196,7 +196,7 @@ public class OperAccessLibraryServiceImpl extends BaseServiceImpl params, String fileName,String findName, HttpServletResponse response) { + try { + XWPFDocument document = BaseUtil.fillDocument(templateFile, params,findName); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/vnd.ms-word"); + ServletOutputStream outputStream; + //设置docx格式 + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); + //创建一个输出流 + outputStream = response.getOutputStream(); + //写入数据 + document.write(outputStream); + // 关闭 + outputStream.close(); + document.close(); + } catch (Exception e) { + log.error("文件导出异常: {}", e.getMessage()); + } + } + + /** + * word文件导出 + * @param documents doc文件 + * @param savePath 存储路径 + */ + public static void exportWord(List documents, String savePath) { + FileOutputStream out = null; + try { + out = new FileOutputStream(savePath); + for (XWPFDocument document : documents) { + document.write(out); + } + } catch (IOException e) { + log.error("word文件导出异常: {}", e.getMessage()); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + log.error("word文件导出-输出流关闭异常: {}", e.getMessage()); + } + } + } + } + + /** + * 数据写入 + * @param templatePath 模板文件路径 + * @param params 数据参数 + * @return 文件 + */ + public static XWPFDocument fillDocument(String templatePath, Map params, String findName) { + XWPFDocument xwpfDocument = null; + try { + xwpfDocument = WordExportUtil.exportWord07(templatePath, params); + if (StringUtils.isNotEmpty(findName)) { + String findText = params.get(findName).toString(); + if (StringUtils.isNotEmpty(findText)) { + JSONArray jsonArray = JSONObject.parseArray(findText); + for (XWPFTable table : xwpfDocument.getTables()) { + for (int row = 0; row < table.getNumberOfRows(); row++) { + for (int col = 0; col < table.getRow(row).getTableCells().size(); col++) { + XWPFTableCell cell = table.getRow(row).getCell(col); + for (XWPFParagraph p : cell.getParagraphs()) { + Iterator iterator = p.getRuns().iterator(); + while (iterator.hasNext()) { + XWPFRun r = iterator.next(); + String text = r.getText(0); + if (findText.equals(text)) { + r.setText("", 0); // 清除原有文本 + XWPFParagraph paragraph = r.getParagraph(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + String name = jsonObject.getString("name"); + String url = jsonObject.getString("url"); + String id = paragraph.getDocument().getPackagePart().addExternalRelationship(url, XWPFRelation.HYPERLINK.getRelation()).getId(); + CTHyperlink cLink = paragraph.getCTP().addNewHyperlink(); + cLink.setId(id); + CTText ctText = CTText.Factory.newInstance(); + ctText.setStringValue(name); + CTR ctr = CTR.Factory.newInstance(); + ctr.setTArray(new CTText[]{ctText}); + cLink.setRArray(new CTR[]{ctr}); + if(i tableList = xwpfDocument.getTables(); + if (tableList.size() > 0) { + // 循环表格 + for (XWPFTable table : tableList) { + // 获取表格所有行数 + List rows = table.getRows(); + // 第三行到倒数第二行为需合并区域 + for (int row = 3; row < rows.size() - 1;) { + XWPFTableCell startCell = rows.get(row).getCell(0); + String startText = startCell.getText(); + int index = row + 1; + for (; index < rows.size(); index++) { + XWPFTableCell endCell = rows.get(index).getCell(0); + String endText = endCell.getText(); + if (!startText.equals(endText)) { + break; + } + } + mergeCellsVertically(table, 0, row, index - 1); + mergeCellsVertically(table, 1, row, index - 1); + mergeCellsVertically(table, 4, row, index - 1); + mergeCellsVertically(table, 5, row, index - 1); + row = index; + } + } + } + } + + /** + * word单元格行合并 + * @param table 表格 + * @param col 合并行所在列 + * @param startRow 开始行 + * @param endRow 结束行 + */ + public static void mergeCellsVertically(XWPFTable table, int col, int startRow, int endRow) { + for (int i = startRow; i <= endRow; i++) { + XWPFTableCell cell = table.getRow(i).getCell(col); + if (i == startRow) { + // The first merged cell is set with RESTART merge value + cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART); + } else { + // Cells which join (merge) the first one, are set with CONTINUE + cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE); + } + } + } + + /** + * 读取本地pdf,这里设置的是预览 + * @param response 响应类 + * @param filePath 文件路径 + */ + public static void readPdf(HttpServletResponse response, String filePath) { + response.reset(); + response.setContentType("application/pdf"); + FileInputStream fileInputStream = null; + OutputStream outputStream = null; + try { + File file = new File(filePath); + fileInputStream = new FileInputStream(file); + outputStream = response.getOutputStream(); + IOUtils.write(IOUtils.toByteArray(fileInputStream), outputStream); + response.setHeader("Content-Disposition", + "inline; filename= " + URLEncoder.encode(file.getName(), "UTF-8")); + outputStream.flush(); + } catch (IOException e) { + log.error("本地pdf文件读取异常: {}", e.getMessage()); + } finally { + try { + if (fileInputStream != null) { + fileInputStream.close(); + } + if (outputStream != null) { + outputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 对象转map + * @param obj 对象类 + * @return map参数 + */ + public static Map obj2Map(Object obj) { + Map result = new HashMap<>(); + if (ObjectUtil.isNotEmpty(obj) && null != obj.getClass()) { + List fieldList = new ArrayList<>(); + Class clazz = obj.getClass(); + while (clazz != null) { + fieldList.addAll(Arrays.asList(clazz.getDeclaredFields())); + clazz = clazz.getSuperclass(); + } + fieldList.forEach (field -> { + field.setAccessible(true); + Object value; + try { + value = field.get(obj); + } catch (IllegalAccessException e) { + throw new ServiceException("获取属性性出错"); + } + if (value instanceof LocalDateTime) { + value = DateUtil.format((LocalDateTime) value, SafeProductConstant.SAFEPRODUCT_EXPORT_DATE); + } + else if (value instanceof Date) { + value = DateUtil.format((Date) value, SafeProductConstant.SAFEPRODUCT_EXPORT_DATE); + } + result.put(field.getName(),value); + }); + } + return result; + } + + /** + * 图片转字节 + * @param tempImgPath 图片路径 + * @return 图片字节 + */ + public static byte[] imgToByte(String tempImgPath) { + File file = new File(tempImgPath); + byte[] buffer = null; + try { + FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + fis.close(); + bos.close(); + buffer = bos.toByteArray(); + } catch (IOException e) { + log.error(e.getMessage()); + } + //删除临时文件 + file.delete(); + return buffer; + } + + /** + * 下载文件 + * @param fileUrl 文件路径 + * @param downloadFileDir 下载路径 + * @return 下载文件路径 + */ + public static String downloadFileByUrl(String fileUrl, String downloadFileDir){ + URL url; + String fileName = null; + try { + url = new URL(fileUrl); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + connection.connect(); + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + InputStream inputStream = connection.getInputStream(); + int lastSlashIndex = fileUrl.lastIndexOf("/"); + if (lastSlashIndex > 0) { + fileName = fileUrl.substring(lastSlashIndex+1); + String filePath = downloadFileDir + fileName; + File file = new File(filePath); + if (file.exists()){ + file.delete(); + } + OutputStream outputStream = Files.newOutputStream(file.toPath()); + // 将文件流拷贝到本地处理 + IOUtils.copy(inputStream, outputStream); + } else { + throw new ServiceException("下载文件路径异常:" + downloadFileDir); + } + } + } catch (Exception e) { + throw new ServiceException("文件图片下载失败!"); + } + return fileName; + } +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/EasyExcelWriterFactory.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/EasyExcelWriterFactory.java new file mode 100644 index 0000000..3ba54b8 --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/EasyExcelWriterFactory.java @@ -0,0 +1,55 @@ +package com.hnac.hzims.operational.access.utils; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; + +import java.io.File; +import java.io.OutputStream; +import java.util.List; + +/** + * @author hx + */ +public class EasyExcelWriterFactory { + private int sheetNo = 0; + private ExcelWriter excelWriter = null; + + public EasyExcelWriterFactory(OutputStream outputStream) { + excelWriter = EasyExcel.write(outputStream).build(); + } + + public EasyExcelWriterFactory(File file) { + excelWriter = EasyExcel.write(file).build(); + } + + public EasyExcelWriterFactory(String filePath) { + excelWriter = EasyExcel.write(filePath).build(); + } + + /** + * 链式模板表头写入 + * @param headClazz 表头格式 + * @param data 数据 List 或者List> + * @return + */ + public EasyExcelWriterFactory writeModel(Class headClazz, List data, String sheetName){ + excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(headClazz).build()); + return this; + } + + /** + * 链式自定义表头写入 + * @param head + * @param data 数据 List 或者List> + * @param sheetName + * @return + */ + public EasyExcelWriterFactory write(List> head, List data, String sheetName){ + excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(head).build()); + return this; + } + + public void finish() { + excelWriter.finish(); + } +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelToolListener.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelToolListener.java new file mode 100644 index 0000000..ea51ea8 --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelToolListener.java @@ -0,0 +1,33 @@ +package com.hnac.hzims.operational.access.utils; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSONObject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Slf4j +@Data +public class ExcelToolListener extends AnalysisEventListener { + + /**保存数据**/ + private List dataList = Collections.synchronizedList(new ArrayList<>()); + + public ExcelToolListener() { + } + + @Override + public void invoke(Object data, AnalysisContext context) { + dataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("解析完成"); + log.info("------结果集为:{}------", JSONObject.toJSONString(dataList)); + } +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelUtil.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelUtil.java new file mode 100644 index 0000000..7531371 --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/ExcelUtil.java @@ -0,0 +1,411 @@ +package com.hnac.hzims.operational.access.utils; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.EasyExcelFactory; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.write.handler.WriteHandler; +import org.apache.poi.ss.formula.functions.T; +import org.omg.CORBA_2_3.portable.OutputStream; + +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author hx + */ +public class ExcelUtil { + /** + * 同步无模型读(默认读取sheet0,从第2行开始读) + * @param filePath + * @return + */ + public static List> syncRead(String filePath){ + return EasyExcelFactory.read(filePath).sheet().doReadSync(); + } + + /** + * 同步无模型读(默认表头占一行,从第2行开始读) + * @param filePath + * @param sheetNo sheet页号,从0开始 + * @return + */ + public static List> syncRead(String filePath, Integer sheetNo){ + return EasyExcelFactory.read(filePath).sheet(sheetNo).doReadSync(); + } + + /** + * 同步无模型读(指定sheet和表头占的行数) + * @param inputStream + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return List> + */ + public static List> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).doReadSync(); + } + + /** + * 同步无模型读(指定sheet和表头占的行数) + * @param file + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return List> + */ + public static List> syncRead(File file, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync(); + } + + /** + * 同步无模型读(指定sheet和表头占的行数) + * @param filePath + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return List> + */ + public static List> syncRead(String filePath, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).doReadSync(); + } + + /** + * 同步按模型读(默认读取sheet0,从第2行开始读) + * @param filePath + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @return + */ + public static List syncReadModel(String filePath, Class clazz){ + return EasyExcelFactory.read(filePath).sheet().head(clazz).doReadSync(); + } + + /** + * 同步按模型读(默认表头占一行,从第2行开始读) + * @param filePath + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @return + */ + public static List syncReadModel(String filePath, Class clazz, Integer sheetNo){ + return EasyExcelFactory.read(filePath).sheet(sheetNo).head(clazz).doReadSync(); + } + + /** + * 同步按模型读(指定sheet和表头占的行数) + * @param inputStream + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static List syncReadModel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync(); + } + + /** + * 同步按模型读(指定sheet和表头占的行数) + * @param file + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static List syncReadModel(File file, Class clazz, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync(); + } + + /** + * 同步按模型读(指定sheet和表头占的行数) + * @param filePath + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static List syncReadModel(String filePath, Class clazz, Integer sheetNo, Integer headRowNum){ + return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync(); + } + + /** + * 异步无模型读(默认读取sheet0,从第2行开始读) + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static void asyncRead(String filePath, AnalysisEventListener excelListener){ + EasyExcelFactory.read(filePath, excelListener).sheet().doRead(); + } + + /** + * 异步无模型读(默认表头占一行,从第2行开始读) + * @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param sheetNo sheet页号,从0开始 + * @return + */ + public static void asyncRead(String filePath, AnalysisEventListener excelListener, Integer sheetNo){ + EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).doRead(); + } + + /** + * 异步无模型读(指定sheet和表头占的行数) + * @param inputStream + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static void asyncRead(InputStream inputStream, AnalysisEventListener excelListener, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(inputStream, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 异步无模型读(指定sheet和表头占的行数) + * @param file + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static void asyncRead(File file, AnalysisEventListener excelListener, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(file, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 异步无模型读(指定sheet和表头占的行数) + * @param filePath + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + * @return + */ + public static void asyncRead(String filePath, AnalysisEventListener excelListener, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 异步按模型读取(默认读取sheet0,从第2行开始读) + * @param filePath + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + */ + public static void asyncReadModel(String filePath, AnalysisEventListener excelListener, Class clazz){ + EasyExcelFactory.read(filePath, clazz, excelListener).sheet().doRead(); + } + + /** + * 异步按模型读取(默认表头占一行,从第2行开始读) + * @param filePath + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + */ + public static void asyncReadModel(String filePath, AnalysisEventListener excelListener, Class clazz, Integer sheetNo){ + EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).doRead(); + } + + /** + * 异步按模型读取 + * @param inputStream + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + */ + public static void asyncReadModel(InputStream inputStream, AnalysisEventListener excelListener, Class clazz, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(inputStream, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 异步按模型读取 + * @param file + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + */ + public static void asyncReadModel(File file, AnalysisEventListener excelListener, Class clazz, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(file, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 异步按模型读取 + * @param filePath + * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等 + * @param clazz 模型的类类型(excel数据会按该类型转换成对象) + * @param sheetNo sheet页号,从0开始 + * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0) + */ + public static void asyncReadModel(String filePath, AnalysisEventListener excelListener, Class clazz, Integer sheetNo, Integer headRowNum){ + EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead(); + } + + /** + * 无模板写文件 + * @param filePath + * @param head 表头数据 + * @param data 表内容数据 + */ + public static void write(String filePath, List> head, List> data){ + EasyExcel.write(filePath).head(head).sheet().doWrite(data); + } + + /** + * 无模板写文件 + * @param filePath + * @param head 表头数据 + * @param data 表内容数据 + * @param sheetNo sheet页号,从0开始 + * @param sheetName sheet名称 + */ + public static void write(String filePath, List> head, List> data, Integer sheetNo, String sheetName){ + EasyExcel.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data); + } + + /** + * 根据excel模板文件写入文件 + * @param filePath + * @param templateFileName + * @param headClazz + * @param data + */ + public static void writeTemplate(String filePath, String templateFileName, Class headClazz, List data){ + EasyExcel.write(filePath, headClazz).withTemplate(templateFileName).sheet().doWrite(data); + } + + /** + * 根据excel模板文件写入文件 + * @param filePath + * @param templateFileName + * @param data + */ + public static void writeTemplate(String filePath, String templateFileName, List data){ + EasyExcel.write(filePath).withTemplate(templateFileName).sheet().doWrite(data); + } + + /** + * 按模板写文件 + * @param filePath + * @param headClazz 表头模板 + * @param data 数据 + */ + public static void write(String filePath, Class headClazz, List data){ + EasyExcel.write(filePath, headClazz).sheet().doWrite(data); + } + + /** + * 按模板写文件 + * @param filePath + * @param headClazz 表头模板 + * @param data 数据 + * @param sheetNo sheet页号,从0开始 + * @param sheetName sheet名称 + */ + public static void write(String filePath, Class headClazz, List data, Integer sheetNo, String sheetName){ + EasyExcel.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data); + } + + /** + * 按模板写文件 + * @param filePath + * @param headClazz 表头模板 + * @param data 数据 + * @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用) + * @param sheetNo sheet页号,从0开始 + * @param sheetName sheet名称 + */ + public static void write(String filePath, Class headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName){ + EasyExcel.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data); + } + + /** + * 按模板写文件(包含某些字段) + * @param filePath + * @param headClazz 表头模板 + * @param data 数据 + * @param includeCols 过滤包含的字段,根据字段名称过滤 + * @param sheetNo sheet页号,从0开始 + * @param sheetName sheet名称 + */ + public static void writeInclude(String filePath, Class headClazz, List data, Set includeCols, Integer sheetNo, String sheetName){ + EasyExcel.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data); + } + + /** + * 按模板写文件(排除某些字段) + * @param filePath + * @param headClazz 表头模板 + * @param data 数据 + * @param excludeCols 过滤排除的字段,根据字段名称过滤 + * @param sheetNo sheet页号,从0开始 + * @param sheetName sheet名称 + */ + public static void writeExclude(String filePath, Class headClazz, List data, Set excludeCols, Integer sheetNo, String sheetName){ + EasyExcel.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data); + } + + /** + * 多个sheet页的数据链式写入 + * ExcelUtil.writeWithSheets(outputStream) + * .writeModel(ExcelModel.class, excelModelList, "sheetName1") + * .write(headData, data,"sheetName2") + * .finish(); + * @param outputStream + * @return + */ + public static EasyExcelWriterFactory writeWithSheets(OutputStream outputStream){ + EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(outputStream); + return excelWriter; + } + + /** + * 多个sheet页的数据链式写入 + * ExcelUtil.writeWithSheets(file) + * .writeModel(ExcelModel.class, excelModelList, "sheetName1") + * .write(headData, data,"sheetName2") + * .finish(); + * @param file + * @return + */ + public static EasyExcelWriterFactory writeWithSheets(File file){ + EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(file); + return excelWriter; + } + + /** + * 多个sheet页的数据链式写入 + * ExcelUtil.writeWithSheets(filePath) + * .writeModel(ExcelModel.class, excelModelList, "sheetName1") + * .write(headData, data,"sheetName2") + * .finish(); + * @param filePath + * @return + */ + public static EasyExcelWriterFactory writeWithSheets(String filePath){ + EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(filePath); + return excelWriter; + } + + /** + * 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel) + * ExcelUtil.writeWithSheets(response, exportFileName) + * .writeModel(ExcelModel.class, excelModelList, "sheetName1") + * .write(headData, data,"sheetName2") + * .finish(); + * @param response + * @param exportFileName 导出的文件名称 + * @return + */ + public static EasyExcelWriterFactory writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException{ + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf-8"); + // 这里URLEncoder.encode可以防止中文乱码 + String fileName = URLEncoder.encode(exportFileName, "UTF-8"); + response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); + EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(response.getOutputStream()); + return excelWriter; + } +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/HtmlModule.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/HtmlModule.java new file mode 100644 index 0000000..bd988c5 --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/HtmlModule.java @@ -0,0 +1,119 @@ +package com.hnac.hzims.operational.access.utils; + +import java.io.*; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 专门用来解析生成PDF文档的html自定义模板 + * @author hx + */ +public class HtmlModule { + public static String buildHtml(List modules, String templatePath) throws Exception { + //读取html + String html = readToString(templatePath); + //找到头的部分 + String headHtml = findInString(html, ".*"); + //找到body部分 + String bodyHtml = findInString(html, ".*"); + //处理body部分 + bodyHtml = bodyHtml.substring(6, bodyHtml.length() - 7); + //构造html + StringBuilder htmlStr = new StringBuilder(); + htmlStr.append(""); + htmlStr.append(headHtml); + htmlStr.append(""); + for (int i = 0; i < modules.size(); i++) { + if (i < 1) { + htmlStr.append("
"); + htmlStr.append(buildHtmlBody(bodyHtml, modules.get(i))); + htmlStr.append("
"); + } else { + htmlStr.append("
"); + htmlStr.append(buildHtmlBody(bodyHtml, modules.get(i))); + htmlStr.append("
"); + } + } + htmlStr.append(""); + htmlStr.append(""); + return htmlStr.toString(); + } + /** + * 利用反射把数据注入模板内容中 + * @param bodyHtml + * @param object + * @return + * @throws IllegalAccessException + */ + public static String buildHtmlBody(String bodyHtml, Object object) throws IllegalAccessException { + //获取该对象所有属性 + Field[] fields = object.getClass().getDeclaredFields(); + //设置值 + String result = bodyHtml; + for (Field field : fields) { + field.setAccessible(true); + String key = field.getName(); + Object objValue = field.get(object); + String value = ""; + if (!Objects.isNull(objValue)) { + value = objValue.toString(); + } + result = result.replace("${" + key + "}", value); + } + return result; + } + + /** + * 读取文件到String中,指定以UTF-8格式读取 + * @param filePath + * @return + * @throws IOException + */ + public static String readToString(String filePath) throws IOException { + StringBuffer buffer = new StringBuffer(); + InputStream is = null; + BufferedReader reader = null; + try { + // 用来保存每行读取的内容 + String line; + is = new FileInputStream(filePath); + reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + // 读取第一行 + line = reader.readLine(); + // 如果 line 为空说明读完了 + while (line != null) { + // 将读到的内容添加到 buffer 中 + buffer.append(line); + // 读取下一行 + line = reader.readLine(); + } + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + reader.close(); + is.close(); + } + return buffer.toString(); + } + + /** + * 查找html内容里的标签内容 + * @param html + * @param regex + * @return + */ + public static String findInString(String html, String regex) { + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(html); + if (matcher.find()) { + return matcher.group(); + } + return null; + } +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TableJsonUtils.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TableJsonUtils.java new file mode 100644 index 0000000..406366f --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TableJsonUtils.java @@ -0,0 +1,230 @@ +package com.hnac.hzims.operational.access.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +public class TableJsonUtils { + + // 获取日志输出对象 + private static final Logger logger = LoggerFactory.getLogger(TableJsonUtils.class); + + + + /** + * 处理建表语句方法 + * + * @param createTableSql 缩进次数。 + * CREATE TABLE `blade_user` ( + * `id` bigint(64) NOT NULL COMMENT '主键', + * `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID', + * `code` varchar(12) DEFAULT NULL COMMENT '用户编号', + * `account` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', + * `password` varchar(45) DEFAULT NULL COMMENT '密码', + * `name` varchar(50) DEFAULT NULL COMMENT '昵称', + * `real_name` varchar(10) DEFAULT NULL COMMENT '真名', + * `avatar` varchar(500) DEFAULT NULL COMMENT '头像', + * `email` varchar(45) DEFAULT NULL COMMENT '邮箱', + * `phone` varchar(45) DEFAULT NULL COMMENT '手机', + * `birthday` datetime DEFAULT NULL COMMENT '生日', + * `sex` smallint(6) DEFAULT NULL COMMENT '性别', + * `role_id` varchar(1000) DEFAULT NULL COMMENT '角色id', + * `dept_id` varchar(1000) DEFAULT NULL COMMENT '部门id', + * `post_id` varchar(1000) DEFAULT NULL COMMENT '岗位id', + * `create_user` bigint(64) DEFAULT NULL COMMENT '创建人', + * `create_dept` bigint(64) DEFAULT NULL COMMENT '创建部门', + * `create_time` datetime DEFAULT NULL COMMENT '创建时间', + * `update_user` bigint(64) DEFAULT NULL COMMENT '修改人', + * `update_time` datetime DEFAULT NULL COMMENT '修改时间', + * `status` int(2) DEFAULT NULL COMMENT '状态', + * `is_deleted` int(2) DEFAULT '0' COMMENT '是否已删除', + * `user_type` int(1) DEFAULT '1' COMMENT '0-平台用户,1单站用户,2-集控用户,3-监控用户', + * `data_scope_type` char(1) DEFAULT '2' COMMENT '数据权限类型 0 只看自己 1 本部门 2 本部门级下属 3 从属机构 4 从属机构及下属 5 个人及下属机构', + * `dept_ids` varchar(1000) DEFAULT NULL COMMENT '从属部门id', + * PRIMARY KEY (`id`) USING BTREE + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户表' + * @return + */ + public static String createTableSqlProcess(String createTableSql) { + // 回车换行符处理 + createTableSql = createTableSql.replaceAll("\r|\n", ""); + + // 将原始的mysql 建表语句,通过最外层括号分割开三部分,中间部分做处理 + int firstParenthesesIndex = createTableSql.indexOf("("); + int lastParenthesesIndex = createTableSql.lastIndexOf(")"); + if (firstParenthesesIndex >0 && lastParenthesesIndex >0){ + String middle = createTableSql.substring(firstParenthesesIndex+1, lastParenthesesIndex); + //数据类型替换 + String regex = "bit\\([0-9]+\\)|bigint\\([0-9]+\\)|smallint\\([0-9]+\\)|mediumint\\([0-9]+\\)|tinyint\\([0-9]+\\)|int\\([0-9]+\\)"; + middle = middle.replaceAll(regex, "varchar(20)"); + + //时间类型替换为 50 个长度 + middle = middle.replaceAll("datetime", "varchar(50)").replaceAll("timestamp","varchar(50)").replaceAll("DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",""); + middle = middle.replaceAll("AUTO_INCREMENT",""); + // TODO: 其他数据类型也需要替换成varchar, 具体多少位,看数据类型而定 + + // 根据逗号分隔,只保留COMMENT 前面的内容, 规范在注释用不要出现英文逗号 + StringBuffer sb = new StringBuffer(); + String[] fileds = middle.split(","); + for(int i=0; i 0){ + // 只保留注释前面部分 + String needField = fileds[i].substring(0, commentIndex); + // `account` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', + // 处理特殊语法字符 + needField = processSpecialKeyWord(needField); + sb.append(needField).append(","); + }else { + // TODO:没有注释COMMENT 的,需要区分处理,如果是表设计有写字段没有加注释,需要加入, + // 但是 主键声明这类不需要 PRIMARY KEY (`id`) USING BTREE ,sqlite 中不识别 using 语法 + String needField = fileds[i]; + if (needField.contains("PRIMARY KEY") || needField.contains("USING") || needField.contains("KEY")){ + logger.info("主键语句去除跳过不要"); + }else { + needField = processSpecialKeyWord(needField); + sb.append(needField).append(","); + } + } + } + sb.append(" OFFLINE_FLAG VARCHAR(2) DEFAULT '0')"); + // 拼接完整的建表语句 + createTableSql = createTableSql.substring(0, firstParenthesesIndex+1) + sb; + // 转成成大写 + createTableSql = createTableSql.toUpperCase(); + logger.info("建表语句净化后="+createTableSql); + + }else { + logger.error("建表语句异常情况"); + } + + + return createTableSql; + } + + + + // 处理特殊语法字符(某些mysql语法支持,但是sqlite不支持), 可扩展, + private static String processSpecialKeyWord(String sql){ + + String needField = sql; + // `account` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号', + // 处理特殊语法字符 + if (needField.contains("CHARACTER SET")){ + int characterIndex = needField.indexOf("CHARACTER SET"); + int defaultIndex = needField.indexOf("DEFAULT"); + if (defaultIndex >0){ + needField = needField.substring(0, characterIndex)+" " + needField.substring(defaultIndex); + }else { + needField = needField.substring(0, characterIndex); + } + } + // 处理特殊语法字符 + if (needField.contains("COLLATE")){ + int characterIndex = needField.indexOf("COLLATE"); + int defaultIndex = needField.indexOf("DEFAULT"); + if (defaultIndex >0){ + needField = needField.substring(0, characterIndex)+" " + needField.substring(defaultIndex); + }else { + needField = needField.substring(0, characterIndex); + } + } + return needField; + } + + + + /** + * 单位缩进字符串。 + */ + private static final String SPACE = " "; + /** + * 生成.json格式文件 + */ + public static boolean createJsonFile(String jsonString, String filePath, String fileName) { + // 标记文件生成是否成功 + boolean flag = true; + + // 拼接文件完整路径 + String fullPath = filePath + File.separator + fileName + ".json"; + + // 生成json格式文件 + try { + // 保证创建一个新文件 + File file = new File(fullPath); + if (!file.getParentFile().exists()) { // 如果父目录不存在,创建父目录 + file.getParentFile().mkdirs(); + } + if (file.exists()) { // 如果已存在,删除旧文件 + file.delete(); + } + file.createNewFile(); + + if(jsonString.indexOf("'")!=-1){ + //将单引号转义一下,因为JSON串中的字符串类型可以单引号引起来的 + jsonString = jsonString.replaceAll("'", "\\'"); + } + if(jsonString.indexOf("\"")!=-1){ + //将双引号转义一下,因为JSON串中的字符串类型可以单引号引起来的 + jsonString = jsonString.replaceAll("\"", "\\\""); + } + + if(jsonString.indexOf("\r\n")!=-1){ + //将回车换行转换一下,因为JSON串中字符串不能出现显式的回车换行 + jsonString = jsonString.replaceAll("\r\n", "\\u000d\\u000a"); + } + if(jsonString.indexOf("\n")!=-1){ + //将换行转换一下,因为JSON串中字符串不能出现显式的换行 + jsonString = jsonString.replaceAll("\n", "\\u000a"); + } + + // 格式化json字符串 +// jsonString = this.formatJson(jsonString); + + // 将格式化后的字符串写入文件 + Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); + write.write(jsonString); + write.flush(); + write.close(); + } catch (Exception e) { + flag = false; + e.printStackTrace(); + } + + // 返回是否成功的标记 + return flag; + } + + + + /** + * 离线文件上传后,各个服务解压后读取自己要的json文件 + * @param fileName + * @return + */ + public static String readJsonFile(String fileName) { + String jsonStr = ""; + try { + File jsonFile = new File(fileName); + FileReader fileReader = new FileReader(jsonFile); + Reader reader = new InputStreamReader(new FileInputStream(jsonFile), StandardCharsets.UTF_8); + int ch = 0; + StringBuffer sb = new StringBuffer(); + while ((ch = reader.read()) != -1) { + sb.append((char) ch); + } + fileReader.close(); + reader.close(); + jsonStr = sb.toString(); + return jsonStr; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + + +} diff --git a/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TimeUtils.java b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TimeUtils.java new file mode 100644 index 0000000..4b4c22f --- /dev/null +++ b/hzims-service/operational/src/main/java/com/hnac/hzims/operational/access/utils/TimeUtils.java @@ -0,0 +1,194 @@ +package com.hnac.hzims.operational.access.utils; + +import org.springblade.core.tool.utils.DateUtil; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class TimeUtils { + + private static int sheet1NowRow, sheet2NowRow; + + + /** + * 相差时数 + * + * @param endDate + * @param nowDate + * @return + * @author ty + */ + public static Long getDifferTime(Date nowDate, Date endDate) { + long nh = 1000 * 60 * 60L; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少小时 + long hour = diff / nh; + return hour; + } + + public static String getYear(Integer year) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为1(即一月) + calendar.set(Calendar.MONTH, Calendar.JANUARY); + // 设置日期为1 + calendar.set(Calendar.DATE, 1); + // 设置时间为0点0分0秒 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + String start = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 00:00:00"; + return start; + } + + public static String getYearEnd(Integer year) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为12(即十二月) + calendar.set(Calendar.MONTH, Calendar.DECEMBER); + // 设置日期为31 + calendar.set(Calendar.DATE, 31); + String end = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 00:00:00"; + return end; + } + public static String getYearEndV2(Integer year) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为12(即十二月) + calendar.set(Calendar.MONTH, Calendar.DECEMBER); + // 设置日期为31 + calendar.set(Calendar.DATE, 31); + String end = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 23:59:59"; + return end; + } + + public static String getMonthStart(Integer year, Integer mon) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份 + calendar.set(Calendar.MONTH,mon-1); + // 设置日期为1 + calendar.set(Calendar.DATE, 1); + String start = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 00:00:00"; + return start; + } + + public static String getMonthEnd(Integer year, Integer mon) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为1(即一月) + calendar.set(Calendar.MONTH, mon); + calendar.set(Calendar.DAY_OF_MONTH, 1); + // 将日期对象减去一天,即为当月的最后一天 + calendar.add(Calendar.DAY_OF_MONTH, -1); + String end = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 00:00:00"; + return end; + } + public static String getMonthEndV2(Integer year, Integer mon) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为1(即一月) + calendar.set(Calendar.MONTH, mon); + calendar.set(Calendar.DAY_OF_MONTH, 1); + // 将日期对象减去一天,即为当月的最后一天 + calendar.add(Calendar.DAY_OF_MONTH, -1); + String end = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE) + " 23:59:59"; + return end; + } + public static List getListByYear(Integer year) { + List monthList=new ArrayList<>(); + for (int i = 0; i <12; i++) { + // 创建一个Calendar对象 + Calendar calendar = Calendar.getInstance(); + // 设置年份 + calendar.set(Calendar.YEAR, year); + // 设置月份为1(即一月) + calendar.set(Calendar.MONTH, i); + calendar.set(Calendar.DAY_OF_MONTH, 1); + // 将日期对象减去一天,即为当月的最后一天 + String mon = DateUtil.format(calendar.getTime(), DateUtil.PATTERN_DATE); + + monthList.add(mon); + } + return monthList; + } + public static List getListByYearMon(Integer year,Integer mon) { + List monthList=new ArrayList<>(); + for (int i = 0; i Z=yguf>p zgqVYh3Wc=6g_HZF`j!|vT}}2(itFu+TEWKNE8mg_a@;PCmuJ^e8n!+Z`hjHI(E^78 zqco2p`zt1A-u)jmCJkQ6KU0uGWU_NERFzPcow>53@EEUI?dE_qJ#VZ_FxCv3Ram}C zRYD(v&Gns8DIi1R*yvG-%;~hv26!lh0mn$s98R9KaBOyZyZcdHKTVvpO%S=ZCgyHi zUulozGHStk-+M1aDbwg52-JlfSP@*usF~VYnwD!>1}?c56eMRwZ(F+=N`O}cl zk$v&73FbUe@;F`$`YZ7$MPje4yw*KoVYz@mMoPvy%VV3tt3U9Col0zsA$!gjG zB)s&eX9*(5_s`lbeBz!n);pM|`&N8N}YPTTU5!DC-!OFFW6AZVY@12ua(zA3$ zr1ASpDU{-`uo##p*FTeMJe^*fBtQO`WclHythY&JJ_%UNThnOCF*0U9V2)GB3=R$@_(6Zb7C}Z6 z$0Dl#odp<1C7YZGy~hW z*l9`x&y|htChJ?~Viu~@kJm2xzd8Ri*v5Gh%5R`DNURXwdg>$CQeq+Rr`J@3v&Ibe z{NUG1*h?yn;qW!~#z-_yFVOndH!I8S>yt@uG@);*<;Zx{NNR8LGE`527gu^+=pOy^ zWu@Mg=7_P={$IU(i8j={lAx!BmmtY+$iFHnbDN@B&?|oUgWkX!!}{AD{hj+ z2BSe*#2S!jDSAt@Yrp^}Nbrvi+M}*{LEq50J#}2cjLMGM-IN{L3Ll0CtDZ3-)?0a{ zY^gv6RL{;-JUP;t=zXG%r1MS+wJ}1EGOE?%@3`(bE2G_(E(-#NLPA$vRS%bai!4QH z8uBIQ4u{Y$3ZBvJJ)|E&FE#{5_71;K<1Q=cWjd+8NS7ya3gXmR6V%C&>Sz;LMu$xV zKQRWYF1&;DJVUt&D4;N85)7b9O3@_-05A&eN2Z6g;x_qEcyCAYBoE%a<8zyDt#i*d zoJ{mWlKV&Os~x&Ox+AeWbQyigE7wbW z7R0AqZqlvM+gWT?6f(ED>)M$dgPhdwd-B{~ZxpcVqC zSeZ~@^trRFgdaYI*0aGgG3H->|9A(Rz^eO+awikqo#XQ$$PJr?e^*wdKpOc3Uc&W! zrT?CD1iRBBVOJBr3$i7uO6G)rHB+=dUO>xEY(&4Ow#yc_5Du=Z@)JC$Z6^9W__b7b zAiG_ZnTCjPdlr6?_s)YU=v+ASK-{oGqp>5O?WZ4xN0}0;HQdmJJkvC4lJrBkfVs@Cf5(@)-pP(d;rr+1)jd%<4L9-8qmDI=_~yRU-1 zVXEO05$uWa9am6sG!1;+W-$Hz-S>i`Bu`qGv%RMfXg(SnG|p7*0ail3hncDX1n{U>H1J z%3@tuTgt1mYqz#QOgKmUmXwV`?7hp7a7H-xhw%3a_^Xk;Ww%A)w5ljCF(t9ld&pqj zAf-tUzGkZ(s0unaq+pOP&~210(JR)}hc7KkyF1r+0sOX~d(Yp25BXe#t4}Ag$WBe| zRkS0Vgsj~wj=rr5HN9fRniq`fhp#Y2>EFc;s19YC2?_OABqK+B1GYy0G51*=>nzU^ zMse}|S;SsB;d=CTG)hL5PTYli(guDdTX%~=$NE9U7{wmH0%U^9j!e`8q zr*{whe)8Iyz9WF3q?f{j%ryrKz2K(I?wHx*NTdj`n4sr3>kZFeVPmuI1fSxz9JGqY*eCqJ;Vt3o; zJCFM}moMXw5G*sHAv@&|RX!>^`y7l&LkkRcsle8nfY5P&c@t|@U^Fd7;Ku!lB#)jn z--BRl0K|VwqaKI(K#S0YxR{Vq&J0c$4oB^mWeF|D?t-KNy@m^B;G`-JoS%MEdFP=i zsMtBYW+jIF_RsaURYq%@?J7!(gM_I~I(Yw8yDX4PEBqjaGHYHV?=n-^GQV)xD1j%&?#K& zF34T$7XOY;a@XY&U`4g<9#$7mIq|zL+j8Ihp-8ubYXmb4gHl9jv+WtR1H)6&t=k;4my3hK2!8 zD+iDwZV2}C==rs+cPl>}<{9yWt(#@>@-Q=md8N$`v2STn6+f~Q7G-Qt?SW3tSu0KcDL3ib7;~CU`d~t;f2S=1i7a3Nl=LFVb zs8rcdCivKvmWO5D61yRIf)~;uqT4QdU1G$77B{t!OMyQeV+^b?Z@7*U3wr33?0zH% z;Bdg+VXp&oCv@QOTSR+~4i`ufb(!On`WUOZSUS=iu=dx0az}OUlg1jF>4p5`F{v6( z_WttT^$G^;CGNWGSB7YVzb>A#FAQftr@Wj$6;s!dwvM951X}{@%F;-%l_|7%zYlOq z+D&Ck`D+~Q&1vry*lDSq=1f39pw;nFMP!M)akpLKIcWDBYb|f5Bn) zw~AqlSN&a>VxzOZ`ixtctPynWqfjso$e<+Vm-|8Mp#Yy@yHNqx!!nv~_B5r2qU+(tf{=^F#&! zGN5HRO1cF5M(sN;kC{=99i(7zjh6I907>SZG3FoX!r*Gu^%>LYm*d7HSz#HiFTxj1 zdY&5`@RyT(ETVbxQ7z2Z&9C%%E_f>c)XZoogpz$=q8$JwDbbKkUqF>`86m0aY^n5J z*KvR1I|vrMVx>QDjeaGnrQ;5Hk6y)-$QsuFLWuog6g0SBPtPCyu9fFPtiED~Jw*AI zs>4NOCtfMRq$pP+dfAnX)(4m1D$f6=NHpMsyZx`&0L0tQYSpnZB9Y&a;+3YTw?HL& zaK>iinZfPXyk>AXg>YsYW0Vw-l;;S`Q=rBGzW-A9mBo)RJ7UfNlAx)ey*udzL{y(7=OE z;ekL&1t?trVtDNg>pIepk=9b3y3vuZ3mV0v!EbU^Mw=rU06Gc`xk;h3Ru=5sM zB_3DsD5}U=l7cUY6z3t4w8_}mxZ1;k9Q3r>A{DuHRPf<|%KT`yYut(e#W#jY?OnNJ z=mX!=DtJWp)SV1=n1#tECUppZ)HSXW>bnIY3z>51>lnP9H!562(xw^mv!^`rZI?Q?P8#u?6`?IzOFD)tDUKq^PIR?-*(s5 zm!;h&$_CB_zYh2LXM;~a+F3BiSZ|m)Ie!f)8%Hgbw#l-kAUE#sX9a2Lla5>yZK+b5 znF#olL_{vKtbohau{7QxFgis>M(aV+5XoXL>H>>g=ykCjz%(1lh`#HmwODgzo=8_& zE7nEQEm_PY9EZ1d!rcKlY0z@o*i4eJ@5kaOj$J*n9oodK#CmIjtbp+e>9nOF zffJ=DzxSw;0Ou!N0=s16g=Y37Z6DX`8g|=ZI_!-wSofIj-kw-IdA%}$0qU!j@wC=h zOaQ>`3$%ue4D!*DqUEwp9Nr^|>s#N*deUk|4eE1>{t!KSIKO1GUq(^o@xl|Dp5LFuahveF{sW!Rd8o1FM3-B z`<>KNB4I?bTz=>#=dKAfvcn_)8jcN~W3fBRZdKN&Tbo`oUeQN9j6M6NJ;T$692eEY1r)?m79jrZ!NOQllV1m>}I$c%bsrkYGdzxi0BBmhmQ2sr)}lF{iUvvtUnUwnErM6jKJ{!Zwkt0@l4 z2&OY%c_rVdP|9fyk%h}InmYh)Ya7PCyu|VzbwZMU3?eU?H=H6L3hwvif|tAz*fN(6 zI+e$8UB^U7`~zA#R%R)IxjW6tO(#?HDr+-H&1(+sEho(*q3kFM<~KI1HXcMeiY)8} zKj&;}LD$6aBt5P(-1QyPVyJ_2;0w9zxR$*Ie3l&oiAM`_aB1qxz z#ix9fQj=q4)mTB^)_SvUb&N?aLV_4_|IW`)zOT{>Sktc)l3z!0FN1+?sHW1BqBsmO zaoqN0DMzb3?3fYZHX+Fh<%<5ri#pf`&BmzxYi=UnfU~C-ktbya!>f8_yR*X|blC0L zDpTu1iUNOV43;hB>rA}~(=!}NS%GjM^nN{onLS8}9F@@(XHGgJM}=(Dk7)$8Q0Aj6 z{6gM7=Go!6cs)ue?;Y(SO91_PSUwuo-G!-|7EdhCn2L$j_Vbe!dj*ElH$)c_*lT42 zQ54VC9Y)@U%>}o#6EJZ30~bOs%B-N%HO?e43L#bvTVTu*AzBEX<5Ddab2vl@^f`c5 z4Y3~-o;f3kpFa_gQ%DRO3E+~t?{^7Cmt$dP*waqh3E;B)ZW17&vBnF|Wt#fvxW@OD zrjI>dqMeZGW*V)vS?f+lM-w+)Dfzu556042HO}1##ut@Rb-J%@V9!OvH9lp$pyz@O z%8n;YOWp;rKnnge9h2w?Ci3y70_A$7ty>D-1fx2+>g zEp6rH=aK?yIgmRgr+@#94!Jsny!c#Vy!B?Z(|EtdtzL*WPrh3VWx|CJP${X@@gOy- zTY)$RZ674M&FDOTUCuz`jBh>(i+DaYz>l4xH$snuwyIhbyG;8@d|ECki|od%En*G{ zKDdV;6Oes#H(rYMrD_0-R@+HwF)X&Z)a87)Gh>Y4IqD#VjB1mmja?Kh`aaLeJb~Ry>zR>g} z+;xL$v??5D4^p}cPK%#!^ho?)w3SB1x5|G!OOX;LK*5=&>xWKgIklGNM zEG}9v7)y4Ll9%5KJ0mJSz83z0+qoM`L0d|p#A8@U^-Fo&kOUdRNUC29{w5a9gUD$W25*89f0@;Kz2#Wb-?)6F8ni+AM0vxj0sOj;-M_td+5S% zsk%mc2YCnMNh*pVzZ`7Sr*e zIjG0WVlcYt$oE3_E>NBaVnxX&+eJ&_DAOJDE<2QlcUTy5HLqd;t!z8cJ~e?&NZDnn zSoveltBO7!U6vQTgh4!>&#j~yP?GEu9YbBz_%Y>xxp)gWf!CXLEd$@PXx}&vOuwUu zD-G74Vmc(et6lx7uyhqU)@DLpzpp)R%&#MB~bu1o17-Q1~oYD{rrj}H*1CqlFvtwogq0}>$n3uUT`2K!64 z(0VsOINAC_D$6DURczKV1dHG@AY!1?W+!7zjbgJAXiyHsri!~|Bp22Naeh&kCf}5B zKZhUfu*}{DrjuD7z5|OSt4@tHWi#GX3n}CC=7btN<7D>bHmQvBPBM)gl+-g1{l33R znSdlKr+9l4J3K2(uVqTFU8Z*Q(KT{rQ+4=Ei#2_eqAYM_;IVn_UDXFF}+OTmw>8Ap@oxQ z1f#poQJyePjfnk1V3yC*fH;Gf%asCr3z9>SJQzpL^M^Cq(!GR1Bee747&E!|%H#8l zS@nUxWE!*N)Sv4JT6$5&v@sHqoeuC|$x@$IaH3RiDraR2wgiZFKkxf`8R{;lsC`b>GMSiC+nlZSjy)sSc__ph6|$PF z&^#hG@wRfrHK!LZ%Y_g*|uVh9h!+#FY^rlbZM6n)l`sv=S4c zYAfDe*hYL*vmzxzH%pcRScIR92PMI+q3e?|A{&Uz!w!p$xrOnpc`P&<0}z6*J~zi6 z?G0(i9{GY}Qcl0;*FhpD9Zx4oIV1a+J&3tkqK=$qhYbAF?)|c_1kf)!!!#ihcw?adzuAJK~u z@MUR85ThmibXE!aDw{6!f-_P!vjo9-?fr+l+J1VW z8(t7rwf*K~z$?E28kV!=KxFY<64a_^>i;b0#Y(BOYW^~D!x+#DdNPR4l*0@cp|9LF zHEoAuZN2MInZ=AwcrPX{S8sjByGj{*Ikz8^j++>l)K7qI$IXC_&1Kc)Dy3p=dt7?k zP4o2TFPC4q)8Y+W9yUD5*oA<41kOsm5l6~CnXPF}5X>mvG*$w)F+&&%I^ zp6@x5?Uez4ipZj9rJ^9P(t*#`kthVSpz?GD54|_+)=1yIs9u$cv7Z!3Mx^um4709R zbffQR&Q2W;)RffiqIefY7MavJ{gE1bW1=<9<^lFv1GTZq_6!I6fs zUdQiIjGXKsu2RGk-I-=+V1X|8C3~AdxSQLDwmDM6`dcK*Tqxgj=hbWv1JO@|Yvzyq z!#jMAkutawdJ3u9J2|AHXg)SDL8)r){;3mTIVu{mbUE~Z-P#7V$95vknDCo4 z9R%2OXy|wu8@4saZUt%GRc7{(Jj@&wn}tKGT#G%avVnLvx2;x$OlWRucZLl!A`3uA z_lc^Q^r()%rKm8o1uG)`2WWw&7_yHV008yZCWby~k|X3oNwl>8Me{?Qw1iOpi+qMw zYY`#TLHo4mDgKKe{YysU|4jjczhv}3f&h5^SMx%VwZ$>w{`R`u9i7cNp=vsB5c;7W z+JXq@&{}Oyihmcr|Dv(~ZMhD8)TRghJFx%&;6ICht(a-bb9G-Sox z%<=3)PZc<=+5g$9O^{hV-8>g)6?s^bO3$ddoh|@oP~8?|Jdq*NePBCT+``W>Jr5Ys zP@mU9mVaR;0B4DarhH3BZrV2$#-~&|0sW|ilZ@h>RmbjVcFj>dNiv}KO0?8=gLeRr zsgNSZny-BqTXlD&d`-j+$~N!y&f;~JojR0OWa6f4rX?Txoq%{p&H zg9Qa--Hyi5i}iE2f6@8p4XLvGVa(*;mCg+PR}b_GV6y5dE*dO-oDZeIdYCd%T3d$@`Jm9pK?JNXgC(c-GW`>fw;7+;yR&iae|y!C@Pe@r2O1A2o&8o3qBhJH&-!3PJ;CoUBImLHU~U zetq->KLiG$Ve{4ADBLPN;91Js9WHs4yxrBys3O@z`&Y>2IFNZH>1GuW(9P_rp z+8l?#?oYD0lVxL_wp2yZ6`rY7tBvd$5<-u&>{RQ`=)1NU3@dCwyu=r-Lj@;i*2mP@ zqew<45;t}?;EPtMgnaiS=>sBg3uQI4Z+M(GFW=I_Sm3PSydyte<3=CZic$g`BOyv$ zRVo&|)0fpMywXc$IHl@s-6@cy0{S4jBUcM)anZcgnJ;2tEE28Q9T-JC3?>-?iN;}2 z2{4}c8h69E2=iiZSIBlOU65Unw&b->9R}OBn!6u`8>9V-uh*2tpcSPH?KBG^EMy4X zGJ|GEduu{s=W89l8}&8V;VL0xpz^|HH^(`c%btU?@G@@99r;c8Yb#>=*x>M^HF*jV zqyiQnDUqyKq7~`=zl4mz(tw4~TLOs#cH_7{-#4fv=3UqP&N{?vD#-^-@tCeY+pBlk?wL62@K#}2IeC^Ut58z1Bc87Fvd!`*{ieYc%rmaIzveZW zOoOOMvkgL1HE}E%SG*JY;jJdM=*xEA1T>#lzm=1IQQ5AVQs}`)$oP^GtK~>0;a-zG z^NgO7Rft;i5QeLse+*tKu6+IW$_;k5O0(H~OtT8bP)%t7V4#5eI-W8u2clhc9;FUJ zxmFLON_BS`pe4>A>y&-gK2<85*S*1~SkBVdbuLCHR2FcvmDnDmABzf##mqRk=$2BhiVU8?R%Yjg)!doxRWrts}e!ETxvoZ@cLXHp`{k1`L@w{ zvfYi^bM2ZAQ*R)<^cvT2dBD3}!A4l{Qgy(?Ka5w%S%N`q=|SNzmji>epz>DqEx9S2 zL0>1=PW>k7FXdWG7f(|ZKOvf%Ajz&H|Io}J<#e=^5o+|k75`ADN{mD-Yg$V09Yh*I z9n+@GiRR(HwK(#VAC<@X{_|(ZR|FRHOFVOZhNESl<~{_@%e_d*4g#*yHgc5g{XNOI zaZ1Zr>4EN&m_wZQ{03T5epSh0!-(k$M%$A53Lc$3=8a`MoFzPN0v2L1A&)WPoEV@+ zOh78;&p4jSzZEf5DsZon5J^OFFyuRRnD?YM%`M6By*&r^M1;gs&AN4h#W>W4J>q77 zWgIK+2i^R99D=P^Rj$CBe0hIkAdi0Qob4L!y$p?v4*ys45uVoG;4J^yIuYoR+;4oR{r4>~W zx=ee}9JP{Qs{A=@&MAd0jY}VrmN|^cNany_ArhQr%>JJob5p<1S@)4g7<-aF_Ox=v z?PW)M$8l=2={A(H@=#4E=ho=aHC&N;8?&Zgk?9GFK3c${NW?T89RPH)r1QJy_>Y41 zfs1d4=%R0Sfm=UCM1BU4gUwjz{o*TTD@H65&ve6fb@U}CQnuL~3kta@un4jtu*Kh_ zwn49a8K`*biK5fa+vH}xi`6jW0=UBo!;CVcb1QGKyS-8M>Tx@EQkO!SlB#~5kL1N; zXZs&EHQE1ARM<@cEr*{~$uK-s6ORir1lw?KuC{ito7Q^<>20q+f$w!O!)Y^VQ1E)_ zDQ?t);P%+4j$m}z(o(hJsfs4%tFlAcka=N@2`sgS6l70mq{NS@rNqyv#+7(=#W@T# z(+9wx5s#@HXU7H{X2;CmA83bJyJE$h9Tvlj0AJ{I-CMIT73Wb3?=`Tfdp41|J}%@Xx&{%DHdw$T(d6^f5krR>RW7o z0iWY&QI^5x&!r0camO!%p&2<_{?-H+!|tC08ka&8%00l1wNr7mx8!!uv70tFaLZFl zH1`#7&!N#pF%HY7RmabcU}Q_>ytiiyWDgiwKBO752{#u+CU(NW>KA;m5s)!b^-CT5 zliVHg`-C;Kt!kXkE9bquerMeoVt16&E?G|(n`84j>LqGgG>Rb{2nU9(UuOvT3P`PM ze-nC$0B4m0T8axw2k_$4zJFR3$1~(t#Di4B=sQZR>ij4S_DEeRGzxGyQ!DD0@nM+T zJQ4b2>q1DT*wjjfPP3+(8J)JGRhZhUSQzcT+ElnhBoV>pcs+}eSSU5Uo9G0)GSI}l zsfTA6;yp|}VIHsY#n+;pV>fZ%9nXa&wb9oi7Qz9Le=+%h3*%0x>KFg*amCe-%mOwlx{k#7A?(fuS+_7C)kYu~+jrg2w zd74?@OLByHq@Av0vD)K7?%IXLz83n9P;0m@Eb**@LGP3YV&3`vdo}I<@y{J5R@ej6 zZR%kqyuX^`+N`bpfA~AaY;)e7(a)cbw&dta^!b;N+_|c}GzI4-nH0g!lr5w0){^nN z1mCX;rfN0w_SP0Wt8XBfU&=q75U8T%+pO*1t|NVYZC&bL!Vj`VtEI?%R3e=e)zNig!>`$t zwgg4>BZmq-#Y3%eMu;p5uH>O>t;HT&yRG(6o`ZNnpiY-3Wsz0R=Cwf= zy!P~C?yyibsGIUh>U-DW;-k>4>TxUNk$x1h$;BXjx*cZbSn8hd5H~auI$h#+H=7s6 zr%TX(tF;0fPD58%03a0kAJQ12u8R$ML*v%b%=LJfep$ia3g%o}Gp&f(hGFNw>c%^W zl}Gvr>?#{Bd0BE`O!SQ_-}c>hs=w|retlV%A!MrNjj6yBcg1b6+_o|}G%G7kL**_} zkxmle$dKeJ*@48O(}6#$v1HKn-;UaS->P@um7`)LIOu3tNzDvH535Sp8!xOElL^P* zMre3oi2)X?)Sy1k(44w{pj_x4xoES&ac9J;#=(hECuWn1xG)QQ#hevgqE9(VYTn>f zDuEmB|!gZt7 z5z*ETr@40QN)}~G2Bb@Dbw+IdMCv!N%CyK5Z!fG5&tK+5oixvtH7w=KL9Ef1NuZP` z2u*Fn7i22Ifm3zigeEd|#E2l$JNpYuFL^}W-{jmRhcr3FLr7tU21zkp2!WYtjbSt? z3Oj*|qh@utsR3_!ImRk7>?G10tFrJaU?jSK0*uANm)Y=QsSC)g8&Zy;QAv3&9wnI# zw5kkB82TYRrFt$G(G#XIgo-=dESpLqpKweGf3R&G4WsWU|62K@3Vy@VBy`J{YHwlk zfJA)=n?!;E=c5xZSuC`ot;!6|l0NOH?6^sewQc54)wp08CgN-nEl!ku#7ip0CI)uBvZ(&-`VU$WMIDS& z)Z5SS84{tspCt!S-ha(=ej&-G$gDiRCD&J4V=G<@gtkL8^9MYibuIF^n)bPZ5Q^`y z6_!XSQ-Tz-LUNRggX|y&n7m-RGSi1XvY(Z9Rx2dIefcXseU}p@TQ{msgs97Ifna`7 z5%MqO9%1NS(<8+mTVmv-r-AKvPu-2@%WKVR81> zc&K`g^i$>mQn8V`!AnYdCi79pR#qua>0{WtNDD3I=t#!uTuc+4I!|!YUSTymd4rou29-MLx)o7_A_D|wxhoe!UJYUS;bXUNx68@#K$3U#BXNQK+`e>RYH5; z?^-t?sj2bdt{0rF^nUr{LMm6Uvxd9d?zyPxVlJ)=a$z|?XMC?NWOy>(0D2C4qE`)H z1Z}aaO2eBDT=ZAe+DBc9SR}lX({Ri>xs*zD;I~8D?kM=yuFt2SCQUgZCtI5&_=`HO zNz-#$+CbpM6e%!>kpY)h#f8*un>5uCtFk`-Is%U|3O+PTO_R*d7cLtW(2onVWaCH2aU-ofvw`6}e&0bDc&Yo<(PL5}!>(1-nZ@b0cLT5W! z)4tZ|+3YaHV)p^V_}7K;aCp=7ASCAU3f>6-nc8FCb-WG7b-x$C(CV3Od=otjgIpaJL{|5G?8ZB~Y%M%#%y z!PCg(^9{WUyiXK7-RZ|_$<_FG@@n^&<9~bq_7`jx>CDiYlmigi%h3ZJx@Un>y(TR1 zIvH)&bth|h#ZXSsRd`5J%Y*?Q9WqU}P?526h0qcohr)Z- zz@OT!392=IFm}+bA3rbyHb7FHc0%552z5*Duu(Pm{*zT7B6Q3n>J*GAXuw5yj*d8l zFLCM1G;qGjGO{Fnq5cxfcM!8lWMI@vqS}cnk@S{V(II?~b;2z^x?i?H+sGu8k_@yf zsu4V=K~d@{qTu#3U86tAE)PJCQrguRyl`heWKC=vrJ!cC{zLyuHo&+uEvzJFwZC?A=_>Oo#68?2=md$7XN(9?u7b@N#G%Cw< z3U90D=GGcrp4FWarxNTaqsB5@8DYj6axF7MB11RcAAY1@jO`STkZ9V3W-wk>>N~*y z+;K)pq&R^#5Wp{J(m|BbvO8}KX1SuX1WD7noIzh3G#d><=>?p^NmbcZ79@Y9KjW(( zINeoO0nzT&iRaX4^V-f2eW3Z89Q6xL^EciKvDETnoWXoz*lreEipBlkx1NXFS{zQ6 zUgwQ-7p{eiwI4wEzi#)|-H^)Fq8}<0%CZB@sV@S?`bed_bF#~S1TCpA!Da*@83iwU zuUpN_i)txzpUH?bjusHM*L7AbU=NTLz3w5l5{!6x$77txQ>RwC#@qA} zMmCTo^|2N=maB*>4Hjx^z&wif7Q<@H%es8_vR0)PtihzbIRNB>1yagtQ$S z-JIE0mNg4%>_ucy>#?ZQ%Onwf_YR}Nal?pl=&SDR3#VXILnB1`#)h}fp`(wEJ__PD z!!n@Ag0A71e00ufU%^t!f^I;eLX@14N%0kt&7!s^)0YX8%9R+F(i7!g-CSps{F(3sWJ0aQZ*Nz@ z$X-O2{F$>83LakDYxyoDe+YFTv5zkj&qo|V#vs?1ncS(kB8E(#UHGVm#~Edc#xuZ@ z^fCi`J58fdX3csA3GCKnZMs?jAC1r}5_oG_HbPE+%ph0B!%6u< ziM(1miRdgmei%_|D6=3A=eUO6UOA<^%fg}pB0w`6PM^m7HMlw<@()4qgZTpzH<-?& zBIAaDf8L@6-tYI(adGY_@`9dq0p4T6YFxF|`M$6>RkDbm_n)zADj{9>EhfHq-#)iAM?eHPje7j%(z#D9tQ zP9A8Yf#TDHx?lwX+Ic7iA=-{=p{me6&g5uJJWvxV7)270``dx)mqn5l6+ z!-b>hLUPB`6zB^odGGTU2;9jZ-YgN7X2ls}i#3?$)`~e}y#EH9@f+iS)FF>*1NJUX zB!;`=YF2;2xnFW9Vb@(lEJFA0uUT(|a3H_riu^=`G8j=E@RF01BF5kt9e@^_k^v1| z`desE(X8Kg_=SjQPrOz#gow|@7k@E1L=m5aO#sR&1(Pit0i#vQi;>h(!8*F>*Z!3L zde}gO{8_a~1 zNjESMW^hKz%+ir#t9LO&NG7nYs-o@*FEsvQ6i2KuNO}PqfH-MkIb=1Nn=|Q53G&)* z?f!55>F|F(wM4xD5ru|jotyBHK-~eKBWD#% zCIgaNm2CoMPS!$8*asZ)W=um+YDj`!A%n{ZgUR5xgO4-ZU2uzzX)X=zqZ|~qP-MHC z5@1L4>70Nf087zzaJu+S;r)F5%&TamQXX5xxXYkDXkkbAbEM}AfZT?X=iT2EIuP4c z&g3+@cjLHN^pTKmEoLTva_t_1VH+%)*?Rsnq5o&z;DJi7*@#_2>1Ai1``*)g!7qUA!>Ey@2&u@!?mB&O1qHtb zCMb`<_&U74K5;r_zMT{bUfcW#%GTW?KTtsi^MZ3ecr92)5Ne z?>{69y!MYrRmt}Pg(7tL0>}dY?vRV+^#|3K`|A{(`u@>!j?4G?!;=hVHBF5-`s{n_ zLxCUePbR=C{v~Gy;;!y~^YA)(nC*PIkzd;QrM=QHrBdkm5tNznbiKM$RW=yDMJn>h zKbRovLLFODB+R*E?gH`XJ+kQ~0KZ;u$t9Eb28nzympsQ$q_LYhDhN}pGdgB#V?TOC zLmCSj_pV9p8~+P?r4~Aof9Rj}7xq@ePQc^QJ;PRlZEmg%9AjM6itq(U{=(=JTU0D} z@ESGuoMzx|g9u%lsxbyZxX-^8>MDkppdzPAZ>Rn=T{}ZcdBxQqxOtTI40aB$A(}Ww z+*cSnto`PPDnWyJbJMw5g@8j?IC0$rRr5<#wN;PVZVe~v-a%4oFF-*!((SiaTxmiU zP5WZ&&m=xZYiRe3mm)&fjC>E>Pcz(ATP@E&OI3H-2!iu}K@RV$@ppBBHUZ6n4b%oz zY&JZjJvd;JY65y@Z+r#LO;MAR%BAuL12m$q=#_Odia~)9eMVnJZp`F*& z<vG7xZVOML%2JBq74JA2oAtu-UTR}Xtz zMstyw>UL&(xBDECnV#fZLw>Zb)aR)oBS9ys7*MJ4yP@4>ygiQ|W{~S0{KTjz-xgUG zlc|yNWg7XU8gRaTx<}(ps%GW=>2MkxM|I(! zuV@ot=EdYbuyY_SABA$ySVlH@{UR);05{<#A;(iZJdc8L(q=`}u8PN*HT$UxM6FsD zHX3AEqjw<4a@LdUPI<)u z+d?Ds+V0iq-NH;KJCGo-FU0gA*lTUo#Wd!T@7agokMxeMh$-Ukipo})UmxI+_tKtI z98GzEy&nLdr$gS!(FMbKLZ#JAv&Pr|=);PHLy{Wd*G!Rmi|d$HKC+k=Gx0zsTg1*d zZO;g63-Cu&_B+BtA{qxnujJVKRUF%w-osX>&1!Vp%A+mwOSC1_`EY&E@5cpn?jdKfIXH>8CQ3mhztA!YyDt7 zCR^0Qzc$Ml2-q%0jKJ)x{a)iR$a2amnQdZOY2CY9UR}P}Cw2#pp`1RFpNE&B`D#Z5 z1~~C_L|j=MDJEwh?yx~w_il)Ua|qT247Au52h?C(oaBo+gSUb@-ySi47ln=hY;BP{ z__9TAGEFe$&?^xi2TOPzb%V^cNuPWpU4%!SY41Ya5^X4c$)a|6t%!VvSMM!uQ1;iv zLAWS?7F70}y-OlwTCu|_KytmmCB^L$|25jtq$^Z;EouA=^>34!&tSw1J)sT}*T98% zXp=x#wa}obA;MbRP;wAgE$x3r?2vIS0pkBne&3HB0180y-q%COv}s8G8CJe0!S7T8_dVP{>i<6x$h#z`A^yLZ&>8?h{}=pkqP37@ dU12CL$fPbmv>`U68yg=&s)qsZqxp}j{|5uohYA1y