commit 62682dcb5b169e55352e8f0739a0fe0efef7a4ba Author: wht <1309375318@qq.com> Date: Mon Mar 31 12:03:06 2025 +0800 init: plm service diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/LOG_HOME_IS_UNDEFINED/PROJECT_NAME_IS_UNDEFINED-task.log b/LOG_HOME_IS_UNDEFINED/PROJECT_NAME_IS_UNDEFINED-task.log new file mode 100644 index 0000000..e69de29 diff --git a/LOG_HOME_IS_UNDEFINED/PROJECT_NAME_IS_UNDEFINED.log b/LOG_HOME_IS_UNDEFINED/PROJECT_NAME_IS_UNDEFINED.log new file mode 100644 index 0000000..e69de29 diff --git a/commons/.gitignore b/commons/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/commons/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/commons/.mvn/wrapper/MavenWrapperDownloader.java b/commons/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/commons/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/commons/.mvn/wrapper/maven-wrapper.jar b/commons/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/commons/.mvn/wrapper/maven-wrapper.jar differ diff --git a/commons/.mvn/wrapper/maven-wrapper.properties b/commons/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/commons/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/commons/pom.xml b/commons/pom.xml new file mode 100644 index 0000000..5783f05 --- /dev/null +++ b/commons/pom.xml @@ -0,0 +1,286 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + commons + 2.1 + jar + Demo project for Spring Boot + + + 1.8 + 4.1 + 7.0.1 + 2.6.12 + 1.0.3 + + + 4.1.2 + 3.6 + 1.4.7 + 2.0.0 + 1.3.17 + 1.56 + 2.0 + 1.1.1 + 5.5.13 + 5.2.0 + 8.5.2 + 3.4.0 + 1.3.8 + 3.3.3 + 2.4.0 + + + + + + org.springframework.data + spring-data-elasticsearch + + + org.apache.commons + commons-lang3 + + + + org.springframework.boot + spring-boot-starter + provided + + + + org.springframework.boot + spring-boot-starter-web + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + commons-io + commons-io + 2.6 + + + org.bouncycastle + bcprov-jdk15on + ${bcprov-jdk15on.version} + + + + org.apache.velocity + velocity-engine-core + ${velocity-engine-core.version} + + + + javax.ws.rs + jsr311-api + ${jsr311-api.version} + + + com.centricsoftware + config + + + com.centricsoftware + mybatis + + + org.projectlombok + lombok + true + + + + org.antlr + ST4 + ${st4.version} + + + + + + + com.oracle + ojdbc6 + + + + net.sourceforge.jexcelapi + jxl + ${jxl.version} + + + + org.apache.poi + poi + ${poi.version} + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + net.sf.jxls + jxls-core + ${jxl.core.version} + + + + commons-net + commons-net + ${commons-net.version} + + + + org.codehaus.jettison + jettison + ${jettson.version} + + + + javax.mail + mail + ${mail.version} + + + + jcifs + jcifs + ${jcifs.version} + + + javax.servlet + servlet-api + + + + + + com.itextpdf + itextpdf + ${itextpdf.version} + + + + com.itextpdf + itext-asian + ${itext-asian.version} + + + com.alibaba + easyexcel + ${easyexcel.version} + + + com.jayway.jsonpath + json-path + ${json-path.version} + + + + + + + + + + com.microsoft.sqlserver + mssql-jdbc + + + + com.google.zxing + core + ${zxing.core.version} + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + 2.0.0.RELEASE + + + io.github.openfeign + feign-core + + + + + io.github.openfeign + feign-core + 10.1.0 + + + io.github.openfeign + feign-okhttp + 10.1.0 + + + commons-fileupload + commons-fileupload + 1.3.3 + + + io.github.openfeign.form + feign-form + 3.8.0 + + + io.github.openfeign.form + feign-form-spring + 3.8.0 + + + + + org.apache.httpcomponents + httpcore + 4.4.9 + + + + org.springframework + spring-test + 5.2.9.RELEASE + + + + + + + AsposeJavaAPI + Aspose Java API + https://repository.aspose.com/repo/ + + + + plmservice-commons + + + src/main/resources + + license.xml + + true + + + + + diff --git a/commons/src/main/java/com/centricsoftware/commons/ant/ControllerLog.java b/commons/src/main/java/com/centricsoftware/commons/ant/ControllerLog.java new file mode 100644 index 0000000..196eed6 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/ant/ControllerLog.java @@ -0,0 +1,40 @@ +package com.centricsoftware.commons.ant; + +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +/** + * 自定义注解,拦截Controller + * @author ZhengGong + * @date 2019/6/25 + */ +@Target({ElementType.TYPE,ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface ControllerLog { + String value() default ""; + + /** + * 是否记录响应信息 + * @author liaochangjiang + * @since 2024-05-07 17:11 + */ + boolean response() default false; + + /** + * 是否接口日志 + */ + boolean interfaceLog() default false; + + /** + * 请求报文中key的字段,支持getByPath + */ + String requestId() default ""; + + /** + * 响应报文中成功的字段,支持getByPath + */ + String responseId() default ""; +} diff --git a/commons/src/main/java/com/centricsoftware/commons/ant/ServiceLog.java b/commons/src/main/java/com/centricsoftware/commons/ant/ServiceLog.java new file mode 100644 index 0000000..dea5a98 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/ant/ServiceLog.java @@ -0,0 +1,15 @@ +package com.centricsoftware.commons.ant; + +import java.lang.annotation.*; + +/** + * 自定义注解,拦截service + * @author ZhengGong + * @date 2019/6/25 + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ServiceLog { + String value() default ""; +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/PlmPageReqVo.java b/commons/src/main/java/com/centricsoftware/commons/dto/PlmPageReqVo.java new file mode 100644 index 0000000..f171193 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/PlmPageReqVo.java @@ -0,0 +1,13 @@ +package com.centricsoftware.commons.dto; + +import lombok.Data; +import lombok.ToString; +import lombok.experimental.Accessors; + +@Data +@ToString(callSuper = true) +@Accessors(chain = true) +public class PlmPageReqVo { + private Long pageNum = 1L; + private Long pageSize = 10L; +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/PlmUtilsException.java b/commons/src/main/java/com/centricsoftware/commons/dto/PlmUtilsException.java new file mode 100644 index 0000000..8c4af1d --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/PlmUtilsException.java @@ -0,0 +1,35 @@ +package com.centricsoftware.commons.dto; + +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; + +/** + * @Description: PLM工具类异常基类 + * @Author: ZhengGong + * @CreateDate: 2019/5/20 15:11 + * @Company: Centric + */ +public class PlmUtilsException extends BaseException { + + public PlmUtilsException(ResCode code) { + super(code); + } + + public PlmUtilsException(ResCode code, Object data) { + super(code,data); + } + + public PlmUtilsException(Integer code, String message) { + super(code,message); + } + + public PlmUtilsException(Integer code, String message, Object data) { + super(code,message,data); + } + + public PlmUtilsException(ResCode code, Throwable e){ + super(code); + } + + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/ResEntity.java b/commons/src/main/java/com/centricsoftware/commons/dto/ResEntity.java new file mode 100644 index 0000000..62d70f8 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/ResEntity.java @@ -0,0 +1,37 @@ +package com.centricsoftware.commons.dto; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 返回信息实体类 + * @author ZhengGong + * @date 2020/4/16 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ResEntity implements Serializable { + /** + * 错误编号 + */ + private Integer code; + /** + * 错误信息 + */ + private String msg; + /** + * 返回对象 + */ + private Object data; + /** + * 是否成功 + */ + private boolean success; +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/WebResponse.java b/commons/src/main/java/com/centricsoftware/commons/dto/WebResponse.java new file mode 100644 index 0000000..857bff9 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/WebResponse.java @@ -0,0 +1,132 @@ +package com.centricsoftware.commons.dto; + + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import lombok.extern.slf4j.Slf4j; +import org.stringtemplate.v4.ST; + +import java.net.URLEncoder; + +/** + * 接口返回信息 + * @author ZhengGong + * @date 2019/9/16 + */ +@Slf4j +public class WebResponse { + + /** + * 失败返回特定的消息实体 + * @param code 错误代码 + * @param message 错误信息 + * @param data 具体消息实体 + * @return 消息实体ResEntity + */ + public static ResEntity failure(Integer code, String message, Object data) { + return ResEntity.builder().code(code).msg(message).data(data).success(false).build(); + } + + /** + * 失败返回特定的消息实体 + * @param code 错误代码 + * @param message 错误信息 + * @return 消息实体ResEntity + */ + public static ResEntity failure(Integer code, String message) { + return ResEntity.builder().code(code).msg(message).success(false).build(); + } + + /** + * 失败返回特定的消息实体 + * @param respCode 错误代码封装枚举类 + * @param data 具体消息实体 + * @return 消息实体ResEntity + */ + public static ResEntity failure(ResCode respCode, Object data) { + return getStringObjectMap(respCode, data,false); + } + + /** + * 失败返回特定的消息实体 + * @param respCode 错误代码封装枚举类 + * @return 消息实体ResEntity + */ + public static ResEntity failure(ResCode respCode) { + return getStringObjectMap(respCode,false); + } + + /** + * 失败返回特定的消息实体 + * @param e 错误基类 + * @return 消息实体ResEntity + */ + public static ResEntity failure(T e){ + return failure(e.getCode(),e.getMessage(),e.getData()); + } + + + + /** + * 错误302到具体的页面 + * @param stp 模板链接 + * @param msg 错误信息 + * @return 页面链接 + */ + public static String failurePage(String stp,String msg) { + try { + ST st=new ST(stp); + st.add("ERROR", URLEncoder.encode(msg,"UTF-8")); + return "redirect:"+st.render(); + } catch (Exception e) { + log.error("error:",e); + return null; + } + } + + + /** + * 成功返回特定的状态码和信息 + * @param respCode 成功代码封装枚举类 + * @param data 具体消息实体 + * @return 消息实体ResEntity + */ + public static ResEntity success(ResCode respCode, Object data) { + return getStringObjectMap(respCode, data,true); + } + + private static ResEntity getStringObjectMap(ResCode respCode, Object data, Boolean success) { + return ResEntity.builder().code( respCode.getCode()).msg( respCode.getMessage()).data(data).success(success).build(); + } + + /** + * 成功返回特定的状态码和信息 + * @param respCode 成功代码封装枚举类 + * @return 消息实体ResEntity + */ + public static ResEntity success(ResCode respCode) { + return getStringObjectMap(respCode,true); + } + + private static ResEntity getStringObjectMap(ResCode respCode, Boolean success) { + return ResEntity.builder().code(respCode.getCode()).msg(respCode.getMessage()).success(success).build(); + } + + public static ResEntity failure(String message){ + return ResEntity.builder().code(ResCode.ERROR.getCode()).msg(message).success(false).build(); + } + public static ResEntity success(String message){ + return ResEntity.builder().code(ResCode.SUCCESS.getCode()).msg(message).success(true).build(); + } + + public static ResEntity autoResponse(Object obj,String error) { + if(StrUtil.isBlank(error)){ + return ResEntity.builder().code(ResCode.SUCCESS.getCode()).msg(ResCode.SUCCESS.getMessage()).success(true).data(obj).build(); + }else{ + return ResEntity.builder().code(ResCode.ERROR.getCode()).msg(error).success(false).data(obj).build(); + } + + } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumList.java b/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumList.java new file mode 100644 index 0000000..9b321d3 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumList.java @@ -0,0 +1,31 @@ +package com.centricsoftware.commons.dto.enum4cache; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; + +@Slf4j +@Data +public class EnumList { + + private String dependsOn; + + private String description; + + private List enums; + + private String nodeName; + + private List values; + + private List enumValues; + + private Map descCache;//描述:fullname 和 fullname:描述 + + private Map displayEnCache;//翻译值:fullname + + private Map displayZhCache;//翻译值:fullname + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumValue.java b/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumValue.java new file mode 100644 index 0000000..1d50c7d --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/dto/enum4cache/EnumValue.java @@ -0,0 +1,20 @@ +package com.centricsoftware.commons.dto.enum4cache; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Data +public class EnumValue { + + private boolean active; + + private String dependsOn; + + private String description; + + private String nodeName; + + private String value; + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/em/C8ImportTypeEnum.java b/commons/src/main/java/com/centricsoftware/commons/em/C8ImportTypeEnum.java new file mode 100644 index 0000000..04d136a --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/em/C8ImportTypeEnum.java @@ -0,0 +1,14 @@ +package com.centricsoftware.commons.em; + +public enum C8ImportTypeEnum { + REF, + STRING, + BOOLEAN, + ENUM, + ENUM_DESC, + ENUM_DISPLAY, + TIME, + INTEGER, + DOUBLE, + REFLIST +} \ No newline at end of file diff --git a/commons/src/main/java/com/centricsoftware/commons/em/C8NativeExportType.java b/commons/src/main/java/com/centricsoftware/commons/em/C8NativeExportType.java new file mode 100644 index 0000000..b32cf5a --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/em/C8NativeExportType.java @@ -0,0 +1,5 @@ +package com.centricsoftware.commons.em; + +public enum C8NativeExportType { + STRING, DOUBLE, INTEGER, BOOLEAN, URL, URL_ID, ENUM, ENUM_KEY, ENUM_DESC, ENUM_DISPLAY, TIME, LONG +} diff --git a/commons/src/main/java/com/centricsoftware/commons/em/ResCode.java b/commons/src/main/java/com/centricsoftware/commons/em/ResCode.java new file mode 100644 index 0000000..21e78bf --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/em/ResCode.java @@ -0,0 +1,67 @@ +package com.centricsoftware.commons.em; + +import lombok.Getter; + +/** + * 错误码枚举类 + * + * @author ZhengGong + * @date 2019/6/12 + */ +@Getter +public enum ResCode { + + SUCCESS(0, "成功!"), + OK(200, "OK"), + ERROR(201, "失败"), + REQUEST_NOT_FOUND(202, "请求不存在!"), + HTTP_BAD_METHOD(203, "请求方式不支持!"), + BAD_REQUEST(204, "请求异常!"), + PARAM_NOT_MATCH(205, "参数不匹配!"), + PARAM_NOT_NULL(206, "参数不能为空!"), + JSON_PARSE_ERROR(207, "JSON转换异常"), + C8_UNSUPPORTED_ENCODING_ERROR(208, "双编码异常"), + AUTHORIZE_ERROR(209, "请求未授权!"), + AUTHORIZE_UP_ERROR(210, "请求未授权,账号或密码错误!"), + CONFIG_NOT_INIT(666, "配置未初始化!"), + + /** + * 业务类 + */ + DMP_DATA_NULL(3001, "找不到对应的数据,或者数据已经同步成功"), + DMP_DATA_TRANSLATE_FAIL(3002, "调用DMP同步数据失败,请重试"), + DMP_FILE_URL_ILLEGAL(3003, "获取到的DMP文件路径不合法"), + DMP_FILE_PUBLISH_ERROR(3004, "DMP发布文件失败"), + SKU_CODE_NOTFOUND(3005, "SKU CODE没有找到相关的SKU数据"), + SKU_CODE_DUPLICATE(3006, "SKU CODE找出重复的数据,请联系管理员"), + SKU_CODE_EMPTY(3007, "SKU CODE为空"), + + + RECORD_LOCK(888, "数据正在执行更新,已被锁定,请稍后再试"), + SYSTEM_RUNTIME_ERROR(999, "系统异常!"); + + private Integer code; + + private String message; + + ResCode(Integer code, String message) { + this.code = code; + this.message = message; + } + + /** + * 通过code返回枚举 + * + * @param code + * @return + */ + public static ResCode parse(Integer code) { + ResCode[] values = values(); + for (ResCode value : values) { + if (value.getCode().equals(code)) { + return value; + } + } + throw new RuntimeException("Unknown code of ResultEnum"); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/exception/BaseException.java b/commons/src/main/java/com/centricsoftware/commons/exception/BaseException.java new file mode 100644 index 0000000..48318ec --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/exception/BaseException.java @@ -0,0 +1,52 @@ +package com.centricsoftware.commons.exception; + +import com.centricsoftware.commons.em.ResCode; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 异常基类 + * @author ZhengGong + * @date 2019/6/18 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class BaseException extends RuntimeException { + private Integer code; + private String message; + private Object data; + + public BaseException(ResCode resCode) { + super(resCode.getMessage()); + this.code = resCode.getCode(); + this.message = resCode.getMessage(); + } + + public BaseException(ResCode resCode, Object data) { + this(resCode); + this.data = data; + } + + public BaseException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public BaseException(Integer code, String message, Object data) { + this(code, message); + this.data = data; + } + + public BaseException(ResCode code, Throwable e){ + this(code); + data = e; + } + + public ResCode getResCode(){ + return ResCode.parse(code); + } + + + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/exception/C8UnsupportedEncodingException.java b/commons/src/main/java/com/centricsoftware/commons/exception/C8UnsupportedEncodingException.java new file mode 100644 index 0000000..f692844 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/exception/C8UnsupportedEncodingException.java @@ -0,0 +1,30 @@ +package com.centricsoftware.commons.exception; + +import com.centricsoftware.commons.em.ResCode; + +/** + * c8 uri双编码异常 + * @author zheng.gong + * @date 2020/5/12 + */ +public class C8UnsupportedEncodingException extends BaseException { + public C8UnsupportedEncodingException(ResCode code) { + super(code); + } + + public C8UnsupportedEncodingException(ResCode code, Object data) { + super(code,data); + } + + public C8UnsupportedEncodingException(Integer code, String message) { + super(code,message); + } + + public C8UnsupportedEncodingException(Integer code, String message, Object data) { + super(code,message,data); + } + + public C8UnsupportedEncodingException(ResCode code, Throwable e){ + super(code); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/exception/ParamException.java b/commons/src/main/java/com/centricsoftware/commons/exception/ParamException.java new file mode 100644 index 0000000..5adcb8c --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/exception/ParamException.java @@ -0,0 +1,30 @@ +package com.centricsoftware.commons.exception; + +import com.centricsoftware.commons.em.ResCode; + +/** + * 参数异常 + * @author ZhengGong + * @date 2019/9/16 + */ +public class ParamException extends BaseException { + public ParamException(ResCode code) { + super(code); + } + + public ParamException(ResCode code, Object data) { + super(code,data); + } + + public ParamException(Integer code, String message) { + super(code,message); + } + + public ParamException(Integer code, String message, Object data) { + super(code,message,data); + } + + public ParamException(ResCode code, Throwable e){ + super(code); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/exception/RequestArgsException.java b/commons/src/main/java/com/centricsoftware/commons/exception/RequestArgsException.java new file mode 100644 index 0000000..5fa9503 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/exception/RequestArgsException.java @@ -0,0 +1,31 @@ +package com.centricsoftware.commons.exception; + +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; + +/** + * 请求参数异常 + * @author ZhengGong + * @date 2019/9/16 + */ +public class RequestArgsException extends BaseException { + public RequestArgsException(ResCode resCode) { + super(resCode); + } + + public RequestArgsException(ResCode resCode, Object data) { + super(resCode, data); + } + + public RequestArgsException(Integer code, String message) { + super(code, message); + } + + public RequestArgsException(Integer code, String message, Object data) { + super(code, message, data); + } + + public RequestArgsException(ResCode code, Throwable e) { + super(code, e); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/CSVParser.java b/commons/src/main/java/com/centricsoftware/commons/utils/CSVParser.java new file mode 100644 index 0000000..b71530f --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/CSVParser.java @@ -0,0 +1,91 @@ +package com.centricsoftware.commons.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +/** + * csv处理工具类 + * 同类方法 {@link cn.hutool.core.text.csv.CsvUtil} + * @author zheng.gong + * @date 2020/4/27 + */ +public class CSVParser { + + public List parse(File file) throws Exception { + List result = new ArrayList(); + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)); + try { + String line = null; + while ((line = in.readLine()) != null) { + result.add(parseLine(line)); + } + } finally { + if (in != null) { + in.close(); + } + } + + return result; + } + + enum State { + Normal, QuoteBegin, Quote, QuoteEnd, + } + + public String[] parseLine(String line) { + ArrayList list = new ArrayList(); + State state = State.Normal; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < line.length(); ++i) { + char ch = line.charAt(i); + switch (state) { + case Normal: + if (ch == ',') { + list.add(sb.toString()); + sb.setLength(0); + } else if (ch == '"') { + state = State.QuoteBegin; + } else { + sb.append(ch); + } + break; + case QuoteBegin: + if (ch == '"') { + state = State.Normal; + sb.append('"'); + } else { + state = State.Quote; + sb.append(ch); + } + break; + case Quote: + if (ch == '"') { + state = State.QuoteEnd; + } else { + sb.append(ch); + } + break; + case QuoteEnd: + if (ch == ',') { + state = State.Normal; + list.add(sb.toString()); + sb.setLength(0); + } else if (ch == '"') { + state = State.Quote; + sb.append('"'); + } else { + state = State.Normal; + sb.append(ch); + } + break; + } + } + list.add(sb.toString()); + return list.toArray(new String[list.size()]); + } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/CommonUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/CommonUtil.java new file mode 100644 index 0000000..0bb9ef7 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/CommonUtil.java @@ -0,0 +1,602 @@ +package com.centricsoftware.commons.utils; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.io.*; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; + +@Slf4j +public class CommonUtil { + + public static final String DAY_FORMAT = "yyyy-MM-dd"; + public static final String DAY_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + public static final String YMD = "yyyyMMdd"; + public static final String YYMD = "yyMMdd"; + public static final String YYMD_HMS = "yyMMddHHmmss"; + public static final String YMD_DASH = "yyyy-MM-dd"; + public static final String YMD_SLASH = "yyyy/MM/dd"; + public static final String YMD_HMS = "yyyyMMddHHmmss"; + public static final String YMD_HMSS = "yyyyMMddHHmmssSSS"; + public static final String YMD_HMS_DASH = "yyyy-MM-dd HH:mm:ss"; + public static final String HMS_DASH = "HH:mm:ss"; + + + /** + * 转换timestamp + * + * @param attrvalue + * @param attrformat + * @param CLASSNAME + * @return + * @author GHUANG + * @version 2019年6月10日 上午11:56:16 + */ + public static String parseTimestamp(String attrvalue, String attrformat, String CLASSNAME) { + DateFormat format = new SimpleDateFormat(attrformat); + String svalue = ""; + try { + Timestamp ts = new Timestamp(format.parse(attrvalue).getTime()); + long tsvalue = ts.getTime() / 1000; + svalue = String.valueOf(tsvalue); + } catch (Exception e) { + log.error(CLASSNAME, e); + } + return svalue; + } + + public static String getCurrentDate(String format) { + SimpleDateFormat sm = new SimpleDateFormat(format); + return sm.format(new Date()); + } + + public static String createTimeNo() { + SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyyMMdd"); + fullDateFormat.setTimeZone(java.util.TimeZone.getTimeZone("GMT+8:00")); + String currentTime = fullDateFormat.format(new Date(System.currentTimeMillis())); + return currentTime; + } + + public static String createTime() { + Timestamp d = new Timestamp(System.currentTimeMillis()); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 定义格式 + return df.format(d); + } + + public static String createCurrentTimestamp(String CLASSNAME) { + String svalue = ""; + try { + Timestamp d = new Timestamp(System.currentTimeMillis()); + long tsvalue = d.getTime() / 1000; + svalue = String.valueOf(tsvalue); + } catch (Exception e) { + log.error(CLASSNAME, e); + } + return svalue; + } + + public static HashMap changeStr2Map(String mapStr) { + HashMap map = new HashMap(); + String cms = mapStr.replace("{", "").replace("}", ""); + String[] mapStrs = cms.split(","); + for (String s : mapStrs) { + String[] ms = s.split("="); + System.out.println(s); + if (ms.length == 2) { + map.put(ms[0], ms[1]); + } + + } + return map; + } + + public static String escapeUrl(String value) { + if (value == null) { + return ""; + } + String s = value.replaceAll("&", "&"); + s = s.replaceAll("<", "<"); + s = s.replaceAll(">", ">"); + s = s.replaceAll("\"", """); + s = s.replaceAll("'", "'"); + + return s; + + } + + /** + * 拆分List + * + * @param list + * @param len + * @return + * @author GHUANG + * @version 2019年11月5日 下午3:35:49 + */ + public static List splitList(List list, int len) { + List result = new ArrayList(); + log.info("input list={},core={}",list,len); + if (list == null || list.size() == 0 || len < 1) { + result.add(list); + } else { + + int size = list.size(); + int count = (size + len - 1) / len; + + for (int i = 0; i < count; i++) { + List subList = list.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1))); + result.add(subList); + } + } + return result; + } + + /** + * 拆分List + * + * @param map + * @param len + * @return + * @author GHUANG + * @version 2019年11月5日 下午3:35:49 + */ + public static List splitMap(LinkedHashMap> map, int len) { + List result = new ArrayList(); + if (map == null || map.size() == 0 || len < 1) { + result.add(map); + } else { + + int size = map.size(); + int count = (size + len - 1) / len; + for (int i = 0; i < count; i++) { + int fromIndex = i * len; + int toIndex = ((i + 1) * len > size ? size : len * (i + 1)); + LinkedHashMap> newmap = new LinkedHashMap>(); + int j = 0; + for (Entry> entry : map.entrySet()) { + if (j >= fromIndex && j < toIndex) { + newmap.put(entry.getKey(), entry.getValue()); + } + j++; + } + result.add(newmap); + } + } + return result; + } + + public static String escapeSpecUrl(String value) { + if (value == null) { + return ""; + } + String s = value.replaceAll("&", "&"); + s = s.replaceAll("\"", """); + s = s.replaceAll("'", "'"); + + // s = stripBadChar(s); + return s; + + } + + public static String getExtractXML(ArrayList> list, String spec) { + String extractXML = ""; + extractXML += "\n"; + for (int i = 0; i < list.size(); i++) { + HashMap map = list.get(i); + String path = map.get("path"); + if (path.length() > 0) { + path = " Path=\"" + path + "\""; + } else { + path = ""; + } + extractXML += " \n"; + } + if (extractXML.length() > 0) { + extractXML += spec; + } + extractXML += "\n"; + return extractXML; + } + + public static String getQueryXML(String nodetype, ArrayList> list, String notcondition, + String order) { + // TODO Auto-generated method stub + String queryXML = ""; + queryXML += "\n"; + queryXML += " \n"; + queryXML += " \n"; + if (notcondition.length() > 0) { + queryXML += notcondition; + } + queryXML += " \n"; + for (HashMap map : list) { + String path = map.get("path"); + String attrkey = map.get("key"); + String attrvalue = map.get("value"); + String attrtype = map.get("type"); + if (attrtype.equalsIgnoreCase("string")) { + queryXML += " \n"; + } else if (attrtype.equalsIgnoreCase("ref")) { + queryXML += " \n"; + } else if (attrtype.equalsIgnoreCase("double")) { + queryXML += " \n"; + } + } + queryXML += " \n"; + queryXML += " \n"; + queryXML += order; + queryXML += "\n"; + + return queryXML; + } + + public static Locale getLocaleFromLocaleName(String localeName) { + Locale locale = null; + if (!StringUtils.isEmpty(localeName)) { + locale = Locale.forLanguageTag(localeName.replace('_', '-')); + } + return locale; + } + + public static String stripBadChar(String s) { + StringBuilder out = new StringBuilder(s.length() * 6); + byte[] bytes = s.getBytes(); + + for (int j = 0; j < bytes.length; ++j) { + byte b = bytes[j]; + int i = b; + if (i < 0) { + i = 256 + i; + } + if (i > '~') { + String cc = "&#" + i + ";"; + out.append(cc); + } else { + char current = (char) b; + if ((current == 0x9) || + (current == 0xA) || + (current == 0xD) || + ((current >= 0x20) && (current <= 0xD7FF)) || + ((current >= 0xE000) && (current <= 0xFFFD)) || + ((current >= 0x10000) && (current <= 0x10FFFF))) { + out.append(current); + } + } + } + + s = out.toString(); + return s; + + } + + public static String writeFileToDisk(byte[] img, String filePath, String fileName) { + String path = ""; + try { + File folder = new File(filePath); + if (!folder.exists()) { + folder.mkdirs(); + } + path = filePath + fileName; + File file = new File(path); + FileOutputStream fops = new FileOutputStream(file); + fops.write(img); + fops.flush(); + fops.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return path; + } + + /** + * + * @param json + * @param classname + * @param LOG + * @return + * @author GHUANG + * @version 2019年6月10日 下午1:08:45 + */ +// public static HashMap parseXML(String type, JSONObject json, String classname) throws Exception{ +// +// String keys = CSProperties.getValue("cs." + type + ".import.keys", ""); +// +// List keylist = Arrays.asList(keys.split(",")); +// HashMap objmap = new HashMap(); +// try { +// for (String key : keylist) { +// String xml = ""; +// String attrname = CSProperties.getValue("cs." + type + ".import." + key + ".attrname", ""); +// String attrtype = CSProperties.getValue("cs." + type + ".import." + key + ".attrtype", ""); +// String refobj = CSProperties.getValue("cs." + type + ".import." + key + ".refobject", ""); +// String refkey = CSProperties.getValue("cs." + type + ".import." + key + ".refkey", ""); +// String refpath = CSProperties.getValue("cs." + type + ".import." + key + ".refpath", ""); +// String attrpath = CSProperties.getValue("cs." + type + ".import." + key + ".attrpath", ""); +// if (json.isNull(key)) { +// continue; +// } +// if (json.has(key)) { +// String attrvalue = json.getString(key); +// if (attrtype.equals("enumKEY")) { +// xml += ""; +// ; +// } else if (attrtype.equals("enumNAME")) { +// for (Entry gmap : NodeUtil.zhLocaleMap.entrySet()) { +// if (gmap.getValue().equals(attrvalue) && gmap.getKey().contains(refobj)) { +// xml += ""; +// +// } +// } +// } else if (attrtype.equals("time")) { +// String timestr = CommonUtil.parseTimestamp(attrvalue, refobj, classname); +// xml += ""; +// } else if (attrtype.equals("integer")) { +// xml += ""; +// } else if (attrtype.equals("ref")) { +// String queryxml = "\r\n" + +// ""; +// List refobjlist = NodeUtil.queryBOByXML(queryxml); +// if (refobjlist.size() > 0) { +// xml += ""; +// +// } +// } else if (attrtype.equals("reflist")) { +// List valuelist = Arrays.asList(attrvalue); +// if (valuelist.size() > 0) { +// xml += ""; +// for (String value : valuelist) { +// String queryxml = "\r\n" + +// ""; +// List refobjlist = NodeUtil.queryBOByXML(queryxml); +// xml += "" + refobjlist.get(0) + ""; +// } +// xml += ""; +// } +// } else { +// xml += " "; +// +// } +// } +// if (attrpath == null || attrpath.length() == 0) { +// if (objmap.containsKey("default")) { +// String tempxml = (String) objmap.get("default"); +// objmap.put("default", xml + tempxml); +// } else { +// objmap.put("default", xml); +// } +// } else { +// System.out.println("---" + attrpath); +// if (objmap.containsKey(attrpath)) { +// String tempxml = (String) objmap.get(attrpath); +// objmap.put(attrpath, xml + tempxml); +// } else { +// objmap.put(attrpath, xml); +// } +// } +// } +// } catch (Exception e) { +// log.info(classname, e); +// } +// return objmap; +// } + + /** + * 四舍五入 + * + * @param attrvalue + * @param dot + * @return + * @author GHUANG + * @version 2019年6月18日 下午7:27:30 + */ + public static String changeDot(String attrvalue, int dot) { + BigDecimal db = new BigDecimal(attrvalue); + return String.valueOf(db.setScale(dot, BigDecimal.ROUND_HALF_UP).doubleValue()); + } + + public static void saveToFile(String fileName, InputStream in) throws IOException { + FileOutputStream fos = null; + BufferedInputStream bis = null; + int BUFFER_SIZE = 1024; + byte[] buf = new byte[BUFFER_SIZE]; + int size = 0; + bis = new BufferedInputStream(in); + File f = new File(fileName); + if (!f.exists())// + { + File parentDir = new File(f.getParent()); + if (!parentDir.exists())// + { + parentDir.mkdirs(); + } + f.createNewFile(); + } + + fos = new FileOutputStream(fileName); + while ((size = bis.read(buf)) != -1) { + fos.write(buf, 0, size); + } + fos.close(); + bis.close(); + } + + public static byte[] readInputStream(InputStream inStream) throws Exception { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = 0; + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + inStream.close(); + return outStream.toByteArray(); + } + + public static String parseTime(String timeValue, String format) { + SimpleDateFormat sdf = new SimpleDateFormat(format); + String resultValue = ""; + try { + long timeLongValue = Long.parseLong(timeValue); + if (timeLongValue > 0) { + Timestamp ts = new Timestamp(timeLongValue); + resultValue = sdf.format(ts); + } + } catch (Exception e) { + e.printStackTrace(); + } + return resultValue; + } + + public static String inputStream2String(InputStream is) + throws UnsupportedEncodingException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuffer sb = new StringBuffer(); + String line = null; + try { + while ((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return sb.toString(); + } + + /*** + * 删除文件夹 + * + * @param folderPath 文件夹完整绝对路径 + */ + public static void delFolder(String folderPath) { + try { + delAllFile(folderPath); // 删除完里面所有内容 + File myFilePath = new File(folderPath); + boolean b = myFilePath.delete(); // 删除空文件夹 + if(!b){ + log.error("删除文件失败,路径错误!"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /*** + * 删除指定文件夹下所有文件 + * + * @param path + * 文件夹完整绝对路径 + * @return + */ + public static boolean delAllFile(String path) { + boolean flag = false; + File file = new File(path); + if (!file.exists()) { + return flag; + } + if (!file.isDirectory()) { + return flag; + } + String[] tempList = file.list(); + File temp = null; + for (int i = 0; i < tempList.length; i++) { + if (path.endsWith(File.separator)) { + temp = new File(path + tempList[i]); + } else { + temp = new File(path + File.separator + tempList[i]); + } + if (temp.isFile()) { + temp.delete(); + } + if (temp.isDirectory()) { + delAllFile(path + "/" + tempList[i]);// 先删除文件夹里面的文件 + delFolder(path + "/" + tempList[i]);// 再删除空文件夹 + flag = true; + } + } + return flag; + } + + /** + * 查找类及其父类的所有属性 + * @param clazz + * @return + */ + public static List getAllFields(Class clazz) { + List fields = Lists.newArrayList(); + while (clazz != null) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + clazz = clazz.getSuperclass(); + } + return fields; + } + /** + * 四舍五入 + * @param d + * @param bitNum + * @return + */ + public static double getDouble(double d,int bitNum){ + BigDecimal b = BigDecimal.valueOf(d);//增加计算精度的处理,四舍弃,五入 + return b.setScale(bitNum, BigDecimal.ROUND_HALF_UP).doubleValue();//保留3位小数 + } + + /** + * 去掉数值末尾的0 + * @param d + * @param bitNum + * @return + */ + public static String getDoubleString(double d,int bitNum){ + BigDecimal b = BigDecimal.valueOf(d);//增加计算精度的处理,四舍弃,五入 + return b.setScale(bitNum, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();//保留3位小数 + } + + public static String timestamp2Date(String str_num,String format ) { + SimpleDateFormat sdf = new SimpleDateFormat(format); + if(StrUtil.isBlank(str_num)) str_num = "0"; + if (str_num.length() == 13) { + String date = sdf.format(new Date(Long.parseLong(str_num))); + //LogUtil.debug("timestamp2Date"+ "将13位时间戳:" + str_num + "转化为字符串:", date); + return date; + } else if(!"0".equals(str_num)) { + String date = sdf.format(new Date(Integer.parseInt(str_num) * 1000L)); + //LogUtil.debug("timestamp2Date" + "将10位时间戳:" + str_num + "转化为字符串:", date); + return date; + }else + return ""; + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/EncodeUtils.java b/commons/src/main/java/com/centricsoftware/commons/utils/EncodeUtils.java new file mode 100644 index 0000000..317e829 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/EncodeUtils.java @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2005-2012 springside.org.cn + */ +package com.centricsoftware.commons.utils; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.StringEscapeUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + +/** + * 封装各种格式的编码解码工具类. + * 1.Commons-Codec的 hex/base64 编码 + * 2.自制的base62 编码 + * 3.Commons-Lang的xml/html escape + * 4.JDK提供的URLEncoder + * + * @author calvin + * @version 2016-01-15 + */ +public class EncodeUtils { + private static final String DEFAULT_URL_ENCODING = "UTF-8"; + private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + + /** + * Hex编码. + */ + public static String encodeHex(byte[] input) { + return new String(Hex.encodeHex(input)); + } + + /** + * Hex解码. + */ + public static byte[] decodeHex(String input) throws DecoderException { + return Hex.decodeHex(input.toCharArray()); + } + + /** + * Base64编码. + */ + public static String encodeBase64(byte[] input) { + return new String(Base64.encodeBase64(input)); + } + + /** + * Base64编码. + */ + public static String encodeBase64(String input) { + try { + return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING))); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + /** + * Base64解码. + */ + public static byte[] decodeBase64(String input) { + return Base64.decodeBase64(input.getBytes()); + } + + /** + * Base64解码. + */ + public static String decodeBase64String(String input) { + try { + return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + /** + * Base62编码。 + */ + public static String encodeBase62(byte[] input) { + char[] chars = new char[input.length]; + for (int i = 0; i < input.length; i++) { + chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)]; + } + return new String(chars); + } + + /** + * Html 转码. + */ + public static String escapeHtml(String html) { + return StringEscapeUtils.escapeHtml4(html); + } + + /** + * Html 解码. + */ + public static String unescapeHtml(String htmlEscaped) { + return StringEscapeUtils.unescapeHtml4(htmlEscaped); + } + + /** + * Xml 转码. + */ + public static String escapeXml(String xml) { + return StringEscapeUtils.escapeXml10(xml); + } + + /** + * Xml 解码. + */ + public static String unescapeXml(String xmlEscaped) { + return StringEscapeUtils.unescapeXml(xmlEscaped); + } + + /** + * URL 编码, Encode默认为UTF-8. + */ + public static String urlEncode(String part) { + try { + return URLEncoder.encode(part, DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + /** + * URL 解码, Encode默认为UTF-8. + */ + public static String urlDecode(String part) { + try { + return URLDecoder.decode(part, DEFAULT_URL_ENCODING); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + public static void main(String[] args){ + System.out.println(EncodeUtils.encodeBase64("plm.2021.666")); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ExcelUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/ExcelUtil.java new file mode 100644 index 0000000..029dc7e --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ExcelUtil.java @@ -0,0 +1,1150 @@ +package com.centricsoftware.commons.utils; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.poi.excel.ExcelReader; +import cn.hutool.poi.excel.ExcelWriter; +import jxl.Cell; +import jxl.CellType; +import jxl.Sheet; +import jxl.Workbook; +import jxl.*; +import jxl.format.UnderlineStyle; +import jxl.write.Boolean; +import jxl.write.Number; +import jxl.write.*; +import lombok.extern.slf4j.Slf4j; +import net.sf.jxls.util.Util; +import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.*; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.channels.FileChannel; +import java.util.*; +import java.util.regex.Pattern; + +//import com.aspose.cells.License; +//import com.aspose.cells.SaveFormat; + +/** + * excel + * 处理工具类,同类方法 + * {@link cn.hutool.poi.excel.ExcelReader} + * {@link cn.hutool.poi.excel.ExcelFileUtil} + * {@link cn.hutool.poi.excel.ExcelUtil} + * {@link cn.hutool.poi.excel.ExcelWriter} + * {@link cn.hutool.poi.excel.ExcelPicUtil} + * @author zheng.gong + * @date 2020/4/27 + */ +@Slf4j +public class ExcelUtil { + public ExcelUtil() { + + } + + /** + * 获取破解excel转换成pdf文件的license文件 + * + * @return + */ +// public static boolean getLicense() { +// // 获取license文件路径 +// String license = "license.xml"; +// boolean result = false; +// try { +// // 获取文件 +// ClassPathResource resource = new ClassPathResource(license); +// // 获取输入流 +// InputStream is = resource.getInputStream(); +// // 通过License的set方法进行破解转换 +// License aposeLic = new License(); +// aposeLic.setLicense(is); +// result = true; +// is.close(); +// } catch (Exception e) { +// log.error("破解excel转换pdf文件的license错误",e); +// } +// return result; +// } + + /** + * @param excelPath + * 需要被转换的excel全路径带文件名 + * @param pdfPath + * 转换之后pdf的全路径带文件名 + */ +// public static void excel2pdf(String excelPath, String pdfPath) { +// if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生 +// return; +// } +// log.info("License文件验证成功!"); +// try { +// log.info("开始转换pdf文件"); +// // 原始excel路径 +// long old = System.currentTimeMillis(); +// // 创建一个工作空间 +// com.aspose.cells.Workbook wb = new com.aspose.cells.Workbook(excelPath); +// // 获取文件输出流 +// FileOutputStream fileOS = new FileOutputStream(new File(pdfPath)); +// // 进行保存,SaveFormat内部有声明可以导出的文件类型,可自由选择 +// wb.save(fileOS, SaveFormat.PDF); +// fileOS.close(); +// long now = System.currentTimeMillis(); +// log.info("共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时 +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } + + + /** + * + * @param filePath + */ + public static void readExcel(String filePath) { + try { + InputStream is = new FileInputStream(filePath); + Workbook rwb = Workbook.getWorkbook(is); + Sheet st = rwb.getSheet("original"); + Cell c00 = st.getCell(0, 0); + String strc00 = c00.getContents(); + if (c00.getType() == CellType.LABEL) { + LabelCell labelc00 = (LabelCell) c00; + strc00 = labelc00.getString(); + } + System.out.println(strc00); + rwb.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + /** + * 根据配置文件,导出需要的数据 + * @param templatePath 模板文件路径 + * @param list 根据配置文件生成的对应数据 + * @return ExcelWriter + */ + public static ExcelWriter exportExcelByConfig(String templatePath, List> list){ + ExcelWriter writer = null; + try (InputStream in = ExcelUtil.class.getClassLoader().getResourceAsStream(templatePath)) { + ExcelReader reader = cn.hutool.poi.excel.ExcelUtil.getReader(in); + writer = reader.getWriter(); + writer.setStyleSet(null); + for (Map stringObjectMap : list) { + int x = (int) stringObjectMap.get("x"); + int y = (int) stringObjectMap.get("y"); + Object value = stringObjectMap.get("value"); + writer.writeCellValue(x,y,value); + } + } catch (Exception e) { + log.error("创建excel失败!", e); + } + + return writer; + } + + + /** + * @param sheet sheet + * @param wb workbook + * @param in 图片输入流 + * @param imageType 图片格式类型 + * @param resize 是否按图片原比例缩放 + * @param dx1 图片在起始单元格x轴坐标 + * @param dy1 图片在起始单元格y轴坐标 + * @param dx2 图片在结束单元格x轴坐标 + * @param dy2 图片在结束单元格y轴坐标 + * @param col1 图片左上角所在的cellNum,从0开始 + * @param row1 图片左上角所在的RowNum,从0开始 + * @param col2 图片右下角所在的cellNum,从0开始 + * @param row2 图片右下角所在的RowNum,从0开始 + */ + public static void insertImage(org.apache.poi.ss.usermodel.Sheet sheet, org.apache.poi.ss.usermodel.Workbook wb, InputStream in, int imageType, boolean resize, + int dx1, int dy1, int dx2, int dy2, + int col1, int row1, int col2, int row2) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + byte[] b = new byte[1024]; + int len; + while ((len = in.read(b)) != -1) { + baos.write(b, 0, len); + } + Drawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = new XSSFClientAnchor(dx1, dy1, dx2, dy2, + col1, row1, col2, row2); + byte[] bytes = baos.toByteArray(); + //是否有缩放 + if (resize) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + BufferedImage imgReader = ImageIO.read(inputStream); +// BufferedImage imgReader = ImgUtil.read(inputStream); + Row row = sheet.getRow(row1); + //单元格列宽 + float cellWidth = 0f; + for (int i = col1; i < col2; i++) { + cellWidth += sheet.getColumnWidthInPixels(i); + } + //x轴缩放比例 + double scalx = 0; + //y轴缩放比例 + double scaly = 0; + //缩放后显示在单元格上的图片长度 + double imgScalWidth = 0; + //缩放后显示在单元格上的图片宽度 + double imgScalHeight = 0; + double contextRatio = 0.9; + //图片原始宽度 + int imgOriginalWidth = imgReader.getWidth(); + //图片原始高度 + double imgOriginalHeight = imgReader.getHeight(); + //单元格行高 + float cellHeight = row.getHeightInPoints() / 72 * 96; +// log.debug("图片原始宽度:{},图片原始高度:{},单元格列宽:{},单元格行高:{}",imgOriginalWidth,imgOriginalHeight,cellWidth,cellHeight); + //如果行宽和列高都小于单元格行宽和列高,则图片不做缩放,原始大小 + + //单元格长宽比 + double cellRatio = cellWidth/cellHeight; + //原始图片长宽比 + double imgRatio = imgOriginalWidth/imgOriginalHeight; + if(cellRatio > 0 && imgRatio > 0 ){ + /* + 原始图片和单元格均为长>宽 标准样式 + */ + imgScalWidth = Math.floor(cellWidth*contextRatio); + imgScalHeight = Math.floor(imgOriginalHeight/imgOriginalWidth*cellWidth); + if(imgScalHeight > cellHeight){ + imgScalWidth = Math.floor(cellHeight*imgOriginalWidth/imgOriginalHeight); + imgScalHeight = Math.floor(cellHeight*contextRatio); + + } + }else if(cellRatio > 0 && imgRatio < 0){ + /* + 原始图片长<宽 长图样式 + */ + imgScalWidth = imgOriginalWidth/imgOriginalHeight*cellHeight*contextRatio; + imgScalHeight = cellHeight*contextRatio; + } + //计算缩率 + scalx = imgScalWidth/cellWidth; + scaly = imgScalHeight/cellHeight; + + //图片左边相对excel格的位置(x偏移) + double doubleDx1 = (cellWidth - imgScalWidth) / 2; + //图片上方相对excel格的位置(y偏移) + double doubleDy1 = (cellHeight - imgScalHeight) / 2; + + int rdx1 = NumberUtil.round((doubleDx1*1000)+dx1,0).intValue(); + int rdy1 = NumberUtil.round((doubleDy1*1000)+dy1,0).intValue(); + log.debug("dx1:{},dy1:{}",rdx1,rdx1); + Drawing patriarch = sheet.createDrawingPatriarch(); + XSSFClientAnchor resizeAnchor = new XSSFClientAnchor(rdx1, rdy1, dx2, dy2,(short) col1, row1, (short) col2, row2); + Picture picture = patriarch.createPicture(resizeAnchor, wb.addPicture(bytes, HSSFWorkbook.PICTURE_TYPE_JPEG)); + + log.debug("scalx:{},scaly:{}",scalx,scaly); + picture.resize(scalx,scaly);//等比缩放 +// picture.resize(0.8);//等比缩放 + } else { + drawing.createPicture(anchor, wb.addPicture(bytes, imageType)); + } + } catch (Exception e) { + log.error("图片处理异常!", e); + } + } + + /** + * 读取Excel + * + * @param filePath + */ + public static ArrayList> readExcel(String filePath, int sheetIndex) { + try { + ArrayList> valueList = new ArrayList>(); + InputStream is = new FileInputStream(filePath); + Workbook rwb = Workbook.getWorkbook(is); + // Sheet st = rwb.getSheet("0")这里有两种方法获取sheet�?,1为名字,而为下标,从0�?�? + Sheet st = rwb.getSheet(sheetIndex); + int clumns = st.getColumns(); + System.out.println("colum=" + clumns); + String[] headers = new String[clumns]; + for (int i = 0; i < clumns; i++) { + String header = st.getCell(i, 0).getContents(); + headers[i] = header; + } + + int rows = st.getRows(); + System.out.println("rows=" + rows); + for (int i = 1; i < rows; i++) { + boolean breakFlag = false; + HashMap map = new HashMap(); + for (int j = 0; j < clumns; j++) { + String header = headers[j]; + String strc = st.getCell(j, i).getContents(); + if (header != null && !"".equals(header)) { + map.put(header, strc.trim()); + } + if (j == 0 && "".equals(strc)) { + breakFlag = true; + } + } + if (breakFlag) { + break; + } + map.put("LineNo", (i + 1) + ""); + valueList.add(map); + } + + // 关闭 + rwb.close(); + return valueList; + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public static boolean copyRealFile(String srcName, String destName) { + try { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(srcName)); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destName)); + int i = 0; + byte[] buffer = new byte[2048]; + while ((i = in.read(buffer)) != -1) { + out.write(buffer, 0, i); + } + out.close(); + in.close(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + + public static HSSFCellStyle bodyStyle(HSSFWorkbook wb, HorizontalAlignment alignStyle) { + HSSFFont bodyFont = wb.createFont(); + bodyFont.setBold(false); + bodyFont.setFontName("宋体"); + bodyFont.setFontHeightInPoints((short) 9); + HSSFCellStyle bodyStyle = wb.createCellStyle(); + bodyStyle.setFont(bodyFont); + bodyStyle.setBorderTop(BorderStyle.THIN); + bodyStyle.setBorderRight(BorderStyle.THIN); + bodyStyle.setBorderBottom(BorderStyle.THIN); + bodyStyle.setBorderLeft((BorderStyle.THIN)); + bodyStyle.setAlignment(alignStyle); + bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER); + return bodyStyle; + } + + public static HSSFCellStyle bodyWrapStyle(HSSFWorkbook wb, HorizontalAlignment alignStyle) { + HSSFFont bodyFont = wb.createFont(); + bodyFont.setBold(false); + bodyFont.setFontName("宋体"); + bodyFont.setFontHeightInPoints((short) 9); + HSSFCellStyle bodyStyle = wb.createCellStyle(); + bodyStyle.setFont(bodyFont); + bodyStyle.setBorderTop(BorderStyle.THIN); + bodyStyle.setBorderRight(BorderStyle.THIN); + bodyStyle.setBorderBottom(BorderStyle.THIN); + bodyStyle.setBorderLeft((BorderStyle.THIN)); + bodyStyle.setAlignment(alignStyle); + bodyStyle.setWrapText(true); + bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER); + bodyStyle.setWrapText(true); + return bodyStyle; + } + + /** + * + * @param resultWorkbook + * @param sheet + * @param imgStream + * @param nodeName + * @param imageStartCol + * @param imageStartRow + * @param imageEndCol + * @param imageEndRow + * @author GHUANG + * @version 2019年5月16日 下午2:36:39 + */ + public static void writePicture(HSSFWorkbook resultWorkbook, HSSFSheet sheet, InputStream imgStream, + String nodeName, int imageStartCol, int imageStartRow, int imageEndCol, int imageEndRow) throws Exception{ + int imagetype = 0; + ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + if (imgStream != null) { + if (nodeName.endsWith("png") || nodeName.endsWith("PNG")) { + imagetype = HSSFWorkbook.PICTURE_TYPE_PNG; + } else { + imagetype = HSSFWorkbook.PICTURE_TYPE_JPEG; + } + byte[] buffer = new byte[1024]; + int len = -1; + try { + while ((len = imgStream.read(buffer)) != -1) { + byteArrayOut.write(buffer, 0, len); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + HSSFPatriarch patriarch = sheet.createDrawingPatriarch(); + HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, + (short) imageStartCol, imageStartRow, + (short) imageEndCol, imageEndRow); + // int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2 + patriarch.createPicture(anchor, resultWorkbook.addPicture(byteArrayOut.toByteArray(), imagetype)); + } + } + + /** + * 输出Excel + * + * @param os + */ + public static void writeExcel(OutputStream os) { + try { + WritableWorkbook wwb = Workbook.createWorkbook(os); + WritableSheet ws = wwb.createSheet("Test Sheet 1", 0); + Label label = new Label(0, 0, "this is a label test"); + ws.addCell(label); + + WritableFont wf = new WritableFont(WritableFont.TIMES, 18, + WritableFont.BOLD, true); + WritableCellFormat wcf = new WritableCellFormat(wf); + Label labelcf = new Label(1, 0, "this is a label test", wcf); + ws.addCell(labelcf); + + WritableFont wfc = new WritableFont(WritableFont.ARIAL, 10, + WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, + jxl.format.Colour.RED); + WritableCellFormat wcfFC = new WritableCellFormat(wfc); + Label labelCF = new Label(1, 0, "This is a Label Cell", wcfFC); + ws.addCell(labelCF); + + // 2.添加Number对象 + Number labelN = new Number(0, 1, 3.1415926); + ws.addCell(labelN); + + // 添加带有formatting的Number对象 + NumberFormat nf = new NumberFormat("#.##"); + WritableCellFormat wcfN = new WritableCellFormat(nf); + Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN); + ws.addCell(labelNF); + + // 3.添加Boolean对象 + Boolean labelB = new jxl.write.Boolean(0, 2, false); + ws.addCell(labelB); + + // 4.添加DateTime对象 + jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, + new java.util.Date()); + ws.addCell(labelDT); + + // 添加带有formatting的DateFormat对象 + DateFormat df = new DateFormat("dd MM yyyy hh:mm:ss"); + WritableCellFormat wcfDF = new WritableCellFormat(df); + DateTime labelDTF = new DateTime(1, 3, new java.util.Date(), wcfDF); + ws.addCell(labelDTF); + + // 添加图片对象,jxl只支持png格式图片 + File image = new File("f:\\2.png"); + WritableImage wimage = new WritableImage(0, 1, 2, 2, image); + ws.addImage(wimage); + // 写入工作�? + wwb.write(); + wwb.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void write2Excel(String fileName, List> valueList) { + try { + WritableWorkbook wwb = Workbook.createWorkbook(new FileOutputStream(fileName)); + // 创建Excel工作�? 指定名称和位�? + WritableSheet ws = wwb.createSheet("Custom Attributes", 0); + + // **************�?工作表中添加数据***************** + + // 1.添加Label对象 + if (valueList.size() <= 0) { + return; + } + + Iterator it = valueList.get(0).keySet().iterator(); + String[] headers = new String[valueList.get(0).keySet().size()]; + int i = 0; + while (it.hasNext()) { + String header = (String) it.next(); + headers[i] = header; + Label label = new Label(i++, 0, header); + ws.addCell(label); + } + + int r = 1; + for (HashMap attValues : valueList) { + for (int j = 0; j < headers.length; j++) { + Label label = new Label(j, r, attValues.get(headers[j])); + ws.addCell(label); + } + r++; + } + + wwb.write(); + wwb.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 拷贝文件,进行修改,其中file1为被copy对象,file2为修改后创建的对象 尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现 + * + * @param file1 + * @param file2 + */ + public static void copyFile(File file1, File file2) { + try { + Workbook rwb = Workbook.getWorkbook(file1); + WritableWorkbook wwb = Workbook.createWorkbook(file2, rwb);// copy + wwb.close(); + rwb.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static float getExcelCellAutoHeight(String str, float fontCountInline) { + float defaultRowHeight = 18.00f;// 每一行的高度指定 + float defaultCount = 0.00f; + for (int i = 0; i < str.length(); i++) { + float ff = getregex(str.substring(i, i + 1)); + defaultCount = defaultCount + ff; + } + System.out.println("defaultCount=" + defaultCount); + return ((int) (defaultCount / fontCountInline) + 1) * defaultRowHeight;// 计算 + } + + public static float getregex(String charStr) { + + if (charStr == " ") { + return 0.5f; + } + // 判断是否为字母或字符 + if (Pattern.compile("^[A-Za-z0-9]+$").matcher(charStr).matches()) { + return 0.50f; + } + // 判断是否为全角 + + if (Pattern.compile("[\u4e00-\u9fa5]+$").matcher(charStr).matches()) { + return 1.00f; + } + // 全角符号 及中文 + if (Pattern.compile("[^x00-xff]").matcher(charStr).matches()) { + return 1.00f; + } + return 0.5f; + + } + + /** + * 使用文件通道的方式复制文件 + * + * @param s 源文件 + * @param t 复制到的新文件 + */ + public static void fileCopy(File s, File t) { + FileInputStream fi = null; + FileOutputStream fo = null; + FileChannel in = null; + FileChannel out = null; + try { + fi = new FileInputStream(s); + fo = new FileOutputStream(t); + in = fi.getChannel();// 得到对应的文件通道 + out = fo.getChannel();// 得到对应的文件通道 + in.transferTo(0, in.size(), out);// 连接两个通道,并且从in通道读取,然后写入out通道 + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + fi.close(); + in.close(); + fo.close(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 功能:拷贝sheet 实际调用 copySheet(targetSheet, sourceSheet, targetWork, sourceWork, true) + * + * @param targetSheet + * @param sourceSheet + * @param targetWork + * @param sourceWork + */ + public static void copySheet(HSSFSheet targetSheet, HSSFSheet sourceSheet, + HSSFWorkbook targetWork, HSSFWorkbook sourceWork) throws Exception { + if (targetSheet == null || sourceSheet == null || targetWork == null || sourceWork == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copySheet()方法时,targetSheet、sourceSheet、targetWork、sourceWork都不能为空,故抛出该异常!"); + } + // String pa = sourceWork.getPrintArea(sourceWork.getSheetIndex(sourceSheet)); + // System.out.print("printarea=" + pa + ",source" + sourceSheet.getSheetName()); + // if (pa != null) + // targetWork.setPrintArea(targetWork.getSheetIndex(targetSheet), pa); + + copySheet(targetSheet, sourceSheet, targetWork, sourceWork, true); + Util.copyPageSetup(targetSheet, sourceSheet); + Util.copyPrintSetup(targetSheet, sourceSheet); + + } + + /** + * 功能:拷贝sheet + * + * @param targetSheet + * @param sourceSheet + * @param targetWork + * @param sourceWork + * @param copyStyle + * boolean 是否拷贝样式 + */ + public static void copySheet(HSSFSheet targetSheet, HSSFSheet sourceSheet, + HSSFWorkbook targetWork, HSSFWorkbook sourceWork, boolean copyStyle) throws Exception { + + if (targetSheet == null || sourceSheet == null || targetWork == null || sourceWork == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copySheet()方法时,targetSheet、sourceSheet、targetWork、sourceWork都不能为空,故抛出该异常!"); + } + targetSheet.setMargin(HSSFSheet.TopMargin, sourceSheet.getMargin(HSSFSheet.TopMargin));// 页边距(上) + targetSheet.setMargin(HSSFSheet.BottomMargin, sourceSheet.getMargin(HSSFSheet.BottomMargin));// 页边距(下) + targetSheet.setMargin(HSSFSheet.LeftMargin, sourceSheet.getMargin(HSSFSheet.LeftMargin));// 页边距(左) + targetSheet.setMargin(HSSFSheet.RightMargin, sourceSheet.getMargin(HSSFSheet.RightMargin));// 页边距(右 + targetSheet.setHorizontallyCenter(true); + targetSheet.setRepeatingColumns(sourceSheet.getRepeatingColumns()); + targetSheet.setRepeatingRows(sourceSheet.getRepeatingRows()); + HSSFFooter sfoot = sourceSheet.getFooter(); + HSSFFooter tfoot = targetSheet.getFooter(); + tfoot.setCenter(sfoot.getCenter()); + tfoot.setLeft(sfoot.getLeft()); + tfoot.setRight(sfoot.getRight()); + // 复制源表中的行 + int maxColumnNum = 0; + + Map styleMap = (copyStyle) ? new HashMap() : null; + + HSSFPatriarch patriarch = targetSheet.createDrawingPatriarch(); // 用于复制注释 + for (int i = sourceSheet.getFirstRowNum(); i <= sourceSheet.getLastRowNum(); i++) { + HSSFRow sourceRow = sourceSheet.getRow(i); + HSSFRow targetRow = targetSheet.createRow(i); + + if (sourceRow != null) { + copyRow(targetRow, sourceRow, + targetWork, sourceWork, patriarch, styleMap); + if (sourceRow.getLastCellNum() > maxColumnNum) { + maxColumnNum = sourceRow.getLastCellNum(); + } + } + } + + // 复制源表中的合并单元格 + mergerRegion(targetSheet, sourceSheet); + + // 设置目标sheet的列宽 + for (int i = 0; i <= maxColumnNum; i++) { + targetSheet.setColumnWidth(i, sourceSheet.getColumnWidth(i)); + } + } + + /** + * 功能:拷贝sheet + * + * @param targetSheet + * @param sourceSheet + * @param targetWork + * @param sourceWork + * @param copyStyle + * boolean 是否拷贝样式 + */ + public static void copySheet(XSSFSheet targetSheet, XSSFSheet sourceSheet, + XSSFWorkbook targetWork, XSSFWorkbook sourceWork, boolean copyStyle) throws Exception { + + if (targetSheet == null || sourceSheet == null || targetWork == null || sourceWork == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copySheet()方法时,targetSheet、sourceSheet、targetWork、sourceWork都不能为空,故抛出该异常!"); + } + targetSheet.setMargin(HSSFSheet.TopMargin, sourceSheet.getMargin(HSSFSheet.TopMargin));// 页边距(上) + targetSheet.setMargin(HSSFSheet.BottomMargin, sourceSheet.getMargin(HSSFSheet.BottomMargin));// 页边距(下) + targetSheet.setMargin(HSSFSheet.LeftMargin, sourceSheet.getMargin(HSSFSheet.LeftMargin));// 页边距(左) + targetSheet.setMargin(HSSFSheet.RightMargin, sourceSheet.getMargin(HSSFSheet.RightMargin));// 页边距(右 + targetSheet.setHorizontallyCenter(true); + targetSheet.setRepeatingColumns(sourceSheet.getRepeatingColumns()); + targetSheet.setRepeatingRows(sourceSheet.getRepeatingRows()); + Footer sfoot = sourceSheet.getFooter(); + Footer tfoot = targetSheet.getFooter(); + tfoot.setCenter(sfoot.getCenter()); + tfoot.setLeft(sfoot.getLeft()); + tfoot.setRight(sfoot.getRight()); + // 复制源表中的行 + int maxColumnNum = 0; + + Map styleMap = (copyStyle) ? new HashMap() : null; + + XSSFDrawing patriarch = targetSheet.createDrawingPatriarch(); // 用于复制注释 + for (int i = sourceSheet.getFirstRowNum(); i <= sourceSheet.getLastRowNum(); i++) { + XSSFRow sourceRow = sourceSheet.getRow(i); + XSSFRow targetRow = targetSheet.createRow(i); + + if (sourceRow != null) { + copyRow(targetRow, sourceRow, + targetWork, sourceWork, patriarch, styleMap); + if (sourceRow.getLastCellNum() > maxColumnNum) { + maxColumnNum = sourceRow.getLastCellNum(); + } + } + } + + // 复制源表中的合并单元格 + mergerRegion(targetSheet, sourceSheet); + + // 设置目标sheet的列宽 + for (int i = 0; i <= maxColumnNum; i++) { + targetSheet.setColumnWidth(i, sourceSheet.getColumnWidth(i)); + } + } + + /** + * 功能:拷贝row + * + * @param targetRow + * @param sourceRow + * @param styleMap + * @param targetWork + * @param sourceWork + * @param targetPatriarch + */ + public static void copyRow(HSSFRow targetRow, HSSFRow sourceRow, + HSSFWorkbook targetWork, HSSFWorkbook sourceWork, HSSFPatriarch targetPatriarch, Map styleMap) + throws Exception { + if (targetRow == null || sourceRow == null || targetWork == null || sourceWork == null + || targetPatriarch == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copyRow()方法时,targetRow、sourceRow、targetWork、sourceWork、targetPatriarch都不能为空,故抛出该异常!"); + } + + // 设置行高 + targetRow.setHeight(sourceRow.getHeight()); + + for (int i = sourceRow.getFirstCellNum(); i <= sourceRow.getLastCellNum(); i++) { + HSSFCell sourceCell = sourceRow.getCell(i); + HSSFCell targetCell = targetRow.getCell(i); + + if (sourceCell != null) { + if (targetCell == null) { + targetCell = targetRow.createCell(i); + } + + // 拷贝单元格,包括内容和样式 + copyCell(targetCell, sourceCell, targetWork, sourceWork, styleMap); + + // 拷贝单元格注释 + // copyComment(targetCell, sourceCell, targetPatriarch); + } + } + } + + /** + * 功能:拷贝row + * + * @param targetRow + * @param sourceRow + * @param styleMap + * @param targetWork + * @param sourceWork + * @param targetPatriarch + */ + public static void copyRow(XSSFRow targetRow, XSSFRow sourceRow, + XSSFWorkbook targetWork, XSSFWorkbook sourceWork, XSSFDrawing targetPatriarch, Map styleMap) + throws Exception { + if (targetRow == null || sourceRow == null || targetWork == null || sourceWork == null + || targetPatriarch == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copyRow()方法时,targetRow、sourceRow、targetWork、sourceWork、targetPatriarch都不能为空,故抛出该异常!"); + } + + // 设置行高 + targetRow.setHeight(sourceRow.getHeight()); + + for (int i = sourceRow.getFirstCellNum(); i <= sourceRow.getLastCellNum(); i++) { + XSSFCell sourceCell = sourceRow.getCell(i); + XSSFCell targetCell = targetRow.getCell(i); + + if (sourceCell != null) { + if (targetCell == null) { + targetCell = targetRow.createCell(i); + } + + // 拷贝单元格,包括内容和样式 + copyCell(targetCell, sourceCell, targetWork, sourceWork, styleMap); + + // 拷贝单元格注释 + // copyComment(targetCell, sourceCell, targetPatriarch); + } + } + } + + /** + * 功能:拷贝cell,依据styleMap是否为空判断是否拷贝单元格样式 + * + * @param targetCell + * 不能为空 + * @param sourceCell + * 不能为空 + * @param targetWork + * 不能为空 + * @param sourceWork + * 不能为空 + * @param styleMap + * 可以为空 + */ + public static void copyCell(HSSFCell targetCell, HSSFCell sourceCell, HSSFWorkbook targetWork, + HSSFWorkbook sourceWork, Map styleMap) { + if (targetCell == null || sourceCell == null || targetWork == null || sourceWork == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copyCell()方法时,targetCell、sourceCell、targetWork、sourceWork都不能为空,故抛出该异常!"); + } + + // 处理单元格样式 + if (styleMap != null) { + if (targetWork == sourceWork) { + targetCell.setCellStyle(sourceCell.getCellStyle()); + // targetCell.getCellStyle().cloneStyleFrom(sourceCell.getCellStyle()); + } else { + String stHashCode = "" + sourceCell.getCellStyle().hashCode(); + HSSFCellStyle targetCellStyle = (HSSFCellStyle) styleMap + .get(stHashCode); + if (targetCellStyle == null) { + targetCellStyle = targetWork.createCellStyle(); + targetCellStyle.cloneStyleFrom(sourceCell.getCellStyle()); + styleMap.put(stHashCode, targetCellStyle); + } + + targetCell.setCellStyle(targetCellStyle); + } + } + + org.apache.poi.ss.usermodel.CellType cellTypeEnum = sourceCell.getCellTypeEnum(); + // 处理单元格内容 + switch (cellTypeEnum) { + case STRING: + targetCell.setCellValue(sourceCell.getRichStringCellValue()); + break; + case NUMERIC: + targetCell.setCellValue(sourceCell.getNumericCellValue()); + break; + case BLANK: + targetCell.setCellType(cellTypeEnum); + break; + case BOOLEAN: + targetCell.setCellValue(sourceCell.getBooleanCellValue()); + break; + case ERROR: + targetCell.setCellErrorValue(FormulaError.forInt(sourceCell.getErrorCellValue())); + break; + case FORMULA: + targetCell.setCellFormula(sourceCell.getCellFormula()); + break; + default: + break; + } + + + } + + /** + * 功能:拷贝cell,依据styleMap是否为空判断是否拷贝单元格样式 + * + * @param targetCell + * 不能为空 + * @param sourceCell + * 不能为空 + * @param targetWork + * 不能为空 + * @param sourceWork + * 不能为空 + * @param styleMap + * 可以为空 + */ + public static void copyCell(XSSFCell targetCell, XSSFCell sourceCell, XSSFWorkbook targetWork, + XSSFWorkbook sourceWork, Map styleMap) { + if (targetCell == null || sourceCell == null || targetWork == null || sourceWork == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copyCell()方法时,targetCell、sourceCell、targetWork、sourceWork都不能为空,故抛出该异常!"); + } + + // 处理单元格样式 + if (styleMap != null) { + if (targetWork == sourceWork) { + targetCell.setCellStyle(sourceCell.getCellStyle()); + } else { + String stHashCode = "" + sourceCell.getCellStyle().hashCode(); + XSSFCellStyle targetCellStyle = (XSSFCellStyle) styleMap + .get(stHashCode); + if (targetCellStyle == null) { + targetCellStyle = targetWork.createCellStyle(); + targetCellStyle.cloneStyleFrom(sourceCell.getCellStyle()); + styleMap.put(stHashCode, targetCellStyle); + } + + targetCell.setCellStyle(targetCellStyle); + } + } + + org.apache.poi.ss.usermodel.CellType cellTypeEnum = sourceCell.getCellTypeEnum(); + // 处理单元格内容 + switch (cellTypeEnum) { + case STRING: + targetCell.setCellValue(sourceCell.getRichStringCellValue()); + break; + case NUMERIC: + targetCell.setCellValue(sourceCell.getNumericCellValue()); + break; + case BLANK: + targetCell.setCellType(cellTypeEnum); + break; + case BOOLEAN: + targetCell.setCellValue(sourceCell.getBooleanCellValue()); + break; + case ERROR: + targetCell.setCellErrorValue(sourceCell.getErrorCellValue()); + break; + case FORMULA: + targetCell.setCellFormula(sourceCell.getCellFormula()); + break; + default: + break; + } + } + + /** + * 功能:拷贝comment + * + * @param targetCell + * @param sourceCell + * @param targetPatriarch + */ + public static void copyComment(HSSFCell targetCell, HSSFCell sourceCell, HSSFPatriarch targetPatriarch) + throws Exception { + if (targetCell == null || sourceCell == null || targetPatriarch == null) { + throw new IllegalArgumentException( + "调用PoiUtil.copyCommentr()方法时,targetCell、sourceCell、targetPatriarch都不能为空,故抛出该异常!"); + } + + // 处理单元格注释 + HSSFComment comment = sourceCell.getCellComment(); + if (comment != null) { + HSSFComment newComment = targetPatriarch.createComment(new HSSFClientAnchor()); + newComment.setAuthor(comment.getAuthor()); + newComment.setColumn(comment.getColumn()); + newComment.setFillColor(comment.getFillColor()); + newComment.setHorizontalAlignment(comment.getHorizontalAlignment()); + newComment.setLineStyle(comment.getLineStyle()); + newComment.setLineStyleColor(comment.getLineStyleColor()); + newComment.setLineWidth(comment.getLineWidth()); + newComment.setMarginBottom(comment.getMarginBottom()); + newComment.setMarginLeft(comment.getMarginLeft()); + newComment.setMarginTop(comment.getMarginTop()); + newComment.setMarginRight(comment.getMarginRight()); + newComment.setNoFill(comment.isNoFill()); + newComment.setRow(comment.getRow()); + newComment.setShapeType(comment.getShapeType()); + newComment.setString(comment.getString()); + newComment.setVerticalAlignment(comment.getVerticalAlignment()); + newComment.setVisible(comment.isVisible()); + targetCell.setCellComment(newComment); + } + } + + /** + * 功能:复制原有sheet的合并单元格到新创建的sheet + * + * @param targetSheet + * @param sourceSheet + */ + public static void mergerRegion(XSSFSheet targetSheet, XSSFSheet sourceSheet) throws Exception { + if (targetSheet == null || sourceSheet == null) { + throw new IllegalArgumentException("调用PoiUtil.mergerRegion()方法时,targetSheet或者sourceSheet不能为空,故抛出该异常!"); + } + + for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { + CellRangeAddress oldRange = sourceSheet.getMergedRegion(i); + CellRangeAddress newRange = new CellRangeAddress( + oldRange.getFirstRow(), oldRange.getLastRow(), + oldRange.getFirstColumn(), oldRange.getLastColumn()); + targetSheet.addMergedRegion(newRange); + } + } + + /** + * 功能:复制原有sheet的合并单元格到新创建的sheet + * + * @param targetSheet + * @param sourceSheet + */ + public static void mergerRegion(HSSFSheet targetSheet, HSSFSheet sourceSheet) throws Exception { + if (targetSheet == null || sourceSheet == null) { + throw new IllegalArgumentException("调用PoiUtil.mergerRegion()方法时,targetSheet或者sourceSheet不能为空,故抛出该异常!"); + } + + for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { + CellRangeAddress oldRange = sourceSheet.getMergedRegion(i); + CellRangeAddress newRange = new CellRangeAddress( + oldRange.getFirstRow(), oldRange.getLastRow(), + oldRange.getFirstColumn(), oldRange.getLastColumn()); + targetSheet.addMergedRegion(newRange); + } + } + + /** + * 功能:重新定义HSSFColor.PINK的色值 + * + * @param workbook + * @return + */ + public static HSSFColor setMBorderColor(HSSFWorkbook workbook) { + HSSFPalette palette = workbook.getCustomPalette(); + HSSFColor hssfColor = null; + byte[] rgb = { (byte) 0, (byte) 128, (byte) 192 }; + try { + hssfColor = palette.findColor(rgb[0], rgb[1], rgb[2]); + if (hssfColor == null) { + palette.setColorAtIndex(HSSFColor.HSSFColorPredefined.PINK.getIndex(), rgb[0], rgb[1], + rgb[2]); + hssfColor = palette.getColor(HSSFColor.HSSFColorPredefined.PINK.getIndex()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return hssfColor; + } + + /** + * 合并单元格,适用所有Excle版本 + * @param fRow + * @param lRow + * @param fCol + * @param lCol + * @param st + */ + public static void addMergeCell(int fRow, int lRow, int fCol, int lCol,org.apache.poi.ss.usermodel.Sheet st){ + CellRangeAddress region1 = new CellRangeAddress(fRow,lRow,fCol,lCol); + st.addMergedRegion(region1); + } + + /** + * 设置富文本格式的值,适用于Excle 2003 + * @param colNo + * @param rowNo + * @param value + * @param ws + */ + public static void setCellValue(int colNo, int rowNo, HSSFRichTextString value,HSSFSheet ws){ + HSSFRow row = ws.getRow(rowNo); + if(row==null) + row = ws.createRow(rowNo); + HSSFCell cell = row.getCell(colNo); + if(cell==null){ + cell = row.createCell(colNo); + } + HSSFCellStyle st = cell.getCellStyle(); + //NodeUtil.outInfo("st="+st + " value="value, logFile); + cell.setCellValue(value); + cell.setCellStyle(st); + } + + /** + * 设置单元格的值,适用于Excel 2007以上 + * @param colNo + * @param rowNo + * @param value + * @param ws + */ + public static void setCellValue(int colNo, int rowNo, HSSFRichTextString value,XSSFSheet ws){ + XSSFRow row = ws.getRow(rowNo); + if(row==null) + row = ws.createRow(rowNo); + XSSFCell cell = row.getCell(colNo); + if(cell==null){ + cell = row.createCell(colNo); + } + XSSFCellStyle st = cell.getCellStyle(); + //NodeUtil.outInfo("st="+st + " value="value, logFile); + cell.setCellValue(value); + cell.setCellStyle(st); + } + + /** + * 设置单元格的值,适用于Excel 2003 + * @param colNo + * @param rowNo + * @param value + * @param ws + */ + public static void setCellValue(int colNo, int rowNo, String value,HSSFSheet ws){ + if(value==null) value = ""; + HSSFRow row = ws.getRow(rowNo); + if(row==null) + row = ws.createRow(rowNo); + HSSFCell cell = row.getCell(colNo); + if(cell==null){ + cell = row.createCell(colNo); + } + HSSFCellStyle st = cell.getCellStyle(); + //NodeUtil.outInfo("st="+st + " value="value, logFile); + cell.setCellValue(value); + cell.setCellStyle(st); + } + + /** + * 设置单元格的值 + * @param colNo + * @param rowNo + * @param value + * @param ws + */ + public static void setCellValue(int colNo, int rowNo, String value,XSSFSheet ws){ + if(value==null) value = ""; + XSSFRow row = ws.getRow(rowNo); + if(row==null) + row = ws.createRow(rowNo); + XSSFCell cell = row.getCell(colNo); + if(cell==null){ + cell = row.createCell(colNo); + } + XSSFCellStyle st = cell.getCellStyle(); + //NodeUtil.outInfo("st="+st + " value="value, logFile); + cell.setCellValue(value); + cell.setCellStyle(st); + } + + // 测试 + public static void main(String[] args) { + try { + fileCopy(new File("D:\\harry\\project\\bestseller\\Integration\\AD\\StickerTemplate.xls"), + new File("D:\\harry\\project\\bestseller\\Integration\\AD\\Sticker1.xls")); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ExportExcel.java b/commons/src/main/java/com/centricsoftware/commons/utils/ExportExcel.java new file mode 100644 index 0000000..8c25d46 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ExportExcel.java @@ -0,0 +1,1057 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.centricsoftware.commons.utils; + +import cn.hutool.core.util.NumberUtil; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.ImageUtils; +import org.apache.poi.ss.util.RegionUtil; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; + +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.io.*; +import java.util.List; +import java.util.*; + +/** + * 导出Excel文件(导出“XLSX”格式,支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion) + * + * @author jeeplus + * @version 2016-04-21 + */ +@Slf4j +public class ExportExcel { + public String fileName = "fileName.xlsx";//导出文件名 + + /** + * 工作薄对象 + */ + private SXSSFWorkbook wb; + /** + * 原始工作薄对象 + */ + private XSSFWorkbook srcWb; + /** + * 工作表对象 + */ + private Sheet sheet; + /** + * 模板sheet + */ + private Sheet templateSheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 无任何参数,创建一个空的excel类 + */ + public ExportExcel() { + XSSFWorkbook workbook = new XSSFWorkbook(); + this.wb = new SXSSFWorkbook(workbook); + this.sheet = workbook.createSheet("Export"); + this.styles = createStyles(wb); + this.templateSheet = this.sheet; + } + + /** + * 读取excel模板,创建excel类 + * + * @param wb + * @param sheet + */ + public ExportExcel(XSSFWorkbook wb, Sheet sheet) { + this.wb = new SXSSFWorkbook(wb); + this.srcWb = wb; + this.sheet = sheet; + this.styles = createStyles(this.wb); + this.templateSheet = sheet; + } + + /** + * 构造函数 + * + * @param title 表格标题,传“空值”,表示无标题 + * @param headers 表头数组 + */ + public ExportExcel(String title, String[] headers) { + XSSFWorkbook workbook = new XSSFWorkbook(); + this.wb = new SXSSFWorkbook(workbook); + this.sheet = workbook.createSheet("Export"); + this.styles = createStyles(this.wb); + this.templateSheet = this.sheet; + initialize(title, Lists.newArrayList(headers)); + } + + /** + * 构造函数 + * + * @param title 表格标题,传“空值”,表示无标题 + * @param headerList 表头列表 + */ + public ExportExcel(String title, List headerList) { + XSSFWorkbook workbook = new XSSFWorkbook(); + this.wb = new SXSSFWorkbook(workbook); + this.sheet = workbook.createSheet("Export"); + this.styles = createStyles(this.wb); + this.templateSheet = this.sheet; + initialize(title, headerList); + } + + public Sheet getSheet() { + return this.sheet; + } + + public void setSheet(Sheet sheet) { + this.sheet = sheet; + } + + public void setRowNum(int rownum) { + this.rownum = rownum; + } + + public SXSSFWorkbook getWb() { + return this.wb; + } + + public XSSFWorkbook getSrcWb() { + return srcWb; + } + + public Sheet getTemplateSheet() { + return templateSheet; + } + + /** + * 初始化函数 + * + * @param title 表格标题,传“空值”,表示无标题 + * @param headerList 表头列表 + */ + public void initialize(String title, List headerList) { + this.rownum = 0; + // Create title + if (StringUtils.isNotBlank(title)) { + Row titleRow = sheet.createRow(rownum++); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), + titleRow.getRowNum(), titleRow.getRowNum(), headerList.size() - 1)); + } + // Create header + if (headerList == null) { + throw new RuntimeException("headerList not null!"); + } + Row headerRow = sheet.createRow(rownum++); + headerRow.setHeightInPoints(16); + for (int i = 0; i < headerList.size(); i++) { + Cell cell = headerRow.createCell(i); + cell.setCellStyle(styles.get("header")); + String[] ss = StringUtils.split(headerList.get(i), "**", 2); + if (ss.length == 2) { + cell.setCellValue(ss[0]); + Comment comment = this.sheet.createDrawingPatriarch().createCellComment( + new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6)); + comment.setString(new XSSFRichTextString(ss[1])); + cell.setCellComment(comment); + } else { + cell.setCellValue(headerList.get(i)); + } + sheet.autoSizeColumn(i); + } + for (int i = 0; i < headerList.size(); i++) { + int colWidth = sheet.getColumnWidth(i) * 2; + sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth); + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) { + Map styles = new HashMap(); + + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("微软雅黑"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setWrapText(true); //自动换行 + Font dataFont = wb.createFont(); + dataFont.setFontName("微软雅黑"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.LEFT); + styles.put("data1", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + styles.put("data2", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.RIGHT); + styles.put("data3", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); +// style.setWrapText(true); + style.setAlignment(HorizontalAlignment.CENTER); + style.setFillForegroundColor(IndexedColors.LIGHT_TURQUOISE.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("微软雅黑"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(IndexedColors.BLACK.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + return styles; + } + + + /** + * 添加一行 + * + * @return 行对象 + */ + public Row addRow() { + return sheet.createRow(rownum++); + } + + /** + * 在指定位置插入一行,下方行向下移 + * + * @param rowIndex + * @return + */ + public Row insertRow(int rowIndex) { + Row row; + if (sheet.getRow(rowIndex) != null) { + int lastRowNo = sheet.getLastRowNum(); + sheet.shiftRows(rowIndex, lastRowNo, 1); + } + row = sheet.createRow(rowIndex); + return row; + } + + /** + * 添加一个单元格 + * + * @param row 添加的行 + * @param column 添加列号 + * @param val 添加值 + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val) { + return this.addCell(row, column, val, 0); + } + + /** + * 添加一个单元格 + * + * @param row 添加的行 + * @param column 添加列号 + * @param val 添加值 + * @param align 对齐方式(1:靠左;2:居中;3:靠右) + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val, int align) { + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + return addCell(row, column, val, style); + } + + /** + * 添加一个单元格 + * + * @param row 添加的行 + * @param column 添加列号 + * @param val 添加值 + * @param style 单元格样式 + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val, CellStyle style) { + Cell cell = row.createCell(column); + try { + if (val == null) { + cell.setCellValue(""); + } else if (val instanceof Integer) { + cell.setCellValue((Integer) val); + //cell.setCellType(CellType.NUMERIC); + } else if (val instanceof Long) { + cell.setCellValue((Long) val); + } else if (val instanceof Double) { + cell.setCellValue((Double) val); + } else if (val instanceof Float) { + cell.setCellValue(Double.parseDouble(val.toString())); + } else if (val instanceof Date) { + cell.setCellValue((Date) val); + } else { + cell.setCellValue((String) val); + //cell.setCellType(CellType.STRING); + } + } catch (Exception ex) { + cell.setCellValue(val.toString()); + } + if (style != null) + cell.setCellStyle(style); + return cell; + } + + /** + * 输出数据流 + * + * @param os 输出数据流 + */ + public ExportExcel write(OutputStream os) throws IOException { + wb.write(os); + return this; + } + + /** + * 输出到客户端 + * + * @param fileName 输出文件名 + */ + public ExportExcel write(HttpServletResponse response, String fileName) throws IOException { + response.reset(); + response.setContentType("application/octet-stream; charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + EncodeUtils.urlEncode(fileName)); + write(response.getOutputStream()); + return this; + } + + /** + * 输出到文件 + * + * @param name 输出文件名 + */ + public ExportExcel writeFile(String name) { + File file = new File(name); + if (!file.getParentFile().exists()) + file.getParentFile().mkdirs(); + try (FileOutputStream os = new FileOutputStream(file)) { + this.write(os); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return this; + } + + /** + * 清理临时文件 + */ + public ExportExcel dispose() { + wb.dispose(); + return this; + } + + /** + * 当导出内容不是entity时,调用此方法 + * + * @param dataList + * @return + */ + public ExportExcel setCustomDataList(List> dataList) { + if (dataList != null && dataList.size() > 0) { + for (List list : dataList) { + Row row = this.addRow(); + for (int i = 0; i < list.size(); i++) { + this.addCell(row, i, list.get(i)); + } + } + } + return this; + } + + public CellStyle getStyle(String name) { + if (styles != null) { + return styles.get(name); + } + return null; + } + + /** + * 合并单元格 + * @param firstRow + * @param lastRow + * @param firstCol + * @param lastCol + * @return + */ + public int addMergedRegion(int firstRow, int lastRow, int firstCol, int lastCol) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); + RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, this.sheet); + RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, this.sheet); + RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, this.sheet); + RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, this.sheet); + return this.sheet.addMergedRegion(cellRangeAddress); + } + + + /** + * 行复制功能 + * + * @param fromRow + * @param toRow + */ + public void copyRow(Row fromRow, Row toRow, boolean copyValueFlag) { + toRow.setHeight(fromRow.getHeight()); + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress cellRangeAddress = sheet.getMergedRegion(i); + if (cellRangeAddress.getFirstRow() == fromRow.getRowNum()) { + CellRangeAddress newCellRangeAddress = new CellRangeAddress(toRow.getRowNum(), (toRow.getRowNum() + + (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())), cellRangeAddress + .getFirstColumn(), cellRangeAddress.getLastColumn()); + sheet.addMergedRegion(newCellRangeAddress); + } + } + for (Iterator cellIt = fromRow.cellIterator(); cellIt.hasNext(); ) { + Cell tmpCell = (Cell) cellIt.next(); + Cell newCell = toRow.createCell(tmpCell.getColumnIndex()); + copyCell(tmpCell, newCell, copyValueFlag); + } + + } + + public void copyRow(Sheet fromSheet, Row fromRow, Sheet toSheet, Row toRow, boolean copyValueFlag) { + toRow.setHeight(fromRow.getHeight()); + for (int i = 0; i < fromSheet.getNumMergedRegions(); i++) { + CellRangeAddress cellRangeAddress = fromSheet.getMergedRegion(i); + if (cellRangeAddress.getFirstRow() == fromRow.getRowNum()) { + CellRangeAddress newCellRangeAddress = new CellRangeAddress(toRow.getRowNum(), (toRow.getRowNum() + + (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())), cellRangeAddress + .getFirstColumn(), cellRangeAddress.getLastColumn()); + toSheet.addMergedRegion(newCellRangeAddress); + } + } + for (Iterator cellIt = fromRow.cellIterator(); cellIt.hasNext(); ) { + Cell tmpCell = (Cell) cellIt.next(); + Cell newCell = toRow.createCell(tmpCell.getColumnIndex()); + copyCell(tmpCell, newCell, copyValueFlag); + } + } + + /** + * 复制单元格 + * + * @param srcCell + * @param distCell + * @param copyValueFlag true则连同cell的内容一起复制 + */ + public void copyCell(Cell srcCell, Cell distCell, boolean copyValueFlag) { + CellStyle newstyle; + CellStyle srcStyle = srcCell.getCellStyle(); + if (this.styles.containsKey(srcStyle.toString())) { + newstyle = this.styles.get(srcStyle.toString()); + } else { + newstyle = wb.createCellStyle(); + newstyle.cloneStyleFrom(srcCell.getCellStyle()); + this.styles.put(srcStyle.toString(), newstyle); + } + //样式 + distCell.setCellStyle(newstyle); + //评论 + if (srcCell.getCellComment() != null) { + distCell.setCellComment(srcCell.getCellComment()); + } + // 不同数据类型处理 + CellType srcCellType = srcCell.getCellType(); + //distCell.setCellType(srcCellType); + if (copyValueFlag) { + if (srcCellType == CellType.NUMERIC) { + if (DateUtil.isCellDateFormatted(srcCell)) { + distCell.setCellValue(srcCell.getDateCellValue()); + } else { + distCell.setCellValue(srcCell.getNumericCellValue()); + } + } else if (srcCellType == CellType.STRING) { + distCell.setCellValue(srcCell.getRichStringCellValue()); + } else if (srcCellType == CellType.BLANK) { + // nothing21 + } else if (srcCellType == CellType.BOOLEAN) { + distCell.setCellValue(srcCell.getBooleanCellValue()); + } else if (srcCellType == CellType.ERROR) { + distCell.setCellErrorValue(srcCell.getErrorCellValue()); + } else if (srcCellType == CellType.FORMULA) { + distCell.setCellFormula(srcCell.getCellFormula()); + } else { // nothing29 + } + } + } + + public void copySheet(Sheet fromSheet, Sheet toSheet) { + // 处理合并单元格 + for (int i = 0; i < fromSheet.getNumMergedRegions(); i++) { + CellRangeAddress cellRangeAddress = fromSheet.getMergedRegion(i); + toSheet.addMergedRegion(cellRangeAddress); + } + for (Iterator rowIt = fromSheet.rowIterator(); rowIt.hasNext(); ) { + Row fromRow = (Row) rowIt.next(); + Row toRow = toSheet.createRow(fromRow.getRowNum()); + // 复制列宽 + if (fromRow.getRowNum() == 0) { + for (Iterator cellIt = fromRow.cellIterator(); cellIt.hasNext(); ) { + Cell tmpCell = (Cell) cellIt.next(); + int colIndex = tmpCell.getColumnIndex(); + toSheet.setColumnWidth(colIndex, fromSheet.getColumnWidth(colIndex)); + } + } + toRow.setHeight(fromRow.getHeight()); + for (Iterator cellIt = fromRow.cellIterator(); cellIt.hasNext(); ) { + Cell tmpCell = (Cell) cellIt.next(); + Cell newCell = toRow.createCell(tmpCell.getColumnIndex()); + copyCell(tmpCell, newCell, true); + } + } + } + + /** + * 删除指定位置的合并单元格 + * + * @author liaochangjiang + * @since 2022-01-11 11:05:50 + */ + public void removeMergedRegion(Sheet sheet, int row, int column) { + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress ca = sheet.getMergedRegion(i); + int firstColumn = ca.getFirstColumn(); + int lastColumn = ca.getLastColumn(); + int firstRow = ca.getFirstRow(); + int lastRow = ca.getLastRow(); + if (row >= firstRow && row <= lastRow) { + if (column >= firstColumn && column <= lastColumn) { + sheet.removeMergedRegion(i); + } + } + } + + } + + /** + * 取消多个合并单元格 + * + * @param sheet + * @param startRow 开始行号 + * @param endRow 结束行号 + * @param startColumn 开始列号 + * @param endColumn 结束列号 + */ + public static void removeMerged(Sheet sheet, Integer startRow, Integer endRow, Integer startColumn, Integer endColumn) { + if (startRow == null) { + startRow = sheet.getFirstRowNum(); + } + if (endRow == null) { + endRow = sheet.getLastRowNum(); + } + //获取所有的单元格 + int sheetMergeCount = sheet.getNumMergedRegions(); + //用于保存要移除的那个合并单元格序号 + List indexList = new ArrayList<>(); + for (int i = 0; i < sheetMergeCount; i++) { + //获取第i个单元格 + CellRangeAddress ca = sheet.getMergedRegion(i); + int firstColumn = ca.getFirstColumn(); + int lastColumn = ca.getLastColumn(); + int firstRow = ca.getFirstRow(); + int lastRow = ca.getLastRow(); + if (startRow <= firstRow && endRow >= lastRow && startColumn <= firstColumn && endColumn >= lastColumn) { + indexList.add(i); + } + } + sheet.removeMergedRegions(indexList); + } + + /** + * startRow开始行startCol开始列colmun跨越的列数 + * excel增加列包括合并列 + * + * @param excel + * @param sheet + * @param startRow + * @param startCol + * @param colmun + */ + public void createNewCols(ExportExcel excel, XSSFSheet sheet, int startRow, + int startCol, int colmun) { + XSSFSheet templateSheet = excel.getSrcWb().getSheetAt(0); + int lastRow = templateSheet.getLastRowNum(); + for (int i = 0; i <= lastRow; i++) { + XSSFRow tmpRow = templateSheet.getRow(i); + XSSFRow toRow = sheet.getRow(startRow + i); + for (int j = 0; j < colmun; j++) { + XSSFCell cell = tmpRow.getCell(j); + if (cell != null) { + XSSFCell distCell = toRow.createCell(startCol + j); + excel.copyCell(cell, distCell, false); + // 设置列宽 + if (i == 1) { + sheet.setColumnWidth(startCol + j, templateSheet.getColumnWidth(j)); + } + } + } + } + int regions = templateSheet.getNumMergedRegions(); + for (int k = 0; k < regions; k++) { + CellRangeAddress region = templateSheet.getMergedRegion(k); + CellRangeAddress newRegion = new CellRangeAddress(region.getFirstRow() + startRow, region.getLastRow() + startRow, + region.getFirstColumn() + startCol, region.getLastColumn() + startCol); + sheet.addMergedRegion(newRegion); + } + } + + public void printSetup(Sheet fromSheet, Sheet toSheet, int zoom, boolean lanscape) { + PrintSetup printSetup = toSheet.getPrintSetup(); + printSetup.setLandscape(lanscape);//false 为横向 + printSetup.setPaperSize(fromSheet.getPrintSetup().getPaperSize()); //纸张 + toSheet.setMargin(Sheet.BottomMargin, fromSheet.getMargin(Sheet.BottomMargin));// 页边距(下) + toSheet.setMargin(Sheet.LeftMargin, fromSheet.getMargin(Sheet.LeftMargin));// 页边距(左) + toSheet.setMargin(Sheet.RightMargin, fromSheet.getMargin(Sheet.RightMargin));// 页边距(右) + toSheet.setMargin(Sheet.TopMargin, fromSheet.getMargin(Sheet.TopMargin));// 页边距(上) + toSheet.setMargin(Sheet.HeaderMargin, fromSheet.getMargin(Sheet.HeaderMargin));//页眉 + toSheet.setMargin(Sheet.FooterMargin, fromSheet.getMargin(Sheet.FooterMargin));//页脚 + toSheet.setZoom(zoom); + toSheet.setHorizontallyCenter(true);//水平居中 + toSheet.setVerticallyCenter(true);//垂直居中 + int[] rowBreaks = fromSheet.getRowBreaks(); + for (int rowBreaksIndex = 0; rowBreaksIndex < rowBreaks.length; rowBreaksIndex++) { + toSheet.setRowBreak(rowBreaks[rowBreaksIndex]); + } + printSetup.setScale(fromSheet.getPrintSetup().getScale()); + } + + public String getCellAddress(Cell cell) { + int colIndex = cell.getColumnIndex(); + // 行的序列号开始于1 + int rowIndex = cell.getRowIndex() + 1; + String colStr = null; + if (colIndex < 26) { + colStr = (char) (colIndex + 65) + ""; + } else { + char a = (char) (colIndex / 26 + 64); + char b = (char) (colIndex % 26 + 65); + colStr = a + "" + b; + } + return colStr + rowIndex; + } + + /** + * 插入缩放图片 + * @author liaochangjiang + * @since 2022-01-21 09:08:01 + */ + public void insertImageResize(InputStream in, int imageType, int col1, int row1, int col2, int row2) { + insertImageResize(0, in, imageType, 0, 0, 0, 0, col1, row1, col2, row2, -0.001); + } + + /** + * @param in 图片输入流 + * @param imageType 图片格式类型 + * @param resize 是否按图片原比例缩放 + * @param dx1 图片在起始单元格x轴坐标 + * @param dy1 图片在起始单元格y轴坐标 + * @param dx2 图片在结束单元格x轴坐标 + * @param dy2 图片在结束单元格y轴坐标 + * dx1,dy1,dx2,dy2,貌似不起效果 + * @param col1 图片左上角所在的cellNum,从0开始 + * @param row1 图片左上角所在的RowNum,从0开始 + * @param col2 图片右下角所在的cellNum,从0开始 + * @param row2 图片右下角所在的RowNum,从0开始 + */ + public void insertImage(InputStream in, int imageType, boolean resize, + int dx1, int dy1, int dx2, int dy2, + int col1, int row1, int col2, int row2){ + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + byte[] b = new byte[1024]; + int len; + while ((len = in.read(b)) != -1) { + baos.write(b, 0, len); + } + Drawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = new XSSFClientAnchor(dx1, dy1, dx2, dy2, + col1, row1, col2, row2); + Picture pic = drawing.createPicture(anchor, wb.addPicture(baos.toByteArray(), imageType)); + if (resize) { + double scale = getImageResizeScale( col1, row1, col2, row2,pic); + pic.resize(scale); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + public void insertImageResize(InputStream in, int imageType, + int dx1, int dy1, int dx2, int dy2, + int col1, int row1, int col2, int row2,double scale1){ + insertImageResize(0,in, imageType, dx1, dy1, dx2, dy2, col1, row1, col2, row2, scale1); + } + + /** + * + * @param dynamic 调整dx1的动态参数,如果发现图片失真,可以通过这个参数调整比例,4表示缩小4%的图片宽度 + * @param in + * @param imageType + * @param dx1 + * @param dy1 + * @param dx2 + * @param dy2 + * @param col1 + * @param row1 + * @param col2 + * @param row2 + * @param scale1 缩放比例,一旦设置,将在计算后的缩放比例上加上此值 + * @throws Exception + */ + public void insertImageResize(int dynamic,InputStream in, int imageType, + int dx1, int dy1, int dx2, int dy2, + int col1, int row1, int col2, int row2,double scale1){ + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + byte[] b = new byte[1024]; + int len; + while ((len = in.read(b)) != -1) { + baos.write(b, 0, len); + } + Drawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = new XSSFClientAnchor(dx1, dy1, dx2, dy2, + col1, row1, col2, row2); + XSSFPicture pic = (XSSFPicture)drawing.createPicture(anchor, wb.addPicture(baos.toByteArray(), imageType)); + XSSFPictureData data = (XSSFPictureData) pic.getPictureData(); + double scale = getImageResizeScale( col1, row1, col2, row2,pic); + scale = scale + scale1; + setAnchor(dynamic,pic,anchor,data,drawing,scale,col1, row1, col2, row2); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + public void setAnchor(int dynamic,XSSFPicture pic,XSSFClientAnchor anchor,XSSFPictureData data,Drawing drawing,double scale,int col1, int row1, int col2, int row2) throws Exception{ + XSSFClientAnchor pref = pic.getPreferredSize(scale); + int row21 = anchor.getRow1() + (pref.getRow2() - pref.getRow1()); + int col21 = anchor.getCol1() + (pref.getCol2() - pref.getCol1()); + Dimension size = ImageUtils.getImageDimension( + data.getPackagePart().getInputStream(), data.getPictureType()); + double scaledWidth = size.getWidth() * scale; + double scaledHeight = size.getHeight() * scale; + XSSFDrawing dra = (XSSFDrawing)drawing; + float w = 0;//行总的Pixels + for(int i=col1;icolumnWidthInPixels&&marginStartBoolean){ + marginStart = marginStart - columnWidthInPixels; + }else{ + marginStartBoolean = false; + } + if(w<0){ + o_col1++;//开始位置+1 + } + if(NumberUtil.round((scaledWidth),1).doubleValue()>NumberUtil.round(w,1).doubleValue()){ +// log.info(NumberUtil.round((scaledWidth+marginW),2).toString()); +// log.info(NumberUtil.round(w,2).toString()); + o_col2 ++;//结束位置+1 + }else{ + double end = scaledWidth + marginW; + double end1 = end - (w +marginW- columnWidthInPixels); + if(end1>0&&marginW!=0){ + marginEnd = end1; + }else{ + marginEnd = end; + } + break; + } + } + int dx1 = (int)(9525*(marginStart+dynamic)); + int dx2 = (int)(9525*(marginEnd)); + anchor.setDx2(dx2); + anchor.setDx1(dx1); + anchor.setCol1(o_col1); + anchor.setCol2(o_col2); + int o_row1=row1;//计算开始行位置 + int o_row2=row1;//计算结束行位置 + double h = 0; + for(int i=row1;icolumnHeightInPixels&&marginStartBoolean){ + marginStart1 = marginStart1 - columnHeightInPixels; + }else{ + marginStartBoolean = false; + } + if(h<0){ + o_row1++;//开始位置+1 + } + if(NumberUtil.round((scaledHeight),1).doubleValue()>NumberUtil.round(h,1).doubleValue()){ + o_row2 ++;//结束位置+1 + }else{ + double end = scaledHeight + marginH; + double end1 = end - (h +marginH- columnHeightInPixels); + if(end1>0&&marginH!=0){ + marginEnd1 = end1; + }else{ + marginEnd1 = end; + } + break; + } + } + int dy1 = (int)(9525*(marginStart1)); + int dy2 = (int)(9525*(marginEnd1)); + anchor.setDy2(dy2); + anchor.setDy1(dy1); + anchor.setRow1(o_row1); + anchor.setRow2(o_row2); + CTPositiveSize2D size2d = pic.getCTPicture().getSpPr().getXfrm().getExt(); + size2d.setCx((long)(scaledWidth*9525)); + size2d.setCy((long)(scaledHeight*9525)); +// log.info("c1={};c2={}",o_col1,o_col2); +// log.info("r1={};r2={}",o_row1,o_row2); +// log.info("dy1={};dy2={}",marginStart1,marginEnd1); +// log.info("dx1={};dx2={}",marginStart,marginEnd); +// log.info("-------------------------------------------------"); + } + + private float getRowHeightInPixels(int rowIndex,XSSFDrawing drawing){ + XSSFSheet sheet = (XSSFSheet)drawing.getParent(); + XSSFRow row = sheet.getRow(rowIndex); + float height = row != null ? row.getHeightInPoints() : sheet.getDefaultRowHeightInPoints(); + return height*96F/72F; + } + + private float getColumnWidthInPixels(int columnIndex,XSSFDrawing drawing){ + XSSFSheet sheet = (XSSFSheet)drawing.getParent(); + CTCol col = sheet.getColumnHelper().getColumn(columnIndex, false); + double numChars = col == null || !col.isSetWidth() ? 9.140625f : col.getWidth(); + return (float)numChars*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH; + } + + private float getColumnWidthInPixels(int columnIndex, XSSFSheet sheet){ + CTCol col = sheet.getColumnHelper().getColumn(columnIndex, false); + double numChars = col == null || !col.isSetWidth() ? 9.140625f : col.getWidth(); + return (float)numChars*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH; + } + + /** + * 计算缩放比例 + * @param col1 + * @param row1 + * @param col2 + * @param row2 + * @return + */ + public double getImageResizeScale( int col1, int row1, int col2, int row2,Picture pic) throws Exception{ + //图片区域长宽 + float cellWidth = 0; + for (int i = col1; i < col2; i++) { +// cellWidth += sheet.getColumnWidth(i); + cellWidth += getColumnWidthInPixels(i,(XSSFSheet)sheet); + } +// cellWidth = cellWidth / 256.0F * 7.0017F;//转成像素 + float cellHeight = 0; + for (int i = row1; i < row2; i++) { + cellHeight += sheet.getRow(i).getHeightInPoints(); + } + cellHeight = cellHeight * 96.0F / 72.0F;//转成像素 + //图片原始长宽 + XSSFPictureData data = (XSSFPictureData) pic.getPictureData(); + Dimension size = ImageUtils.getImageDimension( + data.getPackagePart().getInputStream(), data.getPictureType()); + double imageWidth = size.getWidth(); + double imageHeight = size.getHeight(); + //缩放比例 + if (imageWidth > 0 && imageHeight > 0) { + double scaleWidth = cellWidth / imageWidth; + double scaleHeight = cellHeight / imageHeight; + double scale = Math.min(scaleWidth, scaleHeight); + return scale; + } + return 1.0; + } + + /** + * 生成多个sheet页 + * @param workbook + * @param sheetNum + * @param sheetTitle + * @param headerList + * @param dataList + * @param response + * @throws Exception + */ + public void createMultipleSheetExcel(SXSSFWorkbook workbook, int sheetNum, + String sheetTitle, List headerList, List> dataList, + HttpServletResponse response) throws Exception { + // 生成一个表格 + Sheet sheet = workbook.createSheet(); + workbook.setSheetName(sheetNum, sheetTitle); + Map styles = createStyles(workbook); + // Create header + if (headerList == null) { + throw new RuntimeException("headerList not null!"); + } + Row headerRow = sheet.createRow(0); + headerRow.setHeightInPoints(16); + for (int i = 0; i < headerList.size(); i++) { + Cell cell = headerRow.createCell(i); + cell.setCellStyle(styles.get("header")); + String[] ss = StringUtils.split(headerList.get(i), "**", 2); + if (ss.length == 2) { + cell.setCellValue(ss[0]); + Comment comment = this.sheet.createDrawingPatriarch().createCellComment( + new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6)); + comment.setString(new XSSFRichTextString(ss[1])); + cell.setCellComment(comment); + } else { + cell.setCellValue(headerList.get(i)); + } + sheet.autoSizeColumn(i); + } + for (int i = 0; i < headerList.size(); i++) { + int colWidth = sheet.getColumnWidth(i) * 2; + sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth); + } + + if (dataList != null && dataList.size() > 0) { + int index = 1; + for (List list : dataList) { + Row row = sheet.createRow(index); + for (int i = 0; i < list.size(); i++) { + addCell(row, i, list.get(i)); + } + index++; + } + } + } + + /** + * 读取excel里的字段转为字符串 + */ + public static String getStringCellValue(Row values, int column) { + Cell cell = values.getCell(column); + if (cell == null) { + return ""; + } + //if (cell.getCellType() != CellType.STRING) { + // cell.setCellType(CellType.STRING); + //} + return cell.getStringCellValue(); + } + + /** + * 读取excel文件 + */ + public static Workbook getExcel(InputStream inputStream, String fileName) throws IOException { + Workbook workbook; + try { + if (null == fileName) { + return null; + } + if (fileName.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + } else if (fileName.endsWith(".xlsx")) { + workbook = new XSSFWorkbook(inputStream); + } else { + throw new IOException("The document type not support"); + } + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + } + } + return workbook; + } + + /** + * 复制模板-指向对应的开始行和结束行 + * @author liaochangjiang + * @since 2024-01-30 10:11 + */ + public void copyRows(int start, int end, ExportExcel excel, XSSFSheet fromSheet, XSSFSheet toSheet) { + for (int i = start-1; i < end; i++) { + Row fromRow = fromSheet.getRow(i); + Row toRow = toSheet.createRow(toSheet.getLastRowNum() + 1); + excel.copyRow(fromSheet, fromRow, toSheet, toRow, true); + } + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/FileUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/FileUtil.java new file mode 100644 index 0000000..9ee0d09 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/FileUtil.java @@ -0,0 +1,267 @@ +package com.centricsoftware.commons.utils; + +import jcifs.util.Base64; +import org.apache.commons.fileupload.disk.DiskFileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.io.IOUtils; +import org.apache.http.entity.ContentType; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.commons.CommonsMultipartFile; + +import java.io.*; +import java.net.FileNameMap; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * 从网络获取图片到本地 + * 同类方法{@link cn.hutool.http.HttpUtil} + * @author Harry + * @version 1.0 + * @since + */ +public class FileUtil { + /** + * 测试 + * @param args + */ + public static void main(String[] args) { + String url = "http://www.baidu.com/img/baidu_sylogo1.gif"; + String dest = "D:\\test\\image"; +// String fileName = downloadImage(url,"D:\\test\\image"); +// System.out.println(fileName); +// HttpUtil.downloadFile(url,dest); + } + + public static String downloadImage(String url,String filePath){ + if(!"".equals(url) && url != null){ + byte[] btImg = getImageFromNetByUrl(url); + if(null != btImg && btImg.length > 0){ + String fileName = url.substring(url.lastIndexOf("/")+1); + writeImageToDisk(btImg, filePath,fileName); + return filePath+fileName; + } + } + return null; + } + /** + * 将图片写入到磁盘 + * @param img 图片数据流 + * @param fileName 文件保存时的名称 + */ + public static void writeImageToDisk(byte[] img, String filePath, String fileName){ + try { + File folder = new File(filePath); + folder.mkdirs(); + File file = new File(filePath+fileName); + FileOutputStream fops = new FileOutputStream(file); + fops.write(img); + fops.flush(); + fops.close(); + System.out.println("图片已经写入"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 将图片写入到磁盘 + * @param img 图片数据流 + * @param filePath 文件保存时的路径 + */ + public static void writeImageToDisk(byte[] img, String filePath){ + try { + File file = new File(filePath); + FileOutputStream fops = new FileOutputStream(file); + fops.write(img); + fops.flush(); + fops.close(); + System.out.println("图片已经写入"); + } catch (Exception e) { + e.printStackTrace(); + } + } + /** + * 根据地址获得数据的字节流 + * @param strUrl 网络连接地址 + * @return + */ + public static byte[] getImageFromNetByUrl(String strUrl){ + try { + URL url = new URL(strUrl); + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(5 * 1000); + InputStream inStream = conn.getInputStream();//通过输入流获取图片数据 + byte[] btImg = readInputStream(inStream);//得到图片的二进制数据 + return btImg; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + /** + * 从输入流中获取数据 + * @param inStream 输入流 + * @return + * @throws Exception + */ + public static byte[] readInputStream(InputStream inStream) throws Exception{ + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = 0; + while( (len=inStream.read(buffer)) != -1 ){ + outStream.write(buffer, 0, len); + } + inStream.close(); + return outStream.toByteArray(); + } + + public static void write2File(String filename,String str){ + try { + String strEncode = Base64.encode(str.getBytes()); + byte[] bytes = strEncode.getBytes(); + File file = new File(filename); + FileOutputStream fops = new FileOutputStream(file); + fops.write(bytes); + fops.flush(); + fops.close(); + } catch (Exception e) { + // TODO: handle exception + e.printStackTrace(); + } + } + + public static String byte2hex(byte[] b) // 二进制转字符串 + { + StringBuffer sb = new StringBuffer(); + String stmp = ""; + for (int n = 0; n < b.length; n++) { + stmp = Integer.toHexString(b[n] & 0XFF); + if (stmp.length() == 1){ + sb.append("0" + stmp); + }else{ + sb.append(stmp); + } + + } + return sb.toString(); + } + + public static byte[] hex2byte(String str) { // 字符串转二进制 + if (str == null) + return null; + str = str.trim(); + int len = str.length(); + if (len == 0 || len % 2 == 1) + return null; + byte[] b = new byte[len / 2]; + try { + for (int i = 0; i < str.length(); i += 2) { + b[i / 2] = (byte) Integer.decode("0X" + str.substring(i, i + 2)).intValue(); + } + return b; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static MultipartFile getMultipartFile(InputStream is, String fileName){ + return getMultipartFile(is,fileName,"file"); + } + + public static MultipartFile getMultipartFile(InputStream is, String fileName,String item){ + String dest = "c:/temp/" + fileName; + File file = cn.hutool.core.io.FileUtil.writeFromStream(is, new File(dest)); + return getMultipartFile(file,item); + } + + /** + * 输入流转MultipartFile + * + * @param fileName + * @param inputStream + * @return + */ + public static MultipartFile getMultipartFile(String fileName, InputStream inputStream) { + MultipartFile multipartFile = null; + try { + multipartFile = new MockMultipartFile(fileName, fileName, + ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream); + } catch (Exception e) { + e.printStackTrace(); + } + return multipartFile; + } + + /** + * 通过file获取MultipartFile + * 更新记录:2025-02-25 xulin.xie 修复png透明底变更黑色底的问题 + * @param file + * @return + */ + public static MultipartFile getMultipartFile(File file,String item) { +// DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file", +// MediaType.ALL_VALUE, true, file.getName()); + DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem(item, + MediaType.ALL_VALUE, true, file.getName()); + + try (InputStream input = new FileInputStream(file); OutputStream os = fileItem.getOutputStream()) { + IOUtils.copy(input, os); + }catch (Exception e){ + throw new IllegalArgumentException("Invalid file: " + e, e); + } + MultipartFile multi = new CommonsMultipartFile(fileItem); + return multi; + } + + + /** + * 通过inputStream获取MultipartFile + * + * @param inputstream 输入流 + * @return + */ + public static MultipartFile getMultipartFileByStream(InputStream inputstream, String item) { +// DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem("file", +// MediaType.ALL_VALUE, true, file.getName()); + DiskFileItem fileItem = (DiskFileItem) new DiskFileItemFactory().createItem(item, + MediaType.ALL_VALUE, true, "file"); + + + try (OutputStream os = fileItem.getOutputStream()) { + IOUtils.copy(inputstream, os); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid file: " + e, e); + } + return new CommonsMultipartFile(fileItem); + } + + public static MultipartFile getNewMultipartFile(File file,String item) { + try { + FileInputStream input = new FileInputStream(file); + FileNameMap fileNameMap = URLConnection.getFileNameMap(); + String mimeType = fileNameMap.getContentTypeFor(file.getName()); + String contentType = MediaType.ALL_VALUE; + if ("image/png".equals(mimeType)) { + contentType = MediaType.IMAGE_PNG_VALUE; + } + MockMultipartFile multipartFile = new MockMultipartFile( + "file", + file.getName(), + contentType, + input + ); + return multipartFile; + } catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/FtpUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/FtpUtil.java new file mode 100644 index 0000000..6c7117c --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/FtpUtil.java @@ -0,0 +1,198 @@ +package com.centricsoftware.commons.utils; + +import cn.hutool.extra.ftp.Ftp; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; + +import java.io.*; +import java.net.SocketException; +/** + * Ftp上传下载工具类 + * 同类方法 {@link cn.hutool.extra.ftp.Ftp} + * @author zheng.gong + * @date 2020/4/27 + */ +public class FtpUtil { + + public static void main(String[] args) throws IOException { + //匿名登录(无需帐号密码的FTP服务器) + Ftp ftp = new Ftp("172.0.0.1"); + //进入远程目录 + ftp.cd("/opt/upload"); + //上传本地文件 + ftp.upload("/opt/upload", cn.hutool.core.io.FileUtil.file("e:/test.jpg")); + //下载远程文件 + ftp.download("/opt/upload", "test.jpg", cn.hutool.core.io.FileUtil.file("e:/test2.jpg")); + + //关闭连接 + ftp.close(); + } + + /** + * 获取FTPClient对象 + * + * @param ftpHost + * FTP主机服务器 + * @param ftpPassword + * FTP 登录密码 + * @param ftpUserName + * FTP登录用户名 + * @param ftpPort + * FTP端口 默认为21 + * @return + */ + public static FTPClient getFTPClient(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort) { + FTPClient ftpClient = new FTPClient(); + try { + ftpClient = new FTPClient(); + ftpClient.connect(ftpHost, ftpPort);// 连接FTP服务器 + ftpClient.login(ftpUserName, ftpPassword);// 登陆FTP服务器 + if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { + System.out.println("未连接到FTP,用户名或密码错误。"); + ftpClient.disconnect(); + } else { + System.out.println("FTP连接成功。"); + } + } catch (SocketException e) { + e.printStackTrace(); + System.out.println("FTP的IP地址可能错误,请正确配置。"); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("FTP的端口错误,请正确配置。"); + } + return ftpClient; + } + + /* + * 从FTP服务器下载文件 + * + * @param ftpHost FTP IP地址 + * + * @param ftpUserName FTP 用户名 + * + * @param ftpPassword FTP用户名密码 + * + * @param ftpPort FTP端口 + * + * @param ftpPath FTP服务器中文件所在路径 格式: ftptest/aa + * + * @param localPath 下载到本地的位置 格式:H:/download + * + * @param fileName 文件名称 + */ + public static String downloadFtpFile(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort, + String ftpPath, String localPath, String fileName) { + + FTPClient ftpClient = null; + + try { + ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort); + ftpClient.setControlEncoding("UTF-8"); // 中文支持 + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); + ftpClient.enterLocalPassiveMode(); + ftpClient.changeWorkingDirectory(ftpPath); + + File localFile = new File(localPath + File.separatorChar + fileName); + OutputStream os = new FileOutputStream(localFile); + ftpClient.retrieveFile(fileName, os); + os.close(); + ftpClient.logout(); + + } catch (FileNotFoundException e) { + System.out.println("没有找到" + ftpPath + "文件"); + e.printStackTrace(); + } catch (SocketException e) { + System.out.println("连接FTP失败."); + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("文件读取错误。"); + e.printStackTrace(); + } + + return localPath + File.separatorChar + fileName; + } + + /** + * Description: 向FTP服务器上传文件 + * + * @param ftpHost + * FTP服务器hostname + * @param ftpUserName + * 账号 + * @param ftpPassword + * 密码 + * @param ftpPort + * 端口 + * @param ftpPath + * FTP服务器中文件所在路径 格式: ftptest/aa + * @param fileName + * ftp文件名称 + * @param input + * 文件流 + * @return 成功返回true,否则返回false + */ + public static boolean uploadFile(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort, + String ftpPath, String fileName, InputStream input) { + boolean success = false; + FTPClient ftpClient = null; + try { + int reply; + ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort); + reply = ftpClient.getReplyCode(); + if (!FTPReply.isPositiveCompletion(reply)) { + ftpClient.disconnect(); + return success; + } + ftpClient.setControlEncoding("UTF-8"); // 中文支持 + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); + ftpClient.enterLocalPassiveMode(); + ftpClient.changeWorkingDirectory(ftpPath); + + ftpClient.storeFile(fileName, input); + + input.close(); + ftpClient.logout(); + success = true; + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (ftpClient.isConnected()) { + try { + ftpClient.disconnect(); + } catch (IOException ioe) { + } + } + } + return success; + } + + /** + * 根据文件前缀找出ftp服务器上的完整文件名 + **/ + public static String getFileName(String ftpHost, String ftpUserName, String ftpPassword, int ftpPort, String ftpPath, String prefix) { + FTPClient ftpClient = null; + String filename = ""; + try { + ftpClient = getFTPClient(ftpHost, ftpUserName, ftpPassword, ftpPort); + ftpClient.setControlEncoding("UTF-8"); // 中文支持 + System.out.println("Path is:" + ftpPath); + FTPFile[] files = ftpClient.listFiles(ftpPath); + + for (FTPFile file : files) { + if (file.isFile()) { + if (file.getName().startsWith(prefix)) { + filename = file.getName(); + break; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return filename; + } + + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ImageUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/ImageUtil.java new file mode 100644 index 0000000..0b5008f --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ImageUtil.java @@ -0,0 +1,121 @@ +package com.centricsoftware.commons.utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +/** + * 图片工具类 + * 同类方法{@link cn.hutool.core.img.ImgUtil} + * @author zheng.gong + * @date 2020/4/27 + */ +public class ImageUtil { + + public static void resize(String filePath, int height, int width) { + try { + // Thumbnails.of(filePath).size(1024, 1024).toFile(filePath); + resizePng(new File(filePath), width, height, true); + File f = new File(filePath); + BufferedImage bi = ImageIO.read(f); + Image itemp = bi.getScaledInstance(bi.getWidth(), bi.getHeight(), BufferedImage.SCALE_SMOOTH); + BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // 获取Graphics2D + Graphics2D g2d = newImage.createGraphics(); + // ---------- 增加下面的代码使得背景透明 ----------------- + newImage = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); + g2d.dispose(); + g2d = newImage.createGraphics(); + // ---------- 背景透明代码结束 ----------------- + if (width == itemp.getWidth(null)) { + g2d.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null), + itemp.getHeight(null), null, null); + } else { + g2d.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null), itemp.getHeight(null), + null, null); + } + g2d.dispose(); + ImageIO.write(newImage, "png", f); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void resizePng(File fromFile, int outputWidth, int outputHeight, boolean proportion) { + try { + BufferedImage bi2 = ImageIO.read(fromFile); + int newWidth; + int newHeight; + // 判断是否是等比缩放 + if (proportion) { + // 为等比缩放计算输出的图片宽度及高度 + double rate1 = ((double) bi2.getWidth(null)) / (double) outputWidth; + double rate2 = ((double) bi2.getHeight(null)) / (double) outputHeight; + // 根据缩放比率大的进行缩放控制 + double rate = rate1 > rate2 ? rate1 : rate2; + newWidth = (int) ((bi2.getWidth(null)) / rate); + newHeight = (int) ((bi2.getHeight(null)) / rate); + } else { + newWidth = outputWidth; // 输出的图片宽度 + newHeight = outputHeight; // 输出的图片高度 + } + BufferedImage to = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = to.createGraphics(); + to = g2d.getDeviceConfiguration().createCompatibleImage(newWidth, newHeight, Transparency.TRANSLUCENT); + g2d.dispose(); + g2d = to.createGraphics(); + @SuppressWarnings("static-access") + Image from = bi2.getScaledInstance(newWidth, newHeight, bi2.SCALE_AREA_AVERAGING); + g2d.drawImage(from, 0, 0, null); + g2d.dispose(); + ImageIO.write(to, "png", fromFile); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public static void fillInSquare(String filePath) { + + try { + File f = new File(filePath); + BufferedImage bi = ImageIO.read(f); + + int width = bi.getWidth(); + int height = bi.getHeight(); + // Image itemp = bi.getScaledInstance(width, height, + // BufferedImage.SCALE_SMOOTH); + Image itemp = bi; + BufferedImage image = null; + + if (width >= height) { + image = new BufferedImage(width, width, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + g.setColor(Color.white); + g.fillRect(0, 0, width, width); + g.drawImage(itemp, 0, (width - height) / 2, width, height, Color.white, null); + g.dispose(); + } else { + image = new BufferedImage(height, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + g.setColor(Color.white); + g.fillRect(0, 0, height, height); + g.drawImage(itemp, (height - width) / 2, 0, width, height, Color.white, null); + g.dispose(); + } + + itemp = image; + + ImageIO.write((BufferedImage) itemp, "png", f); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public static void main(String[] args) { + fillInSquare("C:\\C8\\Temp\\KA98308-11-03.jpg"); + } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ImportExcel.java b/commons/src/main/java/com/centricsoftware/commons/utils/ImportExcel.java new file mode 100644 index 0000000..c9c5ce7 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ImportExcel.java @@ -0,0 +1,257 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.centricsoftware.commons.utils; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 导入Excel文件(支持“XLS”和“XLSX”格式) + * @author jeeplus + * @version 2016-03-10 + */ +public class ImportExcel { + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 标题行号 + */ + @Getter + private int headerNum; + + /** + * 构造函数 + * @param fileName 导入文件,读取第一个工作表 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(String fileName, int headerNum) + throws InvalidFormatException, IOException { + this(new File(fileName), headerNum); + } + + /** + * 构造函数 + * @param file 导入文件对象,读取第一个工作表 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(File file, int headerNum) + throws InvalidFormatException, IOException { + this(file, headerNum, 0); + } + + /** + * 构造函数 + * @param fileName 导入文件 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(String fileName, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + this(new File(fileName), headerNum, sheetIndex); + } + + /** + * 构造函数 + * @param file 导入文件对象 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(File file, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + this(file.getName(), new FileInputStream(file), headerNum, sheetIndex); + } + + /** + * 构造函数 + * @param multipartFile 导入文件对象 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(MultipartFile multipartFile, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex); + } + + /** + * 构造函数 + * @param fileName 导入文件对象 + * @param headerNum 标题行号,数据行号=标题行号+1 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ImportExcel(String fileName, InputStream is, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + if (StringUtils.isBlank(fileName)){ + throw new RuntimeException("导入文档为空!"); + }else if(fileName.toLowerCase().endsWith("xls")){ + this.wb = new HSSFWorkbook(is); + }else if(fileName.toLowerCase().endsWith("xlsx")){ + this.wb = new XSSFWorkbook(is); + }else{ + throw new RuntimeException("文档格式不正确!"); + } + if (this.wb.getNumberOfSheets()> getCustomDataList(){ + List> dataList = new ArrayList<>(); + for (int i = this.getDataRowNum(); i <= this.getLastDataRowNum(); i++) { + Row row = this.getRow(i); + List dataRow = new ArrayList<>(); + for(int j = 0; j < this.getLastCellNum(); j++){ + dataRow.add(this.getCellValue(row, j).toString()); + } + dataList.add(dataRow); + } + return dataList; + } + + /** + * 删除行 + * @param startRow 开始行 + * @param endRow 结束行 + */ + public void deleteRow(int startRow,int endRow,int n){ + this.sheet.shiftRows(startRow,endRow,n); + } + +// /** +// * 导入测试 +// */ +// public static void main(String[] args) throws Throwable { +// +// ImportExcel ei = new ImportExcel("target/export.xlsx", 1); +// +// for (int i = ei.getDataRowNum(); i < ei.getLastDataRowNum(); i++) { +// Row row = ei.getRow(i); +// for (int j = 0; j < ei.getLastCellNum(); j++) { +// Object val = ei.getCellValue(row, j); +// System.out.print(val+", "); +// } +// System.out.print("\n"); +// } +// +// } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/IpUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/IpUtil.java new file mode 100644 index 0000000..0e82d4f --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/IpUtil.java @@ -0,0 +1,36 @@ +package com.centricsoftware.commons.utils; + + +import javax.servlet.http.HttpServletRequest; + +/** + * 获取前台的IP地址 + */ +public class IpUtil { + static final String UNKNOWN = "unknown"; + + public static String getIpAddr(HttpServletRequest request) { + if (request == null) { + return UNKNOWN; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + //System.out.println(ip); + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } +} \ No newline at end of file diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/JsonHelper.java b/commons/src/main/java/com/centricsoftware/commons/utils/JsonHelper.java new file mode 100644 index 0000000..e46dad2 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/JsonHelper.java @@ -0,0 +1,349 @@ +package com.centricsoftware.commons.utils; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; + +import java.lang.reflect.Method; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; + +/** + * + * JSON工具类 + * 同类方法{@link cn.hutool.json.JSONUtil} + * @author GHUANG + * @version 2016年3月28日 下午8:15:11 + * + * + */ +public class JsonHelper { + /** + * + * @param javaBean + * @return + * @author GHUANG + * @version 2016年3月28日 下午8:15:27 + */ + public static Map toMap(Object javaBean) { + + Map result = new HashMap(); + Method[] methods = javaBean.getClass().getDeclaredMethods(); + + for (Method method : methods) { + + try { + + if (method.getName().startsWith("get")) { + + String field = method.getName(); + field = field.substring(field.indexOf("get") + 3); + field = field.toLowerCase().charAt(0) + field.substring(1); + + Object value = method.invoke(javaBean, (Object[]) null); + result.put(field, null == value ? "" : value.toString()); + + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + return result; + + } + + public static JSONArray getJAfromList(List list) throws Exception { + JSONArray ja = new JSONArray(); + for (HashMap map : list) { + JSONObject json = new JSONObject(); + for (Entry gmap : map.entrySet()) { + String key = gmap.getKey(); + String value = gmap.getValue(); + json.put(key, value); + } + ja.put(json); + } + return ja; + } + + public static JSONObject convertJS(JSONObject js) throws Exception { + JSONObject json = new JSONObject(); + Iterator it = js.keys(); + while (it.hasNext()) { + String key = (String) it.next(); + String value = js.getString(key); + json.put(value, key); + } + return json; + } + + public static JSONObject getJsonfromMap(HashMap map) throws Exception { + JSONObject json = new JSONObject(); + for (Entry gmap : map.entrySet()) { + String key = gmap.getKey(); + String value = gmap.getValue(); + json.put(key, value); + + } + return json; + } + + /** + * + * @param json + * @return + * @author GHUANG + * @version 2019年5月15日 上午11:13:46 + */ + public static String getJsonToXml(JSONObject json) { + Iterator it = json.keys(); + StringBuffer sb = new StringBuffer(); + while (it.hasNext()) { + String key = it.next(); + String value = json.optString(key); + try { + if (value.startsWith("[")) { + JSONArray ja = new JSONArray(value); + + sb.append("<").append(key).append(">"); + if (ja.length() > 0) { + for (int j = 0; j < ja.length(); j++) { + JSONObject sjs = ja.getJSONObject(j); + if (key.equals("JSON")) { // 去掉非 + sb.append("<").append(key).append(">"); + String sbstr = getJsonToXml(sjs); + sb.append(sbstr); + sb.append(""); + } else { + String sbstr = getJsonToXml(sjs); + sb.append(sbstr); + } + } + } + sb.append(""); + + } else if (value.startsWith("{")) { + JSONObject jsonSon = new JSONObject(value); + sb.append("<").append(key).append(">"); + sb.append(getJsonToXml(jsonSon)); + sb.append(""); + } else { + sb.append("<").append(key).append(">").append(value).append(""); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return sb.toString(); + } + + /** + * + * @param jsonString + * @return + * @throws JSONException + * @author GHUANG + * @version 2016年3月28日 下午8:15:44 + */ + public static HashMap toMap(String jsonString) throws JSONException { + + JSONObject jsonObject = new JSONObject(jsonString); + + HashMap result = new HashMap(); + Iterator iterator = jsonObject.keys(); + String key = null; + String value = null; + + while (iterator.hasNext()) { + + key = (String) iterator.next(); + value = jsonObject.getString(key); + result.put(key, value); + + } + return result; + + } + + /** + * + * @param jsonString + * @return + * @throws JSONException + * @author GHUANG + * @version 2016年3月28日 下午8:15:44 + */ + public static ArrayList toMapfromArray(String jsonString) throws JSONException { + + JSONArray jsonObject = new JSONArray(jsonString); + ArrayList infoList = new ArrayList(); + for (int i = 0; i < jsonObject.length(); i++) { + String jobj = jsonObject.getJSONObject(i).toString(); + HashMap map = JsonHelper.toMap(jobj); + // System.out.println("map="+map.toString()); + infoList.add(map); + } + + return infoList; + + } + + /** + * + * @param bean + * @return + * @author GHUANG + * @version 2016年3月28日 下午8:15:51 + */ + public static JSONObject toJSON(Object bean) { + + return new JSONObject(toMap(bean)); + + } + + /** + * + * @param javabean + * @param data + * @return + * @author GHUANG + * @version 2016年3月28日 下午8:15:57 + */ + public static Object toJavaBean(Object javabean, Map data) { + + Method[] methods = javabean.getClass().getDeclaredMethods(); + for (Method method : methods) { + + try { + if (method.getName().startsWith("set")) { + + String field = method.getName(); + field = field.substring(field.indexOf("set") + 3); + field = field.toLowerCase().charAt(0) + field.substring(1); + method.invoke(javabean, data.get(field)); + + } + } catch (Exception e) { + } + + } + + return javabean; + + } + + /** + * 通过JSONObject获取对应的key值(String) + * + * @param jsonObj + * @param key + * @return + */ + private static String getJSONString(JSONObject jsonObj, String key) { + String retString = ""; + try { + Object obj = jsonObj.get(key); + if (obj != null) { + retString = String.valueOf(obj); + } + } catch (Exception e) { + // TODO: handle exception + retString = ""; + } + return retString; + } + + /** + * + * @param javabean + * @param jsonString + * @throws ParseException + * @throws JSONException + * @author GHUANG + * @version 2016年3月28日 下午8:16:04 + */ + public static void toJavaBean(Object javabean, String jsonString) + throws ParseException, JSONException { + + JSONObject jsonObject = new JSONObject(jsonString); + + Map map = toMap(jsonObject.toString()); + + toJavaBean(javabean, map); + + } + + public static HashMap getJsonToMap(JSONObject json) throws Exception { + HashMap map = new HashMap(); + for (Iterator it = json.keys(); it.hasNext();) { + String key = (String) it.next(); + String value = json.getString(key); + map.put(key, value); + + } + return map; + } + + public static HashMap getMapfromJson(JSONObject json) throws Exception { + HashMap map = new HashMap(); + for (Iterator it = json.keys(); it.hasNext();) { + String key = (String) it.next(); + String value = json.getString(key); + map.put(key, value); + + } + return map; + } + + public static ArrayList getListfromJa(JSONArray ja) throws Exception { + ArrayList list = new ArrayList(); + for (int i = 0; i < ja.length(); i++) { + JSONObject json = ja.getJSONObject(i); + HashMap map = getMapfromJson(json); + list.add(map); + } + return list; + } + + public static JSONObject parseJS(JSONObject a, JSONObject b) throws Exception { + Iterator it = a.keys(); + while (it.hasNext()) { + String key = (String) it.next(); + Object value = a.get(key); + if (value instanceof String) { + b.put(key, value); + } else if (value instanceof JSONArray) { + JSONArray ja = (JSONArray) value; + b.put(key, ja); + } + + } + return b; + } + + public static Date[] getJsonToDateArray(String jsonString) throws ParseException, JSONException { + + JSONArray jsonArray = new JSONArray(jsonString); + Date[] dateArray = new Date[jsonArray.length()]; + String dateString; + Date date; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + for (int i = 0; i < jsonArray.length(); i++) { + dateString = jsonArray.getString(i); + try { + date = sdf.parse(dateString); + dateArray[i] = date; + } catch (Exception e) { + e.printStackTrace(); + } + } + return dateArray; + } + +} \ No newline at end of file diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/JsonUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/JsonUtil.java new file mode 100644 index 0000000..7fa3f1e --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/JsonUtil.java @@ -0,0 +1,225 @@ +package com.centricsoftware.commons.utils; + +import com.fasterxml.jackson.annotation.JsonFilter; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +@Slf4j +public class JsonUtil { + private static ObjectMapper mapper = new ObjectMapper(); + + private static ObjectMapper NULL_STRING_MAPPER; + + /** + * 动态排除的过滤器key + */ + private static final String DYNA_EXCLUDES = "dynaExcludes"; + + @JsonFilter(DYNA_EXCLUDES) + interface DynamicExclude { + + } + + static { + // 如果存在未知属性,则忽略不报错 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + //允许空对象转成json + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // 允许key没有双引号 + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + // 允许key有单引号 + mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + // 防止double变成科学计数法 + mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); + + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addDeserializer(LocalDateTime.class, + new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(CommonUtil.YMD_HMS_DASH))); + javaTimeModule.addSerializer(LocalDateTime.class, + new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(CommonUtil.YMD_HMS_DASH))); + javaTimeModule.addDeserializer(LocalDate.class, + new LocalDateDeserializer(DateTimeFormatter.ofPattern(CommonUtil.YMD_DASH))); + javaTimeModule.addSerializer(LocalDate.class, + new LocalDateSerializer(DateTimeFormatter.ofPattern(CommonUtil.YMD_DASH))); + javaTimeModule.addDeserializer(LocalTime.class, + new LocalTimeDeserializer(DateTimeFormatter.ofPattern(CommonUtil.HMS_DASH))); + javaTimeModule.addSerializer(LocalTime.class, + new LocalTimeSerializer(DateTimeFormatter.ofPattern(CommonUtil.HMS_DASH))); + mapper.registerModule(javaTimeModule); + + NULL_STRING_MAPPER = mapper.copy(); + NULL_STRING_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); + NULL_STRING_MAPPER.getSerializerProvider().setNullValueSerializer(new JsonSerializer() { + @Override + public void serialize(Object param, JsonGenerator jsonGenerator, SerializerProvider paramSerializerProvider) + throws IOException { + jsonGenerator.writeString(""); + } + }); + } + + /** + * 转换为json字符串 + */ + public static String toJSONString(Object obj) { + if (Objects.isNull(obj)) { + return null; + } + try { + return mapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("转换对象到json失败, 参数:" + obj); + } + } + + /** + * 转换为json字符串. null会转成"" + */ + public static String toJSONStringNull2Empty(Object obj) { + if (Objects.isNull(obj)) { + return null; + } + try { + return NULL_STRING_MAPPER.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("转换对象到json失败, 参数:" + obj); + } + } + + /** + * 转换为json字符串, 排除忽略的字段 + */ + public static String toJSONString(Object obj, String[] ignoreFields) { + if (ArrayUtils.isEmpty(ignoreFields)) { + return toJSONString(obj); + } + try { + ObjectMapper objectMapper = mapper.copy().setFilterProvider( + new SimpleFilterProvider().addFilter(DYNA_EXCLUDES, + SimpleBeanPropertyFilter.serializeAllExcept(ignoreFields))); + objectMapper.addMixIn(obj.getClass(), DynamicExclude.class); + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("转换对象到json失败, 参数:" + obj); + } + } + + /** + * 转换为json字符串, 排除忽略的字段 + */ + public static String toJSONStringNull2Empty(Object obj, String[] ignoreFields) { + if (ArrayUtils.isEmpty(ignoreFields)) { + return toJSONStringNull2Empty(obj); + } + try { + ObjectMapper objectMapper = NULL_STRING_MAPPER.copy().setFilterProvider( + new SimpleFilterProvider().addFilter(DYNA_EXCLUDES, + SimpleBeanPropertyFilter.serializeAllExcept(ignoreFields))); + objectMapper.addMixIn(obj.getClass(), DynamicExclude.class); + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("转换对象到json失败, 参数:" + obj); + } + } + + /** + * 转换为json字节数组 + */ + public static byte[] toJSONBytes(Object obj) { + if (Objects.isNull(obj)) { + return new byte[0]; + } + try { + return mapper.writeValueAsBytes(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("转换对象到json失败, 参数:" + obj, e); + } + } + + /** + * 转换为java对象 + */ + public static T parseObject(String json, Class clazz) { + if (org.apache.commons.lang3.StringUtils.isBlank(json)) { + return null; + } + try { + return mapper.readValue(json, clazz); + } catch (IOException e) { + throw new RuntimeException("转换字符串到对象失败,json: " + json); + } + } + + /** + * 转换为java对象 + */ + public static T parseObject(String json, TypeReference ref) { + if (StringUtils.isBlank(json)) { + return null; + } + try { + return mapper.readValue(json, ref); + } catch (IOException e) { + throw new RuntimeException("转换字符串到对象失败,json: " + json); + } + } + + /** + * 转换为对象集合 + */ + public static List parseArray(String json, Class clazz) { + try { + JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, clazz); + return mapper.readValue(json, javaType); + } catch (IOException e) { + throw new RuntimeException("转换字符串到对象数组失败,json: " + json); + } + } + + /** + * 转换为集合 + * + */ + @SuppressWarnings("unchecked") + public static List> parseArray(String json) { + try { + return mapper.readValue(json, List.class); + } catch (IOException e) { + throw new RuntimeException("转换字符串到对象数组失败,json: " + json); + } + } + + /** + * 获取Mapper + * + */ + public static ObjectMapper getMapper() { + return mapper; + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/MailUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/MailUtil.java new file mode 100644 index 0000000..70c7e3e --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/MailUtil.java @@ -0,0 +1,181 @@ +package com.centricsoftware.commons.utils; + +import com.centricsoftware.config.entity.CsProperties; +import lombok.extern.slf4j.Slf4j; + +import javax.mail.Message; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.util.Properties; + +/** + * 邮件工具类,同类型工具类{@link cn.hutool.extra.mail.Mail},按个人喜好使用即可 + * + * @ClassName: MailUtil + * @Description: 发送Email + * @author: Harry + * @date: 2016-5-30 下午9:42:56 + * + */ +@Slf4j +public class MailUtil { + + + + /** + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + // sendMail("mail.bestseller.com.cn","noreply@bestseller.com.cn","PLM + // Admin","bs_stAff_123$","harry.liang@centricsoftware.com","PO:1631P0100087C质检已完成,请给出QAC决策","PO:1631P0100087C质检已完成,请给出QAC决策
PO:1631P0100087C质检已完成,请给出QAC决策"); +// testSendMail(); + } + + public static void testSendMail( String mailToUsers) throws Exception{ + CsProperties properties = SpringUtil.getBean(CsProperties.class); + System.out.println("mailToUsers=" + mailToUsers); + String poNumber = "PO12390393245"; + String qaDecision = "不合格/Fail"; + String wareHouse = "050A"; + String vDecision = "不合格/Fail"; + String mDecision = "不合格/Fail"; + String comment = "不合格/Fail"; + mailToUsers = "harry.liang@centricsoftware.com;jief@bestseller.com.cn"; + try { + if (!"".equals(mailToUsers) && !";".equals(mailToUsers)) { + String title = "PO:" + poNumber + " 已经完成质检(" + qaDecision + "),请尽快提供QAC决策. QA Inspection has been done(" + + qaDecision + "), please make QAC Decision ASAP. "; + String content = "Dear Buyer,
"; + content += "PO Number: " + poNumber + "
"; + content += "Ware House ID:" + wareHouse + "
"; + content += "QAV Result: " + vDecision + "
"; + content += "QAM Result: " + mDecision + "
"; + content += "QA Decision: " + qaDecision + "
"; + content += "QA Comments: " + comment + "
"; + MailUtil.sendMail(properties.getValue("mail.host"), properties.getValue("mail.sender"), + properties.getValue("mail.senderName"), properties.getValue("mail.senderPwd"), mailToUsers, + title, content); + System.out.println("Sent Mail to =" + mailToUsers); + } + } catch (Exception e) { + // TODO: handle exception + System.out.println("Fail to send Mail to =" + mailToUsers); + } + } + + public static void testMail() throws Exception { + Properties prop = new Properties(); + prop.setProperty("mail.host", "mail.bestseller.com.cn"); + prop.setProperty("mail.transport.protocol", "smtp"); + prop.setProperty("mail.smtp.auth", "true"); + // 使用JavaMail发送邮件的5个步骤 + // 1、创建session + Session session = Session.getInstance(prop); + // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 + session.setDebug(true); + // 2、通过session得到transport对象 + Transport ts = session.getTransport(); + // 3、使用邮箱的用户名和密码连上邮件服务器,发送邮件时,发件人需要提交邮箱的用户名和密码给smtp服务器,用户名和密码都通过验证之后才能够正常发送邮件给收件人。 + ts.connect("mail.bestseller.com.cn", "noreply@bestseller.com.cn", "bs_stAff_123$"); + // 4、创建邮件 + Message message = createSimpleMail(session); + // 5、发送邮件 + ts.sendMessage(message, message.getAllRecipients()); + ts.close(); + } + + /** + * 发送邮件 + * + * @param mailHost + * @param sender + * @param senderName + * @param senderPwd + * @param toUsers + * 多个用户使用分号分割 + * @param title + * @param content + */ + public static String sendMail(String mailHost, String sender, String senderName, String senderPwd, String toUsers, + String title, String content) { + Properties prop = new Properties(); + prop.setProperty("mail.host", mailHost); + prop.setProperty("mail.transport.protocol", "smtp"); + prop.setProperty("mail.smtp.auth", "true"); + Transport ts; + String result = "success"; + try { + // 使用JavaMail发送邮件的5个步骤 + // 1、创建session + Session session = Session.getInstance(prop); + // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态 + session.setDebug(true); + // 2、通过session得到transport对象 + ts = session.getTransport(); + // 3、使用邮箱的用户名和密码连上邮件服务器,发送邮件时,发件人需要提交邮箱的用户名和密码给smtp服务器,用户名和密码都通过验证之后才能够正常发送邮件给收件人。 + ts.connect(mailHost, sender, senderPwd); + // 4、创建邮件 + Message message = new MimeMessage(session); + // 指明邮件的发件人 + InternetAddress address = new InternetAddress(sender); + address.setPersonal(senderName); + message.setFrom(address); + // 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发 + if (toUsers.indexOf(";") >= 0) { + String[] toUsersList = toUsers.split(";"); + for (String toUser : toUsersList) { + toUser = toUser.trim(); + if (!"".equals(toUser)) { + message.addRecipient(Message.RecipientType.TO, new InternetAddress(toUser)); + } + } + } else { + message.addRecipient(Message.RecipientType.TO, new InternetAddress(toUsers)); + } + // 邮件的标题 + message.setSubject(title); + // 邮件的文本内容 + message.setContent(content, "text/html;charset=UTF-8"); + // 5、发送邮件 + ts.sendMessage(message, message.getAllRecipients()); + ts.close(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + log.error("", e); + result = "fail"; + } + return result; + } + + /** + * @Method: createSimpleMail + * @Description: 创建一封只包含文本的邮件 + * @Anthor:Harry Liang + * + * @param session + * @return + * @throws Exception + */ + public static MimeMessage createSimpleMail(Session session) + throws Exception { + // 创建邮件对象 + MimeMessage message = new MimeMessage(session); + // 指明邮件的发件人 + InternetAddress address = new InternetAddress("noreply@bestseller.com.cn"); + address.setPersonal("PLM Admin"); + message.setFrom(address); + // 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发 + message.addRecipient(Message.RecipientType.TO, new InternetAddress("harry.liang@centricsoftware.com")); + message.addRecipient(Message.RecipientType.TO, new InternetAddress("jief@bestseller.com.cn")); + // 邮件的标题 + message.setSubject("邮件发送个测试"); + // 邮件的文本内容 + message.setContent("你好啊!", "text/html;charset=UTF-8"); + // 返回创建好的邮件对象 + return message; + } +} \ No newline at end of file diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/MarkImageUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/MarkImageUtil.java new file mode 100644 index 0000000..dd5b0fc --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/MarkImageUtil.java @@ -0,0 +1,55 @@ +package com.centricsoftware.commons.utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; + +public class MarkImageUtil { + + /** + * 添加图标在图片上 + * @author liaochangjiang + * @since 2024-02-21 11:27 + */ + public static InputStream addImgMark(Image con, int iconWidth, int iconHeight, Image img, int x, int y,int width, int height) throws IOException { + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bi.createGraphics(); + g.getDeviceConfiguration().createCompatibleImage(img.getWidth(null), img.getHeight(null), Transparency.TRANSLUCENT); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(img.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); + float clarity = 1f;//透明度 + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, clarity)); + g.drawImage(con.getScaledInstance(iconWidth, iconHeight, Image.SCALE_SMOOTH), x, y, null); + g.dispose(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(bi, "png", os); + // 最终返回的文件流 + return new ByteArrayInputStream(os.toByteArray()); + } + + /** + * 添加文字在图片上 + * @author liaochangjiang + * @since 2024-02-21 11:27 + */ + public static InputStream addTextMark(Image img, int x, int y,int width, int height,String martText, Color color, Font font) throws IOException { + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bi.createGraphics(); + g.getDeviceConfiguration().createCompatibleImage(img.getWidth(null), img.getHeight(null), Transparency.TRANSLUCENT); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(img.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null); + if(null != color) { + g.setColor(color); + } + if(null != font){ + g.setFont(font); + } + g.drawString(martText, x, y); + g.dispose(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(bi, "png", os); + // 最终返回的文件流 + return new ByteArrayInputStream(os.toByteArray()); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/PageUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/PageUtil.java new file mode 100644 index 0000000..3658723 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/PageUtil.java @@ -0,0 +1,21 @@ +package com.centricsoftware.commons.utils; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.commons.dto.PlmPageReqVo; +import com.centricsoftware.config.cons.Constants; + +public class PageUtil { + /** + * 构建分页对象 + * + * @author liaochangjiang + * @since 2021-01-18 15:59:16 + */ + public static Page buildPageParam(PlmPageReqVo req, Class clazz) { + final Page page = new Page<>(req.getPageNum(), req.getPageSize()); + if (page.getSize() > Constants.PAGE_MAX_LIMIT) { + page.setMaxLimit(page.getSize()); + } + return page; + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/SMBFileUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/SMBFileUtil.java new file mode 100644 index 0000000..be2e3c8 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/SMBFileUtil.java @@ -0,0 +1,197 @@ +package com.centricsoftware.commons.utils; + +import com.centricsoftware.config.entity.CsProperties; +import jcifs.smb.NtlmPasswordAuthentication; +import jcifs.smb.SmbFile; +import jcifs.smb.SmbFileInputStream; +import jcifs.smb.SmbFileOutputStream; + +import java.io.*; + +/** + * 向共享盘上传递图片信息 + * 同类方法 {@link cn.hutool.extra.ftp.Ftp} + * @author Harry + * + */ +public class SMBFileUtil { + + public String fName = ""; + public String fPassword = ""; + public String fHost = ""; + public String fFolder = ""; + + public SMBFileUtil(String userName, String password, String host,String folder){ + this.fName = userName; + this.fPassword = password; + this.fHost = host; + this.fFolder = folder; + } + + public SMBFileUtil(){ + CsProperties properties = SpringUtil.getBean(CsProperties.class); + String userName = properties.getValue("cs.imageserver.username"); + String password = properties.getValue("cs.imageserver.password"); + String host = properties.getValue("cs.imageserver.host"); + String folder = properties.getValue("cs.imageserver.folder"); + + this.fName = userName; + this.fPassword = password; + this.fHost = host; + this.fFolder = folder; + } + + public String uploadFile(File localFile){ + InputStream in = null; + OutputStream out = null; + String filepath = ""; + try { + //获取图片 + + String remotePhotoUrl = "smb://"+this.fHost+"/"+this.fFolder; //存放图片的共享目录 + NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",this.fName,this.fPassword); + //SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmssSSS_"); + SmbFile remoteFile = new SmbFile(remotePhotoUrl + "/" + localFile.getName(),auth); + remoteFile.connect(); //尝试连接 + + //如果远程可以连接,那就判断先父节点目录是否存在,不存在就创建 + SmbFile parentDir = new SmbFile(remoteFile.getParent(),auth); + if (!parentDir.exists()) + { + parentDir.mkdirs(); + } + + filepath = "http://"+this.fHost+"/"+this.fFolder.replace("eeka", "")+"/"+localFile.getName(); + in = new BufferedInputStream(new FileInputStream(localFile)); + out = new BufferedOutputStream(new SmbFileOutputStream(remoteFile)); + + byte[] buffer = new byte[4096]; + int len = 0; //读取长度 + while ((len = in.read(buffer, 0, buffer.length)) != -1) { + out.write(buffer, 0, len); + } + out.flush(); //刷新缓冲的输出流 + } + catch (Exception e) { + String msg = "发生错误:" + e.getLocalizedMessage(); + System.out.println(msg); + } + finally { + try { + if(out != null) { + out.close(); + } + if(in != null) { + in.close(); + } + } + catch (Exception e) {} + } + return filepath; + } + + public static void uploadFile(File localFile,String userName, String password, String host,String folder){ + InputStream in = null; + OutputStream out = null; + try { + //获取图片 + + String remotePhotoUrl = "smb://"+host+"/"+folder; //存放图片的共享目录 + //SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmssSSS_"); + NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",userName,password); + SmbFile remoteFile = new SmbFile(remotePhotoUrl + "/" + localFile.getName(),auth); + remoteFile.connect(); //尝试连接 + + //如果远程可以连接,那就判断先父节点目录是否存在,不存在就创建 + SmbFile parentDir = new SmbFile(remoteFile.getParent(),auth); + if (!parentDir.exists()) + { + parentDir.mkdirs(); + } + + System.out.println(remotePhotoUrl+"=connected"); + in = new BufferedInputStream(new FileInputStream(localFile)); + out = new BufferedOutputStream(new SmbFileOutputStream(remoteFile)); + + byte[] buffer = new byte[4096]; + int len = 0; //读取长度 + while ((len = in.read(buffer, 0, buffer.length)) != -1) { + out.write(buffer, 0, len); + } + out.flush(); //刷新缓冲的输出流 + } + catch (Exception e) { + e.printStackTrace(); + String msg = "发生错误:" + e.getLocalizedMessage(); + System.out.println(msg); + } + finally { + try { + if(out != null) { + out.close(); + } + if(in != null) { + in.close(); + } + } + catch (Exception e) {} + } + } + + /** + * 下载文件 + * @return + */ + public byte[] downloadFile(String fileName){ + InputStream in = null ; + ByteArrayOutputStream out = null ; + try { + //创建远程文件对象 + String remotePhotoUrl = "smb://"+this.fHost+"/"+this.fFolder+"/"+fileName; + NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("",this.fName,this.fPassword); + SmbFile remoteFile = new SmbFile(remotePhotoUrl,auth); + remoteFile.connect(); //尝试连接 + + //如果远程可以连接,那就判断先父节点目录是否存在,不存在就创建 + SmbFile parentDir = new SmbFile(remoteFile.getParent(),auth); + if (!parentDir.exists()) + { + parentDir.mkdirs(); + } + + + //创建文件流 + in = new BufferedInputStream(new SmbFileInputStream(remoteFile)); + out = new ByteArrayOutputStream((int)remoteFile.length()); + //读取文件内容 + byte[] buffer = new byte[4096]; + int len = 0; //读取长度 + while ((len = in.read(buffer, 0, buffer.length)) != - 1) { + out.write(buffer, 0, len); + } + + out.flush(); //刷新缓冲的输出流 + return out.toByteArray(); + } + catch (Exception e) { + String msg = "下载远程文件出错:" + e.getLocalizedMessage(); + System.out.println(msg); + } + finally { + try { + if(out != null) { + out.close(); + } + if(in != null) { + in.close(); + } + } + catch (Exception e) {} + } + return null; + } + + public static void main(String[] args) { + uploadFile(new File("D:\\C8\\C8Test.model.xml"), "eeka", "Gst#2017", "10.7.121.10", "eeka/centric/"); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ServicesUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/ServicesUtil.java new file mode 100644 index 0000000..4ef4b13 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ServicesUtil.java @@ -0,0 +1,326 @@ +/** +* @author GHUANG +* @version 2016年3月28日 下午5:48:18 +* +*/ +package com.centricsoftware.commons.utils; + +import org.apache.commons.codec.binary.Base64; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.util.HashMap; + +/** + * 发送http请求工具类 + * 同类方法{@link cn.hutool.http.HttpUtil} + * @author zheng.gong + * @date 2020/4/27 + */ +public class ServicesUtil { + + public static String HtmLog = "ConnectHtmLog"; + + static HashMap initmap = null; + static { + initmap = new HashMap(); + initmap.put("0", ""); + initmap.put("1004", "Exception:服务器网络异常,请稍后重试"); + initmap.put("1001", "Exception:请求参数错误"); + initmap.put("1002", "Exception:该用户不存在"); + initmap.put("1003", "Exception:该sign签名不合法"); + initmap.put("1005", "Exception:SCM错误!"); + initmap.put("1006", "Exception:SCM错误!"); + initmap.put("1007", "Exception:网络连接错误!"); + initmap.put("1008", "Exception:SCM错误!"); + initmap.put("1009", "Exception:SCM错误!"); + } + + /** + * REST + * + * @param targetURL + * @param url + * @param typeId + * @return + * @author GHUANG + * @version 2019年4月20日 上午7:26:42 + */ + public static String getData(String targetURL, String url, String typeId) { + // + String output = ""; + try { + + URL restServiceURL = new URL(targetURL); + + HttpURLConnection httpConnection = (HttpURLConnection) restServiceURL.openConnection(); + httpConnection.setRequestMethod("GET"); + httpConnection.setRequestProperty("Connection", "Keep-Alive");// 维持长连接 + httpConnection.setRequestProperty("Charset", "UTF-8"); + httpConnection.setRequestProperty("Accept", "application/json"); + + if (httpConnection.getResponseCode() != 200) { + throw new RuntimeException("HTTP GET Request Failed with Error code : " + + httpConnection.getResponseCode()); + } + + BufferedReader responseBuffer = new BufferedReader(new InputStreamReader( + (httpConnection.getInputStream()))); + + String line; + while ((line = responseBuffer.readLine()) != null) { + output = output + line; + } + httpConnection.disconnect(); + System.out.println("get Server Success:\n" + output); + if (output.indexOf("{") > 0) { + output = output.substring(output.indexOf("{"), output.lastIndexOf("}") + 1); + } + } catch (Exception e) { + StackTraceElement[] stackArray = e.getStackTrace(); + for (int i = 0; i < stackArray.length; i++) { + StackTraceElement element = stackArray[i]; + } + + e.printStackTrace(); + output = "Exception when PLM try to connect K3 "; + + } + return output; + } + +// static void callWebService(String param, String url, String user, String psd) throws AxisFault { +// System.out.println("call begin....."); +// RPCServiceClient serviceClient = new RPCServiceClient(); +// Options options = serviceClient.getOptions(); +// EndpointReference targetEPR = new EndpointReference(url); +// options.setTo(targetEPR); +// options.setManageSession(true); +// options.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, true); +// options.setTimeOutInMilliSeconds(600000L); +// options.setUserName("connuser"); +// options.setPassword("a87654321"); +// // String response = "application/json "; +// Object[] opAddEntryArgs = new Object[] { param }; +// Class[] classes = new Class[] { String.class }; +// QName opAddEntry = new QName("http://ws.apache.org/axis2", "receive"); +// System.out.println("result=" + serviceClient.invokeBlocking(opAddEntry, opAddEntryArgs, classes)[0]); +// System.out.println("call end..."); +// } + + /** + * REST支持 + * + * @param targetURL + * @param jsonarray + * @param typeId + * @return + * @author GHUANG + * @version 2019年4月20日 上午7:26:30 + */ + + public static String postData(String targetURL, String jsonarray, String user, String password) { + String remsg = ""; + HttpURLConnection httpConnection = null; + try { + System.out.println(targetURL); + URL targetUrl = new URL(targetURL); + System.out.println(user + "----" + password); + httpConnection = (HttpURLConnection) targetUrl.openConnection(); + httpConnection.setDoOutput(true); + httpConnection.setDoInput(true); + httpConnection.setRequestMethod("POST"); + httpConnection.setUseCaches(false); + // 1.4的问题,必须有SOAPAction + httpConnection.setRequestProperty("SOAPAction", "application/soap+xml; charset=utf-8"); + httpConnection.setRequestProperty("Content-Type", "text/xml; charset=utf-8"); + httpConnection.setRequestProperty("Connection", "Keep-Alive");// 维持长连接 + httpConnection.setRequestProperty("Charset", "UTF-8"); + byte[] requestStringBytes = jsonarray.getBytes(StandardCharsets.UTF_8); + httpConnection.setRequestProperty("Content-length", "" + requestStringBytes.length); + String users = user + ":" + password; + httpConnection.addRequestProperty("Authorization", + "Basic " + new String(Base64.encodeBase64(users.getBytes()))); + OutputStream outputStream = httpConnection.getOutputStream(); + outputStream.write(requestStringBytes); + outputStream.flush(); + outputStream.close(); + if (httpConnection.getResponseCode() != 200) { + InputStream err = httpConnection.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); + StringBuffer sb = new StringBuffer(); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + throw new RuntimeException("Failed : HTTP error code : " + + httpConnection.getResponseCode() + ",exception info" + sb.toString()); + } + BufferedReader responseBuffer = new BufferedReader(new InputStreamReader( + (httpConnection.getInputStream()))); +// +// String line; +// System.out.println("Output from Server:\n"); +// while ((line = responseBuffer.readLine()) != null) { +// System.out.println(line); +// } + responseBuffer.close(); + httpConnection.disconnect(); + + } catch (Exception e) { + e.printStackTrace(); + remsg = e.getMessage(); + if (remsg.contains("refused")) { + remsg = "对方接口拒绝连接请求!"; + } + remsg = "Exception:" + remsg; + + } finally { + if (httpConnection != null) { + httpConnection.disconnect(); + } + } + return remsg; + } + + /** + * REST支持SOAP,xml格式param + * + * @param targetURL + * @param param + * @param user + * @param password + * @return + * @author GHUANG + * @version 2019年4月20日 上午7:26:30 + */ + public static String putData(String targetURL, String param, String user, String password) + throws Exception { + String result = ""; + HttpURLConnection con = null; + try { + long t1 = System.currentTimeMillis(); // 执行开始时间记录 + URL url = new URL(targetURL); + con = (HttpURLConnection) url.openConnection(); + con.setDoOutput(true); + con.setDoInput(true); + con.setConnectTimeout(30 * 60 * 1000); + con.setRequestProperty("Content-Type", "text/xml;charset=UTF-8"); + // con.setRequestProperty("Content-Type", "multipart/form-data; charset=UTF-8; "); + con.setRequestProperty("accept", "*/*"); + con.setRequestProperty("Connection", "Keep-Alive"); + con.setDoInput(true); + con.setDoOutput(true); + // con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + // con.setRequestProperty("Charset", "UTF-8"); + // con.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); + con.setRequestMethod("POST"); + con.setUseCaches(false); + String users = user + ":" + password; + con.addRequestProperty("Authorization", "Basic " + new String(Base64.encodeBase64(users.getBytes()))); + con.connect(); + // param = URLEncoder.encode(param, "utf-8"); + OutputStream os = con.getOutputStream(); + OutputStreamWriter out = new OutputStreamWriter(os, StandardCharsets.UTF_8); + // String utf8String = new String(param.getBytes(), "utf-8"); + // NodeUtil.outInfo("-----" + con.getContentEncoding() + "", HtmLog); + out.write(param); + out.flush(); + out.close(); + System.out.println(con.getResponseCode()); + if (con.getResponseCode() == 200) { + BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)); + + br.close(); + } else { + InputStream err = con.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); + StringBuffer sb = new StringBuffer(); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + throw new RuntimeException("Failed : HTTP error code : " + + con.getResponseCode() + ",exception info" + sb.toString()); + } + con.disconnect(); + + } catch (Exception e) { + result = e.getMessage(); + + } finally { + if (con != null) { + con.disconnect(); + } + } + return result; + } + + /** + * 生成sign + * + * @param appkey + * @param secret + * @param sign_method + * @return + */ + public static String getSign(String epid, String appkey, String secret, String timestamp, String jsonStr) { + if (jsonStr.length() == 0) { + return ""; + } + String paramString = epid + appkey + secret + timestamp + jsonStr; + String sign = ""; + sign = byte2hex(encryptMD5(paramString, "UTF-8")); + return sign; + } + + /** + * MD5加密 + * + * @param data + * 字符串 + * + * @param format + * 数据编码方式,如:UTF-8、GBK + * @return byte[]字节数组 + * @throws IOException + */ + public static byte[] encryptMD5(String data, String format) { + byte[] bytes = null; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(data.getBytes(format)); + bytes = md.digest(); + } catch (GeneralSecurityException gse) { + gse.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return bytes; + } + + public static String byte2hex(byte[] bytes) { + StringBuilder sign = new StringBuilder(); + + for (int i = 0; i < bytes.length; ++i) { + String hex = Integer.toHexString(bytes[i] & 0xFF); + if (hex.length() == 1) { + sign.append("0"); + } + sign.append(hex.toUpperCase()); + } + return sign.toString().toLowerCase(); + } + + public static void main(String[] args) throws Exception { + // TODO Auto-generated method stub + // freezeMaterial(); + System.out.println("---" + getData("http://192.168.37.133/plmservice/services/ESHub/pushChangeData?url=111" + + "&attrname=222&attrvalue=22211&flag=y", "", "")); + } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/SpringContextHolder.java b/commons/src/main/java/com/centricsoftware/commons/utils/SpringContextHolder.java new file mode 100644 index 0000000..c3c92c7 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/SpringContextHolder.java @@ -0,0 +1,88 @@ +package com.centricsoftware.commons.utils; + +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ + +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +/** + * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext. + * + */ +@Service +@Lazy(false) +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + + private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class); + + /** + * 取得存储在静态变量中的ApplicationContext. + */ + public static ApplicationContext getApplicationContext() { + assertContextInjected(); + return applicationContext; + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + public static void clearHolder() { + if (logger.isDebugEnabled()){ + logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext); + } + applicationContext = null; + } + + /** + * 实现ApplicationContextAware接口, 注入Context到静态变量中. + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + SpringContextHolder.applicationContext = applicationContext; + } + + public static String getStatic(){ + return SpringContextHolder.getApplicationContext().getApplicationName()+ "/static"; + } + /** + * 实现DisposableBean接口, 在Context关闭时清理静态变量. + */ + @Override + public void destroy(){ + SpringContextHolder.clearHolder(); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder."); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/SpringUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/SpringUtil.java new file mode 100644 index 0000000..229105e --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/SpringUtil.java @@ -0,0 +1,43 @@ +package com.centricsoftware.commons.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +/** + * Spring工具类,只有两个功能即获取容器中注册的bean,向容器中注册bean + * @author zheng.gong + * @date 2020/4/27 + */ +@Component +public class SpringUtil implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringUtil.applicationContext == null) { + SpringUtil.applicationContext = applicationContext; + } + } + + //获取applicationContext + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + //通过name获取 Bean. + public static Object getBean(String name) { + return getApplicationContext().getBean(name); + } + + //通过class获取Bean. + public static T getBean(Class clazz) { + return getApplicationContext().getBean(clazz); + } + + //通过name,以及Clazz返回指定的Bean + public static T getBean(String name, Class clazz) { + return getApplicationContext().getBean(name, clazz); + } +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/StringUtils.java b/commons/src/main/java/com/centricsoftware/commons/utils/StringUtils.java new file mode 100644 index 0000000..421d9d6 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/StringUtils.java @@ -0,0 +1,616 @@ +/** + * Copyright © 2015-2020 JeePlus All rights reserved. + */ +package com.centricsoftware.commons.utils; + +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 + * + * @author jeeplus + * @version 2016-05-22 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + private static final Logger logger = LoggerFactory.getLogger(StringUtils.class); + + private static final char SEPARATOR = '_'; + private static final String CHARSET_NAME = "UTF-8"; + + /** + * 转换为字节数组 + * + * @param str + * @return + */ + public static byte[] getBytes(String str) { + if (str != null) { + try { + return str.getBytes(CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + return null; + } + } else { + return null; + } + } + + /** + * 转换为字节数组 + * + * @param bytes + * @return + */ + public static String toString(byte[] bytes) { + try { + return new String(bytes, CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + return EMPTY; + } + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inString(String str, String... strs) { + if (str != null) { + for (String s : strs) { + if (str.equals(trim(s))) { + return true; + } + } + } + return false; + } + + /** + * 替换掉HTML标签方法 + */ + public static String replaceHtml(String html) { + if (isBlank(html)) { + return ""; + } + String regEx = "<.+?>"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(html); + String s = m.replaceAll(""); + return s; + } + + /** + * 替换为手机识别的HTML,去掉样式及属性,保留回车。 + * + * @param html + * @return + */ + public static String replaceMobileHtml(String html) { + if (html == null) { + return ""; + } + return html.replaceAll("<([a-z]+?)\\s+?.*?>", "<$1>"); + } + + /** + * 替换为手机识别的HTML,去掉样式及属性,保留回车。 + * + * @param txt + * @return + */ + public static String toHtml(String txt) { + if (txt == null) { + return ""; + } + return replace(replace(EncodeUtils.escapeHtml(txt), "\n", "
"), "\t", "    "); + } + + /** + * 缩略字符串(不区分中英文字符) + * + * @param str 目标字符串 + * @param length 截取长度 + * @return + */ + public static String abbr(String str, int length) { + if (str == null) { + return ""; + } + try { + StringBuilder sb = new StringBuilder(); + int currentLength = 0; + for (char c : replaceHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) { + currentLength += String.valueOf(c).getBytes("GBK").length; + if (currentLength <= length - 3) { + sb.append(c); + } else { + sb.append("..."); + break; + } + } + return sb.toString(); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage(), e); + } + return ""; + } + + public static String abbr2(String param, int length) { + if (param == null) { + return ""; + } + StringBuffer result = new StringBuffer(); + int n = 0; + char temp; + boolean isCode = false; // 是不是HTML代码 + boolean isHTML = false; // 是不是HTML特殊字符,如  + for (int i = 0; i < param.length(); i++) { + temp = param.charAt(i); + if (temp == '<') { + isCode = true; + } else if (temp == '&') { + isHTML = true; + } else if (temp == '>' && isCode) { + n = n - 1; + isCode = false; + } else if (temp == ';' && isHTML) { + isHTML = false; + } + try { + if (!isCode && !isHTML) { + n += String.valueOf(temp).getBytes("GBK").length; + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage(), e); + } + + if (n <= length - 3) { + result.append(temp); + } else { + result.append("..."); + break; + } + } + // 取出截取字符串中的HTML标记 + String temp_result = result.toString().replaceAll("(>)[^<>]*(]*/?>", + ""); + // 去掉成对的HTML标记 + temp_result = temp_result.replaceAll("<([a-zA-Z]+)[^<>]*>(.*?)", "$2"); + // 用正则表达式取出标记 + Pattern p = Pattern.compile("<([a-zA-Z]+)[^<>]*>"); + Matcher m = p.matcher(temp_result); + List endHTML = Lists.newArrayList(); + while (m.find()) { + endHTML.add(m.group(1)); + } + // 补全不成对的HTML标记 + for (int i = endHTML.size() - 1; i >= 0; i--) { + result.append(""); + } + return result.toString(); + } + + /** + * 转换为Double类型 + */ + public static Double toDouble(Object val) { + if (val == null) { + return 0D; + } + try { + return Double.valueOf(trim(val.toString())); + } catch (Exception e) { + return 0D; + } + } + + /** + * 转换为Float类型 + */ + public static Float toFloat(Object val) { + return toDouble(val).floatValue(); + } + + /** + * 转换为Long类型 + */ + public static Long toLong(Object val) { + return toDouble(val).longValue(); + } + + /** + * 转换为Integer类型 + */ + public static Integer toInteger(Object val) { + return toLong(val).intValue(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + + s = s.toLowerCase(); + + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCapitalizeCamelCase(String s) { + if (s == null) { + return null; + } + s = toCamelCase(s); + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toUnderScoreCase(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + boolean nextUpperCase = true; + + if (i < (s.length() - 1)) { + nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); + } + + if ((i > 0) && Character.isUpperCase(c)) { + if (!upperCase || !nextUpperCase) { + sb.append(SEPARATOR); + } + upperCase = true; + } else { + upperCase = false; + } + + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 转换为JS获取对象值,生成三目运算返回结果 + * + * @param objectString 对象串 例如:row.user.id + * 返回:!row?'':!row.user?'':!row.user.id?'':row.user.id + */ + public static String jsGetVal(String objectString) { + StringBuilder result = new StringBuilder(); + StringBuilder val = new StringBuilder(); + String[] vals = split(objectString, "."); + for (int i = 0; i < vals.length; i++) { + val.append("." + vals[i]); + result.append("!" + (val.substring(1)) + "?'':"); + } + result.append(val.substring(1)); + return result.toString(); + } + + /** + * 将字符串首字母转大写 + * + * @param str + * @return + */ + public static String firstUpperCase(String str) { + if ((str == null) || (str.length() == 0)) + return str; + char[] ch = str.toCharArray(); + if (ch[0] >= 'a' && ch[0] <= 'z') { + ch[0] = (char) (ch[0] - 32); + } + return new String(ch); + } + + /** + * 将字符串首字母转小写 + * + * @param str + * @return + */ + public static String firstLowerCase(String str) { + if ((str == null) || (str.length() == 0)) + return str; + char[] ch = str.toCharArray(); + if (ch[0] >= 'A' && ch[0] <= 'Z') { + ch[0] = (char) (ch[0] + 32); + } + return new String(ch); + } + + /*** + * 截取字符串 里面的数字 + * + * @param str + * @param breakflag 遇到非字符串是否立即终止 + * @return + * @date:2018年7月10日 + * @author:wuxiaoting + */ + public static String getNumeric(String str, boolean breakflag) { + StringBuilder sBuilder = new StringBuilder(20); + for (int i = 0; i < str.length(); i++) { + if (!Character.isDigit(str.charAt(i))) { + if (breakflag) + break; + continue; + } + sBuilder.append(str.charAt(i)); + } + return sBuilder.toString(); + } + + /** + * 利用正则表达式判断字符串是否是数字 + * + * @param str + * @return + */ + public static boolean isNumeric(String str) { + if (StringUtils.isBlank(str)) + return false; + Pattern pattern = Pattern.compile("^[-+]?(([0-9]+)([.]([0-9]+))?|([.]([0-9]+))?)$"); + Matcher isNum = pattern.matcher(str); + return isNum.matches(); + } + + public static boolean isInteger(String str) { + if (StringUtils.isBlank(str)) + return false; + Pattern pattern = Pattern.compile("[0-9]*"); + Matcher isNum = pattern.matcher(str); + return isNum.matches(); + } + + /** + * 将ErrorStack转化为String. + */ + public static String getStackTraceAsString(Throwable e) { + if (e == null){ + return ""; + } + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } + + /** + * 检查Email格式 + * @param email + * @return + */ + public static boolean checkEmail(String email){ + if (StringUtils.isBlank(email)) { + return false; + } + boolean flag = false; + try{ + String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + Pattern regex = Pattern.compile(check); + Matcher matcher = regex.matcher(email); + flag = matcher.matches(); + }catch(Exception e){ + flag = false; + } + return flag; + } + + public static String getFormatedValue(String valueStr, String format) { + if (StringUtils.isBlank(valueStr) || "".equals(valueStr.trim())) { + valueStr = "0"; + } + DecimalFormat df = new DecimalFormat(format); + return df.format(Double.parseDouble(valueStr.trim())); + } + + /** + * 对字符串采用UTF-8编码后,用MD5进行摘要。 + */ + public static byte[] encryptMD5(String data) throws IOException { + return encryptMD5(data.getBytes(CHARSET_NAME)); + } + + + /** + * 对字节流进行MD5摘要。 + */ + public static byte[] encryptMD5(byte[] data) throws IOException { + byte[] bytes = null; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + bytes = md.digest(data); + } catch (GeneralSecurityException gse) { + throw new IOException(gse.toString()); + } + return bytes; + } + + /** + * 把字节流转换为十六进制表示方式。 + */ + public static String byte2hex(byte[] bytes) { + StringBuilder sign = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(bytes[i] & 0xFF); + if (hex.length() == 1) { + sign.append("0"); + } + sign.append(hex.toUpperCase()); + } + return sign.toString(); + } + + /** + * 将字符型的日期转成long + */ + public static long date2Long(String date,String format) { + DateFormat dateFormat = new SimpleDateFormat(format); + long tsvalue = 0; + try { + Timestamp ts = new Timestamp(dateFormat.parse(date).getTime()); + tsvalue = ts.getTime() / 1000; + } catch (Exception e) { + return 0; + } + return tsvalue; + } + + /** + * 字符型日期是否合法 + * @param date + * @param format + * @return + * @author 谢旭林 + * @version 创建时间:2020年4月8日 下午4:23:47 + * + */ + public static boolean validDateFormat(String date,String format) { + DateFormat dateFormat = new SimpleDateFormat(format); + try { + dateFormat.parse(date); + } catch (Exception e) { + return false; + } + return true; + } + + /** + * 将字符串转译,以免xml报错 + */ + public static String escape4Xml(String xmlStr) { + return xmlStr.replace("&", "&").replace("<", "<").replace(">", ">") + .replace("'", "'").replace("\"", """); + } + + public static List extractMessageByRegular(String msg){ + return extractMessageByRegular(msg,"3"); + } + /** + *正则表达式匹配以##,{},[]为标记的变量 + * @param msg + * @return + */ + public static List extractMessageByRegular(String msg,String type){ + String pattern = ""; + switch (type){ + case "1": + pattern = "(#[^#]*#)";//以##为标记 + break; + case "2": + pattern = "(\\[[^\\]]*\\])"; //以[]为标记 + break; + case "3": + pattern = "(\\{[^\\}]*\\})";//以{}为标记 + break; + default: + pattern = "(\\{[^\\}]*\\})";//以{}为标记 + } + List list=new ArrayList(); + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(msg); + while(m.find()){ + list.add(m.group().substring(1, m.group().length()-1)); + } + return list; + } + + public static String fillValueInMsg(String msg,String variableName,String value){ + return fillValueInMsg( msg, variableName, value,"3"); + } + /** + * 填充msg中以##,{},[]为标记的变量 + * @param msg + * @param variableName + * @param value + * @param type + * @return + */ + public static String fillValueInMsg(String msg,String variableName,String value,String type){ + String newValue = ""; + switch (type){ + case "1": + newValue = "#"+variableName+"#";//以##为标记 + break; + case "2": + newValue = "\\["+variableName+"\\]"; //以[]为标记 + break; + case "3": + newValue = "\\{"+variableName+"\\}";//以{}为标记 + break; + default: + newValue = "\\{"+variableName+"\\}";//以{}为标记 + } + if(StringUtils.isBlank(value)){ + value = ""; + } + return msg.replaceAll(newValue,value); + } + +} diff --git a/commons/src/main/java/com/centricsoftware/commons/utils/ZIPUtil.java b/commons/src/main/java/com/centricsoftware/commons/utils/ZIPUtil.java new file mode 100644 index 0000000..da9aa80 --- /dev/null +++ b/commons/src/main/java/com/centricsoftware/commons/utils/ZIPUtil.java @@ -0,0 +1,393 @@ +package com.centricsoftware.commons.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/** + * 解压Zip文件工具类 + * 同类方法{@link cn.hutool.core.util.ZipUtil} + * @author harry liang + * + */ +@Slf4j +public class ZIPUtil { + private static final int buffer = 2048; + /** + * 解压Zip文件 + * + * @param path + * 文件目录 + * @return 解压缩文件目录 + */ + public static String unZip(String path) { + int count = -1; + String savepath = ""; + + File file = null; + InputStream is = null; + FileOutputStream fos = null; + BufferedOutputStream bos = null; + savepath = path.substring(0, path.lastIndexOf(".")) + File.separator; // 保存解压文件目录 + new File(savepath).mkdir(); // 创建保存目录 + ZipFile zipFile = null; + try { + try { + // zipFile = new ZipFile(path, Charset.forName("gbk")); + zipFile = new ZipFile(path, Charset.forName("utf-8")); // 解决中文乱码问题 + } catch (Exception e) { + + } + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + byte[] buf = new byte[buffer]; + + ZipEntry entry = (ZipEntry) entries.nextElement(); + + String filename = entry.getName(); + boolean ismkdir = false; + if (filename.lastIndexOf("/") != -1) { // 检查此文件是否带有文件夹 + ismkdir = true; + } + filename = savepath + filename; + + if (entry.isDirectory()) { // 如果是文件夹先创建 + file = new File(filename); + file.mkdirs(); + continue; + } + file = new File(filename); + if (!file.exists()) { // 如果是目录先创建 + if (ismkdir) { + new File(filename.substring(0, filename.lastIndexOf("/"))).mkdirs(); // 目录先创建 + } + } + file.createNewFile(); // 创建文件 + + is = zipFile.getInputStream(entry); + fos = new FileOutputStream(file); + bos = new BufferedOutputStream(fos, buffer); + + while ((count = is.read(buf)) > -1) { + bos.write(buf, 0, count); + } + bos.flush(); + bos.close(); + fos.close(); + + is.close(); + } + + zipFile.close(); + + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + try { + if (bos != null) { + bos.close(); + } + if (fos != null) { + fos.close(); + } + if (is != null) { + is.close(); + } + if (zipFile != null) { + zipFile.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + return savepath; + } + + private static final int BUFFER_SIZE = 2 * 1024; + + /** + * + * 压缩成ZIP 方法1 + * + * @param srcDir + * 压缩文件夹路径 + * + * @param out + * 压缩文件输出流 + * + * @param KeepDirStructure + * 是否保留原来的目录结构,true:保留目录结构; + * + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + * + * @throws RuntimeException + * 压缩失败会抛出运行时异常 + * + */ + + public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure) + + throws RuntimeException { + + long start = System.currentTimeMillis(); + + ZipOutputStream zos = null; + + try { + + zos = new ZipOutputStream(out); + + File sourceFile = new File(srcDir); + + compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure); + + long end = System.currentTimeMillis(); + + System.out.println("压缩完成,耗时:" + (end - start) + " ms"); + + } catch (Exception e) { + + throw new RuntimeException("zip error from ZipUtils", e); + + } finally { + + if (zos != null) { + + try { + + zos.close(); + + } catch (IOException e) { + + e.printStackTrace(); + + } + + } + + } + + } + + /** + * + * 压缩成ZIP 方法2 + * + * @param srcFiles + * 需要压缩的文件列表 + * + * @param out + * 压缩文件输出流 + * + * @throws RuntimeException + * 压缩失败会抛出运行时异常 + * + */ + + public static void toZip(List srcFiles, OutputStream out) throws RuntimeException { + + long start = System.currentTimeMillis(); + + ZipOutputStream zos = null; + + try { + + zos = new ZipOutputStream(out); + + for (File srcFile : srcFiles) { + + byte[] buf = new byte[BUFFER_SIZE]; + + zos.putNextEntry(new ZipEntry(srcFile.getName())); + + int len; + + FileInputStream in = new FileInputStream(srcFile); + + while ((len = in.read(buf)) != -1) { + + zos.write(buf, 0, len); + + } + + zos.closeEntry(); + + in.close(); + + } + + long end = System.currentTimeMillis(); + + System.out.println("压缩完成,耗时:" + (end - start) + " ms"); + + } catch (Exception e) { + log.error("ZIP", e); + throw new RuntimeException("zip error from ZipUtils", e); + + } finally { + + if (zos != null) { + + try { + + zos.close(); + + } catch (IOException e) { + + e.printStackTrace(); + + } + + } + + } + + } + + /** + * + * 递归压缩方法 + * + * @param sourceFile + * 源文件 + * + * @param zos + * zip输出流 + * + * @param name + * 压缩后的名称 + * + * @param KeepDirStructure + * 是否保留原来的目录结构,true:保留目录结构; + * + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + * + * @throws Exception + * + */ + + private static void compress(File sourceFile, ZipOutputStream zos, String name, + + boolean KeepDirStructure) throws Exception { + + byte[] buf = new byte[BUFFER_SIZE]; + + if (sourceFile.isFile()) { + + // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字 + + zos.putNextEntry(new ZipEntry(name)); + + // copy文件到zip输出流中 + + int len; + + FileInputStream in = new FileInputStream(sourceFile); + + while ((len = in.read(buf)) != -1) { + + zos.write(buf, 0, len); + + } + + // Complete the entry + + zos.closeEntry(); + + in.close(); + + } else { + + File[] listFiles = sourceFile.listFiles(); + + if (listFiles == null || listFiles.length == 0) { + + // 需要保留原来的文件结构时,需要对空文件夹进行处理 + + if (KeepDirStructure) { + + // 空文件夹的处理 + + zos.putNextEntry(new ZipEntry(name + "/")); + + // 没有文件,不需要文件的copy + + zos.closeEntry(); + + } + + } else { + + for (File file : listFiles) { + + // 判断是否需要保留原来的文件结构 + + if (KeepDirStructure) { + + // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠, + + // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了 + + compress(file, zos, name + "/" + file.getName(), KeepDirStructure); + + } else { + + compress(file, zos, file.getName(), KeepDirStructure); + + } + + } + + } + + } + + } + + public static void main(String[] args) throws Exception { + + /** 测试压缩方法1 */ + + FileOutputStream fos1 = new FileOutputStream(new File("c:/mytest01.zip")); + + ZIPUtil.toZip("D:/log", fos1, true); + + /** 测试压缩方法2 */ + + List fileList = new ArrayList<>(); + + fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/jar.exe")); + + fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/java.exe")); + + FileOutputStream fos2 = new FileOutputStream(new File("c:/mytest02.zip")); + + ZIPUtil.toZip(fileList, fos2); + + } + /* + * public static void main(String[] args) { unZip("F:\\110000002.zip"); String f = "F:\\110000002"; File file = new + * File(f); String[] test=file.list(); for(int i=0;i + + + Aspose.Total for Java + + Enterprise + 20991231 + 20991231 + 8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7 + + sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU= + \ No newline at end of file diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/config/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/config/.mvn/wrapper/MavenWrapperDownloader.java b/config/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/config/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/config/.mvn/wrapper/maven-wrapper.jar b/config/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/config/.mvn/wrapper/maven-wrapper.jar differ diff --git a/config/.mvn/wrapper/maven-wrapper.properties b/config/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/config/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/config/pom.xml b/config/pom.xml new file mode 100644 index 0000000..fa8fdc0 --- /dev/null +++ b/config/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + config + 2.0 + config + Demo project for Spring Boot + + + 1.8 + 3.0.2 + + + + + org.springframework.boot + spring-boot-starter + provided + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.projectlombok + lombok + true + + + + cn.hutool + hutool-all + + + + + + + + com.google.guava + guava + + + + + + + src/main/java + + **/*.xml + + + true + + + src/main/resources + + + + + **/*.yml + logback-spring.xml + + true + + + + + + + + + + + + + + + + + + + + diff --git a/config/src/main/java/com/centricsoftware/config/config/ExecutorConfig.java b/config/src/main/java/com/centricsoftware/config/config/ExecutorConfig.java new file mode 100644 index 0000000..476e4c4 --- /dev/null +++ b/config/src/main/java/com/centricsoftware/config/config/ExecutorConfig.java @@ -0,0 +1,48 @@ +package com.centricsoftware.config.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +@EnableAsync +public class ExecutorConfig { + @Value("${pool.executor.corePoolSize:8}") + private int corePoolSize; + @Value("${pool.executor.maxPoolSize:8}") + private int maxPoolSize; + @Value("${pool.executor.queueCapacity:1000}") + private int queueCapacity; + @Value("${pool.executor.keepAliveTime:300}") + private int keepAliveTime; + //线程池命名 + public static final String THREAD_POOL_NAME = "taskExecutorI"; + + @Bean(THREAD_POOL_NAME) + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + //配置核心线程数 + executor.setCorePoolSize(corePoolSize); + //配置最大线程数 + executor.setMaxPoolSize(maxPoolSize); + //配置队列大小 + executor.setQueueCapacity(queueCapacity); + //配置线程池中的线程的名称前缀 + executor.setThreadNamePrefix("thread-"); + //配置线程空闲后的最大存活时间 + executor.setKeepAliveSeconds(keepAliveTime); + // rejection-policy:当pool已经达到max size的时候,如何处理新任务 + // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 等待所有任务结束后再关闭线程池 + executor.setWaitForTasksToCompleteOnShutdown(true); + //执行初始化 + executor.initialize(); + return executor; + } +} diff --git a/config/src/main/java/com/centricsoftware/config/cons/Constants.java b/config/src/main/java/com/centricsoftware/config/cons/Constants.java new file mode 100644 index 0000000..00929ea --- /dev/null +++ b/config/src/main/java/com/centricsoftware/config/cons/Constants.java @@ -0,0 +1,151 @@ +package com.centricsoftware.config.cons; + + +/** + * 系统内相关常量,公共常量放这里,使用接口 + * @author zheng.gong + * @date 2020/4/17 + */ +public interface Constants { + + /** + * 分页最大单次限制 + */ + public static final int PAGE_MAX_LIMIT = 500; + + /** + * 表名 + * @author zheng.gong + * @date 2020/4/17 + */ + interface PlmTable { + String PLM_SAMPLE="plm_sample"; + } + + interface Bool{ + String TRUE="true"; + String FALSE="false"; + } + + interface SysStr{ + String SESSION_EXPIRE_ERROR_MSG="The client session has expired or is invalid"; + String SECRET="pvKMVgcoYEtwnmqmWhJmaA=="; + String AUTH_CONTENT = "ICICLE_INTERFACE_AUTH"; + String AUTH_ENCODE = "beErF6b+D/CSC7k0TgVRNw=="; + } + + + + /** + * 标点符号 + * @author zheng.gong + * @date 2020/4/17 + */ + interface SysConts{ + /** + * 点号 + */ + String SYMBOL_DOT = "."; + + /** + * 星号 + */ + String SYMBOL_STAR = "*"; + + /** + * 邮箱符号 + */ + String SYMBOL_EMAIL = "@"; + + + + } + + /** + * http请求头 + * @author zheng.gong + * @date 2020/4/17 + */ + interface HttpHeadEncodingType{ + String DEFAULT_CHARSET = "UTF-8"; + + String CONTENT_TYPE_JSON="application/json;charset=UTF-8"; + + String CONTENT_TYPE_XML="text/xml;charset=UTF-8"; + + String CONTENT_TYPE_HTML="text/html;charset=utf-8"; + + String CONTENT_TYPE_FORM="application/x-www-form-urlencoded"; + } + + /** + * redis相关常量 + */ + interface Redis{ + /** + * 默认配置刷新时间 7天 + */ + Integer DEFUALT_PROPERTIES_RELOAD = 604800; + + Integer C8INTERFACE_EXPIRE_TIME=3000; + + } + + /** + * rabbit相关常量 + * 此类为service使用配置 + */ + interface RabbitConsts { + /** + * 直接模式1 + */ + String DIRECT_MODE_QUEUE_ONE = "queue.direct.1"; + + /** + * 队列2 + */ + String QUEUE_TWO = "queue.2"; + + /** + * 队列3 + */ + String QUEUE_THREE = "3.queue"; + + /** + * 分列模式 + */ + String FANOUT_MODE_QUEUE = "fanout.mode"; + + /** + * 主题模式 + */ + String TOPIC_MODE_QUEUE = "topic.mode"; + + /** + * 路由1 + */ + String TOPIC_ROUTING_KEY_ONE = "queue.#"; + + /** + * 路由2 + */ + String TOPIC_ROUTING_KEY_TWO = "*.queue"; + + /** + * 路由3 + */ + String TOPIC_ROUTING_KEY_THREE = "3.queue"; + + /** + * 延迟队列 + */ + String DELAY_QUEUE = "delay.queue"; + + /** + * 延迟队列交换器 + */ + String DELAY_MODE_QUEUE = "delay.mode"; + } + + +} diff --git a/config/src/main/java/com/centricsoftware/config/entity/CenterProperties.java b/config/src/main/java/com/centricsoftware/config/entity/CenterProperties.java new file mode 100644 index 0000000..85d7c85 --- /dev/null +++ b/config/src/main/java/com/centricsoftware/config/entity/CenterProperties.java @@ -0,0 +1,36 @@ +package com.centricsoftware.config.entity; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; + +import java.sql.Struct; +import java.util.Map; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Component +@ConfigurationProperties(prefix = "cs.center") +public class CenterProperties { + Map prop; + + public String getValue(String key, String defValue){ + String value = this.getProp().get(key); + if(StrUtil.isBlank(value)&&StrUtil.isNotBlank(defValue)){ + return defValue; + }else{ + return value; + } + } + + public String getValue(String key){ + return this.getProp().get(key); + } +} diff --git a/config/src/main/java/com/centricsoftware/config/entity/CsProperties.java b/config/src/main/java/com/centricsoftware/config/entity/CsProperties.java new file mode 100644 index 0000000..23c2ed3 --- /dev/null +++ b/config/src/main/java/com/centricsoftware/config/entity/CsProperties.java @@ -0,0 +1,60 @@ +package com.centricsoftware.config.entity; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.Map; +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Component +@ConfigurationProperties(prefix = "cs") +public class CsProperties { + Map plm; + + private Environment env; + + @Autowired + public CsProperties(Environment env) { + this.env = env; + } + + public String getValue(String key, String defValue){ + String value = this.getPlm().get(key); + if(StrUtil.isBlank(value)&&StrUtil.isNotBlank(defValue)){ + return defValue; + }else{ +// if(StrUtil.equals(env.getProperty("password.encrypt"),"true")){ +// // 密钥 +// byte[] encryptkey = Base64.decode(Constants.SysStr.SECRET); +// AES aes = SecureUtil.aes(encryptkey); +// // 解密 +// return aes.decryptStr(value, CharsetUtil.CHARSET_UTF_8); +// } + return value; + } + } + + public String getValue(String key){ + return this.getPlm().get(key); + } + public String getProperty(String key){ + return this.getEnv().getProperty(key); + } + public String getProperty(String key, String defValue){ + String value = this.getEnv().getProperty(key); + if(StrUtil.isBlank(value)) { + value = defValue; + } + return value; + } + +} diff --git a/config/src/main/java/com/centricsoftware/config/entity/EsProperties.java b/config/src/main/java/com/centricsoftware/config/entity/EsProperties.java new file mode 100644 index 0000000..e37d2ed --- /dev/null +++ b/config/src/main/java/com/centricsoftware/config/entity/EsProperties.java @@ -0,0 +1,27 @@ +package com.centricsoftware.config.entity; + +import lombok.*; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "es") +@Data +public class EsProperties { + + private boolean enable; + + private String brand; + + private String scheme = "http"; + /** + * ip:port,以逗号分隔 + */ + private String host; + + private String user; + + private String password; + /** + * 日志索引名 + */ + private String logIndex; +} \ No newline at end of file diff --git a/config/src/main/resources/application-common.yml b/config/src/main/resources/application-common.yml new file mode 100644 index 0000000..6e8e5e9 --- /dev/null +++ b/config/src/main/resources/application-common.yml @@ -0,0 +1,17 @@ +#######################公共配置################### +cs.plm.common: + log: + feign: + #feign日志中,响应报文中标记请求处理状态的字段,支持getByPath + success-field: code + #feign日志中,响应报文中标记请求处理成功的值;可以通过feign的c8_code + success-value: 0,200 + #feign日志中,请求报文中标记主键的字段,支持getByPath + key-field: url,id,URL,ID + +cs.plm: + #是否记录日志,此配置用于开启ES和DB的日志记录方式 + feign-log: true + #用于配置日志的存储方式 + log-save-type: db # db or es + diff --git a/config/src/main/resources/application-es.yml b/config/src/main/resources/application-es.yml new file mode 100644 index 0000000..d0e5eef --- /dev/null +++ b/config/src/main/resources/application-es.yml @@ -0,0 +1,38 @@ +###这个yml文件存放es相关配置(无业务) + +################################开发环境################################ +spring: + profiles: dev + +es: + enable: true + brand: PLM + host: 127.0.0.1:9200 + user: elastic + password: c8admin + logIndex: plm-log-stream + +--- +################################测试环境################################ +spring: + profiles: test + +es: + enable: true + brand: PLM + host: 127.0.0.1:9200 + user: elastic + password: c8admin + +--- +################################正式环境################################ +spring: + profiles: prod + +es: + enable: true + brand: PLM + host: 127.0.0.1:9200 + user: elastic + password: c8admin + diff --git a/config/src/main/resources/application-import.yml b/config/src/main/resources/application-import.yml new file mode 100644 index 0000000..7788333 --- /dev/null +++ b/config/src/main/resources/application-import.yml @@ -0,0 +1,31 @@ +#######################导入配置################### +cs.excel.upload: + ##69码导入 + SKUImport: + nodeType: SKU + create: false + searchXml: + seadEmail: false + tips: "系统中找不到对应的SKU名称:{C8_SKU_URL}
" + isSingleSave: true + columns: + - attributeId: "C8_SKU_URL" + colName: SKU名称 + update: false + required: true + tips: "不存在SKU信息
" + - attributeId: "C8_SKU_69Code" + colName: 69码 + required: true + valid: ^\d{13}$ + tips: "SKU名称为 {C8_SKU_URL} 的69码不是13位纯数字!
" + continueTrans: true + - attributeId: "C8_SKU_69Code" + colName: 69码 + required: true + valid: + validResultRequired: false + update: false + path: "Child:xx" + tips: "SKU名称为 {C8_SKU_URL} 的69码不唯一!
" + type: ref diff --git a/config/src/main/resources/application-integration.yml b/config/src/main/resources/application-integration.yml new file mode 100644 index 0000000..45c4f83 --- /dev/null +++ b/config/src/main/resources/application-integration.yml @@ -0,0 +1,63 @@ +#######################第三方企业APP配置################### +################################开发环境################################ +spring: + profiles: dev + +#企业微信 +cs: + wechat: + url: https://qyapi.weixin.qq.com/cgi-bin + agentid: 1000999 + appkey: ww0215d6203xxxxx + secret: FughJHJeTAgdF426IGRvEfeyzgGG1s6XXXXXXX + flybook: + baseUrl: https://open.feishu.cn/open-apis/ + appid: cli_a19177fb9178500d + secret: uppAX8hRAS73JafIRCFHu7vUssAnynMB + dingtalk: + appkey: + secret: + agentid: + + +--- +################################测试环境################################ +spring: + profiles: test + + +#企业微信 +cs: + wechat: + url: https://qyapi.weixin.qq.com/cgi-bin + agentid: 1000999 + appkey: ww0215d6203xxxxx + secret: FughJHJeTAgdF426IGRvEfeyzgGG1s6XXXXXXX + flybook: + baseUrl: https://open.feishu.cn/open-apis/ + appid: cli_a19177fb9178500d + secret: uppAX8hRAS73JafIRCFHu7vUssAnynMB + dingtalk: + appkey: + secret: + agentid: +--- +################################正式环境################################ +spring: + profiles: prod + +#企业微信 +cs: + wechat: + url: https://qyapi.weixin.qq.com/cgi-bin + agentid: 1000999 + appkey: ww0215d6203xxxxx + secret: FughJHJeTAgdF426IGRvEfeyzgGG1s6XXXXXXX + flybook: + baseUrl: https://open.feishu.cn/open-apis/ + appid: cli_a19177fb9178500d + secret: uppAX8hRAS73JafIRCFHu7vUssAnynMB + dingtalk: + appkey: + secret: + agentid: \ No newline at end of file diff --git a/config/src/main/resources/application-mybatis.yml b/config/src/main/resources/application-mybatis.yml new file mode 100644 index 0000000..87864b3 --- /dev/null +++ b/config/src/main/resources/application-mybatis.yml @@ -0,0 +1,60 @@ +##################数据库配置############## +##############开发环境#################### +spring: + profiles: dev + #数据库配置 + datasource: + dynamic: + lazy: true + primary: c8 + datasource: + c8: + url: jdbc:sqlserver://120.79.18.201:1433;DatabaseName=C8 + username: csidba + password: csidba + hse: + url: jdbc:postgresql://120.79.18.201:25432/exportdb + username: CSIDBA + password: CSIDBA + +--- +##############测试环境#################### +spring: + profiles: test + #数据库配置 + datasource: + dynamic: + lazy: true + primary: c8 + datasource: + c8: + url: jdbc:sqlserver://120.79.18.201:1433;DatabaseName=C8 + username: csidba + password: csidba + hse: + url: jdbc:postgresql://120.79.18.201:25432/exportdb + username: CSIDBA + password: CSIDBA + + +--- +##############正式环境#################### +spring: + profiles: prod + #数据库配置 + datasource: + dynamic: + lazy: true + primary: c8 + datasource: + c8: + url: jdbc:sqlserver://120.79.18.201:1433;DatabaseName=C8 + username: csidba + password: csidba + hse: + url: jdbc:postgresql://120.79.18.201:25432/exportdb + username: CSIDBA + password: CSIDBA + + + diff --git a/config/src/main/resources/application-plm.yml b/config/src/main/resources/application-plm.yml new file mode 100644 index 0000000..6090545 --- /dev/null +++ b/config/src/main/resources/application-plm.yml @@ -0,0 +1,118 @@ +#######################Centric8服务器地址################### +spring: + profiles: dev +cs: + plm: +# http: http://120.79.18.201:80 + http: http://119.23.55.145:80 + #是否自动登陆,默认自动登陆,只有当值不为true或者不为空时不自动登陆 + auto-login: true + enum-display-cache: true + # 是否记录Feign日志 + feign-log: false + #######Centric8服务器登陆用户名/密码 + ###密码加密代码在com.centricsoftware.core.JavaTest中 + user: Administrator + #c8admin + # pwd: centric(bvXcDZBCR1rgv6yr5T8Kn7KnKd4/C4d3) + pwd: c8admin + dbhost: http://119.23.55.145:1433 + dbuser: csidba + dbpwd: csidba + dbname: C8 + # dbtype: Oracle + mail: + host: testmailhost + sender: administrator + senderName: administrator + senderPwd: 123456 + + hse: + host: localhost + portNumber: 9200 + proto: http +#定时任务配置 +task: + #晚上12点,中午12点执行登陆 + cron: 0 0 0,12 * * ? + test: 0/5 * * * * ? +--- +spring: + profiles: test +cs: + plm: + http: http://119.23.55.145.32:80 + #是否自动登陆,默认自动登陆,只有当值不为true或者不为空时不自动登陆 + auto-login: true + enum-display-cache: true + # 是否记录Feign日志 + feign-log: false + #######Centric8服务器登陆用户名/密码 + ###密码加密代码在com.centricsoftware.core.JavaTest中 + user: Administrator + #c8admin + # pwd: centric(bvXcDZBCR1rgv6yr5T8Kn7KnKd4/C4d3) + pwd: c8admin + dbhost: http://119.23.55.145.32:1433 + dbuser: csidba + dbpwd: csidba + dbname: C8 + # dbtype: Oracle + mail: + host: testmailhost + sender: administrator + senderName: administrator + senderPwd: 123456 + hse: + host: localhost + portNumber: 9200 + proto: http +#定时任务配置 +task: + #晚上12点,中午12点执行登陆 + cron: 0 0 0,12 * * ? + test: 0/5 * * * * ? +--- +spring: + profiles: prod +cs: + plm: + http: http://119.23.55.145.32:80 + #是否自动登陆,默认自动登陆,只有当值不为true或者不为空时不自动登陆 + auto-login: true + enum-display-cache: false + # 是否记录Feign日志 + feign-log: false + #######Centric8服务器登陆用户名/密码 + ###密码加密代码在com.centricsoftware.core.JavaTest中 + user: Administrator + #c8admin +# pwd: centric(bvXcDZBCR1rgv6yr5T8Kn7KnKd4/C4d3) + pwd: c8admin + dbhost: http://119.23.55.145.32:1433 + dbuser: csidba + #csidba + dbpwd: csidba + dbname: C8 + # dbtype: Oracle + ##数据库链接 用于DBUtil + pq: + dbuser: csidba + dbpwd: csidba + dbtype: Oracle + dbhost: localhost + dbname: C8REPORT + mail: + host: testmailhost + sender: administrator + senderName: administrator + senderPwd: 123456 + + hse: + host: localhost + portNumber: 9200 + proto: http +#定时任务配置 +task: + #晚上12点,中午12点执行登陆 + cron: 0 0 0,12 * * ? diff --git a/config/src/main/resources/application-pro.yml b/config/src/main/resources/application-pro.yml new file mode 100644 index 0000000..7f1e3f1 --- /dev/null +++ b/config/src/main/resources/application-pro.yml @@ -0,0 +1,94 @@ +###这个yml文件存放项目环境相关配置(无业务) + +################################开发环境################################ +spring: + profiles: dev + mvc: + view: + prefix: / + suffix: .jsp + thymeleaf: + enabled: false + cache: false + check-template-location: false +web: + port: 8088 + logging: + path: D:/plmservice/logs + auth: + username: c8admin + password: c8admin +inter: + fileupload: + msg: + dingding: + http: + systemCode: + token: + bds: + urlPrefix: + signKey: + platformNo: + version: + http: + + +#定时任务配置 +task: + #每周日上午3点刷新缓存 + cron: 0 0 3 ? * SUN +##加密配置 + + +--- +################################测试环境################################ +spring: + profiles: test +web: + port: 8088 + logging: + path: D:/plmservice/logs +inter: + fileupload: + msg: + dingding: + http: + systemCode: + token: + bds: + urlPrefix: + signKey: + platformNo: + version: + http: + +#定时任务配置 +task: + #每周日上午3点刷新缓存 + cron: 0 0 3 ? * SUN +--- +################################正式环境################################ +spring: + profiles: prod +web: + port: 8088 + logging: + path: C:/plmservice/logs +inter: + fileupload: + msg: + dingding: + http: + systemCode: + token: + bds: + urlPrefix: + signKey: + platformNo: + version: + http: + +#定时任务配置 +task: + #每周日上午3点刷新缓存 + cron: 0 0 3 ? * SUN diff --git a/config/src/main/resources/application-rabbitmq.yml b/config/src/main/resources/application-rabbitmq.yml new file mode 100644 index 0000000..51877b8 --- /dev/null +++ b/config/src/main/resources/application-rabbitmq.yml @@ -0,0 +1,38 @@ +###这个yml文件存放rabbitmq相关配置(无业务) + +################################开发环境################################ +spring: + profiles: dev + + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: guest + password: guest + virtual-host: / + +--- +################################测试环境################################ +spring: + profiles: test + + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: guest + password: guest + virtual-host: / +--- +################################正试环境################################ +spring: + profiles: prod + + rabbitmq: + host: 127.0.0.1 + port: 5672 + username: guest + password: guest + virtual-host: / + + + diff --git a/config/src/main/resources/application-redis.yml b/config/src/main/resources/application-redis.yml new file mode 100644 index 0000000..fda45ac --- /dev/null +++ b/config/src/main/resources/application-redis.yml @@ -0,0 +1,73 @@ +###这个yml文件存放redis相关配置(无业务) + +################################开发环境################################ +spring: + profiles: dev + redis: + host: localhost + # 连接超时时间(记得添加单位,Duration) + timeout: 10000ms + # Redis默认情况下有16个分片,这里配置具体使用的分片 + # database: 0 + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 8 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: -1ms + # 连接池中的最大空闲连接 默认 8 + max-idle: 8 + # 连接池中的最小空闲连接 默认 0 + min-idle: 0 + cache: + # 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配 + type: redis + +--- +################################测试环境################################ +spring: + profiles: test + redis: + host: localhost + # 连接超时时间(记得添加单位,Duration) + timeout: 10000ms + # Redis默认情况下有16个分片,这里配置具体使用的分片 + # database: 0 + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 8 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: -1ms + # 连接池中的最大空闲连接 默认 8 + max-idle: 8 + # 连接池中的最小空闲连接 默认 0 + min-idle: 0 + cache: + # 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配 + type: redis + +--- +################################正式环境################################ +spring: + profiles: prod + redis: + host: localhost + # 连接超时时间(记得添加单位,Duration) + timeout: 10000ms + # Redis默认情况下有16个分片,这里配置具体使用的分片 + # database: 0 + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 8 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: -1ms + # 连接池中的最大空闲连接 默认 8 + max-idle: 8 + # 连接池中的最小空闲连接 默认 0 + min-idle: 0 + cache: + # 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配 + type: redis + diff --git a/config/src/main/resources/logback-spring.xml b/config/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..ea27887 --- /dev/null +++ b/config/src/main/resources/logback-spring.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + ${LOG_PATTERN} + utf-8 + + + + + true + + ${LOG_PATTERN} + utf-8 + + ${FILE_NAME} + + ${FILE_NAME_PATTERN} + + 50MB + + + 7 + + + + + + true + + ${LOG_PATTERN} + utf-8 + + ${TASK_FILE_NAME} + + ${TASK_LOG_FILE_NAME_PATTERN} + + 50 MB + + + 30 + + + debug + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/core/.mvn/wrapper/MavenWrapperDownloader.java b/core/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/core/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/core/.mvn/wrapper/maven-wrapper.jar b/core/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/core/.mvn/wrapper/maven-wrapper.jar differ diff --git a/core/.mvn/wrapper/maven-wrapper.properties b/core/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/core/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..2e98bd9 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,202 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + core + 2.0 + + + core + Demo project for Spring Boot Dev 2 + + + 1.8 + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + org.projectlombok + lombok + true + + + + + + + + + com.centricsoftware + config + + + com.centricsoftware + commons + + + com.centricsoftware + enhancement + + + com.centricsoftware + sso + + + + + + + + + + + + + + + + + + + + + + + + + + plmservice + + + org.springframework.boot + spring-boot-maven-plugin + 2.5.1 + + + org.springframework.boot.experimental + spring-boot-thin-layout + 1.0.27.RELEASE + + + + + org.springframework.boot.experimental + spring-boot-thin-maven-plugin + 1.0.27.RELEASE + + + + resolve + + resolve + + false + + + + + + + src/main/resources + + **/*.properties + **/*.xml + **/*.yml + **/*.tld + **/*.doc + + true + + + src/main/resources + + **/*.xlsx + **/*.xls + banner.txt + + + **/*.properties + **/*.xml + **/*.tld + **/*.doc + + false + + + src/main/webapp + META-INF/resources + + + + + diff --git a/core/src/main/java/com/centricsoftware/core/CoreApplication.java b/core/src/main/java/com/centricsoftware/core/CoreApplication.java new file mode 100644 index 0000000..ff79f38 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/CoreApplication.java @@ -0,0 +1,35 @@ +package com.centricsoftware.core; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +/** + * 代码入口 + * @author zheng.gong + * @date 2020/4/21 + */ +@ComponentScan("com.centricsoftware") +@EnableScheduling +@SpringBootApplication +@EnableFeignClients(basePackages = "com.centricsoftware.*") +@EnableWebMvc +@EnableAsync +@Import(cn.hutool.extra.spring.SpringUtil.class) +public class CoreApplication extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(CoreApplication.class); + } + public static void main(String[] args) { + SpringApplication.run(CoreApplication.class, args); + } + +} diff --git a/core/src/main/java/com/centricsoftware/core/aspect/AopAspect.java b/core/src/main/java/com/centricsoftware/core/aspect/AopAspect.java new file mode 100644 index 0000000..309e892 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/aspect/AopAspect.java @@ -0,0 +1,203 @@ +package com.centricsoftware.core.aspect; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.ant.ControllerLog; +import com.centricsoftware.commons.utils.IpUtil; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.modules.c8.service.es.LogElasticsearchService; +import com.centricsoftware.enhancement.service.log.AsyncLogService; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import lombok.extern.slf4j.Slf4j; +import okhttp3.RequestBody; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * AopAspect对controller和service类或者带有自定义注解的类增强减少log代码冗余 + * @author ZhengGong + * @date 2020/4/16 + */ +@Aspect +@Component +@Slf4j +public class AopAspect extends BaseAspect { + + @Resource + private LogElasticsearchService logElasticsearchService; + + @Resource + private AsyncLogService asyncLogService; + + @Resource + private LogServiceImpl logService; + + private ThreadLocal logThreadLocal = new ThreadLocal<>(); + + @Pointcut(value = "(execution(* com.centricsoftware.core.controller..*(..))||@annotation(com.centricsoftware.commons.ant.ControllerLog))") + public void controllerAspect(){} + + @Pointcut("execution(* com.centricsoftware.enhancement.modules.c8.feign.*.*(..))||@annotation(com.centricsoftware.commons.ant.ServiceLog)") + public void serviceAspcet(){} + /** + * 对Controller环绕增强 + * @param joinPoint + * @return + * @throws Throwable + */ + @Around("controllerAspect()") + public Object controllerAround(ProceedingJoinPoint joinPoint) throws Throwable { + log.info("---------------------Controller方法调用开始-----------------------"); + // getControllerArgsDescription(joinPoint); + //是否记录日志 + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + Instant t1 = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)); + Object proceed; + if(StrUtil.equals(csProperties.getValue("feign-log"), Constants.Bool.TRUE, true)){ + proceed = around(joinPoint, log); + }else{ + proceed = joinPoint.proceed(); + } + Instant t2 = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)); + Duration between = Duration.between(t1, t2); + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + log.warn("{}执行开始时间:{},结束时间:{},耗时{}ms",methodSignature.getMethod(),t1,t2,between.toMillis()); + log.debug("方法{}执行成功,返回结果{}",methodSignature.getMethod().getName(),proceed); + log.info("---------------------Controller方法调用结束-----------------------"); + return proceed; + } + + /** + * 对Service环绕增强 + * @param joinPoint + * @return + * @throws Throwable + */ + @Around("serviceAspcet()") + public Object serviceAround(ProceedingJoinPoint joinPoint) throws Throwable { + Object proceed = joinPoint.proceed(); + log.debug("---------------------Service方法调用 开始-----------------------"); + getServiceArgsDescription(joinPoint); + Instant t1 = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)); + Instant t2 = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)); + Duration between = Duration.between(t1, t2); + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + log.debug("{}执行开始时间:{},结束时间:{},耗时{}ms",methodSignature.getMethod(),t1,t2,between.toMillis()); + log.debug("方法{}执行成功,返回结果{}",methodSignature.getMethod(),proceed); + log.debug("---------------------Service方法调用 结束-----------------------"); + return proceed; + } + + public static void getServiceArgsDescription(JoinPoint joinPoint){ + //1.获取到所有的参数值的数组 + Object[] args = joinPoint.getArgs(); + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + //2.获取到方法的所有参数名称的字符串数组 + String[] parameterNames = methodSignature.getParameterNames(); + log.debug("---------------参数列表---------------------"); + for (int i =0 ,len=parameterNames.length;i < len ;i++){ + log.debug("参数名:{},参数值:{}",parameterNames[i],args[i]); + + } + } + // ================日志采集======================= + @Override + protected void preHandle(ProceedingJoinPoint jp, Object reqArgs) { + MethodSignature ms = (MethodSignature) jp.getSignature(); + ControllerLog controllerLog = ms.getMethod().getAnnotation(ControllerLog.class); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + PlmLog plmLog = buildPlmLog(controllerLog, request, reqArgs); + logThreadLocal.set(plmLog); + } + + @Override + protected void finallyHandle(MethodSignature ms, Object result,ControllerLog controllerLog) { + PlmLog plmLog = logThreadLocal.get(); + // 没有注解就不记录接口日志 + if (plmLog != null) { + logElasticsearchService.buildUpdatePlmLog(plmLog, result); + } + saveLog(plmLog,controllerLog);// 最后做日志存储 + logThreadLocal.remove(); + } + + private void saveLog(PlmLog plmLog, ControllerLog controllerLog) { + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + String value = csProperties.getValue("log-save-type"); + if(StrUtil.equals(value, "es", true)){ + logElasticsearchService.saveLog(plmLog); + } + if(StrUtil.equals(value, "db", true)){ + saveLogInDB(plmLog,controllerLog); + } + } + + private void saveLogInDB(PlmLog plmLog,ControllerLog controllerLog) { + FeignLogDto convert = Convert.convert(FeignLogDto.class, plmLog); + if(controllerLog.interfaceLog()){ + convert.setLogType("接口日志"); + }else{ + convert.setLogType("操作日志"); + } + convert.setUrl(plmLog.getPath()); + convert.setReturnMsg(plmLog.getReturnText()); + convert.setRequestId(controllerLog.requestId()); + convert.setResponseId(controllerLog.responseId()); + logService.saveLog(convert); + } + + private PlmLog buildPlmLog(ControllerLog controllerLog, HttpServletRequest request, Object param) { + String requestBody = getRequestBody(request); + String desc = Optional.ofNullable(controllerLog).map(ControllerLog::value).orElse(StringUtils.EMPTY); + PlmLog plmLog = logElasticsearchService.newLog(request.getRequestURI(), IpUtil.getIpAddr(request), param, desc); + plmLog.setDebug(requestBody); + if (request instanceof ContentCachingRequestWrapper) { + ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request; + String body = new String(wrappedRequest.getContentAsByteArray()); + plmLog.setDebug(body); + } + return plmLog; + } + + private String getRequestBody(HttpServletRequest request) { + StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line); + } + }catch (Exception e){ + log.error("获取请求体失败",e); + } + return stringBuilder.toString(); + } + +} diff --git a/core/src/main/java/com/centricsoftware/core/aspect/BaseAspect.java b/core/src/main/java/com/centricsoftware/core/aspect/BaseAspect.java new file mode 100644 index 0000000..8816e92 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/aspect/BaseAspect.java @@ -0,0 +1,203 @@ +package com.centricsoftware.core.aspect; + +import com.centricsoftware.commons.ant.ControllerLog; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.utils.JsonUtil; +import com.google.common.base.Stopwatch; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.omg.CORBA.SystemException; +import org.slf4j.Logger; +import org.springframework.ui.Model; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * 基础环绕切面处理方法 + * @author liaochangjiang + * @since 2024-05-07 11:59 + */ +public class BaseAspect { + + public Object around(ProceedingJoinPoint jp, Logger log) { + Stopwatch stopwatch = Stopwatch.createStarted(); + MethodSignature ms = (MethodSignature) jp.getSignature(); + ControllerLog controllerLog = ms.getMethod().getAnnotation(ControllerLog.class); + boolean logResp = !Objects.isNull(controllerLog) && controllerLog.response(); + final String signatureName = getSignatureName(controllerLog, ms); + Object result = null; + try { + Object reqArgs = getRequestArgs(jp, ms); + String argsJson = StringUtils.EMPTY; + try { + argsJson = JsonUtil.toJSONString(reqArgs, new String[]{}); + log.info("{} 请求参数: {}", signatureName, argsJson); + } catch (Exception e) { + log.warn("序列化请求参数失败", e); + } + + preHandle(jp, argsJson); + result = jp.proceed(); + if (!logResp) { + log.info("{} 耗时: {}", signatureName, stopwatch); + } + return result; + } catch (Throwable e) { + String logPrefix = "系统"; + String errorLogStr = String.format("%s异常 %s", logPrefix, signatureName); + errorLogStr += " 耗时:" + stopwatch; + if (e instanceof SystemException) { + //真实日志信息 + String logText = e.getLocalizedMessage(); + String message = e.getMessage(); + if (StringUtils.isNotEmpty(logText)) { + message = logText; + } + errorLogStr += " logText:" + message; + } + log.error(errorLogStr, e); + result = handleResponse(ms, e, log); + } finally { + stopwatch.stop(); + if (logResp) { + log.info("{} 耗时: {} 返回: {}", signatureName, stopwatch, JsonUtil.toJSONString(result)); + } + finallyHandle(ms, result,controllerLog); + } + return result; + } + + /** + * 处理前调用 + * + * @author liaochangjiang + * @since 2021-09-26 19:12:55 + */ + protected void preHandle(ProceedingJoinPoint jp, Object argsJson) { + + } + + /** + * finally处理时调用 + * + * @author liaochangjiang + * @since 2021-12-27 18:59:08 + */ + protected void finallyHandle(MethodSignature ms, Object result,ControllerLog controllerLog) { + + } + + /** + * 获取请求参数 + * + * @author liaochangjiang + * @since 2022-02-25 13:10:32 + */ + private Object getRequestArgs(ProceedingJoinPoint jp, MethodSignature ms) { + Object reqArgs = new Object(); + if (ArrayUtils.getLength(jp.getArgs()) >= 1 && (jp.getArgs()[0] instanceof ResEntity)) { + reqArgs = jp.getArgs()[0]; + } else { + String name = ms.getMethod().getDeclaringClass().getName(); + if (name.endsWith("Controller") || name.endsWith("C8Feign")) { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + return reqArgs; + } + HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); + String contentType = request.getContentType(); + if (org.apache.commons.lang3.StringUtils.isNotBlank(contentType) && contentType.contains("form-")) { + Map parameterMap = request.getParameterMap(); + if (!parameterMap.isEmpty()) { + return parameterMap; + } + } + return request.getParameterMap(); +// Object firstNormalArgs = getFirstNormalArgs(jp); +// if (firstNormalArgs == null) { +// return request.getParameterMap(); +// } +// reqArgs = firstNormalArgs; + } + } + return reqArgs; + } + + /** + * 获取第一个正常参数 + * + * @author liaochangjiang + * @since 2022-05-07 09:20:32 + */ + private Object getFirstNormalArgs(ProceedingJoinPoint jp) { + if (jp.getArgs().length == 0) { + return null; + } + Object[] args = jp.getArgs(); + for (Object arg : args) { + if (!(arg instanceof ServletRequest) && !(arg instanceof ServletResponse) && !(arg instanceof HttpSession) + && !(arg instanceof MultipartFile) && !(arg instanceof Model)) { + return arg; + } + } + return null; + } + + /** + * 处理异常响应 + * + * @author liaochangjiang + * @since 2021-09-26 19:03:29 + */ + @SuppressWarnings("rawtypes") + protected Object handleResponse(MethodSignature ms, Throwable e,Logger log) { + Class returnType = ms.getReturnType(); + if (returnType == Void.TYPE) { + Optional.ofNullable(RequestContextHolder.getRequestAttributes()).map(t -> (ServletRequestAttributes) t) + .map(ServletRequestAttributes::getResponse) + .ifPresent(resp -> writeResponse(resp,WebResponse.failure(e.getMessage()),log)); + return null; + } + try { + return returnType.newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static void writeResponse(HttpServletResponse response, ResEntity resp, Logger log) { + response.setContentType("application/json;charset=UTF-8"); + try { + response.getWriter().print(JsonUtil.toJSONString(resp)); + if (!resp.isSuccess()) { + response.sendError(resp.getCode(), resp.getMsg()); + } + } catch (IOException e) { + log.error("响应失败", e); + } + } + + protected String getSignatureName(ControllerLog controllerLog, MethodSignature ms) { + if (!Objects.isNull(controllerLog) && StringUtils.isNotBlank(controllerLog.value())) { + return controllerLog.value() + "|" + ms.toShortString(); + } + return ms.toShortString(); + } + +} diff --git a/core/src/main/java/com/centricsoftware/core/config/BootConfig.java b/core/src/main/java/com/centricsoftware/core/config/BootConfig.java new file mode 100644 index 0000000..af8f17e --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/config/BootConfig.java @@ -0,0 +1,77 @@ +package com.centricsoftware.core.config; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.utils.SpringUtil; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import com.centricsoftware.enhancement.modules.c8.service.C8LoginService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; + +import javax.annotation.PostConstruct; +import java.time.LocalDateTime; + +/** + * 包含一些启动后需要注册的信息 + * @author zheng.gong + * @date 2020/4/21 + */ +@Slf4j +@Configuration +public class BootConfig { + + + /** + * 按照标准时间来算,晚上12点,中午12点执行登陆 + */ + @Scheduled(cron = "${task.cron}") + public void job1() { + log.info("【重新登陆】"+LocalDateTime.now()); + } + + /** + * 启动后配置 + * @return LoadingConfig + */ + @Bean + public LoadingConfig cfg(){ + return new LoadingConfig(); + } + + /** + * 启动后的初始化操作 + * @author zheng.gong + * @date 2020/4/23 + */ + static class LoadingConfig{ + @Autowired + CsProperties csProperties; + /** + * 启动后自动登陆PLM + */ + @PostConstruct + public void init(){ + if(StrUtil.equals(csProperties.getValue("auto-login"), Constants.Bool.TRUE,true) || StrUtil.isBlank(csProperties.getValue( + "auto-login"))){ + log.debug("===========系统启动后登陆PLM============"); + C8LoginService loginService = SpringUtil.getBean(C8LoginService.class); + loginService.login(); + log.debug("=========登陆成功!========="); + log.debug("=========开始初始化缓存!========="); + EnumCache enumCache = SpringUtil.getBean(EnumCache.class); + boolean equals = StrUtil.equals(csProperties.getValue("enum-display-cache"), Constants.Bool.TRUE, true); + enumCache.init(equals); + log.debug("=========初始化缓存成功!========="); + + }else{ + log.debug("===========系统启动后不登陆PLM============"); + } + } + } + + +} diff --git a/core/src/main/java/com/centricsoftware/core/config/WebMvcConfig.java b/core/src/main/java/com/centricsoftware/core/config/WebMvcConfig.java new file mode 100644 index 0000000..f0f58ea --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/config/WebMvcConfig.java @@ -0,0 +1,54 @@ +package com.centricsoftware.core.config; + +import com.centricsoftware.enhancement.filter.AddCookieFilter; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; + +/** + * 跨域处理 + * @author zheng.gong + * @date 2020/4/16 + */ +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + private static final long MAX_AGE_SECS = 3600; + + @Resource + AddCookieFilter addCookieFilter; + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE") + .maxAge(MAX_AGE_SECS); + } + + + /** + * 注册拦截器 + * @param registry InterceptorRegistry + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(addCookieFilter) + .addPathPatterns("/**"); + } + + + /** + * 注册拦截器 + * @param registry InterceptorRegistry + */ +// @Override +// public void addInterceptors(InterceptorRegistry registry) { +// registry.addInterceptor(new SimpleAuthInterceptor()).addPathPatterns(interceptLIst()); +// } +// +// public List interceptLIst(){ +// return Lists.newArrayList("/test/testAuth"); +// } +} diff --git a/core/src/main/java/com/centricsoftware/core/controller/TestController.java b/core/src/main/java/com/centricsoftware/core/controller/TestController.java new file mode 100644 index 0000000..0db2f41 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/controller/TestController.java @@ -0,0 +1,193 @@ +package com.centricsoftware.core.controller; + +import cn.hutool.json.JSONUtil; +import cn.hutool.poi.excel.ExcelReader; +import cn.hutool.poi.excel.ExcelUtil; +import com.centricsoftware.commons.ant.ControllerLog; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.utils.ExportExcel; +import com.centricsoftware.commons.utils.IpUtil; +import com.centricsoftware.config.entity.CenterProperties; +import com.centricsoftware.core.dto.DataPackage; +import com.centricsoftware.core.service.CommonExportService; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +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.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * 测试控制类 + * @author zheng.gong + * @date 2020/4/16 + */ +@Slf4j +@RequestMapping("/test") +@Controller +public class TestController { + + @Value("${cs.plm.http}") + private String host; + + @Autowired + CenterProperties centerProperties; + + @Autowired + C8NodeService c8NodeService; + + @Resource + private CommonExportService commonExportService; + + /** + * jsp测试 + * + * @return + */ + @RequestMapping("/hello") + @ResponseBody + @ControllerLog("日志采集") + public ResEntity hello() { + // String name = c8NodeService.queryExpressionResult("`Node Name`", "centric://REFLECTION/INSTANCE/User/Administrator"); + return WebResponse.success("欢迎你:"+"; host:"+host); + } + + /** + * jsp测试 + * @return + */ + @RequestMapping("/testJsp") + public String testJsp(){ + return "index"; + } + + + @ResponseBody + @PostMapping("/testParam") + public ResEntity testDemo(@RequestBody Map map){ + log.debug("get json param:{}", JSONUtil.toJsonStr(map)); + Assert.isTrue(map.containsKey("test"),"不包含test"); + return WebResponse.success(ResCode.SUCCESS,map); + } + + @ResponseBody + @PostMapping("/testProp") + public ResEntity testCsProp(){ + Map plm = centerProperties.getProp(); + return WebResponse.success(ResCode.SUCCESS,plm); + } + + @ResponseBody + @PostMapping("testCookie") + public ResEntity testCookie() throws Exception { + + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @PostMapping("logout") + public ResEntity logout() throws Exception { + return WebResponse.success(ResCode.SUCCESS); + } + + + @RequestMapping("/testXML") + @ResponseBody + @ControllerLog("执行XML日志") + public ResEntity cookie() { + String xml = C8OperationNode.changeNode("C1402").changeAttribute("Code", "string", "10086").getXml(); + String url = c8NodeService.createURL(); + // String materialSecurityGroup1 = C8OperationNode.createNode(url, "MaterialSecurityGroup").getXml(); + c8NodeService.processNode(xml); + return WebResponse.success(xml); + } + + @Resource + EnumCache enumCache; + + @ResponseBody + @RequestMapping("testEnum") + public ResEntity testEnum() throws Exception { + String fullnameByDisplay = enumCache.getFullnameByDisplay("C8_Apeed", "选中"); + return WebResponse.success(ResCode.SUCCESS,fullnameByDisplay); + } + + @ResponseBody + @RequestMapping("/getIP") + public ResEntity getIp(HttpServletRequest request) throws Exception { + String ip = IpUtil.getIpAddr(request); + return WebResponse.success(ResCode.SUCCESS,ip); + } + @ResponseBody + @RequestMapping("/excel") + public ResEntity excel(MultipartFile file) throws Exception { + ExcelReader reader = ExcelUtil.getReader(file.getInputStream()); + List> maps = reader.readAll(); + System.out.println(maps.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + + /** + * Excel导出通用方式 + * @author liaochangjiang + * @since 2024-03-05 14:22 + */ + @PostMapping("exportExcel") + public void export1(HttpServletResponse response) throws Exception { + // final ExportExcel excel = commonExportService.export1(); + // excel.write(response, "区域块化导出.xlsx").dispose(); + // commonExportService.export2(response);// 固定模板填充占位 + // commonExportService.export5(response);// 固定模板填充占位 + // final ExportExcel excel = commonExportService.export3(response);// Launchmap实现 + // excel.write(response, "Launchmap实现.xlsx").dispose(); + // commonExportService.export4(response);// 动态table填充占位 + } + + @ResponseBody + @RequestMapping("/test") + @ControllerLog(value = "测试日志",interfaceLog = true) + public ResEntity testLog(String name,String url,HttpServletResponse response){ + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/test1") + @ControllerLog(value = "测试日志1",interfaceLog = true) + public ResEntity testLog1(@RequestBody FeignLogDto dto){ + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/test2") + public ResEntity test(){ + String xml = C8Search.newSearch("DataPackage") + .attr("C8_DP_Type", "EQ", "C8_DPType:1") + .attr("C8_DP_CloComState", "EQ", "true") + .attrRef("Category1", "EQ", "C1848462", "Child:__Parent__") + .getXml(); + DepPath depPath = DepPath.builderXml() + .xml(xml) + .build(); + List dataPackages = c8NodeService.depPathByEntity(depPath, DataPackage.class); + return WebResponse.success(ResCode.SUCCESS); + } + +} diff --git a/core/src/main/java/com/centricsoftware/core/dto/BaseDto.java b/core/src/main/java/com/centricsoftware/core/dto/BaseDto.java new file mode 100644 index 0000000..a24c7e8 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/dto/BaseDto.java @@ -0,0 +1,13 @@ +package com.centricsoftware.core.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class BaseDto implements Serializable { + @JsonIgnore + private String url; + +} diff --git a/core/src/main/java/com/centricsoftware/core/dto/CommonExportDto.java b/core/src/main/java/com/centricsoftware/core/dto/CommonExportDto.java new file mode 100644 index 0000000..b0fb123 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/dto/CommonExportDto.java @@ -0,0 +1,35 @@ +package com.centricsoftware.core.dto; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.util.ExportUtil.ExportTemplateRegion; +import lombok.Data; + +import java.io.InputStream; + +@Data +@ExportTemplateRegion(row1 = 0, col1 = 2, row2 = 11, col2 = 3, groups = "akaBuyTags") +@ExportTemplateRegion(row1 = 3, col1 = 1, row2 = 9, col2 = 1, groups = "launchmap") +public class CommonExportDto { + + private InputStream styleImageIn; + private InputStream styleImageIn1; + + private Integer index; + + // 名称 + @DepPathField(exp = "$Name") + private String name; + + // 编码 + @DepPathField(exp = "Code") + private String code; + + // 描述 + @DepPathField(exp = "Description") + private String description; + + // 图片 + @DepPathField(exp = "Images[].Viewable") + private String realImgUrl; +} diff --git a/core/src/main/java/com/centricsoftware/core/dto/DataPackage.java b/core/src/main/java/com/centricsoftware/core/dto/DataPackage.java new file mode 100644 index 0000000..6334173 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/dto/DataPackage.java @@ -0,0 +1,32 @@ +package com.centricsoftware.core.dto; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class DataPackage { + + /** + * 唯一id + */ + @DepPathField(exp = "$URL") + private String url; + @DepPathField(exp = "C8_DP_AcceptTechPack") + private boolean acceptTechPack; + @DepPathField(exp = "C8_DP_BOM", type = DepPathEntityType.DOUBLE) + private Double bom; + @DepPathField(exp = "C8_DP_DesignType") + private String designType; + @DepPathField(exp = "C8_DP_DesignType", type = DepPathEntityType.ENUM_DESC) + private String designTypeDesc; + @DepPathField(exp = "C8_DP_DesignType", type = DepPathEntityType.ENUM_VALUE) + private String designTypeValue; + @DepPathField(exp = "C8_DP_DesignActualDate", type = DepPathEntityType.DATA) + private Date designActualDate; + +} diff --git a/core/src/main/java/com/centricsoftware/core/handler/GlobalExceptionHandler.java b/core/src/main/java/com/centricsoftware/core/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..3666fee --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/handler/GlobalExceptionHandler.java @@ -0,0 +1,122 @@ +package com.centricsoftware.core.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import com.centricsoftware.commons.exception.ParamException; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.validation.ConstraintViolationException; + +/** + * 全局异常处理 + * @author ZhengGong + * @date 2019/5/24 + */ +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + @ExceptionHandler(value = NoHandlerFoundException.class) + public ResEntity handleNoHandlerFoundException(NoHandlerFoundException e) { + log.info("-------------------------进入全局异常捕获NoHandlerFoundException---------------------------"); + log.error("【全局异常拦截】NoHandlerFoundException: 请求方法 {}, 请求路径 {}", e.getRequestURL(), e.getHttpMethod()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.REQUEST_NOT_FOUND); + } + @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) + public ResEntity handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { + log.info("-------------------------进入全局异常捕获HttpRequestMethodNotSupportedException---------------------------"); + log.error("【全局异常拦截】HttpRequestMethodNotSupportedException: 当前请求方式 {}, 支持请求方式 {}", e.getMethod(), JSONUtil.toJsonStr(e.getSupportedHttpMethods())); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.HTTP_BAD_METHOD); + } + @ExceptionHandler(value = MethodArgumentNotValidException.class) + public ResEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.info("-------------------------进入全局异常捕获MethodArgumentNotValidException---------------------------"); + log.error("【全局异常拦截】MethodArgumentNotValidException", e); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.BAD_REQUEST.getCode(), e.getBindingResult() + .getAllErrors() + .get(0) + .getDefaultMessage(), false); + } + @ExceptionHandler(value = ConstraintViolationException.class) + public ResEntity handleConstraintViolationException(ConstraintViolationException e) { + log.info("-------------------------进入全局异常捕获ConstraintViolationException---------------------------"); + log.error("【全局异常拦截】ConstraintViolationException", e); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.BAD_REQUEST.getCode(), CollUtil.getFirst(e.getConstraintViolations()) + .getMessage(), null); + } + @ExceptionHandler(value = MethodArgumentTypeMismatchException.class) + public ResEntity handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + log.info("-------------------------进入全局异常捕获MethodArgumentTypeMismatchException---------------------------"); + log.error("【全局异常拦截】MethodArgumentTypeMismatchException: 参数名 {}, 异常信息 {}", e.getName(), e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.PARAM_NOT_MATCH); + } + @ExceptionHandler(value = HttpMessageNotReadableException.class) + public ResEntity handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + log.info("-------------------------进入全局异常捕获HttpMessageNotReadableException---------------------------"); + log.error("【全局异常拦截】HttpMessageNotReadableException: 错误信息 {}", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.PARAM_NOT_NULL); + } + @ExceptionHandler(value = MissingServletRequestParameterException.class) + public ResEntity handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { + log.info("-------------------------进入全局异常捕获MissingServletRequestParameterException---------------------------"); + log.error("【全局异常拦截】MissingServletRequestParameterException: 异常信息 {}", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.PARAM_NOT_MATCH,e.getMessage()); + } + @ExceptionHandler(value = ParamException.class) + public ResEntity handleParamException(ParamException e) { + log.info("-------------------------进入全局异常捕获ParamException---------------------------"); + log.error("【全局异常拦截】ParamException: 异常信息 {}", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.PARAM_NOT_NULL,e.getMessage()); + } + @ExceptionHandler(value = JsonProcessingException.class) + public ResEntity handleJsonProcessingException(JsonProcessingException e) { + log.info("-------------------------进入全局异常捕获JsonProcessingException---------------------------"); + log.error("【全局异常拦截】JsonProcessingException: 异常信息 {}", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.JSON_PARSE_ERROR,e.getMessage()); + } + @ExceptionHandler(value = BaseException.class) + public ResEntity handleBaseException(BaseException e) { + log.error("【全局异常拦截】BaseException: 异常信息 {}", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.parse(e.getCode()),e.getMessage()); + } + + + @ExceptionHandler(value = Exception.class) + public ResEntity handleGlobalException(Exception e) { + log.info("-------------------------进入全局异常捕获Exception---------------------------"); + log.error("【全局异常拦截】: 异常信息 {} ", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.SYSTEM_RUNTIME_ERROR,e.getMessage()); + } + + @ExceptionHandler(value = IllegalArgumentException.class) + public ResEntity handleAssertException(IllegalArgumentException e){ + log.info("-------------------------进入全局异常捕获Exception---------------------------"); + log.error("【全局异常拦截】: 异常信息 {} ", e.getMessage()); + log.error("异常栈:",e); + return WebResponse.failure(ResCode.SYSTEM_RUNTIME_ERROR,e.getMessage()); + } +} diff --git a/core/src/main/java/com/centricsoftware/core/service/CommonExportService.java b/core/src/main/java/com/centricsoftware/core/service/CommonExportService.java new file mode 100644 index 0000000..0f6e519 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/service/CommonExportService.java @@ -0,0 +1,216 @@ +package com.centricsoftware.core.service; + + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillWrapper; +import com.centricsoftware.commons.utils.ExportExcel; +import com.centricsoftware.commons.utils.MarkImageUtil; +import com.centricsoftware.core.dto.CommonExportDto; +import com.centricsoftware.enhancement.modules.c8.component.design.chain.IDepPathSearchChain; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.util.ExportUtil.AnalysisCell; +import com.centricsoftware.enhancement.util.ExportUtil.ExportUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.io.IOUtils; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Slf4j +@Service +public class CommonExportService { + + @Resource + private ExportUtil exportUtil; + + @Resource + private C8NodeService c8NodeService; + + @Resource + private IDepPathSearchChain iDepPathSearchChain; + + // 本地资源类路径 + private static final String classPath = "D:/plmservice/"; + + private List getBoList () { + List list = Lists.newArrayList(); + list.add("C482"); + list.add("C1402"); + list.add("C1403"); + list.add("C523"); + list.add("C525"); + DepPath deppath = DepPath.builder().addUrls(list).build(); + List resultList = iDepPathSearchChain.execute(deppath,CommonExportDto.class,null); + for (CommonExportDto bo : resultList) { + bo.setStyleImageIn(c8NodeService.getFileFromDirectAddr(bo.getRealImgUrl())); + } + return resultList; + } + + public ExportExcel export1() throws Exception { + // 获取配色信息 + List list = getBoList(); + ClassPathResource classPathResource = new ClassPathResource("template/export1.xlsx"); + try (InputStream in = classPathResource.getInputStream()) { + XSSFWorkbook wb = new XSSFWorkbook(Objects.requireNonNull(in)); + ExportExcel excel = new ExportExcel(wb, wb.getSheetAt(0)); + exportUtil.initTemplateRegion(CommonExportDto.class, "akaBuyTags"); + XSSFSheet sheet = excel.getSrcWb().cloneSheet(0, "sheet1"); + excel.setSheet(sheet); + exportUtil.fillDataComment(excel,7,list); + sheet.getPrintSetup().setScale((short) 75); + sheet.getPrintSetup().setTopMargin(0); + wb.removeSheetAt(0); + return excel; + } finally { + exportUtil.remoteTemplateRegion(); + } + } + + /** + * 获取数据 + * @author liaochangjiang + * @since 2024-03-05 15:04 + */ + private CommonExportDto getData () { + // 获取配色信息 + CommonExportDto bo = getBoList().get(0); + // 获取到的BO进行图片填充 + bo.setStyleImageIn(c8NodeService.getFileFromDirectAddr(bo.getRealImgUrl())); + bo.setStyleImageIn1(c8NodeService.getImageViewable("C1616")); + return bo; + } + + /** + * 固定模板导出 + * @author liaochangjiang + * @since 2024-03-05 15:01 + */ + public void export2(HttpServletResponse response) throws Exception { + ExportUtil.setExportResponseInfo(response, "固定模板导出.xlsx");// 设置文件名 + CommonExportDto bo = getData(); + String template = "template/export2.xlsx";// 获取模板文件 + exportUtil.exportByStatic(response.getOutputStream(),template,bo);// 固定模板导出 + } + + /** + * 固定模板导出-多数据源 + * @author liaochangjiang + * @since 2024-03-05 15:01 + */ + public void export5(HttpServletResponse response) throws Exception { + ExportUtil.setExportResponseInfo(response, "固定模板导出-多数据源.xlsx");// 设置文件名 + CommonExportDto bo = getData(); + // 获取配色信息 + List list = getBoList(); + List request = Lists.newArrayList(); + request.add(new FillWrapper("list", list)); + request.add(bo); + String template = "template/export5.xlsx";// 获取模板文件 + exportUtil.exportByStatic(response.getOutputStream(),template,request);// 固定模板导出 + } + + + /** + * 做图片转换-打上星号或者文字 + * @author liaochangjiang + * @since 2024-02-20 21:05 + */ + public InputStream exchangeImage(InputStream sourceIn) throws Exception { + Map imageMap = getImageIcon(); + if (sourceIn == null) { + return null; + } + // 获取初始的图片的宽度 + Image img = ImageIO.read(sourceIn); + // 宽度 + int initX = img.getWidth(null); + // 高度 + int initY = img.getHeight(null); + int iconWidth = 50; + int x = 0; + int y = 0; + // 添加对应的图标坐标 + return MarkImageUtil.addImgMark(imageMap.get("red"),iconWidth,iconWidth, img,x,y,initX,initY); + } + + /** + * 插入文字 + * @return + */ + public InputStream addTextMark(InputStream sourceIn) throws IOException { + if (sourceIn == null) { + return null; + } + // 获取初始的图片的宽度 + Image img = ImageIO.read(sourceIn); + // 宽度 + int initX = img.getWidth(null); + // 高度 + int initY = img.getHeight(null); + Color color = Color.RED; + Font font = new Font("微软雅黑", Font.BOLD, 18); + return MarkImageUtil.addTextMark(img, 10, 10, initX, initY, "测试文字", color, font); + } + + /** + * 获取图片的流集合;用于打星 + * @author liaochangjiang + * @since 2024-02-20 21:04 + */ + public Map getImageIcon() throws Exception { + Map imageMap = new HashMap<>(); + imageMap.put("red", ImageIO.read(Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream("static/icon/red.jpg"))));// 核心款 + return imageMap; + } + + + public ExportExcel export3(HttpServletResponse response) throws Exception { + // 获取配色信息 + List list = getBoList(); + ClassPathResource classPathResource = new ClassPathResource("template/export3.xlsx"); + try (InputStream in = classPathResource.getInputStream()) { + XSSFWorkbook wb = new XSSFWorkbook(Objects.requireNonNull(in)); + ExportExcel excel = new ExportExcel(wb, wb.getSheetAt(0)); + exportUtil.initTemplateRegion(CommonExportDto.class, "launchmap"); + XSSFSheet sheet = excel.getSrcWb().cloneSheet(0, "sheet1"); + excel.setSheet(sheet); + exportUtil.fillDataComment(excel,32,list); + sheet.getPrintSetup().setScale((short) 75); + sheet.getPrintSetup().setTopMargin(0); + wb.removeSheetAt(0); + return excel; + } finally { + exportUtil.remoteTemplateRegion(); + } + } + + public void export4(HttpServletResponse response) throws Exception { + ExportUtil.setExportResponseInfo(response, "列表型导出.xlsx");// 设置文件名 + String template = "template/export4.xlsx";// 获取模板文件 + // 获取数据 + List list = getBoList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).setIndex(i + 1); + } + exportUtil.exportByStatic(response.getOutputStream(),template,list);// 固定模板导出 + } +} diff --git a/core/src/main/java/com/centricsoftware/core/task/config/TaskConfig.java b/core/src/main/java/com/centricsoftware/core/task/config/TaskConfig.java new file mode 100644 index 0000000..350325e --- /dev/null +++ b/core/src/main/java/com/centricsoftware/core/task/config/TaskConfig.java @@ -0,0 +1,39 @@ +package com.centricsoftware.core.task.config; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * 使用java配置文件配置task + * + * @author zheng.gong + * @date 2020/4/21 + */ +@Configuration +@EnableScheduling +@ComponentScan(basePackages = {"com.centricsoftware.core.task.job"}) +public class TaskConfig implements SchedulingConfigurer { + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskExecutor()); + } + + /** + * 这里等同于配置文件配置 + * {@code spring.task.scheduling.pool.size=20} - Maximum allowed number of threads. + * {@code spring.task.scheduling.thread-name-prefix=Job-Thread- } - Prefix to use for the names of newly created threads. + * {@link org.springframework.boot.autoconfigure.task.TaskSchedulingProperties} + */ + @Bean + public Executor taskExecutor() { + return new ScheduledThreadPoolExecutor(20, new BasicThreadFactory.Builder().namingPattern("Job-Thread-%d").build()); + } +} diff --git a/core/src/main/java/com/centricsoftware/task/job/inter/DeleteLogTask.java b/core/src/main/java/com/centricsoftware/task/job/inter/DeleteLogTask.java new file mode 100644 index 0000000..8a87929 --- /dev/null +++ b/core/src/main/java/com/centricsoftware/task/job/inter/DeleteLogTask.java @@ -0,0 +1,38 @@ +package com.centricsoftware.task.job.inter; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 删除接口日志、操作日志等 + */ +@Component +@Slf4j +public class DeleteLogTask { + + @Resource + LogServiceImpl logService; + + //注入cs.plm.feign-log的yml配置 + @Value("${cs.plm.feign-log:false}") + private String feignLog; + + @Scheduled(cron = "0 0 5 * * ? ") //每天凌晨5点 + public void deleteLog() { + log.info("删除日志开始.."); + if(StrUtil.equals(feignLog, Constants.Bool.TRUE, true)){ + int count = logService.deleteLog(60); + log.info("删除日志{}条" ,count); + }else{ + log.info("未启用日志记录,不执行删除操作。启用配置项:{}" ,"cs.plm.feign-log"); + } + log.info("删除日志结束"); + } +} diff --git a/core/src/main/resources/application.yml b/core/src/main/resources/application.yml new file mode 100644 index 0000000..bf2991f --- /dev/null +++ b/core/src/main/resources/application.yml @@ -0,0 +1,39 @@ +###这个yml文件存放系统级别的相关配置 +spring: + application: + name: plmservice + http: + encoding: + charset: UTF-8 + profiles: + active: @profileActive@ + include: redis,rabbitmq,mybatis,plm,import,pro,common,sso,integration,es +server: + port: ${web.port} + tomcat: + uri-encoding: UTF-8 + servlet: + context-path: /plmservice +logging: + config: classpath:logback-spring.xml + file: + path: ${web.logging.path} +feign: + httpclient: + enabled: false + okhttp: + enabled: true + +#最大连接数 +http: + maxTotal: 100 + #并发数 + defaultMaxPerRoute: 20 + #创建连接的最长时间 + connectTimeout: 10000 + #从连接池中获取到连接的最长时间 + connectionRequestTimeout: 5000 + #数据传输的最长时间 + socketTimeout: 100000 + #线程失活检查 + validateAfterInactivity: 30000 diff --git a/core/src/main/resources/banner.txt b/core/src/main/resources/banner.txt new file mode 100644 index 0000000..bf24f54 --- /dev/null +++ b/core/src/main/resources/banner.txt @@ -0,0 +1,7 @@ + _ _ + ___ ___ _ __ | |_ _ __ (_) ___ + / __|/ _ \| '_ \ | __|| '__|| | / __| +| (__| __/| | | || |_ | | | || (__ + \___|\___||_| |_| \__||_| |_| \___| + https://www.centricsoftwarechina.com/about-us/ + :: Spring Boot C8 base on okHttp:: (v2.2.6.RELEASE) \ No newline at end of file diff --git a/core/src/main/resources/bootstrap.yml b/core/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..7027c52 --- /dev/null +++ b/core/src/main/resources/bootstrap.yml @@ -0,0 +1,26 @@ +###这个yml文件存放系统级别的相关配置 +spring: + application: + name: plmservice +# boot: +# admin: +# client: +# url: http://localhost:9001 +logging: + file: + path: D:/plmservice/logs +#management: +# health: +# elasticsearch: +# enabled: false +# redis: +# enabled: false +# db: +# enabled: false +# endpoints: +# web: +# exposure: +# include: '*' +# endpoint: +# logfile: +# external-file: D:/plmservice/logs/plmservice.log \ No newline at end of file diff --git a/core/src/main/resources/jboss-deployment-structure.xml b/core/src/main/resources/jboss-deployment-structure.xml new file mode 100644 index 0000000..0cad80a --- /dev/null +++ b/core/src/main/resources/jboss-deployment-structure.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/core/src/main/resources/template/export1.xlsx b/core/src/main/resources/template/export1.xlsx new file mode 100644 index 0000000..95a9dd4 Binary files /dev/null and b/core/src/main/resources/template/export1.xlsx differ diff --git a/core/src/main/resources/template/export2.xlsx b/core/src/main/resources/template/export2.xlsx new file mode 100644 index 0000000..4a44a4e Binary files /dev/null and b/core/src/main/resources/template/export2.xlsx differ diff --git a/core/src/main/resources/template/export3.xlsx b/core/src/main/resources/template/export3.xlsx new file mode 100644 index 0000000..52d8a6a Binary files /dev/null and b/core/src/main/resources/template/export3.xlsx differ diff --git a/core/src/main/resources/template/export4.xlsx b/core/src/main/resources/template/export4.xlsx new file mode 100644 index 0000000..41f5f8d Binary files /dev/null and b/core/src/main/resources/template/export4.xlsx differ diff --git a/core/src/main/resources/template/export5.xlsx b/core/src/main/resources/template/export5.xlsx new file mode 100644 index 0000000..b5f9484 Binary files /dev/null and b/core/src/main/resources/template/export5.xlsx differ diff --git a/core/src/main/webapp/index.jsp b/core/src/main/webapp/index.jsp new file mode 100644 index 0000000..a3b8f63 --- /dev/null +++ b/core/src/main/webapp/index.jsp @@ -0,0 +1,16 @@ +<%-- + Created by IntelliJ IDEA. + User: zheng.gong + Date: 2020/6/17 + Time: 16:07 + To change this template use File | Settings | File Templates. +--%> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + Title + + + test jsp + + diff --git a/core/src/test/java/com/centricsoftware/core/model/TimeSeries.java b/core/src/test/java/com/centricsoftware/core/model/TimeSeries.java new file mode 100644 index 0000000..d1822b4 --- /dev/null +++ b/core/src/test/java/com/centricsoftware/core/model/TimeSeries.java @@ -0,0 +1,16 @@ +package com.centricsoftware.core.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +@Data +@Accessors(chain = true) +public class TimeSeries { + + private String message; + @JsonProperty("@timestamp") + private LocalDateTime startTime; +} diff --git a/core/src/test/java/com/centricsoftware/core/service/ElasticsearchClientTest.java b/core/src/test/java/com/centricsoftware/core/service/ElasticsearchClientTest.java new file mode 100644 index 0000000..e020189 --- /dev/null +++ b/core/src/test/java/com/centricsoftware/core/service/ElasticsearchClientTest.java @@ -0,0 +1,204 @@ +package com.centricsoftware.core.service; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; +import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; +import co.elastic.clients.elasticsearch._types.aggregations.AggregationBuilders; +import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.core.IndexResponse; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.json.JsonData; +import com.centricsoftware.commons.utils.JsonUtil; +import com.centricsoftware.config.entity.EsProperties; +import com.centricsoftware.core.CoreApplication; +import com.centricsoftware.core.model.TimeSeries; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.dto.log.QueryPlmLogReq; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.annotation.Resource; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@SpringBootTest(classes = CoreApplication.class) +public class ElasticsearchClientTest { + + @Resource + private ElasticsearchClient client; + @Resource + private EsProperties esProperties; + + @Test + public void testSearch() throws IOException { + SearchResponse search = client.search(s -> s + .index(esProperties.getLogIndex()) +// .query(q->q.matchAll(QueryBuilders.matchAll().build())) + .query(q -> q + .term(t -> t + .field("message") + .value(v -> v.stringValue("hell")) + ) + ) + , + TimeSeries.class); + for (Hit hit : search.hits().hits()) { + System.out.println(hit.source()); + } + } + + @Test + public void testInsert() throws IOException { + IndexRequest req = new IndexRequest.Builder() + .index(esProperties.getLogIndex()) + .document(new TimeSeries().setMessage("hello").setStartTime(LocalDateTime.now())) + .build(); + IndexResponse index = client.index(req); + System.out.println(JsonUtil.toJSONString(index)); + } + + @Test + public void testSaveLog() { + PlmLog data = new PlmLog() + .setPath("PLM测试2") + .setName("测试接口") + .setHost("127.0.0.1") + .setBrand("PLM") + .setStartTime(LocalDateTime.now()) + .setEndTime(LocalDateTime.now().plusSeconds(20)) + .setSuccess(true) + .setCostMs(20000L) + .setParam("[{\"printingComment\":\"\",\"TransferKey\":\"C6227096\",\"stylecode\":\"152238715\",\"phase\":\"首样\",\"supplier\":\"\",\"mechcomment\":\"\",\"replicate\":\"右袖袋减短,按样衣画线;袖长做准;\",\"CommentsComfirmDate\":\"2021-07-13 10:45:55\",\"ReType\":\"复版+复花版\",\"replicate1\":\"右袖袋减短,按样衣画线;袖长做准;\",\"replicate2\":\"\",\"replicate3\":\"\",\"issueType\":\"不提供图稿\",\"ReTypePhase\":\"复版+复花版\"}]") + .setReturnCode("0") + .setReturnMsg("操作成功") + .setReturnText("{\"Sucess\":true,\"Msg\":\"接收成功\",\"Detail\":[{\"Result\":true,\"ErrorMsg\":\"\",\"TransferKey\":\"C6227096\"}]}"); + for (int i = 0;i < 10000;i++) { + data.setName("测试接口" + i); + IndexRequest req = new IndexRequest.Builder() + .index(esProperties.getLogIndex()) + .document(data) + .build(); + IndexResponse index = null; + try { + index = client.index(req); + if (log.isDebugEnabled()) { + log.debug("index: {}, id:{}, result:{}", index.index(), index.id(), index.result()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Test + public void testQueryLogs() throws IOException { + QueryPlmLogReq req = new QueryPlmLogReq(); + req.setPath("ESCM.SetView_FB"); + req.setName("测试接口"); + req.setParam("printingComment"); + SearchResponse search = client.search(s -> s + .index(esProperties.getLogIndex()) + .query(q -> { + BoolQuery.Builder builder = QueryBuilders.bool(); + List param = new ArrayList<>(); + if (StringUtils.isNotBlank(req.getPath())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("path").value(v -> v.stringValue(req.getPath())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getName())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("name").value(v -> v.stringValue(req.getName())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getBrand())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("brand").value(v -> v.stringValue(req.getBrand())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getStartTimeBegin())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("@timestamp").gte(JsonData.of(req.getStartTimeBegin())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getStartTimeEnd())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("@timestamp").lt(JsonData.of(req.getStartTimeEnd())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getEndTimeBegin())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("endTime").gte(JsonData.of(req.getEndTimeBegin())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getEndTimeEnd())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("endTime").lt(JsonData.of(req.getEndTimeEnd())).build()) + .build()); + } + if (Objects.nonNull(req.getSuccess())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("success").value(v -> v.booleanValue(req.getSuccess())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getHost())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("host").value(v -> v.stringValue(req.getHost())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getParam())) { + param.add(new Query.Builder() + .matchPhrase(QueryBuilders.matchPhrase().field("param").query(req.getParam()).build()) + .build()); + } + if (Objects.nonNull(req.getReturnCode())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("returnCode").value(v -> v.longValue(req.getReturnCode())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getReturnText())) { + param.add(new Query.Builder() + .matchPhrase(QueryBuilders.matchPhrase().field("returnText").query(req.getReturnText()).build()) + .build()); + } + builder = builder.filter(param); + return q.bool(builder.build()); + }) + .from((int) ((req.getPageNum() - 1) * req.getPageSize().intValue())) + .size(req.getPageSize().intValue()) + , + PlmLog.class); + System.out.println(JsonUtil.toJSONString(search.hits().hits().stream().map(Hit::source).collect(Collectors.toList()))); + } + + @Test + public void testAggs() throws IOException { + SearchResponse resp = client.search(s -> s + .index(esProperties.getLogIndex()) + .query(q -> q + .term(t -> t + .field("brand") + .value(v -> v.stringValue("od-clo")) + ) + ) + .aggregations("desc", Aggregation.of(t -> t.terms(AggregationBuilders.terms().field("name").size(100).build()))) + .size(0) + , PlmLog.class); + Aggregate aggr = resp.aggregations().get("desc"); + List array = aggr.sterms().buckets().array(); + List collect = array.stream().map(StringTermsBucket::key).collect(Collectors.toList()); + System.out.println(JsonUtil.toJSONString(collect)); + } +} diff --git a/core/src/test/postman/Test.http b/core/src/test/postman/Test.http new file mode 100644 index 0000000..dd6823b --- /dev/null +++ b/core/src/test/postman/Test.http @@ -0,0 +1,75 @@ +GET http://localhost:8988//plmservice/test/testRequest +Content-Type: application/json + +{} + +### +#Rest Api session login +POST http://localhost:80/api/item +Content-Type: application/json + +{} + +### +#测试配置文件读取 +POST {{baseUrl}}/test/testProp +Content-Type: application/json + + +### +###用户自定义controller +POST {{baseUrl}}/custom/test +Content-Type: application/json + +{"param": "456"} + +### +POST {{baseUrl}}/watchService/post/DemoStrategyService1Impl +Content-Type: application/json + +{"configName": "style"} + +### +#测试PLM登出 +POST {{baseUrl}}/test/logout +Content-Type: application/json + + +### +#测试cookie +POST {{baseUrl}}/test/testCookie +Content-Type: application/json + + +### + + +### +#测试延迟队列 +POST {{baseUrl}}/test/testRabbitMQ +Content-Type: application/json + +{"msg": "test delay message"} + +### +#测试策略转发实现类 DemoStrategyService1Impl DemoStrategyService2Impl demoStrategyServiceImpl +POST {{baseUrl}}/watchService/post/DemoStrategyService2Impl +Content-Type: application/json + +{"user": "admin","type":"style"} + +### +#testDemo request +POST {{baseUrl}}/test/testDemo +Content-Type: application/json + +{"test": "testjson"} + +### +#testRequest +POST {{baseUrl}}/test/testRequest +Content-Type: application/json + +{"test": "testjson"} + +### \ No newline at end of file diff --git a/core/src/test/postman/http-client.env.json b/core/src/test/postman/http-client.env.json new file mode 100644 index 0000000..e7da21a --- /dev/null +++ b/core/src/test/postman/http-client.env.json @@ -0,0 +1,11 @@ +{ + "dev": { + "baseUrl": "http://localhost:8088/plmservice" + }, + "test": { + "baseUrl": "http://114.55.209.111:8088/plmservice" + }, + "tomcat": { + "baseUrl": "http://localhost:8988/plmservice" + } +} \ No newline at end of file diff --git a/enhancement/.gitignore b/enhancement/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/enhancement/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/enhancement/pom.xml b/enhancement/pom.xml new file mode 100644 index 0000000..40a0ac4 --- /dev/null +++ b/enhancement/pom.xml @@ -0,0 +1,71 @@ + + + + + 2.0.1 + 7.16.2 + + + plmservice + com.centricsoftware + 2.0 + + 4.0.0 + + enhancement + 2.1 + + + + com.centricsoftware + commons + + + com.centricsoftware + redis + + + org.projectlombok + lombok + + + com.centricsoftware + config + + + org.springframework + spring-webmvc + + + org.apache.tomcat.embed + tomcat-embed-core + + + co.elastic.clients + elasticsearch-java + ${elasticsearch.version} + + + jakarta.json + jakarta.json-api + ${jakarta-json.version} + + + org.springframework.boot + spring-boot-test + test + + + junit + junit + test + + + com.aliyun.oss + aliyun-sdk-oss + 3.13.0 + + + diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Column.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Column.java new file mode 100644 index 0000000..a441e90 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Column.java @@ -0,0 +1,33 @@ +package com.centricsoftware.enhancement.ant; + + +import com.centricsoftware.commons.em.C8ImportTypeEnum; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface C8Column { + String attributeId();//C8字段ID + String colName() default "";//导入列名 + //String类型可在这定义正则表达式;ref类型的可以写Search的xml(这里写yml的变量,在变量中写xml,C8Entity中的createXml一样) + //xml中可以写变量,比如{name},程序会自动获取实体类中getName值用于替换变量; + String valid() default ""; + String stringCase() default "";//字符串是否转成大小写,upper:转大写;lower:转小写 + String path() default "";//路径 + C8ImportTypeEnum type() default C8ImportTypeEnum.STRING;//字段类型 + String timeFormat() default "yyyy-MM-dd";//时间格式 + boolean update() default true;//字段是否更新 + boolean required() default true;//字段是否必填 + boolean forceUpdate() default false;//是否强制更新,true表示,如果值为空,c8的数据将被设置为空 + boolean convertParentForceUpdate() default false;//强制更新是否以字段级别的配置为准,默认为false + boolean key() default false; //用来判断是新增还是更新 + String enumPrefix() default "";//enum值的前缀,不需要加冒号 + String refBO() default "";//当type为ref类型时,在这里注明哪个BO + String refAttr() default "Node Name";//当type为ref类型时,默认根据Node Name匹配 + //ref类型过滤xml Active + String split() default ";";//当字段为数组时,默认的分隔符号 + String tips() default "";//重新定制错误信息 + String otherXmlAttr() default "";//保存的时候其他属性(flag等);例子: otherXmlAttr="FG='0x1'" +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8ColumnConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8ColumnConfig.java new file mode 100644 index 0000000..e91fafc --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8ColumnConfig.java @@ -0,0 +1,71 @@ +package com.centricsoftware.enhancement.ant; + +import lombok.Data; +import org.springframework.util.StringUtils; + +@Data +public class C8ColumnConfig { + + private String attributeId;//C8字段ID + + private String colName;//导入列名 + + private String valid ;//String类型可在这定义正则表达式;ref,enum类型的可以写Search的xml,其中enum只要写限制条件就行;enumlist暂不支持校验 + + /** + * 执行valid后,返回的字段值, + * enum类型有效: + * 当addAttribute为true时:该字段不配置的时,返回DependsOn[0].Value,配置则返回validAttr的值。 + * 用途:比如填写材料小类,自动带出材料中类; + * ref类型有效: + * 当valid已配置:如配置了validAttr,则返回valid第一个查询结果的【validAttr】值,可以是表达式,但是必须是ref。 + * 例子:NodeUtil.queryExpressionResult(validAttr,result.get(0)); + */ + private String validAttr; + + private boolean validResultRequired=true;//valid为xml时,true:无返回值报错;false:有返回值报错 + + private String stringCase;//字符串是否转成大小写,upper:转大写;lower:转小写;其他值则不作处理 + + private String path;//路径 + + private String type="string";//C8的字段类型 + + private String timeFormat="yyyy-MM-dd";//时间格式转换 + + private boolean update=true;//字段是否校验更新 + + private boolean required=true;//字段是否校验必填 + + private boolean addAttribute=false;//额外的属性,无需用户导入;比如Subtype;为true时,不校验必填 + + private boolean forceUpdate=true;//是否强制更新,true表示,如果值为空,c8的数据将被设置为空 + + private String enumPrefix;//enum值的前缀,不需要加冒号 + + private String split=";";//当字段为数组时,默认的分隔符号 + + private String tips="";//报错时的提示 + + private boolean key=false;//用来判断是新增还是更新;yml配置的版本目前暂未实现该用法,注解的版本已实现 + + private String refBO;//ref类型的字段,当valid没配置时,并且配置了此字段,则按照refBO和refAttr搜索 + + private String refAttr;//ref类型的字段,当valid没配置时,并且配置了此字段,则按照refBO和refAttr搜索,如果不配置,则按照Node Name搜索;只能是SValue + + private String appendSuffix = "";//对导入的值拼接后缀 + + private boolean removeExp = false;//是否去除表达式 + + private String otherXmlAttr = "";//保存的时候其他属性(flag等);例子: otherXmlAttr : FG='0x1' + + private boolean continueTrans = false; // 如果Excel中列名重复, 是否继续读取重复列的数据,为true时读取,为false时不读取. 但如果为false可能会出现重复列的数值报空值错误. + + public String getId() { + if (StringUtils.isEmpty(this.getPath())) { + return this.getAttributeId(); + } else { + return this.getAttributeId() + " " + this.getPath(); + } + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Entity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Entity.java new file mode 100644 index 0000000..6439b52 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8Entity.java @@ -0,0 +1,45 @@ +package com.centricsoftware.enhancement.ant; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface C8Entity { + + //BO的Node Type + String nodeType(); + + //如果存在错误提示,是否要保存校验成功的行,默认是保存的;当isSingleSave为true时,改指标不生效 + boolean failSave() default true; + + //一旦出现错误信息,后续的数据行是否继续执行 + boolean failContinue() default true; + + //是否添加校验语句,即 + boolean validateNode() default false; + + //是否逐条保存,默认是统一保存 + boolean singleSave() default false; + + //创建的xml,获取yml或者properties中的属性;cs.center.prop前缀省略(upload.style.searchxml表示获取cs.center.prop.upload.style.searchxml) + String createXml() default ""; + + //查询的xml,获取yml或者properties中的属性;cs.center.prop前缀省略 + String searchXml() default ""; + + boolean create() default true;//如果找不到行记录数据是否新增 + + //去重的字段 + String[] removeDuplicate() default {}; + + boolean seadEmail() default false;//默认发送给当前用户 + + boolean redis() default false;//是否发送到redis + + //为所有字段设置是否强制更新,默认为false;如果设置为true,则会覆盖字段级别的设置;如果为false,则此字段不生效 + boolean forceUpdate() default false; + + String tips() default "";//定制错误提示信息 + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8EntityConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8EntityConfig.java new file mode 100644 index 0000000..afde832 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8EntityConfig.java @@ -0,0 +1,61 @@ +package com.centricsoftware.enhancement.ant; + +import com.google.common.collect.Lists; +import lombok.Data; + +import java.util.List; + +@Data +public class C8EntityConfig { + + private String nodeType;//C8的Node Type + + private boolean create = true;//如果找不到行记录数据是否新增 + + private String tips = "";//提示信息:当searchXml查询不到时,create又设置为false的提示信息; + +// private String[] cols;//必须显示声明所有的列 + + //如果存在错误提示,是否要保存校验成功的行,默认是保存的;当isSingleSave为true时,该指标不生效 + private boolean isFailSave = true; + + //一旦出现错误信息,后续的数据行是否继续执行 + private boolean isFailContinue = true; + + //是否添加校验语句,即 + private boolean validateNode = false; + + //是否逐条保存,默认是统一保存 + private boolean isSingleSave = true; + + //是否直接导入,true则将xml发送到redis + private boolean redis = false; + + //创建的xml,获取yml或者properties中的属性;cs.center前缀省略(upload.style.searchxml表示获取cs.center.prop.upload.style.searchxml) + private String createXml; + + //查询的xml,获取yml或者properties中的属性;cs.center前缀省略 + private String searchXml; + + //其他的更新字段,这些字段不在导入的字段中 + private String[] otherChangeXml; + + //去重的字段 + private String[] removeDuplicate; + + //是否发送邮件 + private boolean sendEmail = false; +// private String emailSearchXml = "";//当sendEmail为true时,将按照改字段搜索邮箱 +// private String emailAttr = "Email";//当emailSearchXml搜索到数据时,按照emailAttr取邮箱,默认Email + + //导入前后需要执行的bean名称,需要实现DoBeforeOrAfterImportInterface接口 + //返回值错误信息 + private String beforeOrAfterImportBeanName=""; + + //插件:当校验通过时,额外执行的操作 + private String itemImportAroundAOPBeanName=""; + + private List columns = Lists.newArrayList(); + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8NativeExport.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8NativeExport.java new file mode 100644 index 0000000..08a543f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/C8NativeExport.java @@ -0,0 +1,17 @@ +package com.centricsoftware.enhancement.ant; + +import com.centricsoftware.commons.em.C8NativeExportType; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +@Deprecated +public @interface C8NativeExport { + String attributeName(); + String path() default ""; + C8NativeExportType type() default C8NativeExportType.STRING; + String timeFormat() default "yyyy-MM-dd HH:mm:ss";//时间格式 + String colName() default "";//导出时的列名 +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/ExcelAlias.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/ExcelAlias.java new file mode 100644 index 0000000..4337e6d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/ExcelAlias.java @@ -0,0 +1,18 @@ +package com.centricsoftware.enhancement.ant; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface ExcelAlias { + String colName(); + String attributeId(); + + String path() default ""; + + String type() default "string";//不支持List类型 + + boolean update() default true;//是否更新 + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridColumn.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridColumn.java new file mode 100644 index 0000000..289e432 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridColumn.java @@ -0,0 +1,75 @@ +package com.centricsoftware.enhancement.ant.grid; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface GridColumn { + + /** + * 字段ID:如果不配做,则默认取字段名 + */ + String field() default ""; + + /** + * 字段标题 + */ + String title(); + + /** + * 排序 + */ + boolean sortable() default true; + + /** + * 自定义转换 + * demo: + * 1、this.format.formatBoolean + * 2、this.format.formatDate + */ + String formatter() default ""; + + /** + * Form搜索配置;必须配置为true,才可在Form上搜索 + */ + boolean formFilter() default false; + + /** + * 过滤项:默认文本过滤 + * demo1: 下拉 + * [ + * { label: "是", value: "Y" }, + * { label: "否", value: "N" } + * ] + * demo2: 数值 + * [{ data: { min: "0", max: "0" } }] + * demo3: 文本 + * [{ data: ”“ }] + * demo4: 时间 + * { data: { value: [], valueFormat: "yyyy-MM-dd HH:mm:ss" } } + * 支持:时间、文本、数值、下拉 + */ + String filters() default "[{ data:\"\"}]"; + + /** + * 过滤项:默认文本过滤 + * 列上过滤的组件、支持:时间、文本、数值、下拉 + * {name: "FilterInput"} + * FilterTime、FilterComplex、FilterContent、FilterSelect、FilterInput + */ + String filterRender() default "{name: \"FilterInput\"}"; + + /** + * 列款 + */ + int width() default 150; + + /** + * 固定列:left、right + */ + String fixed() default ""; + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridForm.java b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridForm.java new file mode 100644 index 0000000..6804347 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/ant/grid/GridForm.java @@ -0,0 +1,56 @@ +package com.centricsoftware.enhancement.ant.grid; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface GridForm { + + /** + * 字段ID + */ + String field() default ""; + + /** + * 字段标题 + */ + String title() default ""; + + /** + * 过滤提醒信息: + * icon : el-icon-warning + */ + String titlePrefix() default ""; + + /** + * 所有项的栅格占据的列数(共 24 分栏) + */ + int span() default 4; + + /** + * input, textarea, select, $input, $textarea, $select, $button, $buttons, $radio, $checkbox, $switch + */ + String filterType() default "$input"; + + boolean clearable() default true; + + String props() default ""; + + /** + * 提示信息 + */ + String placeholder() default ""; + + boolean filterable() default true; + + /** + * 如下方选择的,如果是下拉选择的,则需要配置options选项 + * [ + * { value: "C8_Phase:002", label: "首样后" }, + * { value: "C8_Phase:003", label: "预览后" } + * ] + */ + String options() default ""; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/component/CacheComponent.java b/enhancement/src/main/java/com/centricsoftware/enhancement/component/CacheComponent.java new file mode 100644 index 0000000..18cab31 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/component/CacheComponent.java @@ -0,0 +1,42 @@ +package com.centricsoftware.enhancement.component; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * description: + * Date: 2023/3/8 11:23 + */ +@Component +@Slf4j +public class CacheComponent { + + TimedCache cache = CacheUtil.newTimedCache(1 * DateUnit.MINUTE.getMillis()); + + public String getStr(String key){ + Object o = cache.get(key); + return o==null?"":String.valueOf(o); + } + + public Object getObject(String key){ + return cache.get(key); + } + + public void setValue(String key,Object value ){ + cache.put(key,value); + } + + /** + * @param key key + * @param value value + * @param timeOut 毫秒数 + */ + public void setValue(String key,Object value,long timeOut){ + cache.put(key,value,timeOut); + } + +} + diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/component/ExcelAliasHelper.java b/enhancement/src/main/java/com/centricsoftware/enhancement/component/ExcelAliasHelper.java new file mode 100644 index 0000000..a8d0e60 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/component/ExcelAliasHelper.java @@ -0,0 +1,131 @@ +package com.centricsoftware.enhancement.component; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.ExcelReader; +import com.centricsoftware.commons.utils.CommonUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.ant.ExcelAlias; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Map; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/12/8 17:07 + */ +@Service +@Slf4j +public class ExcelAliasHelper { + + /** + * 添加别名 + */ + public void addHeaderAlias(ExcelReader reader, Class clazz) { + List fields = CommonUtil.getAllFields(clazz);//所有的字段 + for (Field field : fields) { + ExcelAlias annotation = field.getAnnotation(ExcelAlias.class); + if (annotation == null) { + continue; + } + reader.addHeaderAlias(annotation.colName(), field.getName()); + } + } + + public String extractOperation(List data, Class clazz) throws Exception { + return this.extractOperation(data, clazz, false, ""); + } + + /** + * 获取Operation + * + * @return 返回Operation + */ + public String extractOperation(List data, Class clazz, boolean isCreate, String boType) { + StringBuilder xml = new StringBuilder(); + StringBuilder error = new StringBuilder(); + Map annoMap = getAnnoList(clazz); + for (E dto : data) { + StringBuilder pathXml = new StringBuilder(); + String url = null; + try { + url = String.valueOf(clazz.getMethod("getUrl").invoke(dto)); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + C8OperationNode c8OperationNode; + if (isCreate) { + c8OperationNode = C8OperationNode.createNode(url, boType); + } else { + if(url==null||"".equals(url)){ + continue; + } + c8OperationNode = C8OperationNode.changeNode(url); + } + for (Map.Entry entry : annoMap.entrySet()) { + String fieldName = entry.getKey(); + Field field = entry.getValue(); + try { + if (StrUtil.isBlank(url)) { + url = String.valueOf(clazz.getMethod("getUrl").invoke(dto)); + } + fieldName = StringUtils.capitalize(fieldName); + String value = String.valueOf(clazz.getMethod("get" + fieldName).invoke(dto)); + ExcelAlias annotation = field.getAnnotation(ExcelAlias.class); + doExtractOperation(url,value, c8OperationNode,pathXml,annotation);//拼接Operation + } catch (IllegalAccessException e) { + error.append("无权限访问字段:").append(fieldName).append(e.getMessage()); + } catch (InvocationTargetException e) { + error.append(e.getMessage()); + } catch (NoSuchMethodException e) { + error.append("找不到方法:").append("get").append(fieldName).append(e.getMessage()); + } + } + xml.append(c8OperationNode.getXml()).append(pathXml); + } + return xml.toString(); + } + + private void doExtractOperation(String url, String value, C8OperationNode c8OperationNode, StringBuilder pathXml, ExcelAlias annotation){ + boolean update = false; + if (annotation != null) { + update = annotation.update(); + } + if (!update) { + return; + } + String attributeId = annotation.attributeId(); + String path = annotation.path(); + String type = annotation.type(); + if (StrUtil.isBlank(path)) { + c8OperationNode.changeAttribute(attributeId, type, value); + } else { + String zeous = C8OperationNode.changeNode(url, path).changeAttribute(attributeId, type, value).getXml(); + pathXml.append(zeous); + } + } + + /** + * 获取字段和注解的映射关系 + */ + private Map getAnnoList(Class clazz){ + Map map = Maps.newHashMap(); + List fields = CommonUtil.getAllFields(clazz);//所有的字段 + for (Field field : fields) { + ExcelAlias annotation = field.getAnnotation(ExcelAlias.class); + String fieldName = field.getName(); + if (annotation == null&&"url".equals(fieldName)) { + continue; + } + map.put(field.getName(),field); + } + return map; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/component/RedisUtils.java b/enhancement/src/main/java/com/centricsoftware/enhancement/component/RedisUtils.java new file mode 100644 index 0000000..a0ad244 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/component/RedisUtils.java @@ -0,0 +1,635 @@ +package com.centricsoftware.enhancement.component; + +import cn.hutool.core.date.DateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundZSetOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Component +public class RedisUtils { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return + */ + public boolean setExpire(String key, long time) { + boolean flag = false; + if (time > 0) { + flag = redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return flag; + } + + /** + * 根据key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + return redisTemplate.hasKey(key); + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete(CollectionUtils.arrayToList(key)); + } + } + } + + /** + * 模糊删除key + * @param keys + */ + public void delByDim(String keys) { + if(keys!=null && !"".equals(keys)){ + Set keySet = redisTemplate.keys(keys); + redisTemplate.delete(keySet); + } + } + + // ============================String============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + */ + public void set(String key, Object value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + */ + public void set(String key, Object value, long time) { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(大于0) + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + // ================================Map================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hGetItem(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hGetMap(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + */ + public void hPutItem(String key, String item, Object value) { + redisTemplate.opsForHash().put(key, item, value); + } + + /** + * 向一张hash表中放入数据,如果不存在将创建,并设置时间 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) + */ + public void hPutItem(String key, String item, Object value, long time) { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + setExpire(key, time); + } + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + */ + public void hPutAll(String key, Map map) { + redisTemplate.opsForHash().putAll(key, map); + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + */ + public void hPutAll(String key, Map map, long time) { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + setExpire(key, time); + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hDelItems(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasItem(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + */ + public void hIncr(String key, String item, int by) { + Object value = hGetItem(key, item); + if (value instanceof Integer) { + value = (int) value + by; + } else if (value instanceof Long) { + value = (long) value + by; + } else { + throw new RuntimeException("ERR hash value is not an integer"); + } + hPutItem(key, item, value); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(大于0) + */ + public void hDecr(String key, String item, int by) { + Object value = hGetItem(key, item); + if (value instanceof Integer) { + value = (int) value - by; + } else if (value instanceof Long) { + value = (long) value - by; + } else { + throw new RuntimeException("ERR hash value is not an integer"); + } + hPutItem(key, item, value); + } + + // ============================set============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0){ + setExpire(key, time); + } + return count; + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasMember(String key, Object value) { + return redisTemplate.opsForSet().isMember(key, value); + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSize(String key) { + return redisTemplate.opsForSet().size(key); + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long sRemoveMembers(String key, Object... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * 返回给定所有集合的差集 + * @param key1 + * @param key2 + * @return + */ + @SuppressWarnings("unchecked") + public Set sDiff(String key1, String... key2){ + Set set = sGet(key1); + if (key2 != null && key2.length > 0) { + if (key2.length == 1) { + set = redisTemplate.opsForSet().difference(key1, key2[0]); + } else { + set = redisTemplate.opsForSet().difference(key1, CollectionUtils.arrayToList(key2)); + } + } + return set; + } + + /** + * 返回给定所有集合的交集 + * @param key1 + * @param key2 + * @return + */ + @SuppressWarnings("unchecked") + public Set sInter(String key1, String... key2){ + Set set = sGet(key1); + if (key2 != null && key2.length > 0) { + if (key2.length == 1) { + set = redisTemplate.opsForSet().intersect(key1, key2[0]); + } else { + set = redisTemplate.opsForSet().intersect(key1, CollectionUtils.arrayToList(key2)); + } + } + return set; + } + + // ===============================list================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetSize(String key) { + return redisTemplate.opsForList().size(key); + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetItem(String key, long index) { + return redisTemplate.opsForList().index(key, index); + } + + /** + * 将单个值追加到list缓存最后,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + */ + public void lRightPush(String key, Object value) { + redisTemplate.opsForList().rightPush(key, value); + } + + /** + * 将单个值追加到list缓存最后,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public void lRightPush(String key, Object value, long time) { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0){ + setExpire(key, time); + } + + } + + /** + * 将集合追加到list缓存最后,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + */ + public void lRightPushAll(String key, List value) { + redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 将集合追加到list缓存最后,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public void lRightPushAll(String key, List value, long time) { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0){ + setExpire(key, time); + } + + } + + /** + * 将单个值插入到list缓存头部,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + */ + public void lLeftPush(String key, Object value) { + redisTemplate.opsForList().leftPush(key, value); + } + + /** + * 将单个值插入到list缓存头部,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public void lLeftPush(String key, Object value, long time) { + redisTemplate.opsForList().leftPush(key, value); + if (time > 0) { + setExpire(key, time); + } + } + + /** + * 将集合插入到list缓存头部,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + */ + public void lLeftPushAll(String key, List value) { + redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 将集合插入到list缓存头部,如果list不存在,则创建 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + */ + public void lLeftPushAll(String key, List value, long time) { + redisTemplate.opsForList().leftPushAll(key, value); + if (time <= 0) { + setExpire(key, time); + } + + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + */ + public void lUpdateItem(String key, long index, Object value) { + redisTemplate.opsForList().set(key, index, value); + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + return redisTemplate.opsForList().remove(key, count, value); + } + + /** + * 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 + * @param key + * @param start 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推 + * @param end 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 + */ + public void lTrim(String key, long start, long end) { + redisTemplate.opsForList().trim(key, start, end); + } + + + /** + * 将value元素及其对应的score 值加入到有序集 key 当中。 + * @param key key + * @param value value + * @param score 排序值 + * @return + */ + public boolean zadd(String key,String value,double score,long time){ + BoundZSetOperations operation=redisTemplate.boundZSetOps(key); + Boolean add = operation.add(value, score); + if (time > 0) { + setExpire(key, time); + operation.removeRangeByScore(0, DateUtil.currentSeconds()-time);//清除过期数据 + } + return add; + } + + public boolean zadd(String key,String value,long score){ + return zadd(key,value,score,0L); + } + + /** + * 将value元素及其对应的score 值加入到有序集 key 当中。 + * @param key key + * @param value value + * @param score 排序值 + * @return + */ + public boolean zadd(String key,String value,Integer score,long time){ + return zadd(key,value,score.doubleValue(),time); + } + + /** + * 将value元素及其对应的score 值加入到有序集 key 当中。 + * @param key key + * @param value value + * @param score 排序值 + * @return + */ + public boolean zadd(String key,String value,Integer score){ + return zadd(key,value,score.doubleValue(),0L); + } + + public Set zreverseRange(String key,int start,int end){ + BoundZSetOperations operation=redisTemplate.boundZSetOps(key); + return operation.reverseRange(start, end); + } + + /** + * @Author liaochangjiang + * @Description //TODO 根据key进行加锁 + * @Date 15:36 2024/3/21 + * @Param [key锁的key值, requestId请求唯一值, expireTime过期时间, timeout等待时间] + * @return boolean + **/ + public boolean tryGetDistributedLock(String key, String requestId, int expireTime, int timeout) { + long start = System.currentTimeMillis(); + long end; + do { + Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(key, requestId, (long)expireTime, TimeUnit.SECONDS); + if (flag != null && flag) { + return true; + } + end = System.currentTimeMillis() - start; + } while(end < (long)(timeout * 1000)); + return false; + } + + /** + * 释放锁 + * @author liaochangjiang + * @since 2024-03-21 16:22 + */ + public boolean releaseDistributedLock(String key, String requestId) { + RedisScript script = new DefaultRedisScript("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", Long.class); + Long result = this.redisTemplate.execute(script, Collections.singletonList(key), new Object[]{requestId}); + return Objects.equals(result, 1L); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/component/StandaloneLockComponent.java b/enhancement/src/main/java/com/centricsoftware/enhancement/component/StandaloneLockComponent.java new file mode 100644 index 0000000..789ff4a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/component/StandaloneLockComponent.java @@ -0,0 +1,87 @@ +package com.centricsoftware.enhancement.component; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.thread.ThreadUtil; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class StandaloneLockComponent { + + //获取锁相关信息 + @Getter + ThreadLocal lockInfo = new ThreadLocal<>(); + + int waitTime = 60 * 60 * 1000;//等待时间1小时;如果1小时内未获取锁,则返回加锁失败 + + /** + * 用于实现锁机制 + * 缓存时间默认60分钟; + */ + TimedCache cache = CacheUtil.newTimedCache(60 * DateUnit.MINUTE.getMillis()); + + public boolean tryLock(String key, String user,int waitTime) { + while (waitTime > 0) { + waitTime -= 500; + boolean lock = lock(key, user); + if (!lock) { + //睡眠500毫秒 + try { + ThreadUtil.safeSleep(500 * DateUnit.MS.getMillis()); + } catch (Exception e) { + log.error("线程休眠报错:" + e.getLocalizedMessage(), e); + } + } else { + return true; + } + } + return false; + } + + public boolean tryLock(String key) { + return tryLock( key, "Admin" , waitTime); + } + + /** + * 尝试加锁;如果10秒内未能获取锁,则返回加锁失败 + * @param key + * @param user + * @return + */ + public boolean tryLock(String key, String user) { + return tryLock( key, user, waitTime); + } + + /** + * 加锁 + * + * @param key 加锁的key + * @param user 加锁用户 + * @return 加锁成功返回true,否则返回false + */ + private synchronized boolean lock(String key, String user) { + boolean contain = cache.containsKey(key); + if (!contain) { + //如果缓存中没值,则加入缓存 + cache.put(key, user); + return true; + } + String u = cache.get(key); + lockInfo.set(u);//设置当前锁用户 + return false; + } + + /** + * 解锁 + * + * @param key + */ + public synchronized void unLock(String key) { + cache.remove(key); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/component/c8/NativeExportComponent.java b/enhancement/src/main/java/com/centricsoftware/enhancement/component/c8/NativeExportComponent.java new file mode 100644 index 0000000..4e44513 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/component/c8/NativeExportComponent.java @@ -0,0 +1,301 @@ +package com.centricsoftware.enhancement.component.c8; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.ExcelWriter; +import com.centricsoftware.commons.em.C8NativeExportType; +import com.centricsoftware.commons.utils.CommonUtil; +import com.centricsoftware.enhancement.ant.C8NativeExport; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import com.centricsoftware.enhancement.modules.c8.dto.nativeexport.ExtractParameterEntity; +import com.centricsoftware.enhancement.modules.c8.dto.nativeexport.ExtractResultEntity; +import com.centricsoftware.enhancement.mapper.ExtractMapper; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 注意:该方法已于2020年后废弃,因为C8版本的更迭可能导致存储过程变化,需要实时维护。 + * 可用DepPath查询方式替代。 + * 通过存储过程批量查询数据,并返回List;可支持分页查询。 + * 需要指定数据库(配置datasource.MSSQL): + * 1、oracle为Oracle数据库,忽略大小写 + * 2、其余的都是MSSQL + */ +@Component +@Slf4j +@Deprecated +public class NativeExportComponent { + @Autowired + EnumCache enumCache; + + @Autowired + ExtractMapper extractMapper; + + @Value("datasource.dbtype") + private String dbtype; + + /** + * + * @param queryXml + * @param clazz + * @return + * @throws Exception + */ + public List queryByExtractProc(String queryXml, Class clazz){ + String detailXmlStr = buildExtractXml(clazz); + List resultList ; + resultList = doExtractProc(queryXml, detailXmlStr); + List ts = new ArrayList<>(); + try { + ts = processResults(resultList, clazz); + } catch (Exception e) { + log.error(e.getLocalizedMessage(),e); + } + return ts; + } + + /** + * 通过存储过程ns_xmlQueryVerticalExtract查询数据 + * @param queryXmlStr 查询XML + * @param detailXmlStr 返回字段XML + * @return + */ + private List doExtractProc(String queryXmlStr, String detailXmlStr) { + if (StrUtil.isBlank(queryXmlStr) || StrUtil.isBlank(detailXmlStr)) { + return null; + } + ExtractParameterEntity parameter = new ExtractParameterEntity(); + parameter.setQueryXml(queryXmlStr); + parameter.setExtractXml(detailXmlStr); + List list = null; + if("oracle".equalsIgnoreCase(dbtype)){ + extractMapper.findByXmlQueryVerticalExtractOracle(parameter); + }else{ + list = extractMapper.findByXmlQueryVerticalExtract(parameter); + parameter.setResultList(list); + } + return parameter.getResultList(); + } + + /** + * 将查询结果行转列,并封装成具体类返回 + * @param resultList + * @param clazz + * @return + * @throws Exception + */ + private List processResults(List resultList, Class clazz) throws Exception { + Map dataMap = Maps.newLinkedHashMap(); + String fieldName = ""; + Object value; + Integer startId; + List fields = CommonUtil.getAllFields(clazz); + Map cacheUrlMap = Maps.newHashMap();//url_id和url的映射缓存 + for (ExtractResultEntity er : resultList) { + startId = er.getStartId(); + T t = dataMap.get(startId) == null ? clazz.newInstance() : dataMap.get(startId); + Field targetField = null; + C8NativeExport annotation = null; + String attrId = er.getAttrId(); + if (StrUtil.isBlank(attrId)) { + continue; + } + for (int i=0;i(dataMap.values()); + } + + /** + * 将tartId映射到clazz + * @param dataMap + * @param clazz + */ + private void fillStartId(Map dataMap,Class clazz,Map cacheUrlMap) throws Exception{ + Field field = null; + Method getMethod = null; + Method setMethod = null; + try { + field = clazz.getDeclaredField("url"); + getMethod = clazz.getMethod("getUrl"); + setMethod = clazz.getMethod("setUrl",field.getType()); + } catch (NoSuchMethodException e) { + log.warn(clazz.getName()+":没有url字段,取消url自动注入"); + } catch (SecurityException e) { + log.warn(clazz.getName()+":没有url字段,取消url自动注入"); + }catch (NoSuchFieldException e) { + log.warn(clazz.getName()+":没有url字段,取消url自动注入"); + } + if(getMethod!=null&&setMethod!=null&&field!=null){ + for (Map.Entry entry : dataMap.entrySet()) { + Integer k = entry.getKey(); + T v = entry.getValue(); + String value; + if(cacheUrlMap.containsKey(k)){ + value = cacheUrlMap.get(k); + }else{ + value = extractMapper.findUrlById(k.toString()); + } + value = "centric:".equals(value) ? "" : value; + clazz.getMethod("setUrl", field.getType()).invoke(v, value); + } + } + } + + private Object getExtractValueByType(ExtractResultEntity er, C8NativeExportType type, String timeFormat, Map cacheUrlMap) { + Object value = null; + switch (type) { + case INTEGER: + value = NumberUtil.parseInt(er.getValueString()); + break; + case DOUBLE: + value = er.getValueNumber(); + break; + case BOOLEAN: + if(StrUtil.isNotEmpty(er.getValueString())){ + value = Boolean.valueOf(er.getValueString()); + }else{ + value = er.getValueBoolean(); + } + break; + case URL_ID: + value = er.getValueUrl(); + break; + case URL: + String urlId = er.getValueUrl(); + if(cacheUrlMap.containsKey(urlId)){ + value = cacheUrlMap.get(urlId); + }else{ + value = extractMapper.findUrlById(er.getValueUrl()); + value = "centric:".equals(value) ? "" : value; + cacheUrlMap.put(urlId,value.toString()); + } + break; + case ENUM: + value = er.getValueString(); + break; + case ENUM_KEY: + String[] enumAll= er.getValueString().split(":"); + value = enumAll.length > 1 ? enumAll[1] : ""; + break; + case ENUM_DESC: + value = enumCache.getDescByFullname(er.getValueString()); + break; + case ENUM_DISPLAY: + value = enumCache.getDisplayByFullname(er.getValueString()); + break; + case TIME: + Double time = er.getValueNumber(); + SimpleDateFormat sm = new SimpleDateFormat(timeFormat); + if (time != null && time != 0) { + Timestamp ts = new Timestamp(time.longValue()); + value = sm.format(ts); + } else { + Timestamp ts = new Timestamp(0); + value = sm.format(ts); + } + break; + default: + value = er.getValueString(); + } + return value; + } + + + /** + * 拼接需要返回的字段 + * @param clazz + * @return + */ + private String buildExtractXml(Class clazz) { + if (clazz == null) { + return ""; + } + StringBuilder xml = new StringBuilder(); + xml.append(""); + List fields = CommonUtil.getAllFields(clazz); + for (int i=0;i"); + } + xml.append(""); + return xml.toString(); + } + + /** + * 为列名添加别名 + * @param writer + * @param clazz + */ + public void export2ExcelAlias(ExcelWriter writer, Class clazz){ + List fields = CommonUtil.getAllFields(clazz); + for (int i=0;i mails) throws Exception { + log.info("otherParam==============="+otherParam); + StringBuilder error = new StringBuilder(); +// ExcelReader reader = ExcelUtil.getReader(file.getInputStream()); +// List> read = reader.readAll(); +// reader.close(); + List> read = readAll(file); + C8EntityConfig config = uploadtProperties.getValue(importName); + String beforeOrAfterImportBeanName = config.getBeforeOrAfterImportBeanName(); + Map beforeMap = Maps.newHashMap(); + if(StrUtil.isNotBlank(beforeOrAfterImportBeanName)){ + DoBeforeOrAfterImportInterface bean = SpringContextHolder.getBean(beforeOrAfterImportBeanName); + beforeMap = bean.before(file, importName, otherParam, mails, error); + } + if(config==null){ + return WebResponse.failure("importName名称错误"); + } + List> readAll = transMapKey(read,config,otherParam,beforeMap);//转换key + List> unique = removeDuplicate(readAll, config);//去重 + if(readAll.size()!=unique.size()){ + error.append("数据存在重复,请校验数据:"+getRemoveDuplicate(config)); + return WebResponse.failure(error.toString()); + } + importPropertiesService.process(importName,config,readAll,error);//保存 + sendMail(config,error.toString(),mails); + if(StrUtil.isNotBlank(beforeOrAfterImportBeanName)){ + DoBeforeOrAfterImportInterface bean = SpringContextHolder.getBean(beforeOrAfterImportBeanName); + String afterXml = bean.after(file, importName, otherParam, mails,error); + if(StrUtil.isNotBlank(afterXml)){ + c8NodeService.processNode(afterXml); + } + } + if(StrUtil.isBlank(error.toString())){ + return WebResponse.success(error.toString()); + }else{ + return WebResponse.failure(error.toString()); + } + } + + /** + * 去重 + * @param readAll + * @param config + * @return + * @throws Exception + */ + private List> removeDuplicate(List> readAll,C8EntityConfig config) throws Exception{ + String[] removeDuplicate = config.getRemoveDuplicate(); + if(removeDuplicate==null){ + return readAll; + } + if(removeDuplicate.length>0){ + List> unique =readAll.stream().collect( + Collectors. collectingAndThen( + Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing( + o -> { + String str = ""; + for(String attr:removeDuplicate){ + String v = Convert.toStr(o.get(attr)); + str = str + v+";"; + } + return str; + }))), ArrayList::new) + ); + return unique; + } + return readAll; + } + + /** + * 将转换map的key + * @param readAll + * @param config + */ + private List> transMapKey(List> readAll,C8EntityConfig config,String otherParam,Map beforeMap){ + JSONObject jsonObject = JSONUtil.parseObj(otherParam); + Map paramMap = Convert.toMap(String.class, String.class, jsonObject); + List> list = Lists.newArrayList(); + List columns = config.getColumns(); + for(Map map : readAll){ + Map m = Maps.newHashMap(); + for (Map.Entry entry : map.entrySet()) { + for(C8ColumnConfig col:columns){ + if(col.getColName().equals(entry.getKey())){ + String val = StrUtil.trim(entry.getValue()+"");//去空格 + String appendSuffix = col.getAppendSuffix(); + if(StrUtil.isNotBlank(appendSuffix)){ + m.put(col.getId(),val+appendSuffix); + }else{ + m.put(col.getId(),val); + } + m.putAll(beforeMap);//将导入前校验的所有返回值放入每一行 + m.put(col.getId()+" old",val);//保存原始数据 + m.putAll(paramMap); + if (!col.isContinueTrans()) break; + } + } + } + list.add(m); + } + return list; + } + + /** + * 获取去重的字段名 + * @param config + * @return + * @throws Exception + */ + public String getRemoveDuplicate(C8EntityConfig config) throws Exception{ + List columns = config.getColumns(); + String[] removeDuplicate = config.getRemoveDuplicate(); + StringBuilder names = new StringBuilder(); + for(String attr : removeDuplicate){ + for(C8ColumnConfig col :columns){ + if(col.getId().equals(attr)){ + names.append(col.getColName()+";"); + } + } + } + return names.toString(); + } + + /** + * 发送邮件 + * 需要配置相关信息: https://hutool.cn/docs/#/extra/%E9%82%AE%E4%BB%B6%E5%B7%A5%E5%85%B7-MailUtil + * @param config + * @param error + * @param mails + */ + private void sendMail(C8EntityConfig config, String error, ArrayList mails){ + boolean sendEmail = config.isSendEmail(); + if(sendEmail){ + String context = StrUtil.isBlank(error)?"数据导入成功;":error; + MailUtil.send(mails, "数据导入通知", context, false); + } + } + + public List> readAll(MultipartFile file) throws Exception{ + List> result = Lists.newArrayList(); + ImportExcel importExcel = new ImportExcel(file, 0, 0); + Row headRow = importExcel.getRow(importExcel.getHeaderNum()); + for (int i = importExcel.getDataRowNum(); i <= importExcel.getLastDataRowNum(); i++) { + Map map = Maps.newHashMap(); + Row row = importExcel.getRow(i); + boolean isValidRow = false;//是否有效行 + for(int j = 0; j < importExcel.getLastCellNum(); j++){ + String key = headRow.getCell(j).toString(); + if(row==null){ + continue; + } + String value = importExcel.getCellValue(row, j).toString(); + if(StrUtil.isNotBlank(value)){ + isValidRow = true; + } + map.put(key,value); + } + if(isValidRow){ + result.add(map); + } + } + return result; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/C8TestController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/C8TestController.java new file mode 100644 index 0000000..7d9aac4 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/C8TestController.java @@ -0,0 +1,663 @@ +package com.centricsoftware.enhancement.controller.c8; +import com.centricsoftware.enhancement.modules.c8.dto.ExpResultEntity; +import com.fasterxml.jackson.core.type.TypeReference; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.utils.FileUtil; +import com.centricsoftware.commons.utils.JsonUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.OperationResultEntity; +import com.centricsoftware.enhancement.modules.c8.service.C8LoginService; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8OperationService; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; + +import org.springframework.core.io.Resource; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@RestController +@Slf4j +public class C8TestController { + + @Autowired + C8LoginService c8LoginService; + + @Autowired + C8NodeService c8NodeService; + + @Autowired + C8OperationService c8OperationService; + + @Autowired + public C8Feign c8Feign; + + @GetMapping("/login") + public ResEntity login() { + c8LoginService.login(); + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("getFileDirect") + public ResEntity getFileDirect() { + InputStream file = c8NodeService.getFileFromDirectAddr("cf://fv/38898"); + cn.hutool.core.io.FileUtil.writeFromStream(file, "D://11.doc"); + return WebResponse.success(ResCode.SUCCESS); + } + + + @GetMapping("/executeExp") + public ResEntity executeExp() { + log.info("测试List: {}", c8NodeService.queryExpressionList("Hierarchy", "C67079"));//测试List + log.info("测试字符: {}", c8NodeService.queryExpressionResult("C8_CStyle_PurQuantity*C8_CStyle_DJ", "C159609")); + log.info("测试日期: {}", c8NodeService.queryExpressionDate("nowTime()", "C159609"));//测试日期 + Date c159609 = c8NodeService.queryExpressionDate0("nowTime()", "C159609");//返回日期 + log.info("测试时间: {}", c8NodeService.queryExpressionTime("nowTime()", "C159609"));//测试时间 + log.info("测试Map: {}", c8NodeService.queryExpressionMap("Images", "C159609"));//测试Map + log.info("测试List: {}", c8NodeService.queryExpressionList("ProductColors.filter(Active).list()", "C0/REW0132|Style"));//测试List + log.info("测试Enum: {}", c8NodeService.queryExpressionEnumkey("C8_Style_PlanPeriod", "C0/REW0132|Style"));//测试List + String[] strings = c8NodeService.queryExpressionArray("", "");//返回数组 + Object o = c8NodeService.queryExpressionResultObject("", "");//返回Object + String url = "C159609"; + c8NodeService.getImageSmallImage(url); + c8NodeService.getImageThumbnail(url); + c8NodeService.getImageViewable(url); + c8NodeService.getFileFromDirectAddr(""); + c8NodeService.getFileFromNode("", ""); + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("/executeSearch") + public ResEntity executeSearch() { + DepPath build = DepPath.builderXml().xml("\n" + "").addPath("Child:Attributes").addPath("Child:ProductColors").build(); + DepPathResult result = c8NodeService.depPathByXml(build); + System.out.println(result.getValue("Attributes.C8_SA_ABC", "C0/REH0151|Style").getStr()); + System.out.println(result.getValue("Attributes", "C0/CW000001|Colorway").getStr()); + + List strings = c8NodeService.queryByXML("search语句");//返回查询结果所有的URL + String s = c8NodeService.queryFirstByXML("search语句");//返回查询结果第一个URL + c8NodeService.queryFirstByNodeName("", "");//通过BO类型和Node Name查询,并且返回第一个URL + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("/executeSearchUrl") + public ResEntity executeSearchUrl() { + DepPath build = DepPath.builder().addUrl("C0/REH0151|Style").build(); + DepPathResult result = c8NodeService.depPathByUrl(build); + System.out.println(result.getValue("Attributes", "C0/REH0151|Style").getStr()); + System.out.println(c8NodeService.querySingleUrl("C0/REH0151|Style")); + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("/executeOperation") + public ResEntity executeOperation() { + BufferedInputStream inputStream = cn.hutool.core.io.FileUtil.getInputStream("D:\\centric\\11.png"); + MultipartFile file = FileUtil.getMultipartFile(inputStream, "11.png"); + OperationResultEntity operationResultEntity = c8OperationService.uploadFile(file, "C3344", "File"); + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("/publishFile") + public ResEntity publishFile() { + BufferedInputStream inputStream = cn.hutool.core.io.FileUtil.getInputStream("D:\\centric\\11.png"); + MultipartFile file = FileUtil.getMultipartFile(inputStream, "11.png", "FL"); + OperationResultEntity operationResultEntity = c8OperationService.publishFile(file, ""); + System.out.println(operationResultEntity.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + + @GetMapping("/testAAA") + public ResEntity testAAA(@RequestParam String url) { + + DepPath build = DepPath.builder().addUrl(url).build(); + DepPathResult result = c8NodeService.depPathByUrl(build); + Map allNodes = result.getAllNodes(); + System.out.println(allNodes); + + + return WebResponse.success(ResCode.SUCCESS, allNodes); + } + + /** + * 客户 Customer + */ + @GetMapping("/api/customer") + public ResEntity customer(@RequestParam Map map, WebRequest webRequest) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append("") + .append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 尺码 ProductSize + */ + @GetMapping("/api/productSize") + public ResEntity ProductSize(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append("") + .append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 颜色库 ColorSpecification + */ + @GetMapping("/api/colorSpecification") + public ResEntity colorSpecification(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append("") + .append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 配色表 Colorway + */ + @GetMapping("/api/colorway") + public ResEntity colorWay(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 款式 Style + */ + @GetMapping("/api/style") + public ResEntity style(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append("") + .append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 版单进度 DataPackage + */ + @GetMapping("/api/dataPackage") + public ResEntity dataPackage(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 材料 Material + */ + @GetMapping("/api/material") + public ResEntity material(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 款式BOM ApparelBOM + */ + @GetMapping("/api/apparelBOM") + public ResEntity apparelBOM(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 搭配 PartMaterial + */ + @GetMapping("/api/partMaterial") + public ResEntity partMaterial(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 图片 Image + */ + @GetMapping("/api/image") + public ResEntity image(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + xmlBuilder.append(""); + + String viewable = (String) map.get("viewable"); + + if (!StrUtil.isBlank(viewable)) { + xmlBuilder.append(String.format( + "", + viewable + )); + } + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 层级分类 Category + */ + @GetMapping("/api/category") + public ResEntity category(@RequestParam Map map) { + StringBuilder xmlBuilder = new StringBuilder(); + + String category = (String) map.get("category"); // 客户: Category1, 大类: Category2 + + ArrayList categoryArray = new ArrayList<>(); + categoryArray.add("Category1"); + categoryArray.add("Category2"); + + if (!StrUtil.isBlank(category)) { + xmlBuilder.append(String.format( + "", + category + )); + } else if (!categoryArray.contains(category)) { + return ResEntity.builder().code(400).msg("category 不在可选范围!!").build(); + } else { + return ResEntity.builder().code(400).msg("category 必填!!").build(); + } + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 根据url码获取信息 + */ + @GetMapping("/searchByUrl") + public ResEntity searchByUrl(@RequestParam Map map) { + String url = (String) map.get("url"); + JSONObject jsonObject = nodeInfoByUrl(url); + return WebResponse.success(ResCode.SUCCESS, jsonObject); + } + + public JSONObject nodeInfoByUrl(String url) { + DepPath build = DepPath.builder().addUrl(url).build(); + DepPathResult result = c8NodeService.depPathByUrl(build); + return result.getAllNodes().get(url); + } + + /** + * 根据年份获取每年style的图片 + */ + @GetMapping("/api/getStyleImageBySeason") + public ResEntity getStyleImageBySeason(@RequestParam Map map) { + String parentSeason = (String) map.get("parentSeason"); + if (parentSeason == null || parentSeason.isEmpty()) { + parentSeason = ""; // 设置默认值,避免拼接时出现 NullPointerException + } + String xmlBuilder = "" + + "" + + ""; + + DepPath depPath = DepPath.builderXml() + .xml(xmlBuilder) + .build(); + + DepPathResult result = c8NodeService.depPathByXml(depPath); + JSONObject jsonResult = JSONUtil.parseObj(result); + + List nodes = Collections.emptyList(); + if (jsonResult.containsKey("nodes") && jsonResult.getJSONArray("nodes") != null) { + nodes = jsonResult.getJSONArray("nodes"); + } + + ArrayList imageUrls = new ArrayList<>(); + + for (Object node : nodes) { + extractImageUrlsFromNode(node, imageUrls); + } + + int size = imageUrls.size(); + HashMap resultMap = new HashMap<>(); + resultMap.put("size", size); + resultMap.put("imageUrls", imageUrls); + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + private void extractImageUrlsFromNode(Object node, List imageUrls) { + JSONObject nodeJson = JSONUtil.parseObj(node); + JSONObject imagesDict = nodeJson.getJSONObject("Images"); + + if (imagesDict != null && !imagesDict.isEmpty()) { + imageUrls.addAll(imagesDict.values().stream() + .filter(value -> value instanceof String) + .map(value -> (String) value) + .collect(Collectors.toList())); + } + } + + /** + * 处理图片文件流 + */ + + @GetMapping("/api/imageStreamViewable") + public ResponseEntity imageStreamViewable(@RequestParam("imageUrl") String imageUrl) { + return processImageStream(imageUrl, c8NodeService::getImageViewable, "_viewable"); + } + + @GetMapping("/api/imageStreamSmall") + public ResponseEntity imageStreamSmall(@RequestParam("imageUrl") String imageUrl) { + return processImageStream(imageUrl, c8NodeService::getImageSmallImage, "_small"); + } + + @GetMapping("/api/imageStreamThumbnail") + public ResponseEntity imageStreamThumbnail(@RequestParam("imageUrl") String imageUrl) { + return processImageStream(imageUrl, c8NodeService::getImageThumbnail, "_thumbnail"); + } + + private ResponseEntity processImageStream(String imageUrl, Function imageFetcher, String infix) { + if (imageUrl == null || imageUrl.isEmpty()) { + return ResponseEntity.badRequest().body(null); + } + + List imageExtensions = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "ico", "svg", "webp"); + + try { + JSONObject imageInfo = nodeInfoByUrl(imageUrl); + if (imageInfo == null || !imageInfo.containsKey("Node Name")) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + + String imageName = imageInfo.getStr("Node Name"); + String extension = ".jpg"; + String fileName = imageUrl + infix + extension; + // 如果图片名称包含.且包含扩展名在imageExtensions内,则使用原始扩展名 + if (imageName != null && imageName.contains(".") && imageExtensions.contains(imageName.substring(imageName.lastIndexOf(".") + 1))) { + extension = "." + imageName.substring(imageName.lastIndexOf(".") + 1); + fileName = imageUrl + infix + extension; + } + + InputStream imageStream = imageFetcher.apply(imageUrl); + if (imageStream == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + + InputStreamResource imageResource = new InputStreamResource(imageStream); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(imageResource); + } catch (Exception e) { + log.error("Exception: {}", imageUrl, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /** + * 通过文件柜地址获取文件的流 + */ + @GetMapping("/api/fileStreamByFileAddr") + public ResponseEntity fileStreamByFileAddr(@RequestParam Map map) { + String fileAddrUrl = (String) map.get("fileAddrUrl"); + if (fileAddrUrl == null || fileAddrUrl.isEmpty()) { + return ResponseEntity.badRequest().body(null); + } + String fileName = (String) map.get("fileName"); + if (fileName == null || fileName.isEmpty()) { + return ResponseEntity.badRequest().body(null); + } + try { + InputStream fileStream = c8NodeService.getFileFromDirectAddr(fileAddrUrl); + InputStreamResource fileResource = new InputStreamResource(fileStream); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(fileResource); + + } catch (Exception e) { + log.error("Exception: {}", fileAddrUrl, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + /** + * 枚举列表 EnumList + */ + @GetMapping("/api/enumList") + public ResEntity enumList(@RequestParam Map map) { + String enumListId = (String) map.get("enumListId"); + StringBuilder xmlBuilder = new StringBuilder(); + // 如果有enumListId,则根据enumListId查询枚举列表 + if (StrUtil.isNotBlank(enumListId)) { + xmlBuilder.append("") + .append(""); + } else { + // 如果没有enumListId,则根据enumListId查询枚举列表 + xmlBuilder.append(""); + } + HashMap resultMap = getResEntity(map, xmlBuilder); + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 枚举值 EnumValue + */ + @GetMapping("/api/enumValue") + public ResEntity enumValue(@RequestParam Map map) { + String enumValueId = (String) map.get("enumValueId"); + StringBuilder xmlBuilder = new StringBuilder(); + + if (StrUtil.isNotBlank(enumValueId)) { + xmlBuilder.append("") + .append(""); + } else { + xmlBuilder.append(""); + } + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 按类型通用查询接口 + */ + @GetMapping("/api/{typeName}") + public ResEntity plmQueryByType(@RequestParam Map map, @PathVariable String typeName) { + log.info("PathVariable: {}", typeName); + log.info("RequestParam: {}", map); + + StringBuilder xmlBuilder = new StringBuilder(); + + if (!StrUtil.isBlank(typeName)) { + xmlBuilder.append(String.format( + "", + typeName + )); + } else { + return ResEntity.builder().code(400).msg("typeName 必填!!").build(); + } + + HashMap resultMap = getResEntity(map, xmlBuilder); + + return WebResponse.success(ResCode.SUCCESS, resultMap); + } + + /** + * 统一处理,获取返回结果 + */ + public HashMap getResEntity(@RequestParam Map map, StringBuilder xmlBuilder) { + // 时间戳字符串 + String modifiedTimeStart = (String) map.get("modifiedTimeStart"); + String modifiedTimeEnd = (String) map.get("modifiedTimeEnd"); + + int page = Convert.toInt(map.get("page"), 1); + int pageSize = Convert.toInt(map.get("pageSize"), 10); + + String sequence = Convert.toStr(map.get("sequence"), "DESC"); + String nodeName = (String) map.get("nodeName"); + String parent = (String) map.get("parent"); + + String sValue = (String) map.get("sValue"); + String rValue = (String) map.get("rValue"); + + ObjectMapper mapper = new ObjectMapper(); + + try { + Map sValueMap = mapper.readValue(sValue, new TypeReference>() {}); + + for (Map.Entry entry : sValueMap.entrySet()) { + xmlBuilder.append(String.format( + "", + entry.getKey(), + entry.getValue() + )); + } + + Map rValueMap = mapper.readValue(rValue, new TypeReference>() {}); + for (Map.Entry entry : rValueMap.entrySet()) { + xmlBuilder.append(String.format( + "", + entry.getKey(), + entry.getValue() + )); + } + } catch (Exception e) { + log.error("Error parsing JSON: {}", e.getMessage()); + } + + int start = (page - 1) * pageSize + 1; + int end = pageSize * page; + + xmlBuilder.append(String.format("", sequence)); + + if (!StrUtil.isBlank(modifiedTimeStart)) { + xmlBuilder.append(String.format( + "", + modifiedTimeStart + )); + } + if (!StrUtil.isBlank(modifiedTimeEnd)) { + xmlBuilder.append(String.format( + "", + modifiedTimeEnd + )); + } + + if (!StrUtil.isBlank(nodeName)) { + xmlBuilder.append(String.format( + "", + nodeName + )); + } + + if (!StrUtil.isBlank(parent)) { + xmlBuilder.append(String.format( + "", + parent + )); + } + + System.out.println("xmlBuilder: " + xmlBuilder.toString()); + + DepPath depPath = DepPath.builderXml() + .xml(xmlBuilder.toString()) + .build(); + + DepPathResult preResult = c8NodeService.depPathByXml(depPath, 1, 1); + + DepPathResult result = c8NodeService.depPathByXml(depPath, start, end); + + + int total = 0; + int curNum = 0; + List nodes = Collections.emptyList(); + + JSONObject preJsonResult = JSONUtil.parseObj(preResult); + if (preJsonResult.getJSONArray("nodes") != null) { + total = preJsonResult.getJSONArray("completeResultRefs").size(); + } + + JSONObject jsonResult = JSONUtil.parseObj(result); + + if (jsonResult.getJSONArray("nodes") != null) { + nodes = jsonResult.getJSONArray("nodes"); + curNum = nodes.size(); + } + String nodeStr = JsonUtil.toJSONString(nodes); + + HashMap resultMap = new HashMap<>(); + resultMap.put("total", total); + resultMap.put("page", page); + resultMap.put("pageSize", pageSize); + resultMap.put("curNum", curNum); + resultMap.put("items", JsonUtil.parseObject(nodeStr, List.class)); + + return resultMap; + } + + @GetMapping("/api/expressionResult") + public ResEntity expressionResult(@RequestParam Map map) { + String expression = (String) map.get("expression"); + String url = (String) map.get("url"); + Object o = c8NodeService.queryExpressionResultObject(expression, url);//返回Object + if (o == null) { + return WebResponse.success(ResCode.SUCCESS, new ArrayList<>()); + } + String input = o.toString(); + // 1. 去除方括号 + String cleaned = input.substring(1, input.length() - 1); + + // 2. 按逗号分割并转为 List + List list = new ArrayList<>(Arrays.asList(cleaned.split(",\\s*"))); + + return WebResponse.success(ResCode.SUCCESS, list); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/FiledPermissionsController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/FiledPermissionsController.java new file mode 100644 index 0000000..151c65a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/c8/FiledPermissionsController.java @@ -0,0 +1,33 @@ +package com.centricsoftware.enhancement.controller.c8; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.service.lui.FieldPermissionsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * description: + * Date: 2023/3/17 11:23 + */ + +@RestController +@Slf4j +public class FiledPermissionsController { + + @Resource + FieldPermissionsService fieldPermissionsService; + + @GetMapping("createAttributeForFieldPermissions") + public ResEntity createAttributeForFieldPermissions(){ + fieldPermissionsService.createLookupItemSubtype(); + fieldPermissionsService.createLookupItemFiled(); + fieldPermissionsService.createView(); + return WebResponse.success(ResCode.SUCCESS,"请大更新系统!"); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogController.java new file mode 100644 index 0000000..6a3aebd --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogController.java @@ -0,0 +1,134 @@ +package com.centricsoftware.enhancement.controller.log; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.utils.PageUtil; +import com.centricsoftware.config.entity.EsProperties; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.dto.log.LogDto; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.dto.log.QueryPlmLogReq; +import com.centricsoftware.enhancement.modules.c8.service.es.LogElasticsearchService; +import com.centricsoftware.enhancement.service.log.impl.CallBackLogServiceImpl; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import com.google.common.collect.Lists; +import org.springframework.beans.factory.annotation.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("log") +public class LogController { + + @Resource + private LogServiceImpl logService; + @Resource + private CallBackLogServiceImpl callBackLogService; + + @Resource + private LogElasticsearchService logElasticsearchService; + + @Resource + EsProperties esProperties; + + @Value("${web.logging.path}") + private String filePath; +// +// @RequestMapping("page") +// public ResEntity page(@RequestParam("current") int current, @RequestParam("size") int size, @RequestBody FeignLogDto dto) { +// Page page = logService.page(current, size, dto); +// return WebResponse.autoResponse(page, null); +// } + + /** + * 初始化查询枚举 + * + * @author liaochangjiang + * @since 2021-12-28 11:01:48 + */ + @GetMapping("init") + public ResEntity init() throws IOException { + List logNames = logElasticsearchService.listLogNames(esProperties.getBrand()); + return WebResponse.success(ResCode.SUCCESS,logNames); + } + + /** + * 查询表格数据 + * + * @author liaochangjiang + * @since 2021-12-28 11:03:19 + */ + @PostMapping("queryData") + public ResEntity queryData(@RequestBody QueryPlmLogReq req) throws IOException { + req.setBrand(esProperties.getBrand()); + Page page = PageUtil.buildPageParam(req, PlmLog.class); + logElasticsearchService.queryLogs(page, req); + return WebResponse.success(ResCode.SUCCESS,page); + } + + /** + * 获取日志目录下的所有文件名字 + * @author liaochangjiang + * @since 2024-10-21 23:11 + */ + @ResponseBody + @RequestMapping("/getFile") + public ResEntity getFile(){ + File total = new File(filePath); + File[] files = total.listFiles(); + List result = Lists.newArrayList(); + if (files != null) { + for (File fileArr : files) { + File[] fileList = fileArr.listFiles(); + LogDto logDto = new LogDto(); + logDto.setLabel(fileArr.getName()); + List fileLogDtoList = Lists.newArrayList(); + if (fileList != null) { + for (File file : fileList) { + LogDto fileLogDto = new LogDto(); + fileLogDto.setLabel(file.getName()); + fileLogDtoList.add(fileLogDto); + } + logDto.setChildren(fileLogDtoList); + result.add(logDto); + } + } + } + return WebResponse.success(ResCode.SUCCESS,result); + } + + /** + * 根据文件名字获取文件数据 + * @author liaochangjiang + * @since 2024-10-22 9:26 + */ + @ResponseBody + @RequestMapping("/getFileByName") + public String getFileByName(String name) { + File file = new File(filePath + "/" + name); + StringBuilder result = new StringBuilder(); + try{ + // 构造一个BufferedReader类来读取文件 + BufferedReader br = new BufferedReader(new FileReader(file)); + String s; + // 使用readLine方法,一次读一行 + while((s = br.readLine())!=null){ + result.append(System.lineSeparator()).append(s); + } + br.close(); + }catch(Exception e){ + e.printStackTrace(); + } + return result.toString(); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogForRelationshipDBController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogForRelationshipDBController.java new file mode 100644 index 0000000..c411b87 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/log/LogForRelationshipDBController.java @@ -0,0 +1,89 @@ +package com.centricsoftware.enhancement.controller.log; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import com.centricsoftware.commons.utils.PageUtil; +import com.centricsoftware.config.entity.EsProperties; +import com.centricsoftware.enhancement.dto.PlmReqDto; +import com.centricsoftware.enhancement.dto.log.*; +import com.centricsoftware.enhancement.modules.c8.service.es.LogElasticsearchService; +import com.centricsoftware.enhancement.service.log.impl.CallBackLogServiceImpl; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("log/db") +public class LogForRelationshipDBController { + + @Resource + private LogServiceImpl logService; + @Resource + private CallBackLogServiceImpl callBackLogService; + + @Resource + private LogElasticsearchService logElasticsearchService; + + @Resource + EsProperties esProperties; + + @Value("${web.logging.path}") + private String filePath; + +// @RequestMapping("page") +// public ResEntity page(@RequestParam("current") int current, @RequestParam("size") int size, @RequestBody FeignLogDto dto) { +// Page page = logService.page(current, size, dto); +// return WebResponse.autoResponse(page, null); +// } + + @PostMapping("queryData") + public ResEntity queryData(@RequestBody FeignLogDto req) throws IOException { + int pageNum = req.getPageNum().intValue(); + int pageSize = req.getPageSize().intValue(); + Page page = logService.page(pageNum, pageSize, req); + return WebResponse.autoResponse(page, null); + } + + /** + * 初始化查询枚举 + * + * @author liaochangjiang + * @since 2021-12-28 11:01:48 + */ + @GetMapping("init") + public ResEntity init() throws IOException { + List logNames = Lists.newArrayList("PLM"); + return WebResponse.success(ResCode.SUCCESS,logNames); + } + + /** + * 主动调用plm日志接口,用于异步接口日志调用 + * @param param feignLog日志DTO + */ + @RequestMapping("/receive/msg") + @ResponseBody + public ResEntity receiveMsg(@RequestBody PlmReqDto param) { + try { + callBackLogService.saveLog(param.getParam()); + return WebResponse.success(ResCode.SUCCESS); + } catch (BaseException e) { + return WebResponse.failure(ResCode.ERROR, e.getData()); + } catch (Exception e) { + return WebResponse.failure(e.getMessage()); + } + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/controller/specification/SpecificationDataSheetController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/specification/SpecificationDataSheetController.java new file mode 100644 index 0000000..a093935 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/controller/specification/SpecificationDataSheetController.java @@ -0,0 +1,57 @@ +package com.centricsoftware.enhancement.controller.specification; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.service.specification.SpecificationDataSheetService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * description:工艺单 + * Author: Xulin.Xie + * Date: 2022/7/6 15:22 + */ +@Slf4j +@Controller +@RequestMapping("SpecificationDataSheet") +public class SpecificationDataSheetController { + + final + private SpecificationDataSheetService specificationDataSheetService; + private final C8NodeService c8NodeService; + + + /** + * localhost:8088/plmservice/SpecificationDataSheet/ResetSDSection?currentVersion=C102687 + *从工艺单中新建:刷新所有行 + */ + @ResponseBody + @RequestMapping("ResetSDSection") + public ResEntity urls(String currentVersion) { + String[] items = c8NodeService.queryExpressionArray("Items",currentVersion); + String error = specificationDataSheetService.resetSection(currentVersion,items); + return WebResponse.success(error); + } + + /** + * 从工艺单中新建:刷新新建的行 + * @param urls 工艺单明细 + * @param currentVersion 工艺单版本 + * @return + */ + @ResponseBody + @RequestMapping("ResetSection") + public ResEntity urls(String[] urls,String currentVersion) { + String error = specificationDataSheetService.resetSection(currentVersion,urls); + return WebResponse.success(""); + } + + public SpecificationDataSheetController(SpecificationDataSheetService specificationDataSheetService, C8NodeService c8NodeService) { + this.specificationDataSheetService = specificationDataSheetService; + this.c8NodeService = c8NodeService; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/AnnoImportLineError.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/AnnoImportLineError.java new file mode 100644 index 0000000..b1410fc --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/AnnoImportLineError.java @@ -0,0 +1,22 @@ +package com.centricsoftware.enhancement.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class AnnoImportLineError { + + private Object lineData; + + private String error; + + private boolean success; + + private int seq; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/PlmReqDto.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/PlmReqDto.java new file mode 100644 index 0000000..e98a80d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/PlmReqDto.java @@ -0,0 +1,19 @@ +package com.centricsoftware.enhancement.dto; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 传输接口的接受基类,所有传入的对象都需要用此对象包含起来 + * 如 : @RequestBody PlmDto param + * + * @param + */ +@Data +public class PlmReqDto implements Serializable { + @JsonProperty("param") + private T param; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/UploadImagesEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/UploadImagesEntity.java new file mode 100644 index 0000000..2c9cd72 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/UploadImagesEntity.java @@ -0,0 +1,19 @@ +package com.centricsoftware.enhancement.dto; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Data +public class UploadImagesEntity { + private String smallImage; + private String viewable; + private String name; + private String imageUrl; + private String url; + private String seq; + private String alt; + private String pid; + private String thumb; + private String src; + private MultipartFile raw; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/OkHttpCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/OkHttpCache.java new file mode 100644 index 0000000..7819420 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/OkHttpCache.java @@ -0,0 +1,8 @@ +package com.centricsoftware.enhancement.dto.cache; + +import org.springframework.stereotype.Component; + +@Component +public class OkHttpCache { + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/ThreadLocalCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/ThreadLocalCache.java new file mode 100644 index 0000000..5a0943b --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/cache/ThreadLocalCache.java @@ -0,0 +1,17 @@ +package com.centricsoftware.enhancement.dto.cache; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/8/4 14:02 + */ +@Slf4j +@Component +public class ThreadLocalCache { + + public ThreadLocal personalCookie = new ThreadLocal<>(); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/excel/ExcelAroundAOP.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/excel/ExcelAroundAOP.java new file mode 100644 index 0000000..1991913 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/excel/ExcelAroundAOP.java @@ -0,0 +1,39 @@ +package com.centricsoftware.enhancement.dto.excel; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +/** + * description:基于yml导入时,如果有设置前置通知,返回的实体类 + * Author: Xulin.Xie + * Date: 2023/2/1 13:29 + */ +@Data +@Builder +public class ExcelAroundAOP { + + /** + * 是否正确 + * false时,将把error返回给用户 + * 如果是后置通知,false将不执行operation保存操作 + */ + @Builder.Default + private boolean success = true; + + @Builder.Default + private String error = ""; + + /** + * 后置通知,如果校验成功,拼接的operation + */ + @Builder.Default + private String operation=""; + + /** + * 如果itemMap有值的话,会将键值对加载到行数据内,可供yml使用 + */ + private Map itemMap; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogDto.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogDto.java new file mode 100644 index 0000000..f4e4906 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogDto.java @@ -0,0 +1,133 @@ +package com.centricsoftware.enhancement.dto.log; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.Date; + +import static org.apache.ibatis.type.JdbcType.BIT; + + +@Slf4j +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName("C8_PS_Operation_Log") +public class FeignLogDto extends FeignLogReqDto{ + + /** + * 接口名称:长度50 + */ + @TableField(value = "name", condition = SqlCondition.LIKE) + private String name; + + /** + * 请求地址:长度150 + */ + private String url; + + /** + * key:长度50 + */ + @TableField(value = "data_key") + private String dataKey; + + /** + * 请求主机:长度50 + */ + private String host; + + /** + * 请求路径:长度100 + */ + private String path; + + /** + * 预留字段,ES版本的日志可能有用:长度50 + */ + private String brand; + + /** + * 发起时间 + */ + @TableField(value = "send_date", condition = "%s>#{%s}") + private Date startTime; + + /** + * 发起时间 + */ + @TableField(value = "end_date", condition = "%s>#{%s}") + private Date endTime; + + /** + * 耗时 + */ + @TableField("cost_ms") + private Long costMs; + + /** + * 响应http状态码:长度100 + */ + @TableField("return_code") + private String returnCode; + + /** + * 响应内容:text + */ + @TableField("response") + private String returnMsg; + + /** + * 请求内容:text + */ + private String debug; + + @TableId(value = "id", type = IdType.AUTO)//指定自增策略 MYSQL + private Integer id; + + /** + * 异步请求,对应的原ID是多少 + */ + private String refid; // 默认为空 + + /** + * 接口方式 post、get... + * 长度10 + */ + private String method; + + /** + * 请求头:长度1000 + */ + private String header; + + /** + * 查询条件:长度1000 + */ + private String param; + + @TableField(value = "success",jdbcType=BIT) + private Boolean success; + + /** + * 日志类型:长度20 + * 接口日志;操作日志 + */ + @TableField(value = "log_type") + private String logType; + + /** + * 响应code + */ + @TableField(exist=false) + private String code; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogReqDto.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogReqDto.java new file mode 100644 index 0000000..f9408f0 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/FeignLogReqDto.java @@ -0,0 +1,46 @@ +package com.centricsoftware.enhancement.dto.log; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; + +import static org.apache.ibatis.type.JdbcType.BIT; + + +@Data +public class FeignLogReqDto{ + + @TableField(exist=false) + private Long pageNum = 1L; + + @TableField(exist=false) + private Long pageSize = 10L; + + @TableField(exist=false) + private String startTimeBegin; + + @TableField(exist=false) + private String startTimeEnd; + + @TableField(exist=false) + private Integer successStr; + + /** + * 请求报文中key字段 + */ + @TableField(exist=false) + private String requestId; + + /** + * 响应报文中状态字段 + */ + @TableField(exist=false) + private String responseId; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/LogDto.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/LogDto.java new file mode 100644 index 0000000..2a2b6c0 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/LogDto.java @@ -0,0 +1,11 @@ +package com.centricsoftware.enhancement.dto.log; + +import lombok.Data; + +import java.util.List; + +@Data +public class LogDto { + private String label; + private List children; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/PlmLog.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/PlmLog.java new file mode 100644 index 0000000..0cf65ee --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/PlmLog.java @@ -0,0 +1,43 @@ +package com.centricsoftware.enhancement.dto.log; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * plm日志 + * + */ +@Data +@Accessors(chain = true) +public class PlmLog implements Serializable { + + private String path; + + private String name; + + private String brand; + @JsonProperty("@timestamp") + private LocalDateTime startTime; + + private LocalDateTime endTime; + + private boolean success; + + private Long costMs; + + private String host; + + private String param; + + private String returnCode; + + private String returnMsg; + + private String returnText; + + private String debug; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/QueryPlmLogReq.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/QueryPlmLogReq.java new file mode 100644 index 0000000..0fc94ad --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/log/QueryPlmLogReq.java @@ -0,0 +1,48 @@ +package com.centricsoftware.enhancement.dto.log; + +import com.centricsoftware.commons.dto.PlmPageReqVo; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * 查询日志请求 + * + */ +@Data +@EqualsAndHashCode() +@ToString(callSuper = true) +@Accessors(chain = true) +public class QueryPlmLogReq extends PlmPageReqVo { + + private String path; + + private String name; + + private String brand; + + private Boolean success; + + private String host; + + private String param; + + private Integer returnCode; + + private String returnText; + + private String startTimeBegin; + + private String startTimeEnd; + + private String endTimeBegin; + + private String endTimeEnd; + + private String debug; + /** + * tid + */ + private String tid; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridOptions.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridOptions.java new file mode 100644 index 0000000..7ca7f3e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridOptions.java @@ -0,0 +1,120 @@ +package com.centricsoftware.enhancement.dto.table; + +import com.centricsoftware.enhancement.dto.table.gridconfig.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * description: 前端工程通用配置类 + * Author: Xulin.Xie + * Date: 2022/8/11 9:57 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class GridOptions { + + /** + * 边框线配置 + */ + @Builder.Default + private boolean border = true; + + /** + * 当表头内容过长时显示为省略号 + */ + @Builder.Default + private boolean showHeaderOverflow = true; + + /** + * 当内容过长时显示为省略号 + */ + @Builder.Default + private boolean showOverflow = true; + + /** + * 是否记录操作记录,左上角小红点显示 + */ + @Builder.Default + private boolean keepSource = true; + + /** + * 是否记录操作记录,左上角小红点显示 + */ + @Builder.Default + private String id = "full_edit_2"; + + /** + * 表格高度设定,目前已经采用mounted去做动态更新 + */ + @Builder.Default + private int height = 0; + + /** + * 行配置项 + */ + @Builder.Default + private RowConfig rowConfig = new RowConfig(); + + /** + * 列配置项 + */ + @Builder.Default + private ColumnBaseConfig columnConfig = new ColumnBaseConfig(); + + /** + * 打印配置项 + */ + @Builder.Default + private PrintConfig printConfig = new PrintConfig(); + + /** + * 排序配置项 + */ + @Builder.Default + private SortConfig sortConfig = new SortConfig(); + + /** + * 分页配置项 + */ + @Builder.Default + private PagerConfig pagerConfig = new PagerConfig(); + + // 工具栏配置 + @Builder.Default + private ToolbarConfig toolbarConfig = new ToolbarConfig(); + + /** + * 具体表格配置 + */ + @Builder.Default + private List columns = new ArrayList<>(); + + /** + * 筛选配置项 + */ + @Builder.Default + private FilterConfig filterConfig = new FilterConfig(); + + /** + * 表单配置项;最前面的筛选 + */ + private FormConfig formConfig; + + /** + * 右侧按钮 + */ + @Builder.Default + private List rightBtnConfigs = new ArrayList<>(); + + @Builder.Default + private int rightBtnWidth = 160; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponse.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponse.java new file mode 100644 index 0000000..310f55f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponse.java @@ -0,0 +1,28 @@ +package com.centricsoftware.enhancement.dto.table; + + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 返回信息实体类 + * @author ZhengGong + * @date 2020/4/16 + */ +@Data +@Builder +public class GridResponse implements Serializable { + + private long total; + /** + * 返回对象 + */ + private List records; + /** + * 是否成功 + */ + private boolean success ; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponseData.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponseData.java new file mode 100644 index 0000000..3c28dbe --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/GridResponseData.java @@ -0,0 +1,20 @@ +package com.centricsoftware.enhancement.dto.table; + + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * 返回信息实体类 + * @author ZhengGong + * @date 2020/4/16 + */ +@Data +@Builder +public class GridResponseData implements Serializable { + + private GridResponse data; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnBaseConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnBaseConfig.java new file mode 100644 index 0000000..8a049cc --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnBaseConfig.java @@ -0,0 +1,21 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/8/12 14:59 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ColumnBaseConfig { + @Builder.Default + private boolean resizable = true; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnConfig.java new file mode 100644 index 0000000..6a5fb87 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ColumnConfig.java @@ -0,0 +1,102 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 列配置 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ColumnConfig { + + /** + * 字段 ID + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String field; + + /** + * 第一列可以展示为复选框:checkbox + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String type; + + /** + * 固定列:"left" + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String fixed; + + /** + * 标题 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String title; + + /** + * 排序 + */ + @Builder.Default + private boolean sortable = true; + + /** + * 宽度 + */ + @Builder.Default + private int width = 150; + + /** + * 自定义转换 + * demo: + * 1、this.format.formatBoolean + * 2、this.format.formatDate + */ + @Builder.Default + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String formatter = ""; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String slots; + + /** + * 过滤项:默认文本过滤 + * demo1: 下拉 + * [ + * { label: "是", value: "Y" }, + * { label: "否", value: "N" } + * ] + * demo2: 数值 + * [{ data: { min: "0", max: "0" } }] + * demo3: 文本 + * [{ data: ”“ }] + * demo4: 时间 + * { data: { value: [], valueFormat: "yyyy-MM-dd HH:mm:ss" } } + * 支持:时间、文本、数值、下拉 + */ + @Builder.Default + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Object filters = new cn.hutool.json.JSON[]{JSONUtil.parse("{ data:\"\"}")}; + + /** + * 过滤项:默认文本过滤 + * 列上过滤的组件、支持:时间、文本、数值、下拉 + * {name: "FilterInput"} + * FilterTime、FilterComplex、FilterContent、FilterSelect、FilterInput + */ + @Builder.Default + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private JSONObject filterRender = JSONUtil.parseObj("{name: \"FilterInput\"}"); + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ExportConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ExportConfig.java new file mode 100644 index 0000000..71f4da9 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ExportConfig.java @@ -0,0 +1,34 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:导出配置 + * Author: Xulin.Xie + * Date: 2022/8/11 13:47 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ExportConfig { + + /** + * 开启远程导出 + */ + private boolean remote = false; + + private boolean useStyle = true; + + private String exportMethod ; + + private String types; + + private boolean resize = true; + + private String modes; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FilterConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FilterConfig.java new file mode 100644 index 0000000..a19f8c1 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FilterConfig.java @@ -0,0 +1,32 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 筛选配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FilterConfig { + + /** + * 去掉筛选的图标 + */ + @Builder.Default + private boolean showIcon = true; + + /** + * 是否启动服务端排序 + */ + @Builder.Default + private boolean remote = false; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormConfig.java new file mode 100644 index 0000000..9034d85 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormConfig.java @@ -0,0 +1,46 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * description: 表单配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:32 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FormConfig { + + /** + * 所有项的标题宽度 + */ + @Builder.Default + private int titleWidth = 80; + + /** + * 所有项的标题对齐方式 + */ + @Builder.Default + private String titleAlign = "right"; + + /** + * 尺寸 + */ + @Builder.Default + private String size = "small"; + + /** + * 列配置 + */ + private List items; + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormItemConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormItemConfig.java new file mode 100644 index 0000000..1a6f051 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FormItemConfig.java @@ -0,0 +1,65 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import cn.hutool.core.text.StrFormatter; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 列配置 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FormItemConfig { + + /** + * 字段名,点击提交的时候会根据这个id去做字段传递,或者可以直接通过this.$refs.xGrid.formdata + */ + private String field; + + /** + * 字段名,点击提交的时候会根据这个id去做字段传递, + */ + private String title; + + /** + * 过滤提醒信息: + * icon : el-icon-warning + */ + @Builder.Default + private String titlePrefix =""; + + public void setTitlePrefix(String message, String icon){ + this.titlePrefix= StrFormatter.format("{message:{},icon:\"{}\"}",message,icon); + } + + /** + * 是否显示标题冒号 + */ + @Builder.Default + private boolean titleColon = true; + + /** + * 是否显示必填字段的红色星号 + */ + @Builder.Default + private boolean titleAsterisk = false; + + /** + * 所有项的栅格占据的列数(共 24 分栏) + */ + @Builder.Default + private int span = 4; + + /** + * 项渲染器配置项 + */ + private FromItemRender itemRender; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FromItemRender.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FromItemRender.java new file mode 100644 index 0000000..672a580 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/FromItemRender.java @@ -0,0 +1,74 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/8/11 13:10 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FromItemRender { + + /** + * input, textarea, select, $input, $textarea, $select, $button, $buttons, $radio, $checkbox, $switch + */ + @Builder.Default + private String name = "$input"; + + /** + * 如下方选择的,如果是下拉选择的,则需要配置options选项 + * [ + * { value: "C8_Phase:002", label: "首样后" }, + * { value: "C8_Phase:003", label: "预览后" } + * ] + */ + @Builder.Default + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List options = new ArrayList<>(); + + // 属性自定义的数据 + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private JSONObject props; + + @Builder.Default + @JsonIgnore + private boolean clearable = true; + + @Builder.Default + @JsonIgnore + private String placeholder = ""; + + @Builder.Default + @JsonIgnore + private boolean filterable = true; + + public JSONObject getProps(){ + if(props==null||props.isEmpty()){ + return JSONUtil.parseObj(StrFormatter.format("{clearable:{},placeholder:\"{}\"},filterable:{}}",clearable,placeholder,filterable)); + }else{ + return props; + } + } + + public void setOptions(String value, String label){ + String format = StrFormatter.format("{value:\"{}\",label:\"{}\"}}", value, label); + options.add(format); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PagerConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PagerConfig.java new file mode 100644 index 0000000..9df758a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PagerConfig.java @@ -0,0 +1,34 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 分页配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PagerConfig { + + /** + * 是否启动服务端排序 + */ + private String[] layouts = {"PrevJump", "PrevPage", "Number", "NextPage", "NextJump", "Sizes", "FullJump", "Total"}; + + private int pageSize = 200; + + /** + * 每页大小选项列表 + */ + private int[] pageSizes = {10, 50, 100, 200, 500, 5000}; + + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PrintConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PrintConfig.java new file mode 100644 index 0000000..cfdf09f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/PrintConfig.java @@ -0,0 +1,26 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * description:GridOptions 打印配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PrintConfig { + + /** + * 执行默认展示需要打印的列 + */ + private List columns; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RightBtnConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RightBtnConfig.java new file mode 100644 index 0000000..595c8b6 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RightBtnConfig.java @@ -0,0 +1,31 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 列配置 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RightBtnConfig { + + /** + * 字段 ID + */ + private String title; + + @Builder.Default + private String status = "primary"; + + private String content; + + private String clickUrl; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RowConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RowConfig.java new file mode 100644 index 0000000..3255e49 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/RowConfig.java @@ -0,0 +1,24 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 行配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RowConfig { + + /** + * 鼠标悬浮,是否高亮 + */ + private boolean isHover = true; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/SortConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/SortConfig.java new file mode 100644 index 0000000..d9a15ae --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/SortConfig.java @@ -0,0 +1,29 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:GridOptions 排序配置项 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SortConfig { + + /** + * default(点击按钮触发), cell(点击表头触发) + */ + private String trigger = "cell"; + + /** + * 是否启动服务端排序 + */ + private boolean remote = false; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolbarConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolbarConfig.java new file mode 100644 index 0000000..e6beb85 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolbarConfig.java @@ -0,0 +1,69 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * description:工具栏 + * Author: Xulin.Xie + * Date: 2022/8/11 13:26 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolbarConfig { + + /** + * 按钮大小 + */ + private String size = "small"; + + /** + * 搜索栏下的按钮,暂时不封装;这边支持单个按钮平铺和下拉列表按钮 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String buttons; + + /** + * 自定义的tool的按钮 + */ + private List tools = ToolsConfig.getClearBtn(); + + /** + * 刷新 + */ + private boolean refresh = true; + + /** + * 上传 + */ + @JsonProperty("import") + private boolean importFile = false; + /** + * 导出 + */ + private boolean export = true; + + /** + * 打印 + */ + private boolean print = true; + + /** + * 是否允许最大化显示 + */ + private boolean zoom = true; + + /** + * 自定义列配置 + */ + private boolean custom = true; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolsConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolsConfig.java new file mode 100644 index 0000000..07acf93 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/dto/table/gridconfig/ToolsConfig.java @@ -0,0 +1,54 @@ +package com.centricsoftware.enhancement.dto.table.gridconfig; + +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * description:GridOptions 列配置 + * Author: Xulin.Xie + * Date: 2022/8/11 10:11 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ToolsConfig { + + public static List getClearBtn(){ + ArrayList objects = Lists.newArrayList(); + ToolsConfig build = ToolsConfig.builder().code("clearFilter").text("清空筛选") + .type("button").icon("el-icon-delete").build(); + objects.add(build); + return objects; + } + + /** + * 字段 ID + */ + private String code = "clearFilter"; + + /** + * 第一列可以展示为复选框:checkbox + */ + private String text; + + private String type; + + private String icon; + + @Builder.Default + private boolean circle = true; + + @Builder.Default + private boolean transfer = true; + + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/es/config/EsAutoConfigure.java b/enhancement/src/main/java/com/centricsoftware/enhancement/es/config/EsAutoConfigure.java new file mode 100644 index 0000000..d596600 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/es/config/EsAutoConfigure.java @@ -0,0 +1,76 @@ +package com.centricsoftware.enhancement.es.config; + + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import com.centricsoftware.commons.utils.JsonUtil; +import com.centricsoftware.config.entity.EsProperties; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; +import org.apache.http.ssl.SSLContextBuilder; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.net.ssl.SSLContext; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableConfigurationProperties(EsProperties.class) +public class EsAutoConfigure { + + @Bean + @ConditionalOnProperty(prefix = "es", name = "enable", havingValue = "true") + public ElasticsearchClient elasticsearchClient(EsProperties properties) { + HttpHost[] httpHosts = Arrays.stream(properties.getHost().split(",")) + .map(url -> { + String[] ipPorts = url.split(":"); + return new HttpHost(ipPorts[0], Integer.parseInt(ipPorts[1]), properties.getScheme()); + }).toArray(HttpHost[]::new); + boolean ssl = Objects.equals("https", properties.getScheme()); + RestClientBuilder builder = RestClient.builder(httpHosts); + if (StringUtils.isNotBlank(properties.getUser()) && StringUtils.isNotBlank(properties.getPassword())) { + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(properties.getUser(), properties.getPassword())); + + builder.setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.disableAuthCaching(); + HttpAsyncClientBuilder clientBuilder = httpClientBuilder + .setKeepAliveStrategy((resp, ctx) -> TimeUnit.MINUTES.toMillis(3)) + .setDefaultCredentialsProvider(credentialsProvider); + if (ssl) { + // 信任所有 + SSLContext sslContext = null; + try { + sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build(); + } catch (Exception e) { + throw new RuntimeException("初始化es失败"); + } + SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslContext, NoopHostnameVerifier.INSTANCE); + clientBuilder.setSSLStrategy(sessionStrategy); + } + return clientBuilder; + }); + } + RestClient restClient = builder.build(); + ElasticsearchTransport transport = new RestClientTransport(restClient, + new JacksonJsonpMapper(JsonUtil.getMapper().copy())); + return new ElasticsearchClient(transport); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/filter/AddCookieFilter.java b/enhancement/src/main/java/com/centricsoftware/enhancement/filter/AddCookieFilter.java new file mode 100644 index 0000000..7f62c60 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/filter/AddCookieFilter.java @@ -0,0 +1,40 @@ +package com.centricsoftware.enhancement.filter; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.dto.cache.ThreadLocalCache; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/8/4 14:20 + */ +@Component +public class AddCookieFilter extends HandlerInterceptorAdapter { + + @Resource + ThreadLocalCache threadLocalCache; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String header = request.getHeader("C8_Cookie"); + if(StrUtil.isNotBlank(header)){ + threadLocalCache.personalCookie.set(header); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, + @Nullable ModelAndView modelAndView) throws Exception { + threadLocalCache.personalCookie.remove();; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/filter/CachingRequestBodyFilter.java b/enhancement/src/main/java/com/centricsoftware/enhancement/filter/CachingRequestBodyFilter.java new file mode 100644 index 0000000..827ae49 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/filter/CachingRequestBodyFilter.java @@ -0,0 +1,22 @@ +package com.centricsoftware.enhancement.filter; + +import org.springframework.stereotype.Component; +import org.springframework.web.filter.GenericFilterBean; +import org.springframework.web.util.ContentCachingRequestWrapper; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@Component +public class CachingRequestBodyFilter extends GenericFilterBean { + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { + HttpServletRequest currentRequest = (HttpServletRequest) servletRequest; + ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest); + chain.doFilter(wrappedRequest, servletResponse); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/ExtractMapper.java b/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/ExtractMapper.java new file mode 100644 index 0000000..434e0a3 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/ExtractMapper.java @@ -0,0 +1,20 @@ +package com.centricsoftware.enhancement.mapper; + +import com.centricsoftware.enhancement.modules.c8.dto.nativeexport.ExtractParameterEntity; +import com.centricsoftware.enhancement.modules.c8.dto.nativeexport.ExtractResultEntity; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ExtractMapper { + + List findByXmlQueryVerticalExtract(ExtractParameterEntity param); + + void findByXmlQueryVerticalExtractOracle(ExtractParameterEntity param); + + @Select({" select url from all_url where url_id=#{id} "}) + String findUrlById(String id); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/LogMapper.java b/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/LogMapper.java new file mode 100644 index 0000000..5c5f243 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/mapper/LogMapper.java @@ -0,0 +1,10 @@ +package com.centricsoftware.enhancement.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import org.springframework.stereotype.Repository; + +@Repository +public interface LogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/ant/DepPathField.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/ant/DepPathField.java new file mode 100644 index 0000000..7f58321 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/ant/DepPathField.java @@ -0,0 +1,29 @@ +package com.centricsoftware.enhancement.modules.c8.ant; + +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; + +import java.lang.annotation.*; + +/** + * DepPath实体类查询时,注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Documented +public @interface DepPathField { + + DepPathEntityType type() default DepPathEntityType.STRING;//类型 + + String exp();//表达式 + + String timeFormat() default "yyyy-MM-dd HH:mm:ss";//时间类型默认格式 + + String conjunctionForListToString() default ",";//List转字符串时,连接符 + + boolean recursion() default false;//递归查询 + + boolean removeDuplicate() default true;//List相关类型是否去重 + + Class generic() default String.class;//类型为List时的泛型 + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/EnumCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/EnumCache.java new file mode 100644 index 0000000..97172f2 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/EnumCache.java @@ -0,0 +1,257 @@ +package com.centricsoftware.enhancement.modules.c8.component; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.dto.enum4cache.EnumList; +import com.centricsoftware.commons.dto.enum4cache.EnumValue; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@Slf4j +public class EnumCache { + + private + + @Autowired + C8NodeService c8NodeService; + + //默认 + TimedCache cache = CacheUtil.newTimedCache(60 * DateUnit.DAY.getMillis()); + + /** + * 初始化缓存 + */ + public void init(boolean display) { + DepPathResult result = getDescInfo();//获取描述枚举 + DepPathResult localeInfo = null; + if (display) localeInfo = getLocaleInfo();//翻译枚举 + for (String url : result.getCompleteResultRefs()) { + doInit(result, localeInfo, url); + } +// log.debug(cache.get("C8_Cat1").toString()); + } + + private void doInit(DepPathResult result, DepPathResult localeInfo, String url) { + EnumList enumList = new EnumList(); + enumList.setDependsOn(result.getValue("DependsOn", url).getStr()); + enumList.setDescription(result.getValue("Description", url).getStr()); + enumList.setEnums(result.getValue("Enums", url).getList()); + enumList.setValues(result.getValue("Values", url).getList()); + enumList.setNodeName(result.getValue("$Name", url).getStr()); + List enumValues = setEnumValue(result, url); + enumList.setDescCache(initDescCache(enumValues));//初始化描述缓存 + if (localeInfo != null) { + enumList.setDisplayZhCache(initLocaleCache(enumValues, localeInfo, "zh"));//初始化中文缓存 + enumList.setDisplayEnCache(initLocaleCache(enumValues, localeInfo, "en"));//初始化英文缓存 + } + enumList.setEnumValues(enumValues); + cache.put(enumList.getNodeName(), enumList); + } + + /** + * 初始化翻译枚举 + */ + private Map initLocaleCache(List enumValues, DepPathResult localeInfo, String locale) { + Map localeInfoMap = localeInfo.getValue("LocaleConfiguration", "centric://REFLECTION/INSTANCE/CompanyInfo/Global").getMap(); + String localeConfigurationUrl = localeInfoMap.getOrDefault(locale, ""); + HashMap map = Maps.newHashMap(); + if (StrUtil.isBlank(localeConfigurationUrl)) return map;//如果本地化为空,则返回空map + for (EnumValue value : enumValues) { + String fullName = value.getValue(); + Map resources = localeInfo.getValue("Resources", localeConfigurationUrl).getMap(); + String orDefault = resources.getOrDefault(fullName, ""); + map.put(fullName, orDefault); + if (StrUtil.isNotBlank(orDefault)) { + map.put(orDefault, fullName); + } + } + return map; + } + + /** + * 初始化描述缓存 + **/ + private Map initDescCache(List enumValues) { + HashMap map = Maps.newHashMap(); + for (EnumValue value : enumValues) { + map.put(value.getValue(), value.getDescription()); + if (StrUtil.isBlank(value.getDescription())) { + continue; + } + map.put(value.getDescription(), value.getValue()); + } + return map; + } + + /** + * 获取翻译值 + */ + private DepPathResult getLocaleInfo() { + DepPath build = DepPath.builder().addUrl("centric://REFLECTION/INSTANCE/CompanyInfo/Global") + .addPath("Child:LocaleConfiguration").build(); + return c8NodeService.depPathByUrl(build); + } + + /** + * 获取描述枚举 + */ + private DepPathResult getDescInfo() { + String xml = ""; + DepPath build = DepPath.builderXml().xml(xml).addPath("Child:Values").build(); + return c8NodeService.depPathByXml(build); + } + + + private List setEnumValue(DepPathResult result, String url) { + List list = Lists.newArrayList(); + List values = result.getValue("Values", url).getList(); + for (String v : values) { + EnumValue enumValue = new EnumValue(); + enumValue.setActive(result.getValue("Active", v).getBoolean()); + enumValue.setDependsOn(result.getValue("DependsOn", v).getStr()); + enumValue.setDescription(result.getValue("Description", v).getStr()); + enumValue.setNodeName(result.getValue("$Name", v).getStr()); + enumValue.setValue(result.getValue("Value", v).getStr()); + list.add(enumValue); + } + return list; + } + + + /** + * 通过enum的key和描述值获取fullname + **/ + public String getFullNameByDesc(String prefix, String desc) { + EnumList enumList = cache.get(prefix); + if (enumList == null) { + initByPrefix(prefix);//初始化单个prefix + enumList = cache.get(prefix); + if (enumList == null) return ""; + } + String orDefault = enumList.getDescCache().getOrDefault(desc, ""); + if (StrUtil.isBlank(orDefault)) { + initByPrefix(prefix);//初始化单个prefix + enumList = cache.get(prefix); + orDefault = enumList.getDescCache().getOrDefault(desc, ""); + } + return orDefault; + } + + /** + * 通过fullname获取描述 + **/ + public String getDescByFullname(String fullname) { + String[] split = fullname.split(":", 2); + if (split.length < 2) { + return ""; + } + String prefix = split[0]; + String s = doGetDescByFullname(prefix, fullname); + if (StrUtil.isBlank(s)) { + initByPrefix(prefix);//初始化单个prefix + } + return doGetDescByFullname(prefix, fullname); + } + + private String doGetDescByFullname(String prefix, String fullname) { + EnumList enumList = cache.get(prefix); + if (enumList == null) { + return ""; + } + return enumList.getDescCache().getOrDefault(fullname, ""); + } + + /** + * 初始化单个EnumList + * @param prefix enum前缀 + */ + private void initByPrefix(String prefix) { + DepPathResult descInfo = getDescInfo(prefix); + DepPathResult localeInfo = getLocaleInfo();//翻译枚举 + List resultRefs = descInfo.getCompleteResultRefs(); + for (String url : resultRefs) { + doInit(descInfo, localeInfo, url); + } + } + + /** + * 获取描述枚举 + */ + private DepPathResult getDescInfo(String prefix) { + String xml = ""; + DepPath build = DepPath.builderXml().xml(xml).addPath("Child:Values").build(); + return c8NodeService.depPathByXml(build); + } + + + /** + * 通过enum的key和显示名获取fullname + **/ + public String getFullnameByDisplay(String prefix, String display) { + String s = doGetFullnameByDisplay(prefix, display); + if (StrUtil.isBlank(s)) { + initByPrefix(prefix);//初始化单个prefix + } + return doGetFullnameByDisplay(prefix, display); + } + + private String doGetFullnameByDisplay(String prefix, String display) { + EnumList enumList = cache.get(prefix); + if (enumList == null) { + return ""; + } + String orDefault = enumList.getDisplayZhCache().getOrDefault(display, ""); + if (StrUtil.isBlank(orDefault)) orDefault = enumList.getDisplayEnCache().getOrDefault(display, ""); + return orDefault; + } + + + /** + * 通过fullname获取显示名 + **/ + public String getDisplayByFullname(String fullname) { + String[] split = fullname.split(":", 2); + if (split.length < 2) { + return ""; + } + String prefix = split[0]; + String s = doGetDisplayByFullname(prefix, fullname); + if (StrUtil.isBlank(s)) { + initByPrefix(prefix);//初始化单个prefix + } + return doGetDisplayByFullname(prefix, fullname); + } + + private String doGetDisplayByFullname(String prefix, String fullname) { + EnumList enumList = cache.get(prefix); + if (enumList == null) { + return ""; + } + String orDefault = enumList.getDisplayZhCache().getOrDefault(fullname, ""); + if (StrUtil.isBlank(orDefault)) orDefault = enumList.getDisplayEnCache().getOrDefault(fullname, ""); + return orDefault; + } + + /** + * 通过prefix获取enumList + * @param prefix enum前缀 + * @return + */ + public EnumList getEnumListByPrefix(String prefix) { + return cache.get(prefix); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResult.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResult.java new file mode 100644 index 0000000..718df31 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResult.java @@ -0,0 +1,41 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import lombok.Data; + +/** + * description:查询C8的参数和返回值 + * CentricResultForFeign 经过封装后的实体类 + * Date: 2024/1/31 14:19 + */ +@Data +public class CentricResult { + + /** + * Feign返回的实体类 + */ + private CentricResultForFeign centricResultForFeign; + + /** + * 原始DepPath + */ + private DepPath depPath; + + /** + * DepPath by URL 可以通过resultNodes获取原始的urls的Nodes + */ + private JSONArray resultNodes; + + /** + * DepPath中addPath加载的Nodes + */ + private JSONArray pathNodes; + + /** + * DepPath by XML 查询结果集的Nodes + */ + private JSONObject completeResultRefs; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultCache.java new file mode 100644 index 0000000..9e55613 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultCache.java @@ -0,0 +1,9 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +/** + * description:NodeService查询缓存 + * Date: 2024/2/1 9:53 + */ +public class CentricResultCache { + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultForFeign.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultForFeign.java new file mode 100644 index 0000000..5964b0b --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/CentricResultForFeign.java @@ -0,0 +1,40 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * description:Feign直接返回的实体类 + * Date: 2024/1/31 16:09 + */ +public class CentricResultForFeign { + + @JsonProperty("Status") + private String status; + + /** + * {Node": [{}]} + */ + @JsonProperty("NODES") + private Object nodes; + + /** + * {Modified": "C159609"} + */ + @JsonProperty("URLS") + private Object urls; + + @JsonProperty("ErrorAdmin") + private String errorAdmin; + + @JsonProperty("Error") + private String error; + + /** + * 发布文件时,需要用publishFile,获取里面的URL + */ + @JsonProperty("FileInfo") + private Object fileInfo; + + @JsonProperty("DBQuery") + private Object dbQuery; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathCache.java new file mode 100644 index 0000000..d29571e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathCache.java @@ -0,0 +1,33 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.text.StrFormatter; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; + +/** + * description: + * Date: 2024/2/1 9:52 + */ +public class DepPathCache { + + TimedCache timedCache = CacheUtil.newTimedCache(DateUnit.MINUTE.getMillis() * 10); + + final String format = "{}::{}"; + + public DepPathResultValue getCache(String exp,String url){ + return timedCache.get(getKey(exp, url)); + } + + public void setCache(String exp,String url,DepPathResultValue value){ + timedCache.put(getKey(exp, url),value); + } + + /** + * 获取缓存key + */ + private String getKey(String exp,String url){ + return StrFormatter.format(this.format,url,exp); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathParser.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathParser.java new file mode 100644 index 0000000..8601701 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathParser.java @@ -0,0 +1,445 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.modules.c8.dto.dep.CentricAttrs; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathExp; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.ParserFieldType; +import com.centricsoftware.enhancement.modules.c8.util.CentricAttributesUtil; +import com.google.common.collect.Lists; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * description: + * Date: 2024/1/31 14:09 + */ +@Data +public class DepPathParser { + + /** + * 表达式分隔标识 + */ + private char SPLIT_CHAR = CharUtil.DOT; + + + /** + * 解析表达式,并且封装成DepPathResultValue返回值 + * @param exp 表达式 + * @param url URL + * @param allNodes DepPath查询返回的所有nodes集合 + * @return + */ + public DepPathResultValue getValueByAnalysisExpression(String exp, String url,HashMap allNodes){ + if(MapUtil.isEmpty(allNodes)){ + /** + * 查询结果为空,返回DepPathResultValue实例 + */ + return new DepPathResultValue(); + } + check(exp);//校验表达式是否合法 + /** + * 解析表达式: + * 1、将表达式拆分为多个子表达式实体类; + * 2、处理表达式中的角标问题 + * 3、维护子表达式的前后依赖关系 + */ + List expList = analysisExpression(exp,url); + /** + * 获取DepPathResultValue: + * 1、完善子表达式实体类的字段类型、子表达式的值 + */ + return getValueFromExp(expList,allNodes); + } + + + /** + * 获取DepPathResultValue: + * 1、完善子表达式实体类的字段类型、子表达式的值 + */ + private DepPathResultValue getValueFromExp(List expList, HashMap allNodes) { + DepPathResultValue value = new DepPathResultValue(); + if(expList.size()==0){ + return value; + } + for(int i=0;i allNodes) { + /** + * attrs封装 + */ + CentricAttrs attrs = new CentricAttrs(); + attrs.setType("ref"); + /** + * 前序信息封装 + */ + String url = currentExp.getUrl(); + DepPathExp preExp = DepPathExp.builder().url(url).value(url).fullExp(currentExp.getFullExp()).attrs(attrs).type(ParserFieldType.REF).build(); + currentExp.setBeforeExp(preExp); + currentExp.setBeforeValue(url); + preExp.setAfterExp(currentExp); + } + + + /** + * 解析表达式:通过分隔符将表达式拆分,并且封装成List + * 1、将表达式拆分为多个子表达式实体类; + * 2、处理表达式中的角标问题 + * 3、维护子表达式的前后依赖关系 + * @param exp 表达式 + * @param url URL + * @return 子表达式List + */ + private List analysisExpression(String exp,String url){ + List expList = Lists.newArrayList(); + List split = StrUtil.split(exp, SPLIT_CHAR); + for(int i=0;iindexInt){ + return jsonArray.get(indexInt); + } + return jsonArray; + } + return currentJSONObject.getStr(subExp); + } + + /** + * 设置当前表达式实体类信息:字段类型与flag;当前value + */ + private CentricAttrs setCurrentDepPathExpInfo(DepPathExp currentExp,JSONObject currentJSONObject){ + if(currentExp.getAttrs()!=null){ + return currentExp.getAttrs(); + } + if(currentJSONObject ==null){ + return null; + } + JSONObject $attrs = currentJSONObject.getJSONObject("$attrs"); + JSONObject jsonObject = $attrs.getJSONObject(currentExp.getExp()); + CentricAttrs centricAttrs = Convert.convert(CentricAttrs.class, jsonObject); + currentExp.setAttrs(centricAttrs); + return centricAttrs; + } + + /** + * 按照子表达式获取值 + */ + private void doGetValueFromExp(DepPathExp currentExp,HashMap allNodes) { + parseDepPathForMap(currentExp,allNodes);//处理preValue为map + parseDepPathForList(currentExp,allNodes);//处理preValue为list + parseDepPathForRef(currentExp,allNodes);//处理preValue为ref + } + + /** + * 当前序值为map: + * 1、遍历前序map的所有value,取出对应的node + * 2、根据子表达式在node中取出对应的值和字段类型 + * 3、根据字段类型,确认返回值类型:如:ref-》list;map-》map;list-》list + */ + private void parseDepPathForMap(DepPathExp currentExp, HashMap allNodes){ + DepPathExp beforeExp = currentExp.getBeforeExp(); + ParserFieldType beforeType = beforeExp.getType(); + if(beforeType!=ParserFieldType.MAP){ + return; + } + Object beforeValue = currentExp.getBeforeValue(); + /* + 修复exp路径中如果有【centric:】会导致结果报错或者为{} + */ + if(beforeValue==null|| ObjectUtil.isEmpty(beforeValue)){ + return; + } + JSONObject beforeJSONObject = Convert.convert(JSONObject.class, beforeValue); + Object values = null; + for (Map.Entry entry : beforeJSONObject.entrySet()) { + Object v = entry.getValue(); + JSONObject jsonObject = allNodes.get(v); + if (jsonObject != null) { + Object o = getCurrentValue(jsonObject, currentExp); + if(o==null){ + continue; + } + values = doParseDepPathForListAndMap(values,o,currentExp); + } + } + /** + * 1、封装当前节点的值; + * 2、重置当前节点的类型 + * 3、赋值:【前序节点】的【后续节点】值 + */ + doParseDepPath(values,currentExp); + } + + + /** + * 前序类型为List, + * 1、前序为List,当前为List,则结果为List:当前的所有list聚合 + * 2、前序为List,当前为Map,则结果为List:当前的所有map的value聚合 + * 3、前序为List,当前为其他,则结果为List:当前值聚合 + */ + private void parseDepPathForList(DepPathExp currentExp, HashMap allNodes) { + DepPathExp beforeExp = currentExp.getBeforeExp(); + ParserFieldType beforeType = beforeExp.getType(); + if (beforeType != ParserFieldType.LIST) { + return; + } + Object beforeValue = currentExp.getBeforeValue(); + /* + 修复exp路径中如果有【centric:】会导致结果报错或者为{} + */ + if(beforeValue==null|| ObjectUtil.isEmpty(beforeValue)){ + return; + } + List list = JSONUtil.parseArray(beforeValue).toList(String.class); + Object values = null; + for (String url : list) { + JSONObject jsonObject = allNodes.get(url); + if (jsonObject != null) { + Object o = getCurrentValue(jsonObject, currentExp); + values = doParseDepPathForListAndMap(values,o,currentExp); + } + } + /** + * 1、封装当前节点的值; + * 2、重置当前节点的类型 + * 3、赋值:【前序节点】的【后续节点】值 + */ + doParseDepPath(values,currentExp); + } + + private Object doParseDepPathForListAndMap(Object values1,Object o, DepPathExp currentExp){ + Object values = values1; + boolean currentTypeIsMap = CentricAttributesUtil.isMap(currentExp);//getCurrentValue后才能获取类型 + boolean currentTypeIsList = CentricAttributesUtil.isList(currentExp);//getCurrentValue后才能获取类型 + if(o ==null){ + return values; + } + if(currentTypeIsList){ + JSONArray m = Convert.convert(JSONArray.class, o); + if(values==null){ + values = new JSONArray(); + } + ((JSONArray)values).addAll(m); + } else if (currentTypeIsMap) { + JSONObject m = Convert.convert(JSONObject.class, o); + if(values==null){ + values = new JSONObject(); + } + ((JSONObject)values).putAll(m); + }else{ + if(values==null){ + values = new JSONArray(); + } + ((JSONArray)values).add(o); + } + return values; + } + + + private void parseDepPathForRef(DepPathExp currentExp, HashMap allNodes){ + DepPathExp beforeExp = currentExp.getBeforeExp(); + ParserFieldType beforeType = beforeExp.getType(); + if(beforeType!=ParserFieldType.REF){ + return; + } + DepPathExp afterExp = currentExp.getAfterExp(); + /* + 修复exp路径中如果有【centric:】会导致结果报错或者为{} + */ + Object beforeValueObj = currentExp.getBeforeValue(); + /* + 修复exp路径中如果有【centric:】会导致结果报错或者为{} + */ + if(beforeValueObj==null|| ObjectUtil.isEmpty(beforeValueObj)){ + return; + } + String beforeValue = (String) beforeValueObj; + JSONObject jsonObject = allNodes.get(beforeValue); + Object values = getCurrentValue(jsonObject, currentExp); + String currentType = "ref"; + CentricAttrs attrs = currentExp.getAttrs(); + if(attrs!=null){ + attrs = currentExp.getAttrs(); + currentType = attrs.getType(); + } + beforeExp.setAfterValue(values); + if(afterExp!=null){ + afterExp.setBeforeValue(values); + } + currentExp.setValue(values); + currentExp.setBeforeValue(beforeExp.getValue()); + String exp = currentExp.getExp(); + if (currentExp.getType() == ParserFieldType.INDEX||"$CR".equals(exp)) { + currentExp.setType(ParserFieldType.REF); + } else { + currentExp.setType(CentricAttributesUtil.getParserFieldType(currentType)); + } + } + + + /** + * 1、封装当前节点的值; + * 2、重置当前节点的类型 + * 3、赋值:【前序节点】的【后续节点】值 + */ + private void doParseDepPath(Object values,DepPathExp currentExp){ + ParserFieldType type = currentExp.getType(); + DepPathExp afterExp = currentExp.getAfterExp(); + DepPathExp beforeExp = currentExp.getBeforeExp(); + currentExp.setValue(values); + currentExp.setBeforeValue(beforeExp.getValue()); + beforeExp.setAfterValue(values); + CentricAttrs attrs = currentExp.getAttrs(); + if(attrs==null){ + return; + } + String currentType = attrs.getType(); + if(afterExp!=null){ + afterExp.setBeforeValue(values); + } + if(values==null){ + return; + } + if(values instanceof List){ + currentExp.setType(type ==ParserFieldType.INDEX?ParserFieldType.INDEX_LIST:ParserFieldType.LIST); + return; + } + if(values instanceof Map){ + currentExp.setType(type ==ParserFieldType.INDEX?ParserFieldType.INDEX_MAP:ParserFieldType.MAP); + return; + } + String exp = currentExp.getExp(); + if("$CR".equals(exp)||CentricAttributesUtil.isRef(currentType)){ + currentExp.setType(ParserFieldType.REF); + } + currentExp.setType(ParserFieldType.OTHER); + } + + + /** + * 校验表达式、allNodes是否合法 + */ + private void check(String exp) { + if(StrUtil.isBlankOrUndefined(exp)){ + new BaseException(ResCode.ERROR,"DepPath查询的表达式不能为空!"); + } + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathResult.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathResult.java new file mode 100644 index 0000000..69accb8 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/dep/DepPathResult.java @@ -0,0 +1,187 @@ +package com.centricsoftware.enhancement.modules.c8.component.dep; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathType; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * description:DepPath查询返回类 + * Date: 2024/1/31 14:16 + */ +@Slf4j +public class DepPathResult { + + @Getter + private HashMap urlNodes; //结果集对应的map + @Getter + private HashMap pathNodes;//path集对应的map + @Getter + private HashMap allNodes = new HashMap<>();//结果集+path集对应的map + @Getter + private DepPath depPath; + @Getter + private CentricResult centricResult; + + /** + * DepPath.ByXML可以通过completeResultRefs获取查询结果集的URL + * DepPath.ByURL可以通过completeResultRefs获取DepPath的addUrls的所有url + */ + @Getter + private List completeResultRefs; + + + /** + * DepPath.ByXML可以通过completeResultRefs获取查询结果集的Node + * DepPath.ByURL可以通过completeResultRefs获取DepPath的addUrls的所有Node + */ + @Getter + private JSONArray resultNodeJSONArray; + + /** + * 解析器 + */ + @Getter + DepPathContext depPathContext; + + + /** + * DepPath的XML和URL的结果集 + */ + @Getter + private JSONArray nodes;// 结果集 + + + public DepPathResult(){ + + } + + /** + * @param depPathContext 上下文参数,包含解析器组件、缓存组件 + */ + public DepPathResult(CentricResult centricResult,DepPathContext depPathContext){ + init(centricResult,depPathContext); + } + + public DepPathResultValue getValue(String exp,String url){ + DepPathResultValue value = getFromCache(exp, url); + if(value!=null){ + return value; + } + value = getValueByAnalysisExpression(exp,url); + setCache(exp,url,value); + return value; + } + + /** + * 解析表达式 + */ + private DepPathResultValue getValueByAnalysisExpression(String exp,String url){ + DepPathParser depPathParser = depPathContext.getDepPathParser(); + if(depPathParser==null){ + new BaseException(ResCode.CONFIG_NOT_INIT,"配置未初始化:找不到DepPathParser组件!"); + } + DepPathResultValue depPathResultValue = depPathParser.getValueByAnalysisExpression(exp, url, allNodes); + return depPathResultValue; + } + + /** + * 从缓存组件中获取数据 + */ + private DepPathResultValue getFromCache(String exp,String url){ + DepPathCache depPathCache = depPathContext.getDepPathCache(); + if(depPathCache!=null){ + DepPathResultValue cache = depPathCache.getCache(exp, url); + if(cache!=null){ + log.debug("DepPath查询,从缓存组件中返回值:exp={},url={},value={}",exp,url,cache.getValue()); + return cache; + } + } + return null; + } + + /** + * 从缓存组件中获取数据 + */ + private void setCache(String exp,String url,DepPathResultValue value){ + DepPathCache depPathCache = depPathContext.getDepPathCache(); + if(depPathCache!=null){ + depPathCache.setCache(exp,url,value); + } + } + + /** + * 初始化 + * @param depPathContext 解析器 + */ + public DepPathResult init(CentricResult centricResult,DepPathContext depPathContext){ + this.depPath = centricResult.getDepPath(); + this.centricResult = centricResult; + this.nodes = centricResult.getResultNodes(); + this.resultNodeJSONArray = centricResult.getResultNodes(); + this.pathNodes = initNodeMap(centricResult.getPathNodes()); + this.urlNodes = initNodeMap(centricResult.getResultNodes()); + this.depPathContext = depPathContext; + initCompleteResultRefs();//初始化所有的URL + return this; + } + + /** + * 初始化DepPath.ByXML的completeResultRefs + */ + private void initCompleteResultRefs() { + DepPathType type = depPath.getType(); + if(type==DepPathType.URL){ + this.completeResultRefs = new ArrayList(depPath.getUrls()); + }else{ + JSONObject completeResult = centricResult.getCompleteResultRefs(); + if (completeResult == null) { + return; + } + this.completeResultRefs = completeResult.get("ref", List.class); + } + } + + /** + * 初始化pathNodes、urlNodes、nodes + */ + private HashMap initNodeMap(JSONArray array){ + if (array == null) { + return new HashMap(); + } + HashMap map = new HashMap(getMapInitialCapacity(array.size())); + array.forEach(i -> { + JSONObject obj = JSONUtil.parseObj(i); + map.put(obj.getStr("$URL"), obj); + }); + this.allNodes.putAll(map); + return map; + } + + + /** + * 获取Map的初始化容量,如果小于16,则按照默认值16;否则初始容量= 长度/0.75+1;向上取整 + * @param length + * @return + */ + private int getMapInitialCapacity(int length){ + int initialCapacity = NumberUtil.round(length/0.75,0).intValue() + 1;//初始化容量因子,提高性能 + if(initialCapacity<16){ + initialCapacity = 16; + } + return initialCapacity; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/IDepPathSearchChain.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/IDepPathSearchChain.java new file mode 100644 index 0000000..2b2f017 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/IDepPathSearchChain.java @@ -0,0 +1,150 @@ +package com.centricsoftware.enhancement.modules.c8.component.design.chain; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.google.common.collect.Lists; +import org.apache.ibatis.reflection.DefaultReflectorFactory; +import org.apache.ibatis.reflection.Reflector; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public interface IDepPathSearchChain { + + + List execute(DepPath depPath, Class clazz, Consumer function); + + List execute(DepPath depPath, List urls, Class clazz, Consumer function); + + List execute(DepPathResult result, Class clazz); + + List execute(DepPathResult result, List urls, Class clazz, Consumer function); + + List execute(DepPathResult result,Class clazz, Consumer function); + + default Reflector initDepPathByEntityContext(DepPathByEntityContext context,DepPathResult result,Class clazz){ + DefaultReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory(); + Reflector reflector = defaultReflectorFactory.findForClass(clazz); + context.setReflector(reflector);//反射器 + context.setDepPathResult(result); + context.setClazz(clazz); + context.setResultUrls(result.getCompleteResultRefs()); + return reflector; + } + + default List getResultUrls(DepPathByEntityContext context,List urls){ + if(urls!=null){ + return urls; + } + DepPathResult depPathResult = context.getDepPathResult(); + List resultRefs = depPathResult.getCompleteResultRefs(); + return resultRefs; + } + + default List getPaths(Class clazz){ + List paths = getPaths(null, clazz); + List collect = new ArrayList<>(); + for (String i : paths) { + if (!i.contains(StrUtil.DOT)) { + continue; + } + if (StrUtil.isBlankOrUndefined(i)) { + continue; + } + String[] split = StrUtil.split(i,StrUtil.DOT); + if(split.length==1){//表达式的长度等于1,则不需要path + continue; + } + //表达式的长度大于1,则只需要length-1的path + String[] sub = ArrayUtil.sub(split, 0, split.length - 1); + String finalPath = StrUtil.join(StrUtil.DOT,sub); + String format = StrFormatter.format("Child:{}", StrUtil.replace(finalPath, StrUtil.DOT, "/Child:")); + collect.add(format); + } + return collect; + } + /** + * 通过class获取DepPath的paths + */ + default List getPaths(String prePath,Class clazz){ + Field[] fieldArray = ReflectUtil.getFields(clazz); + List fields = Arrays.stream(fieldArray).filter(i->i.getAnnotation(DepPathField.class)!=null).collect(Collectors.toList()); + ArrayList allPath = Lists.newArrayList(); + /** + * 获取当前对象的path + */ + allPath.addAll(getCurrentPaths(prePath,fields)); + /** + * 获取处理递对象的path + */ + allPath.addAll(getRecursionPaths(prePath,fields)); + return allPath; + } + + /** + * 处理递归字段 + */ + default List getRecursionPaths(String prePath,List fields){ + List expList = new ArrayList<>(); + for (Field field : fields) { + DepPathField depPathField = field.getAnnotation(DepPathField.class); + if (depPathField.recursion()) { + Class genericClazz = getGenericClazz(field); + String allPre = StrUtil.isBlankOrUndefined(prePath)?"":prePath+StrUtil.DOT; + String format = StrFormatter.format("{}{}", allPre, depPathField.exp()); + List paths = getPaths(format, genericClazz); + expList.addAll(paths); + } + } + return expList; + } + + /** + * 处理当前字段 + */ + default List getCurrentPaths(String prePath,List fields){ + String pre = StrUtil.isBlankOrUndefined(prePath)?"":prePath; + if(StrUtil.isNotBlank(pre)&&!pre.endsWith(StrUtil.DOT)){ + pre += StrUtil.DOT; + } + String finalPre = pre; + List paths= fields.stream() + .filter(i ->!i.getAnnotation(DepPathField.class).recursion()) + .map(i-> StrFormatter.format("{}{}",finalPre,i.getAnnotation(DepPathField.class).exp())) + .distinct() + .collect(Collectors.toList()); + return paths; + } + + + /** + * 获取Field的类,基础类型则返回null + */ + default Class getGenericClazz(Field field){ + Class fieldClazz = field.getType();//获取f的类 + if(fieldClazz.isPrimitive()) return null; //判断是否为基本类型 + if(fieldClazz.isAssignableFrom(List.class)){//判断fc是否和List相同或者其父类 + Type fc = field.getGenericType(); //如果是List类型,得到其Generic的类型 + if(fc instanceof ParameterizedType){ + ParameterizedType pt = (ParameterizedType) fc; + Class genericClazz = (Class)pt.getActualTypeArguments()[0]; + return genericClazz; + } + } + return fieldClazz; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/impl/DefaultDepPathSearchChainImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/impl/DefaultDepPathSearchChainImpl.java new file mode 100644 index 0000000..8a71b27 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/design/chain/impl/DefaultDepPathSearchChainImpl.java @@ -0,0 +1,173 @@ +package com.centricsoftware.enhancement.modules.c8.component.design.chain.impl; + +import cn.hutool.core.util.ReflectUtil; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.component.design.chain.IDepPathSearchChain; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.em.DepPathType; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8SearchService; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.Reflector; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * description: + * Date: 2024/3/14 17:29 + */ +//@ConditionalOnMissingBean(IDepPathSearchChain.class) +@Component +@Slf4j +public class DefaultDepPathSearchChainImpl implements IDepPathSearchChain { + + @Resource + private List handleList; + @Resource + C8SearchService c8SearchService; + + @Override + public List execute(DepPath depPath,Class clazz, Consumer function) { + return execute(depPath,null,clazz,function); + } + + @Override + public List execute(DepPath depPath, List urls,Class clazz, Consumer function) { + depPath.setPaths(new TreeSet<>(getPaths(clazz))); + DepPathType type = depPath.getType(); + DepPathResult depPathResult; + if(type==DepPathType.XML){ + depPathResult = c8SearchService.depPathByXml(depPath); + }else{ + depPathResult = c8SearchService.depPathByUrl(depPath); + } + return execute(depPathResult,urls,clazz,function); + } + + @Override + + public List execute(DepPathResult result,Class clazz) { + return execute(result,clazz,null); + } + + @Override + public List execute(DepPathResult result, Class clazz, Consumer function) { + return execute(result,null,clazz,null); + } + + @Override + public List execute(DepPathResult result, List urls ,Class clazz,Consumer function){ + DepPathByEntityContext context = new DepPathByEntityContext(); + Reflector reflector = initDepPathByEntityContext(context, result, clazz); + List resultUrls = getResultUrls(context, urls); + List list = new ArrayList<>(); + Field[] fields = ReflectUtil.getFields(clazz); + List annotationFields = Arrays.stream(fields).filter(i -> i.getAnnotation(DepPathField.class) != null).collect(Collectors.toList()); + for(String url : resultUrls){ + T o = null; + try { + o = (T) reflector.getDefaultConstructor().newInstance(); + context.setUrl(url); + context.setInstance(o); + for(Field field : annotationFields){ + executeField(context,field,function); + } + if(function!=null){ + function.accept(context); + } + list.add(o); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e ) { + printError(context,e,clazz,url); + throw new RuntimeException(e); + }catch (Exception e){ + printError(context,e,clazz,url); + log.error("错误信息:getMessage={},getLocalizedMessage={}",e.getMessage(),e.getLocalizedMessage()); + throw new RuntimeException(e); + } + } + return list; + } + + /** + * 按字段映射赋值 + */ + private void executeField(DepPathByEntityContext context,Field field,Consumer function) throws InvocationTargetException, IllegalAccessException{ + DepPathField annotation = field.getAnnotation(DepPathField.class); + Reflector reflector = context.getReflector(); + String fieldName = field.getName(); + context.setDepPathField(annotation); + context.setFieldName(fieldName); + context.setField(field); + context.setInvoker(reflector.getSetInvoker(fieldName)); + /** + * 递归查询 + */ + if(annotation.recursion()){ + //递归查询 + executeRecursion(context,function); + return; + } + /** + * 非递归 + */ + for (IDepPathEntitySearchService handleIntercept : handleList) { + if(!handleIntercept.isHandle(context)){ + continue; + } + handleIntercept.process(context); + if(!handleIntercept.isContinue(context)){ + //是否继续执行责任链 + break; + } + } + } + + /** + * 递归查询 + */ + private void executeRecursion(DepPathByEntityContext context,Consumer function) throws InvocationTargetException, IllegalAccessException { + Field field = context.getField(); + DepPathResult depPathResult = context.getDepPathResult(); + Class type = field.getType(); + Class clazz = getGenericClazz(field); + if(clazz==null){ + return; + } + List urls = getFieldListValues(context); + List result = execute(depPathResult, urls,clazz, function); + if(type == List.class){ + Object[] valueArray = new Object[]{result}; + context.getInvoker().invoke(context.getInstance(),valueArray); + }else if(result.size()>0){ + Object[] valueArray = new Object[]{result.get(0)}; + context.getInvoker().invoke(context.getInstance(),valueArray); + } + } + + /** + * 获取Filed的List值,递归状态使用 + */ + private List getFieldListValues(DepPathByEntityContext context){ + DepPathResult depPathResult = context.getDepPathResult(); + DepPathField depPathField = context.getDepPathField(); + String exp = depPathField.exp(); + String url = context.getUrl(); + return depPathResult.getValue(exp, url).getList(depPathField.removeDuplicate(), String.class); + } + + private void printError(DepPathByEntityContext context,Exception e,Class clazz,String url){ + log.error("映射生成实体类报错:实例类名称={},url={},字段名={};cause by:{}",clazz.getName(), url, context.getFieldName(), e.getMessage()); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/ICentricAbstractFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/ICentricAbstractFactory.java new file mode 100644 index 0000000..3dde44c --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/ICentricAbstractFactory.java @@ -0,0 +1,15 @@ +package com.centricsoftware.enhancement.modules.c8.component. + factory; + +/** + * description: + * Date: 2024/1/29 10:50 + */ +public interface ICentricAbstractFactory { + + IDepPathFactory getDepPathFactory(); + + INodeServiceFactory getNodeServiceFactory(); + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/IDepPathFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/IDepPathFactory.java new file mode 100644 index 0000000..d0a8738 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/IDepPathFactory.java @@ -0,0 +1,14 @@ +package com.centricsoftware.enhancement.modules.c8.component.factory; + +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathCache; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathParser; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathContext; + +public interface IDepPathFactory { + + DepPathParser getDepPathParser(DepPathContext context); + + DepPathCache getDepPathCache(DepPathContext context); + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/INodeServiceFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/INodeServiceFactory.java new file mode 100644 index 0000000..246b838 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/INodeServiceFactory.java @@ -0,0 +1,12 @@ +package com.centricsoftware.enhancement.modules.c8.component.factory; + +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResult; +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResultCache; + +public interface INodeServiceFactory { + + ICentricResult getCentricResultFactory(); + + ICentricResultCache getCentricResultCache(); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultDepPathFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultDepPathFactory.java new file mode 100644 index 0000000..8c1377b --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultDepPathFactory.java @@ -0,0 +1,34 @@ +package com.centricsoftware.enhancement.modules.c8.component.factory.impl; + +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathCache; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathParser; +import com.centricsoftware.enhancement.modules.c8.component.factory.IDepPathFactory; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathContext; +import org.springframework.stereotype.Component; + +/** + * description: + * Date: 2024/1/29 11:05 + */ +@Component +public class DefaultDepPathFactory implements IDepPathFactory { + + @Override + public DepPathParser getDepPathParser(DepPathContext context) { + DepPathParser depPathParser = new DepPathParser(); + if(context!=null){ + context.setDepPathParser(depPathParser); + } + return depPathParser; + } + + @Override + public DepPathCache getDepPathCache(DepPathContext context) { + DepPathCache depPathCache = new DepPathCache(); + if(context!=null){ + context.setDepPathCache(depPathCache); + } + return depPathCache; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultICentricAbstractFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultICentricAbstractFactory.java new file mode 100644 index 0000000..cff6ba1 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultICentricAbstractFactory.java @@ -0,0 +1,38 @@ +package com.centricsoftware.enhancement.modules.c8.component.factory.impl; + +import com.centricsoftware.enhancement.modules.c8.component.factory.ICentricAbstractFactory; +import com.centricsoftware.enhancement.modules.c8.component.factory.IDepPathFactory; +import com.centricsoftware.enhancement.modules.c8.component.factory.INodeServiceFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * description:生成DefaultDepPathFactory、DefaultNodeServiceFactory等工厂的超级工厂类 + * 1、可支持其他实现方式,只要实现CentricAbstractFactory接口,并且交给Spring容器管理即可 + * 2、容器中不允许存在多个CentricAbstractFactory的实现类,当配置了其他实现时,DefaultCentricAbstractFactory就会失效 + * 3、当自定义了多个CentricAbstractFactory的实现,将会报错 + * + * Date: 2024/1/31 13:15 + */ +@Component +public class DefaultICentricAbstractFactory implements ICentricAbstractFactory { + + @Resource + IDepPathFactory depPathFactory; + + @Resource + INodeServiceFactory nodeServiceFactory; + + + @Override + public IDepPathFactory getDepPathFactory() { + return depPathFactory; + } + + @Override + public INodeServiceFactory getNodeServiceFactory() { + return nodeServiceFactory; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultINodeServiceFactory.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultINodeServiceFactory.java new file mode 100644 index 0000000..7bfdc52 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/factory/impl/DefaultINodeServiceFactory.java @@ -0,0 +1,26 @@ +package com.centricsoftware.enhancement.modules.c8.component.factory.impl; + +import com.centricsoftware.enhancement.modules.c8.component.factory.INodeServiceFactory; +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResult; +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResultCache; +import org.springframework.stereotype.Component; + +/** + * description: + * Date: 2024/1/29 11:06 + */ +@Component +public class DefaultINodeServiceFactory implements INodeServiceFactory { + + @Override + public ICentricResult getCentricResultFactory() { + return null; + } + + @Override + public ICentricResultCache getCentricResultCache() { + return null; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResult.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResult.java new file mode 100644 index 0000000..6c4e46b --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResult.java @@ -0,0 +1,19 @@ +package com.centricsoftware.enhancement.modules.c8.component.parser; + +import com.centricsoftware.enhancement.modules.c8.component.dep.CentricResultForFeign; + +/** + * 返回的结果集: + * 1、DepPath返回的Result + * 2、C8返回的通用结果集 + * @author Xulin.Xie 2024-1-31 + */ +public interface ICentricResult { + + /** + * 获取Feign返回的实体类,如果需要重写CentricResultForFeign,可重新实现这个接口,并且继承CentricResultForFeign + */ + CentricResultForFeign getCentricResultForFeign(); + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResultCache.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResultCache.java new file mode 100644 index 0000000..e590883 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/ICentricResultCache.java @@ -0,0 +1,16 @@ +package com.centricsoftware.enhancement.modules.c8.component.parser; + + +import com.centricsoftware.enhancement.modules.c8.component.dep.CentricResultCache; + +/** + * 缓存器: + * 1、通过NodeService到C8查询时,执行缓存,避免每次都取C8查询:考虑到场景的通用性,默认的缓存器不支持全局缓存(跨线程) + * @author Xulin.Xie 2024-1-31 + */ +public interface ICentricResultCache { + + CentricResultCache getCentricResultCache(); + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/CentricResultCacheImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/CentricResultCacheImpl.java new file mode 100644 index 0000000..82192f4 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/CentricResultCacheImpl.java @@ -0,0 +1,20 @@ +package com.centricsoftware.enhancement.modules.c8.component.parser.impl; + +import com.centricsoftware.enhancement.modules.c8.component.dep.CentricResultCache; +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResultCache; +import org.springframework.stereotype.Component; + +/** + * description: + * Date: 2024/2/1 9:51 + */ +@Component +public class CentricResultCacheImpl implements ICentricResultCache { + + @Override + public CentricResultCache getCentricResultCache() { + CentricResultCache centricResultCache = new CentricResultCache(); + return centricResultCache; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/DefaultICentricResultImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/DefaultICentricResultImpl.java new file mode 100644 index 0000000..cc04e73 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/component/parser/impl/DefaultICentricResultImpl.java @@ -0,0 +1,22 @@ +package com.centricsoftware.enhancement.modules.c8.component.parser.impl; + +import com.centricsoftware.enhancement.modules.c8.component.dep.CentricResultForFeign; +import com.centricsoftware.enhancement.modules.c8.component.parser.ICentricResult; +import org.springframework.stereotype.Component; + +/** + * 返回的结果集: + * 1、C8返回的通用结果集 + * 可重新实现该类 + * @author Xulin.Xie 2024-1-31 + */ +@Component +public class DefaultICentricResultImpl implements ICentricResult { + + @Override + public CentricResultForFeign getCentricResultForFeign(){ + CentricResultForFeign centricResultForFeign = new CentricResultForFeign(); + return centricResultForFeign; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/FeignConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/FeignConfig.java new file mode 100644 index 0000000..3a06052 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/FeignConfig.java @@ -0,0 +1,129 @@ +package com.centricsoftware.enhancement.modules.c8.config; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.TimeInterval; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.commons.utils.SpringUtil; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.modules.c8.config.okhttp.CookieJarHandler; +import com.centricsoftware.enhancement.dto.cache.ThreadLocalCache; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.modules.c8.service.C8LoginService; +import com.centricsoftware.enhancement.service.log.LogService; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; + +@Configuration +@Slf4j +public class FeignConfig { + + @Bean + public OkHttpClient okHttpClient() { + return new OkHttpClient.Builder().cookieJar(getCookieJar()).addInterceptor(getInterceptor()).build(); + } + + /** + * 添加Cookie + * 后续需要根据条件设置是否添加Cookie + * + * @return + */ + @Bean + public CookieJar getCookieJar() { + return new CookieJarHandler(); + } + + @Bean + public Interceptor getInterceptor() { + return new FeignOkHttpClientResponseInterceptor(); + } + + /** + * okHttp响应拦截器 + * 此拦截器用于C8交互时,判断Session是否过期,如果过期,则自动登录 + */ + public class FeignOkHttpClientResponseInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + TimeInterval timer = DateUtil.timer(); + Request originalRequest = chain.request(); + //封装处理请求日志 + FeignLogDto feignLogDto = startProcessLog(originalRequest); + //设置cookie + originalRequest = setCookie(originalRequest); + //执行 + Response response = chain.proceed(originalRequest);//执行请求,获取返回值 + //校验C8请求是否需要重新登录,如果登录超时,则会重新登录并且重试 + response = checkSession(chain,originalRequest,response); + // + endProcessLog(feignLogDto,response,timer); + return response; + } + } + + /** + * 校验C8请求是否需要重新登录,如果登录超时,则会重新登录并且重试 + */ + private Response checkSession(Interceptor.Chain chain, Request originalRequest, Response response) throws IOException { + C8LoginService c8LoginService = SpringUtil.getBean(C8LoginService.class); + boolean needLogin = c8LoginService.checkSession(originalRequest, response);//校验session + if (needLogin) { + response = chain.proceed(response.request());// //重试请求,获取返回值; + } + return response; + } + + /** + * 设置cookie + */ + private Request setCookie(Request originalRequest) { + ThreadLocalCache cache = SpringContextHolder.getBean(ThreadLocalCache.class); + if (StrUtil.isNotBlank(cache.personalCookie.get())) { + return originalRequest.newBuilder().addHeader("Cookie", cache.personalCookie.get()).build(); + } + return originalRequest; + } + + @Bean + public ObjectMapper jacksonMapper() { + ObjectMapper mapper = new ObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + return mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + /** + * 处理Feign的接口日志 + */ + private FeignLogDto startProcessLog(Request originalRequest) { + LogService logService = SpringUtil.getBean(LogServiceImpl.class); + FeignLogDto feignLogDto = null; + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + //是否记录日志 + if(StrUtil.equals(csProperties.getValue("feign-log"), Constants.Bool.TRUE, true)){ + feignLogDto = logService.setRequestData(originalRequest); + } + return feignLogDto; + } + /** + * 处理Feign的接口日志 + */ + private void endProcessLog(FeignLogDto feignLogDto,Response response,TimeInterval timer) { + if(feignLogDto==null){ + return; + } + LogService logService = SpringUtil.getBean(LogServiceImpl.class); + logService.setResponseData(response, timer, feignLogDto);//封装返回请求报文 + logService.saveLog(feignLogDto);//保存日志 + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/okhttp/CookieJarHandler.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/okhttp/CookieJarHandler.java new file mode 100644 index 0000000..417d71a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/config/okhttp/CookieJarHandler.java @@ -0,0 +1,42 @@ +package com.centricsoftware.enhancement.modules.c8.config.okhttp; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.enhancement.dto.cache.ThreadLocalCache; +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class CookieJarHandler implements CookieJar { + + private ConcurrentHashMap> cookieStore = new ConcurrentHashMap<>(); + + @Override + public void saveFromResponse(HttpUrl httpUrl, List list) { + //保存Cookies + ThreadLocalCache cache = SpringContextHolder.getBean(ThreadLocalCache.class); + if(cache==null){ + cookieStore.put(httpUrl.host(), list); + } + if(cache!=null && StrUtil.isBlank(cache.personalCookie.get())){ + cookieStore.put(httpUrl.host(), list); + } + } + + @Override + public List loadForRequest(HttpUrl httpUrl) { + //加载Cookies + List cookies = cookieStore.get(httpUrl.host()); + ThreadLocalCache cache = SpringContextHolder.getBean(ThreadLocalCache.class); + if(cache!=null && StrUtil.isNotBlank(cache.personalCookie.get())){ + return new ArrayList(); + }else{ + return cookies != null ? cookies : new ArrayList(); + } + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/C8LogReturnEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/C8LogReturnEntity.java new file mode 100644 index 0000000..1a4b2a7 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/C8LogReturnEntity.java @@ -0,0 +1,26 @@ +package com.centricsoftware.enhancement.modules.c8.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class C8LogReturnEntity implements Serializable { + + @JsonProperty("Status") + private String status; + + @JsonProperty("Error") + private String error; + + @JsonProperty("ErrorAdmin") + private String errorAdmin; + + @JsonProperty("NODES") + private Object nodes; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/ExpResultEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/ExpResultEntity.java new file mode 100644 index 0000000..3cebde6 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/ExpResultEntity.java @@ -0,0 +1,42 @@ +package com.centricsoftware.enhancement.modules.c8.dto; + +import cn.hutool.json.JSONObject; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ExpResultEntity implements Serializable { + @JsonProperty("Status") + private String status; + + @JsonProperty("Time") + private String time; + + @JsonProperty("Dependency") + private JSONObject dependency; + + @JsonProperty("Debug") + private JSONObject debug; + + @JsonProperty("Error") + private String error; + + @JsonProperty("ErrorAdmin") + private String errorAdmin; + + @JsonProperty("Result") + private ExpResultEntity.Result result; + + @Data + public class Result { + + @JsonProperty("Type") + private String type; + + @JsonProperty("Value") + private Object value; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/OperationResultEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/OperationResultEntity.java new file mode 100644 index 0000000..38a5cce --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/OperationResultEntity.java @@ -0,0 +1,36 @@ +package com.centricsoftware.enhancement.modules.c8.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class OperationResultEntity extends C8LogReturnEntity { + + @JsonProperty("Status") + private String status; + + /** + * {Node": [{}]} + */ + @JsonProperty("NODES") + private Object nodes; + + /** + * {Modified": "C159609"} + */ + @JsonProperty("URLS") + private Object urls; + + @JsonProperty("ErrorAdmin") + private String errorAdmin; + + @JsonProperty("Error") + private String error; + + @JsonProperty("FileInfo") + private Object fileInfo; + + @JsonProperty("DBQuery") + private Object dbQuery; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/CustomViewConfig.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/CustomViewConfig.java new file mode 100644 index 0000000..b036fd3 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/CustomViewConfig.java @@ -0,0 +1,194 @@ +package com.centricsoftware.enhancement.modules.c8.dto.customview; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.EnumTypeForCV; +import com.google.common.collect.Lists; +import lombok.Data; +import org.apache.poi.ss.formula.functions.T; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * description:定制化视图配置类 + * Date: 2024/5/14 14:58 + */ +@Data +public class CustomViewConfig { + + public CustomViewConfig(String viewUrl){ + this.viewUrl = viewUrl; + } + + /** + * CustomView URL,比如 C3155(_CS_PreferenceView) + */ + private String viewUrl; + + /** + * 行BO名称 + */ + private String targetClass; + + /** + * 视图角色 + */ + private List umEntry; + + /** + * 列配置项 + */ + private List cols = Lists.newArrayList(); + + /** + * 别名,如果需要重新定制返回的列名,则需要配置此属性 + * key:列名,value:别名 + * key默认为attributeId,如果attributeId重复,则默认为attributeId+流水,比如attributeId1、attributeId2+attributeId3 + * 如果是Node Name字段: + * 1、【Node Name:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例:ProductColors + * 2、Node Name:(Style):0为例:Node Name + * 如果执行Node Name规则后还是重复,则再按照attributeId(修改后)+流水执行,比如ProductColors1、ProductColors2、ProductColors3 + */ + private Map aliasMap = new HashMap<>(); + + /** + * 组件会对attributeId进行默认处理,如果配置了aliasMap,则处理后会根据aliasMap进行映射 + * key:fullPath + * value: 字段 + */ + private Map attributeIdMapping = new HashMap<>();; + + /** + * 优化列名,默认为false;优化项: + * 1、开启后,Node Name会被优化为Name; + * 2、如果的是Ref类型的数据,取值则取Node Name + * 3、如果attributeId重复,则向上取一个path,如果再重复,则按照attributeId(原始)+流水执行;注意: + * 3.1 targetClass上的字段(本BO上的字段)不会加流水 + * 3.2 如果向上取再重复了,直接全路径 + * 3.3 matrix字段同样规则 + * 5、enum类型转换 + */ + private boolean optimizationAttributeId = false; + + /** + * CustomView的DepPathResult结果集 + */ + private DepPathResult customViewResult; + + /** + * DepPath的Path + */ + private List paths; + + /** + * List转成String + */ + private boolean listToString = true; + + /** + * List转成String + */ + private String conjunction = String.valueOf(StrUtil.C_COMMA); + + /** + * Map转成String + */ + private boolean mapToString = true; + + /** + * 枚举值转换,默认转成key;无法对单列定义。如果不同列转换不一致,请用Lambda查询定制 + */ + private EnumTypeForCV enumTypeForCV = EnumTypeForCV.KEY; + + + + @Data + public class ColumnConfig { + /** + * 列对应的BO名称: + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例:Colorway + */ + private String businessObjectName; + + /** + * 列最终取值字段 + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例:C8_CW_CostDev1 + */ + private String attributeId; + + /** + * 是否是matrix + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例:true + */ + private boolean matrix; + + /** + * matrix字段名称 + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例:ProductColors,获取的是{ProductColors}中的ProductColors + * 如果重复,则取全路径,比如 A:(Style)Child:B(XX)/Child:ProductColors(Colorway):0{ProductColors}: B.ProductColors + */ + private String matrixAttributeId; + + /** + * matrix路径 + * 以【C8_CA_RatingO3:(Style)Child:C(XXX)/Child:ProductColors(Colorway)/Child:Attributes(ColorwayAttributes):0{ProductColors}】为例:C.ProductColors + */ + private String matrixPath; + + /** + * matrix后的ID + * 以【C8_CA_RatingO3:(Style)Child:ProductColors(Colorway)/Child:Attributes(ColorwayAttributes):0{ProductColors}】为例:Attributes.C8_CA_RatingO3 + */ + private String attributeIdAfterMatrix; + + /** + * 路径:不包括最终的取值字段 + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例: + * ProductColors + */ + private String path; + + /** + * 最后一个path+“.”+attributeId + */ + private String path1; + + /** + * BO路径 + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例: + * Style.Colorway + */ + private String boPath; + + /** + * 全路径,包括最终的取值字段 + * 以【C8_CW_CostDev1:(Style)Child:ProductColors(Colorway):0{ProductColors}】为例: + * ProductColors.C8_CW_CostDev1 + */ + private String fullPath; + + /** + * 原始表达式,_CS_PreferenceView中Keys的元素 + */ + private String originExpressions; + + /** + * 最终取值 + */ + private DepPathResultValue value; + + public String getGroupId() { + if(StrUtil.isBlankOrUndefined(matrixAttributeId)){ + return attributeId; + } + return StrFormatter.format("{}.{}",matrixAttributeId,attributeId); + } + } + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/LambdaParam.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/LambdaParam.java new file mode 100644 index 0000000..064a3d5 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/customview/LambdaParam.java @@ -0,0 +1,16 @@ +package com.centricsoftware.enhancement.modules.c8.dto.customview; + +import cn.hutool.json.JSONObject; +import lombok.Data; + +/** + * description: + * Date: 2024/5/15 16:31 + */ +@Data +public class LambdaParam { + + private CustomViewConfig config; + + private JSONObject result; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/CentricAttrs.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/CentricAttrs.java new file mode 100644 index 0000000..7d74486 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/CentricAttrs.java @@ -0,0 +1,28 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import lombok.Data; + +/** + * description:存储DepPath查询时返回字段的flag和type信息 + * Convert.convert不支持 @JsonProperty,所以此处的字段命名没按照规范命名 + * Date: 2024/2/5 0:12 + */ +@Data +public class CentricAttrs { + + /** + * C8字段类型 + */ + private String Type; + + /** + * C8字段FG + */ + private String FG; + + + private String EXP; + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPath.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPath.java new file mode 100644 index 0000000..6e51350 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPath.java @@ -0,0 +1,145 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import cn.hutool.core.collection.ListUtil; +import com.centricsoftware.enhancement.modules.c8.em.DepPathType; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +@Data +public class DepPath { + + private TreeSet paths = Sets.newTreeSet() ; + private TreeSet urls = Sets.newTreeSet() ; + private String xml; + private DepPathType type; + + public static DepPathBuilder builder(){ + DepPath depPath = new DepPath(); + depPath.setType(DepPathType.URL); + DepPathBuilder builder = depPath.new DepPathBuilder(depPath); + return builder; + } + + public static DepPathXmlBuilder builderXml(){ + DepPath depPath = new DepPath(); + depPath.setType(DepPathType.XML); + DepPathXmlBuilder builder = depPath.new DepPathXmlBuilder(depPath); + return builder; + } + + /** + * 解析path,比如Child:__Parent__/Child:__Parent__,解析为Child:__Parent__和Child:__Parent__/Child:__Parent__ + * @param paths + */ + public static List getPathPart(String paths){ + List list = Lists.newArrayList(); + String[] split = paths.split("/"); + for(int i = 0;i pathPart = DepPath.getPathPart(path); + depPath.getPaths().addAll(pathPart); + return this; + } + public DepPathBuilder addPaths(String[] paths){ + if(paths.length==0){ + return this; + } + ArrayList strings = ListUtil.toList(paths); + strings.forEach(item->{ + List pathPart = DepPath.getPathPart(item); + depPath.getPaths().addAll(pathPart); + }); + return this; + } + + public DepPathBuilder addUrl(String url){ + depPath.getUrls().add(url); + return this; + } + + public DepPathBuilder xml(String xml){ + depPath.setXml(""+xml+""); + return this; + } + + public DepPathBuilder addUrls(String[] urls){ + for(String url:urls){ + depPath.getUrls().add(url); + } + return this; + } + public DepPathBuilder addUrls(List urls){ + for(String url:urls){ + depPath.getUrls().add(url); + } + return this; + } + public DepPath build(){ + return depPath; + } + } + + public class DepPathXmlBuilder{ + DepPath depPath; + DepPathXmlBuilder( DepPath depPath){ + this.depPath = depPath; + } + + public DepPathXmlBuilder xml(String xml){ + depPath.setXml(""+xml+""); + return this; + } + public DepPathXmlBuilder addPath(String path){ + addPaths(path.split(",")); + List pathPart = DepPath.getPathPart(path); + depPath.getPaths().addAll(pathPart); + return this; + } + + public DepPathXmlBuilder addPaths(String[] paths){ + if(paths.length==0){ + return this; + } + ArrayList list = ListUtil.toList(paths); + list.forEach(item->{ + List pathPart = DepPath.getPathPart(item); + depPath.getPaths().addAll(pathPart); + }); + return this; + } + + public DepPathXmlBuilder addUrls(String[] urls){ + for(String url:urls){ + depPath.getUrls().add(url); + } + return this; + } + public DepPath build(){ + return depPath; + } + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathByEntityContext.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathByEntityContext.java new file mode 100644 index 0000000..592721d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathByEntityContext.java @@ -0,0 +1,68 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import lombok.Data; +import org.apache.ibatis.reflection.Reflector; +import org.apache.ibatis.reflection.invoker.Invoker; + +import java.lang.reflect.Field; +import java.util.List; + +/** + * description:DepPath实体类查询上下文参数 + * Date: 2024/3/8 10:34 + */ +@Data +public class DepPathByEntityContext { + + /** + * 当前行URL + */ + private String url; + + /** + * 所有结果集 + */ + private List resultUrls; + + /** + * 查询结果集 + */ + private DepPathResult depPathResult; + + /** + * 缓存了类的定义信息,比如方法签名等 + */ + private Reflector reflector; + + /** + * 实体类的实例 + */ + private Object instance; + + /** + * 方法调用程序,可通过 invoker.invoke(o, new String[]{"值"})对实例进行赋值 + */ + private Invoker invoker; + + + private Class clazz; + + /** + * 实体类字段 + */ + private String fieldName; + + /** + * 实体类字段 + */ + private Field field; + + + /** + * 字段上的注解 + */ + private DepPathField depPathField; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathContext.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathContext.java new file mode 100644 index 0000000..8b8e029 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathContext.java @@ -0,0 +1,24 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathCache; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathParser; +import lombok.Data; + +/** + * description:DepPath查询时,上下文参数 + * Date: 2024/2/1 9:48 + */ +@Data +public class DepPathContext { + + /** + * 解析器组件 + */ + private DepPathParser depPathParser; + + /** + * 缓存组件 + */ + private DepPathCache depPathCache; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathExp.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathExp.java new file mode 100644 index 0000000..95b3676 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathExp.java @@ -0,0 +1,92 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.em.ParserFieldType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * description:解析器组件返回的实体类 + * Date: 2024/2/2 13:11 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DepPathExp { + + /** + * 完整的表达式,比如Style.Images.XXX.XXX + */ + private String fullExp; + + /** + * exp用分隔符分隔后,第N个的原始表达式 + */ + private String originExp; + + /** + * 对originExp做处理后的表达式:比如去除角标 + */ + private String exp; + + /** + * originExp如果还有角标,则提取角标到index + */ + private String index; + + /** + * 子表达式的字段类型:有角标的以INDEX_为前缀 + * 当前表达式执行后,值的类型;和attrs中的类型未必一致。 + * 如:在Style的URL下,表达式为Images.ModifiedBy,attrs的类型为ref, + * 但是type为可能为List(当Images存在多个时),也可能为ref。 + */ + private ParserFieldType type; + + + /** + * 只有JSONArray、JSONObject、String三种类型 + */ + private Object value; + + /** + * 存储当前值的flag和type + */ + private CentricAttrs attrs; + + /** + * 前子表达式值 + */ + private Object beforeValue; + + /** + * 后子表达式值 + */ + private Object afterValue; + + /** + * 前子表达式对象 + */ + private DepPathExp beforeExp; + + /** + * 后子表达式对象 + */ + private DepPathExp afterExp; + + /** + * 原始URL + */ + private String url; + + /** + * 重写toString方法:因为DepPathExp存在循环引用,默认的toString会溢出; + * @return + */ + @Override + public String toString(){ + return value.toString(); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResult0.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResult0.java new file mode 100644 index 0000000..5d1cfc0 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResult0.java @@ -0,0 +1,38 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.dto.C8LogReturnEntity; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Data +public class DepPathResult0 extends C8LogReturnEntity { + + @JsonProperty("Status") + private String status; + + /** + * 该值可能为空, + * 也可能是{ref": "C0/REH0151|Style"} + * 也可能是{ref": ["C0/REH0151|Style"]} + */ + @JsonProperty("CompleteResultRefs") + private Object completeResultRefs; + + /** + * 该字段可能为空 + * 也可能为{ResultNode": []} + */ + @JsonProperty("NODES") + private Object nodes; + + @JsonProperty("TotalCount") + private int totalCount; + + @JsonProperty("HasMoreResults") + private boolean hasMoreResults; + + private DepPath depPath; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResultValue.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResultValue.java new file mode 100644 index 0000000..1a060ab --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/dep/DepPathResultValue.java @@ -0,0 +1,425 @@ +package com.centricsoftware.enhancement.modules.c8.dto.dep; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import com.centricsoftware.enhancement.modules.c8.util.CentricAttributesUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * description:DepPathResult.getValue()返回类 + * Date: 2024/2/2 13:24 + */ +@NoArgsConstructor +@Slf4j +public class DepPathResultValue { + + public DepPathResultValue(Object value){ + this.value = value; + } + + /** + * 原始值 + */ + @Setter + private Object value; + + /** + * 子表达式列表 + */ + @Getter + @Setter + private List expList; + + /** + * 获取List,不去重 + */ + public List getList() { + return getList(false,String.class); + } + + /** + * 获取List:去重 + * @return + */ + public List getListRemoveDuplicate() { + return getList(true,String.class); + } + + /** + * 获取List:可配置List的泛型和是否去重 + * @param removeDuplicate 是否去重 + * @param t List泛型 + */ + public List getList(boolean removeDuplicate,Class t) { + if(value==null){ + return Lists.newArrayList(); + } + Object value1 = getValue(); + List ts = Lists.newArrayList(); + if(CentricAttributesUtil.valueIsList(value1)){ + ts = JSONUtil.parseArray(value1).toList(t); + }else if (CentricAttributesUtil.valueIsMap(value1)) { + Map convert = Convert.convert(Map.class, value1); + ts.addAll(convert.values()); + }else if(value1 instanceof String ){ + ts.add((T)value1); + }else{ + ts = JSONUtil.parseArray(value1).toList(t); + } + if(removeDuplicate) { + return ts.stream().distinct().collect(Collectors.toList()); + } + return ts; + } + + /** + * 获取Map值: + */ + public Map getMap() { + Map map = getMap(String.class, String.class); + return map; + } + + /** + * 获取Map值:指定泛型 + */ + public Map getMap(Class k, Class v) { + Map map = null; + Object value1 = getValue(); + try { + if(value1 instanceof List){ + JSONArray v1 = (JSONArray) value1; + for (Object i : v1) { + map = new HashMap<>(); + map.put((K) i, (V) i); + } + return map; + } + map = Convert.toMap(k,v, value1); + } catch (Exception e) { + HashMap kvHashMap = MapUtil.newHashMap(); + return kvHashMap; + } + if(map == null ){ + return Maps.newHashMap(); + } + return map; + } + + /** + * 获取Long类型的值 + * @return 长整型 + */ + public long getLong() { + Object v = getValue(); + try { + return NumberUtil.parseNumber(v.toString()).longValue(); + } catch (Exception e) { + log.error("表达式={},原始值={}:转化为Long失败,赋默认值0",getLastDepPathExp().getFullExp(),value); + return 0L; + } + } + + /** + * 获取枚举的Value值:比如C8_Phase:01,则返回01 + * @return 枚举的Value + */ + public String getEnumValue(){ + String str = getValue().toString(); + String[] split = str.split(":"); + if (split.length > 1) { + return split[1]; + } + return ""; + } + + /** + * 获取枚举的Key值:比如C8_Phase:01,则返回C8_Phase + * @return 枚举的Key,不带冒号 + */ + public String getEnumKey(){ + String str = getValue().toString(); + String[] split = str.split(":"); + if (split.length > 1) { + return split[0]; + } + return ""; + } + + /** + * 获取日期 + */ + public Date getDate() { + long value = getLong()*1000; + if (0L == value) { + return null; + } + return new Date(value); + } + + /** + * 获取日期字符格式值 + * @return yyyy-MM-dd + */ + public String getDateStr() { + return getDateFormat("yyyy-MM-dd"); + } + + /** + * 获取日期时间字符格式值 + * @return yyyy-MM-dd HH:mm:ss + */ + public String getDateAndTimeStr() { + return getDateFormat("yyyy-MM-dd HH:mm:ss "); + } + + /** + * 格式化获取日期字符值 + * @param format 格式化,比如yyyy-MM-dd、yyyy-MM-dd HH:mm:ss + * @return + */ + public String getDateFormat(String format) { + long value = getLong()*1000; + if (0L == value) { + return ""; + } + Timestamp ts = new Timestamp(value); + SimpleDateFormat sm = new SimpleDateFormat(format); + return sm.format(ts); + } + + /** + * 获取字符串类型的值 + */ + public String getStr(){ + if(value==null){ + return ""; + } + String str = Convert.toStr(getValue()); + if("{}".equals(str)){ + return ""; + } + return str; + } + + /** + * 获取整形的值 + */ + public int getInt(){ + String value = getStr(); + try { + return NumberUtil.parseNumber(value).intValue(); + } catch (Exception e) { + log.error("表达式={},原始值={}:转化为Int失败,赋默认值0",getLastDepPathExp().getFullExp(),getValue()); + return 0; + } + } + + /** + * 获取BigDecimal模式,并且设置保留位数和四舍五入模式 + * @param newScale 保留小数位 + * @param roundingMode 四舍五入模式 + * - CEILING: 舍入模式向正无穷大舍入。 +* - DOWN: 舍入模式向零舍入。 + * - FLOOR: 舍入模式向负无穷大舍入。 + * - HALF_DOWN: 舍入模式向“最近邻居”舍入,除非两个邻居等距,在这种情况下向下舍入。 + * - HALF_EVEN: 舍入模式向“最近邻居”舍入,除非两个邻居等距,在这种情况下,向着偶邻居舍入。 + * - HALF_UP: 舍入模式向“最近邻居”舍入,除非两个邻居等距,在这种情况下向上舍入。 + * - UNNECESSARY: 舍入模式断言所请求的操作具有精确结果,因此不需要舍入。 + * - UP: 舍入模式从零开始舍入。 + */ + public BigDecimal getBigDecimal(int newScale,RoundingMode roundingMode) { + String str = getStr(); + BigDecimal bigDecimal = NumberUtil.toBigDecimal(str); + bigDecimal = bigDecimal.setScale(newScale, roundingMode); + return bigDecimal; + } + + /** + * 四舍五入获取 BigDecimal + * @param newScale 保留小数位 + * @return + */ + public BigDecimal getBigDecimalByHalfUp(int newScale) { + return getBigDecimal(newScale,RoundingMode.HALF_UP); + } + + /** + * 获取Double值,保留所有精度 + */ + public double getDouble(){ + String str = getStr(); + double v = 0.0; + try { + v = NumberUtil.parseNumber(str).doubleValue(); + } catch (Exception e) { + log.error("表达式={},原始值={}:转化为Double失败,赋默认值0.0",getLastDepPathExp().getFullExp(),value); + } + return v; + } + + /** + * 获取Double值,四舍五入模式 + * @param newScale 保留小数位 + */ + public double getDouble(int newScale){ + BigDecimal bigDecimal = getBigDecimalByHalfUp(newScale); + return bigDecimal.doubleValue(); + } + + /** + * 获取布尔类型:值为true、是、Y,返回true,否则为false + * @return + */ + public boolean getBoolean() { + String str = getStr(); + return "true".equals(str)||"是".equals(str)||"Y".equals(str); + } + + /** + * 以conjunction 为分隔符将多个对象转换为字符串 + * @param conjunction 为分隔符将多个对象转换为字符串 + * @param removeDuplicate 是否去重 + * @return 连接后的字符串 + */ + public String getStrForList(String conjunction,boolean removeDuplicate){ + List list = getList(removeDuplicate, String.class); + return StrUtil.join(conjunction, list); + } + + /** + * 以conjunction 为分隔符将多个对象转换为字符串 + * @param conjunction 为分隔符将多个对象转换为字符串 + * @return 连接后的字符串 + */ + public String getStrForList(String conjunction){ + return getStrForList(conjunction, true); + } + + /** + * 以半角逗号【,】为分隔符将多个对象转换为字符串 + * @return 连接后的字符串 + */ + public String getStrForList(){ + return getStrForList(String.valueOf(StrUtil.C_COMMA)); + } + + /** + * 根据C8返回的类型,返回枚举描述 + * @return + */ + public Object getEnumDescByType() { + DepPathExp lastDepPathExp = getLastDepPathExp(); + String type = lastDepPathExp.getAttrs().getType(); + if("enum".equals(type)){ + return getEnumDesc(); + } + if(CentricAttributesUtil.isList(lastDepPathExp)){ + return getEnumDescList(false); + } + return getEnumDesc(); + } + + /** + * 根据C8返回的类型,返回枚举值 + * @return + */ + public Object getEnumValueByType() { + DepPathExp lastDepPathExp = getLastDepPathExp(); + String type = lastDepPathExp.getAttrs().getType(); + if("enum".equals(type)){ + return getEnumValue(); + } + if(CentricAttributesUtil.isList(lastDepPathExp)){ + return getEnumValueList(false); + } + return getEnumValue(); + } + + /** + * 获取枚举描述值 + */ + public String getEnumDesc() { + EnumCache enumCache = getEnumCache(); + String str = getStr(); + return enumCache.getDescByFullname(str); + } + + /** + * 获取枚举描述值:会先对List去重 + */ + public List getEnumDescListRemoveDuplicate() { + return getEnumDescList(true); + } + + /** + * 获取枚举描述值 + * @param removeDuplicate true表示对list值去重 + */ + public List getEnumDescList(boolean removeDuplicate) { + EnumCache enumCache = getEnumCache(); + List reList = Lists.newArrayList(); + List list = getList(removeDuplicate,String.class); + for (String str : list) { + String descByFullname = enumCache.getDescByFullname(str); + if (StrUtil.isNotBlank(descByFullname)) { + reList.add(descByFullname); + } + } + return reList; + } + + /** + * 获取枚举值List + */ + public List getEnumValueList(boolean removeDuplicate) { + List list = getList(removeDuplicate,String.class); + List collect = list.stream().filter(i -> i.contains(StrUtil.COLON)).map(i -> i.split(StrUtil.COLON)[1]).collect(Collectors.toList()); + return collect; + } + + public Object getValue(){ + return value==null?"":value; + } + + private EnumCache enumCache; + private EnumCache getEnumCache() { + if (enumCache != null) return this.enumCache; + EnumCache bean = SpringUtil.getBean(EnumCache.class); + this.enumCache = bean; + return this.enumCache; + } + + /** + * 获取最后一个子表达式 + * @return + */ + public DepPathExp getLastDepPathExp(){ + List expList1 = getExpList(); + if(expList1==null||expList1.size()==0){ + return new DepPathExp(); + } + return expList1.get(expList1.size()-1); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractParameterEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractParameterEntity.java new file mode 100644 index 0000000..cf9d2a5 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractParameterEntity.java @@ -0,0 +1,23 @@ +package com.centricsoftware.enhancement.modules.c8.dto.nativeexport; + +import com.google.common.collect.Lists; +import lombok.Data; + +import java.util.List; + +/** + * NativeExport参数实体类 + */ +@Data +@Deprecated +public class ExtractParameterEntity { + private String queryXml; + private String extractXml; + private String limitUrls=""; + private String delimiter=""; + private int startRow=1; + private int endRow=2147483647; + private String userUrl=""; + private int traceLevel=0; + private List resultList = Lists.newArrayList(); +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractResultEntity.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractResultEntity.java new file mode 100644 index 0000000..fd9f221 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/dto/nativeexport/ExtractResultEntity.java @@ -0,0 +1,24 @@ +package com.centricsoftware.enhancement.modules.c8.dto.nativeexport; + +import lombok.Data; + + +/** + * NativeExport 返回值实体类 + */ +@Data +@Deprecated +public class ExtractResultEntity { + private Integer startId; + private String name; + private String seq; + private String valueString; + private Double valueNumber; + private Boolean valueBoolean; + private String valueUrl; + private String attrId; + + public String getAttrId(){ + return getName()+getSeq(); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/CentricAttributeType.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/CentricAttributeType.java new file mode 100644 index 0000000..e88e2e9 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/CentricAttributeType.java @@ -0,0 +1,48 @@ +package com.centricsoftware.enhancement.modules.c8.em; + +import cn.hutool.core.util.StrUtil; + +/** + * C8的字段类型,除了Enum会有衍生类型,其他的类型与C8一一对应 + * 1、为了将类型做统一管理,此处并没有根据组件单独定义枚举类 + * 2、部分组件未必支持所有的类型,对于不支持的类型,将走默认的处理逻辑 + */ +public enum CentricAttributeType { + + REF("ref","ref"), + REF_LIST("reflist","reflist"), + STRING("string","string"); + + /** + * Java中识别的字段类型,Java中会根据type做不通的逻辑处理。type与c8Type可能是多对1的关系 + * 比如:type为enum、enum_desc、enum_value等对应的c8Type为enum + */ + private String type; + + /** + * C8的字段类型 + */ + private String c8Type; + + + CentricAttributeType(String type, String c8Type ){ + this.type = type; + this.c8Type = c8Type; + } + + public boolean isList() { + if(StrUtil.endWithAny(this.c8Type,"list","vector","set")){ + return true; + } + return false; + } + + public boolean isMap() { + if(StrUtil.endWithAny(this.c8Type,"map")){ + return true; + } + return false; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathEntityType.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathEntityType.java new file mode 100644 index 0000000..da97be5 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathEntityType.java @@ -0,0 +1,93 @@ +package com.centricsoftware.enhancement.modules.c8.em; + +import lombok.Getter; + +import java.util.Date; +import java.util.List; + +/** + * DepPath实体类查询中注解的类型 + */ +@Getter +public enum DepPathEntityType { + + /** + * 对应C8为ref类型,实体类字段为String + */ + STRING("string",String.class), + + DATA_STRING("data_string", String.class), + + DATA("data_string", Date.class), + + REF("ref",String.class), + + /** + * 对应C8为double类型,实体类字段为Double + */ + DOUBLE("double",Double.class), + + /** + * 对应C8为integer类型,实体类字段为Integer + */ + INTEGER("integer",Integer.class), + + /** + * 对应C8为file类型,实体类字段为String + */ + FILE("file",String.class), + + /** + * list相关类型 + */ + LIST("list", List.class), + + /** + * 枚举描述List + */ + ENUM_DESC_LIST("enum_desc_list", List.class), + + /** + * 枚举值List + */ + ENUM_VALUE_LIST("enum_value_list", List.class), + + /** + * 枚举value + */ + ENUM_VALUE("enum_value", String.class), + + /** + * 枚举描述 + */ + ENUM_DESC("enum_desc", String.class), + + /** + * 枚举描述List转字符 + */ + ENUM_DESC_LIST_TO_String("enum_desc_list_to_string", String.class), + + /** + * 枚举值List转字符 + */ + ENUM_VALUE_LIST_TO_String("enum_value_list_to_string", String.class), + + /** + * 对应C8为file类型,实体类字段为String + */ + LIST_TO_String("list_to_string", String.class); + + private String name; + + /** + * 实体类对应的字段类型 + */ + private Class clazz; + + + DepPathEntityType(String name, Class clazz){ + this.name = name; + this.clazz = clazz; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathType.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathType.java new file mode 100644 index 0000000..41fc3b2 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/DepPathType.java @@ -0,0 +1,19 @@ +package com.centricsoftware.enhancement.modules.c8.em; + +/** + * 语言包枚举,用户EnumUtil缓存工具 + */ +public enum DepPathType { + XML("xml"), + URL("url"); + + private String value; + + DepPathType(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/EnumTypeForCV.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/EnumTypeForCV.java new file mode 100644 index 0000000..83dd4ee --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/EnumTypeForCV.java @@ -0,0 +1,28 @@ +package com.centricsoftware.enhancement.modules.c8.em; + +/** + * Custom View Enum枚举类型 + */ +public enum EnumTypeForCV { + + /** + * 原始值 + */ + ORIGIN("origin"), + + /** + * 枚举的Value + */ + KEY("key"), + + /** + * 枚举的描述值 + */ + DESCRIPTION("description"); + + private String value; + + EnumTypeForCV(String value ){ + this.value = value; + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/ParserFieldType.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/ParserFieldType.java new file mode 100644 index 0000000..c6a2c17 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/em/ParserFieldType.java @@ -0,0 +1,49 @@ +package com.centricsoftware.enhancement.modules.c8.em; + +/** + * C8的字段类型,除了Enum会有衍生类型,其他的类型与C8一一对应 + * 1、为了将类型做统一管理,此处并没有根据组件单独定义枚举类 + * 2、部分组件未必支持所有的类型,对于不支持的类型,将走默认的处理逻辑 + */ +public enum ParserFieldType { + + /** + * MAP或者List有角标:表达式解析过程的中间状态, + * 当获取到值后,就会根据值的类型,自动转换为Index_的类型; + * 比如INDEX_LIST、INDEX_MAP + */ + INDEX("index","index"), + + /** + * 表达式有角标:list + */ + INDEX_LIST("index_list","index_list"), + + /** + * 表达式有角标:map + */ + INDEX_MAP("index_map","index_map"), + + LIST("list","list"), + MAP("map","map"), + REF("ref","ref"), + OTHER("other","other"); + + /** + * Java中识别的字段类型,Java中会根据type做不通的逻辑处理。type与c8Type可能是多对1的关系 + * 比如:type为enum、enum_desc、enum_value等对应的c8Type为enum + */ + private String type; + + /** + * C8的字段类型 + */ + private String c8Type; + + + ParserFieldType(String type, String c8Type ){ + this.type = type; + this.c8Type = c8Type; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8Feign.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8Feign.java new file mode 100644 index 0000000..4ad0875 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8Feign.java @@ -0,0 +1,103 @@ +package com.centricsoftware.enhancement.modules.c8.feign; + +import com.centricsoftware.enhancement.modules.c8.dto.ExpResultEntity; +import com.centricsoftware.enhancement.modules.c8.dto.OperationResultEntity; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResult0; +import feign.Response; +import feign.codec.Encoder; +import feign.form.spring.SpringFormEncoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +@Lazy +@FeignClient(url = "${cs.plm.http}",name = "c8-java-op",configuration = C8Feign.MultipartSupportConfig.class) +public interface C8Feign { + +// /** +// * 执行表达式 +// */ +// @PostMapping(value = "/csi-requesthandler/RequestHandler", +// headers = {"Content-Type=multipart/form-data;charset=UTF-8;boundary=C6EE5092158B4206939DC028F7CEC00F"}) +// ExpResultEntity executeExp(@RequestParam Map map); + + /** + * 执行表达式 + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler", + consumes = {"application/x-www-form-urlencoded;charset=UTF-8"}) + ExpResultEntity executeExp(Map map); + + /** + * 执行DepPath + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler", + consumes = {"application/x-www-form-urlencoded;charset=UTF-8"}) + DepPathResult0 depPath(Map map); +// headers = {"Content-Type=multipart/form-data;charset=UTF-8;boundary=C6EE5092158B4206939DC028F7CEC00F"}) +// DepPathResult0 depPath(@RequestParam Map map); + + /** + * 执行ProcessNode + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler", + consumes = {"application/x-www-form-urlencoded;charset=UTF-8"}) + OperationResultEntity excuteOperation(Map map); + + /** + * 获取文件 + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler?c8_file=true", + consumes = {"application/x-www-form-urlencoded;charset=UTF-8"}) + Response getFileFromNode(Map map); + + /** + * 上传文件 + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler", + produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, + consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) + OperationResultEntity excuteOperationFile(@RequestPart("file") MultipartFile file,@RequestParam Map map); + + /** + * 上传文件 + */ + @PostMapping(value = "/csi-requesthandler/RequestHandler", + produces = {MediaType.MULTIPART_FORM_DATA_VALUE}, + consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) + OperationResultEntity publishFile(@RequestPart("FL") MultipartFile file, @RequestParam Map map); + + + class MultipartSupportConfig { + @Autowired + private ObjectFactory messageConverters; + +// @Bean +// public Decoder feignDecoder(){ +// return new ResponseEntityDecoder(new SpringDecoder(messageConverters)); +// } + + @Bean + public Encoder feignFormEncoder() { + return new SpringFormEncoder(new SpringEncoder(messageConverters)); + } +// +// @Bean +// Encoder feignFormEncoder(ObjectFactory converters) { +// return new FormEncoder(new SpringEncoder(converters)); +// } + + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8LoginFeign.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8LoginFeign.java new file mode 100644 index 0000000..c739565 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/feign/C8LoginFeign.java @@ -0,0 +1,42 @@ +package com.centricsoftware.enhancement.modules.c8.feign; + +import com.centricsoftware.enhancement.modules.c8.dto.C8LogReturnEntity; +import feign.codec.Encoder; +import feign.form.FormEncoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.Map; + +/** + * C8登录操作 + */ +@FeignClient(url = "${cs.plm.http}",name = "c8-java-login",configuration = C8LoginFeign.MultipartSupportConfig.class) +public interface C8LoginFeign { + + @PostMapping(value = "/csi-requesthandler/RequestHandler", + consumes = {"application/x-www-form-urlencoded;charset=UTF-8"}) + C8LogReturnEntity login( Map formParams); + + class MultipartSupportConfig { + @Autowired + private ObjectFactory messageConverters; + +// @Bean +// public Encoder feignFormEncoder() { +// return new SpringFormEncoder(new SpringEncoder(messageConverters)); +// } + + @Bean + Encoder feignFormEncoder(ObjectFactory converters) { + return new FormEncoder(new SpringEncoder(converters)); + } + + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8LoginService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8LoginService.java new file mode 100644 index 0000000..f1c122e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8LoginService.java @@ -0,0 +1,132 @@ +package com.centricsoftware.enhancement.modules.c8.service; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.c8.dto.C8LogReturnEntity; +import com.centricsoftware.enhancement.modules.c8.feign.C8LoginFeign; +import com.centricsoftware.enhancement.util.http.C8HttpUtil; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Map; + +@Service +@Slf4j +public class C8LoginService { + + public static String Successful = "Successful"; + + public static String Failed = "Failed"; + + public static String SRV_ERR_SiteAdmin_INVALID_SESSION = "SRV_ERR_SiteAdmin_INVALID_SESSION"; + + @Value("${cs.plm.user}") + private String user; + + @Value("${cs.plm.pwd}") + private String pwd; + + @Value("${cs.plm.http}") + private String http; + + @Autowired + C8LoginFeign c8LoginFeign; + + @Autowired + @Lazy + ObjectMapper mapper; + + /** + * 登录C8 + * @return + */ + public synchronized boolean login(){ + Map map = Maps.newHashMap(); + map.put("Module","DataSource"); + map.put("Operation","SimpleLogin"); + map.put("LoginID",user); + map.put("Password",pwd); + map.put("OutputJSON","2"); + map.put("Fmt.Version","2"); + C8LogReturnEntity login = c8LoginFeign.login(map); + String status = login.getStatus(); + if(Successful.equals(status)){ + return true; + } + //可在此做一些通知操作,比如发邮件通知管理员等 + throw new RuntimeException(StrFormatter.format("登录异常,请联系管理员。异常信息:{};{}",login.getError(),login.getErrorAdmin())); + } + + /** + * 判断是否需要重新登录C8 + * @return 返回true表示需要重试 + */ + public boolean checkSession(Request originalRequest, Response response) throws IOException { + HttpUrl url = originalRequest.url();//获取访问的URL + String path = url.uri().getPath();//获取相对访问路径 + String operation = url.queryParameter("Operation"); + if(StrUtil.isBlank(path)||!path.contains("csi-requesthandler/RequestHandler")||"SimpleLogin".equals(operation)){ + return false; + } + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + //C8交互时,校验是否需要检查session是否过期 + String content = C8HttpUtil.copyResponseBody(response); + C8LogReturnEntity convert = null; + try { + convert = mapper.convertValue(JSONUtil.parse(content), C8LogReturnEntity.class);//将body转成实体类 + } catch (Exception e) { + String isFile = originalRequest.url().queryParameter("c8_file"); + if(!"true".equals(isFile)){ + log.error("返回报文为:{}",content); + log.error("验证session过期拦截器报错:",e); + } + } + if(convert!=null&&Failed.equals(convert.getStatus())){ + if(SRV_ERR_SiteAdmin_INVALID_SESSION.equals(convert.getError())){ +// checkLoginNum(originalRequest);//校验登录次数 + log.info("Session过期,重新登录。",convert.getErrorAdmin()); + log.info("=====================登录开始==================="); + boolean login = login();//重新登录 + if(login){ + log.info("=====================登录成功==================="); + }else{ + log.info("=====================登录失败==================="); + } + return true; + } + } + return false; + } + + /** + * 判断登录次数,超过三次不再登录 + * 该功能暂时不启用:处于半成品状态 + * @param originalRequest + * @return + */ + public void checkLoginNum(Request originalRequest){ + String head = "C8_LoginNum"; + String count = originalRequest.header(head); + if(StrUtil.isNotBlank(count)){ + int i = Integer.parseInt(count); + if(i>=3){ + log.error("自动登录次数超过三次,不再自动登录,抛出运行时异常"); + throw new RuntimeException("Session过期,自动登录失败,请联系管理员"); + } + }else{ + originalRequest.newBuilder().addHeader("C8_LoginNum","1"); + } + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8NodeService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8NodeService.java new file mode 100644 index 0000000..298dc98 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8NodeService.java @@ -0,0 +1,450 @@ +package com.centricsoftware.enhancement.modules.c8.service; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.utils.FileUtil; +import com.centricsoftware.commons.utils.SpringUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.component.design.chain.IDepPathSearchChain; +import com.centricsoftware.enhancement.modules.c8.dto.OperationResultEntity; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8DealExpResultService; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8OperationService; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8SearchService; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +@Slf4j +@Service +public class C8NodeService { + + @Resource + C8DealExpResultService c8DealExpResultService; + + @Resource + C8SearchService c8SearchService; + + @Resource + C8OperationService c8OperationService; + + /** + * ======================================================Operation 相关============================================== + */ + + /** + * 通过传入的urls,拼成or查询语句 + */ + public String getNativeExportXml(String type, List urls, String filterXml) { + if (urls.size() == 0) { + throw new RuntimeException("传入的url不能为空"); + } + StringBuilder xml = new StringBuilder(); + xml.append(""); + xml.append(""); + for (String url : urls) { + xml.append(""); + } + xml.append("").append(filterXml); + return getNativeExportXml(xml.toString()); + } + + public String getNativeExportXmlByUrl(String url) { + return getNativeExportXml(""); + } + + /** + * 将普通search转换为数据库查询语句 + * + * @return 新的url + */ + public String getNativeExportXml(String xml) { + OperationResultEntity operationResultEntity = c8OperationService.getNativeExportXml(xml); + if (!C8LoginService.Successful.equals(operationResultEntity.getStatus())) { + String format = StrFormatter.format("转换查询语句失败。错误信息={}", operationResultEntity.getErrorAdmin()); + throw new RuntimeException(format); + } + String url = JSONUtil.parseObj(operationResultEntity.getDbQuery()).getStr("XML", ""); + if (StrUtil.isBlank(url)) { + throw new RuntimeException("转换查询语句失败。"); + } + return url; + } + + /** + * 获取文件 + * + * @param url C开头的URL + * @param attribute URL对应的属性 + */ + public InputStream getFileFromNode(String url, String attribute) { + return c8OperationService.getFileFromNode(url, attribute); + } + + /** + * 获取图片小图 + */ + public InputStream getImageSmallImage(String url) { + return c8OperationService.getFileFromNode(url, "SmallImage"); + } + + /** + * 获取图片缩略图 + */ + public InputStream getImageThumbnail(String url) { + return c8OperationService.getFileFromNode(url, "Thumbnail"); + } + + /** + * 获取图片原图 + */ + public InputStream getImageViewable(String url) { + return c8OperationService.getFileFromNode(url, "Viewable"); + } + + /** + * 通过文件柜地址获取图片 + */ + public InputStream getFileFromDirectAddr(String url) { + return c8OperationService.getFileFromDirectAddr(url); + } + + /** + * 执行 Operation: 返回nodes + */ + public OperationResultEntity processNode(String xml, boolean singleThreaded, boolean returnNodes, boolean omitResults) { + OperationResultEntity operationResultEntity = c8OperationService.excuteOperation(xml, singleThreaded, returnNodes, omitResults); + if (!C8LoginService.Successful.equals(operationResultEntity.getStatus())) { + String format = StrFormatter.format("执行Operation报错.错误信息={},XML={}", operationResultEntity.getErrorAdmin(), xml); + throw new RuntimeException(format); + } + return operationResultEntity; + } + + public OperationResultEntity processNode(String xml) { + return processNodeMin(xml); + } + + /** + * 创建URL + * + * @return 新的url + */ + public String createURL() { + OperationResultEntity operationResultEntity = c8OperationService.createUrl(); + if (!C8LoginService.Successful.equals(operationResultEntity.getStatus())) { + String format = StrFormatter.format("创建URL报错。错误信息={}", operationResultEntity.getErrorAdmin()); + throw new RuntimeException(format); + } + String url = JSONUtil.parseObj(operationResultEntity.getUrls()).getStr("URL", ""); + if (StrUtil.isBlank(url)) { + throw new RuntimeException("创建URL报错。"); + } + return url; + } + + public String publishImage(MultipartFile file, String fileName) throws Exception { + String fileAddr = publishFile(file, fileName); + StringBuffer xml = new StringBuffer(); + String imageUrl = createURL(); + xml.append("\n") + .append("\n") + .append(""); + processNode(xml.toString()); + return imageUrl; + } + + public String publishImage(MultipartFile file) throws Exception { + return publishImage(file,file.getOriginalFilename()); + } + + /** + * 发布文件,可发布视频 + * @param file 文件 + * @param fileName 文件名,优先获取file.getOriginalFilename(),如果file.getOriginalFilename()有值,fileName可传入空 + * @return 文件柜地址 + */ + public String publishFile(MultipartFile file, String fileName) throws Exception{ + MultipartFile multipartFile = FileUtil.getMultipartFile(file.getInputStream(), file.getOriginalFilename() == null || file.getOriginalFilename() == "" ? fileName : file.getOriginalFilename(), "FL"); + OperationResultEntity operationResultEntity = c8OperationService.publishFile(multipartFile, null); + if (!C8LoginService.Successful.equals(operationResultEntity.getStatus())) { + String format = StrFormatter.format("发布文件报错。错误信息={}", operationResultEntity.getErrorAdmin()); + throw new RuntimeException(format); + } + JSONObject fileInfo = JSONUtil.parseObj(operationResultEntity.getFileInfo()); + String url = fileInfo.getStr("URL", ""); + if (StrUtil.isBlank(url)) { + throw new RuntimeException("发布文件报错。"); + } + return url; + } + + /** + * 不返回修改的node + * + * @param xml + * @return + */ + public OperationResultEntity processNodeMin(String xml) { + return processNode(xml, true, true, true); + } + + /** + * 不返回修改的node的所有字段 + * + * @param xml + * @return + */ + public OperationResultEntity processNodeMax(String xml) { + return processNode(xml, true, true, false); + } + + + /** + * ======================================================查询 相关============================================== + */ + + @Resource + IDepPathSearchChain iDepPathSearchChain; + + + /** + * 通过实体类查询:DepPath.XML DepPath.URL共用 + */ + public List depPathByEntity(DepPath depPath, Class clazz) { + return iDepPathSearchChain.execute(depPath, clazz, null); + } + + /** + * 通过实体类查询:DepPath.XML DepPath.URL共用 + */ + public List depPathByEntity(DepPath depPath, Class clazz, Consumer function) { + List list; + try { + return iDepPathSearchChain.execute(depPath, clazz, function); + } catch (Exception e) { + log.error("获取数据失败", e); + throw new RuntimeException(e); + } + } + + /** + * DepPath xml查询 + */ + public DepPathResult depPathByXml(DepPath depPath, int start, int end) { + return c8SearchService.depPathByXml(depPath, start, end); + } + + /** + * DepPath xml查询 + */ + public DepPathResult depPathByXml(DepPath depPath) { + return c8SearchService.depPathByXml(depPath); + } + + + public String queryFirstByNodeName(String type, String typeName) { + String xml = C8Search.newSearch(type).attr("Node Name", "EQ", typeName).getXml(); + return queryFirstByXML(xml); + } + + public String queryFirstByXML(String xml) { + List strings = queryByXML(xml); + if (strings.size() == 0) { + return ""; + } + return strings.get(0); + } + + /** + * 通过xml查询 + */ + public List queryByXML(String xml) { + DepPath build = DepPath.builderXml().xml(xml).build(); + return c8SearchService.depPathByXml(build).getCompleteResultRefs(); + } + + + /** + * DepPath url查询: DepPath + */ + public DepPathResult depPathByUrl(DepPath depPath) { + return c8SearchService.depPathByUrl(depPath); + } + + /** + * DepPath url查询 : url + */ + public Map querySingleUrl(String url) { + DepPath build = DepPath.builder().addUrl(url).build(); + DepPathResult result = c8SearchService.depPathByUrl(build); + JSONObject jsonObject = result.getAllNodes().get(url); + return Convert.toMap(String.class, String.class, jsonObject); + } + + /** + * DepPath url: url + */ + public DepPathResult queryUrl(String url) { + ArrayList strings = Lists.newArrayList(url); + return queryUrl(strings); + } + + /** + * DepPath url : urls + */ + public DepPathResult queryUrl(List url) { + DepPath build = DepPath.builder().addUrls(url).build(); + return c8SearchService.depPathByUrl(build); + } + + /** + * DepPath url: urls + paths + */ + public DepPathResult queryUrl(List url, List paths) { + return queryUrl(url, (String[]) paths.toArray()); + } + + /** + * DepPath url: urls + paths + */ + public DepPathResult queryUrl(List url, String[] paths) { + DepPath build = DepPath.builder().addUrls(url).addPaths(paths).build(); + return c8SearchService.depPathByUrl(build); + } + + /** + * ======================================================表达式相关============================================== + */ + + /** + * 执行表达式,返回 String + */ + public String queryExpressionResult(String exp, String url) { + return c8DealExpResultService.queryExpressionResult(exp, url); + } + + /** + * 执行表达式,返回boolean + */ + public boolean queryExpressionBoolean(String exp, String url) { + return Boolean.parseBoolean(c8DealExpResultService.queryExpressionResult(exp, url)); + } + + /** + * 执行表达式,返回 Map + * + * @return + */ + public Map queryExpressionMap(String exp, String url) { + return c8DealExpResultService.queryExpressionMap(exp, url); + } + + /** + * 执行表达式,返回 Date + */ + public Date queryExpressionDate0(String exp, String url) { + return c8DealExpResultService.queryExpressionDate0(exp, url); + } + + /** + * 执行表达式,返回字符串日期:yyyy-MM-dd + * + * @return + */ + public String queryExpressionDate(String exp, String url) { + return c8DealExpResultService.queryExpressionDate(exp, url); + } + + /** + * 执行表达式,返回字符串日期+时间:yyyy-MM-dd hh:mm:ss + * + * @return + */ + public String queryExpressionTime(String exp, String url) { + return c8DealExpResultService.queryExpressionTime(exp, url); + } + + /** + * 执行表达式,返回 List + * + * @return + */ + public List queryExpressionList(String exp, String url) { + return c8DealExpResultService.queryExpressionList(exp, url); + } + + public String[] queryExpressionArray(String exp, String url) { + List list = queryExpressionList(exp, url); + String[] re = list.toArray(new String[list.size()]); + return re; + } + + + /** + * 执行表达式,返回 enum的key + * + * @return + */ + public String queryExpressionEnumkey(String exp, String url) { + return c8DealExpResultService.queryExpressionEnumkey(exp, url); + } + + /** + * 执行表达式,返回Object + */ + public Object queryExpressionResultObject(String exp, String url) { + return c8DealExpResultService.queryExpressionResultObject(exp, url); + } + + /** + * 获取配置信息 + * @return CsProperties + */ + public CsProperties getProperties(){ + return SpringUtil.getBean(CsProperties.class); + } + + /** + * 判断当前日期是否是假期 + * @return + */ + public boolean isTodayHoliday(){ + return isSomedayHoliday(new Date()); + } + + /** + * 判断某个给定日期是否是假期 + * @return + */ + public boolean isSomedayHoliday(Date date){ + if(date!=null){ + String queryXML = "\n" + + "\n" + + "\n" + + ""; + List list = queryByXML(queryXML); + return list.size()>0; + }else + return false; + + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8Service.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8Service.java new file mode 100644 index 0000000..2bfd3b2 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/C8Service.java @@ -0,0 +1,15 @@ +package com.centricsoftware.enhancement.modules.c8.service; + +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class C8Service { + + @Autowired + public C8Feign c8Feign; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8DealExpResultService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8DealExpResultService.java new file mode 100644 index 0000000..80d0d7c --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8DealExpResultService.java @@ -0,0 +1,128 @@ +package com.centricsoftware.enhancement.modules.c8.service.curd; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.dto.ExpResultEntity; +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class C8DealExpResultService{ + + @Autowired + public C8Feign c8Feign; + + /** + * 执行表达式,返回String + */ + public String queryExpressionResult(String exp,String url){ + Object o = queryExpressionResultObject(exp, url); + return Convert.toStr(o); + } + + /** + * 执行表达式,返回 Map + * @return + */ + public Map queryExpressionMap(String exp, String url){ + Object o = queryExpressionResultObject(exp, url); + return doQueryExpressionMap(o); + } + + private Map doQueryExpressionMap(Object o){ + if(o==null){ + return Maps.newHashMap(); + } + String s = o.toString(); + if("{}".equals(s)|| StrUtil.isBlank(s)){ + return Maps.newHashMap(); + } + s = s.substring(1,s.length()-1);//去前后的花括号 + HashMap map = Maps.newHashMap(); + String[] split = s.split(","); + for(String i : split){ + String[] split1 = i.split("="); + if(split1.length<2) continue; + map.put(split1[0],split1[1]); + } + return map; + } + + /** + * 执行表达式,返回 Date + */ + public Date queryExpressionDate0(String exp, String url){ + long o = Long.parseLong(queryExpressionResult(exp, url)); + return DateUtil.date(o);//返回的时间为秒级,DateUtil.date只支持毫秒级,所以要乘以1000 + } + + + /** + * 执行表达式,返回字符串日期:yyyy-MM-dd + * @return + */ + public String queryExpressionDate(String exp, String url){ + Date date = queryExpressionDate0(exp, url); + return DateUtil.formatDate(date); + } + + /** + * 执行表达式,返回字符串日期+时间:yyyy-MM-dd hh:mm:ss + * @return + */ + public String queryExpressionTime(String exp, String url){ + Date date = queryExpressionDate0(exp, url); + return DateUtil.formatDateTime(date); + } + + /** + * 执行表达式,返回 List + * @return + */ + public List queryExpressionList(String exp, String url){ + Object o = queryExpressionResultObject(exp, url); + return Convert.toList(String.class,o); + } + + /** + * 执行表达式,返回 enum的key + * @return + */ + public String queryExpressionEnumkey(String exp, String url){ + String fullName = queryExpressionResult(exp, url); + if (fullName.indexOf(":") > 0) { + if (!fullName.endsWith(":")) { + fullName = fullName.substring(fullName.indexOf(":") + 1); + } else { + fullName = ""; + } + } + return fullName; + } + + /** + * 执行表达式,返回Object + */ + public Object queryExpressionResultObject(String exp,String url){ + log.debug("执行表达式:url={};exp={}",url,exp); + Map map = Maps.newHashMap(); + map.put("Module","Expression"); + map.put("Operation","TryJustExpression"); + map.put("URL",url); + map.put("Expression",exp); + map.put("OutputJSON","2"); + ExpResultEntity login = c8Feign.executeExp(map); + return Convert.toStr(login.getResult().getValue()); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8OperationService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8OperationService.java new file mode 100644 index 0000000..9981584 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8OperationService.java @@ -0,0 +1,174 @@ +package com.centricsoftware.enhancement.modules.c8.service.curd; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.dto.OperationResultEntity; +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import com.google.common.collect.Maps; +import feign.Response; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +@Slf4j +@Service +public class C8OperationService { + + @Autowired + public C8Feign c8Feign; + + /** + * 执行 Operation + * + * @param xml xml + * @param singleThreaded 是否单线程执行 + * @param returnNodes 是否返回node详细 + * @param omitResults 是否简单返回 + */ + public OperationResultEntity excuteOperation(String xml, boolean singleThreaded, boolean returnNodes, boolean omitResults) { + String format = StrFormatter.format("{}", xml); + Map map = Maps.newHashMap(); + map.put("Module", "NodeProcessor"); + map.put("Operation", "Execute"); + map.put("UpdatedNodes", format); + map.put("SingleThreaded", singleThreaded);//多线程执行 + map.put("OutputJSON", "2");//返回json + map.put("ReturnNodes", returnNodes);//是否返回node详细 + map.put("OmitResults", omitResults);//简单返回 + map.put("ReturnCNLs", "All");// None, Modified, Deleted, All + return c8Feign.excuteOperation(map); + } + + /** + * 创建URL + * + * @return + */ + public OperationResultEntity createUrl() { + Map map = Maps.newHashMap(); + map.put("Module", "NodeProcessor"); + map.put("Operation", "CreateNodeURL"); + map.put("OutputJSON", "2");//返回json + map.put("Count", "1"); + return c8Feign.excuteOperation(map); + } + + + /** + * 上传文件,可用于上传视频文件, + * @param file 文件 + * @param nodeUrl 上传文件所在的url + * @param attrId 文件存入的字段ID + * @return + */ + public OperationResultEntity uploadFile(MultipartFile file, String nodeUrl, String attrId) { + Map map = Maps.newHashMap(); + map.put("Module", "SiteAdmin"); + map.put("Operation", "MultiFilePublish"); + map.put("OutputJSON", "2"); + map.put("Fmt.AC.Rights", "Current"); + map.put("Fmt.Attr.Info", "Mid"); + map.put("Name", file.getOriginalFilename()); + map.put("NewNodeAttrId", attrId); + map.put("NewFileAttrId", attrId); + map.put("URL", nodeUrl); + map.put("Prepend", "true"); + map.put("NewNodeTypes", "Image"); + map.put("FormFileFieldName", "file"); + return c8Feign.excuteOperationFile(file, map); + } + + /** + * 发布文件 + * @param file 文件 + * @param fileUrl 可以传入URL,则可覆盖地址上的问价,目前没有场景用这个字段,都可以传入空 + * @return 文件柜地址 + */ + public OperationResultEntity publishFile(MultipartFile file, String fileUrl) { + Map map = Maps.newHashMap(); + map.put("Module", "Publisher"); + map.put("Operation", "Publish"); + map.put("OutputJSON", "2"); + map.put("FN", file.getOriginalFilename()); +// map.put("filename", file.getOriginalFilename()); + if (StrUtil.isNotBlank(fileUrl)) { + map.put("URL", fileUrl); + } + return c8Feign.publishFile(file, map); + } + + + public OperationResultEntity createUrls(int num) { + Map map = Maps.newHashMap(); + map.put("Module", "NodeProcessor"); + map.put("Operation", "CreateNodeURL"); + map.put("Count", num); + return c8Feign.excuteOperation(map); + } + + /** + * 将search转换成nativate export查询语句 + * + * @param searchXml + * @return + */ + public OperationResultEntity getNativeExportXml(String searchXml) { + Map map = Maps.newHashMap(); + map.put("Module", "NodeBrowserSupport"); + map.put("Operation", "TranslateQueryToDBXML"); + map.put("Qry.Limit.Path", ""); + map.put("OutputJSON", "2"); + map.put("Qry.XML", "" + searchXml + ""); + map.put("Qry.Limit.End", "10"); + return c8Feign.excuteOperation(map); + } + + /** + * 可获取文件、图片等 + * + * @param url 文件url地址:C开头的URL + * @param attribute url对应的属性 + * @return + */ + public InputStream getFileFromNode(String url, String attribute) { + Map map = Maps.newHashMap(); + map.put("Module", "Publisher"); + map.put("Operation", "GetFromNode"); + map.put("Attribute", attribute); + map.put("URL", url); + map.put("OutputJSON", "2"); + return getFileFromNode(map); + } + + /** + * 可获取文件、图片等 + * + * @param url 文件柜地址,不能放Image上的文件柜地址 + */ + public InputStream getFileFromDirectAddr(String url) { + Map map = Maps.newHashMap(); + map.put("Module", "Publisher"); + map.put("Operation", "GetDirect"); + map.put("URL", url); + return getFileFromNode(map); + } + + private InputStream getFileFromNode(Map map) { + Response response = c8Feign.getFileFromNode(map); + InputStream inputStream = null; + try { + inputStream = response.body().asInputStream(); + } catch (IOException e) { + log.error("获取文件失败:", e); + return null; + } + return inputStream; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8SearchService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8SearchService.java new file mode 100644 index 0000000..91b50b5 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/curd/C8SearchService.java @@ -0,0 +1,108 @@ +package com.centricsoftware.enhancement.modules.c8.service.curd; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.CentricResult; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.component.factory.ICentricAbstractFactory; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResult0; +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import com.centricsoftware.enhancement.modules.c8.service.C8LoginService; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class C8SearchService{ + + @Resource + private C8Feign c8Feign; + + @Resource + private ICentricAbstractFactory centricAbstractFactory; + + /** + * DepPath查询; + * 因为以上的这两个方法是直接调用的NodeUtil中的代码;后续NodeUtil将废弃 + */ + public DepPathResult depPathByUrl(DepPath depPath){ + log.debug("执行DepPath查询:url={};depPath={}",depPath.toString()); + Map map = Maps.newHashMap(); + map.put("Module","Search"); + map.put("Operation","QueryByURL"); + map.put("OutputJSON","2"); + List urls = new ArrayList(depPath.getUrls()); + map.put("Qry.URL",urls); + List paths = new ArrayList(depPath.getPaths()); + map.put("Dep.Path",paths); + DepPathResult0 result = c8Feign.depPath(map); + result.setDepPath(depPath); + return dealResult(result); + } + + public DepPathResult depPathByXml(DepPath depPath){ + return depPathByXml(depPath,1,4999); + } + public DepPathResult depPathByXml(DepPath depPath,int start, int end){ + log.debug("执行DepPath查询:url={};depPath={}",depPath.toString()); + Map map = Maps.newHashMap(); + map.put("Module","Search"); + map.put("Operation","QueryByXML"); + map.put("Qry.Limit.Filter","10000"); + map.put("Fmt.Complete","Ref"); + map.put("OutputJSON","2"); + map.put("Qry.Limit.Begin",start); + map.put("Qry.Limit.End",end); + map.put("Qry.XML",depPath.getXml()); + List paths = new ArrayList(depPath.getPaths()); + map.put("Dep.Path",paths); + DepPathResult0 result = c8Feign.depPath(map); + result.setDepPath(depPath); + return dealResult(result); + } + + private DepPathResult dealResult(DepPathResult0 result){ + String status = result.getStatus(); + if(!C8LoginService.Successful.equals(status)){ + log.error("查询失败:{}",result.getDepPath().getXml()); + throw new RuntimeException("查询失败,"+result.getErrorAdmin()); + } + Object returnValue = result.getNodes(); + if(StrUtil.isBlankOrUndefined(returnValue.toString())){ + returnValue = "{}"; + } + JSONObject nodes = JSONUtil.parseObj(returnValue); + + Object completeResultRefs1 = result.getCompleteResultRefs(); + if(null != completeResultRefs1 && StrUtil.isBlankOrUndefined(completeResultRefs1.toString())){ + completeResultRefs1 = "{}"; + } + + JSONObject completeResultRefs = JSONUtil.parseObj(completeResultRefs1); + JSONArray node = nodes.getJSONArray("Node"); + + JSONArray resultNode = nodes.getJSONArray("ResultNode"); + CentricResult centricResult = new CentricResult(); + centricResult.setResultNodes(resultNode); + centricResult.setDepPath(result.getDepPath()); + centricResult.setPathNodes(node); + centricResult.setCompleteResultRefs(completeResultRefs); + DepPathContext depPathContext = new DepPathContext(); + centricAbstractFactory.getDepPathFactory().getDepPathCache(depPathContext); + centricAbstractFactory.getDepPathFactory().getDepPathParser(depPathContext); + DepPathResult depPathResult = new DepPathResult(centricResult,depPathContext); + return depPathResult; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewHandler.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewHandler.java new file mode 100644 index 0000000..1d6d612 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewHandler.java @@ -0,0 +1,39 @@ +package com.centricsoftware.enhancement.modules.c8.service.customview; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.centricsoftware.enhancement.modules.c8.dto.customview.CustomViewConfig; +import com.centricsoftware.enhancement.modules.c8.dto.customview.LambdaParam; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; + +import java.util.Map; +import java.util.function.Consumer; + +/** + * description: + * Date: 2024/5/14 17:19 + */ +public interface ICustomViewHandler { + + /** + * @param depPath 不需要指定paths,组件自动计算 + */ + JSONArray get(String customViewUrl, DepPath depPath); + + /** + * @param depPath 不需要指定paths,组件自动计算 + */ + JSONArray get(String customViewUrl, Map alisaMap, DepPath depPath); + + /** + * @param depPath 不需要指定paths,组件自动计算 + */ + JSONArray get(String customViewUrl, CustomViewConfig config, DepPath depPath); + + /** + * 支持按行Lambda定义,每一行的所有列执行完后再执行Lambda;Matrix算一行 + * @param depPath 不需要指定paths,组件自动计算 + */ + JSONArray getAndLambdaByLine(String customViewUrl, CustomViewConfig config, DepPath depPath, Consumer function); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewParseService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewParseService.java new file mode 100644 index 0000000..9f7c703 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/ICustomViewParseService.java @@ -0,0 +1,31 @@ +package com.centricsoftware.enhancement.modules.c8.service.customview; + +import com.centricsoftware.enhancement.modules.c8.dto.customview.CustomViewConfig; + +/** + * description:根据Custom View URL解析视图,并封装成配置 + * Date: 2024/5/14 17:17 + */ +public interface ICustomViewParseService { + + /** + * 可传入自定义的配置,解析后的配置会直接封装在config + */ + void parseCustomView(String url, CustomViewConfig config); + + /** + * 如果通过getDefaultCustomViewConfig()获取的配置项,config内就有url,无需在传入 + */ + void parseCustomView(CustomViewConfig config); + + /** + * 按照默认配置解析 + */ + CustomViewConfig parseCustomView(String url); + + /** + * 获取默认配置,返回的config还需要调用parseXXX进行封装数据 + */ + CustomViewConfig getDefaultCustomViewConfig(String url); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewHandlerImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewHandlerImpl.java new file mode 100644 index 0000000..f3b16f6 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewHandlerImpl.java @@ -0,0 +1,236 @@ +package com.centricsoftware.enhancement.modules.c8.service.customview.impl; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.customview.CustomViewConfig; +import com.centricsoftware.enhancement.modules.c8.dto.customview.LambdaParam; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathExp; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.EnumTypeForCV; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewHandler; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewParseService; +import com.centricsoftware.enhancement.modules.c8.util.CentricAttributesUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * description:Custom View Search Handler + * Date: 2024/5/15 16:26 + */ +@Service +@Slf4j +public class DefaultCustomViewHandlerImpl implements ICustomViewHandler { + + @Resource + ICustomViewParseService defaultCustomViewParseServiceImpl; + + @Resource + private C8NodeService c8NodeService; + @Override + public JSONArray get(String customViewUrl, DepPath depPath) { + CustomViewConfig config = defaultCustomViewParseServiceImpl.parseCustomView(customViewUrl); + return getAndLambdaByLine(customViewUrl,config,depPath,null); + } + + @Override + public JSONArray get(String customViewUrl, Map alisaMap,DepPath depPath) { + CustomViewConfig config = defaultCustomViewParseServiceImpl.parseCustomView(customViewUrl); + config.setAliasMap(alisaMap); + return getAndLambdaByLine(customViewUrl,config,depPath,null); + } + + @Override + public JSONArray get(String customViewUrl, CustomViewConfig config,DepPath depPath) { + defaultCustomViewParseServiceImpl.parseCustomView(customViewUrl,config); + return getAndLambdaByLine(customViewUrl,config,depPath,null); + } + + @Override + public JSONArray getAndLambdaByLine(String customViewUrl, CustomViewConfig config, DepPath depPath, Consumer function) { + LambdaParam parameter = new LambdaParam(); + parameter.setConfig(config); + //获取数据 + DepPathResult result = getDepPathData(config,depPath); + List resultRefs = result.getCompleteResultRefs(); + JSONArray array = JSONUtil.createArray(); + for(String url : resultRefs){ + //按视图配置封装数据 + JSONObject json = extractData(url,config,result); + parameter.setResult(json); + if(function!=null){ + function.accept(parameter); + } + array.add(json); + } + return array; + } + + /** + * 按视图配置封装数据 + */ + private JSONObject extractData(String url, CustomViewConfig config, DepPathResult result) { + List cols = config.getCols(); + JSONObject row = JSONUtil.createObj(); + //处理非matrix数据 + List normalCols = cols.stream().filter(i -> !i.isMatrix()).collect(Collectors.toList()); + extractNormalData(url,normalCols,row,config,result); + //处理matrix数据 + List matrixCols = cols.stream().filter(i -> i.isMatrix()).collect(Collectors.toList()); + extractMatrixData(url,matrixCols,row,config,result); + return row; + } + + /** + * 解析Matrix列数据 + * @param url matrix行URL + * @param row 行数据 + * @param result DepPathResult + */ + private void extractMatrixData(String url,List matrixCols, JSONObject row,CustomViewConfig config, DepPathResult result) { + //对matrixCols按照matrixAttributeId进行分组;每个分组表示一行对应的matrix数据(比如款式matrix到配色,这里一个分组表示一行款式对应的所有配色:ProductColors) + Map> matrixColsMap = matrixCols.stream().collect(Collectors.groupingBy(CustomViewConfig.ColumnConfig::getMatrixAttributeId)); + for(Map.Entry> entry : matrixColsMap.entrySet()){ + String matrixAttributeId = entry.getKey(); + //matrix的列 + List matrixColsList = entry.getValue(); + CustomViewConfig.ColumnConfig first = matrixColsList.get(0); + String matrixPath = first.getMatrixPath(); + List matrixUrls = result.getValue(matrixPath, url).getList(); + JSONArray array = JSONUtil.createArray(); + for(String matrixUrl : matrixUrls){ + //matrixUrl表示每个matrix行,如果是款式matrix到配色,则matrix表示是配色的URL + JSONObject matrixRow = JSONUtil.createObj(); + for(CustomViewConfig.ColumnConfig col : matrixColsList){ + String exp = col.getAttributeIdAfterMatrix(); + DepPathResultValue value = getOptimizationValue(exp, matrixUrl,result,config); + Map mapping = config.getAttributeIdMapping(); + String attributeId = col.getAttributeId(); + attributeId = mapping.getOrDefault(attributeId, attributeId); + Object object = transValue(value, config); + String fullPath = col.getFullPath(); + attributeId = mapping.getOrDefault(fullPath, attributeId); + matrixRow.put(attributeId,object); + } + array.add(matrixRow); + } + row.put(matrixAttributeId, array); + } + } + + /** + * 解析非Matrix列数据 + * @param url 行URL + * @param row 行数据 + * @param result DepPathResult + */ + private void extractNormalData(String url,List normalCols, JSONObject row,CustomViewConfig config, DepPathResult result) { + Map aliasMap = config.getAliasMap(); + row.put(aliasMap.getOrDefault("id","id"), url); + for(CustomViewConfig.ColumnConfig col : normalCols){ + String fullPath = col.getFullPath(); + Map mapping = config.getAttributeIdMapping(); + DepPathResultValue value = getOptimizationValue(fullPath, url,result,config); + String attributeId = mapping.getOrDefault(fullPath, fullPath); + Object object = transValue(value, config); + row.put(attributeId, object); + } + } + + /** + * 对List和Map类型进行转换 + */ + private Object transValue(DepPathResultValue value,CustomViewConfig config){ + DepPathExp lastDepPathExp = value.getLastDepPathExp(); + if(CentricAttributesUtil.isEnum(lastDepPathExp)){ + //此处不直接返回,是需要后续的list转换 + transEnumValue(value,config); + } + if(CentricAttributesUtil.valueIsList(value.getValue())){ + boolean listToString = config.isListToString(); + if(listToString){ + //List转String + return value.getStrForList(config.getConjunction()); + }else{ + return value.getList(); + } + } + if(CentricAttributesUtil.valueIsMap(value.getValue())){ + boolean mapToString = config.isMapToString(); + if(mapToString){ + return StrUtil.toString(value.getMap()); + }else{ + return value.getMap(); + } + } + return value.getValue(); + } + + + /** + * 获取值,如果配置了优化项,则需要执行优化 + */ + private DepPathResultValue getOptimizationValue(String exp,String url,DepPathResult result,CustomViewConfig config){ + DepPathResultValue value = result.getValue(exp, url); + boolean optimizationAttributeId = config.isOptimizationAttributeId(); + if(!optimizationAttributeId){ + //不优化Ref + return value; + } + DepPathExp lastDepPathExp = value.getLastDepPathExp(); + boolean ref = CentricAttributesUtil.isRef(lastDepPathExp); + if(!ref&&!exp.endsWith("$CR")){ + //非Ref相关字段,不执行优化 + return value; + } + String finalExp = StrFormatter.format("{}.Node Name",exp); + return result.getValue(finalExp, url); + } + + /** + * 转换枚举值 + */ + private DepPathResultValue transEnumValue(DepPathResultValue value, CustomViewConfig config) { + EnumTypeForCV enumTypeForCV = config.getEnumTypeForCV(); + if(enumTypeForCV==EnumTypeForCV.ORIGIN){ + return value; + } + if(enumTypeForCV==EnumTypeForCV.KEY){ + value.setValue(value.getEnumValueByType()); + } + if(enumTypeForCV==EnumTypeForCV.DESCRIPTION){ + value.setValue(value.getEnumDescByType()); + } + return value; + } + + + /** + * 查询C8,返回DepPathResult + */ + private DepPathResult getDepPathData(CustomViewConfig config,DepPath depPath) { + depPath.getPaths().addAll(config.getPaths()); + DepPathResult result; + if(!StrUtil.isBlankOrUndefined(depPath.getXml())){ + //DepPath.XML + result = c8NodeService.depPathByXml(depPath); + }else{ + //DepPath.URL + result = c8NodeService.depPathByUrl(depPath); + } + return result; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewParseServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewParseServiceImpl.java new file mode 100644 index 0000000..05fa53f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/customview/impl/DefaultCustomViewParseServiceImpl.java @@ -0,0 +1,363 @@ +package com.centricsoftware.enhancement.modules.c8.service.customview.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.customview.CustomViewConfig; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewParseService; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * description: + * Date: 2024/5/14 17:26 + */ +@Service +@Slf4j +public class DefaultCustomViewParseServiceImpl implements ICustomViewParseService { + + @Resource + private C8NodeService c8NodeService; + + @Override + public void parseCustomView(String url, CustomViewConfig config) { + config.setViewUrl(url); + parseCustomView(config); + } + + @Override + public CustomViewConfig parseCustomView(String url) { + CustomViewConfig config = getDefaultCustomViewConfig(url); + parseCustomView(config); + return config; + } + + @Override + public CustomViewConfig getDefaultCustomViewConfig(String url) { + return new CustomViewConfig(url); + } + + @Override + public void parseCustomView(CustomViewConfig config) { + /** + * 初始化配置,封装视图的列配置:将Keys按顺序装载 + */ + initCustomViewConfig(config); + /** + * 解析列配置 + */ + List cols = config.getCols(); + for(CustomViewConfig.ColumnConfig column : cols){ + String originExp = column.getOriginExpressions(); + //设置BO路径和列对应的BO + extractBusinessObjectName(originExp,column); + //提取路径,获取路径和属性ID + extractPath(originExp,column); + //提取matrix信息 + extractMatrixInfo(originExp,column); + } + //封装自定义视图配置 + extractCustomViewConfig(config); + //设置字段映射,如果配置了aliasMap,则用aliasMap覆盖 + initAttributeIdMapping(config); + } + + /** + * 设置字段映射,如果配置了alias则用aliasMap覆盖 + * 1、如果attributeId不重复,则直接使用attributeId作为最终字段名 + * 2、如果attributeId重复,则通过字段+流水作为字段名;根据optimizationAttributeId是否配置,会有不同的流水规则 + * 3、Node Name做了特殊处理。如果非当前对象(targetClass)的Node Name,则会拼接前一个path + * @param config + */ + private void initAttributeIdMapping(CustomViewConfig config) { + Map aliasMap = config.getAliasMap(); + List cols = config.getCols(); + //对cols按照attributeId进行分组;path为空的数据不需要重新映射 + Map> groupByAttributeId = cols.stream().collect(Collectors.groupingBy(CustomViewConfig.ColumnConfig::getGroupId)); + //遍历groupByAttributeId + groupByAttributeId.forEach((attributeId, columnConfigs) -> { + //提取mapping信息 + extractAttributeIdMapping(config,columnConfigs); + }); + //对cols的matrix为true的,按照matrixAttributeId进行分组,如果分组个数大于1,则重置matrixAttributeId为matrixPath + resetMatrixAttributeId(cols); + //按照AlisaMapping重新设置Mapping + resetMappingWithAlisaMapping(config); + } + + /** + * 对cols的matrix为true的,按照matrixAttributeId进行分组,如果分组个数大于1,则重置matrixAttributeId为matrixPath + */ + private void resetMatrixAttributeId( List cols){ + List collect = cols.stream().filter(CustomViewConfig.ColumnConfig::isMatrix).collect(Collectors.toList()); + Map> groupByAttributeId = collect.stream().collect(Collectors.groupingBy(CustomViewConfig.ColumnConfig::getMatrixAttributeId)); + groupByAttributeId.forEach((k,v)->{ + if(v.size()>1){ + v.forEach(columnConfig -> { + columnConfig.setMatrixAttributeId(columnConfig.getMatrixPath()); + }); + } + }); + } + + /** + * 按照AlisaMapping重新设置Mapping + */ + private void resetMappingWithAlisaMapping(CustomViewConfig config) { + Map attributeIdMapping = config.getAttributeIdMapping(); + Map aliasMap = config.getAliasMap(); + if(CollUtil.isEmpty(aliasMap)){ + return; + } + //遍历attributeIdMapping,如果value在alisaMap中的key,则替换value + attributeIdMapping.forEach((key, value) -> { + if(aliasMap.containsKey(value)){ + attributeIdMapping.put(key,aliasMap.get(value)); + } + }); + } + + /** + * 提取Mapping信息 + */ + private void extractAttributeIdMapping(CustomViewConfig config, List columnConfigs) { + //重复 + boolean optimizationAttributeId = config.isOptimizationAttributeId(); + if(optimizationAttributeId){ + //优化attributeId + extractMappingWhenOptimizationAttributeId(config,columnConfigs); + }else{ + extractMappingNotOptimizationAttributeId(config,columnConfigs); + } + } + + /** + * 不优化attributeId,重复则直接拼接流水,不重复则直接使用attributeId作为字段名 + */ + private void extractMappingNotOptimizationAttributeId(CustomViewConfig config,List cols) { + Map mapping = config.getAttributeIdMapping(); + boolean repeat = cols.size() > 1; + if(!repeat){ + //不重复则直接使用attributeId作为字段名 + cols.forEach(column -> mapping.put(column.getFullPath(),column.getAttributeId())); + }else{ + //cols是否存在path为空的 + boolean match = cols.stream().anyMatch(column -> StrUtil.isEmpty(column.getPath())); + //cols中的attributeId重复了,则获取按照attributeId拼接流水 + int seq = 1; + for(int i=0; i cols) { + Map mapping = config.getAttributeIdMapping(); + boolean repeat = cols.size() > 1; + if(!repeat){ + //不重复则直接使用attributeId作为字段名 + cols.forEach(column -> mapping.put(column.getFullPath(),"Node Name".equals(column.getAttributeId())?"Name":column.getAttributeId())); + }else{ + //对cols按照path1进行分组 + Map> groupByPath1 = cols.stream().collect(Collectors.groupingBy(CustomViewConfig.ColumnConfig::getPath1)); + groupByPath1.forEach((path1, columnConfigs) -> { + if(columnConfigs.size()>1){ + //优化后再重复,则直接按照fullPath作为value值 + columnConfigs.forEach(i-> mapping.put(i.getFullPath(), StrUtil.replace(i.getFullPath(),"Node Name","Name"))); + }else{ + columnConfigs.forEach(column -> { + String name = path1.endsWith("Node Name")? StrUtil.replace(path1,"Node Name","Name"):path1; + mapping.put(column.getFullPath(),name); + }); + } + }); + } + } + + /** + * 将明细数据汇总到自定义视图配置中,此处可以做优化 + */ + private void extractCustomViewConfig(CustomViewConfig config) { + List cols = config.getCols(); + boolean optimizationAttributeId = config.isOptimizationAttributeId(); + //将ColumnConfig中的path汇总到config的path字段 + List paths = new ArrayList<>(); + for(CustomViewConfig.ColumnConfig column : cols){ + String path = column.getPath(); + if(optimizationAttributeId){ + path = column.getFullPath(); + } + if(StrUtil.isNotBlank(path)){ + paths.add(path); + } + } + config.setPaths(paths); + //将paths封装成Child:的格式,如果结尾是Node Name或者Name,则去除 + resetPathForDepPath(config); + } + + /** + *将paths封装成Child:的格式,如果结尾是Node Name或者Name,则去除 + */ + private void resetPathForDepPath(CustomViewConfig config) { + ArrayList list = Lists.newArrayList(); + List paths = config.getPaths(); + paths.forEach(i->{ + if(i.endsWith("Node Name")){ + i = StrUtil.subBefore(i,"Node Name",false); + }; + if(i.endsWith("Name")){ + i = StrUtil.subBefore(i,"Name",false); + }; + String replaceStr = i.replaceAll("\\.", "/Child:");//A.B.C=>A/Child:B/Child:C + //A/Child:B/Child:C => >Child:A/Child:B/Child:C + if(!StrUtil.isBlankOrUndefined(replaceStr)){ + String path = StrFormatter.format("Child:{}",replaceStr); + list.add(path); + } + }); + config.setPaths(list); + } + + + /** + * 提取Matrix信息 + */ + public void extractMatrixInfo(String expression,CustomViewConfig.ColumnConfig column) { + Pattern pattern = Pattern.compile("\\{(.*?)\\}$"); // 正则表达式匹配以"{attr}"结尾的部分 + Matcher matcher = pattern.matcher(expression); + if (matcher.find()) { + String matrixAttributeId = matcher.group(1); // 获取匹配到的attr部分 + column.setMatrixAttributeId(matrixAttributeId); + column.setMatrix(true); + String fullPath = column.getFullPath(); + int index = fullPath.indexOf(matrixAttributeId); + Assert.isTrue(index>=0,"路径解析错误:"+fullPath); + //计算matrixPath + column.setMatrixPath(fullPath.substring(0,0+matrixAttributeId.length())); + column.setAttributeIdAfterMatrix(StrUtil.subAfter(fullPath,StrFormatter.format("{}.",matrixAttributeId, index),false)); + } + } + + /** + * 提取路径,获取路径和属性ID + */ + public void extractPath(String expression,CustomViewConfig.ColumnConfig column) { + //清除()、{}、Child:、:0 + String cleanedExp = expression.replaceAll("\\{.*?\\}", "").replaceAll("\\(.*?\\)", "").replace("Child:", ""); + cleanedExp = cleanedExp.replaceAll(":0",""); + //将/换成. + cleanedExp = cleanedExp.replaceAll("/","\\."); + //用:对cleanedExp进行分组,第一个元素即为最终字段,其余元素则是路径 + String[] parts = cleanedExp.split(":"); + if (parts.length > 0) { + String attributeId = parts[0]; + //替换attributeId,按照{"___CR":"$CR","___CT","$CT"}规则,比如attributeId为___CR,则替换为$CR + attributeId = replaceAttributeId(attributeId); + //当parts.length==1时,表示无路径 + String path = ""; + //全路径包括最终字段 + String fullPath = attributeId; + if(parts.length>1){ + path = parts[1]; + fullPath = path+"."+attributeId; + } + column.setAttributeId(attributeId); + column.setPath(path); + column.setFullPath(fullPath); + //对path按照.进行split,获取最后一个,并拼接上attributeId + String path1 = StrUtil.subAfter(path, ".", true); + if(StrUtil.isBlankOrUndefined(path1)){ + column.setPath1(attributeId); + }else{ + column.setPath1(StrFormatter.format("{}.{}",path1,attributeId)); + } + } + } + + /** + * 因为视图有些字段在DepPath查询中没有,需要做转换 + * @param attributeId + * @return + */ + private String replaceAttributeId(String attributeId){ + HashMap map = Maps.newHashMap(); + map.put("___CR","$CR"); + map.put("___CT","$CT"); + if(map.containsKey(attributeId)){ + return map.get(attributeId); + } + return attributeId; + } + + /** + * 设置BO路径和列对应的BO + */ + public void extractBusinessObjectName(String expression,CustomViewConfig.ColumnConfig column) { + List paths = new ArrayList<>(); + Pattern pattern = Pattern.compile("\\((.*?)\\)"); + Matcher matcher = pattern.matcher(expression); + while (matcher.find()) { + paths.add(matcher.group(1)); // group(1) 获取第一个括号内匹配的内容 + } + String boPath = StrUtil.join(".", paths); + //设置BO路径 + column.setBoPath(boPath); + int size = paths.size(); + //设置最终值所属的BO + column.setBusinessObjectName(size >0?paths.get(size-1):""); + } + + /** + * 初始化配置,封装视图的列配置:将Keys按顺序装载 + */ + private void initCustomViewConfig(CustomViewConfig config) { + String viewUrl = config.getViewUrl(); + DepPathResult result = c8NodeService.queryUrl(viewUrl); + Map keys = result.getValue("Keys", viewUrl).getMap(); + List attributes = result.getValue("Attributes",viewUrl).getList(); + String targetClass = result.getValue("TargetClass",viewUrl).getStr(); + config.setTargetClass(targetClass); + List cols = config.getCols(); + for (int i = 0; i < attributes.size(); i++) { + CustomViewConfig.ColumnConfig columnConfig = config.new ColumnConfig(); + String attribute = attributes.get(i); + String key = keys.get(attribute); + columnConfig.setOriginExpressions(key); + cols.add(columnConfig); + } + config.setCustomViewResult(result); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/IDepPathEntitySearchService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/IDepPathEntitySearchService.java new file mode 100644 index 0000000..002542d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/IDepPathEntitySearchService.java @@ -0,0 +1,101 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep; + + +import cn.hutool.core.convert.Convert; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import org.apache.ibatis.reflection.invoker.Invoker; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * DepPath实体类查询字段类型处理接口,每个实现类处理一种字段类型 + */ +public interface IDepPathEntitySearchService { + List getHandleTypes(); + + /** + * 是否执行后续的责任链 + * @param context 上下文参数 + * @return true表示继续执行后续责任链节点;false表示终止 + */ + boolean isContinue(DepPathByEntityContext context); + + /** + * 是否执行当前责任链 + * @param context 上下文参数 + * @return true表示当前节点会处理 + */ + boolean isHandle(DepPathByEntityContext context); + + /** + * 执行当前节点逻辑:赋值逻辑需要通过反射执行 + * @param context 上下文参数 + */ + void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException; + + + /** + * + * @param context 上下文参数 + * @param value 需要设置的值。调用字段对应的setter方法,因为方法可能存在多个入参,所以value为数组 + */ + default void setFieldValue(DepPathByEntityContext context,Object[] value) throws InvocationTargetException, IllegalAccessException { + if(value==null){ + return; + } + /** + * 强制类型转换,避免报错 + */ + for(int i=0;i type = context.getField().getType(); + value[i] = Convert.convert(type,o); + } + Object instance = context.getInstance(); + Invoker invoker = context.getInvoker(); + if(invoker!=null){ + invoker.invoke(instance, value); + } + + } + + /** + * 是否是当前节点需要处理的类型;可以通过重新该方法修改规则 + * @return true表示当前节点需要处理;false表示不需要 + */ + default boolean isCurrentHandleType(DepPathByEntityContext context){ + List handleTypes = getHandleTypes(); + if(handleTypes==null){ + return true; + } + if(handleTypes.size()==0){ + return false; + } + DepPathEntityType type = context.getDepPathField().type(); + if(handleTypes.contains(type)){ + return true; + } + return false; + } + + + /** + * 获取当前值 + * @param context + * @return + */ + default DepPathResultValue getValue(DepPathByEntityContext context){ + DepPathField depPathField = context.getDepPathField(); + String exp = depPathField.exp(); + String url = context.getUrl(); + DepPathResult depPathResult = context.getDepPathResult(); + DepPathResultValue value = depPathResult.getValue(exp, url); + return value; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DateDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DateDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..66fb8cf --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DateDepPathEntitySearchServiceImpl.java @@ -0,0 +1,60 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import cn.hutool.core.convert.Convert; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(999) +@Component +public class DateDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.DATA_STRING); + types.add(DepPathEntityType.DATA); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException{ + DepPathField depPathField = context.getDepPathField(); + DepPathEntityType type = depPathField.type(); + DepPathResultValue value = getValue(context); + Class clazz = type.getClazz(); + Object v = value.getValue(); + if(DepPathEntityType.DATA==type){ + v = value.getDate(); + } + if(DepPathEntityType.DATA_STRING==type){ + v = value.getDateFormat(depPathField.timeFormat()); + } + Object convertValue = Convert.convert(clazz, v); + Object[] valueArray = new Object[]{convertValue}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DefaultDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DefaultDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..33b1b54 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/DefaultDepPathEntitySearchServiceImpl.java @@ -0,0 +1,57 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import cn.hutool.core.convert.Convert; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(999) +@Component +public class DefaultDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.STRING); + types.add(DepPathEntityType.REF); + types.add(DepPathEntityType.INTEGER); + types.add(DepPathEntityType.DOUBLE); + types.add(DepPathEntityType.FILE); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException{ + DepPathField depPathField = context.getDepPathField(); + DepPathEntityType type = depPathField.type(); + DepPathResultValue value = getValue(context); + Class clazz = type.getClazz(); + Object convertValue = Convert.convert(clazz, value.getValue()); + Object[] valueArray = new Object[]{convertValue}; + setFieldValue(context,valueArray); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..131cf31 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumDepPathEntitySearchServiceImpl.java @@ -0,0 +1,56 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(700) +@Component +public class EnumDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.ENUM_VALUE); + types.add(DepPathEntityType.ENUM_DESC); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException{ + DepPathField depPathField = context.getDepPathField(); + DepPathResultValue value = getValue(context); + String valueStr = value.getStr(); + if(depPathField.type()==DepPathEntityType.ENUM_VALUE){ + valueStr = value.getEnumValue(); + } + if(depPathField.type()==DepPathEntityType.ENUM_DESC){ + valueStr = value.getEnumDesc(); + } + Object[] valueArray = new Object[]{valueStr}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..1762375 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListDepPathEntitySearchServiceImpl.java @@ -0,0 +1,58 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(710) +@Component +public class EnumListDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.ENUM_DESC_LIST); + types.add(DepPathEntityType.ENUM_VALUE_LIST); + types.add(DepPathEntityType.ENUM_DESC_LIST_TO_String); + types.add(DepPathEntityType.ENUM_VALUE_LIST_TO_String); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException{ + DepPathField depPathField = context.getDepPathField(); + DepPathResultValue value = getValue(context); + List valueList = value.getList(); + if(depPathField.type()==DepPathEntityType.ENUM_DESC_LIST){ + valueList = value.getEnumDescList(depPathField.removeDuplicate()); + } + if(depPathField.type()==DepPathEntityType.ENUM_VALUE_LIST){ + valueList = value.getEnumValueList(depPathField.removeDuplicate()); + } + Object[] valueArray = new Object[]{valueList}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListToStringDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListToStringDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..18bd33f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/EnumListToStringDepPathEntitySearchServiceImpl.java @@ -0,0 +1,58 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(710) +@Component +public class EnumListToStringDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.ENUM_DESC_LIST_TO_String); + types.add(DepPathEntityType.ENUM_VALUE_LIST_TO_String); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException{ + DepPathField depPathField = context.getDepPathField(); + DepPathResultValue value = getValue(context); + List valueList = value.getList(); + if(depPathField.type()==DepPathEntityType.ENUM_DESC_LIST_TO_String){ + value.getEnumDescList(depPathField.removeDuplicate()); + } + if(depPathField.type()==DepPathEntityType.ENUM_VALUE_LIST_TO_String){ + valueList = value.getEnumValueList(depPathField.removeDuplicate()); + } + String valueStr = StrUtil.join(depPathField.conjunctionForListToString(), valueList); + Object[] valueArray = new Object[]{valueStr}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..dd16455 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListDepPathEntitySearchServiceImpl.java @@ -0,0 +1,50 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(600) +@Component +public class ListDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.LIST); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException { + DepPathField depPathField = context.getDepPathField(); + boolean removeDuplicate = depPathField.removeDuplicate(); + Class generic = depPathField.generic(); + DepPathResultValue value = getValue(context); + Object[] valueArray = new Object[]{value.getList(removeDuplicate,generic)}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListToStringDepPathEntitySearchServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListToStringDepPathEntitySearchServiceImpl.java new file mode 100644 index 0000000..7c55716 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/dep/impl/ListToStringDepPathEntitySearchServiceImpl.java @@ -0,0 +1,51 @@ +package com.centricsoftware.enhancement.modules.c8.service.dep.impl; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathByEntityContext; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.centricsoftware.enhancement.modules.c8.service.dep.IDepPathEntitySearchService; +import com.google.common.collect.Lists; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * description:默认处理器。处理C8字段类型为ref、double、integer、file + * Date: 2024/3/8 12:33 + */ +@Order(800) +@Component +public class ListToStringDepPathEntitySearchServiceImpl implements IDepPathEntitySearchService { + + @Override + public List getHandleTypes() { + List types = Lists.newArrayList(); + types.add(DepPathEntityType.LIST_TO_String); + return types; + } + + @Override + public boolean isContinue(DepPathByEntityContext context) { + return true; + } + + @Override + public boolean isHandle(DepPathByEntityContext context) { + return isCurrentHandleType(context); + } + + @Override + public void process(DepPathByEntityContext context) throws InvocationTargetException, IllegalAccessException { + DepPathField depPathField = context.getDepPathField(); + DepPathResultValue resultValue = getValue(context); + List value = resultValue.getList(depPathField.removeDuplicate(), String.class); + String valueStr = StrUtil.join(depPathField.conjunctionForListToString(),value); + Object[] valueArray = new Object[]{valueStr}; + setFieldValue(context,valueArray); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/es/LogElasticsearchService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/es/LogElasticsearchService.java new file mode 100644 index 0000000..7ae184e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/service/es/LogElasticsearchService.java @@ -0,0 +1,259 @@ +package com.centricsoftware.enhancement.modules.c8.service.es; + +import cn.hutool.json.JSONUtil; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldSort; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; +import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; +import co.elastic.clients.elasticsearch._types.aggregations.AggregationBuilders; +import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.core.IndexResponse; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.core.search.HitsMetadata; +import co.elastic.clients.json.JsonData; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.utils.JsonUtil; +import com.centricsoftware.config.entity.EsProperties; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.dto.log.QueryPlmLogReq; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import java.io.IOException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * es + * @author liaochangjiang + * @since 2024-04-26 15:29 + */ +@Service +@Slf4j +public class LogElasticsearchService { + + @Resource + protected ElasticsearchClient client; + + @Resource + protected EsProperties esProperties; + + /** + * @param api 调用的接口 + * @param ip 调用方IP + * @param param 接口参数 + * @author liaochangjiang + * @since 2021-12-28 17:32:57 + */ + public PlmLog newLog(String api, String ip, Object param, String desc) { + if (param == null) { + param = StringUtils.EMPTY; + } + return new PlmLog() + .setPath(api) + .setName(desc) + .setHost(ip) + .setBrand("PLM") + .setParam((param instanceof String) ? (String) param : JSONUtil.toJsonStr(param)) + .setStartTime(LocalDateTime.now()); + } + + /** + * 更新日志 + * + * @param apiLog log实体类 + * @param success 是否成功 + * @param errorCode 错误编码 + * @param msg 错误信息 + * @param returnText 接口返回值 + */ + public void updateLog(PlmLog apiLog, boolean success, String errorCode, String msg, String returnText) { + int code; + try { + code = Integer.parseInt(errorCode); + } catch (NumberFormatException e) { + code = 500; + } + ResEntity respVo = ResEntity.builder().code(success ? 0 : code).msg(msg).success(true).build(); + if (StringUtils.isNotBlank(returnText)) { + respVo.setData(returnText); + } + buildUpdatePlmLog(apiLog, respVo); + } + + /** + * 构建更新参数 + * + * @author liaochangjiang + * @since 2021-12-27 17:10:23 + */ + @SuppressWarnings("rawtypes") + public void buildUpdatePlmLog(PlmLog data, Object response) { + data.setEndTime(LocalDateTime.now()) + .setCostMs(Duration.between(data.getStartTime(), data.getEndTime()).toMillis()); + if (response == null) { + Optional.ofNullable(RequestContextHolder.getRequestAttributes()).map(t -> (ServletRequestAttributes) t) + .map(ServletRequestAttributes::getResponse).ifPresent( + resp -> data.setSuccess(resp.getStatus() == 200).setReturnCode(String.valueOf(resp.getStatus()))); + return; + } + if (response instanceof ResEntity) { + ResEntity resp = (ResEntity) response; + data.setSuccess(resp.isSuccess()).setReturnCode(String.valueOf(resp.getCode())).setReturnMsg(resp.getMsg()) + .setReturnText(JsonUtil.toJSONString(resp)); + } else { + data.setReturnText(JsonUtil.toJSONString(response)); + } + } + + /** + * 保存日志 + * + * @author liaochangjiang + * @since 2021-12-17 15:39:55 + */ + public void saveLog(PlmLog data) { + IndexRequest req = new IndexRequest.Builder() + .index(esProperties.getLogIndex()) + .document(data) + .build(); + IndexResponse index = null; + try { + index = client.index(req); + if (log.isDebugEnabled()) { + log.debug("index: {}, id:{}, result:{}", index.index(), index.id(), index.result()); + } + } catch (IOException e) { + log.error("保存操作记录失败", e); + } + } + + /** + * 查询分页日志 + * + * @author liaochangjiang + * @since 2021-12-17 16:38:24 + */ + public Page queryLogs(Page page, QueryPlmLogReq req) throws IOException { + SearchResponse search = client.search(s -> s + .index(esProperties.getLogIndex()) + .query(q -> { + BoolQuery.Builder builder = QueryBuilders.bool(); + List param = new ArrayList<>(); + if (StringUtils.isNotBlank(req.getPath())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("path").value(v -> v.stringValue(req.getPath())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getName())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("name").value(v -> v.stringValue(req.getName())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getBrand())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("brand").value(v -> v.stringValue(req.getBrand())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getStartTimeBegin())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("@timestamp").gte(JsonData.of(req.getStartTimeBegin())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getStartTimeEnd())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("@timestamp").lt(JsonData.of(req.getStartTimeEnd())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getEndTimeBegin())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("endTime").gte(JsonData.of(req.getEndTimeBegin())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getEndTimeEnd())) { + param.add(new Query.Builder() + .range(QueryBuilders.range().field("endTime").lt(JsonData.of(req.getEndTimeEnd())).build()) + .build()); + } + if (Objects.nonNull(req.getSuccess())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("success").value(v -> v.booleanValue(req.getSuccess())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getHost())) { + param.add(new Query.Builder() + .term(QueryBuilders.term().field("host").value(v -> v.stringValue(req.getHost())).build()) + .build()); + } + if (StringUtils.isNotBlank(req.getParam())) { + param.add(new Query.Builder() + .matchPhrase(QueryBuilders.matchPhrase().field("param").query(req.getParam()).build()) + .build()); + } + if (Objects.nonNull(req.getReturnCode())) { + param.add(new Query.Builder().term(QueryBuilders.term().field("returnCode") + .value(v -> v.longValue(req.getReturnCode())).build()).build()); + } + if (StringUtils.isNotBlank(req.getReturnText())) { + param.add(new Query.Builder().matchPhrase( + QueryBuilders.matchPhrase().field("returnText").query(req.getReturnText()) + .build()).build()); + } + if (StringUtils.isNotBlank(req.getDebug())) { + param.add(new Query.Builder().matchPhrase( + QueryBuilders.matchPhrase().field("debug").query(req.getDebug()).build()) + .build()); + } + builder = builder.filter(param); + return q.bool(builder.build()); + }) + .from((int) ((req.getPageNum() - 1) * req.getPageSize().intValue())) + .size(req.getPageSize().intValue()) + .sort(t -> t.field(new FieldSort.Builder().field("@timestamp").order(SortOrder.Desc).build())) + , + PlmLog.class); + HitsMetadata hits = search.hits(); + long total = hits.total().value(); + List list = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + return page.setTotal(total).setRecords(list); + } + + /** + * 获取日志接口名列表 + * + * @author liaochangjiang + * @since 2021-12-20 16:07:26 + */ + public List listLogNames(String brand) throws IOException { + SearchResponse resp = client.search(s -> s + .index(esProperties.getLogIndex()) + .query(q -> q + .term(t -> t + .field("brand") + .value(v -> v.stringValue(brand)) + ) + ) + .aggregations("desc", Aggregation.of(t -> t.terms(AggregationBuilders.terms().field("name").size(300).build()))) + .size(0) + , PlmLog.class); + Aggregate aggr = resp.aggregations().get("desc"); + List array = aggr.sterms().buckets().array(); + return array.stream().map(StringTermsBucket::key).collect(Collectors.toList()); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/util/CentricAttributesUtil.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/util/CentricAttributesUtil.java new file mode 100644 index 0000000..9ab8f90 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/c8/util/CentricAttributesUtil.java @@ -0,0 +1,133 @@ +package com.centricsoftware.enhancement.modules.c8.util; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.c8.dto.dep.CentricAttrs; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathExp; +import com.centricsoftware.enhancement.modules.c8.em.ParserFieldType; + +import java.util.List; +import java.util.Map; + +/** + * description: + * Date: 2024/2/5 0:15 + */ +public class CentricAttributesUtil { + + + /** + * 如果C8字段类型为list、vector、set结尾,返回true + */ + public static boolean isList(String type) { + if(StrUtil.endWithAny(type,"list","vector","set")){ + return true; + } + return false; + } + + /** + * 如果C8字段类型为list、vector、set结尾,返回true + */ + public static boolean isList(DepPathExp exp) { + if(exp==null){ + return false; + } + CentricAttrs attrs = exp.getAttrs(); + if(attrs==null){ + return false; + } + String type = attrs.getType(); + return CentricAttributesUtil.isList(type); + } + + /** + * 如果C8字段类型为map结尾,返回true + */ + public static boolean isMap(String type) { + if(StrUtil.endWithAny(type,"map")){ + return true; + } + return false; + } + + /** + * 如果C8字段类型为map结尾,返回true + */ + public static boolean isMap(DepPathExp exp) { + if(exp==null){ + return false; + } + CentricAttrs attrs = exp.getAttrs(); + if(attrs==null){ + return false; + } + String type = attrs.getType(); + return CentricAttributesUtil.isMap(type); + } + + + public static boolean isRef(String type) { + if(StrUtil.startWith(type,"ref")){ + return true; + } + return false; + } + + public static boolean isRef(DepPathExp exp) { + if(exp==null){ + return false; + } + CentricAttrs attrs = exp.getAttrs(); + if(attrs==null){ + return false; + } + String type = attrs.getType(); + return CentricAttributesUtil.isRef(type); + } + + public static boolean isEnum(DepPathExp exp) { + if(exp==null){ + return false; + } + CentricAttrs attrs = exp.getAttrs(); + if(attrs==null){ + return false; + } + String type = attrs.getType(); + if(StrUtil.startWith(type,"enum")){ + return true; + } + return false; + } + + + public static ParserFieldType getParserFieldType(String type) { + if(StrUtil.endWithAny(type,"list","vector","set")){ + return ParserFieldType.LIST; + } + if(StrUtil.endWithAny(type,"map")){ + return ParserFieldType.MAP; + } + if("ref".equals(type)){ + return ParserFieldType.REF; + } + return ParserFieldType.OTHER; + } + + public static boolean valueIsList(Object value){ + if(value instanceof List ||(value.toString().startsWith("[")&&value.toString().endsWith("]"))){ + return true; + } + return false; + } + + public static boolean valueIsMap(Object value){ + if(value instanceof Map ||(value.toString().startsWith("{")&&value.toString().endsWith("}"))) { + return true; + } + return false; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/CustomViewDemoController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/CustomViewDemoController.java new file mode 100644 index 0000000..4ccb1dc --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/CustomViewDemoController.java @@ -0,0 +1,62 @@ +package com.centricsoftware.enhancement.modules.demo.controller; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.modules.c8.dto.customview.CustomViewConfig; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewHandler; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewParseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; + +/** + * description:CustomView 查询测试 + * Date: 2024/5/16 10:17 + */ +@Slf4j +@RequestMapping("/cv") +@Controller +public class CustomViewDemoController { + + @Resource + private ICustomViewHandler defaultCustomViewHandlerImpl; + + /** + * 未优化查询 + */ + @ResponseBody + @RequestMapping("/search") + public ResEntity search() throws Exception { + //以太平鸟测试机,款式-配色视图 + DepPath build = DepPath.builder().addUrl("C36131").build(); + JSONArray jsonArray = defaultCustomViewHandlerImpl.get("C3155", build); + log.info(JSONUtil.toJsonPrettyStr(jsonArray)); + return WebResponse.success(ResCode.SUCCESS); + } + + @Resource + ICustomViewParseService defaultCustomViewParseServiceImpl; + + /** + * 优化查询 + */ + @ResponseBody + @RequestMapping("/search_opt") + public ResEntity searchOptimizationAttributeId() throws Exception { + String cv = "C36131"; + CustomViewConfig config = defaultCustomViewParseServiceImpl.getDefaultCustomViewConfig(cv); + config.setOptimizationAttributeId(true); + //以太平鸟测试机,款式-配色视图 + DepPath build = DepPath.builder().addUrl(cv).build(); + JSONArray jsonArray = defaultCustomViewHandlerImpl.get("C3155",config,build); + log.info(JSONUtil.toJsonPrettyStr(jsonArray)); + return WebResponse.success(ResCode.SUCCESS); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DDLDemoController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DDLDemoController.java new file mode 100644 index 0000000..9875efb --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DDLDemoController.java @@ -0,0 +1,66 @@ +package com.centricsoftware.enhancement.modules.demo.controller; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8SearchAttribute; +import com.centricsoftware.enhancement.modules.dml.util.C8Write; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.ArrayList; + +/** + * description: + * Date: 2024/7/2 16:16 + */ +@Slf4j +@RequestMapping("/ddl") +@Controller +public class DDLDemoController { + + @ResponseBody + @RequestMapping("/search") + public ResEntity search() throws Exception { + C8Search style = C8Search.newSearch("Style"); + C8SearchAttribute or = style.or(); + or.attr("Code","EQ","10086"); + String xml = style.getXml(); + log.info(xml); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/operation") + public ResEntity operation() throws Exception { + String operation = C8Write.changeNode("C10086") + .changeAttribute("Code", "string", "1") + .getXml(); + log.info(operation); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/delete") + public ResEntity delete() throws Exception { + String delete = C8Write.deleteNode("C10086").getXml(); + log.info(delete); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/changeList") + public ResEntity changeList() throws Exception { + ArrayList values = new ArrayList<>(); + values.add("C0001"); + String operation = C8Write.changeNode("C10086") + .changeAttributeList("Colorways", "reflist", values,"ref") + .getXml(); + log.info(operation); + return WebResponse.success(ResCode.SUCCESS); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DepPathDemoController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DepPathDemoController.java new file mode 100644 index 0000000..3a28566 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/DepPathDemoController.java @@ -0,0 +1,151 @@ +package com.centricsoftware.enhancement.modules.demo.controller; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.component.design.chain.IDepPathSearchChain; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPathResultValue; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.service.curd.C8SearchService; +import com.centricsoftware.enhancement.modules.demo.dto.dep.SampleDemoDto; +import com.centricsoftware.enhancement.modules.demo.dto.dep.StyleDemoDTO; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.math.RoundingMode; +import java.util.List; + +/** + * description: + * Date: 2024/3/15 10:10 + */ +@Slf4j +@RequestMapping("/dep") +@Controller +public class DepPathDemoController { + + @Resource + C8SearchService c8SearchService; + + /** + * DEMO的URL为款式 + * @throws Exception + */ + @ResponseBody + @RequestMapping("/url") + public ResEntity depPathByUrl() throws Exception { + DepPath build = DepPath.builder().addUrl("C36133") + .addPath("Child:ActiveColorways/Child:C8_CW_PhotoSizes") + .addPath("Child:ActiveColorways/Child:Images") + .addPath("Child:Images/Child:C8_Image_MappingOSS") + .build(); + DepPathResult depPathResult = c8SearchService.depPathByUrl(build); + log.info("================================================="); + + DepPathResultValue value0 = depPathResult.getValue("$Name", "C36133"); + log.info("$Name={}",value0.getValue()); + + DepPathResultValue value2 = depPathResult.getValue("ActiveColorways.Attributes.C8_CW_PhotoSizes", "C36133"); + log.info("ActiveColorways.Attributes.C8_CW_PhotoSizes={}",value2.getList()); + + DepPathResultValue value21 = depPathResult.getValue("ActiveColorways.C8_CW_PhotoSizes", "C36133"); + DepPathResultValue value22 = depPathResult.getValue("ActiveColorways.C8_CW_PhotoSizes[1]", "C36133"); + log.info("ActiveColorways.C8_CW_PhotoSizes={}",value21.getList()); + log.info("ActiveColorways.C8_CW_PhotoSizes[1]={}",value22.getStr()); + + DepPathResultValue value1 = depPathResult.getValue("ActiveColorways", "C36133"); + log.info("ActiveColorways={}",value1.getList()); + + DepPathResultValue value3 = depPathResult.getValue("Images.Thumbnail", "C36133"); + log.info("List => Images.Thumbnail={}",value3.getList()); + log.info("Map =>Images.Thumbnail={}",value3.getMap()); + + DepPathResultValue value4 = depPathResult.getValue("Images", "C36133"); + log.info("Map =>Images={}",value4.getMap()); + + DepPathResultValue value5 = depPathResult.getValue("ActiveColorways.Images", "C36133"); + log.info("Map =>ActiveColorways.Images={}",value5.getMap()); + + DepPathResultValue value6 = depPathResult.getValue("Images.C8_Image_MappingOSS", "C36133"); + log.info("Map =>Images.C8_Image_MappingOSS={}",value6.getMap()); + + DepPathResultValue value7 = depPathResult.getValue("ModifiedAt", "C36133"); + log.info("Date =>ModifiedAt={}",value7.getDateStr()); + log.info("Date =>ModifiedAt={}",value7.getDateAndTimeStr()); + log.info("Date =>ModifiedAt={}",value7.getDateFormat("yyyy/MM/dd")); + log.info("Date =>ModifiedAt={}",value7.getDate()); + + DepPathResultValue value8 = depPathResult.getValue("C8_Style_GarmentLength", "C36133"); + log.info("Double =>C8_Style_GarmentLength={}",value8.getDouble()); + log.info("Double =>C8_Style_GarmentLength={}",value8.getDouble(2)); + log.info("Double =>C8_Style_GarmentLength={}",value8.getBigDecimal(1, RoundingMode.HALF_UP)); + + log.info("Double 1.24 => {}",new DepPathResultValue("1.24").getDouble(1)); + log.info("Double 1.25 => {}",new DepPathResultValue("1.25").getDouble(1)); + log.info("Double 1.26 => {}",new DepPathResultValue("1.26").getDouble(1)); + log.info("Double 1.99999999 => {}",new DepPathResultValue("1.99999999").getDouble(1)); + log.info("================================================="); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/fix") + public ResEntity fix() throws Exception { + DepPath build = DepPath.builder().addUrl("C193244") + .addPath("Child:CurrentRevision/Child:BOMProductColors") + .build(); + DepPathResult depPathResult = c8SearchService.depPathByUrl(build); + log.info("================================================="); + DepPathResultValue value0 = depPathResult.getValue("CurrentRevision.BOMProductColors", "C193244"); + log.info("value ={}",value0.getList()); + DepPathResultValue value1 = depPathResult.getValue("CurrentRevision.BOMProductColors.Code", "C193244"); + log.info("value ={}",value1.getList()); + log.info("================================================="); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/fix1") + public ResEntity fix1() throws Exception { + DepPath build = DepPath.builder().addUrl("C193244") + .addPath("Child:CurrentRevision/Child:BOMProductColors") + .build(); + DepPathResult depPathResult = c8SearchService.depPathByUrl(build); + log.info("================================================="); + DepPathResultValue value0 = depPathResult.getValue("CurrentRevision.BOMProductColors[0]", "C193244"); + log.info("value ={}",value0.getList()); + log.info("================================================="); + return WebResponse.success(ResCode.SUCCESS); + } + + @Resource + IDepPathSearchChain iDepPathSearchChain; + + @Resource + C8NodeService c8NodeService; + + @ResponseBody + @RequestMapping("/dto") + public ResEntity depPathByDTO(){ + DepPath deppath = DepPath.builder().addUrl("C2567990").build(); + List execute = iDepPathSearchChain.execute(deppath, StyleDemoDTO.class, null); +// List styleDemoDTOS = c8NodeService.depPathByEntity(deppath, StyleDemoDTO.class); + log.info(execute.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + + @ResponseBody + @RequestMapping("/dto1") + public ResEntity depPathByDTO1(){ + DepPath deppath = DepPath.builder().addUrl("C765541").build(); + List execute = iDepPathSearchChain.execute(deppath, SampleDemoDto.class, null); + log.info(execute.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/FeignLogDemoController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/FeignLogDemoController.java new file mode 100644 index 0000000..8410ebc --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/FeignLogDemoController.java @@ -0,0 +1,62 @@ +package com.centricsoftware.enhancement.modules.demo.controller; + +import com.centricsoftware.commons.ant.ControllerLog; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.modules.c8.component.design.chain.IDepPathSearchChain; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.c8.service.customview.ICustomViewHandler; +import com.centricsoftware.enhancement.modules.demo.dto.dep.StyleDemoDTO; +import com.centricsoftware.enhancement.modules.demo.feign.LocalhostFeign; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.util.List; + +/** + * description:CustomView 查询测试 + * Date: 2024/5/16 10:17 + */ +@Slf4j +@RequestMapping("/feign-log") +@Controller +public class FeignLogDemoController { + + @Resource + private ICustomViewHandler defaultCustomViewHandlerImpl; + + @ResponseBody + @RequestMapping("/receive") + public ResEntity search(@RequestBody StyleDemoDTO styleDemoDTO) throws Exception { + log.info(styleDemoDTO.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + @ResponseBody + @RequestMapping("/receive-list") + public ResEntity search(@RequestBody List styleDemoDTO) throws Exception { + log.info(styleDemoDTO.toString()); + return WebResponse.success(ResCode.SUCCESS); + } + + + @Resource + IDepPathSearchChain iDepPathSearchChain; + + @Resource + LocalhostFeign localhostFeign; + @ResponseBody + @RequestMapping("/send") + public ResEntity depPathByDTO1(){ + DepPath deppath = DepPath.builder().addUrl("C120527").build(); + List execute = iDepPathSearchChain.execute(deppath, StyleDemoDTO.class, null); + localhostFeign.sendStyles(execute); + localhostFeign.sendStyle(execute.get(0)); + return WebResponse.success(ResCode.SUCCESS); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/MultipleDataSourceDemoController.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/MultipleDataSourceDemoController.java new file mode 100644 index 0000000..e8532b9 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/controller/MultipleDataSourceDemoController.java @@ -0,0 +1,33 @@ +package com.centricsoftware.enhancement.modules.demo.controller; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.enhancement.modules.demo.service.IMultipleDataSourceDemoService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +/** + * description:多数据源测试类 + * Date: 2024/9/9 15:42 + */ +@Slf4j +@RequestMapping("/multiple-data-source") +@RestController +public class MultipleDataSourceDemoController { + + @Resource + private IMultipleDataSourceDemoService multipleDataSourceDemoService; + @RequestMapping("hse") + public ResEntity searchPartMaterial(){ + List hashMaps = multipleDataSourceDemoService.querySeason(); + log.info("查询结果:{}",hashMaps); + return WebResponse.success(ResCode.SUCCESS,hashMaps); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayAttributesDemoDTO.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayAttributesDemoDTO.java new file mode 100644 index 0000000..f444788 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayAttributesDemoDTO.java @@ -0,0 +1,18 @@ +package com.centricsoftware.enhancement.modules.demo.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import lombok.Data; + +import java.io.Serializable; + +/** + * description: + * Date: 2024/3/15 10:21 + */ +@Data +public class ColorwayAttributesDemoDTO implements Serializable { + + @DepPathField(exp = "__DomainKey__") + private String domainKey; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayDemoDTO.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayDemoDTO.java new file mode 100644 index 0000000..214be6d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/ColorwayDemoDTO.java @@ -0,0 +1,31 @@ +package com.centricsoftware.enhancement.modules.demo.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import lombok.Data; + +import java.io.Serializable; + +/** + * description: + * Date: 2024/3/15 10:21 + */ +@Data +public class ColorwayDemoDTO implements Serializable { + + @DepPathField(exp = "$Name") + private String name; + + @DepPathField(exp = "Code") + private String code; + + @DepPathField(exp = "Active") + private Boolean active; + + @DepPathField(exp = "ClassConfig.$Name") + private String classConfigName; + + + @DepPathField(exp = "Attributes",recursion = true) + private ColorwayAttributesDemoDTO colorwayAttributesDemoDTO; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/SampleDemoDto.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/SampleDemoDto.java new file mode 100644 index 0000000..9cebf4c --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/SampleDemoDto.java @@ -0,0 +1,21 @@ +package com.centricsoftware.enhancement.modules.demo.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class SampleDemoDto { + @DepPathField(exp = "__Parent__.__Parent__") + private String image_url; + + @JsonProperty("customer_no") + @DepPathField(exp = "__Parent__.__Parent__.__Parent__.Collection") + private String customerNo; + + @JsonProperty("plm_url") + @DepPathField(exp = "$URL") + private String plmUrl; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleAttributesDemoDTO.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleAttributesDemoDTO.java new file mode 100644 index 0000000..4dd9f8e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleAttributesDemoDTO.java @@ -0,0 +1,21 @@ +package com.centricsoftware.enhancement.modules.demo.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import lombok.Data; + +import java.io.Serializable; + +/** + * description: + * Date: 2024/3/15 10:20 + */ +@Data +public class StyleAttributesDemoDTO implements Serializable { + + @DepPathField(exp = "ActualSizeRange") + private String actualSizeRange; + + @DepPathField(exp = "BOMMainMaterialCount") + private String count; + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleDemoDTO.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleDemoDTO.java new file mode 100644 index 0000000..446e84f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/dto/dep/StyleDemoDTO.java @@ -0,0 +1,48 @@ +package com.centricsoftware.enhancement.modules.demo.dto.dep; + +import com.centricsoftware.enhancement.modules.c8.ant.DepPathField; +import com.centricsoftware.enhancement.modules.c8.em.DepPathEntityType; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * description: + * Date: 2024/3/15 10:20 + */ +@Data +public class StyleDemoDTO implements Serializable { + + @DepPathField(exp = "$Name") + private String styleName; + + @DepPathField(exp = "$URL") + private String url; + + @DepPathField(exp = "Active") + private Boolean active; + + @DepPathField(exp = "ModifiedAt",timeFormat="yyyy-MM-dd",type = DepPathEntityType.DATA_STRING) + private String dataStr; + + @DepPathField(exp = "AssortmentBOM.CurrentRevision") + private String currentRevision; + + @DepPathField(exp = "CntColorway") + private Integer cntColorway; + + @DepPathField(exp = "Collection",type = DepPathEntityType.REF) + private String collection; + + @DepPathField(exp = "ProductColors",recursion = true) + private List productColors; + + @DepPathField(exp = "ActiveColorways",type = DepPathEntityType.LIST) + private List activeColorways; + + @DepPathField(exp = "Attributes",recursion = true) + private StyleAttributesDemoDTO styleAttributesDemoDTO; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/feign/LocalhostFeign.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/feign/LocalhostFeign.java new file mode 100644 index 0000000..fbe5ca8 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/feign/LocalhostFeign.java @@ -0,0 +1,26 @@ +package com.centricsoftware.enhancement.modules.demo.feign; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.enhancement.modules.c8.feign.C8Feign; +import com.centricsoftware.enhancement.modules.demo.dto.dep.StyleDemoDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + + +/** + * scm 接口调用客户端 + */ +@FeignClient(url = "http://localhost:8088/plmservice/",name = "localhost-api",configuration = C8Feign.MultipartSupportConfig.class) +public interface LocalhostFeign { + + @PostMapping("/feign-log/receive?c8_name=测试款式接口") + ResEntity sendStyle(@RequestBody StyleDemoDTO requestBody); + + @PostMapping("/feign-log/receive-list?c8_name=测试款式接口List") + ResEntity sendStyles(@RequestBody List requestBody); + +} + diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/mapper/MultipleDataSourceDemoMapper.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/mapper/MultipleDataSourceDemoMapper.java new file mode 100644 index 0000000..6ebbd2a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/mapper/MultipleDataSourceDemoMapper.java @@ -0,0 +1,21 @@ +package com.centricsoftware.enhancement.modules.demo.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.HashMap; +import java.util.List; + +/** + * description: + * Date: 2024/9/9 15:48 + */ +@Mapper +@Repository +public interface MultipleDataSourceDemoMapper extends BaseMapper { + + @Select("select * from ed_season limit 1") + List querySeason(); +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/IMultipleDataSourceDemoService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/IMultipleDataSourceDemoService.java new file mode 100644 index 0000000..d320f56 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/IMultipleDataSourceDemoService.java @@ -0,0 +1,16 @@ +package com.centricsoftware.enhancement.modules.demo.service; + +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; + +/** + * description: + * Date: 2024/9/9 15:45 + */ +@Service +public interface IMultipleDataSourceDemoService { + + List querySeason(); +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/impl/MultipleDataSourceDemoServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/impl/MultipleDataSourceDemoServiceImpl.java new file mode 100644 index 0000000..ce64af9 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/demo/service/impl/MultipleDataSourceDemoServiceImpl.java @@ -0,0 +1,27 @@ +package com.centricsoftware.enhancement.modules.demo.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.centricsoftware.enhancement.modules.demo.mapper.MultipleDataSourceDemoMapper; +import com.centricsoftware.enhancement.modules.demo.service.IMultipleDataSourceDemoService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +/** + * description: + * Date: 2024/9/9 15:45 + */ +@Service +public class MultipleDataSourceDemoServiceImpl implements IMultipleDataSourceDemoService { + + @Resource + private MultipleDataSourceDemoMapper multipleDataSourceDemoMapper; + @DS("hse") + public List querySeason() { + List hashMaps = multipleDataSourceDemoMapper.querySeason(); + return hashMaps; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/NodeHelper.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/NodeHelper.java new file mode 100644 index 0000000..bc4d389 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/NodeHelper.java @@ -0,0 +1,58 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.List; +import java.util.function.Consumer; + +/** + * description: + * Date: 2023/11/19 16:26 + */ +public class NodeHelper { + + public static Element setRefList(Document doc,Element element, List list){ + return NodeHelper.setList(doc,element,list,"ref"); + } + + public static Element setListRemoveTag(Document doc,Element element, List list,String lineLabel){ + for(String url : list){ + Element ref = doc.createElement(lineLabel); + ref.setTextContent(url); + element.appendChild(ref); + } + return element; + } + + public static Element setList(Document doc,Element element, List list,String lineLabel){ + Element listElement = doc.createElement("List"); + element.appendChild(listElement); + for(String url : list){ + Element ref = doc.createElement(lineLabel); + ref.setTextContent(url); + listElement.appendChild(ref); + } + return listElement; + } + + public static Element addRefLine(Document doc, String ref, Consumer function){ + Element refElement = doc.createElement("ref"); + refElement.setTextContent(ref); + if(function!=null){ + function.accept(refElement); + } + return refElement; + + } + + public static Element searchNode(String parameter, String op,String value,Document doc){ + Element search = doc.createElement("Node"); + search.setAttribute("Parameter",parameter); + search.setAttribute("Op",op); + search.setAttribute("Value",value); + return search; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/call/C8CallMethod.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/call/C8CallMethod.java new file mode 100644 index 0000000..2d19fe9 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/call/C8CallMethod.java @@ -0,0 +1,111 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node.call; + +import cn.hutool.core.util.XmlUtil; +import com.centricsoftware.enhancement.modules.dml.dto.node.NodeHelper; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.List; + +public class C8CallMethod { + + public C8CallMethod(Document doc, Element firstElement){ + this.doc = doc; + this.firstElement = firstElement; + } + + public Document doc; + public Element firstElement; + + /** + * 标准的callList方法 + */ + public static C8CallMethod callList(String module, String operation, List list, String parameter, Integer limit) { + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("CallList"); + element.setAttribute("Module",module); + element.setAttribute("Operation",operation); + element.setAttribute("Parameter",parameter); + element.setAttribute("Limit",limit!=null?limit.toString():""); + NodeHelper.setRefList(doc,element,list); + C8CallMethod callMethod = new C8CallMethod(doc,element); + return callMethod; + } + + /** + * 标准的CallMethod + */ + public static C8CallMethod newCall(String module, String operation){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("Call"); + doc.appendChild(element); + element.setAttribute("Module",module); + element.setAttribute("Operation",operation); + C8CallMethod callMethod = new C8CallMethod(doc,element); + return callMethod; + } + + /** + * 标准的CallQuery + */ + public static C8CallMethod callQuery(String module, String operation, String param, C8Search search){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("CallQuery"); + element.setAttribute("Module",module); + element.setAttribute("Operation",operation); + element.setAttribute("Parameter",param); + //添加标签 + Element query = doc.createElement("Query"); + element.appendChild(query); + //添加Search语句 + query.appendChild(search.getFirstElement()); + C8CallMethod callQuery = new C8CallMethod(doc,element); + return callQuery; + } + + /** + * Call相关方法下,添加Parameter标签 + */ + public C8CallMethod addParameter(String id, String type,String value){ + Element parameterElement = doc.createElement("Parameter"); + parameterElement.setAttribute("Id",id); + parameterElement.setAttribute("Type",type); + parameterElement.setAttribute("Value",value); + firstElement.appendChild(parameterElement); + return this; + } + + /** + * Call相关方法下,添加DependencyPath标签 + * 例如:Child:Attributes + */ + public C8CallMethod addCallDependencyPath(String[] paths){ + for(String path : paths){ + addCallDependencyPath(path); + } + return this; + } + + + /** + * Call相关方法下,添加DependencyPath标签 + * 例如:Child:Attributes + */ + public C8CallMethod addCallDependencyPath(String path){ + Element dependencyPath = doc.createElement("DependencyPath"); + dependencyPath.setTextContent(path); + firstElement.appendChild(dependencyPath); + return this; + } + + /** + * 获取Operation + * @return + */ + public String getXml(){ + return XmlUtil.toStr(doc,"UTF-8",true,true); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/change/C8OperationNode.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/change/C8OperationNode.java new file mode 100644 index 0000000..751cca4 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/change/C8OperationNode.java @@ -0,0 +1,516 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node.change; + + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.XmlUtil; +import cn.hutool.http.HtmlUtil; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.modules.dml.dto.node.NodeHelper; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; +import com.google.common.collect.Lists; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +public class C8OperationNode { + + public C8OperationNode(Document doc, Element firstElement){ + this.doc = doc; + this.firstElement = firstElement; + doc.appendChild(firstElement); + } + + public Document doc; + public Element firstElement; + + public static C8OperationNode changeNode(String url) { + return C8OperationNode.changeNode(url, ""); + } + + public static C8OperationNode changeNode(String url, String path) { + String pathUrl = url; + if (StrUtil.isNotBlank(path)) { + pathUrl = url+"?Path=" + path.replaceAll(":", "%3A"); + } + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("ChangeNode"); + element.setAttribute("URL",pathUrl); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + public static C8OperationNode createNode(String url, String type){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("CreateNode"); + element.setAttribute("URL",url); + element.setAttribute("Type",type); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * 删除Node + */ + public static C8OperationNode deleteNode(String url){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("DeleteNode"); + element.setAttribute("URL",url); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * 删除用户 + */ + public static C8OperationNode deleteUser(String userId){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("DeleteUser"); + element.setAttribute("UserID",userId); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * 复制Node + */ + public static C8OperationNode copyNode(String url,String fromUrl){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("CopyNode"); + element.setAttribute("URL",url); + element.setAttribute("FromURL",fromUrl); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * 冻结/解冻 + * @param freeze true或者null:冻结; false:解冻; + */ + public static C8OperationNode freezeNode(String url,Boolean freeze){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("FreezeNode"); + element.setAttribute("URL",url); + element.setAttribute("Freeze",freeze==null?"true":freeze.toString()); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * FlushChanges + */ + public static C8OperationNode flushChanges(){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("FlushChanges"); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + + /** + * 校验Node + */ + public static C8OperationNode validateNode(String url){ + Document doc = XmlUtil.createXml(); + Element element = doc.createElement("ValidateNode"); + element.setAttribute("URL",url); + C8OperationNode changeNode = new C8OperationNode(doc,element); + return changeNode; + } + + /** + * @param otherAttributes 如果需要在PublishAttribute中添加其他属性,可以在map中添加,key未属性名,value为对应的值 + */ + public C8OperationNode publishAttribute(String id, String type, String value, HashMap otherAttributes) { + Element publishAttribute = doc.createElement("PublishAttribute"); + publishAttributeBase(publishAttribute,id,type,value,otherAttributes); + return this; + } + + /** + * @param otherAttributes 如果需要在PublishAttribute中添加其他属性,可以在map中添加,key未属性名,value为对应的值 + */ + private C8OperationNode publishAttributeBase(Element element, String id, String type, String value, HashMap otherAttributes) { + firstElement.appendChild(element); + element.setAttribute("Id",id); + element.setAttribute("Type",type); + element.setAttribute("Value",escape(value)); + if(otherAttributes!=null){ + otherAttributes.forEach((k,v)->element.setAttribute(k,v)); + } + return this; + } + + /** + * @param function 可通过入参Element添加ChangeAttribute的属性 + */ + public C8OperationNode publishAttribute(String id, String type, String value, Consumer function) { + Element publishAttribute = doc.createElement("PublishAttribute"); + publishAttributeBase(publishAttribute,id,type,value,null); + if(function!=null){ + function.accept(publishAttribute); + } + return this; + } + + public C8OperationNode publishAttribute(String id, String type, String value) { + publishAttribute(id,type,value,new HashMap<>()); + return this; + } + + /** + * 执行一次性表达式 + */ + public C8OperationNode changeAttributeOneExp(String id, String exp){ + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("ExpOp","OneTimeIfNone"); + element.setAttribute("Id",id); + element.setAttribute("Exp",escape(exp)); + firstElement.appendChild(element); + return this; + } + + /** + * 执行一次性表达式 + * @param attributeUrl 字段的URL,比如款式Node Name的URL为【centric://REFLECTION/BuildInAttribute/Style/Node Name】 + */ + public C8OperationNode changeAttributeOneExpByAttributeUrl(String id, String attributeUrl){ + String exp = StrFormatter.format("ref(\"{}\").Expression.eval()", attributeUrl); + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("ExpOp","OneTimeIfNone"); + element.setAttribute("Id",id); + element.setAttribute("Exp",escape(exp)); + firstElement.appendChild(element); + return this; + } + + /** + * 禁用实时表达式,并对其赋值 + */ + public C8OperationNode changeAttributeDisable(String id , String type, String attributeValue){ + disableAttribute(id); + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("Id",id); + element.setAttribute("Type",type); + element.setAttribute("Value",attributeValue); + firstElement.appendChild(element); + return this; + } + + /** + * 禁用实时表达式 + */ + public C8OperationNode disableAttribute(String id){ + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("AddFG","4194304"); + element.setAttribute("DelFG","16384"); + element.setAttribute("Id",id); + firstElement.appendChild(element); + return this; + } + + /** + * 设置字段的flag为:MODIFIED BY_CLIENT + */ + public C8OperationNode modifyAttribute(String id){ + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("AddFG","262144"); + element.setAttribute("Id",id); + firstElement.appendChild(element); + return this; + } + + /** + * 在ChangeNode中拼接自定义的ChangeAttribute + */ + public C8OperationNode addCustomChangeXml(String changeXml){ + firstElement.setTextContent(changeXml); + return this; + } + + public C8OperationNode changeAttribute(String id, String type, String value){ + return changeAttribute(id,type,value,null); + } + + public C8OperationNode changeAttributeRef(String id, String value){ + modifyAttribute(id); + Element element = changeAttributeBase(id, "ref", escape(value), null); + firstElement.appendChild(element); + return this; + } + + private Element changeAttributeBase(String id, String type,String value,Element element){ + if(element==null){ + element = doc.createElement("ChangeAttribute"); + } + element.setAttribute("Id",id); + element.setAttribute("Type",type); + element.setAttribute("Value",value); + return element; + } + + /** + * @param function 可通过入参Element添加ChangeAttribute的属性 + */ + public C8OperationNode changeAttribute(String id, String type, String value, Function function){ + modifyAttribute(id); + /* + * 如果value不是json,需要转译 + */ + if(!JSONUtil.isJson(value)){ + value = escape(value); + } + Element changeElement = changeAttributeBase(id, type, value,null); + firstElement.appendChild(changeElement); + if(function!=null){ + function.apply(changeElement); + } + return this; + } + + /** + * 将changeNode或者createNode的URL,添加到 @param url的 @param id中 + */ + public C8OperationNode attach(String url, String id){ + Element attach = doc.createElement("Attach"); + attach.setAttribute("URL",url); + attach.setAttribute("Id",id); + firstElement.appendChild(attach); + return this; + } + + /** + * 将changeNode或者createNode的URL,添加到 @param url的 @param id中 + */ + public C8OperationNode attach(List urls, String id){ + for(String url : urls){ + attach(url,id); + } + return this; + } + + /** + * @param lineLabel 行标签,比如等 + * @param addPropertyFunction 在ChangeAttribute中添加属性,入参是Element + * @param addLinePropertyFunction 在ChangeAttribute的明细行中添加属性,入参是Element + */ + public C8OperationNode changeMap(String id, String lineLabel, Consumer addPropertyFunction, Consumer addLinePropertyFunction){ + mapBase("ChangeAttribute",id,lineLabel,addPropertyFunction,addLinePropertyFunction); + return this; + } + + /** + * @param lineLabel 行标签,比如等 + * @param addPropertyFunction 在ChangeAttribute中添加属性,入参是Element + * @param addLinePropertyFunction 在ChangeAttribute的明细行中添加属性,入参是Element + */ + public C8OperationNode publishMap(String id, String lineLabel, Consumer addPropertyFunction, Consumer addLinePropertyFunction){ + mapBase("PublishAttribute",id,lineLabel,addPropertyFunction,addLinePropertyFunction); + return this; + } + + /** + * @param lineLabel 行标签,比如等 + * @param addPropertyFunction 在ChangeAttribute中添加属性,入参是Element + * @param addLinePropertyFunction 在ChangeAttribute的明细行中添加属性,入参是Element + */ + private C8OperationNode mapBase(String changeOrPublish, String id, String lineLabel, Consumer addPropertyFunction, Consumer addLinePropertyFunction){ + //拼接ChangeAttribute + Element element = doc.createElement(changeOrPublish); + element.setAttribute("Id",id); + firstElement.appendChild(element); + if(addPropertyFunction!=null){ + addPropertyFunction.accept(element); + } + //拼接ChangeAttribute下的行 + if(addLinePropertyFunction!=null){ + Element lineElement = doc.createElement(lineLabel); + addLinePropertyFunction.accept(lineElement); + element.appendChild(lineElement); + } + return this; + } + + /** + * 修改通过key修改refmap的值 + */ + public C8OperationNode changeRefMapBySetKey(String id, String key, String value){ + if(StrUtil.isBlank(value)){ + return this; + } + Element element = doc.createElement("ChangeAttribute"); + element.setAttribute("Type","refmap"); + element.setAttribute("Id",id); + element.setAttribute("ValueOp","SetKey"); + firstElement.appendChild(element); + Element refLine = NodeHelper.addRefLine(doc, value, i->i.setAttribute("Key", key)); + element.appendChild(refLine); + return this; + } + + /** + * 修改map的值,覆盖原有值 + */ + public C8OperationNode changeAttributeMap(String id, String type, String lineLabel, Map map){ + return changeAttributeMap(id,type,map,lineLabel,""); + } + + /** + * map中添加值 + */ + public C8OperationNode changeAttributeMapAppend(String id, String type, String lineLabel, Map map){ + return changeAttributeMap(id,type,map,lineLabel,"Append"); + } + + /** + * 修改map字段的信息 + * @param id 字段ID + * @param type map类型:refmap、stringmap等 + * @param map map下的具体信息 + * @param lineLabel map行标签,比如ref、string等 + * @param valueOp ChangeAttribute中的valueOp选项 + */ + public C8OperationNode changeAttributeMap(String id, String type, Map map, String lineLabel, String valueOp){ + changeMap(id,lineLabel,ca->{ + ca.setAttribute("Type",type); + ca.setAttribute("ValueOp",valueOp); + for(String v : map.keySet()){ + Element line = doc.createElement(lineLabel); + line.setAttribute("Key",v); + line.setTextContent(map.get(v)); + ca.appendChild(line); + } + },null); + return this; + } + + /** + * 修改publishAttribute类型未stringmap的字段 + * @param valueOp 比如 ValueOp="Remove" + */ + public C8OperationNode publishAttributeStringMap(String id, Map map, String valueOp){ + publishMap(id,"string",ca->{ + ca.setAttribute("Type","stringmap"); + ca.setAttribute("ValueOp",valueOp); + map.forEach((k,v)->{ + Element line = doc.createElement("string"); + line.setTextContent(map.get(v)); + ca.appendChild(line); + }); + },null); + return this; + } + + /** + * 对list进行赋值,该操作会覆盖原有的值 + */ + public C8OperationNode changeAttributeList(String id, String type, List value, String lineLabel){ + return changeAttributeList(id,type,value,"",lineLabel); + } + + /** + * 对list字段进行操作,通过valueOp字段指定操作类型 + * @param valueOp 标准的valueOp字段 + */ + public C8OperationNode changeAttributeList(String id, String type, String value, String valueOp, String lineLabel){ + List list = Lists.newArrayList(); + list.add(value); + return changeAttributeList(id,type,list,valueOp,lineLabel); + } + + /** + * 对list字段进行操作,通过valueOp字段指定操作类型 + * @param valueOp 标准的valueOp字段 + */ + public C8OperationNode changeAttributeList(String id, String type, List valueList, String valueOp, String lineLabel){ + Element element = doc.createElement("ChangeAttribute"); + firstElement.appendChild(element); + element.setAttribute("Id",id); + element.setAttribute("Type",type); + if(!StrUtil.isBlankOrUndefined(valueOp)){ + element.setAttribute("ValueOp",valueOp); + } + NodeHelper.setListRemoveTag(doc,element,valueList,lineLabel); + return this; + } + + /** + * 在list中追加值 + * @return + */ + public C8OperationNode changeAttributeListAppend(String id, String type, String url, String lineLabel){ + List strings = Lists.newArrayList(url); + return changeAttributeList(id,type,strings,"Append",lineLabel); + } + public C8OperationNode changeAttributeListRemove(String id, String type, String url, String lineLabel){ + List strings = Lists.newArrayList(url); + return changeAttributeList(id,type,strings,"Remove",lineLabel); + } + public C8OperationNode changeAttributeListRemove(String id, String type, List strings, String lineLabel){ + return changeAttributeList(id,type,strings,"Remove",lineLabel); + } + public C8OperationNode changeAttributeListAppend(String id, String type, List strings, String lineLabel){ + return changeAttributeList(id,type,strings,"Append",lineLabel); + } + public C8OperationNode changeAttributeListAppendOrRemove(String id, String type, List strings, String valueOp, String lineLabel){ + return changeAttributeList(id,type,strings,valueOp,lineLabel); + } + + public C8OperationNode publishAttributeStringMap(String id, Map value){ + return publishAttributeStringMap(id,value,""); + } + + + /** + * ChangeQuery、validateQuery、deleteQuery + */ + public static C8OperationNode operationByQuery(String operationType, C8Search search){ + Document doc = search.getDoc(); + Element query = search.getFirstElement(); + Element firstElement = doc.createElement(operationType); + doc.removeChild(query); + firstElement.appendChild(query); + return new C8OperationNode(doc,firstElement); + } + + /** + * 该方法暂时有问题,setTextContent会将内容进行转译 + * ChangeQuery、validateQuery、deleteQuery + */ + public static C8OperationNode operationByQuery(String operationType, String search){ + Document doc = XmlUtil.createXml(); + Element firstElement = doc.createElement(operationType); + Element queryElement = doc.createElement("Query"); + queryElement.setTextContent(search); + firstElement.appendChild(queryElement); + return new C8OperationNode(doc,firstElement); + } + + public static C8OperationNode operationByList(String operationType, List urls){ + Document doc = XmlUtil.createXml(); + Element firstElement = doc.createElement(operationType); + NodeHelper.setRefList(doc,firstElement,urls); + return new C8OperationNode(doc,firstElement); + } + + public String escape(String text){ + String text1 = text; + try{ + text1 = HtmlUtil.escape(text); + }finally { + return text1; + } + } + + public String getXml(){ + return XmlUtil.toStr(doc,"UTF-8",true,true); + } + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/delete/C8Delete.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/delete/C8Delete.java new file mode 100644 index 0000000..0854615 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/delete/C8Delete.java @@ -0,0 +1,19 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node.delete; + +import java.util.List; + +public class C8Delete { + + public static String deleteList(List list){ + StringBuilder xml = new StringBuilder(); + xml.append(""); + xml.append(""); + for(String url : list){ + xml.append("").append(url).append(""); + } + xml.append(""); + xml.append(""); + return xml.toString(); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8Search.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8Search.java new file mode 100644 index 0000000..5d15395 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8Search.java @@ -0,0 +1,157 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node.search; + +import cn.hutool.core.util.XmlUtil; +import com.centricsoftware.enhancement.modules.dml.dto.node.NodeHelper; +import lombok.Getter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * Search构造器 + */ +public class C8Search { + + @Getter + public Document doc; + @Getter + public Element firstElement; + + public C8Search(Document doc,Element firstElement){ + this.doc = doc; + this.firstElement = firstElement; + } + + public static C8Search newSearch(String type){ + Document doc = XmlUtil.createXml(); + Element search = doc.createElement("Query"); + doc.appendChild(search); + Element typeNode = NodeHelper.searchNode("Type", "EQ", type, doc); + search.appendChild(typeNode); + C8Search node = new C8Search(doc,search); + return node; + } + + public C8SearchAttribute or(){ + Element or = doc.createElement("OR"); + firstElement.appendChild(or); + return new C8SearchAttribute(doc,or); + } + + public C8SearchAttribute and(){ + Element and = doc.createElement("AND"); + firstElement.appendChild(and); + return new C8SearchAttribute(doc,and); + } + + /** + * @param sequence ASC、DESC + */ + public C8Search sortByNode(String parameter,String sequence,String path){ + Element order = doc.createElement("OrderByNode"); + order.setAttribute("Path",path); + order.setAttribute("Parameter",parameter); + order.setAttribute("Sequence",sequence); + firstElement.appendChild(order); + return this; + } + + /** + * @param sequence ASC、DESC + */ + public C8Search sortByAttribute(String parameter,String sequence,String path){ + Element order = doc.createElement("OrderByAttribute"); + order.setAttribute("Path",path); + order.setAttribute("Id",parameter); + order.setAttribute("Sequence",sequence); + firstElement.appendChild(order); + return this; + } + + /** + * @param sequence ASC、DESC + */ + public C8Search sortByIndex(String parameter,String sequence,String path){ + Element order = doc.createElement("OrderByIndex"); + order.setAttribute("Path",path); + order.setAttribute("Id",parameter); + order.setAttribute("Sequence",sequence); + firstElement.appendChild(order); + return this; + } + + public C8Search access(String right,String path){ + Element order = doc.createElement("Access"); + order.setAttribute("Path",path); + order.setAttribute("Right",right); + firstElement.appendChild(order); + return this; + } + + + public C8Search dimension(String id, String op,String value){ + return dimension(id,op,value,""); + } + + public C8Search dimension(String id, String op,String value,String path){ + Element attribute = doc.createElement("Attribute"); + firstElement.appendChild(attribute); + attribute.setAttribute("Path",path); + attribute.setAttribute("Id",id); + attribute.setAttribute("Path",path); + attribute.setAttribute("Op",op); + attribute.setAttribute("Dimension",value); + return this; + } + + public C8Search node(String parameter, String op,String value){ + NodeHelper.searchNode(parameter, op,value, doc); + return this; + } + + private C8Search attr(String path, String id, String op, String valueType,String value){ + Element attribute = doc.createElement("Attribute"); + firstElement.appendChild(attribute); + attribute.setAttribute("Path",path); + attribute.setAttribute("Id",id); + attribute.setAttribute("Op",op); + attribute.setAttribute(valueType,value); + return this; + } + + public C8Search attrRef(String id, String op,String value){ + return attr("",id,op,"RValue",value); + } + + public C8Search attrRef(String id, String op,String value,String path){ + return attr(path,id,op,"RValue",value); + } + + public C8Search attr(String id, String op,String value){ + return attr("",id,op,"SValue",value); + } + + public C8Search attr(String id, String op,String value,String path){ + return attr(path,id,op,"SValue",value); + } + + public C8Search attrNum(String id, String op, String valueType,String value,String path){ + return attr(path,id,op,"DValue",value); + } + + public C8Search attrNum(String id, String op, String valueType,String value){ + return attr("",id,op,"DValue",value); + } + + public String getXml(){ + String xml = XmlUtil.toStr(doc, "UTF-8", true, true); + xml = xml.replaceAll("",""); + xml = xml.replaceAll("",""); + return xml; + } + + public String getXmlWithSearchTag(){ + return XmlUtil.toStr(doc, "UTF-8", true, true); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8SearchAttribute.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8SearchAttribute.java new file mode 100644 index 0000000..a5fa0cf --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/dto/node/search/C8SearchAttribute.java @@ -0,0 +1,112 @@ +package com.centricsoftware.enhancement.modules.dml.dto.node.search; + +import cn.hutool.core.util.XmlUtil; +import com.centricsoftware.enhancement.modules.dml.dto.node.NodeHelper; +import lombok.Getter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.List; + +/** + * description:OR和And的Search 构造器 + * Date: 2023/11/19 22:30 + */ +public class C8SearchAttribute { + + @Getter + public Element firstElement; + + private Document doc; + + public C8SearchAttribute(Document doc,Element firstElement){ + this.doc = doc; + this.firstElement = firstElement; + } + + public C8SearchAttribute or(){ + Element or = getDoc().createElement("OR"); + firstElement.appendChild(or); + return new C8SearchAttribute(doc,or); + } + + public C8SearchAttribute and(){ + Element and = getDoc().createElement("AND"); + firstElement.appendChild(and); + return new C8SearchAttribute(doc,and); + } + + + public C8SearchAttribute dimension(String id, String op,String value){ + return dimension(id,op,value,""); + } + + public C8SearchAttribute dimension(String id, String op,String value,String path){ + Element attribute = getDoc().createElement("Attribute"); + firstElement.appendChild(attribute); + attribute.setAttribute("Path",path); + attribute.setAttribute("Id",id); + attribute.setAttribute("Path",path); + attribute.setAttribute("Op",op); + attribute.setAttribute("Dimension",value); + return this; + } + + public C8SearchAttribute node(String parameter, String op,String value){ + NodeHelper.searchNode(parameter, op,value, getDoc()); + return this; + } + + /** + * 查询 + */ + private C8SearchAttribute orList(String path, String id, String op, String valueType, List value){ + for(String url : value){ + attr(path,id,op,valueType,url); + } + return this; + } + + private C8SearchAttribute attr(String path, String id, String op, String valueType,String value){ + Element attribute = getDoc().createElement("Attribute"); + firstElement.appendChild(attribute); + attribute.setAttribute("Path",path); + attribute.setAttribute("Id",id); + attribute.setAttribute("Op",op); + attribute.setAttribute("Path",path); + attribute.setAttribute(valueType,value); + return this; + } + + public C8SearchAttribute attrRef(String id, String op,String value){ + return attr("",id,op,"RValue",value); + } + + public C8SearchAttribute attrRef(String id, String op,String value,String path){ + return attr(path,id,op,"RValue",value); + } + + public C8SearchAttribute attr(String id, String op,String value){ + return attr("",id,op,"SValue",value); + } + + public C8SearchAttribute attr(String id, String op,String value,String path){ + return attr(path,id,op,"SValue",value); + } + + public C8SearchAttribute attrNum(String id, String op, String valueType,String value,String path){ + return attr(path,id,op,"DValue",value); + } + + public C8SearchAttribute attrNum(String id, String op, String valueType,String value){ + return attr("",id,op,"DValue",value); + } + + public Document getDoc(){ + if(doc==null){ + doc = XmlUtil.createXml(); + } + return doc; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/util/C8Write.java b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/util/C8Write.java new file mode 100644 index 0000000..2e66b63 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/modules/dml/util/C8Write.java @@ -0,0 +1,141 @@ +package com.centricsoftware.enhancement.modules.dml.util; + +import com.centricsoftware.enhancement.modules.dml.dto.node.call.C8CallMethod; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.centricsoftware.enhancement.modules.dml.dto.node.search.C8Search; + +import java.util.List; + +/** + * description:@module csi/xml/Writer + * Date: 2023/11/19 16:03 + */ +public class C8Write { + + /** + * 复制Node + */ + public static C8OperationNode copyNode(String url,String fromUrl){ + return C8OperationNode.copyNode(url,fromUrl); + } + + /** + * 删除Node + */ + public static C8OperationNode deleteNode(String url){ + return C8OperationNode.deleteNode(url); + } + + /** + * 删除用户 + */ + public static C8OperationNode deleteUser(String userId){ + return C8OperationNode.deleteUser(userId); + } + + /** + * 冻结/解冻 + * @param freeze true或者null:冻结; false:解冻; + */ + public static C8OperationNode freezeNode(String url,Boolean freeze){ + return C8OperationNode.freezeNode(url,freeze); + } + + public static C8OperationNode flushChanges(){ + return C8OperationNode.flushChanges(); + } + + /** + * 校验Node + */ + public static C8OperationNode validateNode(String url){ + return C8OperationNode.validateNode(url); + } + + /** + * 修改Node + */ + public static C8OperationNode changeNode(String url){ + return C8OperationNode.changeNode(url); + } + + /** + * 修改Node,带路径 + */ + public static C8OperationNode changeNode(String url, String path){ + return C8OperationNode.changeNode(url,path); + } + + /** + * 标准callList + */ + public static C8CallMethod callList(String module, String operation, List list, String parameter, Integer limit){ + return C8CallMethod.callList(module, operation, list, parameter, limit); + } + + /** + * 标准callList + */ + public static C8CallMethod callList(String module, String operation, List list, String parameter){ + return C8CallMethod.callList(module, operation, list, parameter, 10000); + } + + /** + * 标准callList + */ + public static C8CallMethod callMethod(String module, String operation){ + return C8CallMethod.newCall(module, operation); + } + + public static C8CallMethod callQuery(String module, String operation, String param, C8Search search){ + return C8CallMethod.callQuery(module, operation,param,search); + } + + public static C8OperationNode changeQuery( C8Search search){ + return C8OperationNode.operationByQuery("ChangeQuery",search); + } + + /** + * 该方法暂时有问题,setTextContent会将内容进行转译 + */ + public static C8OperationNode changeQuery(String search){ + return C8OperationNode.operationByQuery("ChangeQuery",search); + } + + /** + * 该方法暂时有问题,setTextContent会将内容进行转译 + */ + public static C8OperationNode validateQuery(String search){ + return C8OperationNode.operationByQuery("ValidateQuery",search); + } + + /** + * 该方法暂时有问题,setTextContent会将内容进行转译 + */ + public static C8OperationNode deleteQuery( String search){ + return C8OperationNode.operationByQuery("DeleteQuery", search); + } + + + public static C8OperationNode validateQuery(C8Search search){ + return C8OperationNode.operationByQuery("ValidateQuery",search); + } + + public static C8OperationNode deleteQuery( C8Search search){ + return C8OperationNode.operationByQuery("DeleteQuery", search); + } + + public static C8OperationNode changeList(List urls){ + return C8OperationNode.operationByList("ChangeList", urls); + } + + public static C8OperationNode deleteList(List urls){ + return C8OperationNode.operationByList("DeleteList", urls); + } + + public static C8OperationNode validateList(List urls){ + return C8OperationNode.operationByList("ValidateList", urls); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/ImagesUploadService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/ImagesUploadService.java new file mode 100644 index 0000000..468e76e --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/ImagesUploadService.java @@ -0,0 +1,119 @@ +package com.centricsoftware.enhancement.service; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.dto.UploadImagesEntity; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 多图片编辑方案 + */ +@Service +public class ImagesUploadService { + + @Autowired + C8NodeService c8NodeService; + //图片缓存地址 + @Value("${cs.plm.cache-file-dir:D:\\C8\\JavaService}") + private String cachePath; +// private String classPath = ImagesUploadService.class.getClassLoader().getResource("").getPath(); + + /** + * 删除图片 + * @param url + * @param fieldId + * @param delList + * @throws Exception + */ + public void removeImage(String url,String fieldId,String delList) throws Exception { + JSONArray array = JSONUtil.parseArray(delList); + StringBuilder xml = new StringBuilder(); + xml.append(" "); + xml.append(""); + for(int i =0;i"+pid+""); + } + } + xml.append(""); + xml.append(""); + c8NodeService.processNode(xml.toString()); + } + + /** + * 插入图片 + * @param url + * @param fieldId + * @param saveFiles + */ + public void insertImages(String url, String fieldId, MultipartFile[] saveFiles) throws Exception { + StringBuilder xml = new StringBuilder(); + xml.append(" "); + xml.append(""); + for(MultipartFile file : saveFiles){ +// String filePath = cachePath+file.getName(); +// FileUtil.writeBytes(file.getBytes(),filePath); + String imageUrl = c8NodeService.publishFile(file, file.getName()); + xml.append(""+imageUrl+""); +// FileUtil.del(filePath); + } + xml.append(""); + xml.append(""); + c8NodeService.processNode(xml.toString()); + } + + /** + * 图片编辑初始化 + * @param url + * @param fieldId + * @param error + * @return + */ + public List initImages(String url, String fieldId,StringBuilder error) throws Exception{ + List> list = Lists.newArrayList(); + String fields = c8NodeService.queryExpressionResult(fieldId,url); + if(StrUtil.isBlank(fields)){ + return list; + } + String[] split = fields.split(","); + for(int i=0;i map = Maps.newHashMap(); + map.put("smallImage",pre+smallImage); + map.put("viewable",pre+viewable); + map.put("name",node_name); + map.put("imageUrl",file); + map.put("url",pre+smallImage); + map.put("seq",i+""); + map.put("alt",node_name); + map.put("pid",file); + map.put("thumb",pre+smallImage); + map.put("src",pre+smallImage); + list.add(map); + } + return list; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/TableConfigService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/TableConfigService.java new file mode 100644 index 0000000..0783823 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/TableConfigService.java @@ -0,0 +1,124 @@ +package com.centricsoftware.enhancement.service; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.enhancement.ant.grid.GridColumn; +import com.centricsoftware.enhancement.ant.grid.GridForm; +import com.centricsoftware.enhancement.dto.table.GridOptions; +import com.centricsoftware.enhancement.dto.table.gridconfig.*; +import com.centricsoftware.enhancement.service.importAnnotation.BaseImportService; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.lang.reflect.Field; +import java.util.List; + +@Service +@Slf4j +public class TableConfigService { + @Resource + BaseImportService baseImportService; + + + public GridOptions getCommonGridOptions(Class configClass){ + GridOptions gridOptions = GridOptions.builder().build(); + List fields = baseImportService.getAllFields(configClass); + addColumnConfig(fields,gridOptions);//添加表格列配置 + addFormConfig(fields,gridOptions); + return gridOptions; + } + + public void addRightBtn(GridOptions gridOptions,String title,String content,String clickUrl){ + RightBtnConfig build = RightBtnConfig.builder().title(title).content(content).clickUrl(clickUrl).build(); + gridOptions.getRightBtnConfigs().add(build); + } + + /** + * 添加Grid列配置 + * @param configClass 实体类 + * @param gridOptions 传入需要返回到前端的实体化配置 + */ + public void addColumnConfig(Class configClass,GridOptions gridOptions) { + List fields = baseImportService.getAllFields(configClass); + addColumnConfig(fields,gridOptions);//添加Grid列配置 + addFormConfig(fields,gridOptions);//添加Form搜索配置 + } + + /** + * 添加Form搜索配置 + * @param fields 所有字段 + * @param gridOptions 传入需要返回到前端的实体化配置 + */ + private void addFormConfig(List fields, GridOptions gridOptions) { + + FormConfig formConfig = FormConfig.builder().build(); + List items = Lists.newArrayList(); + for(Field field : fields){ + GridColumn gridAnnotation = AnnotationUtil.getAnnotation(field, GridColumn.class);//获取注解 + if(gridAnnotation!=null) { + if(!gridAnnotation.formFilter()) continue; + } + GridForm formAnnotation = AnnotationUtil.getAnnotation(field, GridForm.class);//获取注解 + FormItemConfig formItemConfig = FormItemConfig.builder().build(); + FromItemRender itemRender = FromItemRender.builder().build(); + if(formAnnotation==null){ + if(gridAnnotation==null) continue; + //未配置GridForm,则取GridColumn上的title、field,其余的都按照默认值 + formItemConfig.setField(StrUtil.isBlank(gridAnnotation.field())?field.getName():gridAnnotation.field()); + formItemConfig.setTitle(gridAnnotation.title()); + }else { + //配置GridForm,以配置的GridForm为准 + formItemConfig.setField(StrUtil.isBlank(formAnnotation.field())?field.getName():formAnnotation.field()); + formItemConfig.setTitle(StrUtil.isBlank(formAnnotation.title())?field.getName():formAnnotation.title()); + formItemConfig.setSpan(formAnnotation.span()); + formItemConfig.setTitlePrefix(formAnnotation.titlePrefix()); + if(StrUtil.isNotBlank(formAnnotation.options())){ + JSONArray objects = JSONUtil.parseArray(formAnnotation.options()); + List convert = Convert.convert(List.class, objects); + itemRender.setOptions(convert); + } + itemRender.setName(formAnnotation.filterType()); + itemRender.setProps(StrUtil.isBlank(formAnnotation.props())?null:JSONUtil.parseObj(formAnnotation.props())); + itemRender.setClearable(formAnnotation.clearable()); + itemRender.setPlaceholder(formAnnotation.placeholder()); + itemRender.setFilterable(formAnnotation.filterable()); + } + formItemConfig.setItemRender(itemRender); + items.add(formItemConfig); + } + formConfig.setItems(items); + gridOptions.setFormConfig(formConfig); + } + + /** + * 添加Grid列配置 + * @param fields 所有字段 + * @param gridOptions 传入需要返回到前端的实体化配置 + */ + public void addColumnConfig(List fields,GridOptions gridOptions) { + List list = Lists.newArrayList(); + for(Field field : fields){ + GridColumn gridAnnotation = AnnotationUtil.getAnnotation(field, GridColumn.class);//获取注解 + if(gridAnnotation==null) continue; + ColumnConfig build = ColumnConfig.builder() + .field(StrUtil.isBlank(gridAnnotation.field())?field.getName():gridAnnotation.field()) + .title(gridAnnotation.title()) + .width(gridAnnotation.width()) + .fixed(gridAnnotation.fixed()) + .sortable(gridAnnotation.sortable()) + .formatter(gridAnnotation.formatter()) + .filters(StrUtil.isBlank(gridAnnotation.filters())?null: JSONUtil.parse(gridAnnotation.filters())) + .filterRender(StrUtil.isBlank(gridAnnotation.filterRender())?null:JSONUtil.parseObj(gridAnnotation.filterRender())) + .build(); + list.add(build); + } + gridOptions.setColumns(list); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesDCLService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesDCLService.java new file mode 100644 index 0000000..894319c --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesDCLService.java @@ -0,0 +1,193 @@ +package com.centricsoftware.enhancement.service.bo; + + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.component.CacheComponent; +import com.centricsoftware.enhancement.modules.c8.dto.OperationResultEntity; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * description:创建BO字段;生成operation + * Date: 2023/3/16 15:51 + */ +@Slf4j +@Service +public class BOAttributesDCLService { + + @Resource + C8NodeService c8NodeService; + + @Resource + CacheComponent cacheComponent; + + private String cantCopyOperation(){ + return "\n" + + "\t\t\tCopyCategory:NoCopy\n" + + "\t\t"; + } + + private String getInitialValue(String type){ + String initValue = """"; + if(type.endsWith("list")||type.endsWith("set")||type.endsWith("vector")){ + initValue = "[]"; + }else if("ref".equals(type)){ + initValue = ""centric:""; + }else if("boolean".equals(type)){ + initValue = "false"; + }else if(type.endsWith("map")){ + initValue = "{}"; + } + return initValue; + } + + public boolean createFieldBooleanNoCopy(String boUrl,String attributeId,String attributeName,String type,String tips,String notes){ + String other = cantCopyOperation(); + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,other); + } + + public boolean createFieldStrNoCopy(String boUrl,String attributeId,String attributeName,String type,String tips,String notes){ + String other = cantCopyOperation(); + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,other); + } + + public boolean createFieldRefNoCopy(String boUrl,String attributeId,String attributeName,String type,String targetClassName,String tips,String notes){ + String other = cantCopyOperation(); + other += "\n" + + " "; + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,other); + } + + public boolean createFieldBoolean(String boUrl,String attributeId,String attributeName,String type,String tips,String notes){ + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,""); + } + + public boolean createFieldStr(String boUrl,String attributeId,String attributeName,String type,String tips,String notes){ + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,""); + } + + public boolean createFieldRef(String boUrl,String attributeId,String attributeName,String type,String targetClassName,String tips,String notes){ + String other = "\n" + + " "; + return createField(boUrl,attributeId,attributeName,type,tips,notes,false,other); + } + + public boolean createField(String boUrl,String attributeId,String attributeName,String type ,String tips,String notes,boolean readOnly,String other){ + String attributeFlag = readOnly?"1":"0"; + String initValue = getInitialValue(type); + String customAttribute = c8NodeService.queryFirstByNodeName("CustomAttribute", attributeId); + if(StrUtil.isNotBlank(customAttribute)){ + return false; + } + String rootClassName = getRootClassName(boUrl); + String fieldUrl = boUrl+"/"+attributeId; + fieldUrl = fieldUrl.replace("BusinessObject","CustomAttribute"); + String operation = " \n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + updateLocaleConfiguration(boUrl,attributeId,attributeName)+ + updateLocaleConfiguration(boUrl,attributeId+"_TT",tips)+ + "\t\n" + + "\t\t\n" + + "\t\n" + + "\t\n"; + try { + OperationResultEntity resultEntity = c8NodeService.processNode(operation); + return true; + }catch (Exception e){ + log.error("新建字段失败:"+e.getCause(),e); + return false; + } + + } + + private String updateLocaleConfiguration(String boUrl,String attributeId,String value){ + String defaultLocale = getDefaultLocale(); + String rootClassName = getRootClassName(boUrl); + String format = StrFormatter.format("{}.Attr.{}", rootClassName, attributeId); + return "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\t"+value+"\n" + + "\t\t\n" + + "\t\n" ; + } + + private String getDefaultLocale(){ + String url = "centric://REFLECTION/INSTANCE/CompanyInfo/Global"; + String key = "DefaultLocale"; + String defaultLocale = cacheComponent.getStr(key); + if(StrUtil.isBlank(defaultLocale)){ + defaultLocale =c8NodeService.queryExpressionResult("DefaultLocale", url); + cacheComponent.setValue(key,defaultLocale,2*60 * DateUnit.MINUTE.getMillis());//2小时失效 + } + return defaultLocale; + } + + private String getRootClassName(String boUrl){ + String key = StrFormatter.format("BO-RootClassName-{}", boUrl); + String rootClassName = cacheComponent.getStr(key); + if(StrUtil.isBlank(rootClassName)){ + rootClassName = c8NodeService.queryExpressionResult("RootClassName", boUrl); + cacheComponent.setValue(key,rootClassName,2*60 * DateUnit.MINUTE.getMillis());//2小时失效 + } + return rootClassName; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesService.java new file mode 100644 index 0000000..6903319 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/bo/BOAttributesService.java @@ -0,0 +1,20 @@ +package com.centricsoftware.enhancement.service.bo; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * description:创建BO字段 + * Date: 2023/3/16 15:51 + */ +@Slf4j +@Service +public class BOAttributesService { + + @Resource + BOAttributesDCLService boAttributesDCLService; + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/BaseImportService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/BaseImportService.java new file mode 100644 index 0000000..5e16bba --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/BaseImportService.java @@ -0,0 +1,136 @@ +package com.centricsoftware.enhancement.service.importAnnotation; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.config.entity.CenterProperties; +import com.centricsoftware.enhancement.ant.C8Column; +import com.centricsoftware.enhancement.ant.C8Entity; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +/** + * 该类主要是一些关于导入的通用工具类 + */ +@Service +@Slf4j +public abstract class BaseImportService implements ImportInterface { + @Autowired + CenterProperties centerProperties; + + @Autowired + C8NodeService c8NodeService; + + /** + * 查找类及其父类的所有字段 + * @param clazz + * @return + */ + public final List getAllFields(Class clazz) { + List fields = Lists.newArrayList(); + while (clazz != null) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + clazz = clazz.getSuperclass(); + } + return fields; + } + + /** + * 获取C8Column注解有key属性的Field + * @param fields + * @return + */ + public final List getKeyFields(List fields){ + List keyFieldList = Lists.newArrayList(); + for (Field f : fields) { + C8Column annotation = f.getAnnotation(C8Column.class); + if (annotation == null) { + continue; + } + if(annotation.key()){ + keyFieldList.add(f); + } + } + log.debug("导入程序中,设置为key的列有:"+keyFieldList.toString()); + return keyFieldList; + } + + /** + * ref字段返回对应的值 + * @param colAnno + * @param value + * @return + */ + public final String getRefValue(C8Column colAnno, String value){ + if(StrUtil.isBlank(value)||"null".equals(value)){ + return ""; + } + String refBO = colAnno.refBO();//获取ref的NodeType + String refAttr = colAnno.refAttr(); + StringBuilder xml = new StringBuilder(); + xml.append(""); + xml.append(""); + try{ + + List list = c8NodeService.queryByXML(xml.toString()); + if(list.size()>0){ + return list.get(0); + } + }catch (Exception e){ + log.error(e.getLocalizedMessage()); + } + return ""; + } + + /** + * 获取实体类上的注解 + * @param clazz + * @return + * @throws Exception + */ + public final C8Entity getC8EntityAnnotation(Class clazz){ + Annotation[] declaredAnnotations = clazz.getDeclaredAnnotationsByType(C8Entity.class); + if(declaredAnnotations.length==0){ + log.error("实例类上未添加C8Entity注解:"+clazz.getClass()); + throw new RuntimeException("实例类上未添加C8Entity注解:"+clazz.getClass()); + } + return (C8Entity) declaredAnnotations[0]; + } + + /** + * xml中将变量赋值 + * @param xml 要转换的xml + * @param clazz class + * @param error 错误信息 + * @param obj 数据 + * @return xml + */ + public void getValueXmlObj(String xml,Class clazz,Object obj,StringBuilder error){ + String result = xml; + List variableNames = StringUtils.extractMessageByRegular(xml); + for(String vari : variableNames){ + String vari1 = null; + try { + Method method = clazz.getMethod("get" + StringUtils.firstUpperCase(vari)); + vari1 = Convert.toStr(method.invoke(obj)); + } catch (Exception e) { + error.append("配置的变量不存在:"+vari+"
"); + log.error("配置的变量不存在:"+vari,e); + } + vari1 = HtmlUtil.escape(vari1);//转义 + result = StringUtils.fillValueInMsg(result, vari, vari1); + } + error.append(result); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DealWithImportDataService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DealWithImportDataService.java new file mode 100644 index 0000000..f7e70c3 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DealWithImportDataService.java @@ -0,0 +1,361 @@ +package com.centricsoftware.enhancement.service.importAnnotation; + + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.em.C8ImportTypeEnum; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.ant.C8Column; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 该类主要是处理数据, + * 比如数据的校验、拼接xml、将ref、enum等数据转换成可导入的格式 + */ +@Service +@Slf4j +public abstract class DealWithImportDataService extends ImportCreateNodeService { + + @Autowired + EnumCache enumCache; + + /**2 + * 将一些ref、enum等数据进行转换 + * @param entity + * @param entityClazz + * @param error + * @return true:无错误 + * @throws Exception + */ + public final boolean transData(Object entity,Class entityClazz,StringBuilder error) throws Exception{ + boolean noError = true; + List allFields = getAllFields(entityClazz);//获取所有的字段 + for(Field f : allFields){ + C8Column annotation = f.getAnnotation(C8Column.class); + if (annotation == null) { + continue; + } + if(!changeEntity(entity, entityClazz,annotation,f,error)){ + noError= false; + } + } + return noError; + } + + /** + * 处理所有字段类型的转换 + * @param entity + * @param annotation + * @param field + * @param error 表示错误信息 + * @return true 表示该条数据无错误;false表示至少有一个错误 + * @throws Exception + */ + public final boolean changeEntity(Object entity, Class entityClazz, C8Column annotation, Field field, StringBuilder error) throws Exception{ + C8ImportTypeEnum type = annotation.type(); + field.setAccessible(true);//不设置的话不能访问private变量 + String oldValue = field.get(entity)+""; + if("null".equals(oldValue)){ + oldValue = ""; + } + if(checkeBlank(oldValue,annotation,error)){ + return false; + } + Object newValue = oldValue; + StringBuilder errorLine = new StringBuilder(); + switch (type){ + case REF: + newValue = changeRef(entity,entityClazz,oldValue+"", annotation,errorLine); + break; + case TIME: + newValue = changeTime(oldValue+"", annotation,errorLine); + break; + case ENUM: + newValue = changeEnum(oldValue, annotation,errorLine); + break; + case ENUM_DESC: + newValue = changeEnumDesc(oldValue, annotation,errorLine); + break; + case ENUM_DISPLAY: + newValue = changeEnumDisplay(oldValue, annotation,errorLine); + break; + case REFLIST: + newValue = changeReflist(oldValue, annotation,errorLine); + break; + case INTEGER: + newValue = changeNumber(oldValue, annotation,errorLine); + break; + case DOUBLE: + newValue = changeDouble(oldValue, annotation,errorLine); + break; + case BOOLEAN: + newValue = changeBoolean(oldValue, annotation,errorLine); + break; + case STRING: + newValue = changeString(oldValue, annotation,errorLine); + break; + } + String tips = annotation.tips(); + if(StrUtil.isNotBlank(errorLine.toString())&&StrUtil.isNotBlank(tips)){ + errorLine = new StringBuilder(); + getValueXmlObj(tips,entityClazz,entity,errorLine); + } + error.append(errorLine); + field.set(entity,newValue); + return StringUtils.isBlank(error.toString()); + } + + /** + * 转换并校验Stirng值 + * @param oldValue + * @param annotation + * @param error + * @return + */ + public String changeString(String oldValue, C8Column annotation, StringBuilder error){ + String newValue = oldValue; + String vaild = annotation.valid();//获取正则表达式 + if(!StringUtils.isBlank(vaild)){ + boolean isMatch = Pattern.matches(vaild, newValue); + if(!isMatch){ + error.append(" [Error]"+oldValue+":正则表达式("+vaild+")检验不通过;(字段:"+annotation.colName()+")
"); + log.error("[Error]"+oldValue+":正则表达式("+vaild+")检验不通过;(字段:"+annotation.colName()+")"); + } + } + if("upper".equals(annotation.stringCase())){ + newValue = newValue.toUpperCase(); + } + if("lower".equals(annotation.stringCase())){ + newValue = newValue.toLowerCase(); + } + return newValue; + } + + /** + * 转换并校验Double值 + * @param oldValue + * @param annotation + * @param error + * @return + */ + public double changeDouble(String oldValue, C8Column annotation, StringBuilder error){ + double newValue = 0.0; + if(!StringUtils.isNumeric(oldValue)){ + error.append(" [Error]"+oldValue+":该字段只能输入数字;(字段:"+annotation.colName()+")
"); + log.error(" [Error]"+oldValue+":该字段只能输入数字;(字段:"+annotation.colName()+")
"); + }else{ + newValue = StringUtils.toDouble(oldValue); + } + return newValue; + } + + /** + * 转换并校验Number值 + * @param oldValue + * @param annotation + * @param error + * @return + */ + public int changeNumber(String oldValue, C8Column annotation, StringBuilder error){ + int newValue = 0; + if(!StringUtils.isNumeric(oldValue)){ + error.append(" [Error]"+oldValue+":该字段只能输入整数;(字段:"+annotation.colName()+")
"); + log.error(" [Error]"+oldValue+":该字段只能输入整数;(字段:"+annotation.colName()+")
"); + }else{ + if(StringUtils.toDouble(oldValue).intValue()!= StringUtils.toInteger(oldValue)){ + error.append(" [Error]"+oldValue+":该字段只能输入整数,不能有小数点;(字段:"+annotation.colName()+")
"); + log.error(" [Error]"+oldValue+":该字段只能输入整数,不能有小数点;(字段:"+annotation.colName()+")
"); + }else{ + newValue = StringUtils.toInteger(oldValue); + } + } + return newValue; + } + + /** + * 转换并校验Boolean值 + * @param oldValue + * @param annotation + * @param error + * @return + */ + public String changeBoolean(String oldValue, C8Column annotation, StringBuilder error){ + String newValue = oldValue; + if("是".equals(oldValue)){ + newValue = "true"; + } + if("否".equals(oldValue)){ + newValue = "false"; + } + if(!"true".equals(newValue.toLowerCase())&&!"false".equals(newValue.toLowerCase())){ + error.append(" "+oldValue+":请规范填写Boolean值;(字段:"+annotation.colName()+")
"); + log.error(" "+oldValue+":请规范填写Boolean值;(字段:"+annotation.colName()+")
"); + } + return newValue.toLowerCase(); + } + + /** + * 转换并校验enumDesc + * @param oldValue + * @param annotation + * @param error + * @return + */ + public String changeEnumDesc(String oldValue, C8Column annotation, StringBuilder error){ + String prefix = annotation.enumPrefix(); + String newValue = enumCache.getFullNameByDesc(prefix,oldValue); + if(!StringUtils.isBlank(oldValue)){ + if((prefix+":").equals(newValue)||"".equals(newValue)){ + log.error(" "+oldValue+":该值(Enum)在系统中不存在;(字段:"+annotation.colName()+")
"); + error.append(""+oldValue+":该值(Enum)在系统中不存在;(字段:"+annotation.colName()+")
"); + } + } + return newValue; + } + + /** + * 转换并校验enumDisplay + * @param oldValue + * @param annotation + * @param error + * @return + */ + public String changeEnumDisplay(String oldValue, C8Column annotation, StringBuilder error){ + String prefix = annotation.enumPrefix(); + String newValue = enumCache.getFullnameByDisplay(prefix,oldValue); + if((prefix+":").equals(newValue)||"".equals(newValue)){ + log.error(" "+oldValue+":该值(Enum)在系统中不存在;(字段:"+annotation.colName()+")
"); + error.append(" "+oldValue+":该值(Enum)在系统中不存在;(字段:"+annotation.colName()+")
"); + } + return newValue; + } + + + /** + * 校验并转换enum值 + * @param oldValue + * @param annotation + * @param error + * @return + */ + public String changeEnum(String oldValue, C8Column annotation, StringBuilder error){ + if("null".equals(oldValue)){ + oldValue = ""; + } + String prefix = annotation.enumPrefix(); + String newValue = prefix+":"+oldValue; + return newValue; + } + + /** + * 校验并且转化ref + * @param ref + * @param annotation + * @param error + * @return + */ + public String changeRef(Object entity, Class entityClazz, String ref, C8Column annotation, StringBuilder error) throws Exception{ + String valid = annotation.valid(); + String validxml = centerProperties.getValue(valid); + if(StrUtil.isNotBlank(validxml)){ + String url = seachXml(entityClazz,entity,validxml, error); + log.debug(annotation.colName()+"的validxml:"+url); + if(!StringUtils.isBlank(url)){ + return url; + } + error.append(" "+ref+":该值(Ref)在系统中不存在;"+annotation.colName()+"(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
") ; + log.error(" "+ref+":该值(Ref)在系统中不存在;"+annotation.colName()+"(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
") ; + return ref; + } + String newValue = getRefValue(annotation,ref); + if(!StringUtils.isBlank(ref)){ + if(StringUtils.isBlank(newValue)){ + error.append(" "+ref+":该值(Ref)在系统中不存在;"+annotation.colName()+"(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
") ; + log.error(" "+ref+":该值(Ref)在系统中不存在;"+annotation.colName()+"(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
") ; + } + } + return newValue; + } + + + /** + * 校验并且转化reflist + * @param reflist + * @param annotation + * @return + */ + public String changeReflist(String reflist, C8Column annotation, StringBuilder error){ + StringBuilder xml = new StringBuilder(); + String splitStr = annotation.split();//获取分隔符 + String[] arr = reflist.split(splitStr); + for(String ref : arr){ + String newValue = getRefValue(annotation,ref); + if(!StringUtils.isBlank(ref)){ + if(StringUtils.isBlank(newValue)){ + error.append(" "+ref+":该值(Reflist)在系统中不存在;(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
"); + log.error(" "+ref+":该值(Reflist)在系统中不存在;(对象:"+annotation.refBO()+",字段:"+annotation.refAttr()+")
"); + }else{ + xml.append(""+newValue+""); + } + } + } + if(StringUtils.isBlank(error.toString())){ + return xml.toString(); + } + return error.toString(); + } + + /** + * 转换并校验Time + * @param value + * @param annotation + * @param error + * @return + */ + public String changeTime(String value, C8Column annotation, StringBuilder error){ + if(StrUtil.isBlank(value)||"null".equals(value)){ + return "0"; + } + String newValue = value; + String format = annotation.timeFormat(); + if("yyyy-MM-dd".equals(format)||"yyyy/MM/dd".equals(format)){ + if(newValue.length()>9){ + newValue = newValue.substring(0,10); + } + } + if(StringUtils.validDateFormat(newValue, format)){ + newValue = StringUtils.date2Long(newValue, format)+""; + }else{ + error.append(" "+value+":日期格式错误;请按照"+format+"格式填写(字段:"+annotation.colName()+")
"); + log.error(" "+value+":日期格式错误;请按照"+format+"格式填写(字段:"+annotation.colName()+")
"); + } + return newValue; + } + + /** + * 校验字段是否为空 + * @param value + * @param annotation + * @param error + * @return + */ + public boolean checkeBlank(String value, C8Column annotation, StringBuilder error){ + if(annotation.required()){ + if(StringUtils.isBlank(value)&&StrUtil.isBlank(annotation.valid())){ + error.append(" "+value+":字段不能为空;(字段:"+annotation.colName()+")
"); + log.error(" "+value+":字段不能为空;(字段:"+annotation.colName()+")
"); + return true; + } + } + return false; + } + + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DefaultImportService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DefaultImportService.java new file mode 100644 index 0000000..4496324 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/DefaultImportService.java @@ -0,0 +1,331 @@ +package com.centricsoftware.enhancement.service.importAnnotation; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.em.C8ImportTypeEnum; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.ant.C8Column; +import com.centricsoftware.enhancement.ant.C8Entity; +import com.centricsoftware.enhancement.component.RedisUtils; +import com.centricsoftware.enhancement.dto.AnnoImportLineError; +import com.centricsoftware.enhancement.util.ImportUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 该类是处理导入的入口类,主要处理xml的拼接、校验等 + */ +@Service +@Slf4j +public class DefaultImportService extends DealWithImportDataService { + + @Value("${cs.excel.redis.prefix:uplaod::prefix::}") + String redisUploadPrefix; + + @Autowired + RedisUtils redisUtils; + + /** + * 单条保存 + * @param obj + * @param entityClazz + * @param error + * @return + * @throws Exception + */ + @Override + public boolean process(Object obj, Class entityClazz, StringBuilder error) throws Exception { + ArrayList list = Lists.newArrayList(); + list.add(obj); + return process(list,entityClazz,error); + } + + /** + * 多条保存:错误信息入参为StringBuilder + * 执行导入的入口,负责调度各个方法 + * @param list + * @param entityClazz + * @param error + * @return + * @throws Exception + */ + @Override + public boolean process(List list, Class entityClazz, StringBuilder error) throws Exception{ + StringBuilder allXml = new StringBuilder(); + C8Entity entityAnno = getC8EntityAnnotation(entityClazz); + for(int i=0;i entityClazz, List errorList) { + StringBuilder error = new StringBuilder(); + StringBuilder allXml = new StringBuilder(); + C8Entity entityAnno = getC8EntityAnnotation(entityClazz); + for(int i=0;i entityClazz,C8Entity entityAnno){ + StringBuilder errorLine = new StringBuilder(); + StringBuilder xml = new StringBuilder(); + int line = i+1; + try { + //获取url,如果系统已经存在则返回匹配的值,如果不存在,则返回一个新建的URL,并且将创建的xml拼在xml变量里面 + boolean transResult = transData(entity,entityClazz,errorLine);//将一些ref、enum等数据进行转换 + String url = getNodeUrl(entity,entityClazz,xml,errorLine); + if(StrUtil.isBlank(url)){ + errorLine.append("获取不到要更新的记录:"+entity.toString()); + appendError(line,error,errorLine);//汇总错误信息 + return true; + } + xml.append(""); + boolean isContinue = continueWhenError(entityAnno,transResult);//当存在错误时,是否继续执行 + if(!isContinue){ + appendError(line,error,errorLine);//汇总错误信息 + return false; + } + Map pathMap = Maps.newHashMap();//保存有path的xml,无结尾 + xml.append(appendSaveXml(url,entity,entityClazz,pathMap,errorLine));//拼接一行数据的xml + xml.append(appendOtherChangeAttributesXml(entity,entityClazz,errorLine));//添加定制化的,可重写该方法 + xml.append(""); + xml.append(ImportUtil.appendPathXml(pathMap)); + appendValidateNode(entityAnno,url,xml);//拼接校验规则语句 + if(entityAnno.singleSave()){//逐条保存 + try { + c8NodeService.processNode(xml.toString()); + }catch (Exception e){ + log.error(e.getLocalizedMessage()); + appendTips(entity,entityClazz,entityAnno,error,line,e); + } + }else{//统一保存 + if(StringUtils.isBlank(errorLine.toString())){ + allXml.append(xml); + } + } + appendError(line,error,errorLine);//汇总错误信息 + } catch (Exception e) { + appendTips(entity,entityClazz,entityAnno,error,line,e); + log.error("第"+line+"行抛出异常:\"+e.getSuppressed()+\"
",e); + } + return true; + } + + /** + * 定制错误提示信息 + * @param entityAnno 注解 + * @param error 错误信息 + * @return 返回值 + */ + private void appendTips( Object data,Class clazz,C8Entity entityAnno,StringBuilder error,int lineNum,Exception e){ + String tips = entityAnno.tips(); + if(StrUtil.isNotBlank(tips)){ + getValueXmlObj(tips,clazz,data,error); + }else{ + error.append("第"+(lineNum+1)+"行抛出异常:"+e.getSuppressed()+"
"); + } + + } + + /** + * 拼接校验规则语句 + * @param entityAnno + * @param url + * @param xml + */ + public final void appendValidateNode(C8Entity entityAnno, String url, StringBuilder xml) { + boolean isValidate = entityAnno.validateNode(); + if(isValidate){ + xml.append(""); + } + } + + /** + * 如果需要在标签内添加语句,重写该方法 + * @param entity 一行数据的实体类 + * @param entityClazz 可通过这个对象获取所有的注解配置 + * @param error 存放错误的信息 + * @return + */ + public String appendOtherChangeAttributesXml(Object entity, Class entityClazz, StringBuilder error){ + return ""; + } + + /** + * 拼接字段保存的xml + * @param entity + * @param entityClazz + * @param error + * @return + * @throws IllegalAccessException + */ + public String appendSaveXml(String url,Object entity,Class entityClazz,Map pathMap,StringBuilder error) throws Exception { + StringBuilder xml = new StringBuilder(); + List allFields = getAllFields(entityClazz); + C8Entity entityAnno = getC8EntityAnnotation(entityClazz); + for(Field field : allFields){ + C8Column annotation = field.getAnnotation(C8Column.class); + field.setAccessible(true); + String value = field.get(entity)+"";//值 + if("null".equals(value)|| StrUtil.isBlank(value)){ + value = ""; + } + if (annotation == null) { + continue; + } + if(!checkContinue(entityAnno,annotation,value)){ + continue; + } + String otherXmlAttr = annotation.otherXmlAttr(); + String fieldXml = ""; + String id = annotation.attributeId();//字段ID + C8ImportTypeEnum type = annotation.type();//字段类型 + String path = annotation.path();//路径 + String typeStr = type.toString().toLowerCase(); + switch (type){ + case REFLIST: + fieldXml = ImportUtil.getChangeAttrListXml(id,typeStr,value,otherXmlAttr); + break; + case ENUM: + case ENUM_DESC: + case ENUM_DISPLAY: + fieldXml = ImportUtil.getChangeAttrXml(id,"enum",value,otherXmlAttr); + break; + default: + fieldXml = ImportUtil.getChangeAttrXml(id,typeStr,value,otherXmlAttr); + } + if(StringUtils.isBlank(path)){ + xml.append(fieldXml); + }else{ + ImportUtil.setPathXml(url,path,pathMap,fieldXml); + } + } + return xml.toString(); + } + + /** + * 当数据存在错误是否继续解析 + * @param entityAnno + * @param transResult + * @return + */ + public final boolean continueWhenError(C8Entity entityAnno, boolean transResult) throws Exception { + if(!transResult){ + return entityAnno.failContinue(); + } + return true; + } + + /** + * 汇总错误信息 + * @param lineNum + * @param error + * @param errorLine + */ + public final void appendError(int lineNum,StringBuilder error,StringBuilder errorLine){ + if(!StringUtils.isBlank(errorLine.toString())){ + error.append("
第" + lineNum + "行出错:
") + .append(errorLine); + } + } + + /** + * 判断是否执行更新 + * @param annotation + * @param value + * @return + */ + public boolean checkContinue( C8Entity entityAnno,C8Column annotation, String value){ + boolean isContinue = true; + boolean update = annotation.update();//是否需要更新 + boolean forceUpdate = annotation.forceUpdate();//是否强制更新 + if(!update) { + isContinue = false; + } + if(entityAnno.forceUpdate()){ + //如果类上强制更新为true,无视字段级别的配置 + if(!annotation.convertParentForceUpdate()){ + //字段上是否配置强制更新以字段级别的更新为准,如果没配置(false),则直接返回强制更新 + return isContinue; + } + } + if(!forceUpdate&& StrUtil.isBlank(value)){ + //值为空时,如果不启用强制更新,将不更新 + isContinue = false; + } + return isContinue; + } + + /** + *存入redis + * @param entityClassName + * @param xml + */ + private void sendRedis(String entityClassName,String xml){ + redisUtils.lRightPush(redisUploadPrefix+entityClassName,xml); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportCreateNodeService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportCreateNodeService.java new file mode 100644 index 0000000..ef5d559 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportCreateNodeService.java @@ -0,0 +1,193 @@ +package com.centricsoftware.enhancement.service.importAnnotation; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.em.C8ImportTypeEnum; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.ant.C8Column; +import com.centricsoftware.enhancement.ant.C8Entity; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +/** + * 该类主要是处理导入的数据是新增还是更新, + * 如果是新增的话,如何新增;更新的话,根据什么规则更新 + * {@link ImportCreateNodeService#searchUrl(List, C8Entity, Object, Class, StringBuilder)} 重写该方法可以修改查找规则} + * {@link ImportCreateNodeService#createUrl(Class entityClazz,Object, StringBuilder, C8Entity, StringBuilder)} 重写该方法可以修改创建规则} + */ +@Service +@Slf4j +public abstract class ImportCreateNodeService extends BaseImportService { + + @Autowired + C8NodeService c8NodeService; + + + /** + * 通过key查询并返回Nodeurl,如果C8不存在,则返回一个新的URL + * @param entity + * @param entityClazz + * @param xml + * @return + */ + public String getNodeUrl(Object entity, Class entityClazz, StringBuilder xml,StringBuilder error) throws Exception{ + List fields = getAllFields(entityClazz);//获取实体类所有的字段 + List keyFields = getKeyFields(fields);//获取key被设置为true的字段 + C8Entity entityAnno = getC8EntityAnnotation(entityClazz); + String url = searchUrl(keyFields,entityAnno,entity,entityClazz,error); + if(StringUtils.isBlank(url)){ + if(!entityAnno.create()){ + //如果配置为查不到就不创建,则返回 + return ""; + } + url = createUrl(entityClazz,entity,xml,entityAnno,error); + } + try { + final Field field = entityClazz.getDeclaredField("url"); + entityClazz.getMethod("set" + StringUtils.firstUpperCase("url"),field.getType()).invoke(entity,url); + } catch (Exception e) { + log.warn("未配置url变量,不进行url注入"); + } + return url; + } + + + /** + * 根据配置的key查询系统中是否存在,不存在则直接创建nodeurl + * 如果匹配的规则不同,可以重新改方法 + * @param keyFields 实体的字段 + * @param entityAnno 实体类上的注解 + * @param entity 实体类 + * @param entityClazz 实体类class + * @return + */ + public String searchUrl(List keyFields, C8Entity entityAnno, Object entity, Class entityClazz, StringBuilder error) throws Exception{ + String searchxml = entityAnno.searchXml(); + searchxml = centerProperties.getValue(searchxml); + if(StrUtil.isNotBlank(searchxml)){ + return seachXml( entityClazz,entity,searchxml, error); + } + String nodeType = entityAnno.nodeType(); + if(StrUtil.isBlank(nodeType)){ + return ""; + } + StringBuilder xml = new StringBuilder(); + xml.append(""); + for(Field field: keyFields){ + C8Column colAnno = field.getAnnotation(C8Column.class); + String attributeId = colAnno.attributeId();//字段ID + C8ImportTypeEnum type = colAnno.type();//字段类型 + String path = colAnno.path();//路径 + String fieldName = field.getName(); + fieldName = StringUtils.firstUpperCase(fieldName); + String value = String.valueOf(entityClazz.getMethod("get" + fieldName).invoke(entity)); + String valueXml = getSearchType(colAnno,value); + xml.append(""); + } + List list = c8NodeService.queryByXML(xml.toString()); + if(list.size()>0){ + return list.get(0); + }else{ + return ""; + } + } + + /** + * 如果配置了searchXml,则解析、赋值并返回xml + * @param entityClazz + * @param entity + * @param xml + * @param error + * @return + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public String seachXml(Class entityClazz,Object entity,String xml,StringBuilder error) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + String newXml = xml; + List params = StringUtils.extractMessageByRegular(xml); + for(String param:params){ + Method method = entityClazz.getMethod("get" + StringUtils.firstUpperCase(param)); + if(method==null){ + continue; + } + String value = String.valueOf(method.invoke(entity)); + newXml = StringUtils.fillValueInMsg(newXml,param,value); + } + List strings = c8NodeService.queryByXML(newXml); + if(strings.size()==0){ + return ""; + }else{ + return strings.get(0); + } + } + + public String createXml(Class entityClazz,Object entity,String createXml,StringBuilder xml) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + String newXml = createXml; + String url = null; + url = c8NodeService.createURL(); + List params = StringUtils.extractMessageByRegular(newXml); + for(String param:params){ + if("url".equals(param)){ + //如果包含url变量,则创建一个url进行替换 + newXml = StringUtils.fillValueInMsg(newXml,param,url); + continue; + } + Method method = method = entityClazz.getMethod("get" + StringUtils.firstUpperCase(param)); + String value = String.valueOf(method.invoke(entity)); + newXml = StringUtils.fillValueInMsg(newXml,param,value); + } + xml.append(newXml); + return url; + } + + + /** + * 创建Url,如果创建Url的方式不同,可以重写该方法,将拼接好的xml放入xml中即可 + * 并且返回创建的Url + * @param entity + * @param xml + * @return + */ + public String createUrl(Class entityClazz, Object entity, StringBuilder xml, C8Entity entityAnno , StringBuilder error) throws Exception{ + String createxml = entityAnno.createXml(); + createxml = centerProperties.getValue(createxml); + String url = c8NodeService.createURL(); +// entityClazz.getMethod("set" + StringUtils.firstUpperCase("url")).invoke(entity); + if(StrUtil.isNotBlank(createxml)){ + return createXml( entityClazz,entity,createxml, xml); + } + String nodeType = entityAnno.nodeType(); + xml.append(""); + return url; + } + + + /** + * 获取查询SValue、RValue,SValue部分 + * @param colAnno + * @param value + * @return + */ + public final String getSearchType(C8Column colAnno, String value){ + C8ImportTypeEnum type = colAnno.type();//字段类型 + String result = ""; + switch (type) { + case INTEGER: + result = "DValue='"+ value+"'"; + break; + case REF: + result = "RValue='"+value+"'"; + break; + default: + result = "SValue='"+ value+"'"; + } + return result; + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportInterface.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportInterface.java new file mode 100644 index 0000000..41256c7 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importAnnotation/ImportInterface.java @@ -0,0 +1,15 @@ +package com.centricsoftware.enhancement.service.importAnnotation; + + +import com.centricsoftware.enhancement.dto.AnnoImportLineError; + +import java.util.List; + +public interface ImportInterface { + //处理Excel导入总入口 + boolean process(List list, Class entityClazz, StringBuilder error) throws Exception; + + boolean process(Object obj, Class entityClazz, StringBuilder error) throws Exception; + + boolean process(List obj, Class entityClazz, List error) throws Exception; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/BaseImportPropertiesService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/BaseImportPropertiesService.java new file mode 100644 index 0000000..9372dc3 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/BaseImportPropertiesService.java @@ -0,0 +1,121 @@ +package com.centricsoftware.enhancement.service.importproperties; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HtmlUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.config.entity.CenterProperties; +import com.centricsoftware.enhancement.ant.C8EntityConfig; +import com.centricsoftware.enhancement.modules.c8.component.EnumCache; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 该类主要是一些关于导入的通用工具类 + */ +@Service +@Slf4j +public abstract class BaseImportPropertiesService{ + @Autowired + CenterProperties centerProperties; + + @Autowired + C8NodeService c8NodeService; + + @Autowired + EnumCache enumCache; + + public String getNodeUrl(Map map, C8EntityConfig config, StringBuilder xml, StringBuilder error) throws Exception{ + String url = searchUrl(map,config,error); + if(!config.isCreate()&& StrUtil.isBlank(url)){ + if(StrUtil.isBlank(config.getTips())){ + error.append("导入的数据有误,匹配不到相应的记录"); + }else{ + error.append(getValueXml(config.getTips(),map,error)); + } + return ""; + } + if(StrUtil.isBlank(error)&& StrUtil.isBlank(url)){ + url = c8NodeService.createURL(); + map.put("url",url); + createUrl(map,config,xml,error); + } + return url; + } + + /** + * 拼接创建的xml + * @param map + * @param config + * @param error + * @return + */ + private String createUrl(Map map, C8EntityConfig config, StringBuilder xml, StringBuilder error){ + String createXml = config.getCreateXml(); + if(StrUtil.isEmpty(createXml)){ + StringBuilder line = new StringBuilder(); + line.append(""); + line.append(""); + return line.toString(); + }else{ + createXml = getValueXml(createXml,map,error); + xml.insert(0,createXml); + return createXml; + } + } + + /** + * 查询数据 + * @param map + * @param config + * @param error + * @return + */ + private String searchUrl(Map map, C8EntityConfig config,StringBuilder error){ + String searchXml = config.getSearchXml(); + if(StrUtil.isBlank(searchXml)){ + return ""; + } + searchXml = getValueXml(searchXml,map,error); + List strings; + try { + strings = c8NodeService.queryByXML(searchXml); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + if(strings.size()>0){ + return strings.get(0); + } + return ""; + } + + + /** + * xml中将变量赋值 + * @param xml + * @param map + * @param error + * @return + */ + public String getValueXml(String xml,Map map,StringBuilder error){ + String result = xml; + List variableNames = StringUtils.extractMessageByRegular(xml); + for(String vari : variableNames){ + if(!map.containsKey(vari)){ + error.append("[Error]:配置的变量不存在:"+vari+"
"); + return "error"; + } + String vari1 = Convert.toStr(map.get(vari)); + vari1 = HtmlUtil.escape(vari1);//转义 + result = StringUtils.fillValueInMsg(result, vari, vari1); + } + return result; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/ImportPropertiesService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/ImportPropertiesService.java new file mode 100644 index 0000000..92466e3 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/ImportPropertiesService.java @@ -0,0 +1,235 @@ +package com.centricsoftware.enhancement.service.importproperties; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.enhancement.ant.C8EntityConfig; +import com.centricsoftware.enhancement.component.RedisUtils; +import com.centricsoftware.enhancement.dto.excel.ExcelAroundAOP; +import com.centricsoftware.enhancement.service.importproperties.plugin.ItemImportInterface; +import com.centricsoftware.enhancement.util.ImportUtil; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class ImportPropertiesService extends TransDataAndAppendXmlService { + + @Autowired + RedisUtils redisUtils; + + @Value("${cs.excel.redis.prefix:uplaod::prefix::}") + String redisUploadPrefix; + + /** + * readAll中的Map,key为C8的属性ID + * @param config + * @param readAll + * @param error + */ + public boolean process(String importName, C8EntityConfig config, List> readAll, StringBuilder error) throws Exception{ + StringBuilder allXml = new StringBuilder(); + for(int i =0;i map = readAll.get(i); + try { + //前置通知,如果前置通过校验不通过,则根据isFailContinue,判断是break还是continue + if(!doItemBeforeAOP(map,config,errorLine)){ + //校验不通过 + if(config.isFailContinue()) continue; + break; + } + if(StrUtil.isNotBlank(errorLine)) { + if(config.isFailContinue()) continue; + break; + } + boolean transResult = transData(map,config,errorLine);//将一些ref、enum等数据进行转换 + boolean isContinue = continueWhenError(config,transResult);//当存在错误时,是否继续执行 + if(!isContinue){ + appendError(i+1,error,errorLine);//汇总错误信息 + break; + }else if(!transResult){ + //校验失败,本行不执行保存操作 + appendError(i+1,error,errorLine);//汇总错误信息 + continue; + } + String url = getNodeUrl(map,config,xml,errorLine); + if(StrUtil.isBlank(url)){ + appendError(i+1,error,errorLine);//汇总错误信息 + continue; + } + map.put("url",url); + Map pathMap = Maps.newHashMap();//保存有path的xml,无结尾 + xml.append(""); + xml.append(appendSaveXml(url,map,config,pathMap,errorLine));// 拼接一行数据的xml + xml.append(appendOtherChangeAttributesXml(url,map,pathMap,config,errorLine));//添加定制化的,可重写该方法 + xml.append(""); + xml.append(ImportUtil.appendPathXml(pathMap)); + appendValidateNode(config,url,xml);//拼接校验规则语句 + if(doItemAfterAOP(url,map,config,xml,errorLine)){ + if(config.isSingleSave()){//逐条保存 + try { + c8NodeService.processNode(xml.toString()); + }catch (Exception e){ + errorLine.append("第"+(i+1)+"行保存失败:"+e.getLocalizedMessage()); + log.error(e.getLocalizedMessage()); + } + }else{//统一保存 + if(StrUtil.isBlank(errorLine.toString())){ + allXml.append(xml); + } + } + } + appendError(i+1,error,errorLine);//汇总错误信息 + } catch (Exception e) { + error.append("第"+(i+1)+"行抛出异常:"+e.getSuppressed()+"
"); + log.error("第"+(i+1)+"行抛出异常:\"+e.getSuppressed()+\"
",e); + } + } + if(StrUtil.isNotBlank(error.toString())){ + if(!config.isFailSave()){ //当失败的时候不保存 + allXml.delete( 0, allXml.length()); + return false; + } + } + if(StrUtil.isNotBlank(allXml.toString())){ + log.debug("执行的xml:"+allXml.toString()); + if(config.isRedis()){ + redisUtils.lRightPush(redisUploadPrefix+importName,allXml.toString()); + }else{ + c8NodeService.processNodeMin(allXml.toString()); + } + } + return true; + } + + /** + * 存在错误是否继续 + * @param config 配置类 + * @param transResult 转换是否成功 + * @return + * @throws Exception + */ + public boolean continueWhenError(C8EntityConfig config, boolean transResult) throws Exception { + if(!transResult){ + return config.isFailContinue(); + } + return true; + } + + /** + * 汇总错误信息 + * @param lineNum + * @param error + * @param errorLine + */ + public final void appendError(int lineNum,StringBuilder error,StringBuilder errorLine){ + if(StrUtil.isNotBlank(errorLine.toString())){ + error.append("
第" + lineNum + "行出错:
") + .append(errorLine); + } + } + + + /** + * 如果需要在标签内添加语句,重写该方法 + * @param map 一行数据的实体类 + * @param config 可通过这个对象获取所有的注解配置 + * @param error 存放错误的信息 + * @return + */ + public String appendOtherChangeAttributesXml(String url,Map map, Map pathMap,C8EntityConfig config, StringBuilder error){ + StringBuilder result = new StringBuilder(); + String[] otherChangeXml = config.getOtherChangeXml(); + if(otherChangeXml==null){ + return ""; + } + for(String xml : otherChangeXml){ + if(xml.contains("=>")){ + //带有path的 + String[] pathxml = xml.split("=>"); + String path = StrUtil.trim(pathxml[0]);//获取path + String changeXml = getValueXml(pathxml[1], map, error);//xml中变量赋值 + ImportUtil.setPathXml(url,path,pathMap,changeXml); + }else{ + result.append(getValueXml(xml, map, error)); + } + } + return result.toString(); + } + + /** + * 拼接校验规则语句 + * @param config + * @param url + * @param xml + */ + public final void appendValidateNode(C8EntityConfig config, String url, StringBuilder xml) { + boolean isValidate = config.isValidateNode(); + if(isValidate){ + xml.append(""); + } + } + + /** + * 行保存前置通知;在获取行URL之前执行 + * @param map 行数据 + * @param config 配置项目 + * @param lineError 行错误信息 + * @return 是否校验通过 + */ + private boolean doItemBeforeAOP(Map map,C8EntityConfig config,StringBuilder lineError){ + String aroundBean = config.getItemImportAroundAOPBeanName(); + if(StrUtil.isNotBlank(aroundBean)){ + ItemImportInterface bean = SpringContextHolder.getBean(aroundBean); + ExcelAroundAOP before = bean.before(map, config); + if(before==null){ + return true; + } + if(!before.isSuccess()){ + //失败的话 + lineError.append(before.getError()); + return false; + } + //如果执行成功 + Map itemMap = before.getItemMap(); + if(itemMap!=null&&!itemMap.isEmpty()){ + map.putAll(itemMap); + } + } + return true; + } + + /** + * 行保存前置通知;在获取行URL之前执行 + * @param map 行数据 + * @param config 配置项目 + * @param lineError 行错误信息 + * @return 是否校验通过 + */ + private boolean doItemAfterAOP(String url, Map map,C8EntityConfig config,StringBuilder operation,StringBuilder lineError){ + String aroundBean = config.getItemImportAroundAOPBeanName(); + if(StrUtil.isNotBlank(aroundBean)){ + ItemImportInterface bean = SpringContextHolder.getBean(aroundBean); + ExcelAroundAOP after = bean.after(url, map,config); + if(after==null){ + return true; + } + if(!after.isSuccess()){ + //失败的话 + lineError.append(after.getError()); + return false; + } + operation.append(after.getOperation()); + } + return true; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/TransDataAndAppendXmlService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/TransDataAndAppendXmlService.java new file mode 100644 index 0000000..09db10b --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/TransDataAndAppendXmlService.java @@ -0,0 +1,532 @@ +package com.centricsoftware.enhancement.service.importproperties; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.ant.C8ColumnConfig; +import com.centricsoftware.enhancement.ant.C8EntityConfig; +import com.centricsoftware.enhancement.util.ImportUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +@Slf4j +@Service +public class TransDataAndAppendXmlService extends BaseImportPropertiesService { + + /** + * 将导入的enum、ref等数据转换成可导入的数据 + * + * @param map + * @param config + * @param errorLine + * @return + * @throws Exception + */ + public boolean transData(Map map, C8EntityConfig config, StringBuilder errorLine) throws Exception { + boolean noError = true; + List columns = config.getColumns(); + for (C8ColumnConfig col : columns) { + if (!changeEntity(map, config, col, errorLine)) { + noError = false; + } + } + return noError; + } + + public final boolean changeEntity(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) throws Exception { + String type = col.getType(); + String key = col.getId(); + String oldValue = Convert.toStr(map.get(key)); + if (checkeBlank(oldValue, col, error)) { + initValue(map,col); + if(StrUtil.isNotBlank(error)){ + return false; + }else{ + return true; + } + } + String newValue = oldValue; + String typeStr = type.toLowerCase(); + switch (typeStr) { + case "ref": + newValue = changeRef(map, config, col, error); + break; + case "time": + newValue = changeTime(map, config, col, error); + break; + case "enum": + newValue = changeEnum(map, config, col, error); + newValue = validEnum(newValue, col, map, error); + break; + case "enum_display": + case "enum_desc": + newValue = changeEnumDesc(map, config, col, error); + newValue = validEnum(newValue, col, map, error); + break; +// case "enum_display": +// newValue = changeEnumDisplay(map,config,col,error); +// validEnum(newValue,col,map,error); +// break; + case "enumlist": + newValue = changeEnumList(map, config, col, error); + break; + case "reflist": + newValue = changeReflist(map, config, col, error); + break; + case "integer": + newValue = changeNumber(map, config, col, error); + break; + case "double": + newValue = changeDouble(map, config, col, error); + break; + case "boolean": + newValue = changeBoolean(map, config, col, error); + break; + case "string": + newValue = changeString(map, config, col, error); + break; + } + if ("null".equals(newValue)) { + newValue = ""; + } + map.put(col.getId(), newValue); + // map.put(key+" old",oldValue); + return StrUtil.isBlank(error.toString()); + } + + private void initValue(Map map, C8ColumnConfig col) { + //如果导入空值,针对不同字段类型设置初始值;否则在forceUpdate模式下会报错 + if ("enum".equals(col.getType()) || "enum_display".equals(col.getType()) || "enum_desc".equals(col.getType())) { + map.put(col.getId(), col.getEnumPrefix() + ":"); + } else if ("ref".equals(col.getType())) { + map.put(col.getId(), "centric:"); + }else if ("integer".equals(col.getType())||"double".equals(col.getType())||"time".equals(col.getType())) { + map.put(col.getId(), "0"); + }else if ("boolean".equals(col.getType())) { + map.put(col.getId(), "false"); + } + } + + + public String changeRef(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String value = Convert.toStr(map.get(col.getId())); + return singleRef(value, map, config, col, error); + } + + public String singleRef(String value, Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + map.put("singleValue", value); + String validxml = col.getValid(); + if (StrUtil.isNotBlank(validxml)) { + String xml = getValueXml(validxml, map, error); + List result = c8NodeService.queryByXML(xml); + boolean validResultRequired = col.isValidResultRequired(); + if (validResultRequired && result.size() > 0) { + //必须有返回值 + if (StrUtil.isNotBlank(col.getValidAttr())) { + String s = null; + try { + s = c8NodeService.queryExpressionResult(col.getValidAttr(), result.get(0)); + } catch (Exception e) { + e.printStackTrace(); + return "centric:"; + } + return s; + } + return result.get(0); + } else if (!validResultRequired && result.size() == 0) { + //必须没有返回值 + return "centric:"; + } + } else if (StrUtil.isNotBlank(col.getRefBO())) { + String xml = ""; + if (StrUtil.isNotBlank(col.getRefAttr())) { + xml += ""; + } else { + xml += ""; + } + List result = c8NodeService.queryByXML(xml); + boolean validResultRequired = col.isValidResultRequired(); + if (validResultRequired && result.size() > 0) { + //必须有返回值 + if (StrUtil.isNotBlank(col.getValidAttr())) { + String s = null; + try { + s = c8NodeService.queryExpressionResult(col.getValidAttr(), result.get(0)); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + return s; + } + return result.get(0); + } else if (!validResultRequired && result.size() == 0) { + //必须没有返回值 + return ""; + } + } + //报错 + String tips = col.getTips(); + if (StrUtil.isNotEmpty(tips)) { + tips = getValueXml(tips, map, error); + log.error("[Error]:" + col.getColName() + ":" + tips + "。"); + error.append("[Error]:" + col.getColName() + ":" + tips + "
"); + } else { + log.error("[Error]:" + col.getColName() + ":该Ref值在系统中不存在:"); + error.append(" [Error]:" + col.getColName() + ":该Ref值在系统中不存在
"); + } + return value; + } + + public String changeTime(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String value = Convert.toStr(map.get(col.getId())); + if (StrUtil.isBlank(value) || "null".equals(value)) { + return "0"; + } + String format = col.getTimeFormat(); + String newValue = value; + if ("yyyy-MM-dd".equals(format) || "yyyy/MM/dd".equals(format)) { + if (newValue.length() > 9) { + newValue = newValue.substring(0, 10); + } + } + if (StringUtils.validDateFormat(newValue, format)) { + newValue = StringUtils.date2Long(newValue, format) + ""; + } else { + newValue = newValue.replaceAll("/", "-"); + if (StringUtils.validDateFormat(newValue, format)) { + newValue = StringUtils.date2Long(newValue, format) + ""; + } else { + error.append(" [Error]:" + value + ":日期格式错误;请按照" + format + "格式填写(字段:" + col.getColName() + ")
"); + } + } + return newValue; + } + + /** + * enum值校验 + * + * @param newValue + * @param col + * @param map + * @param error + * @return + */ + public String validEnum(String newValue, C8ColumnConfig col, Map map, StringBuilder error) { + String validXml = col.getValid(); + if (StringUtils.isBlank(validXml)) { + return newValue; + } + //非必填并且值为空 + if (!col.isRequired() && (col.getEnumPrefix() + ":").equals(newValue)) { + return newValue; + } + StringBuilder xml = new StringBuilder(); + xml.append(""); + if (!col.isAddAttribute()) { + xml.append(""); + } + xml.append(""); + xml.append(validXml); + String valueXml = getValueXml(xml.toString(), map, error); + List strings = c8NodeService.queryByXML(valueXml); + if (strings.size() == 0) { + if (col.isAddAttribute()) { + apendErrorLog("enum自动带值失败", valueXml, col, map, error);//错误拼接 + return ""; + } else { + apendErrorLog("enum校验规则不通过", newValue, col, map, error);//错误拼接 + return ""; + } + } else { + String url = strings.get(0); + if (col.isAddAttribute()) { + String exp = "DependsOn[0].Value"; + if (StrUtil.isNotBlank(col.getValidAttr())) { + exp = col.getValidAttr(); + } + String dependsOn = null; + try { + dependsOn = c8NodeService.queryExpressionResult(exp, url); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + if (StrUtil.isNotBlank(dependsOn)) { + return dependsOn; + } else { + apendErrorLog("enum自动带值失败", valueXml, col, map, error);//错误拼接 + return ""; + } + } else { + return newValue; + } + } + + } + + public String changeEnum(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + if ("null".equals(oldValue)) { + oldValue = ""; + } + String prefix = col.getEnumPrefix(); + String newValue = prefix + ":" + oldValue; + return newValue; + } + + public String changeEnumDesc(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + if ("null".equals(oldValue)) { + oldValue = ""; + } + if (col.isAddAttribute()) { + //额外添加的字段不校验空值 + return oldValue; + } + String prefix = col.getEnumPrefix(); +// String newValue = EnumUtil.getFullNameByDesc(prefix,oldValue); + String newValue = enumCache.getFullNameByDesc(prefix, oldValue); + if ((prefix + ":").equals(newValue) || "".equals(newValue)) { + apendErrorLog(oldValue, col, map, error);//错误拼接 + } + return newValue; + } + + public String changeEnumList(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + StringBuilder newValue = new StringBuilder(); + if (StrUtil.isBlank(oldValue)) { + apendErrorLog(oldValue, col, map, error);//错误拼接 + return ""; + } + oldValue = oldValue.replaceAll(",", ","); + for (String em : oldValue.split(",")) { + String prefix = col.getEnumPrefix(); + String v = enumCache.getFullNameByDesc(prefix, em); + if ((prefix + ":").equals(v) || "".equals(v)) { + apendErrorLog(em, col, map, error);//错误拼接 + } else { + newValue.append("").append(v).append(""); + } + } + return newValue.toString(); + } + + + private void apendErrorLog(String oldValue, C8ColumnConfig col, Map map, StringBuilder error) { + apendErrorLog("Enum值在系统中不存在", oldValue, col, map, error); + } + + private void apendErrorLog(String msg, String oldValue, C8ColumnConfig col, Map map, StringBuilder error) { + String tips = col.getTips(); + String prefix = col.getEnumPrefix(); + if (StringUtils.isNotEmpty(tips)) { + tips = getValueXml(tips, map, error); + log.error("[Error]:" + col.getColName() + ":" + tips + "。" + prefix + ":" + oldValue); + error.append(" [Error]:" + oldValue + ":" + tips + "。(字段:" + col.getColName() + ")
"); + } else { + log.error("[Error]:" + col.getColName() + ":" + msg + ":" + prefix + ":" + oldValue); + error.append(" [Error]:" + oldValue + ":" + msg + ";(字段:" + col.getColName() + ")
"); + } + } + + public String changeReflist(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + StringBuilder xml = new StringBuilder(); + String splitStr = col.getSplit();//获取分隔符 + String reflist = Convert.toStr(map.get(col.getId())); + String[] arr = reflist.split(splitStr); + for (String ref : arr) { + String refValue = singleRef(ref, map, config, col, error); + if (!StringUtils.isBlank(refValue)) { + xml.append("" + refValue + ""); + } + } + return xml.toString(); + } + + public String changeNumber(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + String newValue = oldValue; + if (!StringUtils.isNumeric(oldValue)) { + error.append(" [Error]:" + oldValue + ":该字段只能输入整数;(字段:" + col.getColName() + ")
"); + } else { + if (StringUtils.toDouble(oldValue).intValue() != StringUtils.toInteger(oldValue)) { + error.append(" [Error]:" + oldValue + ":该字段只能输入整数,不能有小数点;(字段:" + col.getColName() + ")
"); + } else { + newValue = StringUtils.toInteger(oldValue) + ""; + } + } + return newValue; + } + + /** + * 如果最后带有%号,则去掉%,并且除以100 + * + * @param map + * @param config + * @param col + * @param error + * @return + */ + public String changeDouble(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + String newValue = oldValue; + if (oldValue.endsWith("%")) { + newValue = StrUtil.sub(newValue, 0, newValue.length()); + } + if (!StringUtils.isNumeric(newValue)) { + error.append(" [Error]:" + oldValue + ":该字段只能输入整数;(字段:" + col.getColName() + ")
"); + } else { + double p = StringUtils.toDouble(newValue); + if (oldValue.endsWith("%")) { + p = p / 100; + } + newValue = p + ""; + } + return newValue; + } + + public String changeBoolean(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + String newValue = oldValue; + if ("否".equals(oldValue)) { + newValue = "false"; + } + if ("是".equals(oldValue)) { + newValue = "true"; + } + if (!"true".equals(newValue.toLowerCase()) && !"false".equals(newValue.toLowerCase())) { + error.append(" [Error]:" + oldValue + ":请规范填写Boolean值;(字段:" + col.getColName() + ")
"); + } + return newValue.toLowerCase(); + } + + public String changeString(Map map, C8EntityConfig config, C8ColumnConfig col, StringBuilder error) { + String oldValue = Convert.toStr(map.get(col.getId())); + String newValue = oldValue; + String vaild = col.getValid();//获取正则表达式 + if (!StringUtils.isBlank(vaild)) { + boolean isMatch = Pattern.matches(vaild, newValue); + if (!isMatch) { + String tips = col.getTips(); + tips = getValueXml(tips, map, error); + if (StrUtil.isNotEmpty(tips)) { + error.append(" [Error]:" + tips); + } else { + error.append(" [Error]:" + oldValue + ":正则表达式(" + vaild + ")检验不通过;(字段:" + col.getColName() + ")
"); + } + } + } + if ("upper".equals(col.getStringCase())) { + newValue = newValue.toUpperCase(); + } + if ("lower".equals(col.getStringCase())) { + newValue = newValue.toLowerCase(); + } + return newValue; + } + + + /** + * 验证是否为空 + * + * @param value + * @param col + * @param error + * @return + */ + public boolean checkeBlank(String value, C8ColumnConfig col, StringBuilder error) { + if (col.isAddAttribute()) { + //如果是配置中加的字段,则无需判断是否为空,他的值从valid中获取 + return false; + } + if (col.isRequired()) { + if (StringUtils.isBlank(value) || "null".equals(value)) { + error.append(" [Error]:" + value + ":字段不能为空;(字段:" + col.getColName() + ")
"); + return true; + } + } else { + return StringUtils.isBlank(value) || "null".equals(value); + } + return false; + } + + /** + * 拼接字段保存的xml + * + * @param url + * @param map + * @param config + * @param pathMap + * @param error + * @return + * @throws IllegalAccessException + */ + public String appendSaveXml(String url, Map map, C8EntityConfig config, Map pathMap, StringBuilder error) throws IllegalAccessException { + StringBuilder xml = new StringBuilder(); + List columns = config.getColumns(); + for (C8ColumnConfig col : columns) { + String value = Convert.toStr(map.get(col.getId())); + if ("null".equals(value) || StrUtil.isBlank(value)) { + value = ""; + } + if (!checkContinue(col, value)) { + continue; + } + String fieldXml = ""; + String id = col.getAttributeId();//字段ID + String type = col.getType();//字段类型 + String path = col.getPath();//路径 + String otherXmlAttr = col.getOtherXmlAttr(); + String typeStr = type.toLowerCase(); + switch (typeStr) { + case "reflist": + fieldXml = ImportUtil.getChangeAttrListXml(col, id, typeStr, value, otherXmlAttr); + break; + case "enum": + case "enum_desc": + case "enum_display": + fieldXml = ImportUtil.getChangeAttrXml(col, id, "enum", value, otherXmlAttr); + break; + case "enumlist": + fieldXml = ImportUtil.getChangeAttrEnumListXml(col, id, "enumlist", value, otherXmlAttr); + break; + default: + fieldXml = ImportUtil.getChangeAttrXml(col, id, typeStr, value, otherXmlAttr); + } + if (StringUtils.isBlank(path)) { + xml.append(fieldXml); + } else { + ImportUtil.setPathXml(url, path, pathMap, fieldXml); + } + } + return xml.toString(); + } + + /** + * 判断是否执行更新 + * + * @param col + * @param value + * @return + */ + public boolean checkContinue(C8ColumnConfig col, String value) { + boolean isContinue = true; + boolean update = col.isUpdate();//是否需要更新 + boolean forceUpdate = col.isForceUpdate();//是否强制更新 + if (!update) { + isContinue = false; + } + if (!forceUpdate && (StrUtil.isEmpty(value) || "null".equals(value))) { + //值为空时,如果不启用强制更新,将不更新 + isContinue = false; + } + return isContinue; + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/DoBeforeOrAfterImportInterface.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/DoBeforeOrAfterImportInterface.java new file mode 100644 index 0000000..6831afd --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/DoBeforeOrAfterImportInterface.java @@ -0,0 +1,13 @@ +package com.centricsoftware.enhancement.service.importproperties.plugin; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.Map; + +public interface DoBeforeOrAfterImportInterface { + Map before(MultipartFile file, String importName, String otherParam, ArrayList mails, StringBuilder error); + + String after(MultipartFile file, String importName, String otherParam, ArrayList mails, StringBuilder error); + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/ItemImportInterface.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/ItemImportInterface.java new file mode 100644 index 0000000..c104753 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/importproperties/plugin/ItemImportInterface.java @@ -0,0 +1,23 @@ +package com.centricsoftware.enhancement.service.importproperties.plugin; + +import com.centricsoftware.enhancement.ant.C8EntityConfig; +import com.centricsoftware.enhancement.dto.excel.ExcelAroundAOP; + +import java.util.Map; + +/** + * @author Xulin.Xie + */ +public interface ItemImportInterface { + /** + * 返回的Map,会把键值对存入Excel行,可供yml使用; + */ + ExcelAroundAOP before(Map map, C8EntityConfig config); + + /** + * 当行校验通过后,在每一行数据存入后面,追加operation + * @param url 行URL + * @param map 行数据:转换后的 + */ + ExcelAroundAOP after(String url, Map map, C8EntityConfig config); +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/AsyncLogService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/AsyncLogService.java new file mode 100644 index 0000000..dfb9117 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/AsyncLogService.java @@ -0,0 +1,73 @@ +package com.centricsoftware.enhancement.service.log; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.ant.ControllerLog; +import com.centricsoftware.commons.em.ResCode; +import com.centricsoftware.commons.exception.BaseException; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.commons.utils.SpringUtil; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.config.config.ExecutorConfig; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.modules.c8.service.es.LogElasticsearchService; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.mapper.LogMapper; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.service.log.impl.LogServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Async(ExecutorConfig.THREAD_POOL_NAME) +@Service +@Slf4j +public class AsyncLogService { + + @Resource + LogMapper logMapper; + + @Resource + C8NodeService c8NodeService; + + @Resource + private LogElasticsearchService logElasticsearchService; + + + public void saveLog(FeignLogDto feignLogDto) { + if (feignLogDto != null) { + String path = feignLogDto.getPath(); + if (StrUtil.isNotBlank(path) && path.contains("csi-requesthandler/RequestHandler")) { + return; + } + logMapper.insert(feignLogDto); + } + } + + + public void saveLog(PlmLog plmLog) { + saveLog(plmLog,null); + } + + /** + * 提供更直接的日志接口,-ES版本 + * @author liaochangjiang + * @since 2024-05-20 17:41 + */ + public void saveLog(PlmLog plmLog, ControllerLog controllerLog) { + if (plmLog == null) { + return; + } + logElasticsearchService.saveLog(plmLog); + } + + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/LogService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/LogService.java new file mode 100644 index 0000000..fbcb727 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/LogService.java @@ -0,0 +1,17 @@ +package com.centricsoftware.enhancement.service.log; + +import cn.hutool.core.date.TimeInterval; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import okhttp3.Request; +import okhttp3.Response; + +public interface LogService { + FeignLogDto setRequestData(Request originalRequest); + + void setResponseData(Response response, TimeInterval timer, FeignLogDto feignLogDto); + + void saveLog(FeignLogDto feignLogDto); + + Page page(int current, int size, FeignLogDto feignLogDto); +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/CallBackLogServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/CallBackLogServiceImpl.java new file mode 100644 index 0000000..b9ade6d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/CallBackLogServiceImpl.java @@ -0,0 +1,62 @@ +package com.centricsoftware.enhancement.service.log.impl; + +import cn.hutool.core.date.TimeInterval; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.dto.log.PlmLog; +import com.centricsoftware.enhancement.mapper.LogMapper; +import com.centricsoftware.enhancement.modules.c8.service.es.LogElasticsearchService; +import com.centricsoftware.enhancement.service.log.AsyncLogService; +import com.centricsoftware.enhancement.service.log.LogService; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Request; +import okhttp3.Response; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 基于es的日志存储 + */ +@Service +@Slf4j +public class CallBackLogServiceImpl implements LogService { + + @Resource + LogMapper logMapper; + @Resource + AsyncLogService asyncLogService; + + @Resource + private LogElasticsearchService logElasticsearchService; + + @Override + public FeignLogDto setRequestData(Request originalRequest) { + return null; + } + + @Override + public void setResponseData(Response response, TimeInterval timer, FeignLogDto feignLogDto) { + } + + @Override + public Page page(int current, int size, FeignLogDto feignLogDto) { + return null; + } + + public void saveLog(FeignLogDto feignLogDto) { + asyncLogService.saveLog(feignLogDto); + } + + /** + * 日志记录-ES版本,用户 + * @author liaochangjiang + * @since 2024-05-20 17:47 + */ + public void saveLog(PlmLog plmLog) { + if (plmLog != null) { + asyncLogService.saveLog(plmLog); + } + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/LogServiceImpl.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/LogServiceImpl.java new file mode 100644 index 0000000..28512da --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/log/impl/LogServiceImpl.java @@ -0,0 +1,283 @@ +package com.centricsoftware.enhancement.service.log.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.TimeInterval; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.json.*; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.dto.log.FeignLogDto; +import com.centricsoftware.enhancement.mapper.LogMapper; +import com.centricsoftware.enhancement.service.log.AsyncLogService; +import com.centricsoftware.enhancement.service.log.LogService; +import com.centricsoftware.enhancement.util.http.C8HttpUtil; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.Buffer; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.*; + + +/** + * 基于DB的日志存储 + */ +@Service +@Slf4j +public class LogServiceImpl implements LogService { + + @Resource + LogMapper logMapper; + @Resource + AsyncLogService asyncLogService; + + /** + * 请求信息封装 + */ + @Override + public FeignLogDto setRequestData(Request originalRequest) { + try { + String headers = originalRequest.headers().toString();//请求头 + String method = originalRequest.method();//请求方式 + Buffer buffer = new Buffer(); + RequestBody body = originalRequest.body(); + body.writeTo(buffer); + String requestMsg = buffer.readString(StandardCharsets.UTF_8); + /* + * 接口名称 + */ + String name = originalRequest.url().queryParameter("c8_name"); + /* + * 接口主键字段 + */ + String key = originalRequest.url().queryParameter("c8_key"); + /* + * 接口成功状态字段 + */ + String code = originalRequest.url().queryParameter("c8_code"); + HttpUrl newUrl = originalRequest.url().newBuilder() + .removeAllQueryParameters("c8_name") + .removeAllQueryParameters("c8_key") + .removeAllQueryParameters("c8_code") + .build(); + String host = originalRequest.url().host(); + String param = originalRequest.url().query(); + String path = originalRequest.url().uri().getPath(); + originalRequest = originalRequest.newBuilder().url(newUrl).build(); + String url = originalRequest.url().url().toString();//请求地址 + return FeignLogDto.builder() + .name(name) + .header(headers) + .method(method) + .host(host) + .path(path) + .dataKey(key) + .url(url) + .code(code) + .param(param) + .debug(requestMsg) + .startTime(new Date()) + .logType("接口日志") + .build(); + } catch (IOException e) { + log.error("设置日志请求信息失败。",e); + } + return null; + } + + /** + * 返回信息封装 + */ + @Override + public void setResponseData(Response response, TimeInterval timer, FeignLogDto feignLogDto) { + if (feignLogDto==null) return; + String responseMsg = C8HttpUtil.copyResponseBody(response); + long spendTime = timer.interval();//用时 + feignLogDto.setReturnMsg(responseMsg); + feignLogDto.setReturnCode(String.valueOf(response.code())); + feignLogDto.setCostMs(spendTime); + feignLogDto.setEndTime(new Date()); + } + + /** + * 保存日志 + */ + @Override + public void saveLog(FeignLogDto feignLogDto) { + String path = feignLogDto.getUrl(); + if (StrUtil.isNotBlank(path) && path.contains("csi-requesthandler/RequestHandler")) { + return; + } + try{ + setLogSuccess(feignLogDto); + setKey(feignLogDto); + asyncLogService.saveLog(feignLogDto); + }catch (Exception e){ + log.error("保存日志失败。",e); + printLog(feignLogDto); + } + } + + private void setKey(FeignLogDto feignLogDto) { + String requestStr = feignLogDto.getDebug(); + doSetKey(requestStr,feignLogDto); + if(StrUtil.isBlankOrUndefined(feignLogDto.getDataKey())){ + String param = feignLogDto.getParam(); + doSetKey(param,feignLogDto); + } + } + + private void doSetKey(String str,FeignLogDto feignLogDto) { + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + String field = feignLogDto.getRequestId(); + if(StrUtil.isBlankOrUndefined(field)){ + field = csProperties.getValue("common.log.feign.key-field"); + } + String[] split = field.split(","); + ArrayList fields = Lists.newArrayList(split); + for(String key : fields){ + String valueInJson = getValueInJsonArray(str, key); + if(!StrUtil.isBlankOrUndefined(valueInJson)){ + feignLogDto.setDataKey(valueInJson); + break; + } + } + } + + /** + * 设置接口响应状态;如果feign方法上配置了c8_code, + * 则按照c8_code配置的字段从响应报文中获取值,如果未配置,则取cs.plm.common.log.feign.success-code + * 再匹配cs.plm.common.log.feign.success-value配置的值,如果返回的code在success-value中,则接口成功 + */ + private void setLogSuccess(FeignLogDto feignLogDto) { + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + String field = feignLogDto.getResponseId(); + if(StrUtil.isBlankOrUndefined(field)){ + field = csProperties.getValue("common.log.feign.success-field"); + } + String value = csProperties.getValue("common.log.feign.success-value"); + String code = feignLogDto.getCode(); + if(!StrUtil.isBlankOrUndefined(code)){ + /* + * 如果配置了 + */ + field = code; + } + String[] split = value.split(","); + ArrayList values = Lists.newArrayList(split); + String responseStr = feignLogDto.getReturnMsg(); + String returnCode = feignLogDto.getReturnCode(); + String valueInJson = getValueInJson(responseStr, field); + if(StrUtil.isBlankOrUndefined(valueInJson)){ + if("200".equals(returnCode)){ + feignLogDto.setSuccess(true); + } + } + if(values.contains(valueInJson)){ + feignLogDto.setSuccess(true); + } + } + + private String getValueInJsonArray(String responseStr,String field){ + if(JSONUtil.isJsonObj(responseStr)){ + return getValueInJson(responseStr,field); + } + if(!JSONUtil.isJsonArray(responseStr)){ + return ""; + } + JSONArray arr = JSONUtil.parseArray(responseStr); + ArrayList urls = Lists.newArrayList(); + for(Object obj : arr){ + String valueInJson = getValueInJson(obj.toString(), field); + if(!StrUtil.isBlankOrUndefined(valueInJson)){ + urls.add(valueInJson); + } + } + return urls.toString(); + } + + private String getValueInJson(String responseStr,String field){ + if(!JSONUtil.isJsonObj(responseStr)){ + return ""; + } + JSONObject json = JSONUtil.parseObj(responseStr); + if(json.containsKey(field)) { + return json.getStr(field); + } + String byPath = (String) json.getByPath(field); + if(StrUtil.isBlankOrUndefined(byPath)){ + return ""; + } + return byPath; + } + + public void printLog(FeignLogDto feignLogDto){ + log.debug("接口日志打印:后续开发前端查询=====================开始=========================="); + try { + JSONConfig jsonConfig = JSONConfig.create().setIgnoreNullValue(false); + JSON parse = JSONUtil.parse(feignLogDto.getDebug(), jsonConfig); + log.debug("请求报文:"); + log.debug(JSONUtil.toJsonPrettyStr(parse)); + log.debug("响应报文:"); + JSON parseResponse = JSONUtil.parse(feignLogDto.getReturnMsg(), jsonConfig); + log.debug(JSONUtil.toJsonPrettyStr(parseResponse)); + log.debug("接口日志打印:后续开发前端查询=====================结束=========================="); + }catch (Exception e){ + log.error("打印日志失败,直接打印原始数据:{}",feignLogDto); + log.error("打印日志失败,异常如下:",e); + } + + } + + @Override + public Page page(int current, int size, FeignLogDto feignLogDto) { + Page page = new Page<>(); + page.setCurrent(current); + page.setSize(size); + QueryWrapper wrapper = getWrapper(feignLogDto); + wrapper.orderByDesc("id"); + return logMapper.selectPage(page, wrapper); + } + + private QueryWrapper getWrapper(FeignLogDto dto){ + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StrUtil.isAllNotBlank(dto.getName()),"name", dto.getName()); + wrapper.like(StrUtil.isAllNotBlank(dto.getDataKey()),"data_key", dto.getDataKey()); + if(dto.getSuccessStr() !=null){ + wrapper.eq("success", dto.getSuccessStr()); + } + wrapper.like(!StrUtil.isBlankOrUndefined(dto.getLogType()), "log_type", dto.getLogType()); + wrapper.like(!StrUtil.isBlankOrUndefined(dto.getParam()), "param", dto.getParam()); + wrapper.like(!StrUtil.isBlankOrUndefined(dto.getReturnMsg()), "response", dto.getReturnMsg()); + wrapper.ge(!StrUtil.isBlankOrUndefined(dto.getStartTimeBegin()) ,"send_date", DateUtil.parse(dto.getStartTimeBegin())); + wrapper.le(!StrUtil.isBlankOrUndefined(dto.getStartTimeEnd()), "send_date", DateUtil.parse(dto.getStartTimeEnd())); + return wrapper; + } + + /** + * 删除日志 + * @param day 删除day天前的数据 + */ + public int deleteLog(int day){ + //获取当前时间60天前,类型为DateTime + DateTime dateTime = DateUtil.offsetDay(new Date(), -day); + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.le("send_date", dateTime); + return logMapper.delete(wrapper); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/lui/FieldPermissionsService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/lui/FieldPermissionsService.java new file mode 100644 index 0000000..bfa187f --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/lui/FieldPermissionsService.java @@ -0,0 +1,116 @@ +package com.centricsoftware.enhancement.service.lui; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.centricsoftware.enhancement.service.bo.BOAttributesDCLService; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.util.C8Constant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * description:自动创建【字段权限】的LookupItemSubtype和字段 + * Date: 2023/3/16 15:38 + */ +@Slf4j +@Service +public class FieldPermissionsService { + + @Resource + C8NodeService c8NodeService; + + @Resource + BOAttributesDCLService boAttributesDCLService; + + + /** + * 创建【字段权限】类型 + * @return LookupItemSubtype URL + */ + public String createLookupItemSubtype(){ + String subtype = c8NodeService.queryFirstByNodeName("LookupItemSubtype", "字段权限"); + if(StrUtil.isNotBlank(subtype)){ + return subtype; + } + subtype = c8NodeService.createURL(); + String operation = C8OperationNode.createNode(subtype, "LookupItemSubtype") + .changeAttribute("Node Name", "ref", "字段权限") + .changeAttribute("Active", "boolean", "true") + .attach("centric://REFLECTION/INSTANCE/LookupItemConfig","Subtypes") + .getXml(); + c8NodeService.processNode(operation); + return subtype; + } + + /** + * 创建【字段权限】字段 + */ + public void createLookupItemFiled(){ + boAttributesDCLService.createFieldStr(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_AuditAttr","受控字段ID","string","设置需要添加权限的字段,值为BO下字段的ID","字段权限"); + boAttributesDCLService.createFieldStr(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_AuditBO","字段所属BO","string","填写BO名称,比如Material","字段权限"); + boAttributesDCLService.createFieldStr(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_RuleEXP1","条件设置","string","与Rule中Exp属性的写法一致,比如【attr('C8_Mat_Type') == 'C8_MatType:801'】","字段权限"); + boAttributesDCLService.createFieldStr(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_AuditEditRoles","可编辑角色","reflist","具备编辑权限的角色,和【条件设置】是【与】的关系","字段权限"); + boAttributesDCLService.createFieldStr(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_RuleEXP","角色权限设置","string","该字段是根据【可编辑角色】自动生成,【可编辑角色】只是用于快速生成【权限设置】","字段权限"); + boAttributesDCLService.createFieldBoolean(C8Constant.BOUrl.LOOKUP_ITEM,"C8_LI_Comment","记录修改日志","boolean","配置是否记录修改日志到Comment","字段权限"); + } + + public void createView(){ + String viewName = "字段权限控制"; + String viewUrl = c8NodeService.queryFirstByNodeName("_CS_PreferenceView", viewName); + if(StrUtil.isNotBlank(viewUrl)){ + return; + } + String luiName = "字段权限"; + String url = c8NodeService.createURL(); + String operation = "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\tNode Name:(LookupItem):0\n" + + "\t\t\tC8_LI_AuditAttr:(LookupItem):0\n" + + "\t\t\tC8_LI_AuditBO:(LookupItem):0\n" + + "\t\t\tC8_LI_RuleEXP1:(LookupItem):0\n" + + "\t\t\tC8_LI_AuditEditRoles:(LookupItem):0\n" + + "\t\t\tC8_LI_RuleEXP:(LookupItem):0\n" + + "\t\t\tC8_LI_Comment:(LookupItem):0\n" + + "\t\t\tActive:(LookupItem):0\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\t1\n" + + "\t\t\t2\n" + + "\t\t\t3\n" + + "\t\t\t4\n" + + "\t\t\t5\n" + + "\t\t\t6\n" + + "\t\t\t7\n" + + "\t\t\t8\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n" + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\t"+url+"\n" + + "\t\t\n" + + "\t\n" + + "\t\n" + + "\t\t\n" + + "\t\t\t"+url+"\n" + + "\t\t\n" + + "\t"; + operation+="\n" + + "\t\t\n" + + "\t\t\t"+url+"\n" + + "\t\t\n" + + "\t"; + c8NodeService.processNode(operation); + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/service/specification/SpecificationDataSheetService.java b/enhancement/src/main/java/com/centricsoftware/enhancement/service/specification/SpecificationDataSheetService.java new file mode 100644 index 0000000..446b82d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/service/specification/SpecificationDataSheetService.java @@ -0,0 +1,104 @@ +package com.centricsoftware.enhancement.service.specification; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.enhancement.modules.c8.component.dep.DepPathResult; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.enhancement.modules.c8.dto.dep.DepPath; +import com.centricsoftware.enhancement.modules.dml.dto.node.change.C8OperationNode; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * description:从工艺单种新建 + * Author: Xulin.Xie + * Date: 2022/7/6 15:25 + */ +@Slf4j +@Service +public class SpecificationDataSheetService { + + private final C8NodeService c8NodeService; + + + /** + * 自定义的【从工艺单中新建】功能,Section需要重置; + * 通过原Section名称,判断是否存在Section,不存在则新建 + */ + public String resetSection(String currentVersion,String[] urls){ + DepPathResult result = getData(urls); + //获取Section映射 + Map sectionMap = getSectionMap(currentVersion,result); + StringBuffer xml = new StringBuffer(); + for(String url : urls){ + String sectionName = result.getValue("Section.$Name", url).getStr(); + if(StrUtil.isBlank(sectionName)) sectionName = result.getValue("Section.Definition.$Name", url).getStr(); + if(sectionMap.containsKey(sectionName)){ + //存在Section,则直接重置 + xml.append(resetSection(url, sectionMap.get(sectionName))); + }else{ + //不存在Section,则新建Section + String sectionUrl = createSection(currentVersion,sectionName,xml); + sectionMap.put(sectionName,sectionUrl);//将section放入缓存 + xml.append(resetSection(url, sectionUrl));//重置Section + } + } + c8NodeService.processNode(xml.toString()); + return ""; + } + + /** + * 创建Section + */ + private String createSection(String currentVersion, String sectionName,StringBuffer xml) { + String section = c8NodeService.createURL(); + String createSection = "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + String changeName = C8OperationNode.changeNode(section).changeAttribute("Node Name", "string", sectionName).getXml(); + xml.append(createSection).append(changeName); + return section; + } + + /** + * 重置Section + */ + private String resetSection(String url, String section) { + return C8OperationNode.changeNode(url).changeAttribute("Section","ref",section).getXml(); + } + + /** + * 获取section name:url映射 + */ + private Map getSectionMap(String currentVersion, DepPathResult result) { + Map sectionMap = Maps.newHashMap(); + List urls = result.getValue("AllSections",currentVersion).getList(); + for(String url : urls){ + String sectionName = result.getValue("$Name", url).getStr(); + sectionMap.put(sectionName, url); + } + return sectionMap; + } + + /** + * 获取数据 + */ + private DepPathResult getData(String[] urls){ + DepPath depPath = DepPath.builder().addUrls(urls).addPath("Child:__Parent__/Child:AllSections") + .addPath("Child:Section/Child:Definition").build(); + return c8NodeService.depPathByUrl(depPath); + } + + public SpecificationDataSheetService(C8NodeService c8NodeService) { + this.c8NodeService = c8NodeService; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/C8Constant.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/C8Constant.java new file mode 100644 index 0000000..e865f1a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/C8Constant.java @@ -0,0 +1,12 @@ +package com.centricsoftware.enhancement.util; + +/** + * description: + * Date: 2023/3/17 10:34 + */ +public class C8Constant { + + public interface BOUrl { + String LOOKUP_ITEM = "centric://REFLECTION/BusinessObject/LookupItem"; + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/AnalysisCell.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/AnalysisCell.java new file mode 100644 index 0000000..9f13ea7 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/AnalysisCell.java @@ -0,0 +1,51 @@ +package com.centricsoftware.enhancement.util.ExportUtil; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 分析单元 + *

+ * 字符串:{{str:字段名|默认字段名}},其中str可省略;str|-4代表保留字符串后4位;|默认字段名可不填 + * 图片:{{img|行跨度,列跨度:字段名}}。 eg. {{img|1,5:imageUrl}} + *

+ * + * @author liaochangjiang + * @since 2022-02-09 08:44:16 + */ +@Data +@Accessors(chain = true) +public class AnalysisCell { + /** + * 行位置 + */ + private int row; + /** + * 列位置 + */ + private int col; + /** + * 填充类型 + */ + private ExportFieldTypeEnum type = ExportFieldTypeEnum.STRING; + /** + * 取值 + */ + private String field; + /** + * 默认取值(field对应的值为空时) + */ + private String defaultField; + /** + * 取值长度,结合StringUtils.substring使用 + */ + private int subLength = 0; + /** + * 行跨度 + */ + private int rowSpan; + /** + * 列跨度 + */ + private int colSpan; +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportFieldTypeEnum.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportFieldTypeEnum.java new file mode 100644 index 0000000..758f6c0 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportFieldTypeEnum.java @@ -0,0 +1,29 @@ +package com.centricsoftware.enhancement.util.ExportUtil; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 导出字段类型 + * @author liaochangjiang + * @since 2024-03-05 10:50 + */ +@Getter +@AllArgsConstructor +public enum ExportFieldTypeEnum { + + STRING("str"), + FIX("fix"), + IMAGE("img"); + + private String shortName; + + public static ExportFieldTypeEnum getByShortName(String shortName) { + for (ExportFieldTypeEnum data : values()) { + if (data.shortName.equals(shortName)) { + return data; + } + } + return null; + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportTemplateRegion.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportTemplateRegion.java new file mode 100644 index 0000000..18bf6c6 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportTemplateRegion.java @@ -0,0 +1,49 @@ +package com.centricsoftware.enhancement.util.ExportUtil; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 导出模板区域 + * @author liaochangjiang + * @since 2024-03-05 10:44 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Repeatable(ExportTemplateRegion.List.class) +public @interface ExportTemplateRegion { + /** + * 开始行 + */ + int row1() default 0; + + /** + * 开始列 + */ + int col1() default 0; + + /** + * 结束行 + */ + int row2() default 0; + + /** + * 结束列 + */ + int col2() default 0; + + /** + * 分组 + */ + String[] groups() default {}; + + @Target({TYPE}) + @Retention(RUNTIME) + @Documented + public @interface List { + ExportTemplateRegion[] value(); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportUtil.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportUtil.java new file mode 100644 index 0000000..fb436c5 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ExportUtil.java @@ -0,0 +1,498 @@ +package com.centricsoftware.enhancement.util.ExportUtil; + +import cn.hutool.json.JSONUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.centricsoftware.commons.utils.EncodeUtils; +import com.centricsoftware.commons.utils.ExportExcel; +import com.centricsoftware.commons.utils.StringUtils; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.Option; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.BiFunction; + +@Slf4j +@Component +public class ExportUtil { + + @Resource + C8NodeService c8NodeService; + + private final ThreadLocal templateRegion = new ThreadLocal<>(); + + private static final String FIELD_PREFIX = "{{"; + + private static int seqCol,seqRow; + + /** + * 设置导出响应相关信息 + * + * @author liaochangjiang + * @since 2022-05-27 14:42:30 + */ + public static void setExportResponseInfo(HttpServletResponse response, String fileName) { + response.reset(); + response.setContentType("application/octet-stream; charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + EncodeUtils.urlEncode(fileName)); + } + + /** + * 获取分析后的导出模板单元 + * + * @author liaochangjiang + * @since 2022-02-07 16:43:35 + */ + public List getAnalysisCells(ExportExcel excel) { + Sheet templateSheet = excel.getTemplateSheet(); + List list = new ArrayList<>(); + ExportTemplateRegion templateRegion = this.templateRegion.get(); + if (templateRegion == null) { + throw new RuntimeException("找不到导出模板区域配置"); + } + int row1 = templateRegion.row1(), row2 = templateRegion.row2(), col1 = templateRegion.col1(), col2 = templateRegion.col2(); + for (int i = row1; i <= row2; i++) { + for (int j = col1; j <= col2; j++) { + Cell cell = null; + try { + cell = templateSheet.getRow(i).getCell(j); + } catch (Exception e) { + log.error("获取模板单元出错, 行:{}, 列:{}", i, j); + } + if (cell != null && StringUtils.isNotBlank(cell.getStringCellValue())) { + final String cellVal = cell.getStringCellValue(); + AnalysisCell analysisCell = new AnalysisCell().setRow(i - row1).setCol(j - col1); + if (!cellVal.contains(FIELD_PREFIX)) { + // 固定值 + analysisCell.setField(cellVal).setType(ExportFieldTypeEnum.FIX); + list.add(analysisCell); + continue; + } + // 模板字段值 + String fieldName = getTemplateField(cellVal); + if (StringUtils.isNotBlank(fieldName)) { + String[] split = fieldName.split(":"); + if (split.length > 1) { + fieldName = split[1]; + String[] shortNames = split[0].split("\\|"); + analysisCell.setType(ExportFieldTypeEnum.getByShortName(shortNames[0])); + if (analysisCell.getType() == ExportFieldTypeEnum.STRING) { + if (shortNames.length > 1) { + analysisCell.setSubLength(Integer.parseInt(shortNames[1])); + } + } else if (analysisCell.getType() == ExportFieldTypeEnum.IMAGE) { + String[] rolCols = shortNames[1].split(","); + analysisCell.setRowSpan(Integer.parseInt(rolCols[0])) + .setColSpan(Integer.parseInt(rolCols[1])); + } + } + if (fieldName.contains("|")) { + String[] fieldNames = fieldName.split("\\|"); + analysisCell.setField(fieldNames[0]); + analysisCell.setDefaultField(fieldNames[1]); + } else { + analysisCell.setField(fieldName); + } + list.add(analysisCell); + } + } + } + } + // 清空模板配置 + for (int i = row1; i <= row2; i++) { + for (int j = col1; j <= col2; j++) { + Optional.ofNullable(excel.getSheet().getRow(i).getCell(j)).ifPresent(t -> t.setCellValue("")); + } + } + return list; + } + + /** + * 获取模板字段名 + * + * @author liaochangjiang + * @since 2022-02-07 16:45:01 + */ + public String getTemplateField(String cellValue) { + if (StringUtils.isNotBlank(cellValue) && cellValue.contains(FIELD_PREFIX)) { + return cellValue.substring(cellValue.indexOf(FIELD_PREFIX) + 2, cellValue.indexOf("}}")); + } + return null; + } + + /** + * 填充数据 + * + * @param excel 导出对象 + * @param analysisCells 分析后的单元字段 + * @param colIdx 当前系列起始列索引 + * @param curColIdx 当前系列内列索引 + * @param curRow 当前行索引 + * @param data 待填充数据 + * @author liaochangjiang + * @since 2022-02-07 16:45:12 + */ + public int fillData(ExportExcel excel, List analysisCells, int colIdx, int curColIdx, int curRow, + Object data) { + return fillData(excel, (XSSFSheet) excel.getSheet(), analysisCells, colIdx, curColIdx, curRow, data, null); + } + + /** + * 填充数据 + * + * @param excel 导出对象 + * @param analysisCells 分析后的单元字段 + * @param colIdx 当前系列起始列索引 + * @param curColIdx 当前系列内列索引 + * @param curRow 当前行索引 + * @param data 待填充数据 + * @param cellCopyPolicy cell复制策略 + * @author liaochangjiang + * @since 2022-09-16 16:19:52 + */ + public int fillData(ExportExcel excel, List analysisCells, int colIdx, int curColIdx, int curRow, + Object data, CellCopyPolicy cellCopyPolicy) { + return fillData(excel, (XSSFSheet) excel.getSheet(), analysisCells, colIdx, curColIdx, curRow, data, null, + cellCopyPolicy); + } + + /** + * 填充数据 + * + * @author liaochangjiang + * @since 2022-09-06 11:26:53 + */ + public int fillData(ExportExcel excel, List analysisCells, int colIdx, int curColIdx, int curRow, + Object data, BiFunction fieldValueTransfer) { + return fillData(excel, (XSSFSheet) excel.getSheet(), analysisCells, colIdx, curColIdx, curRow, data, + fieldValueTransfer); + } + + /** + * 填充数据 + * + * @author liaochangjiang + * @since 2022-09-16 16:20:11 + */ + public int fillData(ExportExcel excel, XSSFSheet sheet, List analysisCells, int colIdx, int curColIdx, + int curRow, Object data, BiFunction fieldValueTransfer) { + return fillData(excel, sheet, analysisCells, colIdx, curColIdx, curRow, data, fieldValueTransfer, + new CellCopyPolicy.Builder().cellValue(false).build()); + } + + /** + * 填充数据 + * + * @param excel 导出对象 + * @param analysisCells 分析后的单元字段 + * @param colIdx 当前系列起始列索引 + * @param curColIdx 当前系列内列索引 + * @param curRow 当前行索引 + * @param data 待填充数据 + * @param fieldValueTransfer 字段转换器 + * @param cellCopyPolicy cell复制策略 + * @author liaochangjiang + * @since 2022-04-27 16:45:12 + */ + public int fillData(ExportExcel excel, XSSFSheet sheet, List analysisCells, int colIdx, int curColIdx, + int curRow, Object data, BiFunction fieldValueTransfer, + CellCopyPolicy cellCopyPolicy) { + ExportTemplateRegion templateRegion = this.templateRegion.get(); + int templateColSpan = templateRegion.col2() - templateRegion.col1() + 1; + if (curColIdx >= templateColSpan) { + // 复制新列 + createNewCols(excel, sheet, colIdx + curColIdx); + } + if (curRow > sheet.getLastRowNum()) { + // 复制新行 + sheet.copyRows(templateRegion.row1(), templateRegion.row2(), curRow, cellCopyPolicy); + } + DocumentContext docCtx = JsonPath.using(Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS)).parse(JSONUtil.toJsonStr(data)); + for (AnalysisCell analysisCell : analysisCells) { + String fieldValue; + switch (analysisCell.getType()) { + case IMAGE: + fieldValue = getFieldValue(docCtx, analysisCell.getField()); + if (StringUtils.isNotBlank(fieldValue)) { + try (InputStream imageIs = c8NodeService.getFileFromDirectAddr(fieldValue)) { + excel.insertImageResize(0, imageIs, XSSFWorkbook.PICTURE_TYPE_PNG, 0, 0, 0, 0, + colIdx + curColIdx + analysisCell.getCol(), curRow + analysisCell.getRow(), + colIdx + curColIdx + analysisCell.getCol() + analysisCell.getColSpan(), + curRow + analysisCell.getRow() + analysisCell.getRowSpan(), -0.002); + } catch (IOException e) { + log.error("获取图片失败,路径:{}", fieldValue); + } + } + break; + case STRING: + fieldValue = getFieldValue(docCtx, analysisCell.getField()); + if (StringUtils.isBlank(fieldValue) && StringUtils.isNotBlank(analysisCell.getDefaultField())) { + fieldValue = getFieldValue(docCtx, analysisCell.getDefaultField()); + } + if (analysisCell.getSubLength() != 0) { + fieldValue = StringUtils.substring(fieldValue, analysisCell.getSubLength()); + } + if (fieldValueTransfer != null) { + fieldValue = fieldValueTransfer.apply(analysisCell, fieldValue); + } + fillCell(sheet, colIdx, curColIdx, curRow, analysisCell, fieldValue); + break; + default: + fieldValue = analysisCell.getField(); + fillCell(sheet, colIdx, curColIdx, curRow, analysisCell, fieldValue); + } + } + curColIdx += templateColSpan; + return curColIdx; + } + + /** + * 填充数据 + * + * @param excel 导出对象 + * @param maxCol 指向最大的列,超过之后换行 + * @param list 待填充数据 + * @author liaochangjiang + * @since 2022-04-27 16:45:12 + */ + public void fillDataComment(ExportExcel excel,int maxCol, List list) { + ExportTemplateRegion templateRegion = this.templateRegion.get(); + int seqColIdx = 0;// 记录处理的行索引 + int colIdx = templateRegion.col1();// 初始开始行 + int curRow = templateRegion.row1();// 初始开始列 + List analysisCells = this.getAnalysisCells(excel); + for (Object data : list) { + // 填充内容 + seqColIdx = this.fillData(excel, analysisCells, colIdx, seqColIdx, curRow, data); + // 记录当满足什么时候进行换行。 + if (seqColIdx + templateRegion.col1() > maxCol) { + seqColIdx = 0;// 并且重置处理的行索引 + curRow += this.getTemplateRegionRowSpan();// 行切换以初始行+目前处理行索引 + } + } + } + + /** + * 填充单元格 + * + * @author liaochangjiang + * @since 2022-10-19 15:23:21 + */ + private void fillCell(XSSFSheet sheet, int colIdx, int curColIdx, int curRow, AnalysisCell analysisCell, + String fieldValue) { + try { + sheet.getRow(curRow + analysisCell.getRow()).getCell(colIdx + curColIdx + analysisCell.getCol()) + .setCellValue(fieldValue); + } catch (Exception e) { + log.error("填充excel失败。curRow:{}, templateRow:{}, curCol:{}, templateCol:{}, fieldValue:{}", curRow, + analysisCell.getRow(), colIdx + curColIdx, analysisCell.getCol(), fieldValue); + throw new RuntimeException("填充excel失败", e); + } + } + + /** + * 获取模板字段对应的值 + * + * @author liaochangjiang + * @since 2022-02-07 16:48:17 + */ + public String getFieldValue(DocumentContext jsonCtx, String field) { + return Optional.ofNullable(jsonCtx.read(String.format("$.%s", field))).map(String::valueOf) + .orElse(StringUtils.EMPTY); + } + + /** + * 创建新列 + * + * @author liaochangjiang + * @since 2022-01-24 16:11:44 + */ + public void createNewCols(ExportExcel excel, XSSFSheet sheet, int colIdx) { + Sheet templateSheet = excel.getTemplateSheet(); + int lastRow = sheet.getLastRowNum(); + ExportTemplateRegion templateRegion = this.templateRegion.get(); + for (int i = 0; i <= lastRow; i++) { + XSSFRow tmpRow = sheet.getRow(i); + for (int j = templateRegion.col1(); j <= templateRegion.col2(); j++) { + XSSFCell cell = tmpRow.getCell(j); + if (cell != null) { + int curColIdx = colIdx + j - templateRegion.col1(); + if (tmpRow.getCell(curColIdx) != null) { + return; + } + XSSFCell distCell = tmpRow.createCell(curColIdx); + excel.copyCell(cell, distCell, false); + // 设置列宽 + if (i == 1) { + sheet.setColumnWidth(curColIdx, sheet.getColumnWidth(j)); + } + int regions = templateSheet.getNumMergedRegions(); + for (int k = 0; k < regions; k++) { + CellRangeAddress region = templateSheet.getMergedRegion(k); + if (region.getLastRow() == i && region.getLastColumn() == j) { + CellRangeAddress newRegion = new CellRangeAddress(i, i, + curColIdx - (region.getLastColumn() - region.getFirstColumn()), curColIdx); + sheet.addMergedRegion(newRegion); + } + } + } + } + } + } + + + /** + * 初始化模板区域 + * + * @author liaochangjiang + * @since 2022-02-18 10:21:41 + */ + public void initTemplateRegion(Class clazz, String group) { + ExportTemplateRegion[] annotations = clazz.getAnnotationsByType(ExportTemplateRegion.class); + ExportTemplateRegion result = null; + for (ExportTemplateRegion region : annotations) { + if (StringUtils.isBlank(group)) { + result = region; + break; + } else if (ArrayUtils.indexOf(region.groups(), group) != -1) { + result = region; + break; + } + } + templateRegion.set(result); + } + + public ThreadLocal getTemplateRegion() { + return templateRegion; + } + + /** + * 获取模板区域行跨度 + * + * @author liaochangjiang + * @since 2022-02-18 10:32:35 + */ + public int getTemplateRegionRowSpan() { + ExportTemplateRegion region = templateRegion.get(); + if (region == null) { + throw new RuntimeException("找不到导出模板区域配置"); + } + return region.row2() - region.row1() + 1; + } + + /** + * 获取模板区域列跨度 + * + * @author liaochangjiang + * @since 2023-01-16 10:05:10 + */ + public int getTemplateRegionColSpan() { + ExportTemplateRegion region = templateRegion.get(); + if (region == null) { + throw new RuntimeException("找不到导出模板区域配置"); + } + return region.col2() - region.col1() + 1; + } + + /** + * 移除模板区域本地变量 + * + * @author liaochangjiang + * @since 2022-02-18 10:22:19 + */ + public void remoteTemplateRegion() { + templateRegion.remove(); + } + + /** + * 固定模板填充占位-EasyExcel + * 不导出前台,会根据传入的输出流输出到某个路径下, 并且做后续的操作,如:可重新将输出流的文件进行PDF文件转化 + * + * @param outputStream 文件输出流 + * @param template 模板文件位置 + * @param data 填充的对象数据 + * @author liaochangjiang + * @since 2022-09-16 16:19:52 + */ + public void exportByStatic(OutputStream outputStream,String template,Object data) { + ClassPathResource classPathResource = new ClassPathResource(template); + try (InputStream in = classPathResource.getInputStream()) { + ExcelWriter excelWriter = EasyExcel + .write(outputStream) + .withTemplate(in) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet(0) + .registerWriteHandler(new ImageModifyHandler()) + .build(); + excelWriter.fill(data, writeSheet); + excelWriter.finish(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void exportByStatic(OutputStream outputStream,String template,List list) { + ClassPathResource classPathResource = new ClassPathResource(template); + try (InputStream in = classPathResource.getInputStream()) { + ExcelWriter excelWriter = EasyExcel + .write(outputStream) + .withTemplate(in) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet(0) + .registerWriteHandler(new ImageModifyHandler()) + .build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + for (Object vo : list) { + excelWriter.fill(vo, fillConfig, writeSheet); + } + excelWriter.finish(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 固定模板填充占位-EasyExcel + * 不导出前台,会根据传入的输出流输出到某个路径下, 并且做后续的操作,如:可重新将输出流的文件进行PDF文件转化 + * + * @param data 填充的对象数据 + * @author liaochangjiang + * @since 2022-09-16 16:19:52 + */ + public InputStream exportByStatic(InputStream in,Object data) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ExcelWriter excelWriter = EasyExcel + .write() + .withTemplate(in) + .file(os) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet(0) + .build(); + excelWriter.fill(data, writeSheet); + excelWriter.finish(); + byte[] buffer = os.toByteArray(); + return new ByteArrayInputStream(buffer); + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ImageModifyHandler.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ImageModifyHandler.java new file mode 100644 index 0000000..0d5278d --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ExportUtil/ImageModifyHandler.java @@ -0,0 +1,182 @@ +package com.centricsoftware.enhancement.util.ExportUtil; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.ImageData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import lombok.Setter; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFPictureData; +import org.springframework.util.CollectionUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Objects; + + +public class ImageModifyHandler implements CellWriteHandler { + @Setter + private List cellRangeAddressList = null; + + /** + * 得到合并行num + * + * @param cell 细胞 + * @param sheet 表 + * @return int + */ + public int getMergeRowNum(Cell cell, Sheet sheet) { + int mergeSize = 1; + List mergedRegions = sheet.getMergedRegions(); + if (CollectionUtils.isEmpty(cellRangeAddressList)) { + for (CellRangeAddress cellRangeAddress : mergedRegions) { + if (cellRangeAddress.isInRange(cell)) { + //获取合并的行数 + mergeSize = cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow() + 1; + break; + } + } + } else { + for (CellRangeAddress cellRangeAddress : cellRangeAddressList) { + //获取合并的行数 + mergeSize = cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow() + 1; + break; + } + } + + return mergeSize; + } + + /** + * 得到合并列num + * + * @param cell 细胞 + * @param sheet 表 + * @return int + */ + public int getMergeColumNum(Cell cell, Sheet sheet) { + int mergeSize = 1; + List mergedRegions = sheet.getMergedRegions(); + if (CollectionUtils.isEmpty(cellRangeAddressList)) { + for (CellRangeAddress cellRangeAddress : mergedRegions) { + if (cellRangeAddress.isInRange(cell)) { + //获取合并的列数 + mergeSize = cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn() + 1; + break; + } + } + } else { + for (CellRangeAddress cellRangeAddress : cellRangeAddressList) { + //获取合并的行数 + mergeSize = cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn() + 1; + break; + } + } + return mergeSize; + } + + /** + * 获取列宽像素-包含合并 + * @author liaochangjiang + * @since 2024-04-24 16:03 + */ + private Double getColumNumPx (Cell cell,Sheet sheet,int mergeColumNum) { + int start = cell.getColumnIndex(); + Row row = cell.getRow(); + double columLength = 0; + for (int i = start;i < start + mergeColumNum;i++) { + columLength += sheet.getColumnWidthInPixels(i); + } + return columLength; + } + + /** + * 获取行高像素-包含合并 + * @author liaochangjiang + * @since 2024-04-24 16:03 + */ + private Double getRowPx (Cell cell,Sheet sheet,int mergeRowNum) { + Row row = cell.getRow(); + int start = row.getRowNum(); + double rowLength = 0; + for (int i = start;i < start + mergeRowNum;i++) { + rowLength += (sheet.getRow(i).getHeightInPoints() / 72) * 96;// 像素单位 + } + return rowLength; + } + + /** + * 后单元格数据转换 + * + * @param writeSheetHolder 写单夹 + * @param writeTableHolder 写表夹 + * @param cellData 单元格数据 + * @param cell 细胞 + * @param head 头 + * @param relativeRowIndex 相对行索引 + * @param isHead 是头 + */ + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + WriteCellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + boolean noImageValue = Objects.isNull(cellData) || CollectionUtils.isEmpty(cellData.getImageDataList()); + if (Objects.equals(Boolean.TRUE, isHead) || noImageValue) { + return; + } + try { + Sheet sheet = cell.getSheet(); + int mergeColumNum = getMergeColumNum(cell, sheet);// 获取实际合并的列 + int mergeRowNum = getMergeRowNum(cell, sheet);// 获取实际合并的行 + double cellWidth = getColumNumPx(cell,sheet,mergeColumNum);// 计算真实的列宽,像素单位 + double cellHeight = getRowPx(cell,sheet,mergeRowNum);// 计算真是的行高,像素单位 + ImageData imageData = cellData.getImageDataList().get(0); + BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(cellData.getImageDataList().get(0).getImage()));// 获取图片的数据 + double imageWidth = bufferedImage.getWidth();// 图片的宽度,像素 + double imageHeight = bufferedImage.getHeight();// 图片的高度,像素 + //等比例缩小图片,直到图片能放在单元格下,每次缩小20% + Integer top = 0,left = 0; + // 比例计算 - 优化 + //厘米转换成像素,按实际需求转换 + // while (true) { + // if(imageHight <= rowLength && imageWidth <= columLength){ + // //计算边框值 + // top = Math.toIntExact(Math.round((rowLength - imageHight)/2)); + // left = Math.toIntExact(Math.round((columLength - imageWidth)/2)); + // break; + // }else { + // imageHight = imageHight * 0.8; + // imageWidth = imageWidth * 0.8; + // } + // } + //缩放比例 + if (imageWidth > 0 && imageHeight > 0) { + double scaleWidth = cellWidth / imageWidth; + double scaleHeight = cellHeight / imageHeight; + double scale = Math.min(scaleWidth, scaleHeight) - 0.001; + top = Math.toIntExact(Math.round((cellHeight - imageHeight * scale)/2)); + left = Math.toIntExact(Math.round((cellWidth - imageWidth * scale)/2)); + } + imageData.setRelativeLastRowIndex(mergeRowNum - 1); + imageData.setRelativeLastColumnIndex(mergeColumNum - 1); + // 上右下左需要留空,通过这种方式调整图片大小,单位为像素 + imageData.setTop(top); + imageData.setRight(left); + imageData.setBottom(top); + imageData.setLeft(left); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/ImportUtil.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ImportUtil.java new file mode 100644 index 0000000..3cfd84a --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/ImportUtil.java @@ -0,0 +1,107 @@ +package com.centricsoftware.enhancement.util; + + +import com.centricsoftware.enhancement.ant.C8ColumnConfig; + +import java.util.Map; + +public class ImportUtil { + /** + * 拼接保存的xml + * @param id + * @param type + * @param value + * @param otherXmlAttr + * @return + */ + public static String getChangeAttrListXml(C8ColumnConfig col, String id, String type, String value, String otherXmlAttr){ + StringBuilder xml = new StringBuilder(); + xml.append( ""); + xml.append(""); + xml.append(""); + return xml.toString(); + } + + public static String getChangeAttrListXml(String id, String type, String value, String otherXmlAttr){ + StringBuilder xml = new StringBuilder(); + xml.append( ""); + xml.append(""); + xml.append(""); + return xml.toString(); + } + + public static String getChangeAttrEnumListXml(C8ColumnConfig col,String id,String type,String value,String otherXmlAttr){ + StringBuilder xml = new StringBuilder(); + xml.append( ""); + xml.append(value); + xml.append(""); + return xml.toString(); + } + + /** + * 拼接保存的xml + * @param id + * @param type + * @param value + * @param otherXmlAttr + * @return + */ + public static String getChangeAttrXml(C8ColumnConfig col,String id,String type,String value,String otherXmlAttr){ + StringBuilder xml = new StringBuilder(); + xml.append( ""); + return xml.toString(); + } + + public static String getChangeAttrXml(String id,String type,String value,String otherXmlAttr){ + StringBuilder xml = new StringBuilder(); + xml.append( ""); + return xml.toString(); + } + + /** + * 封装带有path的xml + * @param url + * @param path + * @param pathMap + * @param fieldxml + */ + public static void setPathXml(String url, String path, Map pathMap, String fieldxml){ + if(pathMap.containsKey(path)){ + StringBuilder xml = pathMap.get(path); + xml.append(fieldxml); + }else{ + StringBuilder xml = new StringBuilder(); + String pathTrans = path.replaceAll(":", "%3A"); + xml.append(""); + xml.append(fieldxml); + pathMap.put(path,xml); + } + } + + /** + * 拼接并返回pathxml + * @param pathMap + * @return + */ + public static String appendPathXml(Map pathMap){ + StringBuilder xml = new StringBuilder(); + for(Map.Entry entry : pathMap.entrySet()){ + xml.append(entry.getValue()); + xml.append(""); + } + return xml.toString(); + } + + /** + * 计算表达式相关 + * @param col + * @return + */ + private static String appendExp(C8ColumnConfig col){ + if(col.isRemoveExp()){ + return "Exp=''"; + } + return ""; + } + +} diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/UploadtProperties.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/UploadtProperties.java new file mode 100644 index 0000000..0e30436 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/UploadtProperties.java @@ -0,0 +1,25 @@ +package com.centricsoftware.enhancement.util; + +import com.centricsoftware.enhancement.ant.C8EntityConfig; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + + +/** + * 导入Excel时,获取cs.excel的配置文件 + */ +@Data +@Component +@ConfigurationProperties(prefix = "cs.excel") +public class UploadtProperties { + Map upload; + + public C8EntityConfig getValue(String key){ + return this.getUpload().containsKey(key)?this.getUpload().get(key):null; + } + +} + diff --git a/enhancement/src/main/java/com/centricsoftware/enhancement/util/http/C8HttpUtil.java b/enhancement/src/main/java/com/centricsoftware/enhancement/util/http/C8HttpUtil.java new file mode 100644 index 0000000..e4f7d39 --- /dev/null +++ b/enhancement/src/main/java/com/centricsoftware/enhancement/util/http/C8HttpUtil.java @@ -0,0 +1,53 @@ +package com.centricsoftware.enhancement.util.http; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Headers; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.http.HttpHeaders; +import okio.Buffer; +import okio.BufferedSource; + +import java.io.IOException; +import java.nio.charset.Charset; + +@Slf4j +public class C8HttpUtil { + + /** + * 复制请求体;只支持UTF-8 + * 传入的Response为okhttp + * 此方法用于C8的交互 + */ + public static String copyResponseBody(Response response){ + try { + ResponseBody responseBody = response.body(); + long contentLength = responseBody.contentLength(); + if(!HttpHeaders.hasBody(response)){ + return "";//无ResponseBody,返回空 + } else if (C8HttpUtil.bodyEncoded(response.headers())) { + //HTTP (encoded body omitted) + return ""; + } else { + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // Buffer the entire body. + Buffer buffer = source.getBuffer(); + Charset charset = Charset.forName("UTF-8"); + if (contentLength != 0) { + return buffer.clone().readString(charset); + } + } + } catch (IOException e) { + log.error("获取返回body出错",e); + return ""; + } + return ""; + } + + public static boolean bodyEncoded(Headers headers) { + String contentEncoding = headers.get("Content-Encoding"); + return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity"); + } + + +} diff --git a/enhancement/src/main/resources/mapper/ExtractMapper.xml b/enhancement/src/main/resources/mapper/ExtractMapper.xml new file mode 100644 index 0000000..b368468 --- /dev/null +++ b/enhancement/src/main/resources/mapper/ExtractMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/pom.xml b/integration/pom.xml new file mode 100644 index 0000000..08be529 --- /dev/null +++ b/integration/pom.xml @@ -0,0 +1,24 @@ + + + + plmservice + com.centricsoftware + 2.0 + + 4.0.0 + integration + 0.10 + + + + + + + + com.centricsoftware + enhancement + + + diff --git a/integration/src/main/java/com/centricsoftware/integration/controller/dingtalk/DingTalkController.java b/integration/src/main/java/com/centricsoftware/integration/controller/dingtalk/DingTalkController.java new file mode 100644 index 0000000..3a3077a --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/controller/dingtalk/DingTalkController.java @@ -0,0 +1,34 @@ +package com.centricsoftware.integration.controller.dingtalk; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.integration.service.dingtalk.DingTalkService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@Slf4j +@RestController +public class DingTalkController { + @Autowired + DingTalkService dingTalkService; + + /** + * 发送钉钉消息到个人用户 + * @param map + * @return + */ + @PostMapping("/sendDingMsg") + public ResEntity sendMsgSingle(@RequestBody Map map){ + String msgContent = (String)map.get("msgContent"); + String userList = (String)map.get("userList"); + log.info("发送钉钉消息:["+msgContent+"]给"+userList); + String result = dingTalkService.sendMsgToUser(msgContent,userList); + log.info("发送结果:"+result); + return WebResponse.success(""); + } +} diff --git a/integration/src/main/java/com/centricsoftware/integration/controller/flybook/FlyBookController.java b/integration/src/main/java/com/centricsoftware/integration/controller/flybook/FlyBookController.java new file mode 100644 index 0000000..b095b0f --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/controller/flybook/FlyBookController.java @@ -0,0 +1,73 @@ +package com.centricsoftware.integration.controller.flybook; + + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.integration.service.flybook.FlyBookService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * 飞书集成发送消息控制器 + */ +@Slf4j +@RestController +public class FlyBookController { + @Autowired + FlyBookService flyBookService; + +// @PostMapping("/sendMsg") +// public ResEntity sendMsg(String server,String url,String name,String status,String users, String msgType,String msgContent){ +// //fbService.senMsgGroup(url,name,status); +// fbService.senMsgSingleByUserId(server,url,name,status,users,msgType,msgContent); +// return WebResponse.success(""); +// } + @PostMapping("/sendMsgSingle") + public ResEntity sendMsgSingle(String server, String url, String name, String status, String users, String msgType, String msgContent){ + flyBookService.senMsgSingleByEmail(server,url,name,status,users,msgType,msgContent); + return WebResponse.success(""); + } + @PostMapping("/sendMsgByEmail") + public ResEntity sendMsgByEmail(@RequestBody Map map){ + String server = (String)map.get("server"); + String url = (String)map.get("url"); + String status = (String)map.get("status"); + String users = (String)map.get("users"); + String msgType = (String)map.get("msgType"); + String name = (String)map.get("name"); + String msgContent = (String)map.get("msgContent"); + flyBookService.senMsgSingleByEmail(server,url,name,status,users,msgType,msgContent); + return WebResponse.success(""); + } +// @PostMapping("/sendMsgByUserId") +// public ResEntity sendMsgToPerson(@RequestBody Map map){ +// String server = (String)map.get("server"); +// String url = (String)map.get("url"); +// String status = (String)map.get("status"); +// String users = (String)map.get("users"); +// String msgType = (String)map.get("msgType"); +// String name = (String)map.get("name"); +// String msgContent = (String)map.get("msgContent"); +// fbService.senMsgSingleByUserId(server,url,name,status,users,msgType,msgContent); +// return WebResponse.success(""); +// } + @PostMapping("/sendMsgByDepartmentId") + public ResEntity sendMsgGroup(@RequestBody Map map){ + String server = (String)map.get("server"); + String name = (String)map.get("name"); + String status = (String)map.get("status"); + String users = (String)map.get("users"); + String msgType = (String)map.get("msgType"); + String url = (String)map.get("url"); + String msgContent = (String)map.get("msgContent"); + flyBookService.sendBatchMsgByDepatmentId(server,url,name,status,users,msgType,msgContent); + return WebResponse.success(""); + } + + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/controller/wechat/WeChatController.java b/integration/src/main/java/com/centricsoftware/integration/controller/wechat/WeChatController.java new file mode 100644 index 0000000..d6dce97 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/controller/wechat/WeChatController.java @@ -0,0 +1,32 @@ +package com.centricsoftware.integration.controller.wechat; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.integration.dto.param.BaseParamDto; +import com.centricsoftware.integration.dto.wechat.WeChatDataDto; +import com.centricsoftware.integration.service.wechat.WeChatService; +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; + +import javax.annotation.Resource; + + +@RestController +@RequestMapping("/wechat") +public class WeChatController { + @Resource + WeChatService weChatService; + + @PostMapping("/getToken") + public ResEntity getToken() { + //return WebResponse.success(weChatService.getToken()); //此接口不对外开放 + return WebResponse.failure("此接口不对外开放"); + } + + @PostMapping("/sendMsg") + public ResEntity sendMsg(@RequestBody BaseParamDto param) { + return weChatService.sendMsg(param); + } +} diff --git a/integration/src/main/java/com/centricsoftware/integration/dto/param/BaseParamDto.java b/integration/src/main/java/com/centricsoftware/integration/dto/param/BaseParamDto.java new file mode 100644 index 0000000..2b776e1 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/dto/param/BaseParamDto.java @@ -0,0 +1,8 @@ +package com.centricsoftware.integration.dto.param; + +import lombok.Data; + +@Data +public class BaseParamDto { + public T param; +} diff --git a/integration/src/main/java/com/centricsoftware/integration/dto/wechat/WeChatDataDto.java b/integration/src/main/java/com/centricsoftware/integration/dto/wechat/WeChatDataDto.java new file mode 100644 index 0000000..bc20be0 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/dto/wechat/WeChatDataDto.java @@ -0,0 +1,115 @@ +package com.centricsoftware.integration.dto.wechat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 微信消息发送实体类 + * 发送微信消息的 https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token= + * + * @author Harry + * @history 2022年07月27日09:08:05 Jerry 优化调整 + */ +@Data +public class WeChatDataDto { + //touser、toparty、totag不能同时为空 + + /** + * 指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。 + * 特殊情况:指定为"@all",则向该企业应用的全部成员发送 + */ + @JsonProperty("touser") + private String touser; + + /** + * 指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。 + * 当touser为"@all"时忽略本参数 + */ + @JsonProperty("toparty") + private String toparty; + + /** + * 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。 + * 当touser为"@all"时忽略本参数 + */ + @JsonProperty("totag") + private String totag; + + /** + * 消息类型,此时固定为:text + */ + @JsonProperty("msgtype") + private String msgtype = "textcard"; + + /** + * 企业用用的agentid + * 企业应用的id,企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值 + */ + @JsonProperty("agentid") + private String agentid; + + /** + * 消息内容,最长不超过2048个字节,超过将截断(支持id转译) + */ +// @JsonProperty("text") +// private Content text; +// +// @JsonProperty("content") +// private String content; + + /** + * 内容体 + */ + @JsonProperty("textcard") + private Textcard text1; + + + /** + * 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0 + */ +// @JsonProperty("safe") +// private int safe = 1; + + /** + * 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。 + */ + @JsonProperty("enable_id_trans") + private int enable_id_trans = 0; + + /** + * 表示是否开启重复消息检查,0表示否,1表示是,默认0 + */ + @JsonProperty("enable_duplicate_check") + private int enable_duplicate_check = 0; + + /** + * 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 + */ + @JsonProperty("duplicate_check_interval") + private int duplicate_check_interval = 1800; +} + +//@Data +//class Content { +// @JsonProperty("content") +// private String content; +//} +@Data +class Textcard { + /** + * 标题,不超过128个字节,超过会自动截断(支持id转译) + */ + @JsonProperty("title") + private String title; + /** + * 描述,不超过512个字节,超过会自动截断(支持id转译) + */ + @JsonProperty("description") + private String description; + /** + * 点击后跳转的链接。最长2048字节,请确保包含了协议头(http/https) + */ + @JsonProperty("url") + private String url; +} + diff --git a/integration/src/main/java/com/centricsoftware/integration/feign/MultipartSupportConfig.java b/integration/src/main/java/com/centricsoftware/integration/feign/MultipartSupportConfig.java new file mode 100644 index 0000000..009a6e7 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/feign/MultipartSupportConfig.java @@ -0,0 +1,21 @@ +package com.centricsoftware.integration.feign; + +import feign.codec.Encoder; +import feign.form.spring.SpringFormEncoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.context.annotation.Bean; + +public class MultipartSupportConfig { + + @Autowired + private ObjectFactory messageConverters; + + @Bean + public Encoder feignFormEncoder() { + return new SpringFormEncoder(new SpringEncoder(messageConverters)); + } + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookFeignRequestInterceptor.java b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookFeignRequestInterceptor.java new file mode 100644 index 0000000..625c326 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookFeignRequestInterceptor.java @@ -0,0 +1,33 @@ +package com.centricsoftware.integration.feign.flybook; + +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.integration.flybook.FlyBookAppAccessToken; +import com.centricsoftware.integration.service.flybook.FlyBookTokenService; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import feign.codec.Encoder; +import feign.form.spring.SpringFormEncoder; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpHeaders; + +public class FlyBookFeignRequestInterceptor implements RequestInterceptor { + + @Override + public void apply(RequestTemplate template) { + FlyBookTokenService bean = SpringContextHolder.getBean(FlyBookTokenService.class); + FlyBookAppAccessToken token = bean.getToken(); + template.header(HttpHeaders.AUTHORIZATION,String.format("%s %s", "Bearer", token.getApp_access_token())); + } + + @Autowired + private ObjectFactory messageConverters; + + @Bean + public Encoder feignFormEncoder() { + return new SpringFormEncoder(new SpringEncoder(messageConverters)); + } +} \ No newline at end of file diff --git a/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookMessageFeign.java b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookMessageFeign.java new file mode 100644 index 0000000..a5373ad --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookMessageFeign.java @@ -0,0 +1,31 @@ +package com.centricsoftware.integration.feign.flybook; + +import com.centricsoftware.integration.flybook.FlyBookBatchMessage; +import com.centricsoftware.integration.flybook.FlyBookMessage; +import com.centricsoftware.integration.flybook.FlyBookResultData; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + + +@FeignClient(url = "${cs.flybook.baseUrl}",name = "fb-msg",configuration = FlyBookFeignRequestInterceptor.class) +public interface FlyBookMessageFeign { + + @PostMapping(value = "/im/v1/messages?receive_id_type=chat_id") + FlyBookResultData sendMsgGroup(@RequestBody FlyBookMessage msg); + + @PostMapping(value = "/im/v1/messages?receive_id_type=email") + FlyBookResultData sendMsgSingleByEmail(@RequestBody FlyBookMessage msg); + +// @PostMapping(value = "/im/v1/messages?receive_id_type=user_id") +// ResultData sendMsgByUserId(@RequestBody FBMessage msg); + + /** + * 批量发送消息给用户 + *FBMessage消息体中department_ids, open_ids, user_ids 三个字段至少填一个 + * @param msg + * @return + */ + @PostMapping(value = "/im/v1/messages/v4/batch_send/") + FlyBookResultData sendBatchMsg(@RequestBody FlyBookBatchMessage msg); +} diff --git a/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookTokenFeign.java b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookTokenFeign.java new file mode 100644 index 0000000..339fe6c --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/feign/flybook/FlyBookTokenFeign.java @@ -0,0 +1,16 @@ +package com.centricsoftware.integration.feign.flybook; + +import com.centricsoftware.integration.feign.MultipartSupportConfig; +import com.centricsoftware.integration.flybook.FlyBookAppAccessToken; +import com.centricsoftware.integration.flybook.FlyBookAppData; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(url = "${cs.flybook.baseUrl}",name = "fb-token",configuration = {MultipartSupportConfig.class}) +public interface FlyBookTokenFeign { + + @PostMapping(value = "/auth/v3/app_access_token/internal") + FlyBookAppAccessToken getAppAccessToken(@RequestBody FlyBookAppData flyBookAppData); + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/feign/wechat/WeChatFeign.java b/integration/src/main/java/com/centricsoftware/integration/feign/wechat/WeChatFeign.java new file mode 100644 index 0000000..2001e83 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/feign/wechat/WeChatFeign.java @@ -0,0 +1,26 @@ +package com.centricsoftware.integration.feign.wechat; + + +import com.centricsoftware.integration.dto.wechat.WeChatDataDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(url = "${cs.wechat.url}", name = "c8-wechat") +public interface WeChatFeign { + + /** + * 获取企业微信鉴权Token + * + * @param corpid appid + * @param corpsecret secret + * @return 微信token + */ + @PatchMapping(value = "/gettoken?corpid={corpid}&corpsecret={corpsecret}", headers = {"Content-Type=application/x-www-form-urlencoded;charset=UTF-8;"}) + String getToken(@PathVariable String corpid, @PathVariable String corpsecret); + + @PostMapping(value = "/message/send?access_token={access_token}", headers = {"Content-Type=application/json;charset=UTF-8;"}) + String sendMessage(@PathVariable String access_token, @RequestBody WeChatDataDto param); +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAccessToken.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAccessToken.java new file mode 100644 index 0000000..9a908d2 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAccessToken.java @@ -0,0 +1,24 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class FlyBookAppAccessToken { + + @JsonProperty("app_access_token") + private String app_access_token; + + @JsonProperty("code") + private String code; + + @JsonProperty("expire") + private String expire; + + @JsonProperty("msg") + private String msg; + + @JsonProperty("tenant_access_token") + private String tenant_access_token; + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAuthenAccessToken.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAuthenAccessToken.java new file mode 100644 index 0000000..8a6ac98 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppAuthenAccessToken.java @@ -0,0 +1,16 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class FlyBookAppAuthenAccessToken { + @JsonProperty("grant_type") + String grantType = "authorization_code"; + @JsonProperty("code") + String code; +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppData.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppData.java new file mode 100644 index 0000000..b2ef1c7 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookAppData.java @@ -0,0 +1,15 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class FlyBookAppData { + + @JsonProperty("app_id") + private String app_id = ""; + + @JsonProperty("app_secret") + private String app_secret = ""; + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookBatchMessage.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookBatchMessage.java new file mode 100644 index 0000000..7d17ab4 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookBatchMessage.java @@ -0,0 +1,30 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class FlyBookBatchMessage implements Serializable { + + @JsonIgnore + private String template = "{\"config\": {\"wide_screen_mode\": true},\"elements\": [ {\"tag\": \"div\",\"text\": {\"content\": \"{}\",\"tag\": \"lark_md\"}},{\"extra\": {\"tag\": \"button\",\"text\": {\"content\": \"点击查看\",\"tag\": \"lark_md\"},\"type\": \"danger\",\"url\": \"{}\"},\"tag\": \"div\",\"text\": {\"content\": \"发送时间:{}\",\"tag\": \"lark_md\"}}],\"header\": {\"template\": \"green\",\"title\": {\"content\": \"{}\",\"tag\": \"plain_text\"}}}"; + + @JsonProperty("content") + private String content; + + @JsonProperty("msg_type") + private String msg_type = "interactive"; + + @JsonProperty("department_ids") + private String[] department_ids = {}; + + @JsonProperty("open_ids") + private String[] open_ids = {}; + + @JsonProperty("user_ids") + private String[] user_ids = {}; + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookMessage.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookMessage.java new file mode 100644 index 0000000..71a1100 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookMessage.java @@ -0,0 +1,25 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class FlyBookMessage implements Serializable { + + @JsonIgnore + private String template = "{\"config\": {\"wide_screen_mode\": true},\"elements\": [ {\"tag\": \"div\",\"text\": {\"content\": \"{}\",\"tag\": \"lark_md\"}},{\"extra\": {\"tag\": \"button\",\"text\": {\"content\": \"进入PLM系统\",\"tag\": \"lark_md\"},\"type\": \"danger\",\"url\": \"{}\"},\"tag\": \"div\",\"text\": {\"content\": \"发送时间:{}\",\"tag\": \"lark_md\"}}],\"header\": {\"template\": \"{}\",\"title\": {\"content\": \"{}\",\"tag\": \"plain_text\"}}}"; + + @JsonProperty("content") + private String content; + + @JsonProperty("msg_type") + private String msg_type = "interactive"; + + @JsonProperty("receive_id") + private String receive_id = ""; + + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookResultData.java b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookResultData.java new file mode 100644 index 0000000..0339857 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/flybook/FlyBookResultData.java @@ -0,0 +1,15 @@ +package com.centricsoftware.integration.flybook; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class FlyBookResultData { + + @JsonProperty + private String code; + @JsonProperty + private Object data; + @JsonProperty + private String msg; +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/dingtalk/DingTalkService.java b/integration/src/main/java/com/centricsoftware/integration/service/dingtalk/DingTalkService.java new file mode 100644 index 0000000..45377bc --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/dingtalk/DingTalkService.java @@ -0,0 +1,118 @@ +package com.centricsoftware.integration.service.dingtalk; + +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.dingtalk.api.DefaultDingTalkClient; +import com.dingtalk.api.DingTalkClient; +import com.dingtalk.api.request.OapiGettokenRequest; +import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request; +import com.dingtalk.api.request.OapiMessageCorpconversationSendbytemplateRequest; +import com.dingtalk.api.response.OapiGettokenResponse; +import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response; +import com.dingtalk.api.response.OapiMessageCorpconversationSendbytemplateResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class DingTalkService { + +// public static void main (String args[]){ +// DingService dt = new DingService(); +// dt.sendMsgToUser("来自于程序的测试消息\\n发送时间:"+new Date(),"2749363561-2001123913"); +// } + + @Autowired + C8NodeService c8NodeService; + + /** + * 发送个人普通消息给用户,异步发送,不解析发送结果,调用成功即认为成功 + * @param msgContent + * @param userList 用户的钉钉id,用逗号分隔,代表发送给多个用户 + */ + public String sendMsgToUser(String msgContent, String userList) { + try { + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2"); + OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request(); + CsProperties properties = c8NodeService.getProperties(); + //用于发送消息的应用的appid,secret + String agentId = properties.getPlm().get("cs.dingtalk.agentid"); + if(StrUtil.isBlank(agentId)) + agentId = "0"; + long agentIntId = 0; + try{ + agentIntId = Long.parseLong(agentId); + }catch(Exception e){} + req.setAgentId(agentIntId); + req.setUseridList(userList); + + //设置消息内容,普通文本消息 + OapiMessageCorpconversationAsyncsendV2Request.Msg msgObj = new OapiMessageCorpconversationAsyncsendV2Request.Msg(); + msgObj.setMsgtype("text"); + OapiMessageCorpconversationAsyncsendV2Request.Text textObj = new OapiMessageCorpconversationAsyncsendV2Request.Text(); + textObj.setContent(msgContent); + msgObj.setText(textObj); + OapiMessageCorpconversationAsyncsendV2Request.OA obj3 = new OapiMessageCorpconversationAsyncsendV2Request.OA(); + OapiMessageCorpconversationAsyncsendV2Request.Body obj4 = new OapiMessageCorpconversationAsyncsendV2Request.Body(); + obj4.setContent(msgContent); + obj3.setBody(obj4); + msgObj.setOa(obj3); + req.setMsg(msgObj); + String token = getAccessToken(); + log.info("token="+token); + OapiMessageCorpconversationAsyncsendV2Response rsp = client.execute(req,token ); + log.info("rsp.getMessage()="+rsp.getMessage()); + log.info("rsp.getBody()="+rsp.getBody()); + return rsp.getBody(); + } catch (Exception e) { + e.printStackTrace(); + } + return "发送消息失败"; + } + + /** + * 获取钉钉端的token + * @return + * @throws Exception + */ + private String getAccessToken() throws Exception { + DefaultDingTalkClient client = + new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); + OapiGettokenRequest request = new OapiGettokenRequest(); + CsProperties properties = c8NodeService.getProperties(); + //用于发送消息的应用的appid,secret + String appid = properties.getPlm().get("cs.dingtalk.appkey");//"dinghrfbr11eavtoufli"; + String secret = properties.getPlm().get("cs.dingtalk.secret");//"WFCkJDizmEoD0BpXQ3QKVjkL4vkszEyhldLOksKG2jmrDoO6XmWsBttl3PPrPnkA" + log.debug("appid="+appid); + log.debug("secret="+secret); + //Appkey + request.setAppkey(appid); + //Appsecret + request.setAppsecret(secret); + /*请求方式*/ + request.setHttpMethod("GET"); + OapiGettokenResponse response = client.execute(request); + return response.getAccessToken(); + } + + /** + * 通过消息模板发送消息,目前只支持第三方企业应用,不支持企业内部应用,该方法暂不使用 + * @param msg + * @param userList + * @return + * @throws Exception + */ + private String sendWorkNofificationByTemplate(String msg, String userList) throws Exception{ + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate"); + OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest(); + req.setAgentId(1480464891L); + req.setUseridList(userList); + req.setTemplateId("e27a9eed42b34a14a2xxxx"); + req.setData("{\"name\":\"淘宝6\",\"name2\":\"http://www.taobao.com\"}"); + OapiMessageCorpconversationSendbytemplateResponse rsp = client.execute(req, getAccessToken()); + return rsp.getBody(); + } + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookService.java b/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookService.java new file mode 100644 index 0000000..d97de3c --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookService.java @@ -0,0 +1,163 @@ +package com.centricsoftware.integration.service.flybook; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import com.centricsoftware.integration.feign.flybook.FlyBookMessageFeign; +import com.centricsoftware.integration.flybook.FlyBookBatchMessage; +import com.centricsoftware.integration.flybook.FlyBookMessage; +import com.centricsoftware.integration.flybook.FlyBookResultData; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +@Slf4j +@Service +public class FlyBookService { + @Autowired + FlyBookMessageFeign flyBookMessageFeign; + + private String location = "/WebAccess/home.html#URL={}"; + + + /** + * 发送飞书消息到个人, By用户的邮箱地址 + * @param server 飞书消息中的地址的server地址 + * @param url 飞书消息中的地址的对象URL + * @param name 暂时定义为:header的名字 + * @param status + * @param emails 邮件地址,逗号分隔 + * @param msgType 预留,根据不同的消息类型发送不同的消息模板 + * @param msgContent 消息内容 + */ + public void senMsgSingleByEmail(String server,String url,String name,String status,String emails, String msgType,String msgContent){ + String[] emailsList = emails.split(","); + for (String email:emailsList) { + if(!StrUtil.isBlank(email)){ + String format1 = StrFormatter.format(server+location, url); + FlyBookMessage msg = new FlyBookMessage(); + //msg.singleReceive(); + msg.setReceive_id(email); + String template = msg.getTemplate(); + String format = StrFormatter.format(template, msgContent,format1, DateUtil.now(),"green",name); + msg.setContent(format); + log.info("msgContent="+msg.getContent()); + try{ + FlyBookResultData result = flyBookMessageFeign.sendMsgSingleByEmail(msg); + log.info("发送飞书消息结束="+result); + log.info("发送飞书消息成功 email="+email); + }catch (Exception e){ + log.error("发送飞书消息失败:email="+email); + e.printStackTrace(); + } + + } + } + } + + /** + * 发送飞书消息到个人, By用户的邮箱地址 + * @param server 飞书消息中的地址的server地址 + * @param url 飞书消息中的地址的对象URL + * @param name 暂时定义为:header的名字 + * @param status + * @param emails 邮件地址,逗号分隔 + * @param msgType 预留,根据不同的消息类型发送不同的消息模板 + * @param msgContent 消息内容 + */ + public void senMsgSingleByEmailAndFormat(String server,String url,String name,String status,String emails, String msgType,String msgContent){ + String[] emailsList = emails.split(","); + for (String email:emailsList) { + if(!StrUtil.isBlank(email)){ + String format1 = StrFormatter.format(server+location, url); + FlyBookMessage msg = new FlyBookMessage(); + //msg.singleReceive(); + msg.setReceive_id(email); + String template = msg.getTemplate(); + String format = StrFormatter.format(template, msgContent,format1, DateUtil.now(),status,name); + msg.setContent(format); + log.info("msgContent="+msg.getContent()); + try{ + FlyBookResultData result = flyBookMessageFeign.sendMsgSingleByEmail(msg); + log.info("发送飞书消息结束="+result); + log.info("发送飞书消息成功 email="+email); + }catch (Exception e){ + log.error("发送飞书消息失败:email="+email); + e.printStackTrace(); + } + + } + } + } + + /** + * 发送飞书消息到个人 + * @param server 飞书消息中的地址的server地址 + * @param url 飞书消息中的地址的对象URL + * @param name 暂时定义为:header的名字 + * @param status + * @param usersId 用户的飞书Id号,逗号分隔 + * @param msgType 预留,根据不同的消息类型发送不同的消息模板 + * @param msgContent 消息内容 + */ +// public void senMsgSingleByUserId(String server,String url,String name,String status,String usersId, String msgType,String msgContent){ +// //Link 模板 +// String[] userList = usersId.split(","); +// for (String userFSId:userList) { +// if(!StrUtil.isBlank(userFSId)){ +// String format1 = StrFormatter.format(server+location, url); +// FBMessage msg = new FBMessage(); +// //msg.singleReceive(); +// msg.setReceive_id(userFSId); +// String template = msg.getTemplate(); +// String format = StrFormatter.format(template, msgContent,format1, DateUtil.now(),name); +// msg.setContent(format); +// log.info("msgContent="+msg.getContent()); +// ResultData result = otherFeign.sendMsgByUserId(msg); +// log.info("result="+result); +// } +// } +// } + + public void sendBatchMsgByUserId(String server,String url,String name,String status,String usersId, String msgType,String msgContent){ + //Link 模板 + if(usersId!=null) { + String format1 = StrFormatter.format(server + location, url); + FlyBookBatchMessage msg = new FlyBookBatchMessage(); + //msg.singleReceive(); + msg.setUser_ids(usersId.split(",")); + String template = msg.getTemplate(); + String format = StrFormatter.format(template, msgContent, format1, DateUtil.now(), name, "", ""); + msg.setContent(format); + log.info("msgContent="+msg.getContent()); + FlyBookResultData rd = flyBookMessageFeign.sendBatchMsg(msg); + log.info("result="+rd ); + } + } + + /** + * 给某个部门发送消息 + * @param server + * @param url + * @param name + * @param status + * @param departmentId + * @param msgType + * @param msgContent + */ + public void sendBatchMsgByDepatmentId(String server,String url,String name,String status,String departmentId, String msgType,String msgContent){ + //Link 模板 + if(departmentId!=null) { + String format1 = StrFormatter.format(server + location, url); + FlyBookBatchMessage msg = new FlyBookBatchMessage(); + //msg.singleReceive(); + msg.setDepartment_ids(departmentId.split(",")); + String template = msg.getTemplate(); + String format = StrFormatter.format(template, msgContent, format1, DateUtil.now(), name, "", ""); + msg.setContent(format); + log.info("msgContent="+msg.getContent()); + flyBookMessageFeign.sendBatchMsg(msg); + } + } +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookTokenService.java b/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookTokenService.java new file mode 100644 index 0000000..459a224 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/flybook/FlyBookTokenService.java @@ -0,0 +1,28 @@ +package com.centricsoftware.integration.service.flybook; + +import com.centricsoftware.config.entity.CsProperties; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.integration.flybook.FlyBookAppAccessToken; +import com.centricsoftware.integration.flybook.FlyBookAppData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.centricsoftware.integration.feign.flybook.FlyBookTokenFeign; + +@Service +public class FlyBookTokenService { + @Autowired + FlyBookTokenFeign flyBookTokenFeign; + @Autowired + C8NodeService c8NodeService; + + public FlyBookAppAccessToken getToken(){ + CsProperties properties = c8NodeService.getProperties(); + String appid = properties.getPlm().get("cs.flybook.appid"); + String secret = properties.getPlm().get("cs.flybook.secret"); + FlyBookAppData flyBookAppData = new FlyBookAppData(); + flyBookAppData.setApp_id(appid); + flyBookAppData.setApp_secret(secret); + return flyBookTokenFeign.getAppAccessToken(flyBookAppData); + } + +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/inter/Initializable.java b/integration/src/main/java/com/centricsoftware/integration/service/inter/Initializable.java new file mode 100644 index 0000000..fb21af4 --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/inter/Initializable.java @@ -0,0 +1,8 @@ +package com.centricsoftware.integration.service.inter; + +/** + * 初始化接口 + */ +public interface Initializable { + public String getToken(); +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/inter/MsgAble.java b/integration/src/main/java/com/centricsoftware/integration/service/inter/MsgAble.java new file mode 100644 index 0000000..d40fd9f --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/inter/MsgAble.java @@ -0,0 +1,13 @@ +package com.centricsoftware.integration.service.inter; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.integration.dto.param.BaseParamDto; + +/** + * 赋予可发送消息的能力 + * + * @author jerry + */ +public interface MsgAble { + public ResEntity sendMsg(BaseParamDto baseDto); +} diff --git a/integration/src/main/java/com/centricsoftware/integration/service/wechat/WeChatService.java b/integration/src/main/java/com/centricsoftware/integration/service/wechat/WeChatService.java new file mode 100644 index 0000000..0198f0a --- /dev/null +++ b/integration/src/main/java/com/centricsoftware/integration/service/wechat/WeChatService.java @@ -0,0 +1,62 @@ +package com.centricsoftware.integration.service.wechat; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.enhancement.modules.c8.service.C8NodeService; +import com.centricsoftware.integration.dto.param.BaseParamDto; +import com.centricsoftware.integration.dto.wechat.WeChatDataDto; +import com.centricsoftware.integration.feign.wechat.WeChatFeign; +import com.centricsoftware.integration.service.inter.Initializable; +import com.centricsoftware.integration.service.inter.MsgAble; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Slf4j +@Service +public class WeChatService implements Initializable, MsgAble { + + @Resource + C8NodeService c8NodeService; + + @Resource + WeChatFeign weChatFeign; + + @Value("${cs.wechat.agentid}") + private String agentId; + + @Value("${cs.wechat.appkey}") + private String appid; + + @Value("${cs.wechat.secret}") + private String secret; + + + /** + * 获取Token 测试用,后续不开放此接口对外 + * 注意:企业微信的应用下面,需要对调用方的外网IP添加白名单,否则获取不到token + * + * @return 返回Token + */ + @Override + public String getToken() { + return weChatFeign.getToken(appid, secret); + } + + + @Override + public ResEntity sendMsg(BaseParamDto baseDto) { + try { + String token = getToken(); // TODO 每次发送都会请求TOKEN, 待优化 + JSONObject obj = JSONUtil.parseObj(token); + String realToken = obj.getStr("access_token"); + return WebResponse.success(weChatFeign.sendMessage(realToken, baseDto.getParam())); + } catch (Exception e) { + return WebResponse.failure(e.toString()); + } + } +} diff --git a/log.sql b/log.sql new file mode 100644 index 0000000..5efa262 --- /dev/null +++ b/log.sql @@ -0,0 +1,40 @@ + + +/****** + sqlserver Object: Table [dbo].[C8_PS_Operation_Log] + 操作日志+接口日志 +******/ +USE [C8] +GO +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +drop table [dbo].[C8_PS_Operation_Log] + +CREATE TABLE [dbo].[C8_PS_Operation_Log]( + [id] [int] IDENTITY(1,1) NOT NULL, + [refid] [nvarchar](50) NULL, + [name] [nvarchar](50) NULL, + [url] [nvarchar](150) NULL, + [data_key] [nvarchar](50) NULL, + [host] [nvarchar](50) NULL, + [path] [nvarchar](100) NULL, + [brand] [nvarchar](50) NULL, + [send_date] [datetime] NULL, + [end_date] [datetime] NULL, + [cost_ms] [int] NULL, + [return_code] [nvarchar](5) NULL, + [response] [ntext] NULL, + [debug] [ntext] NULL, + [method] [nvarchar](10) NULL, + [header] [nvarchar](1000) NULL, + [param] [nvarchar](1000) NULL, + [log_type] [nvarchar](20) NULL, + [success] [bit] NULL, +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + + diff --git a/maven-setting.xml b/maven-setting.xml new file mode 100644 index 0000000..a1b0e27 --- /dev/null +++ b/maven-setting.xml @@ -0,0 +1,42 @@ + + + + D:\mvn_repos + + + + + alimaven + central + aliyun maven + http://maven.aliyun.com/nexus/content/repositories/central/ + + + + nexus-aliyun + * + Nexus aliyun + http://maven.aliyun.com/nexus/content/groups/public + + + + repo1 + central + Human Readable Name for this Mirror. + http://repo1.maven.org/maven2/ + + + + + repo2 + central + Human Readable Name for this Mirror. + http://repo2.maven.org/maven2/ + + + + + + diff --git a/mybatis/.gitignore b/mybatis/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/mybatis/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/mybatis/.mvn/wrapper/MavenWrapperDownloader.java b/mybatis/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/mybatis/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/mybatis/.mvn/wrapper/maven-wrapper.jar b/mybatis/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/mybatis/.mvn/wrapper/maven-wrapper.jar differ diff --git a/mybatis/.mvn/wrapper/maven-wrapper.properties b/mybatis/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/mybatis/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/mybatis/pom.xml b/mybatis/pom.xml new file mode 100644 index 0000000..75429e6 --- /dev/null +++ b/mybatis/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + mybatis + 2.0 + + mybatis + Demo project for Spring Boot + + + UTF-8 + UTF-8 + 1.8 + 3.5.7 + + + + + org.springframework.boot + spring-boot-starter + true + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis.plus.version} + + + + + com.microsoft.sqlserver + mssql-jdbc + ${microsoft.sqlserver.version} + + + + com.oracle + ojdbc6 + + + + + + + + + + + + + org.projectlombok + lombok + true + + + com.centricsoftware + config + + + com.fasterxml.jackson.core + jackson-annotations + + + org.postgresql + postgresql + + + com.baomidou + dynamic-datasource-spring-boot-starter + 3.5.0 + + + + + + + + + + + + + + + + diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/config/MybatisPlusConfig.java b/mybatis/src/main/java/com/centricsoftware/mybatis/config/MybatisPlusConfig.java new file mode 100644 index 0000000..95ba1d9 --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/config/MybatisPlusConfig.java @@ -0,0 +1,77 @@ +package com.centricsoftware.mybatis.config; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.core.config.GlobalConfig; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * mybatis-plus 配置 + * + * @author zheng.gong + * @date 2020/4/20 + */ +@Slf4j +@Configuration +@MapperScan({"com.centricsoftware.core.mapper", "com.centricsoftware.mybatis.mapper", "com.centricsoftware.commons.mapper", "com.centricsoftware.enhancement.mapper","com.centricsoftware.enhancement.modules.demo.mapper"}) +@EnableTransactionManagement +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置最大分页数 + paginationInnerInterceptor.setMaxLimit(10000L); + // 是否对超过最大分页时做溢出处理 + paginationInnerInterceptor.setOverflow(true); + // 添加分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor); + return interceptor; + } + + /** + * 全局配置 + * + * @return GlobalConfig + */ + @Bean + public GlobalConfig globalConfiguration() { + + GlobalConfig.DbConfig conf = new GlobalConfig.DbConfig(); + + /* + AUTO|0:"数据库ID自增", + NONE|1:"无", + INPUT|2:"用户输入ID", + ID_WORKER|3:"TwitterSnowflake方案(数字)", + UUID|4:"全局唯一ID UUID"; + ID_WORKER_STR|5:"TwitterSnowflake方案(字符串)"; + */ + //#主键类型 + conf.setIdType(IdType.AUTO); + conf.setLogicDeleteValue("1"); + conf.setLogicNotDeleteValue("0"); + +// conf.setKeyGenerator(oracleKeyGenerator()); + GlobalConfig globalConfig = new GlobalConfig(); +// globalConfig.setMetaObjectHandler(new MyMetaObjectHandler()); + + /* globalConfig.setDatacenterId(sysProperties.getDataCenterId()); + globalConfig.setWorkerId(sysProperties.getWorkerId());*/ + globalConfig.setDbConfig(conf); + globalConfig.setBanner(false); + return globalConfig; + } + + + +} + diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/entity/PlmStyleEntity.java b/mybatis/src/main/java/com/centricsoftware/mybatis/entity/PlmStyleEntity.java new file mode 100644 index 0000000..18a7f9f --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/entity/PlmStyleEntity.java @@ -0,0 +1,156 @@ +package com.centricsoftware.mybatis.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +/** + * 款式实体demo + * @author zheng.gong + * @date 2020/4/20 + */ +@TableName("plm_style") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class PlmStyleEntity { + /** + * id + */ + @JsonProperty("id") + private Integer id; + + /** + * 图片 + */ + @JsonProperty("image_url") + private String imageUrl; + + /** + * 款号 + */ + @JsonProperty("style_no") + private String styleNo; + + /** + * 款式 + */ + @JsonProperty("style_name") + private String styleName; + + /** + * 品牌季节 + */ + @JsonProperty("season") + private String season; + + /** + * 上市波段 + */ + @JsonProperty("category1") + private String category1; + + /** + * 大类 + */ + @JsonProperty("large_category") + private String largeCategory; + + /** + * 小类 + */ + @JsonProperty("small_category") + private String smallCategory; + + /** + * 系列 + */ + @JsonProperty("proseries") + private String proseries; + + /** + * 主题类型 + */ + @JsonProperty("theme_type") + private String themeType; + + /** + * 系列主题 + */ + @JsonProperty("proseries_theme") + private String proseriesTheme; + + /** + * 特殊工艺 + */ + @JsonProperty("special_tec") + private String specialTec; + + /** + * 设计组 + */ + @JsonProperty("category2") + private String category2; + + /** + * 设计师 + */ + @JsonProperty("designe") + private String designe; + + /** + * 设计要求 + */ + @JsonProperty("design_req") + private String designReq; + + /** + * 吊牌价 + */ + @JsonProperty("price") + private String price; + + /** + * 销售量 + */ + @JsonProperty("total") + private String total; + + /** + * 销售额 + */ + @JsonProperty("accounts") + private String accounts; + + /** + * 创建 + */ + @JsonProperty("create_time") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private String createTime; + + /** + * 配色 + */ + @JsonProperty("color_image_url") + private String colorImageUrl; + + /** + * 状态 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonProperty("status") + private String status; + + /** + * 销售排名图片url + */ + @JsonProperty("rank_image_url") + private String rankImageUrl; + +} diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/handler/MyMetaObjectHandler.java b/mybatis/src/main/java/com/centricsoftware/mybatis/handler/MyMetaObjectHandler.java new file mode 100644 index 0000000..d77ace4 --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/handler/MyMetaObjectHandler.java @@ -0,0 +1,36 @@ +package com.centricsoftware.mybatis.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.ZoneId; + +/** + * 自动属性替换 + * @author ZhengGong + * @date 2019/9/16 + */ +@Component +@Slf4j +public class MyMetaObjectHandler implements MetaObjectHandler { + + + @Override + public void insertFill(MetaObject metaObject) { + log.info("start insert fill ...."); + // 起始版本 3.3.0(推荐使用) +// this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); + } + + @Override + public void updateFill(MetaObject metaObject) { + log.info("start update fill ...."); + // 起始版本 3.3.0(推荐使用) +// this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); + + } +} \ No newline at end of file diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/mapper/PlmStyleMapper.java b/mybatis/src/main/java/com/centricsoftware/mybatis/mapper/PlmStyleMapper.java new file mode 100644 index 0000000..4f535de --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/mapper/PlmStyleMapper.java @@ -0,0 +1,40 @@ +package com.centricsoftware.mybatis.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.centricsoftware.mybatis.entity.PlmStyleEntity; +import com.centricsoftware.mybatis.provider.PlmStyleSqlProvider; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.SelectProvider; +import org.apache.ibatis.annotations.Update; +import org.springframework.stereotype.Component; + +import java.util.List; + + +/** + * 测试款式mapper` + * @author ChangJiang + * @date 2020/5/6 + */ +@Component +public interface PlmStyleMapper extends BaseMapper { + /** + * 将删除的数据还原,用于开发测试 + */ + @Update("update plm_style set status=0") + void restore(); + + /** + * 动态查询 + * @param wrapper 动态查询条件 + * @return List + */ + @Select("select * from plm_style ${ew.customSqlSegment}") + List queryByDynamicCondition(@Param("ew") QueryWrapper wrapper); + + @SelectProvider(type= PlmStyleSqlProvider.class,method = "queryStyleById") + PlmStyleEntity queryByProvider(String styleName,String tableName); +} diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/provider/PlmStyleSqlProvider.java b/mybatis/src/main/java/com/centricsoftware/mybatis/provider/PlmStyleSqlProvider.java new file mode 100644 index 0000000..2e709f6 --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/provider/PlmStyleSqlProvider.java @@ -0,0 +1,41 @@ +package com.centricsoftware.mybatis.provider; + +import org.apache.ibatis.jdbc.SQL; + +/** + * 自定义sql生成器 + * @author zheng.gong + * @date 2020/5/6 + */ +public class PlmStyleSqlProvider { + + + public String queryStyleById(){ + SQL sql = new SQL(); + sql.SELECT("id,image_url,style_no,style_name") + .FROM("plm_style a") + .LEFT_OUTER_JOIN("#{styleName} b") + .WHERE("style_no='X1940002'") + .WHERE("style_name=#{styleName}"); + return sql.toString(); + } + + public String queryStyleById1(){ + SQL sql = new SQL(); + sql.SELECT("id,image_url,style_no,style_name") + .FROM("plm_style a") + .LEFT_OUTER_JOIN("#{styleName} b") + .WHERE("style_no='X1940002'") + .WHERE("style_name=#{styleName}"); + return sql.toString(); + } + public String queryStyleById2(){ + SQL sql = new SQL(); + sql.SELECT("id,image_url,style_no,style_name") + .FROM("plm_style a") + .LEFT_OUTER_JOIN("#{styleName} b") + .WHERE("style_no='X1940002'") + .WHERE("style_name=#{styleName}"); + return sql.toString(); + } +} diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/service/StyleService.java b/mybatis/src/main/java/com/centricsoftware/mybatis/service/StyleService.java new file mode 100644 index 0000000..5ebb6e8 --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/service/StyleService.java @@ -0,0 +1,7 @@ +package com.centricsoftware.mybatis.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.centricsoftware.mybatis.entity.PlmStyleEntity; + +public interface StyleService extends IService { +} diff --git a/mybatis/src/main/java/com/centricsoftware/mybatis/service/impl/StyleServiceImpl.java b/mybatis/src/main/java/com/centricsoftware/mybatis/service/impl/StyleServiceImpl.java new file mode 100644 index 0000000..b40406e --- /dev/null +++ b/mybatis/src/main/java/com/centricsoftware/mybatis/service/impl/StyleServiceImpl.java @@ -0,0 +1,11 @@ +package com.centricsoftware.mybatis.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.centricsoftware.mybatis.entity.PlmStyleEntity; +import com.centricsoftware.mybatis.mapper.PlmStyleMapper; +import com.centricsoftware.mybatis.service.StyleService; +import org.springframework.stereotype.Service; + +@Service +public class StyleServiceImpl extends ServiceImpl implements StyleService { +} diff --git a/mybatis/src/main/resources/mapper/PlmStyleEntityMapper.xml b/mybatis/src/main/resources/mapper/PlmStyleEntityMapper.xml new file mode 100644 index 0000000..81a68fb --- /dev/null +++ b/mybatis/src/main/resources/mapper/PlmStyleEntityMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..13f5a0d --- /dev/null +++ b/pom.xml @@ -0,0 +1,199 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + com.centricsoftware + plmservice + 2.0 + plm-service-http + Demo project for Spring Boot depend on http + + pom + + + core + config + commons + enhancement + redis + mybatis + sso + + + + + + UTF-8 + UTF-8 + 1.8 + 2.2.6.RELEASE + 5.4.1 + 28.1-jre + 2.0 + 2.1 + 2.1 + 0.11 + 0.10 + 2.0 + 2.0 + 2.0 + 2.0 + 3.8.1 + 11.2.0.3 + + 8.2.2.jre8 + 2.0.1 + 7.16.2 + 7.16.2 + 2.2.4 + 2.2.5.RELEASE + + + + + + + + + + + + + + + + com.microsoft.sqlserver + mssql-jdbc + ${microsoft.sqlserver.version} + + + com.oracle + ojdbc6 + ${ojdbc6.version} + + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + cn.hutool + hutool-all + ${hutool.version} + + + + com.google.guava + guava + ${guava.version} + + + com.centricsoftware + config + ${config.version} + + + com.centricsoftware + commons + ${commons.version} + + + com.centricsoftware + redis + ${redis.version} + + + com.centricsoftware + enhancement + ${enhancement.version} + + + com.centricsoftware + rabbitmq + ${rabbitmq.version} + + + com.centricsoftware + mybatis + ${mybatis.version} + + + com.centricsoftware + task + ${task.version} + + + com.centricsoftware + sso + ${sso.version} + + + com.aliyun.oss + aliyun-sdk-oss + 3.13.0 + + + + + + + + + + + + dev + + dev + + + true + + + + test + + test + + + false + + + + prod + + prod + + + false + + + + + + + central + https://maven.aliyun.com/repository/public + + + + + central + https://maven.aliyun.com/repository/public + + + + diff --git a/rabbitmq/.gitignore b/rabbitmq/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/rabbitmq/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/rabbitmq/.mvn/wrapper/MavenWrapperDownloader.java b/rabbitmq/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/rabbitmq/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/rabbitmq/.mvn/wrapper/maven-wrapper.jar b/rabbitmq/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/rabbitmq/.mvn/wrapper/maven-wrapper.jar differ diff --git a/rabbitmq/.mvn/wrapper/maven-wrapper.properties b/rabbitmq/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/rabbitmq/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/rabbitmq/plugins/rabbitmq_delayed_message_exchange-20171215-3.6.x.ez b/rabbitmq/plugins/rabbitmq_delayed_message_exchange-20171215-3.6.x.ez new file mode 100644 index 0000000..84e6e93 Binary files /dev/null and b/rabbitmq/plugins/rabbitmq_delayed_message_exchange-20171215-3.6.x.ez differ diff --git a/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.8.0.ez b/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.8.0.ez new file mode 100644 index 0000000..6332f5f Binary files /dev/null and b/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.8.0.ez differ diff --git a/rabbitmq/pom.xml b/rabbitmq/pom.xml new file mode 100644 index 0000000..dd13b5e --- /dev/null +++ b/rabbitmq/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + rabbitmq + 2.0 + + rabbitmq + Demo project for Spring Boot + + + UTF-8 + UTF-8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + org.projectlombok + lombok + true + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + + + + + + com.centricsoftware + config + + + org.elasticsearch + elasticsearch + + + + + + + diff --git a/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/config/RabbitMqConfig.java b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/config/RabbitMqConfig.java new file mode 100644 index 0000000..0833326 --- /dev/null +++ b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/config/RabbitMqConfig.java @@ -0,0 +1,95 @@ +package com.centricsoftware.rabbitmq.config; + +import com.centricsoftware.config.cons.Constants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * RabbitMQ配置,主要是配置队列,如果提前存在该队列,可以省略本配置类 + * @author zheng.gong + * @date 2020/4/17 + */ +@Slf4j +@Configuration +public class RabbitMqConfig { + + + @Bean + public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) { + connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.SIMPLE); + connectionFactory.setPublisherReturns(true); + RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause)); + return rabbitTemplate; + } +// +// @Bean +// public RabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory){ +// SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); +// factory.setConnectionFactory(connectionFactory); +//// factory.setMessageConverter(new Jackson2JsonMessageConverter()); +// +//// factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); +// return factory; +// } + + + + /** + * 直接模式队列1 + */ + @Bean + public Queue directOneQueue() { + return new Queue(Constants.RabbitConsts.DIRECT_MODE_QUEUE_ONE); + } +// +// /** +// * 队列2 +// */ +// @Bean +// public Queue queueTwo() { +// return new Queue(Constants.RabbitConsts.QUEUE_TWO); +// } +// +// /** +// * 队列3 +// */ +// @Bean +// public Queue queueThree() { +// return new Queue(Constants.RabbitConsts.QUEUE_THREE); +// } +// /** +// * 延迟队列 +// */ +// @Bean +// public Queue delayQueue() { +// return new Queue(Constants.RabbitConsts.DELAY_QUEUE, true); +// } +// +// /** +// * 延迟队列交换器, x-delayed-type 和 x-delayed-message 固定 +// */ +// @Bean +// public CustomExchange delayExchange() { +// Map args = Maps.newHashMap(); +// args.put("x-delayed-type", "direct"); +// return new CustomExchange(Constants.RabbitConsts.DELAY_MODE_QUEUE, "x-delayed-message", true, false, args); +// } + +// /** +// * 延迟队列绑定自定义交换器 +// * +// * @param delayQueue 队列 +// * @param delayExchange 延迟交换器 +// */ +// @Bean +// public Binding delayBinding(Queue delayQueue, CustomExchange delayExchange) { +// return BindingBuilder.bind(delayQueue).to(delayExchange).with(Constants.RabbitConsts.DELAY_QUEUE).noargs(); +// } + +} diff --git a/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DelayQueueHandler.java b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DelayQueueHandler.java new file mode 100644 index 0000000..02268c2 --- /dev/null +++ b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DelayQueueHandler.java @@ -0,0 +1,44 @@ +package com.centricsoftware.rabbitmq.handler; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import com.centricsoftware.config.cons.Constants; +import com.centricsoftware.rabbitmq.message.MessageStruct; +import com.rabbitmq.client.Channel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * 延迟队列处理 + * @author zheng.gong + * @date 2020/4/21 + */ +@Slf4j +@Component +@RabbitListener(queues = Constants.RabbitConsts.DELAY_QUEUE) +public class DelayQueueHandler { + + @RabbitHandler + public void directHandlerManualAck(MessageStruct messageStruct, Message message, Channel channel) { + // 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉 + final long deliveryTag = message.getMessageProperties().getDeliveryTag(); + try { + log.info("延迟队列,手动ACK,接收消息:{},时间:{}", JSONUtil.toJsonStr(messageStruct), DateUtil.now()); + // 通知 MQ 消息已被成功消费,可以ACK了 + channel.basicAck(deliveryTag, false); + //do something + } catch (IOException e) { + try { + // 处理失败,重新压入MQ + channel.basicRecover(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } +} diff --git a/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DirectQueueOneHandler.java b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DirectQueueOneHandler.java new file mode 100644 index 0000000..cc7844b --- /dev/null +++ b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/handler/DirectQueueOneHandler.java @@ -0,0 +1,52 @@ +package com.centricsoftware.rabbitmq.handler; + +import com.centricsoftware.config.cons.Constants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +/** + * 接队列1 处理器 + * @author zheng.gong + * @date 2020/4/17 + */ +@Slf4j +@Component +@RabbitListener(queues = Constants.RabbitConsts.DIRECT_MODE_QUEUE_ONE) +public class DirectQueueOneHandler { + + + + /** + * 如果 spring.rabbitmq.listener.direct.acknowledge-mode: auto,则可以用这个方式,会自动ack + */ + @RabbitHandler + public void directHandlerAutoAck(String messageStruct) { + log.info("直接队列处理器,接收消息:{}", messageStruct); + } + + /** + * 如果acknowledge-mode: manual ,则需要手动ack + * @param messageStruct 测试消息体 + * @param message 消息Object + * @param channel 信道 + */ +// @RabbitHandler +// public void directHandlerManualAck(String messageStruct, Message message, Channel channel) { +// // 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉 +// final long deliveryTag = message.getMessageProperties().getDeliveryTag(); +// try { +// log.info("直接队列1,手动ACK,接收消息:{}", JSONUtil.toJsonStr(messageStruct)); +// // 通知 MQ 消息已被成功消费,可以ACK了 +// channel.basicAck(deliveryTag, false); +// } catch (Exception e) { +// try { +// // 处理失败,重新压入MQ +// channel.basicRecover(); +// } catch (IOException e1) { +// e1.printStackTrace(); +// } +// } +// } +} diff --git a/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/message/MessageStruct.java b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/message/MessageStruct.java new file mode 100644 index 0000000..3aaf311 --- /dev/null +++ b/rabbitmq/src/main/java/com/centricsoftware/rabbitmq/message/MessageStruct.java @@ -0,0 +1,23 @@ +package com.centricsoftware.rabbitmq.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 测试消息体 + * @author zheng.gong + * @date 2020/4/17 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageStruct implements Serializable { + private static final long serialVersionUID = 392365881428311040L; + + private String message; +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8ebd7d8 --- /dev/null +++ b/readme.md @@ -0,0 +1,29 @@ +# 帮助文档 + 启动成功访问以下链接: + http://localhost:8088/plmservice/test/hello +### 1、maven 配置 + 请自行配置好idea的Maven,配置文件可以参考maven-setting.xml + 如果maven镜像是https,可以禁用idea的https证书校验: + -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true + +### 2、JDK版本 + 工程值在JDK8中测试过,高版本或者open-jdk请自行测试 + +### 3、此工程和SpringbootDev1区别 +#### 3.1 C8交互 + 与C8的交互采用okhttp客户端,登录通过拦截器实现自动登录 +#### 3.2 模块版本-预发布 + 后续各个模块将启用版本。后续对各模块的修改,将发布个新版本。 +#### 3.3 新增enhancement模块 + 该模块为C8功能强化模块,封装了日志、导入、导出、C8交互 +#### 3.4 删除NodeUtil.java + 后续的访问,请引入C8NodeService +#### 3.5 其他 + 引入C8Change、C8Delete、C8Search用于构建operation、Search; +### 更新日志 + 已将以下分支合并入2.2-dev分支 + - 2.1-cv-search、 + - 2.1-fix-修复DepPath多路径取值失败问题 + - 2.1-add-multiple-datasource + - 2.1-dev-Excel标准化导出-Joseph + - 2.1-dev-日志模块实现-Joseph \ No newline at end of file diff --git a/redis/.gitignore b/redis/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/redis/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/redis/.mvn/wrapper/MavenWrapperDownloader.java b/redis/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/redis/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/redis/.mvn/wrapper/maven-wrapper.jar b/redis/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/redis/.mvn/wrapper/maven-wrapper.jar differ diff --git a/redis/.mvn/wrapper/maven-wrapper.properties b/redis/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/redis/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/redis/pom.xml b/redis/pom.xml new file mode 100644 index 0000000..1dd9676 --- /dev/null +++ b/redis/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + redis + 2.0 + redis + Demo project for Spring Boot + + + + + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + org.springframework.boot + spring-boot-starter-json + + + + org.projectlombok + lombok + true + + + + com.centricsoftware + config + + + + + diff --git a/redis/src/main/java/com/centricsoftware/redis/component/RedisExecutor.java b/redis/src/main/java/com/centricsoftware/redis/component/RedisExecutor.java new file mode 100644 index 0000000..4167ddd --- /dev/null +++ b/redis/src/main/java/com/centricsoftware/redis/component/RedisExecutor.java @@ -0,0 +1,197 @@ +package com.centricsoftware.redis.component; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +/** + * redis api封装 + * @author zheng.gong + * @date 2020/4/21 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RedisExecutor { + + private final StringRedisTemplate redisTemplate; + + private int validTime; + /** + * 查询key,支持模糊查询 + * + * @param key 传过来时key的前后端已经加入了*,或者根据具体处理 + * */ + public Set keys(String key){ + return redisTemplate.keys(key); + } + + /** + * 字符串获取值 + * @param key redis key + * */ + public Object get(String key){ + return redisTemplate.opsForValue().get(key); + } + + /** + * 字符串存入值 + * 默认过期时间为2小时 + * @param key redis key + * */ + public void set(String key, String value){ + redisTemplate.opsForValue().set(key,value, 7200,TimeUnit.SECONDS); + } + + /** + * 字符串存入值 + * @param expire 过期时间(毫秒计) + * @param key redis key + * */ + public void set(String key, String value,Integer expire){ + redisTemplate.opsForValue().set(key,value, expire,TimeUnit.SECONDS); + } + + /** + * 删出key + * 这里跟下边deleteKey()最底层实现都是一样的,应该可以通用 + * @param key redis key + * */ + public void delete(String key){ + redisTemplate.opsForValue().getOperations().delete(key); + } + + /** + * 添加单个 + * 默认过期时间为两小时 + * @param key key + * @param filed filed + * @param domain 对象 + */ + public void hset(String key,String filed,Object domain){ + redisTemplate.opsForHash().put(key, filed, domain); + } + + /** + * 添加单个 + * @param key key + * @param filed filed + * @param domain 对象 + * @param expire 过期时间(毫秒计) + */ + public void hset(String key,String filed,Object domain,Integer expire){ + redisTemplate.opsForHash().put(key, filed, domain); + redisTemplate.expire(key, expire,TimeUnit.SECONDS); + } + + /** + * 添加HashMap + * + * @param key key + * @param hm 要存入的hash表 + */ + public void hset(String key, HashMap hm){ + redisTemplate.opsForHash().putAll(key,hm); + } + + /** + * 如果key存在就不覆盖 + * 如果变量值存在,在变量中可以添加不存在的的键值对,如果变量不存在,则新增一个变量,同时将键值对添加到该变量 + * @param key redis key + * @param filed new key + * @param domain value + */ + public void hsetAbsent(String key,String filed,Object domain){ + redisTemplate.opsForHash().putIfAbsent(key, filed, domain); + } + + /** + * 查询key和field所确定的值 + * + * @param key 查询的key + * @param field 查询的field + * @return HV + */ + public Object hget(String key,String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * 查询该key下所有值 + * + * @param key 查询的key + * @return Map + */ + public Object hget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 删除key下所有值 + * + * @param key 查询的key + */ + public void deleteKey(String key) { + redisTemplate.opsForHash().getOperations().delete(key); + } + + /** + * 判断key和field下是否有值 + * + * @param key 判断的key + * @param field 判断的field + */ + public Boolean hasKey(String key,String field) { + return redisTemplate.opsForHash().hasKey(key,field); + } + + /** + * 判断key下是否有值 + * + * @param key 判断的key + */ + public Boolean hasKey(String key) { + return redisTemplate.opsForHash().getOperations().hasKey(key); + } + + /** + * String类型设值并设置过期 + * @param k 键 + * @param v 值 + * @param l 时间 + * @param t 类型 + */ + public void set(String k,String v,long l,TimeUnit t){ + redisTemplate.opsForValue().set(k,v,l,t); + } + + /** + * 获取键的过期时间 + * @param k 键 + * @param t 时间类型 + * @return Long + */ + public Long getExpire(String k,TimeUnit t){ + return redisTemplate.opsForValue().getOperations().getExpire(k,t); + } + + /** + * 设置k,v当且仅当k不存在 + * @param k key + * @param v value + * @return Boolean + */ + public Boolean setNx(String k,String v){ + return redisTemplate.execute((RedisCallback) redisConnection -> { + RedisSerializer serializer = redisTemplate.getStringSerializer(); + return redisConnection.setNX(Objects.requireNonNull(serializer.serialize(k)), Objects.requireNonNull(serializer.serialize(v))); + }); + } + +} diff --git a/redis/src/main/java/com/centricsoftware/redis/config/RedisConfig.java b/redis/src/main/java/com/centricsoftware/redis/config/RedisConfig.java new file mode 100644 index 0000000..50b5b75 --- /dev/null +++ b/redis/src/main/java/com/centricsoftware/redis/config/RedisConfig.java @@ -0,0 +1,70 @@ +package com.centricsoftware.redis.config; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.*; + +import java.io.Serializable; + +/** + * RedisConfig + * @author zheng.gong + * @date 2020/4/17 + */ +@Configuration +@AutoConfigureAfter(RedisAutoConfiguration.class) +@EnableCaching +public class RedisConfig { + + + /** + * 默认情况下的模板只能支持RedisTemplate,也就是只能存入字符串,因此支持序列化 + */ + @Bean + public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + + /** + * 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式 + */ + @Bean + public CacheManager cacheManager(RedisConnectionFactory factory) { + // 配置序列化 + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); + RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); + + return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build(); + } + + @Bean + @SuppressWarnings("unchecked") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + RedisSerializer fastJsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.afterPropertiesSet(); + // 开启事务 + redisTemplate.setEnableTransactionSupport(false); + return redisTemplate; + } + +} + + + diff --git a/sso/.gitignore b/sso/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/sso/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/sso/pom.xml b/sso/pom.xml new file mode 100644 index 0000000..af116bb --- /dev/null +++ b/sso/pom.xml @@ -0,0 +1,37 @@ + + + + plmservice + com.centricsoftware + 2.0 + + 4.0.0 + sso + 0.11 + + + + com.centricsoftware + enhancement + 2.1 + + + + org.opensaml + opensaml + 2.6.4 + + + + dom4j + dom4j + 1.6.1 + + + + + + + diff --git a/sso/src/main/java/com/centricsoftware/sso/controller/SamlSSOController.java b/sso/src/main/java/com/centricsoftware/sso/controller/SamlSSOController.java new file mode 100644 index 0000000..9ac4dd2 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/controller/SamlSSOController.java @@ -0,0 +1,64 @@ +package com.centricsoftware.sso.controller; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import com.centricsoftware.sso.service.saml2.common.SAMLService; +import com.centricsoftware.sso.service.saml2.xml.RequestSAMLService; +import com.centricsoftware.sso.service.security.AuthenticationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.codec.binary.Base64; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.annotation.Resource; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.net.URLEncoder; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/11/26 15:55 + */ +@Controller +@Slf4j +public class SamlSSOController { + + @Resource + RequestSAMLService requestSAMLService; + + @Resource + AuthenticationService authenticationService; + + @Resource + SPMetaDataDto spMetaDataDto; + + @Resource + SAMLService samlService; + + @RequestMapping("/sso/idp") + public ResEntity login(HttpServletRequest request, HttpServletResponse response) throws Exception { + //鉴权,可重写实现 + String userId = authenticationService.authentication(request); + String responseSAML = requestSAMLService.getResponseSAML(spMetaDataDto, userId); + response.reset(); + //String redirect = "http://47.107.226.252/WebAccess/home.html#URL=C59445&Tab=CompetitiveStyles&NR=1"; + String redirect = "http://47.107.226.252/WebAccess/home.html#URL%3DC59445%26Tab%3DCompetitiveStyles%26NR%3D1"; // =号及后面的内容需要转义 + log.info(URLEncoder.encode(redirect).toString()); + Cookie cookie = new Cookie(SPMetaDataDto.IDP_COOKIE_KEY, URLEncoder.encode(redirect).toString()); + cookie.setPath("/"); + response.addCookie(cookie); + PrintWriter printWriter = response.getWriter(); + //响应转化成string型 + String form = samlService.getResponseForm(spMetaDataDto.getSpAssertionConsumerURL(), redirect, new Base64().encodeAsString(responseSAML.getBytes("utf-8"))); + log.debug("跳转的报文为:"); + log.debug(form); + printWriter.write(form); + printWriter.flush(); + printWriter.close(); + return null; + } + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/controller/TestSAMLController.java b/sso/src/main/java/com/centricsoftware/sso/controller/TestSAMLController.java new file mode 100644 index 0000000..9457d3b --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/controller/TestSAMLController.java @@ -0,0 +1,33 @@ +package com.centricsoftware.sso.controller; + +import com.centricsoftware.commons.dto.ResEntity; +import com.centricsoftware.commons.dto.WebResponse; +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import com.centricsoftware.sso.service.saml2.xml.RequestSAMLService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; + +@Slf4j +@RequestMapping("/sso") +@Controller +public class TestSAMLController { + + + @Resource + RequestSAMLService requestSAMLService; + /** + * 获取请求报文 + */ + @RequestMapping("/getRequestSAML") + @ResponseBody + public ResEntity getRequestSAML() throws Exception{ + String requestSAML = requestSAMLService.getRequestSAML(new SPMetaDataDto()); + return WebResponse.success("requestSAML:"+requestSAML); + } + + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/AuthorizationRequestDto.java b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/AuthorizationRequestDto.java new file mode 100644 index 0000000..96e4d3e --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/AuthorizationRequestDto.java @@ -0,0 +1,46 @@ +package com.centricsoftware.sso.dto.saml2.sp; + +import lombok.Data; + +/** + * description: 存储请求ID等信息 + * Author: Xulin.Xie + * Date: 2022/11/25 16:59 + */ +@Data +public class AuthorizationRequestDto { + + /** + * 请求ID + */ + private String requestId; + + + /** + * acs地址,即SAMLResponse返回的目标地址 + */ + private String assertionConsumerServiceUrl; + + /** + * 绑定方式 + */ + private String protocolBinding; + + /** + * sp entityId + */ + private String spIssuer; + + /** + * 未使用该参数 + */ + private String destination; + + /** + * 未使用该参数 + */ + private String version; + + + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/C8MetaDataDto.java b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/C8MetaDataDto.java new file mode 100644 index 0000000..7649da3 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/C8MetaDataDto.java @@ -0,0 +1,94 @@ +package com.centricsoftware.sso.dto.saml2.sp; + +import com.centricsoftware.commons.utils.SpringContextHolder; +import com.centricsoftware.config.entity.CsProperties; +import lombok.Data; +import org.opensaml.xml.security.x509.BasicX509Credential; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; +import java.io.InputStream; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.Collections; + +/** + * description:PLM MetaData 信息 + * Author: Xulin.Xie + * Date: 2022/11/25 13:10 + */ +@Data +@Configuration +public class C8MetaDataDto extends SPMetaDataDto{ + + + /** + * PLM IP + */ + @Value("${cs.plm.http}") + private String host; + + + + /** + * 需要初始化证书等相关信息 + */ + public C8MetaDataDto(){ + CsProperties csProperties = SpringContextHolder.getBean(CsProperties.class); + super.setKeyStorePath(csProperties.getValue("sso.keystore")); + super.setKeyStorePwd(csProperties.getValue("sso.keystore-pwd")); + super.setKeyStoreAlias(csProperties.getValue("sso.alias")); + try { + XMLSignatureFactory factory = XMLSignatureFactory.getInstance(); + KeyStore result = KeyStore.getInstance (KeyStore.getDefaultType ()); + setKeyStore(result); + InputStream keyStoreStream = C8MetaDataDto.class.getResourceAsStream(getKeyStorePath()); + result.load (keyStoreStream, getKeyStorePwd().toCharArray ()); + KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) getKeyStore().getEntry (getKeyStoreAlias(), new KeyStore.PasswordProtection(getKeyStorePwd().toCharArray ())); + setKeyPair(new KeyPair(entry.getCertificate ().getPublicKey (), entry.getPrivateKey ())); + KeyInfoFactory kFactory = factory.getKeyInfoFactory (); + setKeyInfo(kFactory.newKeyInfo(Collections.singletonList (kFactory.newX509Data(Collections.singletonList (entry.getCertificate ()))))); + X509Certificate certificate = (X509Certificate) entry.getCertificate(); + BasicX509Credential credential = new BasicX509Credential(); + credential.setEntityCertificate(certificate); + credential.setPrivateKey(entry.getPrivateKey()); + setCredential(credential); + } + catch (Exception ex) { + throw new RuntimeException("生成签名失败:",ex); + } + } + + /** + * C8配置的 SSOIdentityProviderServiceURL + */ + private String idpServiceURL = "http://localhost:8080/idp/sso"; + + /** + * C8配置的 SSOIdentityProviderEntityId + */ + private String idpEntityId = "http://localhost:8080/idp"; + + /** + * C8配置的 SSOServiceProviderAssertionConsumerURL + */ + private String spAssertionConsumerURL; + + /** + * C8配置的 SSOServiceProviderEntityId + */ + private String spEntityId; + + public String getSpAssertionConsumerURL(){ + return "http://"+host+"/csi-requesthandler/sso/sp"; + } + + public String getSpEntityId(){ + return "http://"+ host+"/SSO"; + } + + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/SPMetaDataDto.java b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/SPMetaDataDto.java new file mode 100644 index 0000000..66b91b6 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/dto/saml2/sp/SPMetaDataDto.java @@ -0,0 +1,103 @@ +package com.centricsoftware.sso.dto.saml2.sp; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opensaml.xml.security.x509.BasicX509Credential; + +import javax.xml.crypto.dsig.keyinfo.KeyInfo; +import javax.xml.namespace.QName; +import java.security.KeyPair; +import java.security.KeyStore; + +/** + * description:SP MetaData 信息 + * Author: Xulin.Xie + * Date: 2022/11/25 13:07 + */ +@Data +@NoArgsConstructor +public class SPMetaDataDto{ + + + /** + * C8配置的 SSOIdentityProviderServiceURL + */ + private String idpServiceURL; + + /** + * C8配置的 SSOIdentityProviderEntityId + */ + private String idpEntityId; + + /** + * C8配置的 SSOServiceProviderAssertionConsumerURL + */ + private String spAssertionConsumerURL; + + /** + * C8配置的 SSOServiceProviderEntityId + */ + private String spEntityId; + + /** + * ====================证书相关======================= + */ + + /** + * 证书位置 + */ + private String keyStorePath; + + /** + * 证书密码 + */ + private String keyStorePwd; + + /** + * 证书别名 + */ + private String keyStoreAlias; + + private KeyStore keyStore; + + /** + * 密钥对 + */ + private KeyPair keyPair; + + private KeyInfo keyInfo; + /** + * 基本X 509凭证 + */ + private BasicX509Credential credential; + + + /** + * 初始化参数,正常情况下不需要修改这些默认参数;由于默认参数太多,并不是所有的都提取到实体类做成可配置项;如发现有需要修改的默认参数不能在这里配置的; + * 需要重写对应的接口实现类; + */ + + public static final QName DEFAULT_ELEMENT_NAME = new QName("urn:oasis:names:tc:SAML:2.0:protocol", "AuthnRequest", "saml2p"); + + public static final String SAML2_POST_BINDING_URI = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; + + public static final QName DEFAULT_ELEMENT_NAME_Issuer = new QName("urn:oasis:names:tc:SAML:2.0:assertion", "Issuer", "saml2"); + + public static final QName DEFAULT_ELEMENT_NAME_NameID = new QName("urn:oasis:names:tc:SAML:2.0:protocol", "NameIDPolicy", "saml2p"); + + public static final String UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"; + + public static final String STANDALONE = "standalone"; + + public static final String NAME_ID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; + + public static final String CONFIRMATION_METHOD = "bearer"; + + public static final String IDP_COOKIE_KEY = "redirect"; + /** + * idp 端 cookie的value + */ + public static final String IDP_COOKIE_VALUE = "idp_cookie_value111"; + + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/AssertionService.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/AssertionService.java new file mode 100644 index 0000000..eacc630 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/AssertionService.java @@ -0,0 +1,9 @@ +package com.centricsoftware.sso.service.saml2.common; + +import org.opensaml.saml2.core.Assertion; + +public interface AssertionService { + + public Assertion createStockAuthnAssertion (String idpEntityId, String assertionId, String spEntityId); + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLService.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLService.java new file mode 100644 index 0000000..d17c13b --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLService.java @@ -0,0 +1,44 @@ +package com.centricsoftware.sso.service.saml2.common; + +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import org.opensaml.saml2.core.Assertion; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.Subject; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.io.MarshallingException; +import org.w3c.dom.Document; + +import javax.xml.namespace.QName; + +/** + * description:Utility that uses OpenSAML to carry out common SAML tasks. + * Author: Xulin.Xie + * Date: 2022/11/25 13:46 + */ +public interface SAMLService { + + /** + * easier way to create objects using OpenSAML's builder system. + * cast to SAMLObjectBuilder is caller's choice + */ + @SuppressWarnings ("unchecked") + public T create (Class cls, QName qname); + + /** + Helper method to get an XMLObject as a DOM Document. + 获取XMLObject作为DOM文档的Helper方法。 + */ + public Document asDOMDocument (XMLObject object) throws MarshallingException; + + /** + * 创建Subject + * @param userId 登录名 + * @return + */ + public Subject createSubject(String userId, String format, String confirmationMethod, String recipient); + + Response createResponse (SPMetaDataDto mataData,Assertion assertion, String inResponseTo); + + + String getResponseForm(String idpServiceURL, String redirect, String response) throws Exception; +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLSignature.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLSignature.java new file mode 100644 index 0000000..3b40cc7 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/SAMLSignature.java @@ -0,0 +1,14 @@ +package com.centricsoftware.sso.service.saml2.common; + +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dsig.XMLSignatureException; +import java.security.GeneralSecurityException; + +public interface SAMLSignature { + void signSAMLObject (SPMetaDataDto spMetaDataDto, Document doc, String referenceId, Node signNode) + throws GeneralSecurityException, XMLSignatureException, MarshalException; +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/AssertionServiceImpl.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/AssertionServiceImpl.java new file mode 100644 index 0000000..856a4a8 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/AssertionServiceImpl.java @@ -0,0 +1,63 @@ +package com.centricsoftware.sso.service.saml2.common.impl; + +import com.centricsoftware.sso.service.saml2.common.AssertionService; +import com.centricsoftware.sso.service.saml2.common.SAMLService; +import org.joda.time.DateTime; +import org.opensaml.saml2.core.*; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** +Simple examples of coding to the OpenSAML API. +Methods here can write and parse each of the three +main assertion types: authentication, authorization decision, and attributes. + 编码到OpenSAML API的简单示例。这里的方法可以编写和解析以下三种主要的断言类型: + 身份验证,授权决策和属性。 +*/ +@Service +public class AssertionServiceImpl implements AssertionService { + + @Resource + SAMLService samlService; + + /** + Creates a file whose contents are a SAML authentication assertion. + 创建一个内容为SAML身份验证断言的文件。 + */ + @Override + public Assertion createStockAuthnAssertion (String idpEntityId,String assertionId,String spEntityId) { + DateTime now = new DateTime (); + Issuer issuer = samlService.create (Issuer.class, Issuer.DEFAULT_ELEMENT_NAME); + issuer.setValue (idpEntityId); + issuer.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity"); + Conditions conditions = samlService.create + (Conditions.class, Conditions.DEFAULT_ELEMENT_NAME); + conditions.setNotBefore (now.minusSeconds (15)); + conditions.setNotOnOrAfter (now.plusSeconds (30)); + AudienceRestriction audienceRestriction = samlService.create(AudienceRestriction.class,AudienceRestriction.DEFAULT_ELEMENT_NAME); + Audience audience = samlService.create(Audience.class,Audience.DEFAULT_ELEMENT_NAME); + audience.setAudienceURI(spEntityId); + audienceRestriction.getAudiences().add(audience); + conditions.getAudienceRestrictions().add(audienceRestriction); + AuthnContextClassRef ref = samlService.create (AuthnContextClassRef.class, + AuthnContextClassRef.DEFAULT_ELEMENT_NAME); + ref.setAuthnContextClassRef (AuthnContext.PPT_AUTHN_CTX); + AuthnContext authnContext = samlService.create + (AuthnContext.class, AuthnContext.DEFAULT_ELEMENT_NAME); + authnContext.setAuthnContextClassRef (ref); + AuthnStatement authnStatement = samlService.create + (AuthnStatement.class, AuthnStatement.DEFAULT_ELEMENT_NAME); + authnStatement.setAuthnContext (authnContext); + authnStatement.setAuthnInstant(now); + Assertion assertion = + samlService.create (Assertion.class, Assertion.DEFAULT_ELEMENT_NAME); + assertion.setID (assertionId); + assertion.setIssueInstant (now); + assertion.setIssuer (issuer); + assertion.getStatements ().add (authnStatement); + assertion.setConditions(conditions); + return assertion; + } + +} \ No newline at end of file diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLServiceImpl.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLServiceImpl.java new file mode 100644 index 0000000..06ccf2c --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLServiceImpl.java @@ -0,0 +1,181 @@ +package com.centricsoftware.sso.service.saml2.common.impl; + +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import com.centricsoftware.sso.service.saml2.common.SAMLService; +import org.joda.time.DateTime; +import org.opensaml.DefaultBootstrap; +import org.opensaml.common.impl.SecureRandomIdentifierGenerator; +import org.opensaml.saml2.core.*; +import org.opensaml.xml.Configuration; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.XMLObjectBuilder; +import org.opensaml.xml.io.Marshaller; +import org.opensaml.xml.io.MarshallingException; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.net.URLEncoder; + +/** + * description:Utility that uses OpenSAML to carry out common SAML tasks. + * Author: Xulin.Xie + * Date: 2022/11/25 13:46 + */ +@Service +public class SAMLServiceImpl implements SAMLService { + + private static final String CM_PREFIX = "urn:oasis:names:tc:SAML:2.0:cm:"; + + private DocumentBuilder documentBuilder; + + private SecureRandomIdentifierGenerator generator; + + public SAMLServiceImpl() throws Exception { + DefaultBootstrap.bootstrap (); + generator = new SecureRandomIdentifierGenerator(); + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance (); + factory.setNamespaceAware (true); + documentBuilder = factory.newDocumentBuilder (); + } + + /** + * easier way to create objects using OpenSAML's builder system. + * cast to SAMLObjectBuilder is caller's choice + */ + @SuppressWarnings ("unchecked") + @Override + public T create (Class cls, QName qname) + { + return (T) ((XMLObjectBuilder) + Configuration.getBuilderFactory ().getBuilder (qname)) + .buildObject (qname); + } + + @Override + public Document asDOMDocument(XMLObject object) throws MarshallingException { + Document document = documentBuilder.newDocument (); + Marshaller out = + Configuration.getMarshallerFactory ().getMarshaller (object); + out.marshall (object, document); + return document; + } + + /** + Helper method to get an XMLObject as a DOM Document. + 获取XMLObject作为DOM文档的Helper方法。 + */ + + + /** + Returns a SAML subject. + @param userId The subject name + @param format If non-null, we'll set as the subject name format + @param confirmationMethod If non-null, we'll create a SubjectConfirmation + element and use this as the Method attribute; must be "sender-vouches" + or "bearer", as HOK would require additional parameters and so is NYI + */ + @Override + public Subject createSubject(String userId, String format, String confirmationMethod,String recipient){ + NameID nameID = create (NameID.class, NameID.DEFAULT_ELEMENT_NAME); + nameID.setValue (userId); + if (format != null) nameID.setFormat (format); + Subject subject = create (Subject.class, Subject.DEFAULT_ELEMENT_NAME); + subject.setNameID (nameID); + if (confirmationMethod != null) { + SubjectConfirmation confirmation = create(SubjectConfirmation.class, SubjectConfirmation.DEFAULT_ELEMENT_NAME); + confirmation.setMethod (CM_PREFIX + confirmationMethod); + SubjectConfirmationData confirmationData = create(SubjectConfirmationData.class,SubjectConfirmationData.DEFAULT_ELEMENT_NAME); + confirmationData.setRecipient(recipient); + confirmation.setSubjectConfirmationData(confirmationData); + subject.getSubjectConfirmations ().add (confirmation); + } + return subject; + } + + /** + Helper method to generate a response, based on a pre-built assertion + and query ID. + 根据预先建立的断言和查询ID生成响应的Helper方法。 + */ + @Override + public Response createResponse(SPMetaDataDto mataData,Assertion assertion, String inResponseTo) { + Response response = createResponse (mataData,StatusCode.SUCCESS_URI, inResponseTo); + response.getAssertions ().add (assertion); + return response; + } + @Override + public String getResponseForm(String idpServiceURL, String redirect, String response) throws Exception{ + String encode = URLEncoder.encode(redirect); + encode = URLEncoder.encode(encode); + return "\n" + + "\n" + + "\n" + + "POST data\n" + + "\n" + + "\n" + + "\n" + + "\t
\n" + + "\t\t
\n" + + "\t\t
\n" + + "\t\t\n" + + "\t
\n" + + "\n" + + ""; + } + + + public Response createResponse (SPMetaDataDto mataData,String statusCode, String inResponseTo) { + return createResponse (mataData,statusCode, null, inResponseTo); + } + + /** + Helper method to generate a shell response with a given status code, + status message, and query ID. + 使用给定状态代码,状态消息和查询ID生成Shell响应的Helper方法。 + */ + public Response createResponse(SPMetaDataDto mataData,String statusCode, String message, String inResponseTo) { + Response response = create + (Response.class, Response.DEFAULT_ELEMENT_NAME); + response.setID (generator.generateIdentifier ()); + if (inResponseTo != null) response.setInResponseTo (inResponseTo); + DateTime now = new DateTime (); + response.setIssueInstant (now); + if (mataData.getIdpEntityId() != null) + response.setIssuer (spawnIssuer(mataData)); + StatusCode statusCodeElement = create + (StatusCode.class, StatusCode.DEFAULT_ELEMENT_NAME); + statusCodeElement.setValue (statusCode); + Status status = create (Status.class, Status.DEFAULT_ELEMENT_NAME); + status.setStatusCode (statusCodeElement); + response.setStatus (status); + if (message != null) { + StatusMessage statusMessage = create + (StatusMessage.class, StatusMessage.DEFAULT_ELEMENT_NAME); + statusMessage.setMessage (message); + status.setStatusMessage (statusMessage); + } + return response; + } + + /** + Helper method to spawn a new Issuer element based on our issuer URL. + 根据我们的发行者URL生成新的Issuer元素的Helper方法。 + */ + public Issuer spawnIssuer(SPMetaDataDto mataData) { + String idpEntityId = mataData.getIdpEntityId(); + Issuer result = null; + if (idpEntityId != null) { + result = create (Issuer.class, Issuer.DEFAULT_ELEMENT_NAME); + result.setValue (idpEntityId); + } + return result; + } + + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLSignatureImpl.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLSignatureImpl.java new file mode 100644 index 0000000..e8eea12 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/common/impl/SAMLSignatureImpl.java @@ -0,0 +1,90 @@ +package com.centricsoftware.sso.service.saml2.common.impl; + + +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import com.centricsoftware.sso.service.saml2.common.SAMLSignature; +import org.opensaml.xml.signature.SignatureConstants; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dsig.*; +import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +/** +Utility for signing SAML DOM objects (assertions, requests, and responses) +and for validating and checking signatures on SAML DOM objects. +Unlike the rest of this package, this utility does not rely on OpenSAML; +it operates directly on DOM trees. (There is an import of OpenSAML's +XMLObject type, but that's just for our main method, which in turn is just +for testing purposes.) + 用于签名SAML DOM对象(断言,请求和响应)以及验证和检查SAML DOM对象上的签名的实用程序。与该软件包的其余部分不同,该实用程序不依赖于OpenSAML。 + 它直接在DOM树上运行。 (导入了OpenSAML的XMLObject类型,但这仅用于我们的主要方法,而这又仅用于测试目的 +*/ +@Service +public class SAMLSignatureImpl implements SAMLSignature { + + + + /** + * 签名SAML对象 + * @param doc + * @param referenceId + * @param signNode + * @throws GeneralSecurityException + * @throws XMLSignatureException + * @throws MarshalException + */ + @Override + public void signSAMLObject(SPMetaDataDto spMetaDataDto, Document doc, String referenceId, Node signNode) + throws GeneralSecurityException, XMLSignatureException, MarshalException { + List transforms = new ArrayList(2); + XMLSignatureFactory factory = XMLSignatureFactory.getInstance(); + transforms.add(factory.newTransform(SignatureConstants.TRANSFORM_ENVELOPED_SIGNATURE, (TransformParameterSpec) null)); + transforms.add(factory.newTransform(SignatureConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (TransformParameterSpec) null)); + Reference ref = factory.newReference("#" + referenceId, + factory.newDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA1, null), + transforms, null, null); + SignedInfo signedInfo = factory.newSignedInfo + (factory.newCanonicalizationMethod + (SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS, + (C14NMethodParameterSpec) null), + factory.newSignatureMethod(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1, null), + Collections.singletonList(ref)); + XMLSignature signature = factory.newXMLSignature(signedInfo, spMetaDataDto.getKeyInfo()); + DOMSignContext signContext = new DOMSignContext (spMetaDataDto.getKeyPair().getPrivate(), signNode); + Element element = (Element) doc.getElementsByTagName("saml2:Assertion").item(0); + element.setIdAttribute("ID", true); + signContext.setIdAttributeNS(element, null, "ID"); + signature.sign(signContext); + + Node signatureElement = signNode.getLastChild (); + + boolean foundIssuer = false; + Node elementAfterIssuer = null; + NodeList children = signNode.getChildNodes (); + for (int c = 0; c < children.getLength (); ++c) { + Node child = children.item (c); + if (foundIssuer) { + elementAfterIssuer = child; + break; + } + if (child.getNodeType () == Node.ELEMENT_NODE && child.getLocalName ().equals ("Issuer")) foundIssuer = true; + } + if (!foundIssuer || elementAfterIssuer != null) { + signNode.removeChild (signatureElement); + signNode.insertBefore (signatureElement, foundIssuer ? elementAfterIssuer : signNode.getFirstChild ()); + } + } + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/RequestSAMLService.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/RequestSAMLService.java new file mode 100644 index 0000000..2ef2e09 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/RequestSAMLService.java @@ -0,0 +1,14 @@ +package com.centricsoftware.sso.service.saml2.xml; + +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; + +public interface RequestSAMLService { + + /** + * 获取请求报文 + **/ + String getRequestSAML(SPMetaDataDto sp) throws Exception; + + + String getResponseSAML(SPMetaDataDto mataData, String userId) throws Exception; +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/impl/RequestSAMLServiceImpl.java b/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/impl/RequestSAMLServiceImpl.java new file mode 100644 index 0000000..9db82fa --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/saml2/xml/impl/RequestSAMLServiceImpl.java @@ -0,0 +1,203 @@ +package com.centricsoftware.sso.service.saml2.xml.impl; + +import com.centricsoftware.sso.dto.saml2.sp.AuthorizationRequestDto; +import com.centricsoftware.sso.dto.saml2.sp.SPMetaDataDto; +import com.centricsoftware.sso.service.saml2.common.AssertionService; +import com.centricsoftware.sso.service.saml2.common.SAMLService; +import com.centricsoftware.sso.service.saml2.common.SAMLSignature; +import com.centricsoftware.sso.service.saml2.xml.RequestSAMLService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.saml2.core.*; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; + +import javax.annotation.Resource; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import java.util.Iterator; +import java.util.UUID; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/11/25 13:09 + */ +@Slf4j +@Service +public class RequestSAMLServiceImpl implements RequestSAMLService { + + /** + * 快速生产SAML报文服务 + */ + @Resource + SAMLService samlService; + + @Resource + AssertionService assertionService; + + @Resource + SAMLSignature samlSignature; + + + @Override + public String getRequestSAML(SPMetaDataDto sp) throws Exception{ + AuthnRequest authnRequest = createAuthnRequest(sp); + Document document = samlService.asDOMDocument(authnRequest); + DOMSource source=new DOMSource(document); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer former=tf.newTransformer(); + former.setOutputProperty(SPMetaDataDto.STANDALONE, "yes"); + StringWriter sw = new StringWriter(); + StreamResult sr = new StreamResult(sw); + former.transform(source, sr); + return sw.toString(); + } + + /** + * 获取响应报文 + */ + @Override + public String getResponseSAML(SPMetaDataDto mataData, String userId) throws Exception{ + String requestSAML = getRequestSAML(mataData); + String authnRequestXml = decodeRequestSAML(requestSAML,false); + AuthorizationRequestDto authnRequestField = readeAuthnRequest(authnRequestXml); + return generateSamlResponse(mataData,userId,authnRequestField); + } + + public String generateSamlResponse(SPMetaDataDto mataData, String userId, AuthorizationRequestDto requestField) throws Exception { + //创建Subject + String spEntityId = mataData.getSpEntityId(); + Subject subject = samlService.createSubject(userId, SPMetaDataDto.NAME_ID_PERSISTENT,SPMetaDataDto.CONFIRMATION_METHOD,mataData.getSpAssertionConsumerURL()); + NameID nameID = subject.getNameID(); + nameID.setNameQualifier(spEntityId); + nameID.setSPNameQualifier(spEntityId); + //创建断言Assertion + String assertionId = UUID.randomUUID().toString(); + Assertion assertion = assertionService.createStockAuthnAssertion(mataData.getIdpEntityId(),assertionId,spEntityId); + Issuer issuer = (Issuer) Configuration.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME) + .buildObject(Issuer.DEFAULT_ELEMENT_NAME); + issuer.setValue (mataData.getIdpEntityId()); + assertion.setIssuer(issuer); + assertion.setSubject(subject); + //创建response + Response response = samlService.createResponse(mataData,assertion,requestField.getRequestId()); + //签名 + Document document = samlService.asDOMDocument(response); + log.debug("生成的签名:Document为"); + log.debug(document.toString()); +// samlSignature.signSAMLObject(mataData,document,assertionId, document.getElementsByTagName("saml:Assertion").item(0)); + samlSignature.signSAMLObject(mataData,document,assertionId, document.getElementsByTagName("saml2:Assertion").item(0)); + DOMSource source=new DOMSource(document); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer former=tf.newTransformer(); + former.setOutputProperty(OutputKeys.STANDALONE, "yes"); + StringWriter sw = new StringWriter(); + StreamResult sr = new StreamResult(sw); + former.transform(source, sr); + String result=sw.toString(); + log.debug("Response SAML:"); + log.debug(result); + return result; + } + + /** + * 根据请求报文提取相关信息 + * @param authnRequestXml + */ + private AuthorizationRequestDto readeAuthnRequest(String authnRequestXml) throws Exception{ + if(authnRequestXml == null){ + return null; + } + AuthorizationRequestDto authnRequestField = new AuthorizationRequestDto(); + org.dom4j.Document doc = null; + doc = DocumentHelper.parseText(authnRequestXml); // 将字符串转为XML + Element rootElt = doc.getRootElement(); // 获取根节点 + String version = rootElt.attributeValue("Version"); + String ID = rootElt.attributeValue("ID"); + String destination = rootElt.attributeValue("Destination"); + String assertionConsumerServiceUrl = rootElt.attributeValue("AssertionConsumerServiceURL"); + String protocolBinding = rootElt.attributeValue("ProtocolBinding"); + Iterator elementIterator = rootElt.elementIterator(); + while (elementIterator.hasNext()){ + Element element = elementIterator.next(); + if("Issuer".equals(element.getName())){ + authnRequestField.setSpIssuer(element.getTextTrim()); + break; + } + } + authnRequestField.setVersion(version); + authnRequestField.setRequestId(ID); + authnRequestField.setDestination(destination); + authnRequestField.setAssertionConsumerServiceUrl(assertionConsumerServiceUrl); + authnRequestField.setProtocolBinding(protocolBinding); + return authnRequestField; + } + + /** + * 解析请求报文 + * @param encSAMLRequest 报文 + * @param isDecode true表示需要对encSAMLRequest进行解码;和C8的sso,因为请求报文是本工程自己生成的,没经过网络,所以请求报文并没有编码,无需解码; + * @return + */ + private String decodeRequestSAML(String encSAMLRequest,boolean isDecode) throws Exception{ + if(!isDecode) return encSAMLRequest; + String ret = null; + byte[] decodedBytes = null; + decodedBytes = new Base64().decode(encSAMLRequest.getBytes("UTF-8")); + return new String(inflate(decodedBytes, true)); + } + + private byte[] inflate(byte[] bytes, boolean nowrap) throws Exception { + Inflater decompressor = null; + try(ByteArrayOutputStream out = new ByteArrayOutputStream(); + InflaterInputStream decompressorStream = new InflaterInputStream(new ByteArrayInputStream(bytes), decompressor); + ) + { + decompressor = new Inflater(nowrap); + byte[] buf = new byte[1024]; + int count; + while ((count = decompressorStream.read(buf)) != -1) { + out.write(buf, 0, count); + } + return out.toByteArray(); + } finally { + if (decompressor != null) { + decompressor.end(); + } + } + } + + /** + * 创建AutheRequest对象 + */ + private AuthnRequest createAuthnRequest(SPMetaDataDto mataData){ + AuthnRequest authnRequest = samlService.create(AuthnRequest.class, SPMetaDataDto.DEFAULT_ELEMENT_NAME); + authnRequest.setIssueInstant(new DateTime()); + authnRequest.setDestination(mataData.getIdpServiceURL()); + authnRequest.setProtocolBinding(SPMetaDataDto.SAML2_POST_BINDING_URI); + authnRequest.setID(UUID.randomUUID().toString()); + authnRequest.setAssertionConsumerServiceURL(mataData.getSpAssertionConsumerURL()); + Issuer issuer = samlService.create(Issuer.class, SPMetaDataDto.DEFAULT_ELEMENT_NAME_Issuer); + issuer.setValue(mataData.getSpEntityId()); + authnRequest.setIssuer(issuer); + NameIDPolicy nameIDPolicy = samlService.create(NameIDPolicy.class,SPMetaDataDto.DEFAULT_ELEMENT_NAME_NameID); + nameIDPolicy.setAllowCreate(true); + nameIDPolicy.setFormat(SPMetaDataDto.UNSPECIFIED); + authnRequest.setNameIDPolicy(nameIDPolicy); + return authnRequest; + } + +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/security/AuthenticationService.java b/sso/src/main/java/com/centricsoftware/sso/service/security/AuthenticationService.java new file mode 100644 index 0000000..f7b9227 --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/security/AuthenticationService.java @@ -0,0 +1,20 @@ +package com.centricsoftware.sso.service.security; + +import javax.servlet.http.HttpServletRequest; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/11/26 15:58 + * + * @return + */ +public interface AuthenticationService { + + /** + * 鉴权成功,返回UserId;失败则抛出异常 + * @param request + * @return + */ + String authentication(HttpServletRequest request); +} diff --git a/sso/src/main/java/com/centricsoftware/sso/service/security/impl/AuthenticationServiceImpl.java b/sso/src/main/java/com/centricsoftware/sso/service/security/impl/AuthenticationServiceImpl.java new file mode 100644 index 0000000..bb554bd --- /dev/null +++ b/sso/src/main/java/com/centricsoftware/sso/service/security/impl/AuthenticationServiceImpl.java @@ -0,0 +1,27 @@ +package com.centricsoftware.sso.service.security.impl; + +import com.centricsoftware.sso.service.security.AuthenticationService; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/** + * description: + * Author: Xulin.Xie + * Date: 2022/11/26 15:58 + * + * @return + */ +@Service +public class AuthenticationServiceImpl implements AuthenticationService { + /** + * 鉴权 + * @param request + */ + @Override + public String authentication(HttpServletRequest request) { + return "96262"; + } + + +} diff --git a/sso/src/main/resources/SPKeystore.jks b/sso/src/main/resources/SPKeystore.jks new file mode 100644 index 0000000..36432ba Binary files /dev/null and b/sso/src/main/resources/SPKeystore.jks differ diff --git a/sso/src/main/resources/application-sso.yml b/sso/src/main/resources/application-sso.yml new file mode 100644 index 0000000..8c66cc0 --- /dev/null +++ b/sso/src/main/resources/application-sso.yml @@ -0,0 +1,37 @@ +####################### SSO 证书等相关信息################### +spring: + profiles: dev +cs: + plm: + sso: + #证书位置 + keystore: /SPKeystore.jks + #证书秘钥 + keystore-pwd: centric + #别名 + alias: spkey +--- +spring: + profiles: test +cs: + plm: + sso: + #证书位置 + keystore: /SPKeystore.jks + #证书秘钥 + keystore-pwd: centric + #别名 + alias: spkey + +--- +spring: + profiles: prod +cs: + plm: + sso: + #证书位置 + keystore: /SPKeystore.jks + #证书秘钥 + keystore-pwd: centric + #别名 + alias: spkey \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..c8a1cc3 --- /dev/null +++ b/start.bat @@ -0,0 +1,5 @@ +@echo off +set path=C:\Program Files\Java\jdk1.8.0_45\jre\bin +START "plmservice" "%path%\javaw" -jar plmservice.war +echo 启动 plmservice...... +pause \ No newline at end of file diff --git a/stop.bat b/stop.bat new file mode 100644 index 0000000..ed7d778 --- /dev/null +++ b/stop.bat @@ -0,0 +1,4 @@ +@echo off +wmic process where (commandline LIKE "%%plmservice%%" and caption="javaw.exe") delete +echo 关闭进程结束 +pause \ No newline at end of file diff --git a/task/.gitignore b/task/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/task/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/task/.mvn/wrapper/MavenWrapperDownloader.java b/task/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/task/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/task/.mvn/wrapper/maven-wrapper.jar b/task/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/task/.mvn/wrapper/maven-wrapper.jar differ diff --git a/task/.mvn/wrapper/maven-wrapper.properties b/task/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/task/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/task/pom.xml b/task/pom.xml new file mode 100644 index 0000000..409a0cb --- /dev/null +++ b/task/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + com.centricsoftware + plmservice + 2.0 + ../pom.xml + + + task + 2.0 + + task + Demo project for Spring Boot + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + true + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.commons + commons-lang3 + + + org.projectlombok + lombok + true + + + + + + + com.centricsoftware + config + provided + + + com.centricsoftware + commons + + + commons-beanutils + commons-beanutils + + + poi + org.apache.poi + + + poi-ooxml + org.apache.poi + + + true + + + + + + + + diff --git a/task/src/main/java/com/centricsoftware/task/config/TaskConfig.java b/task/src/main/java/com/centricsoftware/task/config/TaskConfig.java new file mode 100644 index 0000000..b9ab136 --- /dev/null +++ b/task/src/main/java/com/centricsoftware/task/config/TaskConfig.java @@ -0,0 +1,38 @@ +package com.centricsoftware.task.config; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * 使用java配置文件配置task + * @author zheng.gong + * @date 2020/4/21 + */ +@Configuration +@EnableScheduling +@ComponentScan(basePackages = {"com.centricsoftware.task.job"}) +public class TaskConfig implements SchedulingConfigurer { + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskExecutor()); + } + + /** + * 这里等同于配置文件配置 + * {@code spring.task.scheduling.pool.size=20} - Maximum allowed number of threads. + * {@code spring.task.scheduling.thread-name-prefix=Job-Thread- } - Prefix to use for the names of newly created threads. + * {@link org.springframework.boot.autoconfigure.task.TaskSchedulingProperties} + */ + @Bean + public Executor taskExecutor() { + return new ScheduledThreadPoolExecutor(20, new BasicThreadFactory.Builder().namingPattern("Job-Thread-%d").build()); + } +} diff --git a/task/src/main/java/com/centricsoftware/task/job/TaskJob.java b/task/src/main/java/com/centricsoftware/task/job/TaskJob.java new file mode 100644 index 0000000..70c4c4a --- /dev/null +++ b/task/src/main/java/com/centricsoftware/task/job/TaskJob.java @@ -0,0 +1,46 @@ +package com.centricsoftware.task.job; + +public class TaskJob { + + /** + * 按照标准时间来算,每隔 10s 执行一次 + */ + // @Scheduled(cron = "${task.test}") + //public void job1() throws Exception { +// log.info("【开始测试查询链接1】"); +// String queryXML = "\n" + +// ""; +// Document document = NodeUtil.queryByXML(queryXML); +// List list = C8ResponseXML.resultNodeCNLs(document); +// list.forEach(log::info); + // log.info("----------------------------从启动后每隔一段时间执行一次任务--------------------------"); + // } + + /** + * 从启动时间开始,间隔 60s 执行 + * 固定间隔时间 + */ +// @Scheduled(fixedRate = 60000) +// public void job2() throws Exception{ +// log.info("【开始测试查询链接2】"); +// String queryXML = "\n" + +// ""; +// Document document = NodeUtil.queryByXML(queryXML); +// List list = C8ResponseXML.resultNodeCNLs(document); +// list.forEach(log::info); +// } + +// /** +// * 从启动时间开始,延迟 5s 后间隔 4s 执行 +// * 固定等待时间 +// */ +// @Scheduled(fixedDelay = 4000, initialDelay = 5000) +// public void job3() throws Exception{ +// log.info("【开始测试查询链接3】"); +// String queryXML = "\n" + +// ""; +// Document document = NodeUtil.queryByXML(queryXML); +// List list = C8ResponseXML.resultNodeCNLs(document); +// list.forEach(log::info); +// } +} \ No newline at end of file diff --git a/task/src/main/java/com/centricsoftware/task/job/inter/CategoryJob.java b/task/src/main/java/com/centricsoftware/task/job/inter/CategoryJob.java new file mode 100644 index 0000000..2b86ac8 --- /dev/null +++ b/task/src/main/java/com/centricsoftware/task/job/inter/CategoryJob.java @@ -0,0 +1,18 @@ +package com.centricsoftware.task.job.inter; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 产品分类 \ 材料分类 接口 批量定时同步类 + * + * @author jerry + */ +@Component +public class CategoryJob { + + @Scheduled(cron = "0 0 1 * * ? ") //每天凌晨1点 + public void job1() throws Exception { + + } +}