From 0e5f9cc8df2af3b175e8508ee7ac51e6a2c8fda1 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Mon, 7 Nov 2022 09:11:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pqs-common/common-minio/pom.xml | 50 ++ .../com/njcn/minio/bo/MinIoUploadResDTO.java | 24 + .../main/java/com/njcn/minio/bo/Result.java | 104 +++ .../njcn/minio/config/MinIoProperties.java | 35 ++ .../java/com/njcn/minio/utils/MinIoUtils.java | 595 ++++++++++++++++++ pqs-common/pom.xml | 1 + .../com/njcn/harmonic/constant/Param.java | 9 - .../harmonic/enums/HarmonicResponseEnum.java | 9 +- .../pojo/param/AlgorithmSearchParam.java | 25 - .../java/com/njcn/harmonic/pojo/po/DataV.java | 56 -- .../harmonic/pojo/po/PmsAbnormalRules.java | 44 -- .../njcn/harmonic/pojo/po/RMpIntegrityD.java | 61 -- .../njcn/harmonic/pojo/po/RStatAbnormalD.java | 33 - pqs-harmonic/harmonic-boot/pom.xml | 5 + .../controller/DataExceptionController.java | 51 -- .../DataIntegrityRateController.java | 51 -- .../PollutionSubstationController.java | 40 ++ .../mapper/PmsAbnormalRulesMapper.java | 14 - .../harmonic/mapper/RMpIntegrityDMapper.java | 14 - .../harmonic/mapper/RStatAbnormalDMapper.java | 14 - .../harmonic/service/CustomReportService.java | 2 +- .../service/DataExceptionService.java | 20 - .../service/DataIntegrityRateService.java | 20 - .../service/PollutionSubstationService.java | 29 + .../service/impl/CustomReportServiceImpl.java | 151 ++++- .../impl/DataExceptionServiceImpl.java | 182 ------ .../impl/DataIntegrityRateServiceImpl.java | 192 ------ .../impl/PollutionSubstationServiceImpl.java | 164 ++++- pqs-job/job-executor.zip | Bin 73767 -> 0 bytes pqs-job/job-executor/pom.xml | 24 +- .../src/main/resources/bootstrap.yml | 4 + pqs-prepare/harmonic-prepare/pom.xml | 6 + ...ustomReportFeignClientFallbackFactory.java | 4 +- .../DayDataFeignClientFallbackFactory.java | 2 +- .../IntegrityFeignClientFallbackFactory.java | 4 +- ...LimitTargetFeignClientFallbackFactory.java | 4 +- .../LimitrateFeignClientFallbackFactory.java | 4 +- .../NormalFeignClientFallbackFactory.java | 2 +- .../OnlineRateFeignClientFallbackFactory.java | 4 +- .../PollutionFeignClientFallbackFactory.java | 4 +- .../controller/line/DayDataController.java | 11 +- .../controller/line/LimitrateController.java | 9 +- .../line/mapping/ExcelRptTempMapper.xml | 2 +- .../harmonic/pojo/po/DataPolluctionPO.java | 2 +- .../harmonic/pojo/po/PqsCommunicatePO.java | 3 + .../Impl/line/OnlineRateServiceImpl.java | 4 +- .../Impl/line/PollutionServiceImpl.java | 12 +- .../service/Impl/line/ReportServiceImpl.java | 130 +++- pqs-user/user-boot/pom.xml | 5 + .../src/test/java/com/njcn/MinioTest.java | 84 +++ pqs.ipr | 1 + 51 files changed, 1469 insertions(+), 851 deletions(-) create mode 100644 pqs-common/common-minio/pom.xml create mode 100644 pqs-common/common-minio/src/main/java/com/njcn/minio/bo/MinIoUploadResDTO.java create mode 100644 pqs-common/common-minio/src/main/java/com/njcn/minio/bo/Result.java create mode 100644 pqs-common/common-minio/src/main/java/com/njcn/minio/config/MinIoProperties.java create mode 100644 pqs-common/common-minio/src/main/java/com/njcn/minio/utils/MinIoUtils.java delete mode 100644 pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/param/AlgorithmSearchParam.java delete mode 100644 pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/DataV.java delete mode 100644 pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/PmsAbnormalRules.java delete mode 100644 pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RMpIntegrityD.java delete mode 100644 pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RStatAbnormalD.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataExceptionController.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataIntegrityRateController.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/PmsAbnormalRulesMapper.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RMpIntegrityDMapper.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RStatAbnormalDMapper.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataExceptionService.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataIntegrityRateService.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataExceptionServiceImpl.java delete mode 100644 pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataIntegrityRateServiceImpl.java delete mode 100644 pqs-job/job-executor.zip create mode 100644 pqs-user/user-boot/src/test/java/com/njcn/MinioTest.java diff --git a/pqs-common/common-minio/pom.xml b/pqs-common/common-minio/pom.xml new file mode 100644 index 000000000..17fba24b2 --- /dev/null +++ b/pqs-common/common-minio/pom.xml @@ -0,0 +1,50 @@ + + + + pqs-common + com.njcn + 1.0.0 + + 4.0.0 + common-minio + minioss的公共信息 + + 8 + 8 + UTF-8 + + + + + com.njcn + common-core + ${project.version} + + + com.njcn + common-web + ${project.version} + + + me.tongfei + progressbar + + + com.squareup.okhttp3 + okhttp + + + io.minio + minio + + + com.squareup.okhttp3 + okhttp + + + + + + \ No newline at end of file diff --git a/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/MinIoUploadResDTO.java b/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/MinIoUploadResDTO.java new file mode 100644 index 000000000..6447b79a5 --- /dev/null +++ b/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/MinIoUploadResDTO.java @@ -0,0 +1,24 @@ +package com.njcn.minio.bo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author hongawen + * @version 1.0.0 + * @date 2022年10月16日 18:40 + */ +@Data +public class MinIoUploadResDTO implements Serializable { + + private static final long serialVersionUID = 475040120689218785L; + private String minFileName; + private String minFileUrl; + + public MinIoUploadResDTO(String minFileName, String minFileUrl) { + this.minFileName = minFileName; + this.minFileUrl = minFileUrl; + } + +} diff --git a/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/Result.java b/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/Result.java new file mode 100644 index 000000000..4e466f4d0 --- /dev/null +++ b/pqs-common/common-minio/src/main/java/com/njcn/minio/bo/Result.java @@ -0,0 +1,104 @@ +package com.njcn.minio.bo; + +import java.io.Serializable; + +/** + * @author hongawen + * @version 1.0.0 + * @date 2022年10月16日 18:41 + */ +public class Result implements Serializable { + + private static final long serialVersionUID = 6273326371984994386L; + private Integer code; + private String msg; + private T data; + + private Result() { + this.code = 200; + this.msg = "OK"; + } + + private Result(T data) { + this.code = 200; + this.msg = "OK"; + this.setData(data); + } + + private Result(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + private Result(Integer code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + public Result setError(Integer code, String msg) { + this.setCode(code); + this.setMsg(msg); + return this; + } + + public boolean isSuccess() { + return this.getCode().equals(200); + } + + public static Result ok() { + return new Result(); + } + + public static Result ok(T data) { + return new Result(data); + } + + public static Result ok(Integer code, String msg) { + return new Result(code, msg); + } + + public static Result ok(Integer code, String msg, T data) { + return new Result(code, msg, data); + } + + public static Result error() { + return new Result(500, "failed"); + } + + public static Result error(String msg) { + return new Result(500, msg); + } + + public static Result error(Integer code, String msg) { + return new Result(code, msg); + } + + public static Result error(Integer code, String msg, T data) { + return new Result(code, msg, data); + } + + public Integer getCode() { + return this.code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return this.msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return this.data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/pqs-common/common-minio/src/main/java/com/njcn/minio/config/MinIoProperties.java b/pqs-common/common-minio/src/main/java/com/njcn/minio/config/MinIoProperties.java new file mode 100644 index 000000000..08b22ed43 --- /dev/null +++ b/pqs-common/common-minio/src/main/java/com/njcn/minio/config/MinIoProperties.java @@ -0,0 +1,35 @@ +package com.njcn.minio.config; + +import io.minio.MinioClient; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +/** + * @author hongawen + * @version 1.0.0 + * @date 2022年10月16日 18:37 + */ +@Data +@Component +@ConfigurationProperties(prefix = "min.io") +public class MinIoProperties { + + /** + * Minio 服务端ip + */ + private String endpoint; + + private String accessKey; + + private String secretKey; + + private String bucket; + + @Bean + public MinioClient getMinioClient() { + return MinioClient.builder() + .endpoint(endpoint).credentials(accessKey, secretKey).build(); + } +} diff --git a/pqs-common/common-minio/src/main/java/com/njcn/minio/utils/MinIoUtils.java b/pqs-common/common-minio/src/main/java/com/njcn/minio/utils/MinIoUtils.java new file mode 100644 index 000000000..04237d254 --- /dev/null +++ b/pqs-common/common-minio/src/main/java/com/njcn/minio/utils/MinIoUtils.java @@ -0,0 +1,595 @@ +package com.njcn.minio.utils; + +import com.njcn.minio.bo.MinIoUploadResDTO; +import com.njcn.minio.config.MinIoProperties; +import io.minio.*; +import io.minio.Result; +import io.minio.http.Method; +import io.minio.messages.Bucket; +import io.minio.messages.DeleteError; +import io.minio.messages.DeleteObject; +import io.minio.messages.Item; +import lombok.SneakyThrows; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.*; + + +@Configuration +@EnableConfigurationProperties({MinIoProperties.class}) +public class MinIoUtils { + + @Resource + private MinioClient instance; + + private static final String SEPARATOR_DOT = "."; + + private static final String SEPARATOR_ACROSS = "-"; + + private static final String SEPARATOR_STR = ""; + + // 存储桶名称 + private static final String chunkBucKet = "miniobucket"; + + /** + * 不排序 + */ + public final static boolean NOT_SORT = false; + + /** + * 排序 + */ + public final static boolean SORT = true; + + /** + * 默认过期时间(分钟) + */ + private final static Integer DEFAULT_EXPIRY = 60; + + /** + * 删除分片 + */ + public final static boolean DELETE_CHUNK_OBJECT = true; + /** + * 不删除分片 + */ + public final static boolean NOT_DELETE_CHUNK_OBJECT = false; + + /** + * 判断桶是否存在 + * @param bucketName 桶名 + * @return boolean + * @author exe.wangtaotao + * @date 2020/10/21 16:33 + */ + public boolean bucketExists(String bucketName) { + try { + return instance.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + + /** + * 创建存储桶 + * 创建 bucket + * + * @param bucketName 桶名 + */ + public void makeBucket(String bucketName) { + try { + boolean isExist = bucketExists(bucketName); + if (!isExist) { + instance.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * @return java.util.List + * @Description 获取文件存储服务的所有存储桶名称 + * @author exe.wangtaotao + * @date 2020/10/21 16:35 + */ + public List listBucketNames() { + List bucketList = listBuckets(); + List bucketListName = new ArrayList<>(); + for (Bucket bucket : bucketList) { + bucketListName.add(bucket.name()); + } + return bucketListName; + } + + /** + * @return java.util.List + * @Description 列出所有存储桶 + */ + @SneakyThrows + private List listBuckets() { + return instance.listBuckets(); + } + + + /** + * 获取对象文件名称列表 + * + * @param bucketName 存储桶名称 + * @param prefix 对象名称前缀(文件夹 /xx/xx/xxx.jpg 中的 /xx/xx/) + * @return objectNames + */ + public List listObjectNames(String bucketName, String prefix) { + return listObjectNames(bucketName, prefix, NOT_SORT); + } + + + /** + * 获取对象文件名称列表 + * + * @param bucketName 存储桶名称 + * @param prefix 对象名称前缀(文件夹 /xx/xx/xxx.jpg 中的 /xx/xx/) + * @param sort 是否排序(升序) + * @return objectNames + */ + @SneakyThrows + public List listObjectNames(String bucketName, String prefix, Boolean sort) { + boolean flag = bucketExists(bucketName); + if (flag) { + ListObjectsArgs listObjectsArgs; + if (null == prefix) { + listObjectsArgs = ListObjectsArgs.builder() + .bucket(bucketName) + .recursive(true) + .build(); + } else { + listObjectsArgs = ListObjectsArgs.builder() + .bucket(bucketName) + .prefix(prefix) + .recursive(true) + .build(); + } + Iterable> chunks = instance.listObjects(listObjectsArgs); + List chunkPaths = new ArrayList<>(); + for (Result item : chunks) { + chunkPaths.add(item.get().objectName()); + } + if (sort) { + chunkPaths.sort(new Str2IntComparator(false)); + } + return chunkPaths; + } + return new ArrayList<>(); + } + + /** + * 在桶下创建文件夹,文件夹层级结构根据参数决定 + * + * @param bucket 桶名称 + * @param WotDir 格式为 xxx/xxx/xxx/ + */ + @SneakyThrows + public String createDirectory(String bucket, String WotDir) { + if (!this.bucketExists(bucket)) { + return null; + } + instance.putObject(PutObjectArgs.builder().bucket(bucket).object(WotDir).stream( + new ByteArrayInputStream(new byte[]{}), 0, -1) + .build()); + return WotDir; + } + + + /** + * 删除一个文件 + * + * @param bucketName 桶名称 + * @param objectName /xx/xx/xxx.jpg + */ + @SneakyThrows + public boolean removeObject(String bucketName, String objectName) { + + if (!bucketExists(bucketName)) { + return false; + } + instance.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .build()); + return true; + } + + /** + * @param bucketName 桶名称 + * @param objectNames /xx/xx/xxx.jpg + * @return java.util.List + * @Description 删除指定桶的多个文件对象, 返回删除错误的对象列表,全部删除成功,返回空列表 + * @author exe.wangtaotao + * @date 2020/10/21 16:43 + */ + @SneakyThrows + public List removeObjects(String bucketName, List objectNames) { + if (!bucketExists(bucketName)) { + return new ArrayList<>(); + } + List deleteObjects = new ArrayList<>(objectNames.size()); + for (String objectName : objectNames) { + deleteObjects.add(new DeleteObject(objectName)); + } + List deleteErrorNames = new ArrayList<>(); + Iterable> results = instance.removeObjects( + RemoveObjectsArgs.builder() + .bucket(bucketName) + .objects(deleteObjects) + .build()); + for (Result result : results) { + DeleteError error = result.get(); + deleteErrorNames.add(error.objectName()); + } + return deleteErrorNames; + } + + + /** + * 获取访问对象的外链地址 + * 获取文件的下载url + * + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @param expiry 过期时间(分钟) 最大为7天 超过7天则默认最大值 + * @return viewUrl + */ + @SneakyThrows + public String getObjectUrl(String bucketName, String objectName, Integer expiry) { + expiry = expiryHandle(expiry); + return instance.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.GET) + .bucket(bucketName) + .object(objectName) + .expiry(expiry) + .build() + ); + } + + + /** + * 创建上传文件对象的外链 + * + * @param bucketName 存储桶名称 + * @param objectName 欲上传文件对象的名称 + * @return uploadUrl + */ + public String createUploadUrl(String bucketName, String objectName) { + return createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY); + } + + /** + * 创建上传文件对象的外链 + * + * @param bucketName 存储桶名称 + * @param objectName 欲上传文件对象的名称 + * @param expiry 过期时间(分钟) 最大为7天 超过7天则默认最大值 + * @return uploadUrl + */ + @SneakyThrows + public String createUploadUrl(String bucketName, String objectName, Integer expiry) { + expiry = expiryHandle(expiry); + return instance.getPresignedObjectUrl( + GetPresignedObjectUrlArgs.builder() + .method(Method.PUT) + .bucket(bucketName) + .object(objectName) + .expiry(expiry) + .build() + ); + } + + +// /** +// * 批量下载 +// * +// * @param directory +// * @return +// */ +// @SneakyThrows +// public List downLoadMore(String bucket, String directory) { +// Iterable> objs = instance.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(directory).useUrlEncodingType(false).build()); +// List list = new ArrayList<>(); +// for (io.minio.Result result : objs) { +// String objectName = null; +// objectName = result.get().objectName(); +// ObjectStat statObject = instance.statObject(StatObjectArgs.builder().bucket(bucket).object(objectName).build()); +// if (statObject != null && statObject.length() > 0) { +// String fileurl = instance.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).object(statObject.name()).method(Method.GET).build()); +// list.add(fileurl); +// } +// } +// return list; +// } +// + + /** + * @param multipartFile 文件 + * @param bucketName 桶名 + * @param directory image/ + * @return java.lang.String + * @Description 文件上传 + * @author exe.wangtaotao + * @date 2020/10/21 13:45 + */ + public MinIoUploadResDTO upload(MultipartFile multipartFile, String bucketName, String directory) throws Exception { + if (!this.bucketExists(bucketName)) { + this.makeBucket(bucketName); + } + InputStream inputStream = multipartFile.getInputStream(); + directory = Optional.ofNullable(directory).orElse(""); + String minFileName = directory + minFileName(multipartFile.getOriginalFilename()); + //上传文件到指定目录 + instance.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(minFileName) + .contentType(multipartFile.getContentType()) + .stream(inputStream, inputStream.available(), -1) + .build()); + inputStream.close(); + // 返回生成文件名、访问路径 + return new MinIoUploadResDTO(minFileName, getObjectUrl(bucketName, minFileName, DEFAULT_EXPIRY)); + } + + /** + * @param response + * @return java.lang.String + * @Description 下载文件 + * @author exe.wangtaotao + * @date 2020/10/21 15:18 + */ + public void download(HttpServletResponse response, String bucketName, String minFileName) throws Exception { + InputStream fileInputStream = instance.getObject(GetObjectArgs.builder() + .bucket(bucketName) + .object(minFileName).build()); + response.setHeader("Content-Disposition", "attachment;filename=" + minFileName); + response.setContentType("application/force-download"); + response.setCharacterEncoding("UTF-8"); + IOUtils.copy(fileInputStream, response.getOutputStream()); + } + + + /** + * 批量创建分片上传外链 + * + * @param bucketName 存储桶名称 + * @param objectMD5 欲上传分片文件主文件的MD5 + * @param chunkCount 分片数量 + * @return uploadChunkUrls + */ + public List createUploadChunkUrlList(String bucketName, String objectMD5, Integer chunkCount) { + if (null == bucketName) { + bucketName = chunkBucKet; + } + if (null == objectMD5) { + return null; + } + objectMD5 += "/"; + if (null == chunkCount || 0 == chunkCount) { + return null; + } + List urlList = new ArrayList<>(chunkCount); + for (int i = 1; i <= chunkCount; i++) { + String objectName = objectMD5 + i + ".chunk"; + urlList.add(createUploadUrl(bucketName, objectName, DEFAULT_EXPIRY)); + } + return urlList; + } + + /** + * 创建指定序号的分片文件上传外链 + * + * @param bucketName 存储桶名称 + * @param objectMD5 欲上传分片文件主文件的MD5 + * @param partNumber 分片序号 + * @return uploadChunkUrl + */ + public String createUploadChunkUrl(String bucketName, String objectMD5, Integer partNumber) { + if (null == bucketName) { + bucketName = chunkBucKet; + } + if (null == objectMD5) { + return null; + } + objectMD5 += "/" + partNumber + ".chunk"; + return createUploadUrl(bucketName, objectMD5, DEFAULT_EXPIRY); + } + + + /** + * 获取分片文件名称列表 + * + * @param bucketName 存储桶名称 + * @param ObjectMd5 对象Md5 + * @return objectChunkNames + */ + public List listChunkObjectNames(String bucketName, String ObjectMd5) { + if (null == bucketName) { + bucketName = chunkBucKet; + } + if (null == ObjectMd5) { + return null; + } + return listObjectNames(bucketName, ObjectMd5, SORT); + } + + /** + * 获取分片名称地址HashMap key=分片序号 value=分片文件地址 + * + * @param bucketName 存储桶名称 + * @param ObjectMd5 对象Md5 + * @return objectChunkNameMap + */ + public Map mapChunkObjectNames(String bucketName, String ObjectMd5) { + if (null == bucketName) { + bucketName = chunkBucKet; + } + if (null == ObjectMd5) { + return null; + } + List chunkPaths = listObjectNames(bucketName, ObjectMd5); + if (null == chunkPaths || chunkPaths.size() == 0) { + return null; + } + Map chunkMap = new HashMap<>(chunkPaths.size()); + for (String chunkName : chunkPaths) { + Integer partNumber = Integer.parseInt(chunkName.substring(chunkName.indexOf("/") + 1, chunkName.lastIndexOf("."))); + chunkMap.put(partNumber, chunkName); + } + return chunkMap; + } + + + /** + * 合并分片文件成对象文件 + * + * @param chunkBucKetName 分片文件所在存储桶名称 + * @param composeBucketName 合并后的对象文件存储的存储桶名称 + * @param chunkNames 分片文件名称集合 + * @param objectName 合并后的对象文件名称 + * @return true/false + */ + @SneakyThrows + public boolean composeObject(String chunkBucKetName, String composeBucketName, List chunkNames, String objectName, boolean isDeleteChunkObject) { + if (null == chunkBucKetName) { + chunkBucKetName = chunkBucKet; + } + List sourceObjectList = new ArrayList<>(chunkNames.size()); + for (String chunk : chunkNames) { + sourceObjectList.add( + ComposeSource.builder() + .bucket(chunkBucKetName) + .object(chunk) + .build() + ); + } + instance.composeObject( + ComposeObjectArgs.builder() + .bucket(composeBucketName) + .object(objectName) + .sources(sourceObjectList) + .build() + ); + if (isDeleteChunkObject) { + removeObjects(chunkBucKetName, chunkNames); + } + return true; + } + + /** + * 合并分片文件成对象文件 + * + * @param bucketName 存储桶名称 + * @param chunkNames 分片文件名称集合 + * @param objectName 合并后的对象文件名称 + * @return true/false + */ + public boolean composeObject(String bucketName, List chunkNames, String objectName) { + return composeObject(chunkBucKet, bucketName, chunkNames, objectName, NOT_DELETE_CHUNK_OBJECT); + } + + /** + * 合并分片文件成对象文件 + * + * @param bucketName 存储桶名称 + * @param chunkNames 分片文件名称集合 + * @param objectName 合并后的对象文件名称 + * @return true/false + */ + public boolean composeObject(String bucketName, List chunkNames, String objectName, boolean isDeleteChunkObject) { + return composeObject(chunkBucKet, bucketName, chunkNames, objectName, isDeleteChunkObject); + } + + /** + * 合并分片文件,合并成功后删除分片文件 + * + * @param bucketName 存储桶名称 + * @param chunkNames 分片文件名称集合 + * @param objectName 合并后的对象文件名称 + * @return true/false + */ + public boolean composeObjectAndRemoveChunk(String bucketName, List chunkNames, String objectName) { + return composeObject(chunkBucKet, bucketName, chunkNames, objectName, DELETE_CHUNK_OBJECT); + } + + + /** + * @param originalFileName 原始名称 + * @return java.lang.String + * @Description 生成上传文件名 + * @author exe.wangtaotao + * @date 2020/10/21 15:07 + */ + private String minFileName(String originalFileName) { + String suffix = originalFileName; + if (originalFileName.contains(SEPARATOR_DOT)) { + suffix = originalFileName.substring(originalFileName.lastIndexOf(SEPARATOR_DOT)); + } + return UUID.randomUUID().toString().replace(SEPARATOR_ACROSS, SEPARATOR_STR).toUpperCase() + suffix; + } + + + /** + * 将分钟数转换为秒数 + * + * @param expiry 过期时间(分钟数) + * @return expiry + */ + private static int expiryHandle(Integer expiry) { + expiry = expiry * 60; + if (expiry > 604800) { + return 604800; + } + return expiry; + } + + static class Str2IntComparator implements Comparator { + private final boolean reverseOrder; // 是否倒序 + + public Str2IntComparator(boolean reverseOrder) { + this.reverseOrder = reverseOrder; + } + + @Override + public int compare(String arg0, String arg1) { + Integer intArg0 = Integer.parseInt(arg0.substring(arg0.indexOf("/") + 1, arg0.lastIndexOf("."))); + Integer intArg1 = Integer.parseInt(arg1.substring(arg1.indexOf("/") + 1, arg1.lastIndexOf("."))); + if (reverseOrder) { + return intArg1 - intArg0; + } else { + return intArg0 - intArg1; + } + } + } + + /*** + * 根据url地址获取对象名称 + * @author hongawen + * @date 2022/10/17 20:05 + * @param objectUrl 对象地址 + * @return String 对象名称 + */ + public static String getObjectNameByUrl(String objectUrl) { + if(objectUrl.indexOf("?") < 0){ + return "unknownFile"; + } + String objectName = objectUrl.substring(0, objectUrl.indexOf("?")); + return objectName.substring(objectName.lastIndexOf("/") + 1); + } +} diff --git a/pqs-common/pom.xml b/pqs-common/pom.xml index 7dfe0edd9..cfd3d1c8a 100644 --- a/pqs-common/pom.xml +++ b/pqs-common/pom.xml @@ -22,6 +22,7 @@ common-influxdb common-poi common-echarts + common-minio diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/constant/Param.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/constant/Param.java index b83c5980b..f5d36619f 100644 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/constant/Param.java +++ b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/constant/Param.java @@ -62,13 +62,4 @@ public interface Param { String PHASIC_TYPE = "phasic_type"; String PARENT_ID = "0"; - /** - * 算法处理 - */ - String TARGET_FREQ = "freq"; - String TARGET_RMS = "rms"; - String TARGET_RMS_LVR = "rms_lvr"; - String TARGET_V_THD = "v_thd"; - String TARGET_V_UNBALANCE = "v_unbalance"; - } diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/enums/HarmonicResponseEnum.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/enums/HarmonicResponseEnum.java index abb53039c..ad4859128 100644 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/enums/HarmonicResponseEnum.java +++ b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/enums/HarmonicResponseEnum.java @@ -26,14 +26,7 @@ public enum HarmonicResponseEnum { CUSTOM_TYPE("A00555","字典中未查询到报表模板类型"), CUSTOM_REPORT_ACTIVE("A00556","不存在激活的自定义报告模板"), CUSTOM_REPORT_EMPTY("A00557","自定义报表模板异常,模板数据为空"), - - ALGORITHM_LINE_EMPTY("A00558","算法监测点数据为空"), - ALGORITHM_FREP_RULE("A00559","该监测点频率数据异常"), - ALGORITHM_RMS_RULE("A00560","该监测点相变压数据异常"), - ALGORITHM_RMS_LVR_RULE("A00561","该监测点线变压数据异常"), - ALGORITHM_V_THD_RULE("A00562","该监测点电压总谐波畸变率数据异常"), - ALGORITHM_V_UNBALANCE_RULE("A00563","该监测点负序电压不平衡度数据异常"), - ALGORITHM_DATA_ERROR("A00564","未获取到data数据"), + CUSTOM_REPORT_FILE("A00558","上传文件服务器错误,请检查数据"), ; private final String code; diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/param/AlgorithmSearchParam.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/param/AlgorithmSearchParam.java deleted file mode 100644 index 213f0f6ef..000000000 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/param/AlgorithmSearchParam.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.njcn.harmonic.pojo.param; - -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; - -/** - * 算法通用查询参数 - * - * @author qijian - * @date 2022/10/26 - */ -@Data -public class AlgorithmSearchParam { - - @ApiModelProperty(name = "id",value = "编号") - private String id; - - @ApiModelProperty(name = "type",value = "时间类型") - private Integer type; - - @ApiModelProperty(name = "datadate",value = "查询时间") - private String datadate; - - -} diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/DataV.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/DataV.java deleted file mode 100644 index c3532cac6..000000000 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/DataV.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.njcn.harmonic.pojo.po; - -import lombok.Data; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; - -import java.time.Instant; - -/** - * DataV influxDB别名映射表 - * - * @author qijian - * @version 1.0.0 - * @createTime 2022/10/27 15:27 - */ -@Data -@Measurement(name = "data_v") -public class DataV { - - @Column(name = "time") - private Instant time; - - @Column(name = "line_id") - private String lineId; - - @Column(name = "freq_max") - private Double frepMAX; - - @Column(name = "freq_min") - private Double frepMIN; - - @Column(name = "rms_max") - private Double rmsMAX; - - @Column(name = "rms_min") - private Double rmsMIN; - - @Column(name = "rms_lvr_max") - private Double rmsLvrMAX; - - @Column(name = "rms_lvr_min") - private Double rmsLvrMIN; - - @Column(name = "v_thd_max") - private Double vThdMAX; - - @Column(name = "v_thd_min") - private Double vThdMIN; - - @Column(name = "v_unbalance_max") - private Double vUnbalanceMAX; - - @Column(name = "v_unbalance_min") - private Double vUnbalanceMIN; - -} diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/PmsAbnormalRules.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/PmsAbnormalRules.java deleted file mode 100644 index 3652ee8f8..000000000 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/PmsAbnormalRules.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.njcn.harmonic.pojo.po; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.io.Serializable; - -/** - * (PmsAbnormalRules)实体类 - * - * @author qijian - * @since 2022-10-27 14:49:22 - */ -@Data -@TableName(value = "pms_abnormal_rules") -public class PmsAbnormalRules implements Serializable { - private static final long serialVersionUID = -68797682413850371L; - /** - * 主键 - */ - private String id; - /** - * 规则类型(字典 0 数据异常 1......) - */ - private Integer type; - /** - * 指标名称 - */ - private String targetName; - /** - * 对应字段 - */ - private String target; - /** - * 下限 - */ - private Double lowerLimit; - /** - * 上限 - */ - private Double upperLimit; - -} - diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RMpIntegrityD.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RMpIntegrityD.java deleted file mode 100644 index 02a7faf29..000000000 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RMpIntegrityD.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.njcn.harmonic.pojo.po; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.io.Serializable; -import java.util.Date; - -/** - * (RMpIntegrityD)实体类 - * - * @author qijian - * @since 2022-10-26 14:10:32 - */ -@Data -@TableName(value = "r_mp_integrity_d") -public class RMpIntegrityD implements Serializable { - private static final long serialVersionUID = 320784653665837465L; - /** - * 监测点id - */ - private String measurementPointId; - /** - * 生成数据的时间,每天统计一次 - */ - private Date dataDate; - /** - * 有效接入分钟数量 - */ - private Integer effectiveMinuteCount; - /** - * 频率平均值指标数据个数 - */ - private Integer freqCount; - /** - * 相电压有效值平均值指标数据个数 - */ - private Integer phaseVoltageCount; - /** - * 线电压有效值平均值指标数据个数 - */ - private Integer lineVoltageCount; - /** - * 电压总谐波畸变率平均值指标数据个数 - */ - private Integer vThdCount; - /** - * 三相电压不平衡度平均值指标数据个数 - */ - private Integer unbalanceCount; - /** - * 监测点短时闪变、电压波动类指标数据个数 - */ - private Integer pstCount; - /** - * 监测点长时闪变指标数据个数 - */ - private Integer pltCount; - -} - diff --git a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RStatAbnormalD.java b/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RStatAbnormalD.java deleted file mode 100644 index a7e8d5d49..000000000 --- a/pqs-harmonic/harmonic-api/src/main/java/com/njcn/harmonic/pojo/po/RStatAbnormalD.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.njcn.harmonic.pojo.po; - -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.io.Serializable; -import java.util.Date; - -/** - * (RStatAbnormalD)实体类 - * - * @author qijian - * @since 2022-10-27 14:49:43 - */ -@Data -@TableName(value = "r_stat_abnormal_d") -public class RStatAbnormalD implements Serializable { - private static final long serialVersionUID = 130540916944391303L; - /** - * 时间 - */ - private Date dataDate; - /** - * 监测点ID - */ - private String measurementPointId; - /** - * 数据是否异常(0异常,1正常) - */ - private Integer valueAlarm; - -} - diff --git a/pqs-harmonic/harmonic-boot/pom.xml b/pqs-harmonic/harmonic-boot/pom.xml index 12fae3af9..b896515f6 100644 --- a/pqs-harmonic/harmonic-boot/pom.xml +++ b/pqs-harmonic/harmonic-boot/pom.xml @@ -60,6 +60,11 @@ event-api ${project.version} + + com.njcn + common-minio + ${project.version} + diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataExceptionController.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataExceptionController.java deleted file mode 100644 index da726468f..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataExceptionController.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.njcn.harmonic.controller; - -import com.njcn.common.pojo.annotation.OperateInfo; -import com.njcn.common.pojo.enums.common.LogEnum; -import com.njcn.common.pojo.enums.response.CommonResponseEnum; -import com.njcn.common.pojo.response.HttpResult; -import com.njcn.common.utils.HttpResultUtil; -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; -import com.njcn.harmonic.service.DataExceptionService; -import com.njcn.web.controller.BaseController; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author qijian - * @date 2022/10/26 - * 数据是否异常 - */ -@Validated -@Slf4j -@RestController -@RequestMapping("/dataException") -@Api(tags = "数据是否异常") -@AllArgsConstructor -public class DataExceptionController extends BaseController { - - private final DataExceptionService dataExceptionService; - - @OperateInfo(info = LogEnum.BUSINESS_COMMON) - @PostMapping("/lineDataException") - @ApiOperation("监测点数据是否异常") - @ApiImplicitParam(name = "algorithmSearchParam", value = "算法通用查询参数", required = true) - public HttpResult lineDataException(@RequestBody @Validated AlgorithmSearchParam algorithmSearchParam) { - String methodDescribe = getMethodDescribe("lineDataException"); - boolean res = dataExceptionService.lineDataException(algorithmSearchParam); - if(res){ - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); - }else { - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe); - } - - } -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataIntegrityRateController.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataIntegrityRateController.java deleted file mode 100644 index 4d6a72e35..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/DataIntegrityRateController.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.njcn.harmonic.controller; - -import com.njcn.common.pojo.annotation.OperateInfo; -import com.njcn.common.pojo.enums.common.LogEnum; -import com.njcn.common.pojo.enums.response.CommonResponseEnum; -import com.njcn.common.pojo.response.HttpResult; -import com.njcn.common.utils.HttpResultUtil; -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; -import com.njcn.harmonic.service.DataIntegrityRateService; -import com.njcn.web.controller.BaseController; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author qijian - * @date 2022/10/26 - * 数据完整率算法 - */ -@Validated -@Slf4j -@RestController -@RequestMapping("/dataIntegrityRate") -@Api(tags = "数据完整率算法") -@AllArgsConstructor -public class DataIntegrityRateController extends BaseController { - - private final DataIntegrityRateService dataIntegrityRateService; - - @OperateInfo(info = LogEnum.BUSINESS_COMMON) - @PostMapping("/lineDataIntegrityRate") - @ApiOperation("监测点日数据完整率") - @ApiImplicitParam(name = "algorithmSearchParam", value = "算法通用查询参数", required = true) - public HttpResult lineDataIntegrityRate(@RequestBody @Validated AlgorithmSearchParam algorithmSearchParam) { - String methodDescribe = getMethodDescribe("lineDataIntegrityRate"); - boolean res = dataIntegrityRateService.lineDataIntegrityRate(algorithmSearchParam); - if(res){ - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); - }else { - return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe); - } - - } -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/PollutionSubstationController.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/PollutionSubstationController.java index 5e367db50..7496ae099 100644 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/PollutionSubstationController.java +++ b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/controller/PollutionSubstationController.java @@ -6,8 +6,13 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; import com.njcn.common.utils.HttpResultUtil; import com.njcn.common.utils.LogUtil; +import com.njcn.device.pq.pojo.dto.PollutionLineDTO; +import com.njcn.device.pq.pojo.dto.PollutionSubstationDTO; +import com.njcn.harmonic.pojo.param.HarmonicPublicParam; import com.njcn.harmonic.pojo.param.PollutionSubstationQuryParam; import com.njcn.harmonic.pojo.vo.PollutionSubstationVO; +import com.njcn.harmonic.pojo.vo.PollutionVO; +import com.njcn.harmonic.service.IPollutionService; import com.njcn.harmonic.service.PollutionSubstationService; import com.njcn.web.controller.BaseController; import io.swagger.annotations.Api; @@ -41,6 +46,9 @@ public class PollutionSubstationController extends BaseController { private final PollutionSubstationService pollutionSubstationService; + private final IPollutionService pollutionService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) @PostMapping("/getPollutionSubstationData") @ApiOperation("按变电站及指标类型展示污染") @@ -52,5 +60,37 @@ public class PollutionSubstationController extends BaseController { return HttpResultUtil.assembleCommonResponseResult (CommonResponseEnum.SUCCESS, list, methodDescribe); } + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/deptSubstationRelations") + @ApiOperation("污区图-部门变电站关系") + @ApiImplicitParam(name = "param", value = "实体参数", required = true) + public HttpResult> deptSubstationRelations(@RequestBody HarmonicPublicParam param) { + String methodDescribe = getMethodDescribe("deptSubstationRelations"); + LogUtil.njcnDebug(log, "{},实体参数:{}", methodDescribe, param); + List list = pollutionSubstationService.getDeptSubstationRelations(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/getSubstationInfoById") + @ApiOperation("污区图-根据部门获取变电站详情") + @ApiImplicitParam(name = "param", value = "部门参数", required = true) + public HttpResult> getSubstationInfoById(@RequestBody HarmonicPublicParam param) { + String methodDescribe = getMethodDescribe("getSubstationInfoById"); + LogUtil.njcnDebug(log, "{},部门参数:{}", methodDescribe, param); + List list = pollutionSubstationService.getSubstationInfoById(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/getLineInfoById") + @ApiOperation("污区图-根据变电站获取监测点详情") + @ApiImplicitParam(name = "param", value = "变电站参数", required = true) + public HttpResult> getLineInfoById(@RequestBody HarmonicPublicParam param) { + String methodDescribe = getMethodDescribe("getLineInfoById"); + LogUtil.njcnDebug(log, "{},变电站参数:{}", methodDescribe, param); + List list = pollutionSubstationService.getLineInfoById(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe); + } } diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/PmsAbnormalRulesMapper.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/PmsAbnormalRulesMapper.java deleted file mode 100644 index c0432d6a7..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/PmsAbnormalRulesMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.njcn.harmonic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.njcn.harmonic.pojo.po.PmsAbnormalRules; - -/** - * PmsAbnormalRulesMapper - * - * @author qijian - * @date 2022/10/26 - */ -public interface PmsAbnormalRulesMapper extends BaseMapper { - -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RMpIntegrityDMapper.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RMpIntegrityDMapper.java deleted file mode 100644 index 908c761fd..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RMpIntegrityDMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.njcn.harmonic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.njcn.harmonic.pojo.po.RMpIntegrityD; - -/** - * RMpIntegrityDMapper - * - * @author qijian - * @date 2022/10/26 - */ -public interface RMpIntegrityDMapper extends BaseMapper { - -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RStatAbnormalDMapper.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RStatAbnormalDMapper.java deleted file mode 100644 index 47464fd72..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/mapper/RStatAbnormalDMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.njcn.harmonic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.njcn.harmonic.pojo.po.RStatAbnormalD; - -/** - * RStatAbnormalDMapper - * - * @author qijian - * @date 2022/10/26 - */ -public interface RStatAbnormalDMapper extends BaseMapper { - -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/CustomReportService.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/CustomReportService.java index 92f9631a3..b9c946367 100644 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/CustomReportService.java +++ b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/CustomReportService.java @@ -31,7 +31,7 @@ public interface CustomReportService { * @author qijian * @date 2022/10/18 */ - boolean updateCustomReportTemplate(ReportTemplateParam reportTemplateParam); + boolean updateCustomReportTemplate(ReportTemplateParam.UpdateReportTemplateParam reportTemplateParam); /** * 根据id获取模板 diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataExceptionService.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataExceptionService.java deleted file mode 100644 index 3fe269b18..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataExceptionService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.njcn.harmonic.service; - -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; - -/** - * 数据是否异常 - * - * @author qijian - * @version 1.0.0 - * @createTime 2022/10/26 - 10:09 - */ -public interface DataExceptionService { - - /** - * 监测点数据是否异常 - * @author qijian - * @date 2022/10/26 - */ - boolean lineDataException(AlgorithmSearchParam algorithmSearchParam); -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataIntegrityRateService.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataIntegrityRateService.java deleted file mode 100644 index 8fcbec654..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/DataIntegrityRateService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.njcn.harmonic.service; - -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; - -/** - * 数据完整率算法 - * - * @author qijian - * @version 1.0.0 - * @createTime 2022/10/26 - 10:09 - */ -public interface DataIntegrityRateService { - - /** - * 监测点日数据完整率 - * @author qijian - * @date 2022/10/26 - */ - boolean lineDataIntegrityRate(AlgorithmSearchParam algorithmSearchParam); -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/PollutionSubstationService.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/PollutionSubstationService.java index eaa2cc6cd..95ce639d2 100644 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/PollutionSubstationService.java +++ b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/PollutionSubstationService.java @@ -1,9 +1,13 @@ package com.njcn.harmonic.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.device.pq.pojo.dto.PollutionLineDTO; +import com.njcn.device.pq.pojo.dto.PollutionSubstationDTO; +import com.njcn.harmonic.pojo.param.HarmonicPublicParam; import com.njcn.harmonic.pojo.param.PollutionSubstationQuryParam; import com.njcn.harmonic.pojo.po.RStatPollutionSubstationM; import com.njcn.harmonic.pojo.vo.PollutionSubstationVO; +import com.njcn.harmonic.pojo.vo.PollutionVO; import java.util.List; /** @@ -26,4 +30,29 @@ public interface PollutionSubstationService extends IService getPollutionSubstationData(PollutionSubstationQuryParam pollutionSubstationQuryParam); + /** + * @Description: getDeptSubstationRelations + * @Param: [param] + * @return: java.util.List + * @Author: clam + * @Date: 2022/11/3 + */ + List getDeptSubstationRelations(HarmonicPublicParam param); + /** + * @Description: getSubstationInfoById + * @Param: [param] + * @return: java.util.List + * @Author: clam + * @Date: 2022/11/3 + */ + List getSubstationInfoById(HarmonicPublicParam param); + + /** + * @Description: getLineInfoById + * @Param: [param] + * @return: java.util.List + * @Author: clam + * @Date: 2022/11/3 + */ + List getLineInfoById(HarmonicPublicParam param); } diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/CustomReportServiceImpl.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/CustomReportServiceImpl.java index 40b88d042..c6bd76aff 100644 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/CustomReportServiceImpl.java +++ b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/CustomReportServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.njcn.common.config.GeneralInfo; import com.njcn.common.pojo.enums.common.DataStateEnum; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.harmonic.enums.HarmonicResponseEnum; @@ -29,19 +30,30 @@ import com.njcn.influxdb.config.InfluxDbConfig; import com.njcn.influxdb.param.InfluxDBSqlConstant; import com.njcn.influxdb.param.InfluxDBTableConstant; import com.njcn.influxdb.utils.InfluxDbUtils; +import com.njcn.minio.bo.MinIoUploadResDTO; +import com.njcn.minio.config.MinIoProperties; +import com.njcn.minio.utils.MinIoUtils; import com.njcn.system.api.DicDataFeignClient; import com.njcn.user.api.DeptFeignClient; import com.njcn.user.pojo.dto.DeptDTO; import com.njcn.web.utils.WebUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.influxdb.dto.QueryResult; import org.springframework.beans.BeanUtils; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.annotation.Resource; +import java.io.*; +import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -75,6 +87,14 @@ public class CustomReportServiceImpl implements CustomReportService { private final DeptTempMapper deptTempMapper; + private final GeneralInfo generalInfo; + + @Resource + private MinIoUtils minIoUtils; + + @Resource + private MinIoProperties minIoProperties; + @Resource private InfluxDbConfig influxDbConfig; @@ -89,6 +109,10 @@ public class CustomReportServiceImpl implements CustomReportService { throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_JSON); } + //文件上传到Minio服务器,存入文件名 + MinIoUploadResDTO minIoUploadResDTO = contentToMinio(reportTemplateParam.getContent()); + reportTemplateParam.setContent(minIoUploadResDTO.getMinFileName()); + //新增模板表 ExcelRptTemp excelRptTemp = new ExcelRptTemp(); BeanUtils.copyProperties(reportTemplateParam, excelRptTemp); @@ -110,7 +134,7 @@ public class CustomReportServiceImpl implements CustomReportService { } @Override - public boolean updateCustomReportTemplate(ReportTemplateParam reportTemplateParam) { + public boolean updateCustomReportTemplate(ReportTemplateParam.UpdateReportTemplateParam reportTemplateParam) { checkName(reportTemplateParam, true); //检验模板json数据规范 @@ -120,6 +144,14 @@ public class CustomReportServiceImpl implements CustomReportService { throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_JSON); } + //删除之前的文件 + ExcelRptTemp excelRptTempOld = excelRptTempMapper.selectById(reportTemplateParam.getId()); + minIoUtils.removeObject(minIoProperties.getBucket(), excelRptTempOld.getContent()); + + //文件上传到Minio服务器,存入文件名 + MinIoUploadResDTO minIoUploadResDTO = contentToMinio(reportTemplateParam.getContent()); + reportTemplateParam.setContent(minIoUploadResDTO.getMinFileName()); + //修改模板数据 ExcelRptTemp excelRptTemp = new ExcelRptTemp(); BeanUtils.copyProperties(reportTemplateParam, excelRptTemp); @@ -159,10 +191,12 @@ public class CustomReportServiceImpl implements CustomReportService { @Override public ExcelRptTemp getCustomReportTemplateById(String id) { - return excelRptTempMapper.selectById(id); + ExcelRptTemp excelRptTemp = excelRptTempMapper.selectById(id); + String contentUrl = minIoUtils.getObjectUrl(minIoProperties.getBucket(), excelRptTemp.getContent(), 7 * 24 * 60 * 60); + excelRptTemp.setContent(contentUrl); + return excelRptTemp; } - @Override public List getTemplateList(ReportSearchParam reportSearchParam) { return excelRptTempMapper.getReportTemplateList(reportSearchParam); @@ -203,7 +237,9 @@ public class CustomReportServiceImpl implements CustomReportService { List reportTemplateDTOList = new ArrayList<>(); JSONArray jsonArray = null; try { - jsonArray = JSONUtil.parseArray(excelRptTemp.getContent()); + //通过文件服务器获取 + String objectUrl = minIoUtils.getObjectUrl(minIoProperties.getBucket(), excelRptTemp.getContent(), 7 * 24 * 60 * 60); + jsonArray = JSONUtil.parseArray(urlToString(objectUrl)); jsonArray.forEach(item -> { JSONObject jsonObject = (JSONObject) item; JSONArray itemArr = (JSONArray) jsonObject.get("celldata"); @@ -294,7 +330,9 @@ public class CustomReportServiceImpl implements CustomReportService { }); } - String content = jsonArray.toString(); + //文件上传到Minio服务器,存入文件名 + MinIoUploadResDTO minIoUploadResDTO = contentToMinio(jsonArray.toString()); + String content = minIoUploadResDTO.getMinFileName(); //根据模板激活状态,判断是否进库(未激活不进库,已激活进库) if (DataStateEnum.ENABLE.getCode().equals(reportSearchParam.getActivation())) { //存入报表库 @@ -309,7 +347,7 @@ public class CustomReportServiceImpl implements CustomReportService { excelRptMapper.insert(excelRpt); } - return content; + return minIoUtils.getObjectUrl(minIoProperties.getBucket(), content, 7 * 24 * 60 * 60); } @Override @@ -468,4 +506,105 @@ public class CustomReportServiceImpl implements CustomReportService { endList.add(data); } + /** + * 上传文件到Minio + * + * @param content 文件 + * @return 成功标记 + */ + private MinIoUploadResDTO contentToMinio(String content) { + //上传到minio + String businessTempPath = generalInfo.getBusinessTempPath(); + File file = stringToFile(content, businessTempPath + File.separator + "a.json"); + MultipartFile multiFile = getMultipartFile(file); + try { + //把名称存入数据 + MinIoUploadResDTO upload = minIoUtils.upload(multiFile, minIoProperties.getBucket(), "report/"); + return upload; + } catch (Exception e) { + throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_FILE); + } + } + + /** + * 将字符串写入指定文件 + * + * @param res 原字符串 + * @param filePath 文件路径 + * @return 成功标记 + */ + public File stringToFile(String res, String filePath) { + boolean flag = true; + BufferedReader bufferedReader = null; + BufferedWriter bufferedWriter = null; + File distFile = new File(filePath); + try { + if (!distFile.getParentFile().exists()){ + distFile.getParentFile().mkdirs(); + } + bufferedReader = new BufferedReader(new StringReader(res)); + bufferedWriter = new BufferedWriter(new FileWriter(distFile)); + //先清空 + bufferedWriter.write(""); + char buf[] = new char[1024]; //字符缓冲区 + int len; + while ((len = bufferedReader.read(buf)) != -1) { + bufferedWriter.write(buf, 0, len); + } + bufferedWriter.flush(); + bufferedReader.close(); + bufferedWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return distFile; + } + + /** + * 将文件转成Multipart + * + * @param file 文件 + * @return 成功标记 + */ + private MultipartFile getMultipartFile(File file) { + FileItem item = new DiskFileItemFactory().createItem("file" + , MediaType.MULTIPART_FORM_DATA_VALUE + , true + , file.getName()); + try (InputStream input = new FileInputStream(file); + OutputStream os = item.getOutputStream()) { + // 流转移 + IOUtils.copy(input, os); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid file: " + e, e); + } + + return new CommonsMultipartFile(item); + } + + /** + * 将文件Url转成String + * + * @param objectUrl 文件url + * @return 成功标记 + */ + private String urlToString(String objectUrl) throws IOException { + URL url = new URL(objectUrl); + BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); + StringBuffer buffer = new StringBuffer(); + String line = " "; + while ((line = in.readLine()) != null){ + buffer.append(line); + } + return buffer.toString(); + } + } diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataExceptionServiceImpl.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataExceptionServiceImpl.java deleted file mode 100644 index c4ec703d6..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataExceptionServiceImpl.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.njcn.harmonic.service.impl; - -import cn.hutool.core.date.DateUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.njcn.common.pojo.exception.BusinessException; -import com.njcn.device.pq.api.LineFeignClient; -import com.njcn.device.pq.pojo.vo.LineDetailDataVO; -import com.njcn.harmonic.constant.Param; -import com.njcn.harmonic.enums.HarmonicResponseEnum; -import com.njcn.harmonic.mapper.PmsAbnormalRulesMapper; -import com.njcn.harmonic.mapper.RStatAbnormalDMapper; -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; -import com.njcn.harmonic.pojo.po.DataV; -import com.njcn.harmonic.pojo.po.PmsAbnormalRules; -import com.njcn.harmonic.pojo.po.RStatAbnormalD; -import com.njcn.harmonic.service.DataExceptionService; -import com.njcn.influxdb.config.InfluxDbConfig; -import com.njcn.influxdb.param.InfluxDBPublicParam; -import com.njcn.influxdb.param.InfluxDBSqlConstant; -import com.njcn.influxdb.param.InfluxDBTableConstant; -import com.njcn.influxdb.utils.InfluxDbUtils; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.influxdb.dto.QueryResult; -import org.influxdb.impl.InfluxDBResultMapper; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; - -/** - * 数据是否异常 - * - * @author qijian - * @version 1.0.0 - * @createTime 2022/10/26 - 10:09 - */ -@Service -@RequiredArgsConstructor -@Slf4j -public class DataExceptionServiceImpl implements DataExceptionService { - - private final InfluxDbUtils influxDbUtils; - - private final LineFeignClient lineFeignClient; - - private final PmsAbnormalRulesMapper pmsAbnormalRulesMapper; - - private final RStatAbnormalDMapper rStatAbnormalDMapper; - - @Resource - private InfluxDbConfig influxDbConfig; - - /** - * 监测点数据是否异常 - * @author qijian - * @date 2022/10/26 - */ - @Override - public boolean lineDataException(AlgorithmSearchParam algorithmSearchParam) { - //测试 -// InfluxDbUtils influxDBUtil = new InfluxDbUtils("admin", "njcnpqs", "http://192.168.1.18:8086", "pqsbase", ""); - //初始化 - InfluxDBResultMapper resultMapper = new InfluxDBResultMapper(); - String searchSql; - String sql; - QueryResult query; - DataV dataV; - Date date = DateUtil.parse(algorithmSearchParam.getDatadate()); - String lineId = algorithmSearchParam.getId(); - - //入库数据初始化 - RStatAbnormalD rStatAbnormalD = new RStatAbnormalD(); - rStatAbnormalD.setDataDate(date); - rStatAbnormalD.setMeasurementPointId(lineId); - rStatAbnormalD.setValueAlarm(0); - - //1、取出规则 - List pmsAbnormalRules = pmsAbnormalRulesMapper.selectList(null); - - //2、取出电压 - List lineIds = new ArrayList<>(); - lineIds.add(lineId); - List lineDetailList = lineFeignClient.getLineDetailList(lineIds).getData(); - if (lineDetailList.size() == 0){ - throw new BusinessException(HarmonicResponseEnum.ALGORITHM_LINE_EMPTY); - } - String scale = lineDetailList.get(0).getScale().replace("kV",""); - - //3、根据规则表进行判断 - //取前四项进行比较(相别为A) - searchSql = "MAX(freq) as freq_max,MIN(freq) as freq_min,MAX(rms) as rms_max,MIN(rms) as rms_min,MAX(rms_lvr) as rms_lvr_max,MIN(rms_lvr) as rms_lvr_min,MAX(v_thd) as v_thd_max,MIN(v_thd) as v_thd_min "; - sql = getAppend(lineId, date, searchSql, "A"); - query = influxDbUtils.query(sql); - dataV = resultMapper.toPOJO(query, DataV.class).get(0); - - //开始判断业务 - HarmonicResponseEnum harmonicResponseEnum = null; - for (PmsAbnormalRules pmsAbnormalRule : pmsAbnormalRules) { - //每项数据进行上下限比较(MAX和MIN),若有一项不在数据范围内,则为异常 - switch (pmsAbnormalRule.getTarget()) { - case Param.TARGET_FREQ: - //频率:正常比较 - if (dataV.getFrepMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getFrepMAX() > pmsAbnormalRule.getUpperLimit()){ - harmonicResponseEnum = HarmonicResponseEnum.ALGORITHM_FREP_RULE; - } - break; - case Param.TARGET_RMS: - //相电压有效值特殊处理:在【0.85p.u.,1.2p.u.】之间;p.u=电压等级/1.732 - pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * (Double.parseDouble(scale) / 1.732))); - pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * (Double.parseDouble(scale) / 1.732))); - if (dataV.getRmsMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getRmsMAX() > pmsAbnormalRule.getUpperLimit()){ - harmonicResponseEnum = HarmonicResponseEnum.ALGORITHM_RMS_RULE; - } - break; - case Param.TARGET_RMS_LVR: - //线电压有效值特殊处理:在【0.85p.u.,1.2p.u.】之间;p.u=电压等级 - pmsAbnormalRule.setLowerLimit((pmsAbnormalRule.getLowerLimit() * Double.parseDouble(scale))); - pmsAbnormalRule.setUpperLimit((pmsAbnormalRule.getUpperLimit() * Double.parseDouble(scale))); - if (dataV.getRmsLvrMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getRmsLvrMAX() > pmsAbnormalRule.getUpperLimit()){ - harmonicResponseEnum = HarmonicResponseEnum.ALGORITHM_RMS_LVR_RULE; - } - break; - case Param.TARGET_V_THD: - //电压总谐波畸变率:正常比较 - if (dataV.getVThdMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getVThdMAX() > pmsAbnormalRule.getUpperLimit()){ - harmonicResponseEnum = HarmonicResponseEnum.ALGORITHM_V_THD_RULE; - } - break; - case Param.TARGET_V_UNBALANCE: - //三相不平衡度:正常比较(相别为T) - searchSql = "MAX(v_unbalance) as v_unbalance_max,MIN(v_unbalance) as v_unbalance_min "; - sql = getAppend(lineId, date, searchSql, "T"); - query = influxDbUtils.query(sql); - dataV = resultMapper.toPOJO(query, DataV.class).get(0); - if (dataV.getVUnbalanceMIN() < pmsAbnormalRule.getLowerLimit() || dataV.getVUnbalanceMAX() > pmsAbnormalRule.getUpperLimit()){ - harmonicResponseEnum = HarmonicResponseEnum.ALGORITHM_V_UNBALANCE_RULE; - } - break; - default: - break; - } - - if (harmonicResponseEnum != null){ - //入库 - LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(RStatAbnormalD::getMeasurementPointId, lineId).eq(RStatAbnormalD::getDataDate, date); - RStatAbnormalD rStatAbnormalDOne = rStatAbnormalDMapper.selectOne(lambdaQueryWrapper); - if (Objects.isNull(rStatAbnormalDOne)){ - rStatAbnormalDMapper.insert(rStatAbnormalD); - } - throw new BusinessException(harmonicResponseEnum); - } - - } - return true; - } - - /** - * 拼装sql - * @param id,date,searchSql,tableName 参数 - * @return 结果 - */ - private String getAppend(String id, Date date, String searchSql,String type) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(InfluxDBSqlConstant.SELECT).append(searchSql) - .append(InfluxDBSqlConstant.FROM).append(InfluxDBPublicParam.DATA_V) - .append(InfluxDBSqlConstant.WHERE).append(InfluxDBPublicParam.TIME).append(InfluxDBSqlConstant.GE).append(InfluxDBSqlConstant.QM).append(DateUtil.beginOfDay(date)).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.TIME).append(InfluxDBSqlConstant.LE).append(InfluxDBSqlConstant.QM).append(DateUtil.endOfDay(date)).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.LINE_ID).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(id).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.VALUE_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(InfluxDBTableConstant.AVG).append(InfluxDBSqlConstant.QM); - if (InfluxDBTableConstant.PHASE_TYPE_T.equals(type)){ - stringBuilder.append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.PHASIC_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(InfluxDBTableConstant.PHASE_TYPE_T).append(InfluxDBSqlConstant.QM); - }else{ - stringBuilder.append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.PHASIC_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(InfluxDBTableConstant.PHASE_TYPE_A).append(InfluxDBSqlConstant.QM); - } - return stringBuilder.toString(); - } -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataIntegrityRateServiceImpl.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataIntegrityRateServiceImpl.java deleted file mode 100644 index 8a7cdfa40..000000000 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/DataIntegrityRateServiceImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.njcn.harmonic.service.impl; - -import cn.hutool.core.date.DateUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.njcn.common.pojo.exception.BusinessException; -import com.njcn.device.pq.api.LineFeignClient; -import com.njcn.device.pq.pojo.vo.LineDetailDataVO; -import com.njcn.harmonic.enums.HarmonicResponseEnum; -import com.njcn.harmonic.mapper.RMpIntegrityDMapper; -import com.njcn.harmonic.pojo.param.AlgorithmSearchParam; -import com.njcn.harmonic.pojo.po.RMpIntegrityD; -import com.njcn.harmonic.service.DataIntegrityRateService; -import com.njcn.influxdb.config.InfluxDbConfig; -import com.njcn.influxdb.param.InfluxDBPublicParam; -import com.njcn.influxdb.param.InfluxDBSqlConstant; -import com.njcn.influxdb.param.InfluxDBTableConstant; -import com.njcn.influxdb.utils.InfluxDbUtils; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.influxdb.dto.QueryResult; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; - -/** - * 数据完整率算法 - * - * @author qijian - * @version 1.0.0 - * @createTime 2022/10/26 - 10:09 - */ -@Service -@RequiredArgsConstructor -@Slf4j -public class DataIntegrityRateServiceImpl implements DataIntegrityRateService { - - private final InfluxDbUtils influxDbUtils; - - private final RMpIntegrityDMapper rMpIntegrityDMapper; - - private final LineFeignClient lineFeignClient; - - @Resource - private InfluxDbConfig influxDbConfig; - - /** - * 监测点日数据完整率 - * @author qijian - * @date 2022/10/26 - */ - @Override - public boolean lineDataIntegrityRate(AlgorithmSearchParam algorithmSearchParam) { - //测试 -// InfluxDbUtils influxDBUtil = new InfluxDbUtils("admin", "njcnpqs", "http://192.168.1.18:8086", "pqsbase", ""); - //初始化 - String searchSql; - String tableName; - String sql; - QueryResult query; - QueryResult.Series series; - List columns; - List> values; - RMpIntegrityD rMpIntegrityD = new RMpIntegrityD(); - Date date = DateUtil.parse(algorithmSearchParam.getDatadate()); - - //1、有效接入分钟数量:根据监测点编号获取统计间隔,1440 / 统计间隔 = 有效接入分钟数量 - List lineIds = new ArrayList<>(); - lineIds.add(algorithmSearchParam.getId()); - List lineDetailList = lineFeignClient.getLineDetailList(lineIds).getData(); - if (lineDetailList.size() == 0){ - throw new BusinessException(HarmonicResponseEnum.ALGORITHM_LINE_EMPTY); - } - Integer effectiveMinuteCount = 1440 / lineDetailList.get(0).getTimeInterval(); - rMpIntegrityD.setEffectiveMinuteCount(effectiveMinuteCount); - - //2、根据data_v表获取五项稳态指标日数量(count) - searchSql = "count(freq) as freqCount,count(rms) as phaseVoltageCount,count(rms_lvr) as lineVoltageCount,count(v_thd) as vThdCount,count(v_unbalance) as unbalanceCount "; - tableName = InfluxDBPublicParam.DATA_V; - sql = getAppend(algorithmSearchParam.getId(), date, searchSql, tableName); - query = influxDbUtils.query(sql); - series = getSeries(query); - - if (Objects.nonNull(series.getColumns())){ - columns = series.getColumns(); - values = series.getValues(); - for (List columnValue : values) { - for (int i = 0; i < columnValue.size(); i++) { - if (columns.get(i).equals("freqCount")) { - rMpIntegrityD.setFreqCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - }else if (columns.get(i).equals("phaseVoltageCount")) { - rMpIntegrityD.setPhaseVoltageCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - }else if (columns.get(i).equals("lineVoltageCount")) { - rMpIntegrityD.setLineVoltageCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - }else if (columns.get(i).equals("vThdCount")) { - rMpIntegrityD.setVThdCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - }else if (columns.get(i).equals("unbalanceCount")) { - rMpIntegrityD.setUnbalanceCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - } - } - } - } - - //3、根据day_flicker表获取两项闪变指标数量(count) - searchSql = "count(pst) as pstCount,count(plt) as pltCount "; - tableName = InfluxDBPublicParam.DATA_FLICKER; - sql = getAppend(algorithmSearchParam.getId(), date, searchSql, tableName); - query = influxDbUtils.query(sql); - series = getSeries(query); - - if (Objects.nonNull(series.getColumns())){ - columns = series.getColumns(); - values = series.getValues(); - for (List columnValue : values) { - for (int i = 0; i < columnValue.size(); i++) { - if (columns.get(i).equals("pstCount")) { - rMpIntegrityD.setPstCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - }else if (columns.get(i).equals("pltCount")) { - rMpIntegrityD.setPltCount(Integer.parseInt(convertDoubleToString(columnValue.get(i)))); - } - } - } - } - - //4、存库 - LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(RMpIntegrityD::getMeasurementPointId, algorithmSearchParam.getId()).eq(RMpIntegrityD::getDataDate, date); - RMpIntegrityD rMpIntegrityDOne = rMpIntegrityDMapper.selectOne(lambdaQueryWrapper); - if (Objects.nonNull(rMpIntegrityDOne)){ - rMpIntegrityDMapper.update(rMpIntegrityD,lambdaQueryWrapper); - }else{ - rMpIntegrityD.setMeasurementPointId(algorithmSearchParam.getId()); - rMpIntegrityD.setDataDate(date); - rMpIntegrityDMapper.insert(rMpIntegrityD); - } - - return true; - } - - /** - * Double转String - * @param val 参数 - * @return 结果 - */ - private String convertDoubleToString(Object val) { - DecimalFormat decimalFormat = new DecimalFormat("###################.###########"); - return decimalFormat.format(val); - } - - /** - * 取series - * @param query 参数 - * @return 结果 - */ - private QueryResult.Series getSeries(QueryResult query) { - QueryResult.Series series = new QueryResult.Series(); - List results = query.getResults(); - if (results.size() != 0) { - QueryResult.Result result = results.get(0); - if (result.getSeries() != null){ - List seriess = result.getSeries(); - if (seriess.size() != 0) { - series = seriess.get(0); - } - } - } - return series; - } - - /** - * 拼装sql - * @param id,date,searchSql,tableName 参数 - * @return 结果 - */ - private String getAppend(String id, Date date, String searchSql,String tableName) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(InfluxDBSqlConstant.SELECT).append(searchSql) - .append(InfluxDBSqlConstant.FROM).append(tableName) - .append(InfluxDBSqlConstant.WHERE).append(InfluxDBPublicParam.TIME).append(InfluxDBSqlConstant.GE).append(InfluxDBSqlConstant.QM).append(DateUtil.beginOfDay(date)).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.TIME).append(InfluxDBSqlConstant.LE).append(InfluxDBSqlConstant.QM).append(DateUtil.endOfDay(date)).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.LINE_ID).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(id).append(InfluxDBSqlConstant.QM) - .append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.PHASIC_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(InfluxDBTableConstant.PHASE_TYPE_A).append(InfluxDBSqlConstant.QM); - if (InfluxDBPublicParam.DATA_V.equals(tableName)){ - stringBuilder.append(InfluxDBSqlConstant.AND).append(InfluxDBPublicParam.VALUE_TYPE).append(InfluxDBSqlConstant.EQ).append(InfluxDBSqlConstant.QM).append(InfluxDBTableConstant.AVG).append(InfluxDBSqlConstant.QM); - } - return stringBuilder.toString(); - } -} diff --git a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/PollutionSubstationServiceImpl.java b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/PollutionSubstationServiceImpl.java index a4ace95cb..075163a48 100644 --- a/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/PollutionSubstationServiceImpl.java +++ b/pqs-harmonic/harmonic-boot/src/main/java/com/njcn/harmonic/service/impl/PollutionSubstationServiceImpl.java @@ -8,21 +8,27 @@ import com.njcn.common.pojo.exception.BusinessException; import com.njcn.device.pq.api.GeneralDeviceInfoClient; import com.njcn.device.pq.api.LineFeignClient; import com.njcn.device.pq.api.SubstationFeignClient; -import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO; -import com.njcn.device.pq.pojo.dto.PollutionSubstationDTO; -import com.njcn.device.pq.pojo.dto.SubstationDTO; +import com.njcn.device.pq.pojo.dto.*; +import com.njcn.harmonic.mapper.RMpPollutionDPOMapper; +import com.njcn.harmonic.mapper.RStatPollutionOrgMPOMapper; import com.njcn.harmonic.mapper.RStatPollutionSubstationMMapper; import com.njcn.harmonic.pojo.param.HarmonicPublicParam; import com.njcn.harmonic.pojo.param.PollutionSubstationQuryParam; +import com.njcn.harmonic.pojo.po.RMpPollutionDPO; +import com.njcn.harmonic.pojo.po.RStatPollutionOrgMPO; import com.njcn.harmonic.pojo.po.RStatPollutionSubstationM; import com.njcn.harmonic.pojo.vo.PollutionSubstationVO; +import com.njcn.harmonic.pojo.vo.PollutionVO; import com.njcn.harmonic.service.PollutionSubstationService; +import com.njcn.web.utils.RequestUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; @@ -52,6 +58,8 @@ public class PollutionSubstationServiceImpl extends ServiceImpl + * @Author: clam + * @Date: 2022/11/3 + */ + @Override + public List getDeptSubstationRelations(HarmonicPublicParam harmonicPublicParam) { + harmonicPublicParam.setServerName(generalInfo.getMicroServiceName()); + List list = new ArrayList<>(); + List lineList = new ArrayList<>(); + List lineInfo = new ArrayList<>(); + PollutionParamDTO paramDTO = new PollutionParamDTO(); + + String pollutionType = harmonicPublicParam.getStatisticalType ( ).getCode ( ); + + String searchBeginTime = harmonicPublicParam.getSearchBeginTime ( ).substring (0,7); + if (StringUtils.isBlank(RequestUtil.getDeptIndex())){ + return list; + } + List sub = generalDeviceInfoClient.getPracticalRunDeviceInfo(harmonicPublicParam).getData(); + sub.forEach (temp ->{ + PollutionVO pollutionVO = new PollutionVO (); + String detpid = temp.getIndex (); + String name =temp.getName (); + List subIndexes = temp.getSubIndexes ( ); + pollutionVO.setId (detpid); + pollutionVO.setName (name); + pollutionVO.setData (-1.0); + QueryWrapper rStatPollutionOrgMPOQueryWrapper = new QueryWrapper<> (); + rStatPollutionOrgMPOQueryWrapper.eq ("org_id", detpid). + eq ("pollution_type", pollutionType). + apply("DATE_FORMAT( data_date ,'%Y-%m') = '"+searchBeginTime+"'"); + RStatPollutionOrgMPO rStatPollutionOrgMPO = rStatPollutionOrgMPOMapper.selectOne (rStatPollutionOrgMPOQueryWrapper); + ; + + Optional.ofNullable (rStatPollutionOrgMPO).ifPresent (a->pollutionVO.setData (a.getValue ())); + + List subPollutionVO = new ArrayList<>(); + subIndexes.forEach (subIndex->{ + PollutionVO pollutionsubVO = new PollutionVO (); + PollutionSubstationDTO pollutionSubstationDTO = lineFeignClient.getSubstationInfo(subIndex).getData(); + String id = pollutionSubstationDTO.getId ( ); + pollutionsubVO.setId (id); + pollutionsubVO.setName ( pollutionSubstationDTO.getName ()); + pollutionsubVO.setPid (temp.getIndex ()); + pollutionsubVO.setData (-1.0); + QueryWrapper wrapper = new QueryWrapper<> (); + wrapper.eq ("substation_id",id). + eq ("pollution_type", pollutionType). + apply("DATE_FORMAT( data_date ,'%Y-%m') = '"+searchBeginTime+"'"); + RStatPollutionSubstationM rStatPollutionSubstationM = pollutionSubstationMMapper.selectOne (wrapper); + + Optional.ofNullable (rStatPollutionSubstationM).ifPresent (t->pollutionsubVO.setData (t.getValue ())); + subPollutionVO.add (pollutionsubVO) ; + }); + pollutionVO.setChildren (subPollutionVO); + + list.add (pollutionVO); + }); + + if (!CollectionUtils.isEmpty(list)){ + return list.stream().sorted(Comparator.comparing(PollutionVO::getData).reversed().thenComparing(PollutionVO::getName)).collect(Collectors.toList()); + } + return list; + } + + /** + * @Description: getSubstationInfoById + * @Param: [param] + * @return: java.util.List + * @Author: clam + * @Date: 2022/11/3 + */ + @Override + public List getSubstationInfoById(HarmonicPublicParam deptParam) { + deptParam.setServerName(generalInfo.getMicroServiceName()); + List list = new ArrayList<>(); + List sub = generalDeviceInfoClient.getPracticalRunDeviceInfoAsSubstation(deptParam).getData(); + + String pollutionType = deptParam.getStatisticalType ( ).getCode ( ); + SimpleDateFormat s = new SimpleDateFormat(); + String searchBeginTime = deptParam.getSearchBeginTime ( ).substring (0,7); + sub.forEach(item->{ + PollutionSubstationDTO pollutionSubstationDTO = lineFeignClient.getSubstationInfo(item.getIndex()).getData(); + + QueryWrapper wrapper = new QueryWrapper<> (); + wrapper.eq ("substation_id",pollutionSubstationDTO.getId ()). + eq ("pollution_type", pollutionType). + apply("DATE_FORMAT( data_date ,'%Y-%m') = '"+searchBeginTime+"'"); + RStatPollutionSubstationM rStatPollutionSubstationM = pollutionSubstationMMapper.selectOne (wrapper); + + Optional.ofNullable (rStatPollutionSubstationM).ifPresent (t->pollutionSubstationDTO.setData (t.getValue ())); + + list.add(pollutionSubstationDTO); + }); + if (!CollectionUtils.isEmpty(list)){ + return list.stream().sorted(Comparator.comparing(PollutionSubstationDTO::getData).reversed().thenComparing(PollutionSubstationDTO::getName)).collect(Collectors.toList()); + } + return list; + } + + /** + * @Description: getLineInfoById + * @Param: [param] + * @return: java.util.List + * @Author: clam + * @Date: 2022/11/3 + */ + @Override + public List getLineInfoById(HarmonicPublicParam harmonicPublicParam) { + harmonicPublicParam.setServerName(generalInfo.getMicroServiceName()); + List list = new ArrayList<>(); + List line = new ArrayList<>(); + + + String pollutionType = harmonicPublicParam.getStatisticalType ( ).getCode ( ); + + String searchBeginTime = harmonicPublicParam.getSearchBeginTime ( ).substring (0,7); + PollutionParamDTO paramDTO = new PollutionParamDTO(); + if (StringUtils.isBlank(RequestUtil.getDeptIndex())){ + return list; + } + List sub = generalDeviceInfoClient.getPracticalRunDeviceInfoAsSubstation(harmonicPublicParam).getData(); + sub.forEach(item->{ + if (Objects.equals(harmonicPublicParam.getId(),item.getIndex())){ + if (!CollectionUtils.isEmpty(item.getLineIndexes())){ + line.addAll(item.getLineIndexes()); + } + } + }); + if (!CollectionUtils.isEmpty(line)){ + paramDTO.setLineList(line); + list = lineFeignClient.getLineInfo(paramDTO).getData(); + + List lineData = rMpPollutionDPOMapper.selectMaxList ( line,pollutionType,searchBeginTime); + if (!CollectionUtils.isEmpty(lineData)){ + list.stream().map(list1->lineData.stream().filter(list2-> Objects.equals(list1.getId(),list2.getLineId ())).findAny().map(m->{ + /*todo 根据 pollutionType映射lineData取哪个字段目前先取一个值测试 */ + list1.setData(m.getVDev ()); + return list1; + })).collect(Collectors.toList()); + } + } else { + return list; + } + return list.stream().sorted(Comparator.comparing(PollutionLineDTO::getData).reversed().thenComparing(PollutionLineDTO::getName)).collect(Collectors.toList()); + } } diff --git a/pqs-job/job-executor.zip b/pqs-job/job-executor.zip deleted file mode 100644 index c0034f1bcba198c8ce7fd594d2c11cfd70ce44bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73767 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 diff --git a/pqs-job/job-executor/pom.xml b/pqs-job/job-executor/pom.xml index 27d03fa6a..061e5a8f9 100644 --- a/pqs-job/job-executor/pom.xml +++ b/pqs-job/job-executor/pom.xml @@ -66,19 +66,17 @@ harmonic-prepare ${project.version} - - - com.njcn - harmonic-prepare - 1.0.0 - compile - - - com.njcn - common-swagger - - - + + + + + + + + + + + diff --git a/pqs-job/job-executor/src/main/resources/bootstrap.yml b/pqs-job/job-executor/src/main/resources/bootstrap.yml index 819857924..263f9f63b 100644 --- a/pqs-job/job-executor/src/main/resources/bootstrap.yml +++ b/pqs-job/job-executor/src/main/resources/bootstrap.yml @@ -3,6 +3,10 @@ microservice: ename: @artifactId@ name: '@name@' version: @version@ + sentinel: + url: @sentinel.url@ + gateway: + url: @gateway.url@ server: port: 10218 #feign接口开启服务熔断降级处理 diff --git a/pqs-prepare/harmonic-prepare/pom.xml b/pqs-prepare/harmonic-prepare/pom.xml index 373ba0c74..3fe999e2f 100644 --- a/pqs-prepare/harmonic-prepare/pom.xml +++ b/pqs-prepare/harmonic-prepare/pom.xml @@ -99,6 +99,12 @@ ${org.projectlombok.version} provided + + + com.njcn + common-minio + ${project.version} + diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/CoustomReportFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/CoustomReportFeignClientFallbackFactory.java index 8016babd5..f2a94cdf8 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/CoustomReportFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/CoustomReportFeignClientFallbackFactory.java @@ -35,8 +35,8 @@ public class CoustomReportFeignClientFallbackFactory implements FallbackFactory< Enum finalExceptionEnum = exceptionEnum; return new CoustmReportFeignClient() { @Override - public HttpResult batchReport(LineParam reportParam){ - log.error("{}异常,降级处理,异常为:{}", "自定义报表预处理任务: ", throwable.toString()); + public HttpResult batchReport(@RequestBody LineParam reportParam){ + log.error("{}异常,降级处理,异常为:{}", "Date数据转Day数据: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/DayDataFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/DayDataFeignClientFallbackFactory.java index 87219d04b..0e772755f 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/DayDataFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/DayDataFeignClientFallbackFactory.java @@ -33,7 +33,7 @@ public class DayDataFeignClientFallbackFactory implements FallbackFactory finalExceptionEnum = exceptionEnum; return new DayDataFeignClient() { @Override - public HttpResult dayDataHanlder(LineParam jobParam){ + public HttpResult dayDataHanlder(@RequestBody LineParam jobParam){ log.error("{}异常,降级处理,异常为:{}", "Date数据转Day数据: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/IntegrityFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/IntegrityFeignClientFallbackFactory.java index 8e042d525..bdd909121 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/IntegrityFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/IntegrityFeignClientFallbackFactory.java @@ -30,8 +30,8 @@ public class IntegrityFeignClientFallbackFactory implements FallbackFactory finalExceptionEnum = exceptionEnum; return new IntegrityFeignClient() { @Override - public HttpResult computeDataIntegrity(LineParam lineParam) { - log.error("{}异常,降级处理,异常为:{}", "数据完整性预处理: ", throwable.toString()); + public HttpResult computeDataIntegrity(@RequestBody @Validated LineParam lineParam) { + log.error("{}异常,降级处理,异常为:{}", "数据完整性处理: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitTargetFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitTargetFeignClientFallbackFactory.java index d61790409..2c6844bd3 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitTargetFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitTargetFeignClientFallbackFactory.java @@ -34,8 +34,8 @@ public class LimitTargetFeignClientFallbackFactory implements FallbackFactory
  • finalExceptionEnum = exceptionEnum; return new LimitTargetFeignClient() { @Override - public HttpResult getLimitTargetData(LineParam lineParam) { - log.error("{}异常,降级处理,异常为:{}", "越限次数数据预处理: ", throwable.toString()); + public HttpResult getLimitTargetData(@RequestBody @Validated LineParam lineParam) { + log.error("{}异常,降级处理,异常为:{}", "越限数据: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitrateFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitrateFeignClientFallbackFactory.java index 2d4acc659..93780ce7f 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitrateFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/LimitrateFeignClientFallbackFactory.java @@ -33,8 +33,8 @@ public class LimitrateFeignClientFallbackFactory implements FallbackFactory finalExceptionEnum = exceptionEnum; return new LimitrateFeignClient() { @Override - public HttpResult limitRateHanlder(LineParam limitRateHanlderParam ){ - log.error("{}异常,降级处理,异常为:{}", "越限是否数据预处理: ", throwable.toString()); + public HttpResult limitRateHanlder(@RequestBody LineParam limitRateHanlderParam ){ + log.error("{}异常,降级处理,异常为:{}", "越限数据处理: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/NormalFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/NormalFeignClientFallbackFactory.java index dff23eb79..f614566f6 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/NormalFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/NormalFeignClientFallbackFactory.java @@ -28,7 +28,7 @@ public class NormalFeignClientFallbackFactory implements FallbackFactory getNormLimitData() { - log.error("{}异常,降级处理,异常为:{}", "告警数据预处理: ", throwable.toString()); + log.error("{}异常,降级处理,异常为:{}", "告警数据: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/OnlineRateFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/OnlineRateFeignClientFallbackFactory.java index 532b409f3..b3f509d66 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/OnlineRateFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/OnlineRateFeignClientFallbackFactory.java @@ -30,8 +30,8 @@ public class OnlineRateFeignClientFallbackFactory implements FallbackFactory finalExceptionEnum = exceptionEnum; return new OnlineRateFeignClient() { @Override - public HttpResult getOnlineRateData(LineParam lineParam){ - log.error("{}异常,降级处理,异常为:{}", "在线率数据预处理: ", throwable.toString()); + public HttpResult getOnlineRateData(@RequestBody @Validated LineParam lineParam){ + log.error("{}异常,降级处理,异常为:{}", "在线率: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/PollutionFeignClientFallbackFactory.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/PollutionFeignClientFallbackFactory.java index 00d221c43..c7063a0f8 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/PollutionFeignClientFallbackFactory.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/api/line/fallback/PollutionFeignClientFallbackFactory.java @@ -31,8 +31,8 @@ public class PollutionFeignClientFallbackFactory implements FallbackFactory finalExceptionEnum = exceptionEnum; return new PollutionFeignClient() { @Override - public HttpResult processPollutionData(LineParam lineParam){ - log.error("{}异常,降级处理,异常为:{}", "污区数据预处理: ", throwable.toString()); + public HttpResult processPollutionData(@RequestBody @Validated LineParam lineParam){ + log.error("{}异常,降级处理,异常为:{}", "污区数据: ", throwable.toString()); throw new BusinessException(finalExceptionEnum); } }; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/DayDataController.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/DayDataController.java index 826cdbada..d5f1861f9 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/DayDataController.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/DayDataController.java @@ -6,7 +6,7 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; import com.njcn.common.utils.HttpResultUtil; import com.njcn.device.pq.api.LineFeignClient; -import com.njcn.prepare.harmonic.pojo.param.LimitRateHanlderParam; +import com.njcn.prepare.harmonic.pojo.param.LineParam; import com.njcn.prepare.harmonic.service.line.DayDataService; import com.njcn.web.controller.BaseController; import io.swagger.annotations.Api; @@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -47,7 +48,8 @@ public class DayDataController extends BaseController { @ApiImplicitParam(value = "jobParam",name = "jobParam",required = true) @PostMapping("dayDataHanlder") @OperateInfo(info = LogEnum.BUSINESS_MEDIUM) - public HttpResult dayDataHanlder(@RequestBody LimitRateHanlderParam jobParam ){ + public HttpResult dayDataHanlder(@RequestBody LineParam jobParam ){ + log.info(LocalDateTime.now()+"dayDataHanlder开始执行"); String methodDescribe = getMethodDescribe("dayDataHanlder"); Boolean result = true; List indexLists = new ArrayList<> (); @@ -56,9 +58,10 @@ public class DayDataController extends BaseController { }else{ indexLists = jobParam.getLineIds (); } + String startTime = jobParam.getDataDate ()+" "+"00:00:00"; + String endTime = jobParam.getDataDate ()+" "+"23:59:59"; - - dayDataService.dayDataJobHandler (indexLists,jobParam.getStartTime (),jobParam.getEndTime ()); + dayDataService.dayDataJobHandler (indexLists,startTime,endTime); if (result){ return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); } else { diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/LimitrateController.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/LimitrateController.java index 73003c744..d0c71a76c 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/LimitrateController.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/controller/line/LimitrateController.java @@ -6,7 +6,7 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; import com.njcn.common.utils.HttpResultUtil; import com.njcn.device.pq.api.LineFeignClient; -import com.njcn.prepare.harmonic.pojo.param.LimitRateHanlderParam; +import com.njcn.prepare.harmonic.pojo.param.LineParam; import com.njcn.prepare.harmonic.service.Impl.line.LimitRateService; import com.njcn.web.controller.BaseController; import io.swagger.annotations.Api; @@ -48,7 +48,7 @@ public class LimitrateController extends BaseController { @ApiImplicitParam(value = "limitRateHanlderParam",name = "limitRateHanlderParam",required = true) @PostMapping("LimitRateHanlder") @OperateInfo(info = LogEnum.BUSINESS_MEDIUM) - public HttpResult limitRateHanlder(@RequestBody LimitRateHanlderParam limitRateHanlderParam ){ + public HttpResult limitRateHanlder(@RequestBody LineParam limitRateHanlderParam ){ String methodDescribe = getMethodDescribe("limitRateHanlder"); Boolean result = true; List indexLists = new ArrayList<> (); @@ -57,9 +57,10 @@ public class LimitrateController extends BaseController { }else{ indexLists = limitRateHanlderParam.getLineIds (); } + String startTime = limitRateHanlderParam.getDataDate ()+" "+"00:00:00"; + String endTime = limitRateHanlderParam.getDataDate ()+" "+"23:59:59"; - - limitRateService.limitRateJobHandler (indexLists,limitRateHanlderParam.getStartTime (),limitRateHanlderParam.getEndTime ()); + limitRateService.limitRateJobHandler (indexLists,startTime,endTime); if (result){ return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); } else { diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/mapper/line/mapping/ExcelRptTempMapper.xml b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/mapper/line/mapping/ExcelRptTempMapper.xml index 976991c0a..3193e7530 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/mapper/line/mapping/ExcelRptTempMapper.xml +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/mapper/line/mapping/ExcelRptTempMapper.xml @@ -2,7 +2,7 @@ - SELECT DISTINCT t1.* FROM diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/DataPolluctionPO.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/DataPolluctionPO.java index e75c41516..b6de9c596 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/DataPolluctionPO.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/DataPolluctionPO.java @@ -12,7 +12,7 @@ import java.time.Instant; * @createTime 2022/10/21 13:45 */ @Data -@Measurement(name = "data_polluction") +@Measurement(name = "harmonic_pollution") public class DataPolluctionPO { @Column(name = "line_id") diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/PqsCommunicatePO.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/PqsCommunicatePO.java index 3611ad983..4d442e0c6 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/PqsCommunicatePO.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/pojo/po/PqsCommunicatePO.java @@ -22,6 +22,9 @@ public class PqsCommunicatePO { @Column(name = "line_id") private String lineId; + @Column(name = "dev_id") + private String devId; + @Column(name = "type") private Integer type; diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/OnlineRateServiceImpl.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/OnlineRateServiceImpl.java index 6c8be660e..3460dc4a2 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/OnlineRateServiceImpl.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/OnlineRateServiceImpl.java @@ -77,7 +77,7 @@ public class OnlineRateServiceImpl implements OnlineRateService { * 获取pqs_communicate数据 */ private List getCommunicateData(String lineId){ - QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where line_id = '" + lineId +"' order by time desc limit 1 tz('Asia/Shanghai')"); + QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where dev_id = '" + lineId +"' order by time desc limit 1 tz('Asia/Shanghai')"); InfluxDBResultMapper resultMapper = new InfluxDBResultMapper(); return resultMapper.toPOJO(sqlResult, PqsCommunicatePO.class); } @@ -86,7 +86,7 @@ public class OnlineRateServiceImpl implements OnlineRateService { * 获取范围时间内的pqs_communicate数据 */ private List getCommunicateData(String lineId, String date){ - QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where time >= '" + date + " 00:00:00' and time <= '" + date + " 23:59:59' and line_id = '" + lineId +"' order by time asc tz('Asia/Shanghai')"); + QueryResult sqlResult = influxDbUtils.query("SELECT * FROM pqs_communicate where time >= '" + date + " 00:00:00' and time <= '" + date + " 23:59:59' and dev_id = '" + lineId +"' order by time asc tz('Asia/Shanghai')"); InfluxDBResultMapper resultMapper = new InfluxDBResultMapper(); return resultMapper.toPOJO(sqlResult, PqsCommunicatePO.class); } diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/PollutionServiceImpl.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/PollutionServiceImpl.java index 1ab4a4052..4eede772f 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/PollutionServiceImpl.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/PollutionServiceImpl.java @@ -106,14 +106,14 @@ public class PollutionServiceImpl implements PollutionService { pollutionList = processPollutionList(lineIdList,harmonicVoltageList,harmonicCurrentList,frequencyDeviationList,voltageDeviationList,threePhaseVoltageList,negativeSequenceList,interharmonicVoltageList,voltageFlickerList); Date dateOut = new Date(); - //入表data_polluction + //入表harmonic_pollution if (StrUtil.isNotBlank(lineParam.getDataDate())){ dateOut = DateUtil.parse(lineParam.getDataDate()); } insertPolluction(pollutionList,dateOut.getTime()); - LogUtil.njcnDebug(log, "监测点污染指标数据data_polluction插入耗时:{}", timer.intervalRestart()); + LogUtil.njcnDebug(log, "监测点污染指标数据harmonic_pollution插入耗时:{}", timer.intervalRestart()); }else { - //获取data_polluction数据 + //获取harmonic_pollution数据 pollutionList = getDataPolluction(lineParam); } //MySql入表污区图表等 @@ -153,7 +153,7 @@ public class PollutionServiceImpl implements PollutionService { List pollutionDTOList = new ArrayList<>(); InfluxDBResultMapper resultMapper = new InfluxDBResultMapper(); for (String lineId : lineList){ - String sql="SELECT * FROM data_polluction where line_id = '" + lineId +"' "+processDate(lineParam.getDataDate(),lineParam.getType()); + String sql="SELECT * FROM harmonic_pollution where line_id = '" + lineId +"' "+processDate(lineParam.getDataDate(),lineParam.getType()); QueryResult dataPolluctionResult = influxDbUtils.query(sql); List threePhaseList = resultMapper.toPOJO(dataPolluctionResult, DataPolluctionPO.class); for (DataPolluctionPO dataPolluction : threePhaseList){ @@ -624,7 +624,7 @@ public class PollutionServiceImpl implements PollutionService { } /** - * 监测点污染指标数据入表 data_polluction + * 监测点污染指标数据入表 harmonic_pollution */ private void insertPolluction(List list, long time){ List records = new ArrayList(); @@ -640,7 +640,7 @@ public class PollutionServiceImpl implements PollutionService { fields.put("i_all",item.getIAll()); fields.put("v_inharm",item.getVInharm()); fields.put("plt",item.getPlt()); - Point point = influxDbUtils.pointBuilder("data_polluction", time, TimeUnit.MILLISECONDS,tags, fields); + Point point = influxDbUtils.pointBuilder("harmonic_pollution", time, TimeUnit.MILLISECONDS,tags, fields); BatchPoints batchPoints = BatchPoints.database(influxDbUtils.getDbName()).tag("line_id", item.getLineId()).retentionPolicy("").consistency(InfluxDB.ConsistencyLevel.ALL).build(); batchPoints.point(point); records.add(batchPoints.lineProtocol()); diff --git a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/ReportServiceImpl.java b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/ReportServiceImpl.java index dcf768672..a8aef2217 100644 --- a/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/ReportServiceImpl.java +++ b/pqs-prepare/harmonic-prepare/src/main/java/com/njcn/prepare/harmonic/service/Impl/line/ReportServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alibaba.cloud.commons.lang.StringUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.njcn.common.config.GeneralInfo; import com.njcn.common.pojo.constant.BizParamConstant; import com.njcn.common.pojo.enums.common.DataStateEnum; import com.njcn.common.pojo.exception.BusinessException; @@ -16,6 +17,9 @@ import com.njcn.harmonic.pojo.dto.ReportTemplateDTO; import com.njcn.influxdb.param.InfluxDBSqlConstant; import com.njcn.influxdb.param.InfluxDBTableConstant; import com.njcn.influxdb.utils.InfluxDbUtils; +import com.njcn.minio.bo.MinIoUploadResDTO; +import com.njcn.minio.config.MinIoProperties; +import com.njcn.minio.utils.MinIoUtils; import com.njcn.prepare.harmonic.constant.Param; import com.njcn.prepare.harmonic.mapper.line.ExcelRptMapper; import com.njcn.prepare.harmonic.mapper.line.ExcelRptTempMapper; @@ -25,9 +29,18 @@ import com.njcn.prepare.harmonic.pojo.po.ExcelRptTemp; import com.njcn.prepare.harmonic.service.line.ReportService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.io.IOUtils; import org.influxdb.dto.QueryResult; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.commons.CommonsMultipartFile; +import javax.annotation.Resource; +import java.io.*; +import java.net.URL; import java.util.*; import java.util.stream.Collectors; @@ -49,6 +62,14 @@ public class ReportServiceImpl implements ReportService { private final ExcelRptMapper excelRptMapper; + private final GeneralInfo generalInfo; + + @Resource + private MinIoUtils minIoUtils; + + @Resource + private MinIoProperties minIoProperties; + @Override public boolean batchReport(LineParam reportParam) { @@ -77,7 +98,8 @@ public class ReportServiceImpl implements ReportService { for (ExcelRptTemp excelRptTemp : reportTemplateList) { try { //获取content解析数据 - jsonArray = JSONUtil.parseArray(excelRptTemp.getContent()); + String objectUrl = minIoUtils.getObjectUrl(minIoProperties.getBucket(), excelRptTemp.getContent(), 7 * 24 * 60 * 60); + jsonArray = JSONUtil.parseArray(urlToString(objectUrl)); dataList = getDataList(jsonArray); } catch (Exception e) { throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_JSON); @@ -120,7 +142,9 @@ public class ReportServiceImpl implements ReportService { //月:例如2022十月份,传入2022-10-01进行匹配,有则更新无则插入 //周:例如2022年第五周,传入2022-01-23(周一)进行匹配,有则更新无则插入 //日:直接插入,无需配对 - String afterContent = jsonArray.toString(); + //文件上传到Minio服务器,存入文件名 + MinIoUploadResDTO minIoUploadResDTO = contentToMinio(jsonArray.toString()); + String afterContent = minIoUploadResDTO.getMinFileName(); if (BizParamConstant.STAT_BIZ_DAY.equals(reportParam.getType().toString())){ rptInsert(reportParam, lineId, excelRptTemp, afterContent); }else{ @@ -299,4 +323,106 @@ public class ReportServiceImpl implements ReportService { } + /** + * 上传文件到Minio + * + * @param content 文件 + * @return 成功标记 + */ + private MinIoUploadResDTO contentToMinio(String content) { + //上传到minio + String businessTempPath = generalInfo.getBusinessTempPath(); + File file = stringToFile(content, businessTempPath + File.separator + "a.json"); + MultipartFile multiFile = getMultipartFile(file); + try { + //把名称存入数据 + MinIoUploadResDTO upload = minIoUtils.upload(multiFile, minIoProperties.getBucket(), "report/"); + return upload; + } catch (Exception e) { + throw new BusinessException(HarmonicResponseEnum.CUSTOM_REPORT_FILE); + } + } + + /** + * 将字符串写入指定文件 + * + * @param res 原字符串 + * @param filePath 文件路径 + * @return 成功标记 + */ + public File stringToFile(String res, String filePath) { + boolean flag = true; + BufferedReader bufferedReader = null; + BufferedWriter bufferedWriter = null; + File distFile = new File(filePath); + try { + if (!distFile.getParentFile().exists()){ + distFile.getParentFile().mkdirs(); + } + bufferedReader = new BufferedReader(new StringReader(res)); + bufferedWriter = new BufferedWriter(new FileWriter(distFile)); + //先清空 + bufferedWriter.write(""); + char buf[] = new char[1024]; //字符缓冲区 + int len; + while ((len = bufferedReader.read(buf)) != -1) { + bufferedWriter.write(buf, 0, len); + } + bufferedWriter.flush(); + bufferedReader.close(); + bufferedWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return distFile; + } + + /** + * 将文件转成Multipart + * + * @param file 文件 + * @return 成功标记 + */ + private MultipartFile getMultipartFile(File file) { + FileItem item = new DiskFileItemFactory().createItem("file" + , MediaType.MULTIPART_FORM_DATA_VALUE + , true + , file.getName()); + try (InputStream input = new FileInputStream(file); + OutputStream os = item.getOutputStream()) { + // 流转移 + IOUtils.copy(input, os); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid file: " + e, e); + } + + return new CommonsMultipartFile(item); + } + + /** + * 将文件Url读取,转为String + * + * @param objectUrl 文件url + * @return 成功标记 + */ + private String urlToString(String objectUrl) throws IOException { + URL url = new URL(objectUrl); + BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); + StringBuffer buffer = new StringBuffer(); + String line = " "; + while ((line = in.readLine()) != null){ + buffer.append(line); + } + return buffer.toString(); + } + + } diff --git a/pqs-user/user-boot/pom.xml b/pqs-user/user-boot/pom.xml index 614b52fb9..90e79a5d8 100644 --- a/pqs-user/user-boot/pom.xml +++ b/pqs-user/user-boot/pom.xml @@ -44,6 +44,11 @@ common-swagger ${project.version} + + com.njcn + common-minio + ${project.version} + diff --git a/pqs-user/user-boot/src/test/java/com/njcn/MinioTest.java b/pqs-user/user-boot/src/test/java/com/njcn/MinioTest.java new file mode 100644 index 000000000..9baa77ed6 --- /dev/null +++ b/pqs-user/user-boot/src/test/java/com/njcn/MinioTest.java @@ -0,0 +1,84 @@ +package com.njcn; + +import com.njcn.minio.config.MinIoProperties; +import com.njcn.minio.utils.MinIoUtils; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.io.IOUtils; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.commons.CommonsMultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @author hongawen + * @version 1.0.0 + * @date 2022年11月02日 19:49 + */ +public class MinioTest extends BaseJunitTest { + + @Resource + private MinIoUtils minIoUtils; + + @Resource + private MinIoProperties minIoProperties; + + /*** + * 上传 + */ + @Test + public void upload() throws Exception { + String strUrl = "C:\\Users\\DELL\\Desktop\\功能测试文件\\text.json"; + File file = new File(strUrl); + MultipartFile cMultiFile = getMultipartFile(file); + System.out.println(minIoUtils.upload(cMultiFile, minIoProperties.getBucket(), "day/")); + } + + + /*** + * 删除 + */ + @Test + public void removeObject(){ + String name = "day/8D113DD5CE4B4AB2ABB5E531373E3D88.txt"; + minIoUtils.removeObject(minIoProperties.getBucket(), name); + } + + + /*** + * 根据对象名获取查看的url + */ + @Test + public void getObjectUrl(){ + String name = "day/8D113DD5CE4B4AB2ABB5E531373E3D88.txt"; + System.out.println(minIoUtils.getObjectUrl(minIoProperties.getBucket(), name, 7 * 24 * 60 * 60)); + } + + + + public static MultipartFile getMultipartFile(File file) { + FileItem item = new DiskFileItemFactory().createItem("file" + , MediaType.MULTIPART_FORM_DATA_VALUE + , true + , file.getName()); + try (InputStream input = new FileInputStream(file); + OutputStream os = item.getOutputStream()) { + // 流转移 + IOUtils.copy(input, os); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid file: " + e, e); + } + + return new CommonsMultipartFile(item); + } + + + + +} diff --git a/pqs.ipr b/pqs.ipr index 317fff00c..01b3d6fec 100644 --- a/pqs.ipr +++ b/pqs.ipr @@ -289,6 +289,7 @@ +