commit eb9908d993d01d19101dfdb8582a723419a6c97e Author: RENCHAO <542607047@qq.com> Date: Thu Jun 15 09:52:01 2023 +0800 工作目录第一次备份 diff --git a/Test/Drools/pom.xml b/Test/Drools/pom.xml new file mode 100644 index 00000000..ce85713d --- /dev/null +++ b/Test/Drools/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.renchao + Drools + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + + 8 + 8 + + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + org.springframework.boot + spring-boot-starter-web + + + + org.kie + kie-spring + 7.6.0.Final + + + org.springframework + spring-tx + + + org.springframework + spring-beans + + + org.springframework + spring-core + + + org.springframework + spring-context + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/Test/Drools/src/main/java/com/renchao/Application.java b/Test/Drools/src/main/java/com/renchao/Application.java new file mode 100644 index 00000000..032920a4 --- /dev/null +++ b/Test/Drools/src/main/java/com/renchao/Application.java @@ -0,0 +1,11 @@ +package com.renchao; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/Test/Drools/src/main/java/com/renchao/bean/Order.java b/Test/Drools/src/main/java/com/renchao/bean/Order.java new file mode 100644 index 00000000..aa0a5787 --- /dev/null +++ b/Test/Drools/src/main/java/com/renchao/bean/Order.java @@ -0,0 +1,31 @@ +package com.renchao.bean; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) +public class Order { + + /** + * 订单原价金额 + */ + private int price; + + /** + *下单人 + */ + private User user; + + /** + *积分 + */ + private int score; + + /** + * 下单日期 + */ + private Date bookingDate; +} \ No newline at end of file diff --git a/Test/Drools/src/main/java/com/renchao/bean/User.java b/Test/Drools/src/main/java/com/renchao/bean/User.java new file mode 100644 index 00000000..c249f60d --- /dev/null +++ b/Test/Drools/src/main/java/com/renchao/bean/User.java @@ -0,0 +1,9 @@ +package com.renchao.bean; + +import lombok.Data; + +@Data +public class User { + private String name; + private Integer level; +} diff --git a/Test/Drools/src/main/java/com/renchao/config/DroolsAutoConfiguration.java b/Test/Drools/src/main/java/com/renchao/config/DroolsAutoConfiguration.java new file mode 100644 index 00000000..5cddf475 --- /dev/null +++ b/Test/Drools/src/main/java/com/renchao/config/DroolsAutoConfiguration.java @@ -0,0 +1,67 @@ +package com.renchao.config; + + +import org.kie.api.KieBase; +import org.kie.api.KieServices; +import org.kie.api.builder.*; +import org.kie.api.runtime.KieContainer; +import org.kie.internal.io.ResourceFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +import java.io.IOException; + + +/** + *

规则引擎自动配置类

+ */ +@Configuration +public class DroolsAutoConfiguration { + + private static final String RULES_PATH = "rules/"; + + private KieServices getKieServices() { + return KieServices.Factory.get(); + } + + @Bean + @ConditionalOnMissingBean(KieFileSystem.class) + public KieFileSystem kieFileSystem() throws IOException { + KieFileSystem kieFileSystem = getKieServices().newKieFileSystem(); + for (Resource file : getRuleFiles()) { + kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8")); + } + return kieFileSystem; + } + + private Resource[] getRuleFiles() throws IOException { + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*"); + } + + @Bean + @ConditionalOnMissingBean(KieContainer.class) + public KieContainer kieContainer() throws IOException { + final KieRepository kieRepository = getKieServices().getRepository(); + + kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId()); + + KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem()); + kieBuilder.buildAll(); + + KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId()); + + return kieContainer; + } + + @Bean + @ConditionalOnMissingBean(KieBase.class) + public KieBase kieBase() throws IOException { + return kieContainer().getKieBase(); + } + +} \ No newline at end of file diff --git a/Test/Drools/src/main/resources/rules/TestRules.drl b/Test/Drools/src/main/resources/rules/TestRules.drl new file mode 100644 index 00000000..fe56a982 --- /dev/null +++ b/Test/Drools/src/main/resources/rules/TestRules.drl @@ -0,0 +1,47 @@ +package rules + +import com.renchao.bean.Order + +rule "zero" + no-loop true + lock-on-active true + salience 1 + when + $s : Order(price <= 100) + then + $s.setScore(0); + update($s); +end + +rule "add100" + no-loop true + lock-on-active true + salience 1 + when + $s : Order(price > 100 && price <= 500) + then + $s.setScore(100); + update($s); +end + +rule "add500" + no-loop true + lock-on-active true + salience 1 + when + $s : Order(price > 500 && price <= 1000) + then + $s.setScore(500); + update($s); +end + +rule "add1000" + no-loop true + lock-on-active true + salience 1 + when + $s : Order(price > 1000) + then + $s.setScore(1000); + update($s); +end \ No newline at end of file diff --git a/Test/Drools/src/test/java/com/renchao/DroolsOrderTests.java b/Test/Drools/src/test/java/com/renchao/DroolsOrderTests.java new file mode 100644 index 00000000..e47a164e --- /dev/null +++ b/Test/Drools/src/test/java/com/renchao/DroolsOrderTests.java @@ -0,0 +1,96 @@ +package com.renchao; + +import com.renchao.bean.Order; +import com.renchao.bean.User; +import org.junit.jupiter.api.Test; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.annotation.Resource; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * 需求 + * 计算额外积分金额 规则如下: 订单原价金额 + * 100以下, 不加分 + * 100-500 加100分 + * 500-1000 加500分 + * 1000 以上 加1000分 + */ +@SpringBootTest +public class DroolsOrderTests { + @Resource + private KieContainer kieContainer; + + + @Test + public void droolsOrderTest() throws Exception { + KieSession kieSession = kieContainer.newKieSession(); + List orderList = getInitData(); + for (Order order: orderList) { + // 1-规则引擎处理逻辑 + kieSession.insert(order); + kieSession.fireAllRules(); + // 2-执行完规则后, 执行相关的逻辑 + addScore(order); + } + kieSession.dispose(); + } + + + + private static void addScore(Order o){ + System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore()); + } + + private static List getInitData() throws Exception { + List orderList = new ArrayList<>(); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + { + Order order = new Order(); + order.setPrice(80); + order.setBookingDate(df.parse("2015-07-01")); + User user = new User(); + user.setLevel(1); + user.setName("Name1"); + order.setUser(user); + order.setScore(111); + orderList.add(order); + } + { + Order order = new Order(); + order.setPrice(200); + order.setBookingDate(df.parse("2015-07-02")); + User user = new User(); + user.setLevel(2); + user.setName("Name2"); + order.setUser(user); + orderList.add(order); + } + { + Order order = new Order(); + order.setPrice(800); + order.setBookingDate(df.parse("2015-07-03")); + User user = new User(); + user.setLevel(3); + user.setName("Name3"); + order.setUser(user); + orderList.add(order); + } + { + Order order = new Order(); + order.setPrice(1500); + order.setBookingDate(df.parse("2015-07-04")); + User user = new User(); + user.setLevel(4); + user.setName("Name4"); + order.setUser(user); + orderList.add(order); + } + return orderList; + } +} diff --git a/Test/MyMaven/pom.xml b/Test/MyMaven/pom.xml new file mode 100644 index 00000000..345554da --- /dev/null +++ b/Test/MyMaven/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + org.example + MyMaven + 1.0-SNAPSHOT + + + 1.8 + 8 + 8 + + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + + com.alibaba + easyexcel + 2.2.6 + + + + com.google.guava + guava + 29.0-jre + + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + com.fasterxml.jackson.core + jackson-databind + 2.14.1 + + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.26 + + + + cn.hutool + hutool-all + 5.8.9 + + + junit + junit + 4.13.1 + compile + + + + + org.drools + drools-compiler + 7.6.0.Final + + + + + org.springframework + spring-core + 5.3.20 + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + + org.springframework.boot + spring-boot-starter-web + 2.5.14 + + + + + org.hibernate.validator + hibernate-validator + 6.0.9.Final + + + + + + + + + + + + + + + + org.openjdk.jmh + jmh-core + 1.36 + + + + + org.openjdk.jmh + jmh-generator-annprocess + 1.36 + + + + + + \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/chemical/DangerChemicalInfo.java b/Test/MyMaven/src/main/java/com/renchao/chemical/DangerChemicalInfo.java new file mode 100644 index 00000000..51ab329b --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/chemical/DangerChemicalInfo.java @@ -0,0 +1,42 @@ +package com.renchao.chemical; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +@Data +public class DangerChemicalInfo { + @ExcelProperty(value = "序号") + private String number; + + @ExcelProperty(value = "日期") + private String date; + + @ExcelProperty(value = "样品名称及型号") + private String nameAndModel; + + @ExcelProperty(value = "进口国别/地区") + private String country; + + @ExcelProperty(value = "进口收货人/消费使用单位") + private String receiver; + + @ExcelProperty(value = "生产商/发货人") + private String consignor; + + @ExcelProperty(value = "成分/组分") + private String composition; + + @ExcelProperty(value = "危险特性") + private String hazardousProperties; + + @ExcelProperty(value = "危险类别") + private String hazardCategory; + + @ExcelProperty(value = "联合国编号") + private String unNumber; + + @ExcelProperty(value = "建议包装类别") + private String packingCategory; + + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/chemical/DataProcessing.java b/Test/MyMaven/src/main/java/com/renchao/chemical/DataProcessing.java new file mode 100644 index 00000000..ebfc9048 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/chemical/DataProcessing.java @@ -0,0 +1,113 @@ +package com.renchao.chemical; + +import com.alibaba.excel.EasyExcel; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DataProcessing { + /** + * 危险类别正则 + */ + private static final Pattern HAZARD_CATEGORY_PATTERN1 = Pattern.compile("类别[::]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?)"); + private static final Pattern HAZARD_CATEGORY_PATTERN2 = Pattern.compile("(\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?"); + + /** + * 联合国编号正则 + */ + private static final Pattern UN_NUMBER_PATTERN = Pattern.compile("([Uu][Nn])?(\\d{4})"); + + /** + * 建议包装类别正则 + */ + private static final Pattern PACKING_CATEGORY_PATTERN = Pattern.compile("[,,::;;/](I{1,3}|([ⅠⅡⅢ]))|\\d{4}[,,::;;/]([123])"); + + + public static void main(String[] args) { + String filePath = "C:/Users/RENCHAO/Desktop/gggg.xlsx"; + List list = EasyExcel.read(filePath).head(DangerChemicalInfo.class).sheet("第二次Mysql插入数据").doReadSync(); + for (DangerChemicalInfo info : list) { + String properties = info.getHazardousProperties(); + if (properties == null) { + continue; + } + info.setHazardCategory(processHazardCategory(properties)); + info.setUnNumber(processUnNumber(properties)); + info.setPackingCategory(processPackingCategory(properties)); + } + + EasyExcel.write("C:/Users/RENCHAO/Desktop/资料/bbr.xlsx", DangerChemicalInfo.class).sheet("Sheet1").doWrite(list); + System.out.println("完成!!!!"); + } + + /** + * 处理 危险类别 + */ + private static String processHazardCategory(String properties) { + String category = processHazardCategory(properties, HAZARD_CATEGORY_PATTERN1); + if (category != null) { + return category; + } + return processHazardCategory(properties, HAZARD_CATEGORY_PATTERN2); + } + + + private static String processHazardCategory(String properties, Pattern pattern) { + int index = 0; + if (pattern == HAZARD_CATEGORY_PATTERN1) { + index = 1; + } + Matcher matcher = pattern.matcher(properties); + String category; + if (matcher.find()) { + // 后面是否又括号 + String brackets = matcher.group(index + 6); + if (brackets == null) { + category = matcher.group(index); + } else { + category = matcher.group(index + 3) + "+" + brackets; + } + return category.replace("/", "+"); + } + return null; + } + + /** + * 处理 联合国编号 + */ + private static String processUnNumber(String properties) { + Matcher matcher = UN_NUMBER_PATTERN.matcher(properties); + if (matcher.find()) { + return matcher.group(2); + } + return null; + } + + + /** + * 建议包装类别 + */ + private static String processPackingCategory(String properties) { + Matcher matcher = PACKING_CATEGORY_PATTERN.matcher(properties); + if (matcher.find()) { + String category; + if (matcher.group(3) != null) { + category = matcher.group(3); + } else { + category = matcher.group(1); + } + if ("III".equals(category) || "3".equals(category)) { + return "Ⅲ"; + } + if ("II".equals(category) || "2".equals(category)) { + return "Ⅱ"; + } + if ("I".equals(category) || "1".equals(category)) { + return "Ⅰ"; + } + return category; + } + return null; + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/drools/DroolsTest.java b/Test/MyMaven/src/main/java/com/renchao/drools/DroolsTest.java new file mode 100644 index 00000000..d7050b65 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/drools/DroolsTest.java @@ -0,0 +1,106 @@ +package com.renchao.drools; + + +import org.junit.Test; +import org.kie.api.KieServices; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class DroolsTest { + KieContainer kieContainer; + + public DroolsTest() { + KieServices kieServices = KieServices.Factory.get(); + // 获取Kie容器对象(默认容器对象 + this.kieContainer = kieServices.newKieClasspathContainer(); + kieContainer.newKieSession().dispose(); + } + + @Test + public void test02() { + KieSession kieSession1 = kieContainer.newKieSession(); + List list = new ArrayList<>(); + list.add("aa"); + list.add("bb"); + kieSession1.insert(list); + System.out.println("第一个===="); + kieSession1.fireAllRules(); +// kieSession1.fireAllRules(match -> "book_discount_8".equals(match.getRule().getName())); + System.out.println("第二个===="); + kieSession1.insert(new ArrayList<>()); + kieSession1.fireAllRules(); +// +// kieSession1.fireAllRules(); + + } + + @Test + public void test03() { + KieSession kieSession = kieContainer.newKieSession(); + Order order = new Order(); +// order.setStringList(null); + kieSession.insert(order); + kieSession.fireAllRules(); + + } + + @Test + public void test01() { + + extracted(); + extracted(); + extracted(); + extracted(); + + } + + private void extracted() { + long l = System.currentTimeMillis(); + KieSession kieSession = kieContainer.newKieSession(); + Integer[] integers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Set set = new HashSet<>(); + List list = new ArrayList<>(); + kieSession.insert(set); + kieSession.insert(list); + for (Integer integer : integers) { + kieSession.insert(integer); + kieSession.fireAllRules(match -> "book_discount_7".equals(match.getRule().getName())); + kieSession.fireAllRules(); + } + kieSession.dispose(); + System.out.println(set); + System.out.println(list); + System.out.println("耗时:" + (System.currentTimeMillis() - l)); + } + + @Test + public void test() { + KieSession kieSession = kieContainer.newKieSession(); + + Order order = new Order(); + order.setOriginalPrice(60d); + + // 将order对象插入工作内存 + kieSession.insert(order); + + String str = "我试一试的。。。。。"; + kieSession.insert(str); + + System.out.println("匹配规则前优惠后价格:" + order.getRealPrice()); + + // 匹配对象 + // 激活规则,由drools框架自动进行规则匹配。若匹配成功,则执行 + kieSession.fireAllRules(); + + // 关闭会话 + kieSession.dispose(); + + System.out.println("优惠前价格:" + order.getOriginalPrice() + "\n优惠后价格:" + order.getRealPrice()); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/drools/Order.java b/Test/MyMaven/src/main/java/com/renchao/drools/Order.java new file mode 100644 index 00000000..f5c548fb --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/drools/Order.java @@ -0,0 +1,16 @@ +package com.renchao.drools; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 订单 + */ +@Data +public class Order { + private Double originalPrice; // 订单原始价格,即优惠前的价格 + private Double realPrice; // 订单真实价格,即优惠后的价格 + private List stringList = new ArrayList<>(); +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_01_HelloWorld.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_01_HelloWorld.java new file mode 100644 index 00000000..b3b90d04 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_01_HelloWorld.java @@ -0,0 +1,104 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +public class JMHSample_01_HelloWorld { + + /** + * This is our first benchmark method. + * + * JMH works as follows: users annotate the methods with @Benchmark, and + * then JMH produces the generated code to run this particular benchmark as + * reliably as possible. In general one might think about @Benchmark methods + * as the benchmark "payload", the things we want to measure. The + * surrounding infrastructure is provided by the harness itself. + * + * Read the Javadoc for @Benchmark annotation for complete semantics and + * restrictions. At this point we only note that the methods names are + * non-essential, and it only matters that the methods are marked with + * @Benchmark. You can have multiple benchmark methods within the same + * class. + * + * Note: if the benchmark method never finishes, then JMH run never finishes + * as well. If you throw an exception from the method body the JMH run ends + * abruptly for this benchmark and JMH will run the next benchmark down the + * list. + * + * Although this benchmark measures "nothing" it is a good showcase for the + * overheads the infrastructure bear on the code you measure in the method. + * There are no magical infrastructures which incur no overhead, and it is + * important to know what are the infra overheads you are dealing with. You + * might find this thought unfolded in future examples by having the + * "baseline" measurements to compare against. + */ + + @Benchmark + public void wellHelloThere() { + // this method was intentionally left blank. + } + + /** + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You are expected to see the run with large number of iterations, and + * very large throughput numbers. You can see that as the estimate of the + * harness overheads per method call. In most of our measurements, it is + * down to several cycles per call. + * + * a) Via command-line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_01 + * + * JMH generates self-contained JARs, bundling JMH together with it. + * The runtime options for the JMH are available with "-h": + * $ java -jar target/benchmarks.jar -h + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_01_HelloWorld.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_02_BenchmarkModes.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_02_BenchmarkModes.java new file mode 100644 index 00000000..e961a0f9 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_02_BenchmarkModes.java @@ -0,0 +1,183 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * 基准测试-模式: + * + * @see Mode + * + * @author childe + * @date 2018/9/19 11:29 + **/ +public class JMHSample_02_BenchmarkModes { + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(JMHSample_02_BenchmarkModes.class.getSimpleName()) + .exclude(JMHSample_02_BenchmarkModes.class.getSimpleName() + ".measureAll") + .exclude(JMHSample_02_BenchmarkModes.class.getSimpleName() + ".measureMultiple") + .output("JMHSample_02_BenchmarkModes_result.sampleLog") + .forks(1) + .build(); + + new Runner(options).run(); + } + + /* + * JMH generates lots of synthetic code for the benchmarks for you during + * the benchmark compilation. JMH can measure the benchmark methods in lots + * of modes. Users may select the default benchmark mode with a special + * annotation, or select/override the mode via the runtime options. + * + * With this scenario, we start to measure something useful. Note that our + * payload code potentially throws exceptions, and we can just declare them + * to be thrown. If the code throws the actual exception, the benchmark + * execution will stop with an error. + * + * When you are puzzled with some particular behavior, it usually helps to + * look into the generated code. You might see the code is doing not + * something you intend it to do. Good experiments always follow up on the + * experimental setup, and cross-checking the generated code is an important + * part of that follow up. + * + * The generated code for this particular sample is somewhere at + * target/generated-sources/annotations/.../JMHSample_02_BenchmarkModes.java + */ + + /** + *

Throughput: 每单位时间的操作 ops/time

+ * + *

持续调用标记有{@link Benchmark}的方法,计算所有工作线程的总吞吐量。 + * 这个模式基于时间,一致运行到迭代时间结束

+ * + * Mode.Throughput, as stated in its Javadoc, measures the raw throughput by + * continuously calling the benchmark method in a time-bound iteration, and + * counting how many times we executed the method. + * + * We are using the special annotation to select the units to measure in, + * although you can use the default. + */ + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + public static void measureThroughput() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.AverageTime measures the average execution time, and it does it + * in the way similar to Mode.Throughput. + * + * Some might say it is the reciprocal throughput, and it really is. + * There are workloads where measuring times is more convenient though. + */ + + /** + *

Average time: 每次操作的平均时间 time/op

+ * + *

持续调用标记有{@link Benchmark}的方法,计算调用所有工作线程的平均时间。这个是{@link Mode#Throughput}的倒数, + * 采用不同的聚合政策。这个模式基于时间,一直运行到迭代时间结束

+ */ + @Benchmark + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public static void measureAverageTime() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.SampleTime samples the execution time. With this mode, we are + * still running the method in a time-bound iteration, but instead of + * measuring the total time, we measure the time spent in *some* of + * the benchmark method calls. + * + * This allows us to infer the distributions, percentiles, etc. + * + * JMH also tries to auto-adjust sampling frequency: if the method + * is long enough, you will end up capturing all the samples. + */ + + /** + *

Sample time: 对每次操作的时间进行采样 Sampling time

+ * + *

This mode automatically adjusts the sampling + * frequency, but may omit some pauses which missed the sampling measurement.

+ *

+ * 持续调用标记有{@link Benchmark}的方法,随机采集调用需要的时间。这个模式自动调整采样频率, + * 但可能会忽略一些错过采样测量的暂停??(但是可能会因为测量的暂停遗漏一些采样) + * 这个模式基于时间,一直运行到迭代时间结束 + *

+ */ + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public static void measureSampleTime() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * Mode.SingleShotTime measures the single method invocation time. As the Javadoc + * suggests, we do only the single benchmark method invocation. The iteration + * time is meaningless in this mode: as soon as benchmark method stops, the + * iteration is over. + * + * This mode is useful to do cold startup tests, when you specifically + * do not want to call the benchmark method continuously. + */ + + /** + * + *

Single shot time: 测量单次操作的时间。

+ *

+ * 运行一次{@link Benchmark}的调用,并且测量它的时间。 + * 这种模式对下面几种情况很有用:当你不想隐藏热身调用时来检测"冷"性能;你想看到调用的进展;你想记录每一个样本。 + * 这个模式是基于工作的,仅在单次调用{@link Benchmark}时运行。 + *

+ * 这种模式的注意事项包括: + *
    + *
  • 通常需要更多的预热/测量迭代
  • + *
  • 如果基准测试很小,定时器开销可能很大;如果这是一个问题,切换到{@link Mode#SampleTime} 模式
  • + *
+ */ + @Benchmark + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public static void measureSingleShotTime() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /* + * We can also ask for multiple benchmark modes at once. All the tests + * above can be replaced with just a single test like this: + */ + + @Benchmark + @BenchmarkMode({Mode.Throughput, Mode.AverageTime, Mode.SampleTime, Mode.SingleShotTime}) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureMultiple() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + + /** + * Meta-mode: all the benchmark modes. + * This is mostly useful for internal JMH testing. + */ + + @Benchmark + @BenchmarkMode(Mode.All) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + public void measureAll() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(100); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_03_States.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_03_States.java new file mode 100644 index 00000000..6e0c5f29 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_03_States.java @@ -0,0 +1,85 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * + * @see Scope + * + * @author childe + * @date 2018/9/20 16:02 + **/ +public class JMHSample_03_States { + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_03_States.class.getSimpleName()) + .output("JMHSample_03_States_result.sampleLog") + .threads(4) + .forks(1) + .build(); + + new Runner(opt).run(); + } + + /* + * 有些情况下,你需要在基准测试执行时维护一些状态。因为JHM常常用来构建并发基准测试, + * 我们选择了一个明确的state-bearing(状态支撑)对象概念。 + * + * 下面是两个状态对象。他们的类名不是必需的,只是我们需要在类上标记@State。 + * 这些对象在需要时被创建,在整个基准试验期间重复使用。 + * + * 这个重要的状态属性始终由其中一个基准线程实例化,然后可以访问该状态。 + * 这意味着你可以初始化字段,就像在工作线程中那样(ThreadLocals是你的,等等) + * + */ + + @State(Scope.Benchmark) + public static class BenchmarkState { + { + System.out.println("swwwws"); + } + volatile double x = Math.PI; + } + + @State(Scope.Thread) + public static class ThreadState { + { + System.out.println("ss"); + } + volatile double x = Math.PI; + } + + /* + * Benchmark方法可以引用这个状态,JHM在调用这些方法时会注入适当的状态。 + * 你可以没有状态,或者只有一个,又或者引用多个。 + * 这使你构建一个多线程的基准测试轻而易举。 + */ + + @Benchmark + public void measureUnshared(ThreadState state) { + // All benchmark threads will call in this method. + // + // However, since ThreadState is the Scope.Thread, each thread + // will have it's own copy of the state, and this benchmark + // will measure unshared case. + state.x++; + } + + @Benchmark + public void measureShared(BenchmarkState state) { + // All benchmark threads will call in this method. + // + // Since BenchmarkState is the Scope.Benchmark, all threads + // will share the state instance, and we will end up measuring + // shared case. + state.x++; + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_04_DefaultState.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_04_DefaultState.java new file mode 100644 index 00000000..0794ddd0 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_04_DefaultState.java @@ -0,0 +1,37 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * 幸运的是,大多情况下你只需要一个单独的对象。 + * 这种情况下,我们可以用benchmark的实例来标记@State。 + * 然后,我们就可以像其他任何Java程序那样来引用他。 + * + * @author childe + * @date 2018/9/20 17:08 + **/ +@State(Scope.Thread) +public class JMHSample_04_DefaultState { + + private double x = Math.PI; + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(JMHSample_04_DefaultState.class.getSimpleName()) + .output("JMHSample_04_DefaultState_result.sampleLog") + .threads(1) + .forks(1) + .build(); + new Runner(options).run(); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void measureDefaultState() { + x++; + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_05_StateFixtures.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_05_StateFixtures.java new file mode 100644 index 00000000..9decedd8 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_05_StateFixtures.java @@ -0,0 +1,113 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * @see Level + * @see Setup + * @see TearDown + * + * @author childe + * @date 2018/10/8 11:23 + **/ +@State(Scope.Thread) +public class JMHSample_05_StateFixtures { + + private double x; + + /* + * Since @State objects are kept around during the lifetime of the + * benchmark, it helps to have the methods which do state housekeeping. + * These are usual fixture methods, you are probably familiar with them from + * JUnit and TestNG. + * + * Fixture methods make sense only on @State objects, and JMH will fail to + * compile the test otherwise. + * + * As with the State, fixture methods are only called by those benchmark + * threads which are using the state. That means you can operate in the + * thread-local context, and (not) use synchronization as if you are + * executing in the context of benchmark thread. + * + * Note: fixture methods can also work with static fields, although the + * semantics of these operations fall back out of State scope, and obey + * usual Java rules (i.e. one static field per class). + * + * 因为@State对象在benchmark的生命周期中保持不变,它可以帮助我们使用状态管理的方法。 + * 这些fixture方法看起来和JUnit和TestNG很像 + * @see org.openjdk.jmh.annotations.Level。 + * + * fixture方法只对@State对象有意义,否则,JHM在编译时会报错。 + * + * 与State一样,fixture方法仅被那些使用state的benchmark线程调用。 这意味着你可以在线程本地上下文中操作, + * 并且(不)使用同步,就像你在benchmark线程的上下文中执行一样。 + * + * 注意:fixture方法也可以使用静态字段,尽管这些操作的语义从State范围中退出,并遵守通常的Java规则(即每个类一个静态字段)。 + */ + + /* + * Level默认每个@Benchmark前执行 + * Ok, let's prepare our benchmark: + */ + + @Setup + public void prepare() { + x = Math.PI; + } + + /* + * Level默认每个@TearDown后执行 + * And, check the benchmark went fine afterwards: + */ + + @TearDown + public void check() { + assert x > Math.PI : "Nothing changed?"; + } + + /* + * This method obviously does the right thing, incrementing the field x + * in the benchmark state. check() will never fail this way, because + * we are always guaranteed to have at least one benchmark call. + */ + + @Benchmark + public void measureRight() { + x++; + } + + /* + * This method, however, will fail the check(), because we deliberately + * have the "typo", and increment only the local variable. This should + * not pass the check, and JMH will fail the run. + */ + + @Benchmark + public void measureWrong() { + double x = 0; + x++; + } + + /* + * java -ea //启动断言 + * java -ea:pkname... //在包pkname及其子包下起用断言 + * java -ea:pkname.classname //对类 pkname.classname启用断言 + * 停用断言与启用设置类似 + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_05_StateFixtures.class.getSimpleName()) + .forks(1) + .jvmArgs("-ea") + .output("JMHSample_05_StateFixtures.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_06_FixtureLevel.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_06_FixtureLevel.java new file mode 100644 index 00000000..c7d7bf79 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_06_FixtureLevel.java @@ -0,0 +1,79 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * @see Level + * @see Setup + * @see TearDown + * + * @author childe + * @date 2018/10/8 15:36 + **/ +@State(Scope.Thread) +public class JMHSample_06_FixtureLevel { + + private double x; + + /* + * Fixture methods have different levels to control when they should be run. + * There are at least three Levels available to the user. These are, from + * top to bottom: + * + * Level.Trial: before or after the entire benchmark run (the sequence of iterations) + * Level.Iteration: before or after the benchmark iteration (the sequence of invocations) + * Level.Invocation; before or after the benchmark method invocation (WARNING: read the Javadoc before using) + * + * Time spent in fixture methods does not count into the performance + * metrics, so you can use this to do some heavy-lifting. + * + * fixture方法在运行时有几个不同的等级。 + * 一共有三个等级供我们使用: + * + * Level.Trial:整个基准测试(一个@Benchmark注解为一个基准测试)运行之前或之后(迭代序列) + * Level.Iteration:在基准迭代之前或之后(调用序列) + * Level.Invocation:该等级表现比较复杂 @see org.openjdk.jmh.annotations.Level + * + * fixture方法的耗时不会被统计进性能指标,所以可以用它来做一些比较重的操作。 + */ + + @TearDown(Level.Iteration) + public void check() { + assert x > Math.PI : "Nothing changed?"; + } + + @Benchmark + public void measureRight() { + x++; + } + + @Benchmark + public void measureWrong() { + double x = 0; + x++; + } + + /* + * java -ea //启动断言 + * java -ea:pkname... //在包pkname及其子包下起用断言 + * java -ea:pkname.classname //对类 pkname.classname启用断言 + * 停用断言与启用设置类似 + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_06_FixtureLevel.class.getSimpleName()) + .forks(1) + .jvmArgs("-ea") + // switch to "true" to fail the complete run + .shouldFailOnError(false) + .output("JMHSample_06_FixtureLevel.sampleLog") + .build(); + + new Runner(opt).run(); + } +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_07_FixtureLevelInvocation.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_07_FixtureLevelInvocation.java new file mode 100644 index 00000000..6eb4c850 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_07_FixtureLevelInvocation.java @@ -0,0 +1,137 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.*; + +/** + * Fixtures have different Levels to control when they are about to run. + * Level.Invocation is useful sometimes to do some per-invocation work + * which should not count as payload (e.g. sleep for some time to emulate + * think time) + * + * 在他们运行的时候fixture有不同的等级进行控制。 + * Level.Invocation一些情况下很有用,比如:每次调用前做一些工作,并且这些工作不会被统计为有效载荷。 + * + * @author childe + * @date 2018/10/8 17:13 + **/ +@OutputTimeUnit(TimeUnit.MICROSECONDS) +public class JMHSample_07_FixtureLevelInvocation { + + /* + * Fixtures have different Levels to control when they are about to run. + * Level.Invocation is useful sometimes to do some per-invocation work, + * which should not count as payload. PLEASE NOTE the timestamping and + * synchronization for Level.Invocation helpers might significantly offset + * the measurement, use with care. See Level.Invocation javadoc for further + * discussion. + * + * 在他们运行的时候fixture有不同的等级进行控制。 + * Level.Invocation一些情况下很有用,比如:每次调用前做一些工作,并且这些工作不会被统计为有效载荷。 + * 请注意Level.Invocation助手的时间戳和同步可能会显着抵消测量,请谨慎使用。 + * + * Consider this sample: + * 看下面的例子: + */ + + /* + * This state handles the executor. + * Note we create and shutdown executor with Level.Trial, so + * it is kept around the same across all iterations. + */ + + @State(Scope.Benchmark) + public static class NormalState { + ExecutorService service; + + @Setup(Level.Trial) + public void up() { + System.out.println("up"); + service = Executors.newCachedThreadPool(); + } + + @TearDown(Level.Trial) + public void down() { + service.shutdown(); + } + + } + + /* + * This is the *extension* of the basic state, which also + * has the Level.Invocation fixture method, sleeping for some time. + * + * @State 是可继承的 + */ + + public static class LaggingState extends NormalState { + static final int SLEEP_TIME = Integer.getInteger("sleepTime", 10); + + @Setup(Level.Invocation) + public void lag() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(SLEEP_TIME); + } + } + + /* + * This allows us to formulate the task: measure the task turnaround in + * "hot" mode when we are not sleeping between the submits, and "cold" mode, + * when we are sleeping. + */ + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public double measureHot(NormalState e, final Scratch s) throws ExecutionException, InterruptedException { + return e.service.submit(new Task(s)).get(); + } + + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public double measureCold(LaggingState e, final Scratch s) throws ExecutionException, InterruptedException { + return e.service.submit(new Task(s)).get(); + } + + /* + * This is our scratch state which will handle the work. + */ + + @State(Scope.Thread) + public static class Scratch { + + private double p; + + private double doWork() { + p = Math.log(p); + return p; + } + } + + public static class Task implements Callable { + private Scratch s; + + private Task(Scratch s) { + this.s = s; + } + + @Override + public Double call() { + return s.doWork(); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_07_FixtureLevelInvocation.class.getSimpleName()) + .forks(1) + .output("JMHSample_07_FixtureLevelInvocation.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_08_DeadCode.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_08_DeadCode.java new file mode 100644 index 00000000..7d3873e4 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_08_DeadCode.java @@ -0,0 +1,74 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * Dead-Code Elimination (DCE) + * 死代码消除对基准测试造成的影响 + * + * @author childe + * @date 2018/10/9 11:18 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_08_DeadCode { + + /* + * The downfall of many benchmarks is Dead-Code Elimination (DCE): compilers + * are smart enough to deduce some computations are redundant and eliminate + * them completely. If the eliminated part was our benchmarked code, we are + * in trouble. + * + * Fortunately, JMH provides the essential infrastructure to fight this + * where appropriate: returning the result of the computation will ask JMH + * to deal with the result to limit dead-code elimination (returned results + * are implicitly consumed by Blackholes, see JMHSample_09_Blackholes). + * + * 许多基准测试的失败是死代码消除(DCE): + * 编译器非常聪明,可以推断出一些冗余的计算并完全消除它们。 + * 如果被淘汰的部分是我们的基准代码,我们就遇到了麻烦。 + * + * 幸运的是JMH提供了必要的基础设施,以便在适当的时候来避免这种情况:返回计算结果将要求JMH进行处理以限制死码消除( + * 返回的结果可以饮食被Blackholes消费,参见 JMHSample_09_Blackholes + * ) + */ + + private double x = Math.PI; + + @Benchmark + public void baseline() { + // do nothing, this is a baseline + // 基准线 + } + + @Benchmark + public void measureWrong() { + // This is wrong: result is not used and the entire computation is optimized away. + // 这是错误的:结果没有被使用,整个计算将会被编译器优化。 + Math.log(x); + } + + @Benchmark + public double measureRight() { + // This is correct: the result is being used. + return Math.log(x); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_08_DeadCode.class.getSimpleName()) + .output("JMHSample_08_DeadCode.sampleLog") + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_09_Blackholes.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_09_Blackholes.java new file mode 100644 index 00000000..ca6857ed --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_09_Blackholes.java @@ -0,0 +1,101 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * 死代码消除解决 + * + * @author childe + * @date 2018/10/9 11:44 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class JMHSample_09_Blackholes { + + /* + * Should your benchmark require returning multiple results, you have to + * consider two options (detailed below). + * + * NOTE: If you are only producing a single result, it is more readable to + * use the implicit return, as in JMHSample_08_DeadCode. Do not make your benchmark + * code less readable with explicit Blackholes! + * + * 你的基准测试是否需要返回付哦个测试结果,你需要考虑下民两个问题。 + * 注意:如果你只会产生一个结果,应该使用更易读的明确return,就像JMHSample_08_DeadCode。 + * 不要使用明确的Blackholes来降低您的基准代码的可读性! + */ + + double x1 = Math.PI; + double x2 = Math.PI * 2; + + /* + * Baseline measurement: how much single Math.sampleLog costs. + */ + + @Benchmark + public double baseline() { + return Math.log(x1); + } + + /* + * While the Math.sampleLog(x2) computation is intact(完好), Math.sampleLog(x1) + * is redundant and optimized out. + */ + + @Benchmark + public double measureWrong() { + Math.log(x1); + return Math.log(x2); + } + + /* + * This demonstrates(演示) Option A: + * + * Merge multiple results into one and return it. + * This is OK when is computation is relatively heavyweight, and merging + * the results does not offset the results much. + * + * 演示选项A: + * 合并多个结果并返回。 + * 这种方式行得通,但相对较重,并且这个结果不会对结果有太大影响。 + */ + + @Benchmark + public double measureRight_1() { + return Math.log(x1) + Math.log(x2); + } + + /* + * This demonstrates Option B: + * + * Use explicit Blackhole objects, and sink the values there. + * (Background: Blackhole is just another @State object, bundled with JMH). + * + * 演示选项B: + * 明确的使用Blackhole对象,并且把值汇入。 + * (背后:Blackhole就像另一个@State对象,绑定在JMH)。 + */ + + @Benchmark + public void measureRight_2(Blackhole bh) { + bh.consume(Math.log(x1)); + bh.consume(Math.log(x2)); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_09_Blackholes.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_10_ConstantFold.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_10_ConstantFold.java new file mode 100644 index 00000000..992a7d72 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_10_ConstantFold.java @@ -0,0 +1,84 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * DCE的常量折叠(constant-folding) + * + * @author childe + * @date 2018/10/9 14:27 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_10_ConstantFold { + + /* + * The flip side of dead-code elimination is constant-folding. + * + * If JVM realizes the result of the computation is the same no matter what, + * it can cleverly optimize it. In our case, that means we can move the + * computation outside of the internal JMH loop. + * + * This can be prevented by always reading the inputs from non-final + * instance fields of @State objects, computing the result based on those + * values, and follow the rules to prevent DCE. + * + * 死代码消除的另一面是常量折叠。 + * 如果JVM意识到计算结果不管怎样计算都是不变的,他便会巧妙的优化掉。 + * 在我们的例子中,这意味着我们可以把计算移到JMH内部循环之外。 + * + * 这可以通过始终从@State对象的non-final字段读取输入,根据这些值计算结果,并遵循规则来防止DCE。 + */ + + // IDEs will say "Oh, you can convert this field to local variable". Don't. Trust. Them. + // (While this is normally fine advice, it does not work in the context of measuring correctly.) + // 哈哈,竟然言中 + private double x = Math.PI; + + // IDEs will probably also say "Look, it could be final". Don't. Trust. Them. Either. + // (While this is normally fine advice, it does not work in the context of measuring correctly.) + // 哈哈,原因说错了 + + private final double wrongX = Math.PI; + + @Benchmark + public double baseline() { + // simply return the value, this is a baseline + return Math.PI; + } + + @Benchmark + public double measureWrong_1() { + // This is wrong: the source is predictable, and computation is foldable. + return Math.log(Math.PI); + } + + @Benchmark + public double measureWrong_2() { + // This is wrong: the source is predictable, and computation is foldable. + return Math.log(wrongX); + } + + @Benchmark + public double measureRight() { + // This is correct: the source is not predictable. + return Math.log(x); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_10_ConstantFold.class.getSimpleName()) + .forks(1) + .output("JMHSample_10_ConstantFold.sampleLog") + .build(); + + new Runner(opt).run(); + } +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_11_Loops.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_11_Loops.java new file mode 100644 index 00000000..9eb1b16a --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_11_Loops.java @@ -0,0 +1,134 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * 基准测试方法中进行循环是坏做法 + * + * @author childe + * @date 2018/10/9 15:51 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_11_Loops { + + /* + * It would be tempting for users to do loops within the benchmarked method. + * (This is the bad thing Caliper taught everyone). These tests explain why + * this is a bad idea. + * + * Looping is done in the hope of minimizing the overhead of calling the + * test method, by doing the operations inside the loop instead of inside + * the method call. Don't buy this argument; you will see there is more + * magic happening when we allow optimizers to merge the loop iterations. + * + * 在基准测试方法中做循环是很诱人的。这是Caliper教给大家的坏事情。以下测试告诉你原因。 + * + * 循环是为了通过在循环内而不是在方法调用内部进行操作来最小化调用测试方法的开销。??? + * 不要买这个论点;当我们允许优化器合并循环迭代时,你会发现有更多的魔法发生。 + */ + + /* + * Suppose we want to measure how much it takes to sum two integers: + */ + + int x = 1; + int y = 2; + + /* + * This is what you do with JMH. + */ + + @Benchmark + public int measureRight() { + return (x + y); + } + + /* + * The following tests emulate the naive looping. + * This is the Caliper-style benchmark. + * + * Caliper风格的基准测试 + */ + + private int reps(int reps) { + int s = 0; + for (int i = 0; i < reps; i++) { + s += (x + y); + } + return s; + } + + /* + * We would like to measure this with different repetitions count. + * Special annotation is used to get the individual operation cost. + */ + + @Benchmark + @OperationsPerInvocation(1) + public int measureWrong_1() { + return reps(1); + } + + @Benchmark + @OperationsPerInvocation(10) + public int measureWrong_10() { + return reps(10); + } + + @Benchmark + @OperationsPerInvocation(100) + public int measureWrong_100() { + return reps(100); + } + + @Benchmark + @OperationsPerInvocation(1000) + public int measureWrong_1000() { + return reps(1000); + } + + @Benchmark + @OperationsPerInvocation(10000) + public int measureWrong_10000() { + return reps(10000); + } + + @Benchmark + @OperationsPerInvocation(100000) + public int measureWrong_100000() { + return reps(100000); + } + + /* + * You might notice the larger the repetitions count, the lower the "perceived" + * cost of the operation being measured. Up to the point we do each addition with 1/20 ns, + * well beyond what hardware can actually do. + * + * This happens because the loop is heavily unrolled/pipelined, and the operation + * to be measured is hoisted from the loop. Morale: don't overuse loops, rely on JMH + * to get the measurement right. + * + * 你可能已经注意到,循环次数阅读,统计出来的时间越短。到目前位置,每次操作的时间已经是1/20 ns,远远超过硬件的实际能力。 + * + * 发生这种情况是因为循环被大量unrolled/pipelined,并且要从循环中提升要测量的操作。规则:不要过度使用循环,依靠JMH来正确测量。 + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_11_Loops.class.getSimpleName()) + .forks(1) + .output("JMHSample_11_Loops.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_12_Forking.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_12_Forking.java new file mode 100644 index 00000000..b9b514d9 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_12_Forking.java @@ -0,0 +1,162 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * Use non-forked runs only for debugging purposes, not for actual performance runs + * 使用non-forked运行仅用于调试目的,而不是用于实际性能运行 + * + * @author childe + * @date 2018/10/9 17:17 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_12_Forking { + + /* + * JVMs are notoriously good at profile-guided optimizations. This is bad + * for benchmarks, because different tests can mix their profiles together, + * and then render the "uniformly bad" code for every test. Forking (running + * in a separate process) each test can help to evade this issue. + * + * JMH will fork the tests by default. + * + * JVM擅长profile-guided优化。但这对基准不友好,因为不同的测试可以把他们的profiles混合在一起, + * 然后为每一个测试渲染"uniformly bad"代码???。forking(运行在不同的进程)每个测试可以避免这个问题。 + * + * JMH默认fork所有test。 + */ + + /* + * Suppose we have this simple counter interface, and two implementations. + * Even though those are semantically the same, from the JVM standpoint, + * those are distinct classes. + * + * 假设我们又一个简单的统计接受,并且有两个实现。 + * 即使他们逻辑相同,站在JVM角度看他们是不同的类。 + */ + + public interface Counter { + int inc(); + } + + public class Counter1 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + public class Counter2 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + /* + * And this is how we measure it. + * Note this is susceptible for same issue with loops we mention in previous examples. + */ + + public int measure(Counter c) { + int s = 0; + for (int i = 0; i < 10; i++) { + s += c.inc(); + } + return s; + } + + /* + * These are two counters. + */ + + Counter c1 = new Counter1(); + Counter c2 = new Counter2(); + + /* + * We first measure the Counter1 alone... + * Fork(0) helps to run in the same JVM. + */ + + @Benchmark + @Fork(0) + public int measure_1_c1() { + return measure(c1); + } + + /* + * Then Counter2... + */ + + @Benchmark + @Fork(0) + public int measure_2_c2() { + return measure(c2); + } + + /* + * Then Counter1 again... + */ + + @Benchmark + @Fork(0) + public int measure_3_c1_again() { + return measure(c1); + } + + /* + * These two tests have explicit @Fork annotation. + * JMH takes this annotation as the request to run the test in the forked JVM. + * It's even simpler to force this behavior for all the tests via the command + * line option "-f". The forking is default, but we still use the annotation + * for the consistency. + * + * This is the test for Counter1. + */ + + @Benchmark + @Fork(1) + public int measure_4_forked_c1() { + return measure(c1); + } + + /* + * ...and this is the test for Counter2. + */ + + @Benchmark + @Fork(1) + public int measure_5_forked_c2() { + return measure(c2); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note that C1 is faster, C2 is slower, but the C1 is slow again! This is because + * the profiles for C1 and C2 had merged together. + * + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_12_Forking.class.getSimpleName()) + .output("JMHSample_12_Forking.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_13_RunToRun.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_13_RunToRun.java new file mode 100644 index 00000000..8dfd39b9 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_13_RunToRun.java @@ -0,0 +1,118 @@ +package com.renchao.jmh; + + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * run-to-run variance + * 运行间差异 + * + * @author childe + * @date 2018/10/10 09:16 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class JMHSample_13_RunToRun { + + /* + * Forking also allows to estimate run-to-run variance. + * + * JVMs are complex systems, and the non-determinism is inherent for them. + * This requires us to always account the run-to-run variance as the one + * of the effects in our experiments. + * + * Luckily, forking aggregates the results across several JVM launches. + * + * fork还允许估计运行间的差异。 + * JVM是复杂的系统,非决定论是他们固有的。 + * 这要求我们始终将运行间差异视为我们实验中的影响之一。 + * + * 幸运的是,fork聚合了多个JVM启动的结果。 + */ + + /* + * In order to introduce readily measurable run-to-run variance, we build + * the workload which performance differs from run to run. Note that many workloads + * will have the similar behavior, but we do that artificially to make a point. + * + * 为了引入易于测量的逐次运行差异,我们构建了性能因运行而异的工作负载。 + * 许多工作负载都会有类似的行为,但我们会人为地指出这一点。 + */ + + @State(Scope.Thread) + public static class SleepyState { + public long sleepTime; + + @Setup + public void setup() { + // 每次fork都会执行,fork相当于重复多次某个被标记@Benchmark注解的方法,但它会合并结果。 + sleepTime = (long) (Math.random() * 1000); + } + } + + /* + * Now, we will run this different number of times. + */ + + @Benchmark + @Fork(1) + public void baseline(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + @Benchmark + @Fork(5) + public void fork_1(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + @Benchmark + @Fork(20) + public void fork_2(SleepyState s) throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(s.sleepTime); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the baseline is random within [0..1000] msec; and both forked runs + * are estimating the average 500 msec with some confidence. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_13 -wi 0 -i 3 + * (we requested no warmup, 3 measurement iterations; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_13_RunToRun.class.getSimpleName()) + .warmupIterations(0) + .measurementIterations(3) + .output("JMHSample_13_RunToRun.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_15_Asymmetric.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_15_Asymmetric.java new file mode 100644 index 00000000..30536a3d --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_15_Asymmetric.java @@ -0,0 +1,122 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 非对称分组执行 + * + * 例子场景:生产者/消费者 + * + * @author childe + * @date 2018/10/10 10:40 + **/ +@State(Scope.Group) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_15_Asymmetric { + + /* + * So far all the tests were symmetric: the same code was executed in all the threads. + * At times, you need the asymmetric test. JMH provides this with the notion of @Group, + * which can bind several methods together, and all the threads are distributed among + * the test methods. + * + * Each execution group contains of one or more threads. Each thread within a particular + * execution group executes one of @Group-annotated @Benchmark methods. Multiple execution + * groups may participate in the run. The total thread count in the run is rounded to the + * execution group size, which will only allow the full execution groups. + * + * Note that two state scopes: Scope.Benchmark and Scope.Thread are not covering all + * the use cases here -- you either share everything in the state, or share nothing. + * To break this, we have the middle ground Scope.Group, which marks the state to be + * shared within the execution group, but not among the execution groups. + * + * Putting this all together, the example below means: + * a) define the execution group "g", with 3 threads executing inc(), and 1 thread + * executing get(), 4 threads per group in total; + * b) if we run this test case with 4 threads, then we will have a single execution + * group. Generally, running with 4*N threads will create N execution groups, etc.; + * c) each execution group has one @State instance to share: that is, execution groups + * share the counter within the group, but not across the groups. + * + * 到目前位置,我们的测试都是对称的:所有的线程都运行相同的代码。 + * 是时候了解非对称测试了。JMH提供了@Group注解来把几个方法绑定到一起,所有线程都分布在测试方法中。 + * + * 每个执行组包含一个或者多个线程。特定执行组中的每个线程执行@Group-annotated @Benchmark方法之一。 + * 多个执行组可以参与运行。运行中的总线程数将四舍五入为执行组大小,这将只允许完整的执行组。??? + * + * 注意那两个状态的作用域:Scope.Benchmark 和 Scope.Thread没有在这个用例中覆盖 -- 你要么在状态中共享每个东西,要么不共享。 + * 我们使用Scope.Group状态用来表明在执行组内共享,而不在组间共享。 + * + * 以下事例含义: + * a)定义执行组"g",它有3个线程来执行inc(),1个线程来执行get(),每个分组共有4个线程; + * b)如果我们用4个线程来运行这个测试用例,我们将会有一个单独的执行组。通常,用4*N个线程来创建N个执行组。 + * c)每个执行组内共享一个@State实例:也就是执行组内共享counter,而不是跨组共享。 + */ + + private AtomicInteger counter; + + @Setup + public void up() { + counter = new AtomicInteger(); + } + + @Benchmark + @Group("g") + @GroupThreads(3) + public int inc() { + return counter.incrementAndGet(); + } + + @Benchmark + @Group("g") + @GroupThreads(1) + public int get() { + return counter.get(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will have the distinct metrics for inc() and get() from this run. + * + * 在此次运行中我们讲分别获得inc()和get()的指标。 + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_15 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_15_Asymmetric.class.getSimpleName()) + .forks(1) + .output("JMHSample_15_Asymmetric.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_16_CompilerControl.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_16_CompilerControl.java new file mode 100644 index 00000000..73b8fb31 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_16_CompilerControl.java @@ -0,0 +1,161 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * Method Inlining + * 方法内敛,是JVM对代码的编译优化,常见的编译优化 @see http://www.importnew.com/2009.html + * + * 官方白皮书 + * From http://www.oracle.com/technetwork/java/whitepaper-135217.html#method: + * + * The frequency of virtual method invocations in the Java programming language is an important optimization bottleneck. + * Once the Java HotSpot adaptive optimizer has gathered information during execution about program hot spots, + * it not only compiles the hot spot into native code, but also performs extensive method inlining on that code. + * + * Inlining has important benefits. + * It dramatically reduces the dynamic frequency of method invocations, which saves the time needed to perform those method invocations. + * But even more importantly, inlining produces much larger blocks of code for the optimizer to work on. + * This creates a situation that significantly increases the effectiveness of traditional compiler optimizations, + * overcoming a major obstacle to increased Java programming language performance. + * + * Inlining is synergistic with other code optimizations, because it makes them more effective. + * As the Java HotSpot compiler matures, the ability to operate on large, inlined blocks of code will open the door to a host of even more advanced optimizations in the future. + * + * Java编程语言中虚拟方法调用的频率是一个重要的优化瓶颈。 + * 一旦Java HotSpot自适应优化器在执行期间收集有关程序热点的信息,它不仅将热点编译为本机代码,而且还对该代码执行大量方法内联。 + * + * 内联具有重要的好处。 + * 它大大降低了方法调用的动态频率,从而节省了执行这些方法调用所需的时间。 + * 但更重要的是,内联会为优化程序生成更大的代码块。 + * 这创造了一种能够显着提高传统编译器优化效率的情况,克服了增加Java编程语言性能的主要障碍。 + * + * 内联与其他代码优化具有协同作用,因为它使它们更有效。 + * 随着Java HotSpot编译器的成熟,对大型内联代码块进行操作的能力将为未来的一系列更高级的优化打开大门。 + * + * @author childe + * @date 2018/10/10 14:16 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_16_CompilerControl { + + /* + * We can use HotSpot-specific functionality to tell the compiler what + * do we want to do with particular methods. To demonstrate the effects, + * we end up with 3 methods in this sample. + * + * 我们使用HotSpot特定的功能来告诉编译器我们想对特定的方法做怎么。 + * 为了证明这种效果,我们在这个例子中写了三个测试方法。 + */ + + /** + * These are our targets: + * - first method is prohibited from inlining + * - second method is forced to inline + * - third method is prohibited from compiling + * + * We might even place the annotations directly to the benchmarked + * methods, but this expresses the intent more clearly. + * + * 这是我们的目标: + * - 第一个方法禁止内敛 + * - 第二个方法强制内敛 + * - 第三个方法禁止编译 + * + * 我们甚至可以将注释直接放在基准测试方法中,但这更清楚地表达了意图。 + */ + + public void target_blank() { + // this method was intentionally left blank + // 方法故意留空 + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public void target_dontInline() { + // this method was intentionally left blank + } + + @CompilerControl(CompilerControl.Mode.INLINE) + public void target_inline() { + // this method was intentionally left blank + } + + /** + * Exclude the method from the compilation. + */ + @CompilerControl(CompilerControl.Mode.EXCLUDE) + public void target_exclude() { + // this method was intentionally left blank + } + + /* + * These method measures the calls performance. + * + * 这些方法来测量调用性能。 + */ + + @Benchmark + public void baseline() { + // this method was intentionally left blank + } + + @Benchmark + public void blank() { + target_blank(); + } + + @Benchmark + public void dontinline() { + target_dontInline(); + } + + @Benchmark + public void inline() { + target_inline(); + } + + @Benchmark + public void exclude() { + target_exclude(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the performance of the baseline, blank, and inline methods are the same. + * dontinline differs a bit, because we are making the proper call. + * exclude is severely slower, becase we are not compiling it at all. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_16 -wi 0 -i 3 -f 1 + * (we requested no warmup iterations, 3 iterations, single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_16_CompilerControl.class.getSimpleName()) + .warmupIterations(0) + .measurementIterations(3) + .forks(1) + .output("JMHSample_16_CompilerControl.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_17_SyncIterations.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_17_SyncIterations.java new file mode 100644 index 00000000..51bbb976 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_17_SyncIterations.java @@ -0,0 +1,117 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/11 17:01 + **/ +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class JMHSample_17_SyncIterations { + + /* + * This is the another thing that is enabled in JMH by default. + * + * Suppose we have this simple benchmark. + */ + + private double src; + + @Benchmark + public double test() { + double s = src; + for (int i = 0; i < 1000; i++) { + s = Math.sin(s); + } + return s; + } + + /* + * It turns out if you run the benchmark with multiple threads, + * the way you start and stop the worker threads seriously affects + * performance. + * + * The natural way would be to park all the threads on some sort + * of barrier, and the let them go "at once". However, that does + * not work: there are no guarantees the worker threads will start + * at the same time, meaning other worker threads are working + * in better conditions, skewing the result. + * + * The better solution would be to introduce bogus iterations, + * ramp up the threads executing the iterations, and then atomically + * shift the system to measuring stuff. The same thing can be done + * during the rampdown. This sounds complicated, but JMH already + * handles that for you. + * + * 事实证明如果你用多线程来跑benchmark,你启动和停止工作线程的方式会严重影响性能。 + * + * 通常的做法是,让所有的线程都挂起在一些有序的屏障上,然后让他们一起开始。 + * 然而,这种做法是不奏效的:没有谁能够保证工作线程在同一时间开始,这就意味着其他工作线程在更好的条件下运行,从而扭曲了结果。 + * + * 更好的解决方案是引入虚假迭代,增加执行迭代的线程,然后将系统原子地转换为测量内容。在减速期间可以做同样的事情。 + * 这听起来很复杂,但是JMH已经帮你处理好了。 + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will need to oversubscribe the system to make this effect + * clearly visible; however, this effect can also be shown on the + * unsaturated systems.* + * + * 您需要超额预订系统才能使此效果清晰可见;然而,这种效应也可以在不饱和系统上显示出来。 + * + * Note the performance of -si false version is more flaky, even + * though it is "better". This is the false improvement, granted by + * some of the threads executing in solo. The -si true version more stable + * and coherent. + * + * -si false版本的性能更加不稳定,即使它“更好”。 -si true版本更稳定,更连贯。 + * + * -si true is enabled by default. + * + * Say, $CPU is the number of CPUs on your machine. + * + * You can run this test with: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_17 \ + * -w 1s -r 1s -f 1 -t ${CPU*16} -si {true|false} + * (we requested shorter warmup/measurement iterations, single fork, + * lots of threads, and changeable "synchronize iterations" option) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_17_SyncIterations.class.getSimpleName()) + .warmupTime(TimeValue.seconds(1)) + .measurementTime(TimeValue.seconds(1)) + .threads(Runtime.getRuntime().availableProcessors()*16) + .forks(1) + // try to switch to "false", default is true + .syncIterations(false) + .output("JMHSample_17_SyncIterations_false.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_18_Control.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_18_Control.java new file mode 100644 index 00000000..6ad50c28 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_18_Control.java @@ -0,0 +1,91 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Control; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 同benchmark交互 + * @see Control + * @author childe + * @date 2018/10/12 15:12 + **/ +@State(Scope.Group) +public class JMHSample_18_Control { + + /* + * Sometimes you need the tap into the harness mind to get the info + * on the transition change. For this, we have the experimental state object, + * Control, which is updated by JMH as we go. + * + * 有时您需要点击线束头脑以获取有关转换更改的信息。为此,我们有实验状态对象Control,它由JMH随时更新。 + * + * WARNING: The API for Contro class is considered unstable, and can be changed without notice. + */ + + /* + * In this example, we want to estimate the ping-pong speed for the simple + * AtomicBoolean. Unfortunately, doing that in naive manner will livelock + * one of the threads, because the executions of ping/pong are not paired + * perfectly. We need the escape hatch to terminate the loop if threads + * are about to leave the measurement. + * + * 在这个例子中,我们想要估计简单的AtomicBoolean的ping / pong速度。 + * 不幸的是,以天真的方式执行此操作将会锁定其中一个线程,因为ping / pong的执行不能完美配对。 + * 如果线程即将离开测量,我们需要"逃生舱口"来终止循环。 + */ + + public final AtomicBoolean flag = new AtomicBoolean(); + + @Benchmark + @Group("pingpong") + public void ping(Control cnt) { + // 业务在迭代时间未结束前,一直运行。 + while (!cnt.stopMeasurement && !flag.compareAndSet(false, true)) { + // this body is intentionally left blank + } + } + + @Benchmark + @Group("pingpong") + public void pong(Control cnt) { + while (!cnt.stopMeasurement && !flag.compareAndSet(true, false)) { + // this body is intentionally left blank + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_18 -t 2 -f 1 + * (we requested 2 threads and single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_18_Control.class.getSimpleName()) + .threads(2) + .forks(1) + .output("JMHSample_18_Control.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_20_Annotations.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_20_Annotations.java new file mode 100644 index 00000000..480b8b47 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_20_Annotations.java @@ -0,0 +1,79 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * 基于注解的基准测试 + * + * @author childe + * @date 2018/10/12 16:28 + **/ +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Fork(1) +public class JMHSample_20_Annotations { + + double x1 = Math.PI; + + /* + * In addition to all the command line options usable at run time, + * we have the annotations which can provide the reasonable defaults + * for the some of the benchmarks. This is very useful when you are + * dealing with lots of benchmarks, and some of them require + * special treatment. + * + * Annotation can also be placed on class, to have the effect over + * all the benchmark methods in the same class. The rule is, the + * annotation in the closest scope takes the precedence: i.e. + * the method-based annotation overrides class-based annotation, + * etc. + * + * 除了运行时所有的命令行选项外,我们还可以通过注解给一些基准测试提供默认值。 + * 在你处理大量基准测试时这个很有用,其中一些需要特别处理。 + * + * 注解可以放在class上,来影响这个class中所有的基准测试方法。规则是,靠近作用域的注解有优先权: + * 比如,方法上的注解可以覆盖类上的注解。 + * + * 注意:命令行优先级最高。 + */ + + @Benchmark + @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + public double measure() { + return Math.log(x1); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note JMH honors the default annotation settings. You can always override + * the defaults via the command line or API. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_20 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_20_Annotations.class.getSimpleName()) + .output("JMHSample_20_Annotations.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_21_ConsumeCPU.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_21_ConsumeCPU.java new file mode 100644 index 00000000..2305f530 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_21_ConsumeCPU.java @@ -0,0 +1,139 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * Consume some amount of time tokens. + * + * 消费一些时间令牌。 + * + * 有时测试消耗一定CPU周期。通过静态的BlackHole.consumeCPU(tokens)方法来实现。 + * Token是一些CPU指令。这样编写方法代码就可以达到运行时间依赖于该参数的目的(不被任何JIT/CPU优化)。 + * + * @author childe + * @date 2018/10/13 15:03 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_21_ConsumeCPU { + + /* + * At times you require the test to burn some of the cycles doing nothing. + * In many cases, you *do* want to burn the cycles instead of waiting. + * + * For these occasions, we have the infrastructure support. Blackholes + * can not only consume the values, but also the time! Run this test + * to get familiar with this part of JMH. + * + * (Note we use static method because most of the use cases are deep + * within the testing code, and propagating blackholes is tedious). + * + * 有时你需要测试来烧掉一些无所事事的循环。 + * 在许多情况下,你想要燃烧周期而不是等待。 + * + * 在这些场合,我们有基础设施支持。 + * Blackhole不仅可以消耗数值,还可以消耗时间!运行此测试以熟悉JMH的这一部分。 + * + * (注意:我们使用静态方法,因为大多数用例都在测试代码的深处,传播Blackhole很繁琐) + */ + + @Benchmark + public void consume_0000() { + Blackhole.consumeCPU(0); + } + + @Benchmark + public void consume_0001() { + Blackhole.consumeCPU(1); + } + + @Benchmark + public void consume_0002() { + Blackhole.consumeCPU(2); + } + + @Benchmark + public void consume_0004() { + Blackhole.consumeCPU(4); + } + + @Benchmark + public void consume_0008() { + Blackhole.consumeCPU(8); + } + + @Benchmark + public void consume_0016() { + Blackhole.consumeCPU(16); + } + + @Benchmark + public void consume_0032() { + Blackhole.consumeCPU(32); + } + + @Benchmark + public void consume_0064() { + Blackhole.consumeCPU(64); + } + + @Benchmark + public void consume_0128() { + Blackhole.consumeCPU(128); + } + + @Benchmark + public void consume_0256() { + Blackhole.consumeCPU(256); + } + + @Benchmark + public void consume_0512() { + Blackhole.consumeCPU(512); + } + + @Benchmark + public void consume_1024() { + Blackhole.consumeCPU(1024); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the single token is just a few cycles, and the more tokens + * you request, then more work is spent (almost linearly) + * + * 注意:单个token只有几个周期,请求的令牌越多则消耗更多的工作(近乎线性) + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_21 -f 1 + * (we requested single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_21_ConsumeCPU.class.getSimpleName()) + .forks(1) + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_23_AuxCounters.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_23_AuxCounters.java new file mode 100644 index 00000000..16eef333 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_23_AuxCounters.java @@ -0,0 +1,104 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * AuxCounters是实验类,慎用!未来可能删除。 + * CAVEAT: THIS IS AN EXPERIMENTAL API, it may be changed or removed in future without prior warning. + * + * 所以不深入了解了。大意是作为@State的辅助类,可以分别统计操作对象的每个字段值。 + * @author childe + * @date 2018/10/16 15:20 + **/ +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class JMHSample_23_AuxCounters { + + /* + * In some weird cases you need to get the separate throughput/time + * metrics for the benchmarked code depending on the outcome of the + * current code. Trying to accommodate the cases like this, JMH optionally + * provides the special annotation which treats @State objects + * as the object bearing user counters. See @AuxCounters javadoc for + * the limitations. + */ + + @State(Scope.Thread) + @AuxCounters(AuxCounters.Type.OPERATIONS) + public static class OpCounters { + // These fields would be counted as metrics + public int case1; + public int case2; + + // This accessor will also produce a metric + public int total() { + return case1 + case2; + } + } + + @State(Scope.Thread) + // 计算“事件”,即工作量生命周期中的一次性事件。此计数器不会按时间标准化。 + @AuxCounters(AuxCounters.Type.EVENTS) + public static class EventCounters { + // This field would be counted as metric + public int wows; + } + + /* + * This code measures the "throughput" in two parts of the branch. + * The @AuxCounters state above holds the counters which we increment + * ourselves, and then let JMH to use their values in the performance + * calculations. + */ + + @Benchmark + public void splitBranch(OpCounters counters) { + if (Math.random() < 0.1) { + counters.case1++; + } else { + counters.case2++; + } + } + + @Benchmark + public void runSETI(EventCounters counters) { + float random = (float) Math.random(); + float wowSignal = (float) Math.PI / 4; + if (random == wowSignal) { + // WOW, that's unusual. + counters.wows++; + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_23 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_23_AuxCounters.class.getSimpleName()) + .output("JMHSample_23_AuxCounters.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_24_Inheritance.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_24_Inheritance.java new file mode 100644 index 00000000..a38c4373 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_24_Inheritance.java @@ -0,0 +1,109 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/16 17:17 + **/ +public class JMHSample_24_Inheritance { + + /* + * In very special circumstances, you might want to provide the benchmark + * body in the (abstract) superclass, and specialize it with the concrete + * pieces in the subclasses. + * + * The rule of thumb is: if some class has @Benchmark method, then all the subclasses + * are also having the "synthetic" @Benchmark method. The caveat is, because we only + * know the type hierarchy during the compilation, it is only possible during + * the same compilation session. That is, mixing in the subclass extending your + * benchmark class *after* the JMH compilation would have no effect. + * + * Note how annotations now have two possible places. The closest annotation + * in the hierarchy wins. + * + * 我们可以使用模版模式通过抽象方法来分离实现。 + * 经验法则是:如果一些类有@Benchmark方法,那么它所有的子类都继承@Benchmark方法。 + * 注意,因为我们只知道编译期间的类型层次结构,所以只能在同一个编译会话期间使用。 + * 也就是说,在JMH编译之后混合扩展benchmark类的子类将不起作用。 + * + * 注释现在有两个可能的地方。靠近的注视将被采纳。 + */ + + @BenchmarkMode(Mode.AverageTime) + @Fork(1) + @State(Scope.Thread) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static abstract class AbstractBenchmark { + int x; + + @Setup + public void setup() { + x = 42; + } + + @Benchmark + @Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) + public double bench() { + return doWork() * doWork(); + } + + protected abstract double doWork(); + } + + @BenchmarkMode(Mode.Throughput) + public static class BenchmarkLog extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.log(x); + } + } + + public static class BenchmarkSin extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.sin(x); + } + } + + public static class BenchmarkCos extends AbstractBenchmark { + @Override + protected double doWork() { + return Math.cos(x); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test, and observe the three distinct benchmarks running the squares + * of Math.sampleLog, Math.sin, and Math.cos, accordingly. + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_24 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_24_Inheritance.class.getSimpleName()) + .output("JMHSample_24_Inheritance.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_26_BatchSize.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_26_BatchSize.java new file mode 100644 index 00000000..7dfe1c3b --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_26_BatchSize.java @@ -0,0 +1,121 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.LinkedList; +import java.util.List; + +/** + * desc + * + * @author childe + * @date 2018/10/17 10:05 + **/ +@State(Scope.Thread) +public class JMHSample_26_BatchSize { + + /* + * Sometimes you need to evaluate operation which doesn't have + * the steady state. The cost of a benchmarked operation may + * significantly vary from invocation to invocation. + * + * In this case, using the timed measurements is not a good idea, + * and the only acceptable benchmark mode is a single shot. On the + * other hand, the operation may be too small for reliable single + * shot measurement. + * + * We can use "batch size" parameter to describe the number of + * benchmark calls to do per one invocation without looping the method + * manually and protect from problems described in JMHSample_11_Loops. + * + * 有时你需要评估没有稳定状态的操作。基准操作的成本可能因调用而异。 + * + * 这种情况下,使用时间检测不是一个好主意,唯一可以接受的基准模型是single shot(单次测量)。 + * 另一方面,对于可靠的单次测量,操作可能太小。 + * + * 我们可以使用“batch size”参数来描述每次调用执行的基准调用次数,而无需手动循环方法并防止JMHSample_11_Loops中描述的问题。 + */ + + /* + * Suppose we want to measure insertion in the middle of the list. + */ + + List list = new LinkedList<>(); + + @Benchmark + @Warmup(iterations = 5, time = 1) + @Measurement(iterations = 5, time = 1) + @BenchmarkMode(Mode.AverageTime) + public List measureWrong_1() { + list.add(list.size() / 2, "something"); + return list; + } + + @Benchmark + @Warmup(iterations = 5, time = 5) + @Measurement(iterations = 5, time = 5) + @BenchmarkMode(Mode.AverageTime) + public List measureWrong_5() { + list.add(list.size() / 2, "something"); + return list; + } + + /* + * This is what you do with JMH. + */ + + @Benchmark + @Warmup(iterations = 5, batchSize = 5000) + @Measurement(iterations = 5, batchSize = 5000) + @BenchmarkMode(Mode.SingleShotTime) + public List measureRight() { + list.add(list.size() / 2, "something"); + return list; + } + + @Setup(Level.Iteration) + public void setup(){ + list.clear(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can see completely different results for measureWrong_1 and measureWrong_5; this + * is because the workload has no steady state. The result of the workload is dependent + * on the measurement time. measureRight does not have this drawback, because it measures + * the N invocations of the test method and measures it's time. + * + * We measure batch of 5000 invocations and consider the batch as the single operation. + * + * 您可以看到measureWrong_1和measureWrong_5的完全不同的结果;这是因为工作负载没有稳定状态。工作量的结果取决于测量时间。 + * measureRight没有这个缺点,因为它测量测试方法的N次调用并测量它的时间。 + * + * 我们测量5000次调用批次并将批次视为单个操作。 + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_26 -f 1 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_26_BatchSize.class.getSimpleName()) + .forks(1) + .output("JMHSample_26_BatchSize.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_27_Params.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_27_Params.java new file mode 100644 index 00000000..41609f90 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_27_Params.java @@ -0,0 +1,76 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/17 14:30 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Benchmark) +public class JMHSample_27_Params { + + /** + * In many cases, the experiments require walking the configuration space + * for a benchmark. This is needed for additional control, or investigating + * how the workload performance changes with different settings. + * + * 大多情况下,一个基准需要跑在不同的配置下。这需要额外的控制,或者调查工作性能如何随着不同设定改变。 + * + * 如下每个arg参数会完全组合一遍certainty。 + */ + + @Param({"1", "31", "65", "101", "103"}) + public int arg; + + @Param({"0", "1", "2", "4", "8", "16", "32"}) + public int certainty; + + @Benchmark + public boolean bench() { + // 该计算的时常随着certainty的变大而变长 + return BigInteger.valueOf(arg).isProbablePrime(certainty); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note the performance is different with different parameters. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_27 + * + * You can juggle parameters through the command line, e.g. with "-p arg=41,42" + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_27_Params.class.getSimpleName()) +// .param("arg", "41", "42") // Use this to selectively constrain/override parameters + .output("JMHSample_27_Params.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_28_BlackholeHelpers.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_28_BlackholeHelpers.java new file mode 100644 index 00000000..0d18c264 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_28_BlackholeHelpers.java @@ -0,0 +1,126 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/17 15:14 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@State(Scope.Thread) +public class JMHSample_28_BlackholeHelpers { + + /** + * Sometimes you need the black hole not in @Benchmark method, but in + * helper methods, because you want to pass it through to the concrete + * implementation which is instantiated in helper methods. In this case, + * you can request the black hole straight in the helper method signature. + * This applies to both @Setup and @TearDown methods, and also to other + * JMH infrastructure objects, like Control. + * + * Below is the variant of {@link com.cxd.benchmark.JMHSample_08_DeadCode} + * test, but wrapped in the anonymous classes. + * + * 有时你不需要Blackhole在@Benchmark方法中,而是在helper方法中,因为你想把它传递给在helper方法中的具体实现的实例。 + * 在这种清凉夏,你可以通过helper方法签名获得Blackhole。 + * 这可以应用在被标注为@Setup和@TearDown的方法上,也包括其他JMH脚手架对象,比如Control。 + * + * {@link com.cxd.benchmark.JMHSample_08_DeadCode}是它的变种,但是他被包装在匿名类中。 + */ + + public interface Worker { + void work(); + } + + private Worker workerBaseline; + private Worker workerRight; + private Worker workerWrong; + + @Setup + public void setup(final Blackhole bh) { + workerBaseline = new Worker() { + double x; + + @Override + public void work() { + // do nothing + } + }; + + workerWrong = new Worker() { + double x; + + @Override + public void work() { + Math.log(x); + } + }; + + workerRight = new Worker() { + double x; + + @Override + public void work() { + bh.consume(Math.log(x)); + } + }; + + } + + @Benchmark + public void baseline() { + workerBaseline.work(); + } + + @Benchmark + public void measureWrong() { + workerWrong.work(); + } + + @Benchmark + public void measureRight() { + workerRight.work(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You will see measureWrong() running on-par with baseline(). + * Both measureRight() are measuring twice the baseline, so the logs are intact. + * + * measureWrong()的测量结果和baseline()很相近。 + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_28 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_28_BlackholeHelpers.class.getSimpleName()) + .output("JMHSample_28_BlackholeHelpers.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_30_Interrupts.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_30_Interrupts.java new file mode 100644 index 00000000..738a1f5c --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_30_Interrupts.java @@ -0,0 +1,106 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/17 15:31 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Group) +@Timeout(time = 10) +public class JMHSample_30_Interrupts { + + /* + * JMH can also detect when threads are stuck in the benchmarks, and try + * to forcefully interrupt the benchmark thread. JMH tries to do that + * when it is arguably sure it would not affect the measurement. + * + * JMH还可以检测线程何时卡在基准测试中,并尝试强制中断基准线程。 + * 在可以确定它不会影响测量时,JMH会尝试这样做。 + */ + + /* + * In this example, we want to measure the simple performance characteristics + * of the ArrayBlockingQueue. Unfortunately, doing that without a harness + * support will deadlock one of the threads, because the executions of + * take/put are not paired perfectly. Fortunately for us, both methods react + * to interrupts well, and therefore we can rely on JMH to terminate the + * measurement for us. JMH will notify users about the interrupt actions + * nevertheless, so users can see if those interrupts affected the measurement. + * JMH will start issuing interrupts after the default or user-specified timeout + * had been reached. + * + * This is a variant of org.openjdk.jmh.samples.JMHSample_18_Control, but without + * the explicit control objects. This example is suitable for the methods which + * react to interrupts gracefully. + * + * 在这个例子中,我们想测量ArrayBlockingQueue的简单性能特征。 + * 不幸的是,在没有工具支持的情况下执行此操作会使其中一个线程死锁,因为take / put的执行不能完美配对。 + * 幸运的是,这两种方法都能很好地应对中断,因此我们可以依赖JMH来中断测量。 + * JMH将通知用户有关中断操作的信息,因此用户可以查看这些中断是否会影响测量。 + * 在达到默认或用户指定的超时后,JMH将开始发出中断。 + * + * 这是 {@link com.cxd.benchmark.JMHSample_18_Control}的一个变种,但是没有明确的控制对象。 + * 这个例子很适合那些需要优雅应对中断的方法。 + */ + + private BlockingQueue q; + + @Setup + public void setup() { + q = new ArrayBlockingQueue<>(1); + } + + @Group("Q") + @Benchmark + public Integer take() throws InterruptedException { + return q.take(); + } + + @Group("Q") + @Benchmark + public void put() throws InterruptedException { + q.put(42); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_30 -t 2 -f 5 -to 10 + * (we requested 2 threads, 5 forks, and 10 sec timeout) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_30_Interrupts.class.getSimpleName()) + .threads(2) + .forks(5) +// .timeout(TimeValue.seconds(10)) + .output("JMHSample_30_Interrupts_annotation.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_31_InfraParams.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_31_InfraParams.java new file mode 100644 index 00000000..0171d499 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_31_InfraParams.java @@ -0,0 +1,124 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.infra.ThreadParams; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * 通过JMH提供的脚手架获取JMH的一些运行信息 + * + * @author childe + * @date 2018/10/17 16:35 + **/ +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +public class JMHSample_31_InfraParams { + + /* + * There is a way to query JMH about the current running mode. This is + * possible with three infrastructure objects we can request to be injected: + * - BenchmarkParams: covers the benchmark-global configuration + * - IterationParams: covers the current iteration configuration + * - ThreadParams: covers the specifics about threading + * + * Suppose we want to check how the ConcurrentHashMap scales under different + * parallelism levels. We can put concurrencyLevel in @Param, but it sometimes + * inconvenient if, say, we want it to follow the @Threads count. Here is + * how we can query JMH about how many threads was requested for the current run, + * and put that into concurrencyLevel argument for CHM constructor. + * + * 有一种方式用来查JMH并发运行的模型。通过请求注入以下三个脚手架对象我们就可以做到: + * - BenchmarkParams: 涵盖了benchmark的全局配置 + * - IterationParams: 涵盖了当前迭代的配置 + * - ThreadParams: 涵盖了指定线程的配置 + * + * 假设我们想检查ConcurrentHashMap如何在不同的并行级别下扩展。我们可以可以把concurrencyLevel通过@Param传入, + * 但有时不方便,比如,我们想让他和@Threads一致。以下是我们如何查询JMH关于当前运行请求的线程数, + * 并将其放入ConcurrentHashMap构造函数的concurrencyLevel参数中。 + */ + + static final int THREAD_SLICE = 1000; + + private ConcurrentHashMap mapSingle; + private ConcurrentHashMap mapFollowThreads; + + @Setup + public void setup(BenchmarkParams params) { + int capacity = 16 * THREAD_SLICE * params.getThreads(); + // 并发级别数量似乎只会影响initcapacity(仅在initcapacity小于并发数量时)。这么测试好像没什么意义。 + mapSingle = new ConcurrentHashMap<>(capacity, 0.75f, 1); + mapFollowThreads = new ConcurrentHashMap<>(capacity, 0.75f, params.getThreads()); + } + + /* + * Here is another neat trick. Generate the distinct set of keys for all threads: + * + * 这是另一个巧妙的伎俩。为所有线程生成不同的密钥集: + */ + + @State(Scope.Thread) + public static class Ids { + private List ids; + + @Setup + public void setup(ThreadParams threads) { + ids = new ArrayList<>(); + for (int c = 0; c < THREAD_SLICE; c++) { + ids.add("ID" + (THREAD_SLICE * threads.getThreadIndex() + c)); + } + } + } + + @Benchmark + public void measureDefault(Ids ids) { + for (String s : ids.ids) { + mapSingle.remove(s); + mapSingle.put(s, s); + } + } + + @Benchmark + public void measureFollowThreads(Ids ids) { + for (String s : ids.ids) { + mapFollowThreads.remove(s); + mapFollowThreads.put(s, s); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_31 -t 4 -f 5 + * (we requested 4 threads, and 5 forks; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_31_InfraParams.class.getSimpleName()) + .threads(4) + .forks(5) + .output("JMHSample_31_InfraParams.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_32_BulkWarmup.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_32_BulkWarmup.java new file mode 100644 index 00000000..41015bc1 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_32_BulkWarmup.java @@ -0,0 +1,131 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.WarmupMode; + +import java.util.concurrent.TimeUnit; + +/** + * 预热方式不同测量的结果大不一样。 + * + * @author childe + * @date 2018/10/17 17:09 + **/ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_32_BulkWarmup { + + /* + * This is an addendum to JMHSample_12_Forking test. + * + * Sometimes you want an opposite configuration: instead of separating the profiles + * for different benchmarks, you want to mix them together to test the worst-case + * scenario. + * + * JMH has a bulk warmup feature for that: it does the warmups for all the tests + * first, and then measures them. JMH still forks the JVM for each test, but once the + * new JVM has started, all the warmups are being run there, before running the + * measurement. This helps to dodge the type profile skews, as each test is still + * executed in a different JVM, and we only "mix" the warmup code we want. + * + * 这是JMHSample_12_Forking测试的附录。 + * + * 有时你想要一个相反的配置:您可以将它们混合在一起以测试最坏情况,而不是分离不同基准的配置文件。 + * + * JMH有一个批量预热特性:它首先预热所有测试,然后测量他们。JMH仍然为每个测试fork出一个JVM,但当新的JVM启动, + * 所有的预热在测量开始前都会执行。 + * 这有助于避免类型配置文件偏差???,因为每个测试仍然在不同的JVM中执行,我们只“混合”我们想要的预热代码。 + */ + + /* + * These test classes are borrowed verbatim from JMHSample_12_Forking. + */ + + public interface Counter { + int inc(); + } + + public class Counter1 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + public class Counter2 implements Counter { + private int x; + + @Override + public int inc() { + return x++; + } + } + + Counter c1 = new Counter1(); + Counter c2 = new Counter2(); + + /* + * And this is our test payload. Notice we have to break the inlining of the payload, + * so that in could not be inlined in either measure_c1() or measure_c2() below, and + * specialized for that only call. + */ + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int measure(Counter c) { + int s = 0; + for (int i = 0; i < 10; i++) { + s += c.inc(); + } + return s; + } + + @Benchmark + public int measure_c1() { + return measure(c1); + } + + @Benchmark + public int measure_c2() { + return measure(c2); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * Note how JMH runs the warmups first, and only then a given test. Note how JMH re-warmups + * the JVM for each test. The scores for C1 and C2 cases are equally bad, compare them to + * the scores from JMHSample_12_Forking. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_32 -f 1 -wm BULK + * (we requested a single fork, and bulk warmup mode; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_32_BulkWarmup.class.getSimpleName()) + // .includeWarmup(...) <-- this may include other benchmarks into warmup +// .warmupMode(WarmupMode.BULK) // see other WarmupMode.* as well + .warmupMode(WarmupMode.INDI) // see other WarmupMode.* as well + .forks(1) + .output("JMHSample_32_BulkWarmup_indi.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_34_SafeLooping.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_34_SafeLooping.java new file mode 100644 index 00000000..129d8a62 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_34_SafeLooping.java @@ -0,0 +1,199 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.concurrent.TimeUnit; + +/** + * 如何避免编译器的优化,按照预想跑循环 + * + * @author childe + * @date 2018/10/17 17:37 + **/ +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class JMHSample_34_SafeLooping { + + /* + * JMHSample_11_Loops warns about the dangers of using loops in @Benchmark methods. + * Sometimes, however, one needs to traverse through several elements in a dataset. + * This is hard to do without loops, and therefore we need to devise a scheme for + * safe looping. + * + * JMHSample_11_Loops警告我们在@Benchmark方法中使用循环很危险。 + * 然而,有时我们需要便利数据集中的元素。很难避免循环,因此我们需要为安全循环设计一种方式。 + */ + + /* + * Suppose we want to measure how much it takes to execute work() with different + * arguments. This mimics a frequent use case when multiple instances with the same + * implementation, but different data, is measured. + * + * 假设我们想要测量以不同参数运行work()时的性能。当测量具有相同实现但数据不同的多个实例时,这模仿了一个常见的用例。 + */ + + static final int BASE = 42; + + static int work(int x) { + return BASE + x; + } + + /* + * Every benchmark requires control. We do a trivial control for our benchmarks + * by checking the benchmark costs are growing linearly with increased task size. + * If it doesn't, then something wrong is happening. + * + * 每个基准都需要控制。我们对的基准进行了微不足道的控制,通过检查基准成本随着任务规模的增加而线性增长。 + * 如果没有,那么就出现了问题。 + */ + + @Param({"1", "10", "100", "1000"}) + int size; + + int[] xs; + + @Setup + public void setup() { + xs = new int[size]; + for (int c = 0; c < size; c++) { + xs[c] = c; + } + } + + /* + * First, the obviously wrong way: "saving" the result into a local variable would not + * work. A sufficiently smart compiler will inline work(), and figure out only the last + * work() call needs to be evaluated. Indeed, if you run it with varying $size, the score + * will stay the same! + * + * 首先,这个错误很明显:把结果保存在了局部变量中,这是达不到效果。一个足够只能的编译器会把work()进行内敛优化, + * 并指出只有最后一次work()调用需要被操作。确实,如果你用一系列$size(x数组的长度)来测试,会发现他们的结果是一样的。 + */ + + @Benchmark + public int measureWrong_1() { + int acc = 0; + for (int x : xs) { + acc = work(x); + } + return acc; + } + + /* + * Second, another wrong way: "accumulating" the result into a local variable. While + * it would force the computation of each work() method, there are software pipelining + * effects in action, that can merge the operations between two otherwise distinct work() + * bodies. This will obliterate the benchmark setup. + * + * In this example, HotSpot does the unrolled loop, merges the $BASE operands into a single + * addition to $acc, and then does a bunch of very tight stores of $x-s. The final performance + * depends on how much of the loop unrolling happened *and* how much data is available to make + * the large strides. + * + * 然后,另外一个错误:把计算结果保存在局部变量中。 + * 虽然它会强制计算每个work()方法,但是有一些软件的pipelining可以合并两个不同的work()体之间的操作。这将消除基准设置。 + * + * HotSpot执行unrolled循环,将$BASE操作数合并为$acc的单个添加,然后执行一堆非常紧凑的$x-s存储。 + * 最终的性能取决于循环unrolled的次数以及可用于实现大幅度跨越的数据量。 + */ + + @Benchmark + public int measureWrong_2() { + int acc = 0; + for (int x : xs) { + acc += work(x); + } + return acc; + } + + /* + * Now, let's see how to measure these things properly. A very straight-forward way to + * break the merging is to sink each result to Blackhole. This will force runtime to compute + * every work() call in full. (We would normally like to care about several concurrent work() + * computations at once, but the memory effects from Blackhole.consume() prevent those optimization + * on most runtimes). + * + * 现在,让我们看下如何正确测量。一个很直接打断合并的方法就是把每个结果都下沉到Blackhole。 + * 这会强制在运行时为每次调用work()方法都会进行计算。我们通常喜欢同时关注几个并发的work()计算,但是Blackhole.consume()的内存效果阻止了大多数运行时的优化) + */ + + @Benchmark + public void measureRight_1(Blackhole bh) { + for (int x : xs) { + bh.consume(work(x)); + } + } + + /* + * DANGEROUS AREA, PLEASE READ THE DESCRIPTION BELOW. + * + * Sometimes, the cost of sinking the value into a Blackhole is dominating the nano-benchmark score. + * In these cases, one may try to do a make-shift "sinker" with non-inlineable method. This trick is + * *very* VM-specific, and can only be used if you are verifying the generated code (that's a good + * strategy when dealing with nano-benchmarks anyway). + * + * You SHOULD NOT use this trick in most cases. Apply only where needed. + * + * 危险区域,请阅读以下解释。 + * + * 有时,将结果下沉到Blackhole的花费会影响nano-benchmark的结果。 + * 在这些情况下,我们或许可以尝试用non-inlineable的方法来做切换。?这个技巧非常特定于VM?,只有在验证生成的代码?时才能使用(这在处理nano-benchmarks时是一个很好的策略)。 + * + * 大多情况下不要使用该技巧。仅在需要时使用。 + */ + + @Benchmark + public void measureRight_2() { + for (int x : xs) { + sink(work(x)); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public static void sink(int v) { + // IT IS VERY IMPORTANT TO MATCH THE SIGNATURE TO AVOID AUTOBOXING. + // The method intentionally does nothing. + } + + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You might notice measureWrong_1 does not depend on $size, measureWrong_2 has troubles with + * linearity, and otherwise much faster than both measureRight_*. You can also see measureRight_2 + * is marginally faster than measureRight_1. + * + * 你会注意到measureWrong_1结果不受$size影响,measureWrong_2不是线性增长并且比measureRight_*要快的多。 + * 你也会看到measureRight_2比measureRight_1稍快。 + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_34 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHSample_34_SafeLooping.class.getSimpleName()) + .forks(3) + .output("JMHSample_34_SafeLooping.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_35_Profilers.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_35_Profilers.java new file mode 100644 index 00000000..e8642d74 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_35_Profilers.java @@ -0,0 +1,612 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.profile.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 全部配置 {@link org.openjdk.jmh.profile} + * + * @author childe + * @date 2018/10/18 19:21 + **/ +public class JMHSample_35_Profilers { + /* + * This sample serves as the profiler overview. + * + * JMH has a few very handy profilers that help to understand your benchmarks. While + * these profilers are not the substitute for full-fledged external profilers, in many + * cases, these are handy to quickly dig into the benchmark behavior. When you are + * doing many cycles of tuning up the benchmark code itself, it is important to have + * a quick turnaround for the results. + * + * Use -lprof to list the profilers. There are quite a few profilers, and this sample + * would expand on a handful of most useful ones. Many profilers have their own options, + * usually accessible via -prof :help. + * + * Since profilers are reporting on different things, it is hard to construct a single + * benchmark sample that will show all profilers in action. Therefore, we have a couple + * of benchmarks in this sample. + * + * 这个例子是profiler(分析器)的概览。 + * + * JMH有一些很便利的profiler来帮助我们理解benchmark。 + * 虽然这些分析器不能替代成熟的外部分析器,但在许多情况下,这些分析器很容易快速挖掘基准行为。 + * + * 使用-lprof列出所有profiler。本例中只展示常用的一些。许多profiler有他们自己的选项,通过-prof :help获取。 + * + * 因为profiler报告不同的事情,很难在一个基准测试中展示所有profiler的行为。因为有好几个例子。 + * + * */ + + /* + * ================================ MAPS BENCHMARK ================================ + */ + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Maps { + private Map map; + + @Param({"hashmap", "treemap"}) + private String type; + + private int begin; + private int end; + + @Setup + public void setup() { + switch (type) { + case "hashmap": + map = new HashMap<>(); + break; + case "treemap": + map = new TreeMap<>(); + break; + default: + throw new IllegalStateException("Unknown type: " + type); + } + + begin = 1; + end = 256; + for (int i = begin; i < end; i++) { + map.put(i, i); + } + } + + @Benchmark + public void test(Blackhole bh) { + for (int i = begin; i < end; i++) { + bh.consume(map.get(i)); + } + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof stack + * $ java -jar target/benchmarks.jar JMHSample_35.*Maps -prof gc + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(Maps.class.getSimpleName()) +// .addProfiler(StackProfiler.class) + .addProfiler(GCProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Running this benchmark will yield something like: + + Benchmark (type) Mode Cnt Score Error Units + JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op + JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op + + Running with -prof stack will yield: + + ....[Thread state: RUNNABLE]........................................................................ + 99.0% 99.0% org.openjdk.jmh.samples.JMHSample_35_Profilers$Maps.test + 0.4% 0.4% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub + 0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0 + 0.2% 0.2% java.lang.Integer.valueOf + 0.2% 0.2% sun.misc.Unsafe.compareAndSwapInt + + ....[Thread state: RUNNABLE]........................................................................ + 78.0% 78.0% java.util.TreeMap.getEntry + 21.2% 21.2% org.openjdk.jmh.samples.JMHSample_35_Profilers$Maps.test + 0.4% 0.4% java.lang.Integer.valueOf + 0.2% 0.2% sun.reflect.NativeMethodAccessorImpl.invoke0 + 0.2% 0.2% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Maps_test.test_avgt_jmhStub + + Stack profiler is useful to quickly see if the code we are stressing actually executes. As many other + sampling profilers, it is susceptible for sampling bias: it can fail to notice quickly executing methods, + for example. In the benchmark above, it does not notice HashMap.get. + + Stack profiler可以快速查看我们所强调的代码是否实际执行。 + 和其他许采样分析器一样,它容易受到采样偏差的影响:它无法快速发现执行方法,例如:在上面的基准测试中,它没有注意到HashMap.get。 + + Next up, GC profiler. Running with -prof gc will yield: + + Benchmark (type) Mode Cnt Score Error Units + + JMHSample_35_Profilers.Maps.test hashmap avgt 5 1553.201 ± 6.199 ns/op + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate hashmap avgt 5 1257.046 ± 5.675 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm hashmap avgt 5 2048.001 ± 0.001 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space hashmap avgt 5 1259.148 ± 315.277 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm hashmap avgt 5 2051.519 ± 520.324 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space hashmap avgt 5 0.175 ± 0.386 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm hashmap avgt 5 0.285 ± 0.629 B/op + JMHSample_35_Profilers.Maps.test:·gc.count hashmap avgt 5 29.000 counts + JMHSample_35_Profilers.Maps.test:·gc.time hashmap avgt 5 16.000 ms + + JMHSample_35_Profilers.Maps.test treemap avgt 5 5177.065 ± 361.278 ns/op + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate treemap avgt 5 377.251 ± 26.188 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.alloc.rate.norm treemap avgt 5 2048.003 ± 0.001 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space treemap avgt 5 392.743 ± 174.156 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Eden_Space.norm treemap avgt 5 2131.767 ± 913.941 B/op + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space treemap avgt 5 0.131 ± 0.215 MB/sec + JMHSample_35_Profilers.Maps.test:·gc.churn.PS_Survivor_Space.norm treemap avgt 5 0.709 ± 1.125 B/op + JMHSample_35_Profilers.Maps.test:·gc.count treemap avgt 5 25.000 counts + JMHSample_35_Profilers.Maps.test:·gc.time treemap avgt 5 26.000 ms + + There, we can see that the tests are producing quite some garbage. "gc.alloc" would say we are allocating 1257 + and 377 MB of objects per second, or 2048 bytes per benchmark operation. "gc.churn" would say that GC removes + the same amount of garbage from Eden space every second. In other words, we are producing 2048 bytes of garbage per + benchmark operation. + 我们可以看到测试产生了相当多的垃圾。"gc.alloc"表示我们每秒分配1257和277MB内存,或者为每个基准测试操作分配了2048字节。 + "gc.churn"表示GC每秒从Eden区域删除与GC相同量的内存。换句话说,我们每个基准测试操作产生了2048字节的垃圾。 + + If you look closely at the test, you can get a (correct) hypothesis this is due to Integer autoboxing. + 如果仔细观察测试,可以得到一个(正确的)假设,这是由于Integer自动装箱造成的。为什么??? + + Note that "gc.alloc" counters generally produce more accurate data, but they can also fail when threads come and + go over the course of the benchmark. "gc.churn" values are updated on each GC event, and so if you want a more accurate + data, running longer and/or with small heap would help. But anyhow, always cross-reference "gc.alloc" and "gc.churn" + values with each other to get a complete picture. + + 请注意,"gc.alloc"计数器通常会产生更准确的数据,但是当线程来回顾基准测试的过程,它们也会失败。 + "gc.churn"值会在每个GC事件上更新,因此如果您想要更准确的数据,运行更长时间和/或使用小堆会有所帮助。 + 但无论如何,总是互相引用“gc.alloc”和“gc.churn”值来获得完整的图片。 + + It is also worth noticing that non-normalized counters are dependent on benchmark performance! Here, "treemap" + tests are 3x slower, and thus both allocation and churn rates are also comparably lower. It is often useful to look + into non-normalized counters to see if the test is allocation/GC-bound (figure the allocation pressure "ceiling" + for your configuration!), and normalized counters to see the more precise benchmark behavior. + + 值得注意的是,非标准化计数器依赖于benchmark的性能! + "treemap"测试速度慢了3倍,因此分配和回收速率也相对较低。 + 查看 非规范化计数器以查看测试是否为allocation/GC-bound(计算配置的分配压力“上限”)和 规范化计数器以查看更精确的benchmark行为 通常很有用。 + + As most profilers, both "stack" and "gc" profile are able to aggregate samples from multiple forks. It is a good + idea to run multiple forks with the profilers enabled, as it improves results error estimates. + + 与大多数分析器一样,"stack"和"gc"配置文件都能够聚合来自多个fork的样本。在启用分析器的情况下运行多个fork是个好主意,因为它可以改善结果误差估计。 + */ + } + + /* + * ================================ CLASSLOADER BENCHMARK ================================ + */ + + + @State(Scope.Thread) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(3) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Classy { + + /** + * Our own crippled classloader, that can only load a simple class over and over again. + */ + public static class XLoader extends URLClassLoader { + private static final byte[] X_BYTECODE = new byte[]{ + (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE, 0x00, 0x00, 0x00, 0x34, 0x00, 0x0D, 0x0A, 0x00, 0x03, 0x00, + 0x0A, 0x07, 0x00, 0x0B, 0x07, 0x00, 0x0C, 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, + 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, + 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, + 0x69, 0x6C, 0x65, 0x01, 0x00, 0x06, 0x58, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x04, 0x00, 0x05, 0x01, 0x00, + 0x01, 0x58, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, + 0x63, 0x74, 0x00, 0x20, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, + (byte) 0xB7, 0x00, 0x01, (byte) 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, + }; + + public XLoader() { + super(new URL[0], ClassLoader.getSystemClassLoader()); + } + + @Override + protected Class findClass(final String name) { + return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length); + } + + } + + @Benchmark + public Class load() throws ClassNotFoundException { + return Class.forName("X", true, new XLoader()); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof cl + * $ java -jar target/benchmarks.jar JMHSample_35.*Classy -prof comp + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(Classy.class.getSimpleName()) + .addProfiler(ClassloaderProfiler.class) +// .addProfiler(CompilerProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Running with -prof cl will yield: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Classy.load avgt 15 34215.363 ± 545.892 ns/op + JMHSample_35_Profilers.Classy.load:·class.load avgt 15 29374.097 ± 716.743 classes/sec + JMHSample_35_Profilers.Classy.load:·class.load.norm avgt 15 1.000 ± 0.001 classes/op + JMHSample_35_Profilers.Classy.load:·class.unload avgt 15 29598.233 ± 3420.181 classes/sec + JMHSample_35_Profilers.Classy.load:·class.unload.norm avgt 15 1.008 ± 0.119 classes/op + + Here, we can see the benchmark indeed load class per benchmark op, and this adds up to more than 29K classloads + per second. We can also see the runtime is able to successfully keep the number of loaded classes at bay, + since the class unloading happens at the same rate. + + 这里,我们可以看到基准在每个基准操作的确有加载类,这种操作有每秒超过29K的类加载量。 + 我们看到运行时能够成功地保持加载的类的数量,因为类的卸载以同样速率在发生。 + + This profiler is handy when doing the classloading performance work, because it says if the classes + were actually loaded, and not reused across the Class.forName calls. It also helps to see if the benchmark + performs any classloading in the measurement phase. For example, if you have non-classloading benchmark, + you would expect these metrics be zero. + + 在进行类加载性能工作时,此分析器很方便,因为它表示类是否实际加载,而不是在Class.forName调用中重用。 + 它还有助于查看基准测试是否在测量阶段执行任何类加载。例如,如果您有非类加载基准,那么您可能希望这些指标为零。 + + Another useful profiler that could tell if compiler is doing a heavy work in background, and thus interfering + with measurement, -prof comp: + + 另一个有用的profiler可以告诉我们编译器会否在后台做了一些较重的操作,这些操作会影响我们的检测,-prof comp: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Classy.load avgt 5 33523.875 ± 3026.025 ns/op + JMHSample_35_Profilers.Classy.load:·compiler.time.profiled avgt 5 5.000 ms + JMHSample_35_Profilers.Classy.load:·compiler.time.total avgt 5 479.000 ms + + We seem to be at proper steady state: out of 479 ms of total compiler work, only 5 ms happen during the + measurement window. It is expected to have some level of background compilation even at steady state. + + 我们似乎看到了稳定状态:在超过479ms的编译器工作重,仅有5ms发生在检测窗口期。 + 即使在稳定状态下,也会有一定程度的后台编译。 + + As most profilers, both "cl" and "comp" are able to aggregate samples from multiple forks. It is a good + idea to run multiple forks with the profilers enabled, as it improves results error estimates. + + 像其他profilers一样,"cl"和"comp"能够聚合多个fork的采样。这对运行多个开启了profiler的fork是件好事,改善了错误的误差。 + */ + } + + /* + * ================================ ATOMIC LONG BENCHMARK ================================ + */ + + @State(Scope.Benchmark) + @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) + @Fork(1) + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public static class Atomic { + private AtomicLong n; + + @Setup + public void setup() { + n = new AtomicLong(); + } + + @Benchmark + public long test() { + return n.incrementAndGet(); + } + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perf -f 1 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfnorm -f 3 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfasm -f 1 (Linux) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof xperfasm -f 1 (Windows) + * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof dtraceasm -f 1 (Mac OS X) + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(Atomic.class.getSimpleName()) +// .addProfiler(LinuxPerfProfiler.class) +// .addProfiler(LinuxPerfNormProfiler.class) +// .addProfiler(LinuxPerfAsmProfiler.class) +// .addProfiler(WinPerfAsmProfiler.class) + // 需要开启用户sudo免密 + .addProfiler(DTraceAsmProfiler.class) + .build(); + + new Runner(opt).run(); + } + + /* + Dealing with nanobenchmarks like these requires looking into the abyss of runtime, hardware, and + generated code. Luckily, JMH has a few handy tools that ease the pain. If you are running Linux, + then perf_events are probably available as standard package. This kernel facility taps into + hardware counters, and provides the data for user space programs like JMH. Windows has less + sophisticated facilities, but also usable, see below. + + One can simply run "perf stat java -jar ..." to get the first idea how the workload behaves. In + JMH case, however, this will cause perf to profile both host and forked JVMs. + + -prof perf avoids that: JMH invokes perf for the forked VM alone. For the benchmark above, it + would print something like: + + Perf stats: + -------------------------------------------------- + + 4172.776137 task-clock (msec) # 0.411 CPUs utilized + 612 context-switches # 0.147 K/sec + 31 cpu-migrations # 0.007 K/sec + 195 page-faults # 0.047 K/sec + 16,599,643,026 cycles # 3.978 GHz [30.80%] + stalled-cycles-frontend + stalled-cycles-backend + 17,815,084,879 instructions # 1.07 insns per cycle [38.49%] + 3,813,373,583 branches # 913.870 M/sec [38.56%] + 1,212,788 branch-misses # 0.03% of all branches [38.91%] + 7,582,256,427 L1-dcache-loads # 1817.077 M/sec [39.07%] + 312,913 L1-dcache-load-misses # 0.00% of all L1-dcache hits [38.66%] + 35,688 LLC-loads # 0.009 M/sec [32.58%] + LLC-load-misses:HG + L1-icache-loads:HG + 161,436 L1-icache-load-misses:HG # 0.00% of all L1-icache hits [32.81%] + 7,200,981,198 dTLB-loads:HG # 1725.705 M/sec [32.68%] + 3,360 dTLB-load-misses:HG # 0.00% of all dTLB cache hits [32.65%] + 193,874 iTLB-loads:HG # 0.046 M/sec [32.56%] + 4,193 iTLB-load-misses:HG # 2.16% of all iTLB cache hits [32.44%] + L1-dcache-prefetches:HG + 0 L1-dcache-prefetch-misses:HG # 0.000 K/sec [32.33%] + + 10.159432892 seconds time elapsed + + We can already see this benchmark goes with good IPC, does lots of loads and lots of stores, + all of them are more or less fulfilled without misses. The data like this is not handy though: + you would like to normalize the counters per benchmark op. + + This is exactly what -prof perfnorm does: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Atomic.test avgt 15 6.551 ± 0.023 ns/op + JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 0.933 ± 0.026 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.001 ± 0.022 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 12.267 ± 1.324 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.001 ± 0.006 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.090 ± 0.402 #/op + JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.001 ± 0.011 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.001 ± 0.004 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.152 ± 0.385 #/op + JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 0.670 ± 0.048 #/op + JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁶ #/op + JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 26.790 ± 1.393 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.278 ± 0.277 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.113 ± 0.437 #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.001 ± 0.034 #/op + JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 28.729 ± 1.297 #/op + JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁷ #/op + JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 26.734 ± 2.081 #/op + + It is customary to trim the lines irrelevant to the particular benchmark. We show all of them here for + completeness. + + We can see that the benchmark does ~12 loads per benchmark op, and about ~4 stores per op, most of + them fitting in the cache. There are also ~6 branches per benchmark op, all are predicted as well. + It is also easy to see the benchmark op takes ~28 instructions executed in ~27 cycles. + + The output would get more interesting when we run with more threads, say, -t 8: + + Benchmark Mode Cnt Score Error Units + JMHSample_35_Profilers.Atomic.test avgt 15 143.595 ± 1.968 ns/op + JMHSample_35_Profilers.Atomic.test:·CPI avgt 3 17.741 ± 28.761 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-load-misses avgt 3 0.175 ± 0.406 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-loads avgt 3 11.872 ± 0.786 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-store-misses avgt 3 0.184 ± 0.505 #/op + JMHSample_35_Profilers.Atomic.test:·L1-dcache-stores avgt 3 4.422 ± 0.561 #/op + JMHSample_35_Profilers.Atomic.test:·L1-icache-load-misses avgt 3 0.015 ± 0.083 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-loads avgt 3 0.015 ± 0.128 #/op + JMHSample_35_Profilers.Atomic.test:·LLC-stores avgt 3 1.036 ± 0.045 #/op + JMHSample_35_Profilers.Atomic.test:·branch-misses avgt 3 0.224 ± 0.492 #/op + JMHSample_35_Profilers.Atomic.test:·branches avgt 3 6.524 ± 2.873 #/op + JMHSample_35_Profilers.Atomic.test:·bus-cycles avgt 3 13.475 ± 14.502 #/op + JMHSample_35_Profilers.Atomic.test:·context-switches avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·cpu-migrations avgt 3 ≈ 10⁻⁶ #/op + JMHSample_35_Profilers.Atomic.test:·cycles avgt 3 537.874 ± 595.723 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-load-misses avgt 3 0.001 ± 0.006 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-loads avgt 3 12.032 ± 2.430 #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-store-misses avgt 3 ≈ 10⁻⁴ #/op + JMHSample_35_Profilers.Atomic.test:·dTLB-stores avgt 3 4.557 ± 0.948 #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-load-misses avgt 3 ≈ 10⁻³ #/op + JMHSample_35_Profilers.Atomic.test:·iTLB-loads avgt 3 0.016 ± 0.052 #/op + JMHSample_35_Profilers.Atomic.test:·instructions avgt 3 30.367 ± 15.052 #/op + JMHSample_35_Profilers.Atomic.test:·minor-faults avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·page-faults avgt 3 ≈ 10⁻⁵ #/op + JMHSample_35_Profilers.Atomic.test:·ref-cycles avgt 3 538.697 ± 590.183 #/op + + Note how this time the CPI is awfully high: 17 cycles per instruction! Indeed, we are making almost the + same ~30 instructions, but now they take >530 cycles. Other counters highlight why: we now have cache + misses on both loads and stores, on all levels of cache hierarchy. With a simple constant-footprint + like ours, that's an indication of sharing problems. Indeed, our AtomicLong is heavily-contended + with 8 threads. + + "perfnorm", again, can (and should!) be used with multiple forks, to properly estimate the metrics. + + The last, but not the least player on our field is -prof perfasm. It is important to follow up on + generated code when dealing with fine-grained benchmarks. We could employ PrintAssembly to dump the + generated code, but it will dump *all* the generated code, and figuring out what is related to our + benchmark is a daunting task. But we have "perf" that can tell what program addresses are really hot! + This enables us to contrast the assembly output. + + -prof perfasm would indeed contrast out the hottest loop in the generated code! It will also point + fingers at "lock xadd" as the hottest instruction in our code. Hardware counters are not very precise + about the instruction addresses, so sometimes they attribute the events to the adjacent code lines. + + Hottest code regions (>10.00% "cycles" events): + ....[Hottest Region 1].............................................................................. + [0x7f1824f87c45:0x7f1824f87c79] in org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub + + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201) + ; implicit exception: dispatches to 0x00007f1824f87d21 + 0x00007f1824f87c25: test %r11d,%r11d + 0x00007f1824f87c28: jne 0x00007f1824f87cbd ;*ifeq + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201) + 0x00007f1824f87c2e: mov $0x1,%ebp + 0x00007f1824f87c33: nopw 0x0(%rax,%rax,1) + 0x00007f1824f87c3c: xchg %ax,%ax ;*aload + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@13 (line 199) + 0x00007f1824f87c40: mov 0x8(%rsp),%r10 + 0.00% 0x00007f1824f87c45: mov 0xc(%r10),%r11d ;*getfield n + ; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@1 (line 280) + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 0.19% 0.02% 0x00007f1824f87c49: test %r11d,%r11d + 0x00007f1824f87c4c: je 0x00007f1824f87cad + 0x00007f1824f87c4e: mov $0x1,%edx + 0x00007f1824f87c53: lock xadd %rdx,0x10(%r12,%r11,8) + ;*invokevirtual getAndAddLong + ; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@8 (line 200) + ; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280) + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 95.20% 95.06% 0x00007f1824f87c5a: add $0x1,%rdx ;*ladd + ; - java.util.concurrent.atomic.AtomicLong::incrementAndGet@12 (line 200) + ; - org.openjdk.jmh.samples.JMHSample_35_Profilers$Atomic::test@4 (line 280) + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@16 (line 199) + 0.24% 0.00% 0x00007f1824f87c5e: mov 0x10(%rsp),%rsi + 0x00007f1824f87c63: callq 0x00007f1824e2b020 ; OopMap{[0]=Oop [8]=Oop [16]=Oop [24]=Oop off=232} + ;*invokevirtual consume + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@19 (line 199) + ; {optimized virtual_call} + 0.20% 0.01% 0x00007f1824f87c68: mov 0x18(%rsp),%r10 + 0x00007f1824f87c6d: movzbl 0x94(%r10),%r11d ;*getfield isDone + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@29 (line 201) + 0.00% 0x00007f1824f87c75: add $0x1,%rbp ; OopMap{r10=Oop [0]=Oop [8]=Oop [16]=Oop [24]=Oop off=249} + ;*ifeq + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@32 (line 201) + 0.20% 0.01% 0x00007f1824f87c79: test %eax,0x15f36381(%rip) # 0x00007f183aebe000 + ; {poll} + 0x00007f1824f87c7f: test %r11d,%r11d + 0x00007f1824f87c82: je 0x00007f1824f87c40 ;*aload_2 + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@35 (line 202) + 0x00007f1824f87c84: mov $0x7f1839be4220,%r10 + 0x00007f1824f87c8e: callq *%r10 ;*invokestatic nanoTime + ; - org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub@36 (line 202) + 0x00007f1824f87c91: mov (%rsp),%r10 + .................................................................................................... + 96.03% 95.10% + + perfasm would also print the hottest methods to show if we indeed spending time in our benchmark. Most of the time, + it can demangle VM and kernel symbols as well: + + ....[Hottest Methods (after inlining)].............................................................. + 96.03% 95.10% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_avgt_jmhStub + 0.73% 0.78% org.openjdk.jmh.samples.generated.JMHSample_35_Profilers_Atomic_test::test_AverageTime + 0.63% 0.00% org.openjdk.jmh.infra.Blackhole::consume + 0.23% 0.25% native_write_msr_safe ([kernel.kallsyms]) + 0.09% 0.05% _raw_spin_unlock ([kernel.kallsyms]) + 0.09% 0.00% [unknown] (libpthread-2.19.so) + 0.06% 0.07% _raw_spin_lock ([kernel.kallsyms]) + 0.06% 0.04% _raw_spin_unlock_irqrestore ([kernel.kallsyms]) + 0.06% 0.05% _IO_fwrite (libc-2.19.so) + 0.05% 0.03% __srcu_read_lock; __srcu_read_unlock ([kernel.kallsyms]) + 0.04% 0.05% _raw_spin_lock_irqsave ([kernel.kallsyms]) + 0.04% 0.06% vfprintf (libc-2.19.so) + 0.04% 0.01% mutex_unlock ([kernel.kallsyms]) + 0.04% 0.01% _nv014306rm ([nvidia]) + 0.04% 0.04% rcu_eqs_enter_common.isra.47 ([kernel.kallsyms]) + 0.04% 0.02% mutex_lock ([kernel.kallsyms]) + 0.03% 0.07% __acct_update_integrals ([kernel.kallsyms]) + 0.03% 0.02% fget_light ([kernel.kallsyms]) + 0.03% 0.01% fput ([kernel.kallsyms]) + 0.03% 0.04% rcu_eqs_exit_common.isra.48 ([kernel.kallsyms]) + 1.63% 2.26% <...other 319 warm methods...> + .................................................................................................... + 100.00% 98.97% + + ....[Distribution by Area].......................................................................... + 97.44% 95.99% + 1.60% 2.42% + 0.47% 0.78% + 0.22% 0.29% + 0.15% 0.07% + 0.07% 0.38% + 0.05% 0.06% + 0.00% 0.00% + 0.00% 0.00% + .................................................................................................... + 100.00% 100.00% + + Since program addresses change from fork to fork, it does not make sense to run perfasm with more than + a single fork. + */ + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_36_BranchPrediction.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_36_BranchPrediction.java new file mode 100644 index 00000000..aa3ab9bf --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_36_BranchPrediction.java @@ -0,0 +1,138 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.profile.DTraceAsmProfiler; +import org.openjdk.jmh.profile.LinuxPerfNormProfiler; +import org.openjdk.jmh.profile.LinuxPerfProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * 分支预测,底层做的一件很牛逼的运行优化 + * @author childe + * @date 2018-10-20 + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +@State(Scope.Benchmark) +public class JMHSample_36_BranchPrediction { + + /* + * This sample serves as a warning against regular data sets. + * + * It is very tempting to present a regular data set to benchmark, either due to + * naive generation strategy, or just from feeling better about regular data sets. + * Unfortunately, it frequently backfires: the regular datasets are known to be + * optimized well by software and hardware. This example exploits one of these + * optimizations: branch prediction. + * + * Imagine our benchmark selects the branch based on the array contents, as + * we are streaming through it: + * + * 这个例子用作对常规数据集的警告。 + * + * 由于天真的生成策略,或者只是感觉对常规数据集更好,将常规数据集呈现为基准测试是非常诱人的。 + * 不幸的是,它经常事与愿违:已知常规数据集可以通过软件和硬件很好地进行优化。 + * 此示例利用了以下优化之一:分支预测。 + * + * 假设我们的基准测试通过数组的内容来选择分支,因为我们正在通过它进行流式传输: + */ + + private static final int COUNT = 1024 * 1024; + + private byte[] sorted; + private byte[] unsorted; + + @Setup + public void setup() { + sorted = new byte[COUNT]; + unsorted = new byte[COUNT]; + Random random = new Random(1234); + random.nextBytes(sorted); + random.nextBytes(unsorted); + Arrays.sort(sorted); + } + + @Benchmark + @OperationsPerInvocation(COUNT) + public void sorted(Blackhole bh1, Blackhole bh2) { + for (byte v : sorted) { + if (v > 0) { + bh1.consume(v); + } else { + bh2.consume(v); + } + } + } + + @Benchmark + @OperationsPerInvocation(COUNT) + public void unsorted(Blackhole bh1, Blackhole bh2) { + for (byte v : unsorted) { + if (v > 0) { + bh1.consume(v); + } else { + bh2.consume(v); + } + } + } + + /* + There is a substantial difference in performance for these benchmarks! + + It is explained by good branch prediction in "sorted" case, and branch mispredicts in "unsorted" + case. -prof perfnorm conveniently highlights that, with larger "branch-misses", and larger "CPI" + for "unsorted" case: + + Benchmark Mode Cnt Score Error Units + JMHSample_36_BranchPrediction.sorted avgt 25 2.160 ± 0.049 ns/op + JMHSample_36_BranchPrediction.sorted:·CPI avgt 5 0.286 ± 0.025 #/op + JMHSample_36_BranchPrediction.sorted:·branch-misses avgt 5 ≈ 10⁻⁴ #/op + JMHSample_36_BranchPrediction.sorted:·branches avgt 5 7.606 ± 1.742 #/op + JMHSample_36_BranchPrediction.sorted:·cycles avgt 5 8.998 ± 1.081 #/op + JMHSample_36_BranchPrediction.sorted:·instructions avgt 5 31.442 ± 4.899 #/op + + JMHSample_36_BranchPrediction.unsorted avgt 25 5.943 ± 0.018 ns/op + JMHSample_36_BranchPrediction.unsorted:·CPI avgt 5 0.775 ± 0.052 #/op + JMHSample_36_BranchPrediction.unsorted:·branch-misses avgt 5 0.529 ± 0.026 #/op <--- OOPS + JMHSample_36_BranchPrediction.unsorted:·branches avgt 5 7.841 ± 0.046 #/op + JMHSample_36_BranchPrediction.unsorted:·cycles avgt 5 24.793 ± 0.434 #/op + JMHSample_36_BranchPrediction.unsorted:·instructions avgt 5 31.994 ± 2.342 #/op + + It is an open question if you want to measure only one of these tests. In many cases, you have to measure + both to get the proper best-case and worst-case estimate! + */ + + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_36 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_36_BranchPrediction.class.getSimpleName() + ".*") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_37_CacheAccess.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_37_CacheAccess.java new file mode 100644 index 00000000..5656583f --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_37_CacheAccess.java @@ -0,0 +1,126 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/22 17:10 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +@State(Scope.Benchmark) +public class JMHSample_37_CacheAccess { + + /* + * This sample serves as a warning against subtle differences in cache access patterns. + * + * Many performance differences may be explained by the way tests are accessing memory. + * In the example below, we walLevelk the matrix either row-first, or col-first: + */ + + private final static int COUNT = 4096; + private final static int MATRIX_SIZE = COUNT * COUNT; + + private int[][] matrix; + + @Setup + public void setup() { + matrix = new int[COUNT][COUNT]; + Random random = new Random(1234); + for (int i = 0; i < COUNT; i++) { + for (int j = 0; j < COUNT; j++) { + matrix[i][j] = random.nextInt(); + } + } + } + + @Benchmark + @OperationsPerInvocation(MATRIX_SIZE) + public void colFirst(Blackhole bh) { + for (int c = 0; c < COUNT; c++) { + for (int r = 0; r < COUNT; r++) { + bh.consume(matrix[r][c]); + } + } + } + + @Benchmark + @OperationsPerInvocation(MATRIX_SIZE) + public void rowFirst(Blackhole bh) { + for (int r = 0; r < COUNT; r++) { + for (int c = 0; c < COUNT; c++) { + bh.consume(matrix[r][c]); + } + } + } + + /* + Notably, colFirst accesses are much slower, and that's not a surprise: Java's multidimensional + arrays are actually rigged, being one-dimensional arrays of one-dimensional arrays. Therefore, + pulling n-th element from each of the inner array induces more cache misses, when matrix is large. + -prof perfnorm conveniently highlights that, with >2 cache misses per one benchmark op: + + 值得注意的是,colFirst访问速度要慢得多,这并不奇怪: + Java的多维数组实际上是被操纵的,是一维数组的一维数组。 + 因此,当矩阵很大时,从每个内部阵列中拉出第n个元素会引起更多的高速缓存未命中。 + -prof perfnorm清晰的展示出,每个基准操作有2个缓存未命中: + + Benchmark Mode Cnt Score Error Units + JMHSample_37_MatrixCopy.colFirst avgt 25 5.306 ± 0.020 ns/op + JMHSample_37_MatrixCopy.colFirst:·CPI avgt 5 0.621 ± 0.011 #/op + JMHSample_37_MatrixCopy.colFirst:·L1-dcache-load-misses avgt 5 2.177 ± 0.044 #/op <-- OOPS + JMHSample_37_MatrixCopy.colFirst:·L1-dcache-loads avgt 5 14.804 ± 0.261 #/op + JMHSample_37_MatrixCopy.colFirst:·LLC-loads avgt 5 2.165 ± 0.091 #/op + JMHSample_37_MatrixCopy.colFirst:·cycles avgt 5 22.272 ± 0.372 #/op + JMHSample_37_MatrixCopy.colFirst:·instructions avgt 5 35.888 ± 1.215 #/op + + JMHSample_37_MatrixCopy.rowFirst avgt 25 2.662 ± 0.003 ns/op + JMHSample_37_MatrixCopy.rowFirst:·CPI avgt 5 0.312 ± 0.003 #/op + JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-load-misses avgt 5 0.066 ± 0.001 #/op + JMHSample_37_MatrixCopy.rowFirst:·L1-dcache-loads avgt 5 14.570 ± 0.400 #/op + JMHSample_37_MatrixCopy.rowFirst:·LLC-loads avgt 5 0.002 ± 0.001 #/op + JMHSample_37_MatrixCopy.rowFirst:·cycles avgt 5 11.046 ± 0.343 #/op + JMHSample_37_MatrixCopy.rowFirst:·instructions avgt 5 35.416 ± 1.248 #/op + + So, when comparing two different benchmarks, you have to follow up if the difference is caused + by the memory locality issues. + + 所以,在比较两个不同的基准时,如果差异是由内存局部性问题引起的,则必须跟进。 + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_37 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_37_CacheAccess.class.getSimpleName() + ".*") + .output("JMHSample_37_CacheAccess.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} \ No newline at end of file diff --git a/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_38_PerInvokeSetup.java b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_38_PerInvokeSetup.java new file mode 100644 index 00000000..39ad0f77 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/jmh/JMHSample_38_PerInvokeSetup.java @@ -0,0 +1,177 @@ +package com.renchao.jmh; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * desc + * + * @author childe + * @date 2018/10/22 19:47 + **/ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(5) +public class JMHSample_38_PerInvokeSetup { + + /* + * This example highlights the usual mistake in non-steady-state benchmarks. + * + * Suppose we want to test how long it takes to bubble sort an array. Naively, + * we could make the test that populates an array with random (unsorted) values, + * and calls sort on it over and over again: + * + * 此示例突显非稳态基准测试(non-steady-state benchmarks)中的常见错误。 + * + * 假设我们要测试对数组进行冒泡排序的耗时。 + * 天真地,我们可以使用随机(未排序)值填充数组,并一遍又一遍地调用sort测试: + */ + + private void bubbleSort(byte[] b) { + boolean changed = true; + while (changed) { + changed = false; + for (int c = 0; c < b.length - 1; c++) { + if (b[c] > b[c + 1]) { + byte t = b[c]; + b[c] = b[c + 1]; + b[c + 1] = t; + changed = true; + } + } + } + } + + // Could be an implicit State instead, but we are going to use it + // as the dependency in one of the tests below + // 可能是一个隐式状态,但我们将在下面的一个测试中依赖它 + + @State(Scope.Benchmark) + public static class Data { + + @Param({"1", "16", "256"}) + int count; + + byte[] arr; + + @Setup + public void setup() { + arr = new byte[count]; + Random random = new Random(1234); + random.nextBytes(arr); + } + } + + @Benchmark + public byte[] measureWrong(Data d) { + bubbleSort(d.arr); + return d.arr; + } + + /* + * The method above is subtly wrong: it sorts the random array on the first invocation + * only. Every subsequent call will "sort" the already sorted array. With bubble sort, + * that operation would be significantly faster! + * + * This is how we might *try* to measure it right by making a copy in Level.Invocation + * setup. However, this is susceptible to the problems described in Level.Invocation + * Javadocs, READ AND UNDERSTAND THOSE DOCS BEFORE USING THIS APPROACH. + * + * 上面的方法是巧妙的错误:它只在第一次调用时对随机数组进行排序。 + * 后续每个调用都将"排序"已排序的数组。 + * 通过冒泡排序,操作速度会明显加快! + * + * 我们可以尝试通过在Level.Invocation中制作数组的副本来正确测量它。 + * 在使用Level.Invocation这些方法之前请阅读并理解这些文档。 + */ + + @State(Scope.Thread) + public static class DataCopy { + byte[] copy; + + @Setup(Level.Invocation) + public void setup2(Data d) { + copy = Arrays.copyOf(d.arr, d.arr.length); + } + } + + @Benchmark + public byte[] measureNeutral(DataCopy d) { + bubbleSort(d.copy); + return d.copy; + } + + /* + * In an overwhelming majority of cases, the only sensible thing to do is to suck up + * the per-invocation setup costs into a benchmark itself. This work well in practice, + * especially when the payload costs dominate the setup costs. + * + * 在绝大多数情况下,唯一明智的做法是将每次调用设置成本吸收到基准测试本身。 + * + * 这在实践中很有效,特别是当有效载荷成本主导设置成本时 + * (即:测试本身耗时远远大于准备数据时,准备数据时间对测试本身的影响可以忽略)。 + */ + + @Benchmark + public byte[] measureRight(Data d) { + byte[] c = Arrays.copyOf(d.arr, d.arr.length); + bubbleSort(c); + return c; + } + + /* + Benchmark (count) Mode Cnt Score Error Units + + JMHSample_38_PerInvokeSetup.measureWrong 1 avgt 25 2.408 ± 0.011 ns/op + JMHSample_38_PerInvokeSetup.measureWrong 16 avgt 25 8.286 ± 0.023 ns/op + JMHSample_38_PerInvokeSetup.measureWrong 256 avgt 25 73.405 ± 0.018 ns/op + + JMHSample_38_PerInvokeSetup.measureNeutral 1 avgt 25 15.835 ± 0.470 ns/op + JMHSample_38_PerInvokeSetup.measureNeutral 16 avgt 25 112.552 ± 0.787 ns/op + JMHSample_38_PerInvokeSetup.measureNeutral 256 avgt 25 58343.848 ± 991.202 ns/op + + JMHSample_38_PerInvokeSetup.measureRight 1 avgt 25 6.075 ± 0.018 ns/op + JMHSample_38_PerInvokeSetup.measureRight 16 avgt 25 102.390 ± 0.676 ns/op + JMHSample_38_PerInvokeSetup.measureRight 256 avgt 25 58812.411 ± 997.951 ns/op + + We can clearly see that "measureWrong" provides a very weird result: it "sorts" way too fast. + "measureNeutral" is neither good or bad: while it prepares the data for each invocation correctly, + the timing overheads are clearly visible. These overheads can be overwhelming, depending on + the thread count and/or OS flavor. + + 明显可以看出"measureWrong"跑出来一个奇怪的结果:它的排序太快了。 + "measureNeutral"一般般:它正确地为每个调用准备数据,时间开销清晰可见。在不同的线程数 和/或 OS风格下,这些开销可能会非常大。 + */ + + /* + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_38 + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + JMHSample_38_PerInvokeSetup.class.getSimpleName() + ".*") + .output("JMHSample_38_PerInvokeSetup.sampleLog") + .build(); + + new Runner(opt).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/spring/AnnotationUtilsTest.java b/Test/MyMaven/src/main/java/com/renchao/spring/AnnotationUtilsTest.java new file mode 100644 index 00000000..265069fb --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/spring/AnnotationUtilsTest.java @@ -0,0 +1,14 @@ +package com.renchao.spring; + +import com.renchao.spring.bean.Anonymous; +import com.renchao.spring.bean.TestController; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationUtils; + +public class AnnotationUtilsTest { + public static void main(String[] args) { + Anonymous annotation = AnnotationUtils.findAnnotation(TestController.class, Anonymous.class); + System.out.println(annotation); + System.out.println(AnnotatedElementUtils.isAnnotated(TestController.class, Anonymous.class)); + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/spring/bean/Anonymous.java b/Test/MyMaven/src/main/java/com/renchao/spring/bean/Anonymous.java new file mode 100644 index 00000000..faf076ab --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/spring/bean/Anonymous.java @@ -0,0 +1,19 @@ +package com.renchao.spring.bean; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author admin + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/Test/MyMaven/src/main/java/com/renchao/spring/bean/BaseController.java b/Test/MyMaven/src/main/java/com/renchao/spring/bean/BaseController.java new file mode 100644 index 00000000..ae4fe40c --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/spring/bean/BaseController.java @@ -0,0 +1,5 @@ +package com.renchao.spring.bean; + +@Anonymous +public class BaseController { +} diff --git a/Test/MyMaven/src/main/java/com/renchao/spring/bean/TestController.java b/Test/MyMaven/src/main/java/com/renchao/spring/bean/TestController.java new file mode 100644 index 00000000..bc2ed67a --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/spring/bean/TestController.java @@ -0,0 +1,15 @@ +package com.renchao.spring.bean; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Anonymous +public class TestController extends BaseController { + + @RequestMapping({"/cc","dd"}) + @Anonymous + public String testF() { + return "testFeign.test01()"; + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/test/DateDemo.java b/Test/MyMaven/src/main/java/com/renchao/test/DateDemo.java new file mode 100644 index 00000000..d74c2770 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/DateDemo.java @@ -0,0 +1,70 @@ +package com.renchao.test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +import lombok.Data; +import org.hibernate.validator.HibernateValidator; +import org.junit.Test; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.validation.annotation.Validated; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.Set; + + +public class DateDemo { + public static void main(String[] args) throws JsonProcessingException { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); + SimpleModule simpleModule = new SimpleModule() +// .addSerializer(Long.class, ToStringSerializer.instance) + .addSerializer(Long.TYPE, ToStringSerializer.instance); + builder.modules(simpleModule); + ObjectMapper build = builder.build(); + String str = "{\"id\":22,\"name\":\"aaa\",\"age\":55}"; + Bean bean1 = build.readValue(str, Bean.class); + + + Bean bean = new Bean(); + bean.setId(22L); + bean.setName("aaa"); + String s = build.writeValueAsString(bean); + System.out.println(s); + } + + @Data + static class Bean { + @NotBlank(message = "aabb") + private String name; + @NotNull(message = "唯一标识不能为空") + private Long id; + + @NotNull(message = "cc") + private String age; + } + + @Test + public void test01() { + Bean bean = new Bean(); + Validator validator = Validation.byProvider(HibernateValidator.class) + .configure().failFast(false).buildValidatorFactory().getValidator(); + Set> validate = validator.validate(bean); + for (ConstraintViolation violation : validate) { + System.out.println(violation.getLeafBean()); + System.out.println(violation.getMessage()); + System.out.println(violation.getMessageTemplate()); + System.out.println(violation.getPropertyPath()); + System.out.println("======================"); + } + + + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/test/DemoData.java b/Test/MyMaven/src/main/java/com/renchao/test/DemoData.java new file mode 100644 index 00000000..0ca674c4 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/DemoData.java @@ -0,0 +1,38 @@ +package com.renchao.test; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DemoData { + public static void main(String[] args) { + String filePath = "C:/Users/RENCHAO/Desktop/资料/easyexcel-user1.xls"; + List list = EasyExcel.read(filePath).head(UserEntity.class).sheet().doReadSync(); + System.out.println(list); + } + + + @Test + public void test01() { + String str = "[{\"component\":\"聚酯树脂\",\"content\":\"48%\"},{\"component\":\"乙酸乙酯\",\"content\":\"52%\"}]"; +// String str = "ssss"; + + System.out.println(JSON.isValid(str)); + List> list = JSON.parseObject(str, new TypeReference>>() {}); + for (Map map : list) { + System.out.println(map.get("component") + "::" + map.get("content")); + } + ArrayList strings = new ArrayList<>(); + strings.add("cc"); + strings.add("dd"); + System.out.println(String.join("||", strings)); + } +} + diff --git a/Test/MyMaven/src/main/java/com/renchao/test/JMHTest.java b/Test/MyMaven/src/main/java/com/renchao/test/JMHTest.java new file mode 100644 index 00000000..a94e07da --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/JMHTest.java @@ -0,0 +1,80 @@ +package com.renchao.test; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +//@BenchmarkMode(Mode.AverageTime) +//@OutputTimeUnit(TimeUnit.MICROSECONDS) +//@State(Scope.Thread) +public class JMHTest { + /** + * 测试数据 + */ + private final static String DATA = "大白有点菜"; + +// private List arrayList = new ArrayList<>(); +// private List linkedList = new LinkedList<>(); + +// /** +// * 初始化 ArrayList 和 LinkedList +// */ +// @Setup(Level.Iteration) +// public void setUp() +// { +// this.arrayList = new ArrayList<>(); +// this.linkedList = new LinkedList<>(); +// } + +// /** +// * ArrayList的add方法 +// * @return +// */ +// @Benchmark +// public List arrayListAdd() { +// this.arrayList.add(DATA); +// return arrayList; +// } + +// /** +// * LinkedList的add方法 +// * @return +// */ +// @Benchmark +// public List linkedListAdd() { +// this.linkedList.add(DATA); +// return this.linkedList; +// } + + @Benchmark + public int Test() { + int i = 5; + int k = i * 8; + return k; + } + + public static void main(String[] args) throws RunnerException { + final Options opts = new OptionsBuilder() + .include(JMHTest.class.getSimpleName()) + .forks(1) +// .output("Benchmark1.log") + .build(); + new Runner(opts).run(); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/test/JMHTest2.java b/Test/MyMaven/src/main/java/com/renchao/test/JMHTest2.java new file mode 100644 index 00000000..dff56786 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/JMHTest2.java @@ -0,0 +1,96 @@ +package com.renchao.test; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class JMHTest2 { + /** + * Most of the time, you need to maintain some state while the benchmark is + * running. Since JMH is heavily used to build concurrent benchmarks, we + * opted for an explicit notion of state-bearing objects. + * + * Below are two state objects. Their class names are not essential, it + * matters they are marked with @State. These objects will be instantiated + * on demand, and reused during the entire benchmark trial. + * + * The important property is that state is always instantiated by one of + * those benchmark threads which will then have the access to that state. + * That means you can initialize the fields as if you do that in worker + * threads (ThreadLocals are yours, etc). + */ + + @State(Scope.Benchmark) + public static class BenchmarkState { + volatile double x = Math.PI; + } + + @State(Scope.Thread) + public static class ThreadState { + volatile double x = Math.PI; + } + + /* + * Benchmark methods can reference the states, and JMH will inject the + * appropriate states while calling these methods. You can have no states at + * all, or have only one state, or have multiple states referenced. This + * makes building multi-threaded benchmark a breeze. + * + * For this exercise, we have two methods. + */ + + @Benchmark + public void measureUnshared(ThreadState state) { + // All benchmark threads will call in this method. + // + // However, since ThreadState is the Scope.Thread, each thread + // will have it's own copy of the state, and this benchmark + // will measure unshared case. + state.x++; + } + + @Benchmark + public void measureShared(BenchmarkState state) { + // All benchmark threads will call in this method. + // + // Since BenchmarkState is the Scope.Benchmark, all threads + // will share the state instance, and we will end up measuring + // shared case. + state.x++; + } + + + + /** + * ============================== HOW TO RUN THIS TEST: ==================================== + * + * You are expected to see the drastic difference in shared and unshared cases, + * because you either contend for single memory location, or not. This effect + * is more articulated on large machines. + * + * You can run this test: + * + * a) Via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar JMHSample_03 -t 4 -f 1 + * (we requested 4 threads, single fork; there are also other options, see -h) + * + * b) Via the Java API: + * (see the JMH homepage for possible caveats when running from IDE: + * http://openjdk.java.net/projects/code-tools/jmh/) + */ + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(JMHTest2.class.getSimpleName()) + .threads(4) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/test/MyTest.java b/Test/MyMaven/src/main/java/com/renchao/test/MyTest.java new file mode 100644 index 00000000..f2276509 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/MyTest.java @@ -0,0 +1,179 @@ +package com.renchao.test; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.CryptoException; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.renchao.utils.RsaUtil; +import org.apache.commons.lang3.time.DateUtils; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.TimeZone; +import java.util.regex.Pattern; + +public class MyTest { + private static final Pattern pattern = Pattern.compile("[+/]"); + public static void main(String[] args) throws JsonProcessingException { + String str = "ee, tt,,cc "; + List split = StrUtil.split(str, ",", true, true); + split.forEach(System.out::println); + } + + @Test + public void test01() { + String str = "I:X,II:X.Y,III:X.Y.Z"; + String[] split = str.split(","); + HashMap> map = new HashMap<>(); + for (String s : split) { + String[] m = s.split(":"); + map.put(m[0], CharSequenceUtil.split(m[1], ".")); + } + System.out.println(); + } + + + + @Test + public void test03() throws IOException { + String str = "任超"; + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + byte[] bytes1 = str.getBytes(StandardCharsets.ISO_8859_1); + + System.out.println(new String(bytes,StandardCharsets.UTF_8)); + System.out.println(new String(bytes1,StandardCharsets.ISO_8859_1)); + } + + @Test + public void test04() { + String str = "2bce7d71-d60b-4858-a852-15380eba6c84"; + String s = RsaUtil.decryptStr(str); + System.out.println(s); + } + + + @Test + public void test05() { + String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB"; + String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ=="; + + RSA rsa = new RSA(privateKey,publicKey); + + //获得私钥 + System.out.println("获得私钥:" + rsa.getPrivateKey()); + System.out.println("获得私钥:" + rsa.getPrivateKeyBase64()); + //获得公钥 + System.out.println("获得公钥:" + rsa.getPublicKey()); + System.out.println("获得公钥:" + rsa.getPublicKeyBase64()); + + //公钥加密,私钥解密 + byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); + System.out.println("加密后的串:" + rsa.encryptBase64("我是一段测试aaaa",KeyType.PublicKey)); + byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey); + System.out.println("公钥加密,私钥解密:" + new String(decrypt)); + + //Junit单元测试 + //Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8)); + + //私钥加密,公钥解密 + byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey); + System.out.println("加密后的串:" + rsa.encryptBase64("url=/checkInInfo/reviewResult&token=2bce7d71-d60b-4858-a852-15380eba6c84×tamp=1574821902392",KeyType.PrivateKey)); + byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey); + System.out.println("私钥加密,公钥解密:" + new String(decrypt2)); + } + + @Test + public void test06() { + String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB"; + String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ=="; + + RSA rsa = new RSA(null,publicKey); + + String str = "mlVdU379GcmXAFJvtdJ5rhoLrTOg89/efKmQvCtcYnEFUdZYuivURy0T62S0jN+KCKJ70LpfX7uoBMitBixAK8sUNq9YF9GF4GmnhSJHHklsgEujz+ULwPIG3kbhYpjKCl0RQXwdjyz0htNgLn6MR6M7JQ2fAbtR6nDL1Z2W8Ig="; + + String decrypt = rsa.decryptStr(str, KeyType.PublicKey); + System.out.println(decrypt); + } + + /** + * 私钥加密 + */ + @Test + public void test07() throws UnsupportedEncodingException { + String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB"; + String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ=="; + + RSA rsa = new RSA(privateKey,null); + + String str = "url=/checkInInfo/reviewResult&token=5bce7d71-d60b-4858-a852-15380eba6c84×tamp=1681440129945"; + + String base64 = rsa.encryptBase64(str, KeyType.PrivateKey); + System.out.println(base64); + System.out.println(URLEncoder.encode(base64,"UTF-8")); + System.out.println("==========PublicKey解密============="); + + RSA rsa2 = new RSA(null, publicKey); + try { + System.out.println(rsa2.decryptStr(base64 + "ss", KeyType.PublicKey)); + } catch (CryptoException e) { + System.out.println("发生异常===================" + e); + } + } + + + @Test + public void test() { + List list = new ArrayList<>(); + list.add("聚酯树脂"); + list.add("聚酯树脂33"); + list.add("4,4'-(1-甲基亚乙基)双苯酚与(氯甲基)环氧乙烷的聚合物(平均分子量≤700)"); + System.out.println(JSONUtil.toJsonStr(list)); + System.out.println(this.getClass().getResource("/")); + } + + @Test + public void test00() throws IOException { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream("template/declarationInformationExport.docx"); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + byte[] byteArray = outputStream.toByteArray(); + long l = System.currentTimeMillis(); + for (int i = 0; i < 50000; i++) { +// InputStream inputStream2 = getClass().getClassLoader().getResourceAsStream("template/declarationInformationExport.docx"); +// InputStream inputStream2 = new FileInputStream("C:\\Users\\RENCHAO\\IdeaProjects\\Test\\MyMaven\\src\\main\\resources\\template\\declarationInformationExport.docx"); + InputStream inputStream2 = new ByteArrayInputStream(byteArray); + byte[] bytes = new byte[1024 * 1024]; + inputStream2.read(bytes); + } + System.out.println(System.currentTimeMillis() - l); + } + + @Test + public void test08() { + System.out.println(new Date()); + System.out.println(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant())); + System.out.println(TimeZone.getDefault()); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/test/UserEntity.java b/Test/MyMaven/src/main/java/com/renchao/test/UserEntity.java new file mode 100644 index 00000000..b1642200 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/UserEntity.java @@ -0,0 +1,38 @@ +package com.renchao.test; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserEntity { + @ExcelProperty(value = "姓名") + private String name; + + @ExcelProperty(value = "年龄") + private int age; + + @ExcelProperty(value = "操作时间") + private Date time; + + public static void main(String[] args) { + List dataList = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + UserEntity userEntity = new UserEntity(); + userEntity.setName("张三" + i); + userEntity.setAge(20 + i); + userEntity.setTime(new Date(System.currentTimeMillis() + i)); + dataList.add(userEntity); + } + EasyExcel.write("C:/Users/RENCHAO/Desktop/资料/easyexcel-user1.xls", UserEntity.class).sheet("用户信息").doWrite(dataList); + } +} + diff --git a/Test/MyMaven/src/main/java/com/renchao/test/ValidationUtil.java b/Test/MyMaven/src/main/java/com/renchao/test/ValidationUtil.java new file mode 100644 index 00000000..77a5f206 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/test/ValidationUtil.java @@ -0,0 +1,123 @@ +package com.renchao.test; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; + +import lombok.Data; +import org.hibernate.validator.HibernateValidator; +public class ValidationUtil { + /** + * 开启快速结束模式 failFast (true) + */ + private static final Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator(); + /** + * 校验对象 + * + * @param t bean + * @param groups 校验组 + * @return ValidResult + */ + public static ValidResult validateBean(T t,Class...groups) { + ValidResult result = new ValidResult(); + Set> violationSet = validator.validate(t,groups); + boolean hasError = violationSet != null && violationSet.size() > 0; + result.setHasErrors(hasError); + if (hasError) { + for (ConstraintViolation violation : violationSet) { + result.addError(violation.getPropertyPath().toString(), violation.getMessage()); + } + } + return result; + } + /** + * 校验bean的某一个属性 + * + * @param obj bean + * @param propertyName 属性名称 + * @return ValidResult + */ + public static ValidResult validateProperty(T obj, String propertyName) { + ValidResult result = new ValidResult(); + Set> violationSet = validator.validateProperty(obj, propertyName); + boolean hasError = violationSet != null && violationSet.size() > 0; + result.setHasErrors(hasError); + if (hasError) { + for (ConstraintViolation violation : violationSet) { + result.addError(propertyName, violation.getMessage()); + } + } + return result; + } + /** + * 校验结果类 + */ + @Data + static class ValidResult { + + /** + * 是否有错误 + */ + private boolean hasErrors; + + /** + * 错误信息 + */ + private List errors; + + public ValidResult() { + this.errors = new ArrayList<>(); + } + public boolean hasErrors() { + return hasErrors; + } + + public void setHasErrors(boolean hasErrors) { + this.hasErrors = hasErrors; + } + + /** + * 获取所有验证信息 + * @return 集合形式 + */ + public List getAllErrors() { + return errors; + } + /** + * 获取所有验证信息 + * @return 字符串形式 + */ + public String getErrors(){ + StringBuilder sb = new StringBuilder(); + for (ErrorMessage error : errors) { + sb.append(error.getPropertyPath()).append(":").append(error.getMessage()).append(" \n"); + } + return sb.toString(); + } + + public void addError(String propertyName, String message) { + this.errors.add(new ErrorMessage(propertyName, message)); + } + } + + @Data + static class ErrorMessage { + + private String propertyPath; + + private String message; + + public ErrorMessage() { + } + + public ErrorMessage(String propertyPath, String message) { + this.propertyPath = propertyPath; + this.message = message; + } + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/utils/RsaUtil.java b/Test/MyMaven/src/main/java/com/renchao/utils/RsaUtil.java new file mode 100644 index 00000000..caf5f8ab --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/utils/RsaUtil.java @@ -0,0 +1,15 @@ +package com.renchao.utils; + +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; + +public class RsaUtil { + + private static final RSA PUB_RSA = new RSA(null, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMdrstRnZe65DiLQtGUuA33JKdwf4P4oBSd0itbUYmn/8N4Z9kVKap/yLZhXU8JRoI7yJLizR0iszlllbrRor+HBhB56NaHnZ1zTshELII/QXLCbqfevVDOcfvMs5OGa7pThxn3r08H7RCdko9xVWYDZvf3xJzrGe9Zj//zbe5ywIDAQAB"); + + + public static String decryptStr(String content) { + return PUB_RSA.decryptStr(content, KeyType.PublicKey); + } + +} diff --git a/Test/MyMaven/src/main/java/com/renchao/word/XWPFDocumentDemo.java b/Test/MyMaven/src/main/java/com/renchao/word/XWPFDocumentDemo.java new file mode 100644 index 00000000..bd303134 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/word/XWPFDocumentDemo.java @@ -0,0 +1,226 @@ +package com.renchao.word; + +import com.microsoft.schemas.office.office.CTLock; +import com.microsoft.schemas.vml.CTGroup; +import com.microsoft.schemas.vml.CTShape; +import com.microsoft.schemas.vml.CTShapetype; +import com.microsoft.schemas.vml.CTTextPath; +import com.microsoft.schemas.vml.STExt; +import com.microsoft.schemas.vml.STTrueFalse; +import org.apache.poi.util.IOUtils; +import org.apache.poi.wp.usermodel.HeaderFooterType; +import org.apache.poi.xwpf.extractor.XWPFWordExtractor; +import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFHeader; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.apache.poi.xwpf.usermodel.XWPFTable; +import org.apache.poi.xwpf.usermodel.XWPFTableCell; +import org.apache.poi.xwpf.usermodel.XWPFTableRow; +import org.junit.Test; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class XWPFDocumentDemo { + + private static byte[] b = null; + + + public static void main(String[] args) throws IOException { +// File file = new File("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\allReviewInfoTemplate.docx"); + FileOutputStream outputStream = new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\abcd.zip"); + + // b 22530 + long l = System.currentTimeMillis(); + ZipOutputStream out = new ZipOutputStream(outputStream); + for (int i = 0; i < 1000; i++) { + extracted(out, i); + } + out.close(); + outputStream.close(); + + System.out.println(System.currentTimeMillis() - l); + + } + + private static byte[] getByte() throws IOException { + if (b != null) { + return b; + } + InputStream is = new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\aaa.docx"); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + b = outputStream.toByteArray(); + return b; + + } + + private static void extracted(ZipOutputStream out, int i) throws IOException { + XWPFDocument doc = new XWPFDocument(new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\aaa.docx")); +// XWPFDocument doc = new XWPFDocument(new ByteArrayInputStream(getByte())); + + List tables = doc.getTables(); + + List cells; + List rows; + // 委托单位 + rows = tables.get(0).getRows(); + cells = rows.get(0).getTableCells(); + cells.get(1).setText("My测试的委托单位"); + + // 违规货物信息 + rows = tables.get(1).getRows(); + cells = rows.get(0).getTableCells(); + cells.get(1).setText("inOutType"); + cells.get(3).setText("goodsName"); + cells.get(5).setText("goodsId"); + + cells = rows.get(1).getTableCells(); + cells.get(1).setText("unNo"); + cells.get(3).setText("composition"); + cells.get(5).setText("transportNameCn"); + + cells = rows.get(2).getTableCells(); + cells.get(1).setText("dangerType"); + cells.get(3).setText("packageClass"); + cells.get(5).setText("chemicalName"); + + cells = rows.get(3).getTableCells(); + cells.get(1).setText("remarks"); + + + // 危规货物属性 + rows = tables.get(2).getRows(); + cells = rows.get(0).getTableCells(); + cells.get(1).setText("hsCode"); + cells.get(3).setText("ciqName"); + cells.get(5).setText("ciqNo"); + + cells = rows.get(1).getTableCells(); + cells.get(1).setText("goodsAttr"); + cells.get(3).setText("packSign"); + + // 输出到本地目录 +// FileOutputStream fos = new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\aaa\\AAA.docx"); +// fos.flush(); +// fos.close(); + out.putNextEntry(new ZipEntry("aaaaa" + i + ".docx")); + ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); + doc.write(arrayOutputStream); + out.write(arrayOutputStream.toByteArray()); + doc.close(); + } + + + @Test + public void test02() throws IOException { + XWPFDocument doc = new XWPFDocument(new FileInputStream("C:\\Users\\RENCHAO\\Desktop\\DeclarationInformationExport.docx")); + + waterMarkDocxDocument(doc, "CCIC 2023-06"); + + doc.write(new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\DIE.docx")); + doc.close(); + + + } + + /** + * 为文档添加水印
+ * 实现参考了{@link org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy#createWatermark(String)} + * + * @param doc 需要被处理的docx文档对象 + * @param waterMark 水印内容 + */ + private void waterMarkDocxDocument(XWPFDocument doc, String waterMark) { + // 获取文档的所有页眉,如果文档没有页眉,则新建一个默认页眉 + List headerList = new ArrayList<>(doc.getHeaderList()); + if (headerList.isEmpty()) { + headerList.add(doc.createHeader(HeaderFooterType.DEFAULT)); + } + // 遍历所有的header,这里包含所有的分页 + for (XWPFHeader header : headerList) { + int size = header.getParagraphs().size(); + if (size == 0) { + header.createParagraph(); + } + XWPFParagraph paragraph = header.getParagraphArray(0); + CTP ctp = paragraph.getCTP(); + byte[] rsidR = doc.getDocument().getBody().getPArray(0).getRsidR(); + byte[] rsidRDefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault(); + ctp.setRsidP(rsidR); + ctp.setRsidRDefault(rsidRDefault); + CTPPr ppr = ctp.addNewPPr(); + ppr.addNewPStyle().setVal("Header"); + // 开始加水印 + CTR ctr = ctp.addNewR(); + CTRPr ctrpr = ctr.addNewRPr(); + ctrpr.addNewNoProof(); + CTGroup group = CTGroup.Factory.newInstance(); + CTShapetype shapeType = group.addNewShapetype(); + CTTextPath shapeTypeTextPath = shapeType.addNewTextpath(); + shapeTypeTextPath.setOn(STTrueFalse.T); + shapeTypeTextPath.setFitshape(STTrueFalse.T); + CTLock lock = shapeType.addNewLock(); + lock.setExt(STExt.VIEW); + CTShape shape = group.addNewShape(); + shape.setId("PowerPlusWaterMarkObject"); + shape.setSpid("_x0000_s102"); + shape.setType("#_x0000_t136"); + // 设置形状样式(旋转,位置,相对路径等参数) + shape.setStyle(getShapeStyle()); + shape.setFillcolor("#d8d8d8"); + // 字体设置为实心 + shape.setStroked(STTrueFalse.FALSE); + // 绘制文本的路径 + CTTextPath shapeTextPath = shape.addNewTextpath(); + // 设置文本字体与大小 + shapeTextPath.setStyle("font-family:Arial;font-size:1pt"); + shapeTextPath.setString(waterMark); + CTPicture pict = ctr.addNewPict(); + pict.set(group); + } + } + + /** + * 构建Shape的样式参数 + */ + private String getShapeStyle() { + // 文本path绘制的定位方式 + return "position: " + "absolute" + + ";margin-top: " + 0 + + ";width: " + "415pt" + + ";height: " + "207.5pt" + + ";z-index: " + "-251654144" + + ";mso-wrap-edited: " + "f" + + ";mso-position-horizontal: " + "center" + + ";mso-position-horizontal-relative: " + "margin" + + ";mso-position-vertical-relative: " + "margin" + + ";mso-position-vertical: " + "center" + + ";rotation: " + 315; + } +} diff --git a/Test/MyMaven/src/main/java/com/renchao/word/ss b/Test/MyMaven/src/main/java/com/renchao/word/ss new file mode 100644 index 00000000..0014c095 --- /dev/null +++ b/Test/MyMaven/src/main/java/com/renchao/word/ss @@ -0,0 +1,550 @@ +package com.jiuyv.business.domain; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.jiuyv.common.annotation.Excel; +import com.jiuyv.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 货物申报信息对象 tbl_goods_declare_info + * + * @author jiuyv + * @date 2023-02-07 + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class TblGoodsDeclareInfo extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 物料编号 + */ + @Excel(name = "物料编号") + @NotBlank(message = "物料编号不能为空") + private String goodsId; + + /** + * 版本 + */ + @Excel(name = "版本") + private Long version; + + /** + * 委托单位ID + */ + @Excel(name = "委托单位ID") + @NotBlank(message = "委托单位ID不能为空") + private String delegateId; + + /** + * 委托单位名称 + */ + @Excel(name = "委托单位名称") + @NotBlank(message = "委托单位名称不能为空") + private String delegateName; + + /** + * 货物名称 + */ + @Excel(name = "货物名称") + @NotBlank(message = "货物名称不能为空") + private String goodsName; + + /** + * 出入境标识 + */ + @Excel(name = "出入境标识") + @NotBlank(message = "出入境标识不能为空") + private String inOutType; + + /** + * $column.columnComment + */ + @Excel(name = "联合国编号") + @NotBlank(message = "UN编号不能为空") + private String unNo; + + /** + * 成分含量 + */ + @Excel(name = "成分含量") + @NotBlank(message = "成分含量不能为空") + private String composition; + + /** + * 正式运输名称 + */ + @Excel(name = "正式运输名称") + private String transportNameCn; + + /** + * 危险类别 + */ + @Excel(name = "危险类别") + private String dangerType; + + /** + * 包装类别 + */ + @Excel(name = "包装类别") + private String packageClass; + + /** + * 关联危险品货物ID + */ + @Excel(name = "关联危险品货物ID") + private Long goodsInfoId; + + /** + * 化学品名称 + */ + @Excel(name = "化学品名称") + @NotBlank(message = "化学品名称不能为空") + private String chemicalName; + + /** + * 关联危化品目录ID + */ + @Excel(name = "关联危化品目录ID") + private Long catalogueInfoId; + + /** + * 备注 + */ + @Excel(name = "备注") + private String remarks; + + /** + * HS编码 + */ + @Excel(name = "HS编码") + @NotBlank(message = "HS编码不能为空") + private String hsCode; + + /** + * 检验检疫名称 + */ + @Excel(name = "检验检疫名称") + private String ciqName; + + /** + * CIQ代码 + */ + @Excel(name = "CIQ代码") + private String ciqNo; + + /** + * 关联CIQ危险化学品ID + */ + @Excel(name = "关联CIQ危险化学品ID") + @NotNull(message = "关联CIQ危险化学品ID不能为空") + private Long ciqGoodsInfoId; + + /** + * 货物属性 + */ + @Excel(name = "货物属性") + private String goodsAttr; + + /** + * 包装UN标记 + */ + @Excel(name = "包装UN标记") + private String packSign; + + /** + * 状态(0正常 1废除) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=废除") + private String status; + + /** + * 数据状态(0存在 2删除) + */ + @Excel(name = "数据状态", readConverterExp = "0=存在,2=删除") + private String dataStatus; + + /** + * rectoken + */ + @Excel(name = "rectoken") + private String recToken; + + /** + * 创建人id + */ + @Excel(name = "创建人id") + private Long createById; + + /** + * 修改人id + */ + @Excel(name = "修改人id") + private Long updateById; + + /** + * $column.columnComment + */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String rsv1; + + /** + * $column.columnComment + */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String rsv2; + + /** + * $column.columnComment + */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String rsv3; + + /** + * 关联分类鉴定信息ID + */ + @Excel(name = "关联分类鉴定信息ID") + private Long dangerIdentifyInfoId; + + /** + * 货代公司ID + */ + @Excel(name = "货代公司ID") + private Long agentId; + + /** + * 货代公司 + */ + @Excel(name = "货代公司") + private String agentName; + + private String recTokenEt; + + /** + * 随附文件 + */ + private List files; + + /** + * 文件信息 + */ + private List attachFileInfos; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setGoodsId(String goodsId) { + this.goodsId = goodsId; + } + + public String getGoodsId() { + return goodsId; + } + + public void setVersion(Long version) { + this.version = version; + } + + public Long getVersion() { + return version; + } + + public void setDelegateId(String delegateId) { + this.delegateId = delegateId; + } + + public String getDelegateId() { + return delegateId; + } + + public void setDelegateName(String delegateName) { + this.delegateName = delegateName; + } + + public String getDelegateName() { + return delegateName; + } + + public void setGoodsName(String goodsName) { + this.goodsName = goodsName; + } + + public String getGoodsName() { + return goodsName; + } + + public void setInOutType(String inOutType) { + this.inOutType = inOutType; + } + + public String getInOutType() { + return inOutType; + } + + public void setUnNo(String unNo) { + this.unNo = unNo; + } + + public String getUnNo() { + return unNo; + } + + public void setComposition(String composition) { + this.composition = composition; + } + + public String getComposition() { + return composition; + } + + public void setTransportNameCn(String transportNameCn) { + this.transportNameCn = transportNameCn; + } + + public String getTransportNameCn() { + return transportNameCn; + } + + public void setDangerType(String dangerType) { + this.dangerType = dangerType; + } + + public String getDangerType() { + return dangerType; + } + + public void setPackageClass(String packageClass) { + this.packageClass = packageClass; + } + + public String getPackageClass() { + return packageClass; + } + + public void setGoodsInfoId(Long goodsInfoId) { + this.goodsInfoId = goodsInfoId; + } + + public Long getGoodsInfoId() { + return goodsInfoId; + } + + public void setChemicalName(String chemicalName) { + this.chemicalName = chemicalName; + } + + public String getChemicalName() { + return chemicalName; + } + + public void setCatalogueInfoId(Long catalogueInfoId) { + this.catalogueInfoId = catalogueInfoId; + } + + public Long getCatalogueInfoId() { + return catalogueInfoId; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getRemarks() { + return remarks; + } + + public void setHsCode(String hsCode) { + this.hsCode = hsCode; + } + + public String getHsCode() { + return hsCode; + } + + public void setCiqName(String ciqName) { + this.ciqName = ciqName; + } + + public String getCiqName() { + return ciqName; + } + + public void setCiqNo(String ciqNo) { + this.ciqNo = ciqNo; + } + + public String getCiqNo() { + return ciqNo; + } + + public void setCiqGoodsInfoId(Long ciqGoodsInfoId) { + this.ciqGoodsInfoId = ciqGoodsInfoId; + } + + public Long getCiqGoodsInfoId() { + return ciqGoodsInfoId; + } + + public void setGoodsAttr(String goodsAttr) { + this.goodsAttr = goodsAttr; + } + + public String getGoodsAttr() { + return goodsAttr; + } + + public void setPackSign(String packSign) { + this.packSign = packSign; + } + + public String getPackSign() { + return packSign; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getStatus() { + return status; + } + + public void setDataStatus(String dataStatus) { + this.dataStatus = dataStatus; + } + + public String getDataStatus() { + return dataStatus; + } + + public void setRecToken(String recToken) { + this.recToken = recToken; + } + + public String getRecToken() { + return recToken; + } + + public void setCreateById(Long createById) { + this.createById = createById; + } + + public Long getCreateById() { + return createById; + } + + public void setUpdateById(Long updateById) { + this.updateById = updateById; + } + + public Long getUpdateById() { + return updateById; + } + + public void setRsv1(String rsv1) { + this.rsv1 = rsv1; + } + + public String getRsv1() { + return rsv1; + } + + public void setRsv2(String rsv2) { + this.rsv2 = rsv2; + } + + public String getRsv2() { + return rsv2; + } + + public void setRsv3(String rsv3) { + this.rsv3 = rsv3; + } + + public String getRsv3() { + return rsv3; + } + + public void setDangerIdentifyInfoId(Long dangerIdentifyInfoId) { + this.dangerIdentifyInfoId = dangerIdentifyInfoId; + } + + public Long getDangerIdentifyInfoId() { + return dangerIdentifyInfoId; + } + + public void setAgentId(Long agentId) { + this.agentId = agentId; + } + + public Long getAgentId() { + return agentId; + } + + public String getRecTokenEt() { + return recTokenEt; + } + + public void setRecTokenEt(String recTokenEt) { + this.recTokenEt = recTokenEt; + } + + public List getFiles() { + return files; + } + + public void setFiles(List files) { + this.files = files; + } + + public List getAttachFileInfos() { + return attachFileInfos; + } + + public void setAttachFileInfos(List attachFileInfos) { + this.attachFileInfos = attachFileInfos; + } + + public String getAgentName() { + return agentName; + } + + public void setAgentName(String agentName) { + this.agentName = agentName; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("id", getId()) + .append("goodsId", getGoodsId()).append("version", getVersion()).append("delegateId", getDelegateId()) + .append("delegateName", getDelegateName()).append("goodsName", getGoodsName()) + .append("inOutType", getInOutType()).append("unNo", getUnNo()).append("composition", getComposition()) + .append("transportNameCn", getTransportNameCn()).append("dangerType", getDangerType()) + .append("packageClass", getPackageClass()).append("goodsInfoId", getGoodsInfoId()) + .append("chemicalName", getChemicalName()).append("catalogueInfoId", getCatalogueInfoId()) + .append("remarks", getRemarks()).append("hsCode", getHsCode()).append("ciqName", getCiqName()) + .append("ciqNo", getCiqNo()).append("ciqGoodsInfoId", getCiqGoodsInfoId()) + .append("goodsAttr", getGoodsAttr()).append("packSign", getPackSign()).append("status", getStatus()) + .append("dataStatus", getDataStatus()).append("recToken", getRecToken()) + .append("createById", getCreateById()).append("createBy", getCreateBy()) + .append("createTime", getCreateTime()).append("updateById", getUpdateById()) + .append("updateBy", getUpdateBy()).append("updateTime", getUpdateTime()).append("rsv1", getRsv1()) + .append("rsv2", getRsv2()).append("rsv3", getRsv3()) + .append("dangerIdentifyInfoId", getDangerIdentifyInfoId()).append("agentId", getAgentId()).toString(); + } +} diff --git a/Test/MyMaven/src/main/resources/META-INF/kmodule.xml b/Test/MyMaven/src/main/resources/META-INF/kmodule.xml new file mode 100644 index 00000000..b2ba087a --- /dev/null +++ b/Test/MyMaven/src/main/resources/META-INF/kmodule.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/Test/MyMaven/src/main/resources/rules/bookDiscount.drl b/Test/MyMaven/src/main/resources/rules/bookDiscount.drl new file mode 100644 index 00000000..a744dbd6 --- /dev/null +++ b/Test/MyMaven/src/main/resources/rules/bookDiscount.drl @@ -0,0 +1,137 @@ +// 图书优惠规则 +package book.discount +import com.renchao.drools.Order +import java.util.List +import java.util.Set +import java.lang.Integer +import java.util.ArrayList +import org.springframework.util.CollectionUtils + +// 规则一:所购图书总价在100元以下的没有优惠 +rule "book_discount_1" + when + $order: Order(originalPrice < 100) // 匹配模式,到规则引擎中(工作内存)查找Order对象,命名为$order + $str: String(true) + then + $order.setRealPrice($order.getOriginalPrice()); + System.out.println("成功匹配到规则一,所购图书总价在100元以下无优惠"); + System.out.println(">>>>>" + $str); + List list = $order.getStringList(); + list.add("aaa"); + list.add("bbb"); + list.add("ccc"); + for (String s : list) { + if (s.equals("bbb")){ + System.out.println(s); + } + } + insert(list); +end + +// 规则二:所购图书总价在100~200的优惠20元 +rule "book_discount_2" + when + $order: Order(originalPrice >= 100 && originalPrice < 200) + then + Double originalPrice = $order.getOriginalPrice(); + System.out.println(">>>>>>>>" + originalPrice); + $order.setRealPrice($order.getOriginalPrice() - 20); + System.out.println("成功匹配到规则二,所购图书总价在100~200元之间"); +end + +// 规则三:所购图书总价在200~300元的优惠50元 +rule "book_discount_3" + when + $order: Order(originalPrice >= 200 && originalPrice < 300) + then + $order.setRealPrice($order.getOriginalPrice() - 50); + System.out.println("成功匹配到规则三,所购图书总价在200~300元之间"); +end + +// 规则四:所购图书总价在300元及以上的优惠100元 +rule "book_discount_4" + when + $order: Order(originalPrice >= 300) + then + $order.setRealPrice($order.getOriginalPrice() - 100); + System.out.println("成功匹配到规则四,所购图书总价在300元及以上"); +end + +// 规则5:所购图书总价在300元及以上的优惠100元 +rule "book_discount_5" + when + $list: List(size > 0) + then + String join = String.join(",",$list); + System.out.println("list不是空的。。。。》》》》》" + join); +end + +// 规则6 +rule "book_discount_6" + when + $list: List(CollectionUtils.isEmpty($list)) + then + System.out.println("list是空的。。。。》》》》》hehe"); +end + + +// 规则7 +rule "book_discount_7" + when + $set: Set(true) + $list: List(true) + $i: Integer($i > 6) + then + $set.add($i * 2); + $list.add($i); +end + + +// 规则8 +rule "book_discount_8" + when + $list: List(size > 1) + then + System.out.println($list); + List list = new ArrayList<>(); + insert(list); +end + + +// 规则9 +rule "book_discount_9" + salience 10 + activation-group "test" +// no-loop true + when + $order: Order(stringList == null/* && stringList.size() == 0*/) + then + System.out.println("999 Order的list是空的。。。。》》》》》"); +// update($order) +end + +// 规则9 +rule "book_discount_10" + salience 20 + activation-group "test" + no-loop true + when + $order: Order(stringList != null && stringList.size() == 0) + then + System.out.println("10 10 Order的list是空的。。。。》》》》》"); + List list = new ArrayList<>(); + list.add("a"); + list.add("b"); + $order.setStringList(list); + update($order) +end + + +// 规则11 +rule "book_discount_11" + when + $order: Order(stringList != null && stringList.size() == 2) + then + System.out.println("11 11 Order的list是空的。。。。》》》》》"); +// update($order) +end \ No newline at end of file diff --git a/Test/MyMaven/src/main/resources/template/declarationInformationExport.docx b/Test/MyMaven/src/main/resources/template/declarationInformationExport.docx new file mode 100644 index 00000000..56228de2 Binary files /dev/null and b/Test/MyMaven/src/main/resources/template/declarationInformationExport.docx differ diff --git a/Test/out/production/Test/META-INF/22spring.factories b/Test/out/production/Test/META-INF/22spring.factories new file mode 100644 index 00000000..65f5bb04 --- /dev/null +++ b/Test/out/production/Test/META-INF/22spring.factories @@ -0,0 +1 @@ +ss \ No newline at end of file diff --git a/Test/src/META-INF/22spring.factories b/Test/src/META-INF/22spring.factories new file mode 100644 index 00000000..65f5bb04 --- /dev/null +++ b/Test/src/META-INF/22spring.factories @@ -0,0 +1 @@ +ss \ No newline at end of file diff --git a/Test/src/com/renchao/RSADemo.java b/Test/src/com/renchao/RSADemo.java new file mode 100644 index 00000000..bf65910c --- /dev/null +++ b/Test/src/com/renchao/RSADemo.java @@ -0,0 +1,70 @@ +package com.renchao; + +import org.junit.Test; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class RSADemo { + public static void main(String[] args) throws Exception { + // 生成密钥对 + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + PublicKey publicKey = keyPair.getPublic(); + System.out.println(publicKey.getFormat()); + PrivateKey privateKey = keyPair.getPrivate(); + + // 加密消息 + String message = "Hello RSA"; + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encryptedMessage = cipher.doFinal(message.getBytes()); + System.out.println("Encrypted message: " + Base64.getEncoder().encodeToString(encryptedMessage)); + + // 解密密文 + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decryptedMessage = cipher.doFinal(encryptedMessage); + System.out.println("Decrypted message: " + new String(decryptedMessage)); + } + + @Test + public void test01() throws Exception { +// String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG03gR1w6i3E6h6+N9F2///BnRrkzPc7RT4qZKKl2b/rolym0EYl3QZTsIV5oQngT93TLtld7EK5svdwUabX6kzqd8yDDChZXS/E7/FrufN6Hwf9S3O3ZzkhEyd45HmRHV4aNRFsS/NviEZx83D6FR94l0SPnomvPkVqM8UnafnQIDAQAB"; + String publicKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUAPS4EiEkyKAay4dRY7hWuyTSewj3X1g9NXj6832Eup0VE+xxGfsDiU5xlZBenFcLT8nn88q3mYit5DowuwxTCmem2TIAfkxdAnZ4vm7ndVbugQTu3TDB5R7LIGRjNF62lfwzYc7ywJFHVH/7dVfh4/uaijjQeDhznlBxM57NgwIDAQAB"; + String privateKeyStr = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMbTeBHXDqLcTqHr430Xb//8GdGuTM9ztFPipkoqXZv+uiXKbQRiXdBlOwhXmhCeBP3dMu2V3sQrmy93BRptfqTOp3zIMMKFldL8Tv8Wu583ofB/1Lc7dnOSETJ3jkeZEdXho1EWxL82+IRnHzcPoVH3iXRI+eia8+RWozxSdp+dAgMBAAECgYAJjtfqT6LR/HJBQXQ9qrdFIIrjNBRYMrE8CRzCWvgGDEBJmcoU2F+3KW6lj4SGAPqvc4dDuZ0sZAZBSWDy7MmWL+Zz2z44sulxsOsb3DJqIyBSAr5D6mhrRmu7MJA5AGgDHo/2gn+9Cji2JQBHBFe18BzJdr2tIM4uAYTVB6EW8QJBAPCrnHohSDtgLSmHrbORP/cIS8OOF/M3PsYfHZ3cpdrKk2zs1rXAHJq80GlmhSQx8tezx6wt63Cph0reiHbOMRkCQQDTfYqahFR0NTFFfTBfSJKQEqoiRYMnOrjkkOOgFv6cBwYd16pnqTfNISSYkBsOcDO09qiMILW96MoJONCV458lAkEAmMrqueK9X+zMX0xjK9hwOp5Ks2lXrTKKqO+CNwGpTkFD3WhzW8oOnvJ2giPzLSqE2QqrHpW8nrcSTKcBDiQTqQJABORmjGR7P6TrWtwmfk3Ddim4XcqV2hZ1qHPhkBZ4FUvkTFRs0LENZWVa31yWA6N8zrbV90fabGYyJjx2NsFpMQJARtRflzJjWc/49nzu+om41bz9Ngg07/S8Rxe8AlZbSlCxggmp/KUBcoVgNJCa5qGsX2AvTOCXaHngp+YLtHHPBQ=="; + + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + Cipher rsa = Cipher.getInstance("RSA"); + // 加密 Hello + KeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + System.out.println(publicKey); + System.out.println(System.currentTimeMillis()); + rsa.init(Cipher.ENCRYPT_MODE, publicKey); + String str = "Admin1234," + System.currentTimeMillis(); + byte[] bytes = rsa.doFinal(str.getBytes(StandardCharsets.UTF_8)); + String s = Base64.getEncoder().encodeToString(bytes); + System.out.println(s); + + + // 解密 +// KeySpec keySpec2 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)); +// PrivateKey privateKey = keyFactory.generatePrivate(keySpec2); +// rsa.init(Cipher.DECRYPT_MODE, privateKey); +// byte[] bytes1 = rsa.doFinal(Base64.getDecoder().decode(s)); +// System.out.println("解密后:" + new String(bytes1, StandardCharsets.UTF_8)); + + + } + + +} diff --git a/Test/src/com/renchao/RegExDemo.java b/Test/src/com/renchao/RegExDemo.java new file mode 100644 index 00000000..a16997e2 --- /dev/null +++ b/Test/src/com/renchao/RegExDemo.java @@ -0,0 +1,141 @@ +package com.renchao; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RegExDemo { + public static void main(String[] args) { + String str = "2.3+5.1+8"; + String[] split = str.split("\\+"); + System.out.println(Arrays.toString(split)); + } + + @Test + public void test() { + String str = "2.3/5.1+8Dd./"; + System.out.println(str.replaceAll("[+/a-zA-Z]", "")); + } + + + @Test + public void test02() { +// String str = "3,UN1263,Ⅲ"; +// String str = "2.2/UN1956/P200"; +// String str = "6.1/3/8,UN2740,Ⅰ"; + String str = "2.3(8);UN1079;I"; +// String str = "正式运输名称:丁醇类;联合国编号:UN1120;危险货物类别:3;包装类:II;海洋污染物:否。"; +// String str = "正式运输名称:四氯化硅;联合国编号:UN1818;危险货物类别:2.3(8);包装类:II;海洋污染物:否。"; +// String str = "正式运输名称:四氯化硅;联合国编号:UN1818;危险货物类别:8;包装类:II;海洋污染物:否。"; +// Pattern pattern = Pattern.compile("类别[::]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?)"); +// Pattern pattern = Pattern.compile("(\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?)([((]((\\d(\\.\\d)?[+/])*(\\d(\\.\\d)?))[))])?"); +// Pattern pattern = Pattern.compile("([Uu][Nn])(\\d{4})"); +// Pattern pattern = Pattern.compile("[IⅠⅡⅢ]{1,3}"); + Pattern pattern = Pattern.compile("[,,::;;/](I{1,3}|([ⅠⅡⅢ]))|\\d{4}[,,::;;/]([123])"); + Matcher matcher = pattern.matcher(str); + if (matcher.find()) { + System.out.println("0位置:" + matcher.group()); + System.out.println("1位置:" + matcher.group(1)); + System.out.println("2位置:" + matcher.group(2)); + System.out.println("3位置:" + matcher.group(3)); + System.out.println("4位置:" + matcher.group(4)); + System.out.println("5位置:" + matcher.group(5)); + System.out.println("6位置:" + matcher.group(6)); + System.out.println("7位置:" + matcher.group(7)); + System.out.println("8位置:" + matcher.group(8)); + } + } + + @Test + public void test04() { + String str = "url=/checkInInfo/reviewResult&token=2bce7d71-d60b-4858-a852-15380eba6c84×tamp=1574821902392"; + Pattern pattern = Pattern.compile("[0-9a-f]+(-[0-9a-f]+){4}"); + + Matcher matcher = pattern.matcher(str); + + if (matcher.find()) { + System.out.println("0位置:" + matcher.group()); +// System.out.println("1位置:" + matcher.group(1)); + } + } + + @Test + public void test05() { +// String str = "乙酸乙酯30-50%,2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物1-10%"; +// String str = "2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物25-40%;甲乙酮10-20%;乙酸乙酯10-20%;2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物25-40%"; + String str = "乙酸乙酯30-50%,甲乙酮10-20%,4,4'-(1-甲基亚乙基)二苯酚与2,2'-[(1-甲基亚乙基)二(4,1-亚苯基氧亚甲基)]二(环氧乙烷)的聚合物2.5-10%"; +// Pattern pattern = Pattern.compile("([^%]+)%"); (?=[0-9>>≥<<≤-]+%) +// Pattern pattern = Pattern.compile("([^%]+)([0-9>>≥<<≤-]+%)"); +// Pattern pattern = Pattern.compile("(-?\\d[,-]?\\d?\\D+)([0-9>>≥<<≤-]+%)"); + Pattern pattern = Pattern.compile("([;;,,]?)(((-?\\d[,-]?\\d?['-]*)?[^%0-9>>≥.<<≤-]+)+)([0-9>>≥.<<≤-]+%)"); +// Pattern pattern = Pattern.compile("-?\\d[,-]?\\d?['-]*"); + Pattern pattern2 = Pattern.compile("(\\d+)%"); + Matcher matcher = pattern.matcher(str); + Map map = new HashMap<>(); + while (matcher.find()) { + String group = matcher.group(); + System.out.println("0===>>> " + group); + System.out.println("1===>>> " + matcher.group(1)); + System.out.println("2===>>> " + matcher.group(2)); + System.out.println("3===>>> " + matcher.group(3)); + System.out.println("4===>>> " + matcher.group(4)); + System.out.println("5===>>> " + matcher.group(5)); + Matcher matcher2 = pattern2.matcher(group); + if (matcher2.find()) { + map.put(group, Integer.valueOf(matcher2.group(1))); + } + System.out.println("=============="); + } + +// System.out.println(map); + } + + @Test + public void test055() { + String str = "乙酸乙酯30-50%,2-乙基-2-(羟甲基)-1,3-丙二醇与1,3-二异氰酸根合甲基苯和2,2'-氧二(乙醇)的聚合物1-10%"; + Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+"); + Matcher matcher = pattern.matcher(str); + while (matcher.find()) { + System.out.println(matcher.group()); + } + } + + + @Test + public void test06() { + String content = "3.3+2.3+8"; + String regStr = "(\\d(\\.\\d)?\\+)*(\\d(\\.\\d)?)"; + +// String content = "1A2/Y2.2/100/22 CN/C230409 PI:007"; +// String regStr = "[0-9A-Z]{2,3}/[A-Z]\\d\\.\\d/.+"; +// String regStr = "([^/]+/)*[^/]+"; + + + boolean b = Pattern.matches(regStr, content); + System.out.println("整体匹配= " + b); + } + + @Test + public void test07() { + String str = "a,b,c"; + String[] split = str.split("[,,]"); + for (String s : split) { + System.out.println(s); + } + } + + @Test + public void test08() { + Pattern p = Pattern.compile("产品.*含"); + + String str = "产品中主要含1-甲氧基-2-乙酰"; + + String s = p.matcher(str).replaceAll(""); + System.out.println(s); + } + +} diff --git a/Test/src/com/renchao/StringFormatDemo.java b/Test/src/com/renchao/StringFormatDemo.java new file mode 100644 index 00000000..c9142d16 --- /dev/null +++ b/Test/src/com/renchao/StringFormatDemo.java @@ -0,0 +1,37 @@ +package com.renchao; + +import org.junit.Test; + +import java.io.IOException; +import java.util.UUID; + +public class StringFormatDemo { + public static void main(String[] args) { + String format = String.format("aaa%s,bbb%s,ccc%s", "111", 222, "333"); + System.out.println(format); + + String s = String.format("%s/%s/%s/%s.pdf", "c:", "a", "b", UUID.randomUUID()); + System.out.println(s); + } + + @Test + public void test01() { + StringFormatDemo demo = new StringFormatDemo(); + String t = demo.t(); + System.out.println(t); + } + + + public String t() { + try { +// int i =4 / 0; + return "try"; + } catch (Exception e) { + return "catch"; + } finally { +// return "finally"; + System.out.println("ccc"); + } + } + +} diff --git a/Test/src/com/renchao/ZipDemo.java b/Test/src/com/renchao/ZipDemo.java new file mode 100644 index 00000000..0348202f --- /dev/null +++ b/Test/src/com/renchao/ZipDemo.java @@ -0,0 +1,52 @@ +package com.renchao; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class ZipDemo { + public static void main(String[] args) throws Exception { + // 创建一个ZipOutputStream类的对象 + ZipOutputStream out = new ZipOutputStream(new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\新建文件夹\\output.zip")); + // 创建一个File对象,表示要压缩的文件夹 + File file = new File("C:\\Users\\RENCHAO\\Desktop\\aa"); + // 调用compress方法,将file对象压缩到out对象中 + compress(out, file, ""); + // 关闭out对象 + out.close(); + } + + public static void compress(ZipOutputStream out, File file, String base) throws Exception { + if (file.isDirectory()) { + // 如果file对象是一个目录,则获取该目录下的所有文件和目录 + File[] files = file.listFiles(); + // 如果files数组不为空,则遍历files数组中的所有元素 + if (files != null && files.length > 0) { + for (File f : files) { + // 递归调用compress方法,将f对象压缩到out对象中 + compress(out, f, base + file.getName() + "/"); + } + } + } else { + // 如果file对象是一个文件,则创建一个ZipEntry对象,表示该文件在压缩包中的路径和名称 + ZipEntry entry = new ZipEntry(base + file.getName()); + // 将entry对象添加到out对象中 + out.putNextEntry(entry); + // 创建一个FileInputStream类的对象 + FileInputStream in = new FileInputStream(file); + // 创建一个byte数组 + byte[] buffer = new byte[1024]; + int len; + // 读取in对象中的数据到buffer数组中,直到读取完毕 + while ((len = in.read(buffer)) > 0) { + // 将buffer数组中的数据写入到out对象中 + out.write(buffer, 0, len); + } + // 关闭in对象 + in.close(); + } + } +} diff --git a/Test/src/com/renchao/ZonedDateTimeDemo.java b/Test/src/com/renchao/ZonedDateTimeDemo.java new file mode 100644 index 00000000..a63910da --- /dev/null +++ b/Test/src/com/renchao/ZonedDateTimeDemo.java @@ -0,0 +1,28 @@ +package com.renchao; + + +import org.junit.Test; + +import java.time.ZoneId; +import java.time.ZonedDateTime; + +public class ZonedDateTimeDemo { + + @Test + public void test09() throws InterruptedException { +// long timestamp = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli(); +// System.out.println(timestamp); +//// Thread.sleep(5000); +// System.out.println(System.currentTimeMillis() - timestamp); +// System.out.println(30<<1<<2); + + ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 4, 13, 12, 57, 0, 0, ZoneId.systemDefault()); + long l = zonedDateTime.toInstant().toEpochMilli(); + System.out.println(l); + System.out.println(System.currentTimeMillis()); + + System.out.println((System.currentTimeMillis() - 1681365120000L) /1000/60); + System.out.println(System.currentTimeMillis()); + } + +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/pom.xml b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/pom.xml new file mode 100644 index 00000000..8e786785 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/pom.xml @@ -0,0 +1,40 @@ + + + + agile-data + com.jiuyv + 0.0.1-SNAPSHOT + + 4.0.0 + + agile-data-api + + + 8 + 8 + + + + + com.fasterxml.jackson.core + jackson-annotations + + + jakarta.validation + jakarta.validation-api + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-web + + + + \ No newline at end of file diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/api/AgileApi.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/api/AgileApi.java new file mode 100644 index 00000000..673be4aa --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/api/AgileApi.java @@ -0,0 +1,29 @@ +package com.jiuyv.agile.data.api; + +import com.jiuyv.agile.data.web.R; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +/** + * 敏捷API + * + * @author yulei + */ +public interface AgileApi { + + /** + * 发布接口 + * @param id + * @return + */ + @PostMapping(value = "/dataApis/{id}/release") + public R releaseDataApi(@PathVariable Long id); + + /** + * 注销接口 + * @param id + * @return + */ + @PostMapping(value = "/dataApis/{id}/cancel") + public R cancelDataApi(@PathVariable Long id); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/AlgorithmCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/AlgorithmCrypto.java new file mode 100644 index 00000000..ebdb021e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/AlgorithmCrypto.java @@ -0,0 +1,37 @@ +package com.jiuyv.agile.data.dto.enums; + +public enum AlgorithmCrypto { + + BASE64("1", "BASE64加密"), + MD5("2", "MD5加密"), + SHA_1("3", "SHA_1加密"), + SHA_256("4", "SHA_256加密"), + AES("5", "AES加密"), + DES("6", "DES加密"); + + private final String key; + + private final String val; + + AlgorithmCrypto(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static AlgorithmCrypto getAlgorithmCrypto(String algorithmCrypt) { + for (AlgorithmCrypto type : AlgorithmCrypto.values()) { + if (type.key.equals(algorithmCrypt)) { + return type; + } + } + return BASE64; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/CipherType.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/CipherType.java new file mode 100644 index 00000000..1a45b3be --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/CipherType.java @@ -0,0 +1,33 @@ +package com.jiuyv.agile.data.dto.enums; + +public enum CipherType { + + REGEX("1", "正则替换"), + ALGORITHM("2", "加密算法"); + + private final String key; + + private final String val; + + CipherType(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static CipherType getCipherType(String cipherType) { + for (CipherType type : CipherType.values()) { + if (type.key.equals(cipherType)) { + return type; + } + } + return REGEX; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/ParamType.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/ParamType.java new file mode 100644 index 00000000..808db3b2 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/ParamType.java @@ -0,0 +1,83 @@ +package com.jiuyv.agile.data.dto.enums; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; + +public enum ParamType { + + String("1", "字符串"), + Integer("2", "整型"), + Float("3", "浮点型"), + Date("4", "时间"), + List("5", "集合"); + + private final String key; + + private final String val; + + ParamType(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static Object parse(ParamType paramType, Object obj) { + if (obj == null) { + return null; + } + switch (paramType) { + case String: + try { + return (java.lang.String)obj; + } catch (Exception e) { + throw new RuntimeException("参数值[" + obj + "]不是" + String.getVal() + "数据类型]"); + } + case Float: + try { + return new BigDecimal(obj.toString()).doubleValue(); + } catch (Exception e) { + throw new RuntimeException("参数值[" + obj + "]不是" + Float.getVal() + "数据类型]"); + } + case Integer: + try { + return (java.lang.Integer)obj; + } catch (Exception e) { + throw new RuntimeException("参数值[" + obj + "]不是" + Integer.getVal() + "数据类型]"); + } + case List: + try { + return (java.util.List)obj; + } catch (Exception e) { + throw new RuntimeException("参数值[" + obj + "]不是" + List.getVal() + "数据类型]"); + } + case Date: + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return sdf.parse(obj.toString()); + } catch (Exception e) { + try { + return sdf.parse(obj.toString()); + } catch (Exception ex) { + throw new RuntimeException("参数值[" + obj + "]不是" + Date.getVal() + "数据类型]"); + } + } + } + return null; + } + + public static ParamType getParamType(String paramType) { + for (ParamType type : ParamType.values()) { + if (type.key.equals(paramType)) { + return type; + } + } + return String; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/RegexCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/RegexCrypto.java new file mode 100644 index 00000000..c6991b4f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/RegexCrypto.java @@ -0,0 +1,39 @@ +package com.jiuyv.agile.data.dto.enums; + +public enum RegexCrypto { + + CHINESE_NAME("1", "中文姓名"), + ID_CARD("2", "身份证号"), + FIXED_PHONE("3", "固定电话"), + MOBILE_PHONE("4", "手机号码"), + ADDRESS("5", "地址"), + EMAIL("6", "电子邮箱"), + BANK_CARD("7", "银行卡号"), + CNAPS_CODE("8", "公司开户银行联号"); + + private final String key; + + private final String val; + + RegexCrypto(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static RegexCrypto getRegexCrypto(String regexCrypt) { + for (RegexCrypto type : RegexCrypto.values()) { + if (type.key.equals(regexCrypt)) { + return type; + } + } + return CHINESE_NAME; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/WhereType.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/WhereType.java new file mode 100644 index 00000000..adee2031 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/enums/WhereType.java @@ -0,0 +1,50 @@ +package com.jiuyv.agile.data.dto.enums; + +public enum WhereType { + + EQUALS("1", "=", "等于"), + NOT_EQUALS("2", "!=", "不等于"), + LIKE("3", "LIKE", "全模糊查询"), + LIKE_LEFT("4", "LIKE", "左模糊查询"), + LIKE_RIGHT("5", "LIKE", "右模糊查询"), + GREATER_THAN("6", ">", "大于"), + GREATER_THAN_EQUALS("7", ">=", "大于等于"), + LESS_THAN("8", "<", "小于"), + LESS_THAN_EQUALS("9", "<=", "小于等于"), + NULL("10", "IS NULL", "是否为空"), + NOT_NULL("11", "IS NOT NULL", "是否不为空"), + IN("12", "IN", "IN"); + + private final String type; + + private final String key; + + private final String desc; + + WhereType(String type, String key, String desc) { + this.type = type; + this.key = key; + this.desc = desc; + } + + public String getType() { + return type; + } + + public String getKey() { + return key; + } + + public String getDesc() { + return desc; + } + + public static WhereType getWhereType(String whereType) { + for (WhereType type : WhereType.values()) { + if (type.type.equals(whereType)) { + return type; + } + } + return EQUALS; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ApiMaskQuery.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ApiMaskQuery.java new file mode 100644 index 00000000..773c7dae --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ApiMaskQuery.java @@ -0,0 +1,46 @@ +package com.jiuyv.agile.data.dto.request; + +import java.util.Objects; + +/** + *

+ * 数据API脱敏信息表 查询实体 + *

+ */ +public class ApiMaskQuery extends BaseQueryParams { + + private static final long serialVersionUID=1L; + + private String maskName; + + private Long apiId; + + public String getMaskName() { + return maskName; + } + + public void setMaskName(String maskName) { + this.maskName = maskName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApiMaskQuery that = (ApiMaskQuery) o; + return maskName.equals(that.maskName) && apiId.equals(that.apiId); + } + + @Override + public int hashCode() { + return Objects.hash(maskName, apiId); + } + + public Long getApiId() { + return apiId; + } + + public void setApiId(Long apiId) { + this.apiId = apiId; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/BaseQueryParams.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/BaseQueryParams.java new file mode 100644 index 00000000..1070a244 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/BaseQueryParams.java @@ -0,0 +1,82 @@ +package com.jiuyv.agile.data.dto.request; + + +import java.io.Serializable; +import java.util.List; + +public class BaseQueryParams implements Serializable { + + private static final long serialVersionUID = 1L; + + // 关键字 + private String keyword; + // 当前页码 + private Integer pageNum = 1; + // 分页条数 + private Integer pageSize = 20; + // 排序 + private List orderList; + // 数据权限 + private String dataScope; + + public class OrderItem{ + private String column; + private boolean asc; + + public String getColumn() { + return column; + } + + public void setColumn(String column) { + this.column = column; + } + + public boolean isAsc() { + return asc; + } + + public void setAsc(boolean asc) { + this.asc = asc; + } + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public List getOrderList() { + return orderList; + } + + public void setOrderList(List orderList) { + this.orderList = orderList; + } + + public String getDataScope() { + return dataScope; + } + + public void setDataScope(String dataScope) { + this.dataScope = dataScope; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/DbSchema.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/DbSchema.java new file mode 100644 index 00000000..17e93b57 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/DbSchema.java @@ -0,0 +1,96 @@ +package com.jiuyv.agile.data.dto.request; + + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 数据源连接信息Model + */ +public class DbSchema implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主机 + */ + @NotBlank(message = "主机不能为空") + private String host; + + /** + * 端口 + */ + @NotNull(message = "端口不能为空") + private Integer port; + + /** + * 用户名 + */ + @NotBlank(message = "用户名不能为空") + private String username; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 数据库 + */ + private String dbName; + + /** + * 服务名 + */ + private String sid; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDbName() { + return dbName; + } + + public void setDbName(String dbName) { + this.dbName = dbName; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ExecuteConfig.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ExecuteConfig.java new file mode 100644 index 00000000..f9189ff3 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ExecuteConfig.java @@ -0,0 +1,97 @@ +package com.jiuyv.agile.data.dto.request; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * 执行配置信息Model + */ +public class ExecuteConfig implements Serializable { + + private static final long serialVersionUID=1L; + + + /** + * 数据源 + */ + @NotBlank(message = "数据源不能为空") + private Long sourceId; + + /** + * 配置方式 + */ + @NotNull(message = "配置方式不能为空") + private String configType; + + /** + * 数据库表主键 + */ + private Long tableId; + + /** + * 数据库表 + */ + private String tableName; + + /** + * 表字段列表 + */ + @Valid + private List fieldParams; + + /** + * 解析SQL + */ + private String sqlText; + + public Long getSourceId() { + return sourceId; + } + + public void setSourceId(Long sourceId) { + this.sourceId = sourceId; + } + + public String getConfigType() { + return configType; + } + + public void setConfigType(String configType) { + this.configType = configType; + } + + public Long getTableId() { + return tableId; + } + + public void setTableId(Long tableId) { + this.tableId = tableId; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public List getFieldParams() { + return fieldParams; + } + + public void setFieldParams(List fieldParams) { + this.fieldParams = fieldParams; + } + + public String getSqlText() { + return sqlText; + } + + public void setSqlText(String sqlText) { + this.sqlText = sqlText; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldParam.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldParam.java new file mode 100644 index 00000000..9d108a0a --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldParam.java @@ -0,0 +1,167 @@ +package com.jiuyv.agile.data.dto.request; + +import java.io.Serializable; + +/** + * 字段信息Model + */ +public class FieldParam implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 列名 + */ + private String columnName; + + /** + * 数据类型 + */ + private String dataType; + + /** + * 数据长度 + */ + private Integer dataLength; + + /** + * 数据精度 + */ + private Integer dataPrecision; + + /** + * 数据小数位 + */ + private Integer dataScale; + + /** + * 是否主键 + */ + private String columnKey; + + /** + * 是否允许为空 + */ + private String columnNullable; + + /** + * 列的序号 + */ + private Integer columnPosition; + + /** + * 列默认值 + */ + private String dataDefault; + + /** + * 列注释 + */ + private String columnComment; + + /** + * 作为请求参数 + */ + private String reqable; + + /** + * 作为返回参数 + */ + private String resable; + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public Integer getDataLength() { + return dataLength; + } + + public void setDataLength(Integer dataLength) { + this.dataLength = dataLength; + } + + public Integer getDataPrecision() { + return dataPrecision; + } + + public void setDataPrecision(Integer dataPrecision) { + this.dataPrecision = dataPrecision; + } + + public Integer getDataScale() { + return dataScale; + } + + public void setDataScale(Integer dataScale) { + this.dataScale = dataScale; + } + + public String getColumnKey() { + return columnKey; + } + + public void setColumnKey(String columnKey) { + this.columnKey = columnKey; + } + + public String getColumnNullable() { + return columnNullable; + } + + public void setColumnNullable(String columnNullable) { + this.columnNullable = columnNullable; + } + + public Integer getColumnPosition() { + return columnPosition; + } + + public void setColumnPosition(Integer columnPosition) { + this.columnPosition = columnPosition; + } + + public String getDataDefault() { + return dataDefault; + } + + public void setDataDefault(String dataDefault) { + this.dataDefault = dataDefault; + } + + public String getColumnComment() { + return columnComment; + } + + public void setColumnComment(String columnComment) { + this.columnComment = columnComment; + } + + public String getReqable() { + return reqable; + } + + public void setReqable(String reqable) { + this.reqable = reqable; + } + + public String getResable() { + return resable; + } + + public void setResable(String resable) { + this.resable = resable; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldRule.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldRule.java new file mode 100644 index 00000000..56e14b32 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/FieldRule.java @@ -0,0 +1,53 @@ +package com.jiuyv.agile.data.dto.request; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 字段信息Model + */ +public class FieldRule implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 字段名称 + */ + @NotBlank(message = "字段名称不能为空") + private String fieldName; + /** + * 脱敏类型 + */ + @NotNull(message = "脱敏类型不能为空") + private String cipherType; + /** + * 规则类型 + */ + @NotNull(message = "规则类型不能为空") + private String cryptType; + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public String getCipherType() { + return cipherType; + } + + public void setCipherType(String cipherType) { + this.cipherType = cipherType; + } + + public String getCryptType() { + return cryptType; + } + + public void setCryptType(String cryptType) { + this.cryptType = cryptType; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/RateLimit.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/RateLimit.java new file mode 100644 index 00000000..b02abe22 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/RateLimit.java @@ -0,0 +1,52 @@ +package com.jiuyv.agile.data.dto.request; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 限流信息Model + */ +public class RateLimit implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 是否限流:0:否,1:是 + */ + @NotNull(message = "是否限流不能为空") + private String enable; + + /** + * 请求次数默认5次 + */ + private Integer times = 5; + + /** + * 请求时间范围默认60秒 + */ + private Integer seconds = 60; + + public String getEnable() { + return enable; + } + + public void setEnable(String enable) { + this.enable = enable; + } + + public Integer getTimes() { + return times; + } + + public void setTimes(Integer times) { + this.times = times; + } + + public Integer getSeconds() { + return seconds; + } + + public void setSeconds(Integer seconds) { + this.seconds = seconds; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqParam.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqParam.java new file mode 100644 index 00000000..0f7a9b73 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqParam.java @@ -0,0 +1,111 @@ +package com.jiuyv.agile.data.dto.request; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 请求参数信息Model + */ +public class ReqParam implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 参数名称 + */ + @NotBlank(message = "参数名称不能为空") + private String paramName; + + /** + * 是否为空 + */ + @NotNull(message = "是否为空不能为空") + private String nullable; + + /** + * 描述 + */ + @NotBlank(message = "描述不能为空") + private String paramComment; + + /** + * 操作符 + */ + @NotNull(message = "操作符不能为空") + private String whereType; + + /** + * 参数类型 + */ + @NotNull(message = "参数类型不能为空") + private String paramType; + + /** + * 示例值 + */ + @NotBlank(message = "示例值不能为空") + private String exampleValue; + + /** + * 默认值 + */ + @NotBlank(message = "默认值不能为空") + private String defaultValue; + + public String getParamName() { + return paramName; + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + public String getNullable() { + return nullable; + } + + public void setNullable(String nullable) { + this.nullable = nullable; + } + + public String getParamComment() { + return paramComment; + } + + public void setParamComment(String paramComment) { + this.paramComment = paramComment; + } + + public String getWhereType() { + return whereType; + } + + public void setWhereType(String whereType) { + this.whereType = whereType; + } + + public String getParamType() { + return paramType; + } + + public void setParamType(String paramType) { + this.paramType = paramType; + } + + public String getExampleValue() { + return exampleValue; + } + + public void setExampleValue(String exampleValue) { + this.exampleValue = exampleValue; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqVo.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqVo.java new file mode 100644 index 00000000..db704542 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/request/ReqVo.java @@ -0,0 +1,34 @@ +package com.jiuyv.agile.data.dto.request; + +import java.io.Serializable; + +/** + * API请求参数 + * + * @author yulei + */ +public class ReqVo implements Serializable { + + + /** 报文内容 */ + private String body; + + /** 签名 */ + private String sign; + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/ResParam.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/ResParam.java new file mode 100644 index 00000000..9189f389 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/ResParam.java @@ -0,0 +1,82 @@ +package com.jiuyv.agile.data.dto.response; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 返回参数信息Model + */ +public class ResParam implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 字段名称 + */ + @NotBlank(message = "字段名称不能为空") + private String fieldName; + + /** + * 描述 + */ + @NotBlank(message = "描述不能为空") + private String fieldComment; + + /** + * 数据类型 + */ + @NotNull(message = "数据类型不能为空") + private String dataType; + + /** + * 示例值 + */ + @NotBlank(message = "示例值不能为空") + private String exampleValue; + + /** + * 字段别名 + */ + private String fieldAliasName; + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public String getFieldComment() { + return fieldComment; + } + + public void setFieldComment(String fieldComment) { + this.fieldComment = fieldComment; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public String getExampleValue() { + return exampleValue; + } + + public void setExampleValue(String exampleValue) { + this.exampleValue = exampleValue; + } + + public String getFieldAliasName() { + return fieldAliasName; + } + + public void setFieldAliasName(String fieldAliasName) { + this.fieldAliasName = fieldAliasName; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/RespVo.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/RespVo.java new file mode 100644 index 00000000..815abc68 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/dto/response/RespVo.java @@ -0,0 +1,55 @@ +package com.jiuyv.agile.data.dto.response; + +import com.jiuyv.agile.data.web.ResultCode; + +import java.io.Serializable; + +/** + * API返回参数 + * + * @author yulei + */ +public class RespVo implements Serializable { + + /** 返回码*/ + private Integer respCode; + + /** 返回说明 */ + private String respDesc; + + public RespVo() { + } + + public RespVo(Integer code, String message) { + this.respCode = code; + this.respDesc = message; + } + + public static RespVo ok(){ + return new RespVo(ResultCode.SUCCESS.getCode(),ResultCode.SUCCESS.getMessage()); + } + + public Integer getRespCode() { + return respCode; + } + + public void setRespCode(Integer respCode) { + this.respCode = respCode; + } + + public String getRespDesc() { + return respDesc; + } + + public void setRespDesc(String respDesc) { + this.respDesc = respDesc; + } + + @Override + public String toString() { + return "RespVo{" + + "respCode='" + respCode + '\'' + + ", respDesc='" + respDesc + '\'' + + '}'; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/IErrorCode.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/IErrorCode.java new file mode 100644 index 00000000..13731d15 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/IErrorCode.java @@ -0,0 +1,20 @@ +package com.jiuyv.agile.data.web; + +/** + * @ClassName : IErrorCode + * @Description : 常用API返回对接接口 + * @Author : sky + * @Date: 2023-06-06 16:33 + */ +public interface IErrorCode { + + /** + * 返回码 + */ + Integer getCode(); + + /** + * 返回信息 + */ + String getMessage(); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/R.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/R.java new file mode 100644 index 00000000..dfff893f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/R.java @@ -0,0 +1,107 @@ +package com.jiuyv.agile.data.web; + + +import com.jiuyv.agile.data.web.constant.HttpStatus; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author admin + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/ResultCode.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/ResultCode.java new file mode 100644 index 00000000..e741aa28 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/ResultCode.java @@ -0,0 +1,70 @@ +package com.jiuyv.agile.data.web; + +/** + * 常用API返回对象 + * Created by macro on 2019/4/19. + */ +public enum ResultCode implements IErrorCode { + /** success : 成功. */ + SUCCESS(200, "操作成功"), + ACCEPTED(202, "请求已经被接受"), + NO_CONTENT(204, "操作已经执行成功,但是没有返回数据"), + MOVED_PERM(301, "资源已被移除"), + + SEE_OTHER(303, "重定向"), + NOT_MODIFIED(304, "资源没有被修改"), + BAD_REQUEST(400, "参数列表错误(缺少,格式不匹配)"), + UNAUTHORIZED(401, "未授权"), + FORBIDDEN(403, "访问受限,授权过期"), + NOT_FOUND(404, "资源,服务未找到"), + BAD_METHOD(405, "不允许的http方法"), + CONFLICT(409, "资源冲突,或者资源被锁"), + ERROR(500, "系统内部错误"), + NOT_IMPLEMENTED(501, "接口未实现"), + //PROCESS("process", "订单处理中,请稍后查询"), + //FAILED("failed", "操作失败"), + //VALIDATE_FAILED("input_not_valid", "输入不合法"), + //REPEAT_SUBMIT("duplicate_req_id", "请勿重复提交"), + //AUTHORIZATION_LIMIT("authorization limit", "权限限制"), + //UNAUTHORIZED("401", "暂未登录或token已经过期"), + // + //ILLEGALLY("illegally request", "不合法请求"), + ///** input_not_valid : 输入不合法. */ + //INPUT_NOT_VALID("input_not_valid", "输入不合法"), + ///** duplicate_req_id : 重复请求. */ + //DUPLICATE_REQ_ID("duplicate_req_id","重复请求"), + ///** duplicate_order : 订单重复. */ + //DUPLICATE_ORDER("duplicate_order","订单重复"), + // + ///** product_limit : 商品限购 */ + //SYS_ERROR("sys_error","系统异常"), + ///** business_error : 调用返回非成功 */ + //BUSINESS_ERROR("business_error","调用返回非成功"), + ///** sql_lock_exception : 乐观锁异常*/ + //SQL_EXCEPTION("sql_lock_exception","乐观锁异常"), + // + ///** file_not_exist,请输入正确的权益号 */ + //FILE_NOT_EXIST("file_not_exist","文件不存在"), + ///** file_no_data,文件内容为空 */ + //FILE_NO_DATA("file_no_data","文件内容为空"); + ; + + + private Integer code; + private String message; + + private ResultCode(Integer code, String message) { + this.code = code; + this.message = message; + } + + @Override + public Integer getCode() { + return code; + } + @Override + public String getMessage() { + return message; + } + +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/constant/HttpStatus.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/constant/HttpStatus.java new file mode 100644 index 00000000..8720a334 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-api/src/main/java/com/jiuyv/agile/data/web/constant/HttpStatus.java @@ -0,0 +1,89 @@ +package com.jiuyv.agile.data.web.constant; + +/** + * 返回状态码 + * + * @author admin + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/pom.xml b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/pom.xml new file mode 100644 index 00000000..51d10e2a --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/pom.xml @@ -0,0 +1,76 @@ + + + + agile-data + com.jiuyv + 0.0.1-SNAPSHOT + + 4.0.0 + + agile-data-service + + + 8 + 8 + + + + + org.springframework.boot + spring-boot-starter-web + + + com.jiuyv + agile-data-api + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.apache.commons + commons-lang3 + + + + com.fasterxml.jackson.core + jackson-databind + + + org.hibernate.validator + hibernate-validator + + + + javax.servlet + javax.servlet-api + provided + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + + org.postgresql + postgresql + + + + com.zaxxer + HikariCP + + + + + + \ No newline at end of file diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/AgileDataApplication.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/AgileDataApplication.java new file mode 100644 index 00000000..0600f85d --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/AgileDataApplication.java @@ -0,0 +1,30 @@ +package com.jiuyv.agile.data; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * @author yulei + */ +@SpringBootApplication +@EnableScheduling +public class AgileDataApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(AgileDataApplication.class); + + public static void main(String[] args) { + SpringApplication.run(AgileDataApplication.class, args); + LOGGER.info("(♥◠‿◠)ノ゙ 模块启动成功゙ \n"+ + " ___ ___ ___ \n"+ + " |\\ \\ |\\ \\ / /| \n"+ + " \\ \\ \\ \\ \\ \\/ / / \n"+ + " __ \\ \\ \\ \\ \\ / / \n"+ + " |\\ \\\\_\\ \\ \\/ / / \n"+ + " \\ \\________\\ __/ / / \n"+ + " \\|________| |\\___/ / \n"+ + " \\|___|/ \n" + ); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/common/constants/DataConstant.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/common/constants/DataConstant.java new file mode 100644 index 00000000..bfb895ac --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/common/constants/DataConstant.java @@ -0,0 +1,211 @@ +package com.jiuyv.agile.data.common.constants; + +public class DataConstant { + + /** + * Oauth2安全相关常量 + */ + public enum Security { + //请求头TOKEN名称 + TOKENHEADER("gatewayToken"), + //请求头TOKEN值 + TOKENVALUE("datax:gateway:123456"), + //OAUTH2请求头 + AUTHORIZATION("Authorization"), + //OAUTH2令牌类型 + TOKENTYPE("bearer "), + //security授权角色前缀 + ROLEPREFIX("ROLE_"); + + Security(String val){ + this.val = val; + } + + private final String val; + + public String getVal() { + return val; + } + } + + /** + * 通用的是否 + */ + public enum TrueOrFalse { + FALSE("0",false), + TRUE("1",true); + + TrueOrFalse(String key, boolean val){ + this.key = key; + this.val = val; + } + + private final String key; + private final boolean val; + + public String getKey() { + return key; + } + + public boolean getVal() { + return val; + } + } + + /** + * 用户认证返回额外信息 + */ + public enum UserAdditionalInfo { + LICENSE("license", "datax"), + USER("user", "用户"), + USERID("user_id", "用户ID"), + USERNAME("username", "用户名"), + NICKNAME("nickname", "用户昵称"), + DEPT("user_dept", "用户部门"), + ROLE("user_role", "用户角色"), + POST("user_post", "用户岗位"); + + UserAdditionalInfo(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } + + /** + * 通用的启用禁用状态 + */ + public enum EnableState { + DISABLE("0","禁用"), + ENABLE("1","启用"); + + EnableState(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } + + /** + * 流程审核状态 + */ + public enum AuditState{ + WAIT("1","待提交"), + BACK("2", "已退回"), + AUDIT("3","审核中"), + AGREE("4","通过"), + REJECT("5","不通过"), + CANCEL("6", "已撤销"); + + AuditState(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } + + /** + * 菜单类型 + */ + public enum MenuType{ + MODULE("0","模块"), + MENU("1","菜单"), + BUTTON("2","按钮"); + + MenuType(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } + + /** + * 数据范围 + */ + public enum DataScope{ + ALL("1","全部数据权限"), + CUSTOM("2","自定义数据权限"), + DEPT("3","本部门数据权限"), + DEPTANDCHILD("4","本部门及以下数据权限"), + SELF("5","仅本人数据权限"); + DataScope(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } + + /** + * Api状态 + */ + public enum ApiState{ + WAIT("1","待发布"), + RELEASE("2","已发布"), + CANCEL("3","已下线"); + ApiState(String key, String val){ + this.key = key; + this.val = val; + } + + private final String key; + private final String val; + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/config/ApiMappingConfig.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/config/ApiMappingConfig.java new file mode 100644 index 00000000..2d70b1a8 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/config/ApiMappingConfig.java @@ -0,0 +1,33 @@ +package com.jiuyv.agile.data.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.handler.MappingHandlerMapping; +import com.jiuyv.agile.data.handler.RequestHandler; +import com.jiuyv.agile.data.handler.RequestInterceptor; +import com.jiuyv.agile.data.service.impl.ApiMappingEngine; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +@Configuration +public class ApiMappingConfig { + + @Bean + public MappingHandlerMapping mappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping, + ApiMappingEngine apiMappingEngine, + ObjectMapper objectMapper) { + MappingHandlerMapping mappingHandlerMapping = new MappingHandlerMapping(); + mappingHandlerMapping.setHandler(requestHandler(apiMappingEngine, objectMapper)); + mappingHandlerMapping.setRequestMappingHandlerMapping(requestMappingHandlerMapping); + return mappingHandlerMapping; + } + + @Bean + public RequestHandler requestHandler(ApiMappingEngine apiMappingEngine, ObjectMapper objectMapper) { + RequestHandler handler = new RequestHandler(); + handler.setApiMappingEngine(apiMappingEngine); + handler.setObjectMapper(objectMapper); + handler.setRequestInterceptor(new RequestInterceptor()); + return handler; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/AgileApiController.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/AgileApiController.java new file mode 100644 index 00000000..47fec9f4 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/AgileApiController.java @@ -0,0 +1,43 @@ +package com.jiuyv.agile.data.controller; + +import com.jiuyv.agile.data.service.AgileApiService; +import com.jiuyv.agile.data.web.R; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 敏捷API控制 + * + * @author yulei + */ +@RestController +public class AgileApiController { + + @Autowired + private AgileApiService agileApiService; + + /** + * 发布接口 + * @param id + * @return + */ + @PostMapping(value = "/dataApis/{id}/release") + public R releaseDataApi(@PathVariable Long id){ + agileApiService.releaseDataApi(id); + return R.ok(); + } + + /** + * 注销接口 + * @param id + * @return + */ + @PostMapping(value = "/dataApis/{id}/cancel") + public R cancelDataApi(@PathVariable Long id){ + agileApiService.cancelDataApi(id); + return R.ok(); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/ts.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/ts.java new file mode 100644 index 00000000..8010cb0c --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/ts.java @@ -0,0 +1,7 @@ +package com.jiuyv.agile.data.controller; + +import com.jiuyv.agile.data.api.AgileApi; + + +public interface ts extends AgileApi { +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/tt.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/tt.java new file mode 100644 index 00000000..217db4e6 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/controller/tt.java @@ -0,0 +1,31 @@ +package com.jiuyv.agile.data.controller; + +import com.jiuyv.agile.data.api.AgileApi; +import com.jiuyv.agile.data.service.AgileApiService; +import com.jiuyv.agile.data.web.R; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class tt implements AgileApi { + /** + * 发布接口 + * + * @param id + * @return + */ + @Override + public R releaseDataApi(Long id) { + return null; + } + + /** + * 注销接口 + * + * @param id + * @return + */ + @Override + public R cancelDataApi(Long id) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DataSourceFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DataSourceFactory.java new file mode 100644 index 00000000..040528e8 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DataSourceFactory.java @@ -0,0 +1,12 @@ +package com.jiuyv.agile.data.database; + +public interface DataSourceFactory { + + /** + * 创建数据源实例 + * + * @param property + * @return + */ + DbQuery createDbQuery(DbQueryProperty property); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbColumn.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbColumn.java new file mode 100644 index 00000000..d4eb1e9b --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbColumn.java @@ -0,0 +1,134 @@ +package com.jiuyv.agile.data.database; + +public class DbColumn { + + /** + * 列名 + */ + private String colName; + + /** + * 数据类型 + */ + private String dataType; + + /** + * 数据长度 + */ + private String dataLength; + + /** + * 数据精度 + */ + private String dataPrecision; + + /** + * 数据小数位 + */ + private String dataScale; + + /** + * 是否主键 + */ + private Boolean colKey; + + /** + * 是否允许为空 + */ + private Boolean nullable; + + /** + * 列的序号 + */ + private Integer colPosition; + + /** + * 列默认值 + */ + private String dataDefault; + + /** + * 列注释 + */ + private String colComment; + + public String getColName() { + return colName; + } + + public void setColName(String colName) { + this.colName = colName; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public String getDataLength() { + return dataLength; + } + + public void setDataLength(String dataLength) { + this.dataLength = dataLength; + } + + public String getDataPrecision() { + return dataPrecision; + } + + public void setDataPrecision(String dataPrecision) { + this.dataPrecision = dataPrecision; + } + + public String getDataScale() { + return dataScale; + } + + public void setDataScale(String dataScale) { + this.dataScale = dataScale; + } + + public Boolean getColKey() { + return colKey; + } + + public void setColKey(Boolean colKey) { + this.colKey = colKey; + } + + public Boolean getNullable() { + return nullable; + } + + public void setNullable(Boolean nullable) { + this.nullable = nullable; + } + + public Integer getColPosition() { + return colPosition; + } + + public void setColPosition(Integer colPosition) { + this.colPosition = colPosition; + } + + public String getDataDefault() { + return dataDefault; + } + + public void setDataDefault(String dataDefault) { + this.dataDefault = dataDefault; + } + + public String getColComment() { + return colComment; + } + + public void setColComment(String colComment) { + this.colComment = colComment; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbDialect.java new file mode 100644 index 00000000..3d84a57d --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbDialect.java @@ -0,0 +1,48 @@ +package com.jiuyv.agile.data.database; + +import org.springframework.jdbc.core.RowMapper; + +/** + * 表数据查询接口 + */ +public interface DbDialect { + + RowMapper tableMapper(); + + RowMapper columnMapper(); + + /** + * 获取指定表的所有列 + * + * @param dbName + * @param tableName + * @return + */ + String columns(String dbName, String tableName); + + /** + * 获取数据库下的 所有表 + * + * @param dbName + * @return + */ + String tables(String dbName); + + /** + * 构建 分页 sql + * + * @param sql + * @param offset + * @param count + * @return + */ + String buildPaginationSql(String sql, long offset, long count); + + /** + * 包装 count sql + * + * @param sql + * @return + */ + String count(String sql); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbMD5Util.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbMD5Util.java new file mode 100644 index 00000000..db80263f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbMD5Util.java @@ -0,0 +1,33 @@ +package com.jiuyv.agile.data.database; + +import java.security.MessageDigest; + +public class DbMD5Util { + + private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * MD5加密 + */ + public static String encrypt(String value){ + return encrypt(value.getBytes()); + } + + /** + * MD5加密 + */ + public static String encrypt(byte[] value){ + try { + byte[] bytes = MessageDigest.getInstance("MD5").digest(value); + char[] chars = new char[32]; + for (int i = 0; i < chars.length; i = i + 2) { + byte b = bytes[i / 2]; + chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf]; + chars[i + 1] = HEX_CHARS[b & 0xf]; + } + return new String(chars); + } catch (Exception e) { + throw new RuntimeException("md5 encrypt error", e); + } + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQuery.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQuery.java new file mode 100644 index 00000000..7d0b322b --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQuery.java @@ -0,0 +1,113 @@ +package com.jiuyv.agile.data.database; + +import java.sql.Connection; +import java.util.List; +import java.util.Map; + +/** + * 表数据查询接口 + */ +public interface DbQuery { + + /** + * 获取数据库连接 + */ + Connection getConnection(); + + /** + * 检测连通性 + */ + boolean valid(); + + /** + * 关闭数据源 + */ + void close(); + + /** + * 获取指定表 具有的所有字段列表 + * @param dbName + * @param tableName + * @return + */ + List getTableColumns(String dbName, String tableName); + + /** + * 获取指定数据库下 所有的表信息 + * + * @param dbName + * @return + */ + List getTables(String dbName); + + /** + * 获取总数 + * + * @param sql + * @return + */ + int count(String sql); + + /** + * 获取总数带查询参数 + * + * @param sql + * @return + */ + int count(String sql, Object[] args); + + /** + * 获取总数带查询参数 NamedParameterJdbcTemplate + * + * @param sql + * @return + */ + int count(String sql, Map params); + + /** + * 查询结果列表 + * + * @param sql + * @return + */ + List> queryList(String sql); + + /** + * 查询结果列表带查询参数 + * + * @param sql + * @param args + * @return + */ + List> queryList(String sql, Object[] args); + + /** + * 查询结果分页 + * + * @param sql + * @param offset + * @param size + * @return + */ + PageResult> queryByPage(String sql, long offset, long size); + + /** + * 查询结果分页带查询参数 + * @param sql + * @param args + * @param offset + * @param size + * @return + */ + PageResult> queryByPage(String sql, Object[] args, long offset, long size); + + /** + * 查询结果分页带查询参数 NamedParameterJdbcTemplate + * @param sql + * @param params + * @param offset + * @param size + * @return + */ + PageResult> queryByPage(String sql, Map params, long offset, long size); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQueryProperty.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQueryProperty.java new file mode 100644 index 00000000..c761194d --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbQueryProperty.java @@ -0,0 +1,99 @@ +package com.jiuyv.agile.data.database; + +import org.springframework.util.StringUtils; + +import java.io.Serializable; + +//@AllArgsConstructor +public class DbQueryProperty implements Serializable { + + private static final long serialVersionUID = 1L; + + private String dbType; + private String host; + private String username; + private String password; + private Integer port; + private String dbName; + private String sid; + + /** + * 参数合法性校验 + */ + public void viald() { + if (StringUtils.isEmpty(dbType) || StringUtils.isEmpty(host) || + StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || + StringUtils.isEmpty(port)) { + throw new RuntimeException("参数不完整"); + } + if (DbType.OTHER.getDb().equals(dbType)) { + throw new RuntimeException("不支持的数据库类型"); + } + } + + public DbQueryProperty(String dbType, String host, String username, String password, Integer port, String dbName, String sid) { + this.dbType = dbType; + this.host = host; + this.username = username; + this.password = password; + this.port = port; + this.dbName = dbName; + this.sid = sid; + } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getDbName() { + return dbName; + } + + public void setDbName(String dbName) { + this.dbName = dbName; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbTable.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbTable.java new file mode 100644 index 00000000..ce6cbd4e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbTable.java @@ -0,0 +1,30 @@ +package com.jiuyv.agile.data.database; + +public class DbTable { + + /** + * 表名 + */ + private String tableName; + + /** + * 表注释 + */ + private String tableComment; + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getTableComment() { + return tableComment; + } + + public void setTableComment(String tableComment) { + this.tableComment = tableComment; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbType.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbType.java new file mode 100644 index 00000000..c9284d93 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DbType.java @@ -0,0 +1,87 @@ +package com.jiuyv.agile.data.database; + +/** + * 数据库类型 + */ +public enum DbType { + + /** + * MYSQL + */ + MYSQL("1", "MySql数据库", "jdbc:mysql://${host}:${port}/${dbName}?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useUnicode=true&useSSL=false"), + /** + * MARIADB + */ + MARIADB("2", "MariaDB数据库", "jdbc:mariadb://${host}:${port}/${dbName}"), + /** + * ORACLE + */ + ORACLE("3", "Oracle11g及以下数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"), + /** + * oracle12c new pagination + */ + ORACLE_12C("4", "Oracle12c+数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"), + /** + * POSTGRESQL + */ + POSTGRE_SQL("5", "PostgreSql数据库", "jdbc:postgresql://${host}:${port}/${dbName}"), + /** + * SQLSERVER2005 + */ + SQL_SERVER2008("6", "SQLServer2008及以下数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"), + /** + * SQLSERVER + */ + SQL_SERVER("7", "SQLServer2012+数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"), + /** + * UNKONWN DB + */ + OTHER("8", "其他数据库", ""); + + /** + * 数据库名称 + */ + private final String db; + + /** + * 描述 + */ + private final String desc; + + /** + * url + */ + private final String url; + + public String getDb() { + return this.db; + } + + public String getDesc() { + return this.desc; + } + + public String getUrl() { + return this.url; + } + + DbType(String db, String desc, String url) { + this.db = db; + this.desc = desc; + this.url = url; + } + + /** + * 获取数据库类型 + * + * @param dbType 数据库类型字符串 + */ + public static DbType getDbType(String dbType) { + for (DbType type : DbType.values()) { + if (type.db.equals(dbType)) { + return type; + } + } + return OTHER; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DialectFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DialectFactory.java new file mode 100644 index 00000000..811cb8ad --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/DialectFactory.java @@ -0,0 +1,15 @@ +package com.jiuyv.agile.data.database; + +import com.jiuyv.agile.data.database.dialect.DialectRegistry; + +/** + * 方言工厂类 + */ +public class DialectFactory { + + private static final DialectRegistry DIALECT_REGISTRY = new DialectRegistry(); + + public static DbDialect getDialect(DbType dbType) { + return DIALECT_REGISTRY.getDialect(dbType); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/PageResult.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/PageResult.java new file mode 100644 index 00000000..c945c0eb --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/PageResult.java @@ -0,0 +1,53 @@ +package com.jiuyv.agile.data.database; + + +import java.io.Serializable; +import java.util.List; + +//@Accessors(chain = true) +public class PageResult implements Serializable { + + private static final long serialVersionUID = 1L; + + private Integer pageNum; + private Integer pageSize; + private Integer total; + private List data; + + public PageResult(Integer total, List data) { + this.total = total; + this.data = data; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/DefaultSqlCache.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/DefaultSqlCache.java new file mode 100644 index 00000000..87c6165f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/DefaultSqlCache.java @@ -0,0 +1,123 @@ +package com.jiuyv.agile.data.database.cache; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class DefaultSqlCache extends LinkedHashMap> implements SqlCache { + + private int capacity; + + private long expire; + + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + public DefaultSqlCache(int capacity, long expire) { + super((int) Math.ceil(capacity / 0.75) + 1, 0.75f, true); + // 容量 + this.capacity = capacity; + // 固定过期时间 + this.expire = expire; + } + + @Override + public void put(String key, Object value, long ttl) { + long expireTime = Long.MAX_VALUE; + if (ttl >= 0) { + expireTime = System.currentTimeMillis() + (ttl == 0 ? this.expire : ttl); + } + lock.writeLock().lock(); + try { + // 封装成过期时间节点 + put(key, new ExpireNode<>(expireTime, value)); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public Object get(String key) { + lock.readLock().lock(); + ExpireNode expireNode; + try { + expireNode = super.get(key); + } finally { + lock.readLock().unlock(); + } + if (expireNode == null) { + return null; + } + // 惰性删除过期的 + if (this.expire > -1L && expireNode.expire < System.currentTimeMillis()) { + try { + lock.writeLock().lock(); + super.remove(key); + } finally { + lock.writeLock().unlock(); + } + return null; + } + return expireNode.value; + } + + @Override + public void delete(String key) { + try { + lock.writeLock().lock(); + Iterator>> iterator = super.entrySet().iterator(); + // 清除key的缓存 + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (entry.getKey().equals(key)) { + iterator.remove(); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + + @Override + protected boolean removeEldestEntry(Map.Entry> eldest) { + if (this.expire > -1L && size() > capacity) { + clean(); + } + // lru淘汰 + return size() > this.capacity; + } + + /** + * 清理已过期的数据 + */ + private void clean() { + try { + lock.writeLock().lock(); + Iterator>> iterator = super.entrySet().iterator(); + long now = System.currentTimeMillis(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + // 判断是否过期 + if (next.getValue().expire < now) { + iterator.remove(); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + + /** + * 过期时间节点 + */ + static class ExpireNode { + long expire; + Object value; + public ExpireNode(long expire, Object value) { + this.expire = expire; + this.value = value; + } + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/SqlCache.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/SqlCache.java new file mode 100644 index 00000000..78e5c88a --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/cache/SqlCache.java @@ -0,0 +1,38 @@ +package com.jiuyv.agile.data.database.cache; + +import com.jiuyv.agile.data.database.DbMD5Util; + +import java.util.Arrays; + +/** + * SQL缓存接口 + */ +public interface SqlCache { + + /** + * 计算key + */ + default String buildSqlCacheKey(String sql, Object[] args) { + return DbMD5Util.encrypt(sql + ":" + Arrays.toString(args)); + } + + /** + * 存入缓存 + * @param key key + * @param value 值 + */ + void put(String key, Object value, long ttl); + + /** + * 获取缓存 + * @param key key + * @return + */ + T get(String key); + + /** + * 删除缓存 + * @param key key + */ + void delete(String key); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/AbstractDataSourceFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/AbstractDataSourceFactory.java new file mode 100644 index 00000000..f816251a --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/AbstractDataSourceFactory.java @@ -0,0 +1,57 @@ +package com.jiuyv.agile.data.database.datasource; + +import com.jiuyv.agile.data.database.*; +import com.jiuyv.agile.data.database.query.AbstractDbQueryFactory; +import com.jiuyv.agile.data.database.query.CacheDbQueryFactoryBean; +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StringUtils; + +import javax.sql.DataSource; + +public abstract class AbstractDataSourceFactory implements DataSourceFactory { + + @Override + public DbQuery createDbQuery(DbQueryProperty property) { + property.viald(); + DbType dbType = DbType.getDbType(property.getDbType()); + DataSource dataSource = createDataSource(property); + DbQuery dbQuery = createDbQuery(dataSource, dbType); + return dbQuery; + } + + public DbQuery createDbQuery(DataSource dataSource, DbType dbType) { + DbDialect dbDialect = DialectFactory.getDialect(dbType); + if(dbDialect == null){ + throw new RuntimeException("该数据库类型正在开发中"); + } + AbstractDbQueryFactory dbQuery = new CacheDbQueryFactoryBean(); + dbQuery.setDataSource(dataSource); + dbQuery.setJdbcTemplate(new JdbcTemplate(dataSource)); + dbQuery.setDbDialect(dbDialect); + return dbQuery; + } + + public DataSource createDataSource(DbQueryProperty property) { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl(trainToJdbcUrl(property)); + dataSource.setUsername(property.getUsername()); + dataSource.setPassword(property.getPassword()); + return dataSource; + } + + protected String trainToJdbcUrl(DbQueryProperty property) { + String url = DbType.getDbType(property.getDbType()).getUrl(); + if (StringUtils.isEmpty(url)) { + throw new RuntimeException("无效数据库类型!"); + } + url = url.replace("${host}", property.getHost()); + url = url.replace("${port}", String.valueOf(property.getPort())); + if (DbType.ORACLE.getDb().equals(property.getDbType()) || DbType.ORACLE_12C.getDb().equals(property.getDbType())) { + url = url.replace("${sid}", property.getSid()); + } else { + url = url.replace("${dbName}", property.getDbName()); + } + return url; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/CacheDataSourceFactoryBean.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/CacheDataSourceFactoryBean.java new file mode 100644 index 00000000..2fde6c45 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/CacheDataSourceFactoryBean.java @@ -0,0 +1,63 @@ +package com.jiuyv.agile.data.database.datasource; + + +import com.jiuyv.agile.data.database.DbQueryProperty; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class CacheDataSourceFactoryBean extends AbstractDataSourceFactory { + + /** + * 数据源缓存 + */ + private static Map dataSourceMap = new ConcurrentHashMap<>(); + + @Override + public DataSource createDataSource(DbQueryProperty property) { + String key = property.getHost() + ":" + property.getPort() + ":" + property.getUsername() + ":" + property.getDbName(); + String s = compress(key); + DataSource dataSource = dataSourceMap.get(s); + if (null == dataSource) { + synchronized (CacheDataSourceFactoryBean.class) { + dataSource = super.createDataSource(property); + dataSourceMap.put(s, dataSource); + } + } + return dataSource; + } + + // 压缩 + public static String compress(String str) { + if (str == null || str.length() == 0) { + return str; + } + MessageDigest md = null; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + if (md != null) { + md.update(str.getBytes()); + byte[] b = md.digest(); + int i; + StringBuffer buf = new StringBuffer(); + for (byte value : b) { + i = value; + if (i < 0) + i += 256; + if (i < 16) + buf.append("0"); + buf.append(Integer.toHexString(i)); + } + str = buf.toString().substring(8, 24).toUpperCase(); + } + return str; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/DefaultDataSourceFactoryBean.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/DefaultDataSourceFactoryBean.java new file mode 100644 index 00000000..297a387b --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/datasource/DefaultDataSourceFactoryBean.java @@ -0,0 +1,4 @@ +package com.jiuyv.agile.data.database.datasource; + +public class DefaultDataSourceFactoryBean extends AbstractDataSourceFactory{ +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/AbstractDbDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/AbstractDbDialect.java new file mode 100644 index 00000000..86751f55 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/AbstractDbDialect.java @@ -0,0 +1,34 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbDialect; + +/** + * 方言抽象类 + */ +public abstract class AbstractDbDialect implements DbDialect { + + @Override + public String columns(String dbName, String tableName) { + return "select column_name AS COLNAME, ordinal_position AS COLPOSITION, column_default AS DATADEFAULT, is_nullable AS NULLABLE, data_type AS DATATYPE, " + + "character_maximum_length AS DATALENGTH, numeric_precision AS DATAPRECISION, numeric_scale AS DATASCALE, column_key AS COLKEY, column_comment AS COLCOMMENT " + + "from information_schema.columns where table_schema = '" + dbName + "' and table_name = '" + tableName + "' order by ordinal_position "; + } + + @Override + public String tables(String dbName) { + return "SELECT table_name AS TABLENAME, table_comment AS TABLECOMMENT FROM information_schema.tables where table_schema = '" + dbName + "' "; + } + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + // 获取 分页实际条数 + StringBuilder sqlBuilder = new StringBuilder(originalSql); + sqlBuilder.append(" LIMIT ").append(offset).append(" , ").append(count); + return sqlBuilder.toString(); + } + + @Override + public String count(String sql) { + return "SELECT COUNT(*) FROM ( " + sql + " ) TEMP"; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/DialectRegistry.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/DialectRegistry.java new file mode 100644 index 00000000..7f80ef55 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/DialectRegistry.java @@ -0,0 +1,27 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbDialect; +import com.jiuyv.agile.data.database.DbType; + +import java.util.EnumMap; +import java.util.Map; + +public class DialectRegistry { + + private final Map dialect_enum_map = new EnumMap<>(DbType.class); + + public DialectRegistry() { + dialect_enum_map.put(DbType.MARIADB, new MariaDBDialect()); + dialect_enum_map.put(DbType.MYSQL, new MySqlDialect()); + dialect_enum_map.put(DbType.ORACLE_12C, new Oracle12cDialect()); + dialect_enum_map.put(DbType.ORACLE, new OracleDialect()); + dialect_enum_map.put(DbType.POSTGRE_SQL, new PostgreDialect()); + dialect_enum_map.put(DbType.SQL_SERVER2008, new SQLServer2008Dialect()); + dialect_enum_map.put(DbType.SQL_SERVER, new SQLServerDialect()); + dialect_enum_map.put(DbType.OTHER, new UnknownDialect()); + } + + public DbDialect getDialect(DbType dbType) { + return dialect_enum_map.get(dbType); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MariaDBDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MariaDBDialect.java new file mode 100644 index 00000000..92571114 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MariaDBDialect.java @@ -0,0 +1,7 @@ +package com.jiuyv.agile.data.database.dialect; + +/** + * MariaDB 数据库方言 + */ +public class MariaDBDialect extends MySqlDialect { +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MySqlDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MySqlDialect.java new file mode 100644 index 00000000..52ab68a0 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/MySqlDialect.java @@ -0,0 +1,41 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; + +/** + * MySql 数据库方言 + */ +public class MySqlDialect extends AbstractDbDialect { + + @Override + public RowMapper columnMapper() { + return (ResultSet rs, int rowNum) -> { + DbColumn entity = new DbColumn(); + entity.setColName(rs.getString("COLNAME")); + entity.setDataType(rs.getString("DATATYPE")); + entity.setDataLength(rs.getString("DATALENGTH")); + entity.setDataPrecision(rs.getString("DATAPRECISION")); + entity.setDataScale(rs.getString("DATASCALE")); + entity.setColKey("PRI".equals(rs.getString("COLKEY")) ? true : false); + entity.setNullable("YES".equals(rs.getString("NULLABLE")) ? true : false); + entity.setColPosition(rs.getInt("COLPOSITION")); + entity.setDataDefault(rs.getString("DATADEFAULT")); + entity.setColComment(rs.getString("COLCOMMENT")); + return entity; + }; + } + + @Override + public RowMapper tableMapper() { + return (ResultSet rs, int rowNum) -> { + DbTable entity = new DbTable(); + entity.setTableName(rs.getString("TABLENAME")); + entity.setTableComment(rs.getString("TABLECOMMENT")); + return entity; + }; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/Oracle12cDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/Oracle12cDialect.java new file mode 100644 index 00000000..6e6cb02e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/Oracle12cDialect.java @@ -0,0 +1,14 @@ +package com.jiuyv.agile.data.database.dialect; + +/** + * ORACLE Oracle12c+数据库方言 + */ +public class Oracle12cDialect extends OracleDialect { + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + StringBuilder sqlBuilder = new StringBuilder(originalSql); + sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY "); + return sqlBuilder.toString(); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/OracleDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/OracleDialect.java new file mode 100644 index 00000000..3502dcaa --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/OracleDialect.java @@ -0,0 +1,69 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; + +/** + * Oracle Oracle11g及以下数据库方言 + */ +public class OracleDialect extends AbstractDbDialect { + + @Override + public String columns(String dbName, String tableName) { + return "select columns.column_name AS colName, columns.data_type AS DATATYPE, columns.data_length AS DATALENGTH, columns.data_precision AS DATAPRECISION, " + + "columns.data_scale AS DATASCALE, columns.nullable AS NULLABLE, columns.column_id AS COLPOSITION, columns.data_default AS DATADEFAULT, comments.comments AS COLCOMMENT," + + "case when t.column_name is null then 0 else 1 end as COLKEY " + + "from sys.user_tab_columns columns LEFT JOIN sys.user_col_comments comments ON columns.table_name = comments.table_name AND columns.column_name = comments.column_name " + + "left join ( " + + "select col.column_name as column_name, con.table_name as table_name from user_constraints con, user_cons_columns col " + + "where con.constraint_name = col.constraint_name and con.constraint_type = 'P' " + + ") t on t.table_name = columns.table_name and columns.column_name = t.column_name " + + "where columns.table_name = UPPER('" + tableName + "') order by columns.column_id "; + } + + @Override + public String tables(String dbName) { + return "select tables.table_name AS TABLENAME, comments.comments AS TABLECOMMENT from sys.user_tables tables " + + "LEFT JOIN sys.user_tab_comments comments ON tables.table_name = comments.table_name "; + } + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( "); + sqlBuilder.append(originalSql).append(" ) TMP WHERE ROWNUM <=").append((offset >= 1) ? (offset + count) : count); + sqlBuilder.append(") WHERE ROW_ID > ").append(offset); + return sqlBuilder.toString(); + } + + @Override + public RowMapper columnMapper() { + return (ResultSet rs, int rowNum) -> { + DbColumn entity = new DbColumn(); + entity.setColName(rs.getString("COLNAME")); + entity.setDataType(rs.getString("DATATYPE")); + entity.setDataLength(rs.getString("DATALENGTH")); + entity.setDataPrecision(rs.getString("DATAPRECISION")); + entity.setDataScale(rs.getString("DATASCALE")); + entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false); + entity.setNullable("Y".equals(rs.getString("NULLABLE")) ? true : false); + entity.setColPosition(rs.getInt("COLPOSITION")); + entity.setDataDefault(rs.getString("DATADEFAULT")); + entity.setColComment(rs.getString("COLCOMMENT")); + return entity; + }; + } + + @Override + public RowMapper tableMapper() { + return (ResultSet rs, int rowNum) -> { + DbTable entity = new DbTable(); + entity.setTableName(rs.getString("TABLENAME")); + entity.setTableComment(rs.getString("TABLECOMMENT")); + return entity; + }; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/PostgreDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/PostgreDialect.java new file mode 100644 index 00000000..550366c8 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/PostgreDialect.java @@ -0,0 +1,68 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; + +/** + * Postgre 数据库方言 + */ +public class PostgreDialect extends AbstractDbDialect { + + @Override + public String columns(String dbName, String tableName) { + return "select col.column_name AS COLNAME, col.ordinal_position AS COLPOSITION, col.column_default AS DATADEFAULT, col.is_nullable AS NULLABLE, col.udt_name AS DATATYPE, " + + "col.character_maximum_length AS DATALENGTH, col.numeric_precision AS DATAPRECISION, col.numeric_scale AS DATASCALE, des.description AS COLCOMMENT, " + + "case when t.colname is null then 0 else 1 end as COLKEY " + + "from information_schema.columns col left join pg_description des on col.table_name::regclass = des.objoid and col.ordinal_position = des.objsubid " + + "left join ( " + + "select pg_attribute.attname as colname from pg_constraint inner join pg_class on pg_constraint.conrelid = pg_class.oid " + + "inner join pg_attribute on pg_attribute.attrelid = pg_class.oid and pg_attribute.attnum = any(pg_constraint.conkey) " + + "where pg_class.relname = '" + tableName + "' and pg_constraint.contype = 'p' " + + ") t on t.colname = col.column_name " + + "where col.table_catalog = '" + dbName + "' and col.table_schema = 'public' and col.table_name = '" + tableName + "' order by col.ordinal_position "; + } + + @Override + public String tables(String dbName) { + return "select relname AS TABLENAME, cast(obj_description(relfilenode, 'pg_class') as varchar) AS TABLECOMMENT from pg_class " + + "where relname in (select tablename from pg_tables where schemaname = 'public' and position('_2' in tablename) = 0) "; + } + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + StringBuilder sqlBuilder = new StringBuilder(originalSql); + sqlBuilder.append(" LIMIT ").append(count).append(" offset ").append(offset); + return sqlBuilder.toString(); + } + + @Override + public RowMapper columnMapper() { + return (ResultSet rs, int rowNum) -> { + DbColumn entity = new DbColumn(); + entity.setColName(rs.getString("COLNAME")); + entity.setDataType(rs.getString("DATATYPE")); + entity.setDataLength(rs.getString("DATALENGTH")); + entity.setDataPrecision(rs.getString("DATAPRECISION")); + entity.setDataScale(rs.getString("DATASCALE")); + entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false); + entity.setNullable("YES".equals(rs.getString("NULLABLE")) ? true : false); + entity.setColPosition(rs.getInt("COLPOSITION")); + entity.setDataDefault(rs.getString("DATADEFAULT")); + entity.setColComment(rs.getString("COLCOMMENT")); + return entity; + }; + } + + @Override + public RowMapper tableMapper() { + return (ResultSet rs, int rowNum) -> { + DbTable entity = new DbTable(); + entity.setTableName(rs.getString("TABLENAME")); + entity.setTableComment(rs.getString("TABLECOMMENT")); + return entity; + }; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServer2008Dialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServer2008Dialect.java new file mode 100644 index 00000000..240ae556 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServer2008Dialect.java @@ -0,0 +1,102 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.util.StringUtils; + +import java.sql.ResultSet; + +/** + * SQLServer 2005 数据库方言 + */ +public class SQLServer2008Dialect extends AbstractDbDialect { + + @Override + public String columns(String dbName, String tableName) { + return "select columns.name AS colName, columns.column_id AS COLPOSITION, columns.max_length AS DATALENGTH, columns.precision AS DATAPRECISION, columns.scale AS DATASCALE, " + + "columns.is_nullable AS NULLABLE, types.name AS DATATYPE, CAST(ep.value AS NVARCHAR(128)) AS COLCOMMENT, e.text AS DATADEFAULT, " + + "(select top 1 ind.is_primary_key from sys.index_columns ic left join sys.indexes ind on ic.object_id = ind.object_id and ic.index_id = ind.index_id and ind.name like 'PK_%' where ic.object_id=columns.object_id and ic.column_id=columns.column_id) AS COLKEY " + + "from sys.columns columns LEFT JOIN sys.types types ON columns.system_type_id = types.system_type_id " + + "LEFT JOIN syscomments e ON columns.default_object_id= e.id " + + "LEFT JOIN sys.extended_properties ep ON ep.major_id = columns.object_id AND ep.minor_id = columns.column_id AND ep.name = 'MS_Description' " + + "where columns.object_id = object_id('" + tableName + "') order by columns.column_id "; + } + + @Override + public String tables(String dbName) { + return "select tables.name AS TABLENAME, CAST(ep.value AS NVARCHAR(128)) AS TABLECOMMENT " + + "from sys.tables tables LEFT JOIN sys.extended_properties ep ON ep.major_id = tables.object_id AND ep.minor_id = 0"; + } + + private static String getOrderByPart(String sql) { + String loweredString = sql.toLowerCase(); + int orderByIndex = loweredString.indexOf("order by"); + if (orderByIndex != -1) { + return sql.substring(orderByIndex); + } else { + return ""; + } + } + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + StringBuilder pagingBuilder = new StringBuilder(); + String orderby = getOrderByPart(originalSql); + String distinctStr = ""; + + String loweredString = originalSql.toLowerCase(); + String sqlPartString = originalSql; + if (loweredString.trim().startsWith("select")) { + int index = 6; + if (loweredString.startsWith("select distinct")) { + distinctStr = "DISTINCT "; + index = 15; + } + sqlPartString = sqlPartString.substring(index); + } + pagingBuilder.append(sqlPartString); + + // if no ORDER BY is specified use fake ORDER BY field to avoid errors + if (StringUtils.isEmpty(orderby)) { + orderby = "ORDER BY CURRENT_TIMESTAMP"; + } + StringBuilder sql = new StringBuilder(); + sql.append("WITH selectTemp AS (SELECT ").append(distinctStr).append("TOP 100 PERCENT ") + .append(" ROW_NUMBER() OVER (").append(orderby).append(") as __row_number__, ").append(pagingBuilder) + .append(") SELECT * FROM selectTemp WHERE __row_number__ BETWEEN ") + //FIX#299:原因:mysql中limit 10(offset,size) 是从第10开始(不包含10),;而这里用的BETWEEN是两边都包含,所以改为offset+1 + .append(offset + 1) + .append(" AND ") + .append(offset + count).append(" ORDER BY __row_number__"); + return sql.toString(); + } + + @Override + public RowMapper columnMapper() { + return (ResultSet rs, int rowNum) -> { + DbColumn entity = new DbColumn(); + entity.setColName(rs.getString("COLNAME")); + entity.setDataType(rs.getString("DATATYPE")); + entity.setDataLength(rs.getString("DATALENGTH")); + entity.setDataPrecision(rs.getString("DATAPRECISION")); + entity.setDataScale(rs.getString("DATASCALE")); + entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false); + entity.setNullable("1".equals(rs.getString("NULLABLE")) ? true : false); + entity.setColPosition(rs.getInt("COLPOSITION")); + entity.setDataDefault(rs.getString("DATADEFAULT")); + entity.setColComment(rs.getString("COLCOMMENT")); + return entity; + }; + } + + @Override + public RowMapper tableMapper() { + return (ResultSet rs, int rowNum) -> { + DbTable entity = new DbTable(); + entity.setTableName(rs.getString("TABLENAME")); + entity.setTableComment(rs.getString("TABLECOMMENT")); + return entity; + }; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServerDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServerDialect.java new file mode 100644 index 00000000..5f3ac692 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/SQLServerDialect.java @@ -0,0 +1,14 @@ +package com.jiuyv.agile.data.database.dialect; + +/** + * SQLServer 数据库方言 + */ +public class SQLServerDialect extends SQLServer2008Dialect { + + @Override + public String buildPaginationSql(String originalSql, long offset, long count) { + StringBuilder sqlBuilder = new StringBuilder(originalSql); + sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY "); + return sqlBuilder.toString(); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/UnknownDialect.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/UnknownDialect.java new file mode 100644 index 00000000..cb99e535 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/dialect/UnknownDialect.java @@ -0,0 +1,41 @@ +package com.jiuyv.agile.data.database.dialect; + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import org.springframework.jdbc.core.RowMapper; + +/** + * 未知 数据库方言 + */ +public class UnknownDialect extends AbstractDbDialect { + + @Override + public String columns(String dbName, String tableName) { + throw new RuntimeException("不支持的数据库类型"); + } + + @Override + public String tables(String dbName) { + throw new RuntimeException("不支持的数据库类型"); + } + + @Override + public String buildPaginationSql(String sql, long offset, long count) { + throw new RuntimeException("不支持的数据库类型"); + } + + @Override + public String count(String sql) { + throw new RuntimeException("不支持的数据库类型"); + } + + @Override + public RowMapper columnMapper() { + throw new RuntimeException("不支持的数据库类型"); + } + + @Override + public RowMapper tableMapper() { + throw new RuntimeException("不支持的数据库类型"); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/AbstractDbQueryFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/AbstractDbQueryFactory.java new file mode 100644 index 00000000..0ba7e7fa --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/AbstractDbQueryFactory.java @@ -0,0 +1,134 @@ +package com.jiuyv.agile.data.database.query; + +import com.jiuyv.agile.data.database.*; +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +public abstract class AbstractDbQueryFactory implements DbQuery { + + protected DataSource dataSource; + + protected JdbcTemplate jdbcTemplate; + + protected DbDialect dbDialect; + + @Override + public Connection getConnection() { + try { + return dataSource.getConnection(); + } catch (SQLException e) { + throw new RuntimeException("获取数据库连接出错"); + } + } + + @Override + public boolean valid() { + Connection conn = null; + try { + conn = dataSource.getConnection(); + return conn.isValid(0); + } catch (SQLException e) { + throw new RuntimeException("检测连通性出错"); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + throw new RuntimeException("关闭数据库连接出错"); + } + } + } + + } + + @Override + public void close() { + if (dataSource instanceof HikariDataSource) { + ((HikariDataSource) dataSource).close(); + } else { + throw new RuntimeException("不合法数据源类型"); + } + } + + @Override + public List getTableColumns(String dbName, String tableName) { + String sql = dbDialect.columns(dbName, tableName); + return jdbcTemplate.query(sql, dbDialect.columnMapper()); + } + + @Override + public List getTables(String dbName) { + String sql = dbDialect.tables(dbName); + return jdbcTemplate.query(sql, dbDialect.tableMapper()); + } + + @Override + public int count(String sql) { + return jdbcTemplate.queryForObject(dbDialect.count(sql), Integer.class); + } + + @Override + public int count(String sql, Object[] args) { + return jdbcTemplate.queryForObject(dbDialect.count(sql), args, Integer.class); + } + + @Override + public int count(String sql, Map params) { + NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); + return namedJdbcTemplate.queryForObject(dbDialect.count(sql), params, Integer.class); + } + + @Override + public List> queryList(String sql) { + return jdbcTemplate.queryForList(sql); + } + + @Override + public List> queryList(String sql, Object[] args) { + return jdbcTemplate.queryForList(sql, args); + } + + @Override + public PageResult> queryByPage(String sql, long offset, long size) { + int total = count(sql); + String pageSql = dbDialect.buildPaginationSql(sql, offset, size); + List> records = jdbcTemplate.queryForList(pageSql); + return new PageResult<>(total, records); + } + + @Override + public PageResult> queryByPage(String sql, Object[] args, long offset, long size) { + int total = count(sql, args); + String pageSql = dbDialect.buildPaginationSql(sql, offset, size); + List> records = jdbcTemplate.queryForList(pageSql, args); + return new PageResult<>(total, records); + } + + @Override + public PageResult> queryByPage(String sql, Map params, long offset, long size) { + int total = count(sql, params); + String pageSql = dbDialect.buildPaginationSql(sql, offset, size); + NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); + List> records = namedJdbcTemplate.queryForList(pageSql, params); + return new PageResult<>(total, records); + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void setDbDialect(DbDialect dbDialect) { + this.dbDialect = dbDialect; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/CacheDbQueryFactoryBean.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/CacheDbQueryFactoryBean.java new file mode 100644 index 00000000..3d2fee64 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/database/query/CacheDbQueryFactoryBean.java @@ -0,0 +1,105 @@ +package com.jiuyv.agile.data.database.query; + + +import com.jiuyv.agile.data.database.DbColumn; +import com.jiuyv.agile.data.database.DbTable; +import com.jiuyv.agile.data.database.PageResult; +import com.jiuyv.agile.data.database.cache.DefaultSqlCache; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class CacheDbQueryFactoryBean extends AbstractDbQueryFactory { + + /** + * 默认缓存5分钟 + */ + private static long DEFAULT_EXPIRE = 5 * 60 * 1000; + private static DefaultSqlCache sqlCache = new DefaultSqlCache(100, DEFAULT_EXPIRE); + + private T putCacheValue(String key, T value, long ttl) { + sqlCache.put(key, value, ttl); + return value; + } + + @Override + public List getTableColumns(String dbName, String tableName) { + Object[] args = new Object[]{dbName, tableName}; + Optional o = Optional.ofNullable(sqlCache.get(sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":getTableColumns", args))); + return super.getTableColumns(dbName, tableName); + } + + @Override + public List getTables(String dbName) { + Object[] args = new Object[]{dbName}; + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":getTables", args); + return (List) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.getTables(dbName), DEFAULT_EXPIRE)); + } + + @Override + public int count(String sql) { + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, null); + return (int) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.count(sql), DEFAULT_EXPIRE)); + } + + @Override + public int count(String sql, Object[] args) { + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args); + return (int) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.count(sql, args), DEFAULT_EXPIRE)); + } + + @Override + public int count(String sql, Map params) { + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, params.values().toArray()); + return (int) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.count(sql, params), DEFAULT_EXPIRE)); + } + + @Override + public List> queryList(String sql) { + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, null); + return (List>) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.queryList(sql), DEFAULT_EXPIRE)); + } + + @Override + public List> queryList(String sql, Object[] args) { + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args); + return (List>) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.queryList(sql, args), DEFAULT_EXPIRE)); + } + + @Override + public PageResult> queryByPage(String sql, long offset, long size) { + Object[] args = new Object[]{offset, size}; + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args); + return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.queryByPage(sql, offset, size), DEFAULT_EXPIRE)); + } + + @Override + public PageResult> queryByPage(String sql, Object[] args, long offset, long size) { + Object[] objects = Arrays.copyOf(args, args.length + 2); + objects[args.length] = offset; + objects[args.length + 1] = size; + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, objects); + return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.queryByPage(sql, args, offset, size), DEFAULT_EXPIRE)); + } + + @Override + public PageResult> queryByPage(String sql, Map params, long offset, long size) { + Object[] args = params.values().toArray(); + Object[] objects = Arrays.copyOf(args, args.length + 2); + objects[args.length] = offset; + objects[args.length + 1] = size; + String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, objects); + return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey)) + .orElse(putCacheValue(cacheKey, super.queryByPage(sql, params, offset, size), DEFAULT_EXPIRE)); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AbstractFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AbstractFactory.java new file mode 100644 index 00000000..55e12ed2 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AbstractFactory.java @@ -0,0 +1,8 @@ +package com.jiuyv.agile.data.factory; + +import com.jiuyv.agile.data.factory.crypto.Crypto; + +public abstract class AbstractFactory { + + public abstract Crypto getCrypto(String type); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AlgorithmFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AlgorithmFactory.java new file mode 100644 index 00000000..a7fe4e89 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/AlgorithmFactory.java @@ -0,0 +1,16 @@ +package com.jiuyv.agile.data.factory; + +import com.jiuyv.agile.data.dto.enums.AlgorithmCrypto; +import com.jiuyv.agile.data.factory.crypto.AlgorithmRegistry; +import com.jiuyv.agile.data.factory.crypto.Crypto; + +public class AlgorithmFactory extends AbstractFactory { + + private static final AlgorithmRegistry ALGORITHM_REGISTRY = new AlgorithmRegistry(); + + @Override + public Crypto getCrypto(String type) { + AlgorithmCrypto crypto = AlgorithmCrypto.getAlgorithmCrypto(type); + return ALGORITHM_REGISTRY.getAlgorithm(crypto); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/FactoryProducer.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/FactoryProducer.java new file mode 100644 index 00000000..d91ca01f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/FactoryProducer.java @@ -0,0 +1,17 @@ +package com.jiuyv.agile.data.factory; + +import com.jiuyv.agile.data.dto.enums.CipherType; + +public class FactoryProducer { + + public static AbstractFactory getFactory(String type){ + CipherType cipherType = CipherType.getCipherType(type); + switch (cipherType) { + case REGEX: + return new RegexFactory(); + case ALGORITHM: + return new AlgorithmFactory(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/RegexFactory.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/RegexFactory.java new file mode 100644 index 00000000..32d5ea36 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/RegexFactory.java @@ -0,0 +1,16 @@ +package com.jiuyv.agile.data.factory; + +import com.jiuyv.agile.data.dto.enums.RegexCrypto; +import com.jiuyv.agile.data.factory.crypto.Crypto; +import com.jiuyv.agile.data.factory.crypto.RegexRegistry; + +public class RegexFactory extends AbstractFactory { + + private static final RegexRegistry REGEX_REGISTRY = new RegexRegistry(); + + @Override + public Crypto getCrypto(String type) { + RegexCrypto crypto = RegexCrypto.getRegexCrypto(type); + return REGEX_REGISTRY.getRegex(crypto); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/ADDRESSCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/ADDRESSCrypto.java new file mode 100644 index 00000000..daa025dd --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/ADDRESSCrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [地址] 只显示前六位,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****> + */ +public class ADDRESSCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.rightPad(StringUtils.left(content, 6), StringUtils.length(content), "*"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AESCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AESCrypto.java new file mode 100644 index 00000000..fc983702 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AESCrypto.java @@ -0,0 +1,72 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.SecureRandom; +import java.util.Base64; + +public class AESCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + //1.构造密钥生成器,指定为AES算法,不区分大小写 + KeyGenerator kGen = KeyGenerator.getInstance("AES"); + //2.根据 RULES 规则初始化密钥生成器,根据传入的字节数组生成一个128位的随机源 + kGen.init(128, new SecureRandom(SLAT.getBytes(CHARSET_UTF8))); + //3.产生原始对称密钥 + SecretKey secretKey = kGen.generateKey(); + //4.获得原始对称密钥的字节数组 + byte[] enCodeFormat = secretKey.getEncoded(); + //5.根据字节数组生成AES密钥 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); + //6.根据指定算法AES生成密码器 + Cipher cipher = Cipher.getInstance("AES"); + //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY + cipher.init(Cipher.ENCRYPT_MODE, key); + //8.根据密码器的初始化方式--加密:将数据加密 + byte[] AES_encrypt = cipher.doFinal(content.getBytes(CHARSET_UTF8)); + //9.将字符串返回 + return Base64.getEncoder().encodeToString(AES_encrypt); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public String decrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + //1.构造密钥生成器,指定为AES算法,不区分大小写 + KeyGenerator kGen = KeyGenerator.getInstance("AES"); + //2.根据 RULES 规则初始化密钥生成器,根据传入的字节数组生成一个128位的随机源 + kGen.init(128, new SecureRandom(SLAT.getBytes(CHARSET_UTF8))); + //3.产生原始对称密钥 + SecretKey secretKey = kGen.generateKey(); + //4.获得原始对称密钥的字节数组 + byte[] enCodeFormat = secretKey.getEncoded(); + //5.根据字节数组生成AES密钥 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); + //6.根据指定算法AES生成密码器 + Cipher cipher = Cipher.getInstance("AES"); + //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY + cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 + //8.根据密码器的初始化方式--加密:将数据加密 + byte[] AES_decode = cipher.doFinal(Base64.getDecoder().decode(content)); + return new String(AES_decode, CHARSET_UTF8); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AlgorithmRegistry.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AlgorithmRegistry.java new file mode 100644 index 00000000..1a4beb55 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/AlgorithmRegistry.java @@ -0,0 +1,24 @@ +package com.jiuyv.agile.data.factory.crypto; + +import com.jiuyv.agile.data.dto.enums.AlgorithmCrypto; + +import java.util.EnumMap; +import java.util.Map; + +public class AlgorithmRegistry { + + private final Map algorithm_enum_map = new EnumMap<>(AlgorithmCrypto.class); + + public AlgorithmRegistry() { + algorithm_enum_map.put(AlgorithmCrypto.BASE64, new BASE64Crypto()); + algorithm_enum_map.put(AlgorithmCrypto.AES, new AESCrypto()); + algorithm_enum_map.put(AlgorithmCrypto.DES, new DESCrypto()); + algorithm_enum_map.put(AlgorithmCrypto.MD5, new MD5Crypto()); + algorithm_enum_map.put(AlgorithmCrypto.SHA_1, new SHA1Crypto()); + algorithm_enum_map.put(AlgorithmCrypto.SHA_256, new SHA256Crypto()); + } + + public Crypto getAlgorithm(AlgorithmCrypto crypto) { + return algorithm_enum_map.get(crypto); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BANKCARDCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BANKCARDCrypto.java new file mode 100644 index 00000000..955dfcd7 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BANKCARDCrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234> + */ +public class BANKCARDCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.left(content, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(content, 4), StringUtils.length(content), "*"), "******")); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BASE64Crypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BASE64Crypto.java new file mode 100644 index 00000000..e0b1ab05 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/BASE64Crypto.java @@ -0,0 +1,34 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Base64; + +public class BASE64Crypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + byte[] encode = Base64.getEncoder().encode(content.getBytes(CHARSET_UTF8)); + return new String(encode, CHARSET_UTF8); + } catch (Exception e) { + } + return null; + } + + @Override + public String decrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + byte[] decode = Base64.getDecoder().decode(content.getBytes(CHARSET_UTF8)); + return new String(decode, CHARSET_UTF8); + } catch (Exception e) { + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CHINESENAMECrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CHINESENAMECrypto.java new file mode 100644 index 00000000..aa20f76d --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CHINESENAMECrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [中文姓名] 只显示第一个汉字,其他隐藏为星号<例子:李**> + */ +public class CHINESENAMECrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.rightPad(StringUtils.left(content, 1), StringUtils.length(content), "*"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CNAPSCODECrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CNAPSCODECrypto.java new file mode 100644 index 00000000..43b24f6c --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/CNAPSCODECrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [公司开户银行联号] 公司开户银行联行号,显示前四位,其他用星号隐藏,每位1个星号<例子:1234********> + */ +public class CNAPSCODECrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.rightPad(StringUtils.left(content, 4), StringUtils.length(content), "*"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/Crypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/Crypto.java new file mode 100644 index 00000000..46582406 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/Crypto.java @@ -0,0 +1,17 @@ +package com.jiuyv.agile.data.factory.crypto; + +public interface Crypto { + + /** + * 字符编码 + */ + String CHARSET_UTF8 = "UTF-8"; + /** + * 密码盐 + */ + String SLAT = "DATAX:20200101"; + + String encrypt(String content); + + String decrypt(String content); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/DESCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/DESCrypto.java new file mode 100644 index 00000000..16c7ad3e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/DESCrypto.java @@ -0,0 +1,54 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.SecureRandom; +import java.util.Base64; + +public class DESCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + KeyGenerator kGen = KeyGenerator.getInstance("DES"); + kGen.init(56, new SecureRandom(SLAT.getBytes(CHARSET_UTF8))); + SecretKey secretKey = kGen.generateKey(); + byte[] enCodeFormat = secretKey.getEncoded(); + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "DES"); + Cipher cipher = Cipher.getInstance("DES"); + cipher.init(Cipher.ENCRYPT_MODE, key); + byte[] DES_encrypt = cipher.doFinal(content.getBytes(CHARSET_UTF8)); + return Base64.getEncoder().encodeToString(DES_encrypt); + } catch (Exception e) { + } + return null; + } + + @Override + public String decrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + try { + KeyGenerator kGen = KeyGenerator.getInstance("DES"); + kGen.init(56, new SecureRandom(SLAT.getBytes(CHARSET_UTF8))); + SecretKey secretKey = kGen.generateKey(); + byte[] enCodeFormat = secretKey.getEncoded(); + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "DES"); + Cipher cipher = Cipher.getInstance("DES"); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] DES_decrypt = cipher.doFinal(Base64.getDecoder().decode(content)); + return new String(DES_decrypt, CHARSET_UTF8); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/EMAILCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/EMAILCrypto.java new file mode 100644 index 00000000..1881c749 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/EMAILCrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [电子邮箱] 只显示前三后显示邮箱后缀,其他隐藏为星号<例子:312****@qq.com> + */ +public class EMAILCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return content.replaceAll("(\\w{3}).*@(\\w+)", "$1****@$2"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/FIXEDPHONECrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/FIXEDPHONECrypto.java new file mode 100644 index 00000000..3e5f8d7b --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/FIXEDPHONECrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [固定电话] 后四位,其他隐藏<例子:****1234> + */ +public class FIXEDPHONECrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.leftPad(StringUtils.right(content, 4), StringUtils.length(content), "*"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/IDCARDCrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/IDCARDCrypto.java new file mode 100644 index 00000000..b080621f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/IDCARDCrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762> + */ +public class IDCARDCrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.leftPad(StringUtils.right(content, 4), StringUtils.length(content), "*"); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MD5Crypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MD5Crypto.java new file mode 100644 index 00000000..e262ae7d --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MD5Crypto.java @@ -0,0 +1,32 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import java.security.MessageDigest; +import java.util.Base64; + +public class MD5Crypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + md5.update(content.getBytes(CHARSET_UTF8)); + md5.update(SLAT.getBytes(CHARSET_UTF8)); + } catch (Exception ignored) { + } + if (md5 != null) { + return Base64.getEncoder().encodeToString(md5.digest()); + } + return null; + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MOBILEPHONECrypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MOBILEPHONECrypto.java new file mode 100644 index 00000000..5ecd6786 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/MOBILEPHONECrypto.java @@ -0,0 +1,22 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +/** + * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234> + */ +public class MOBILEPHONECrypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + return StringUtils.left(content, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(content, 4), StringUtils.length(content), "*"), "***")); + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/RegexRegistry.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/RegexRegistry.java new file mode 100644 index 00000000..f907df19 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/RegexRegistry.java @@ -0,0 +1,26 @@ +package com.jiuyv.agile.data.factory.crypto; + +import com.jiuyv.agile.data.dto.enums.RegexCrypto; + +import java.util.EnumMap; +import java.util.Map; + +public class RegexRegistry { + + private final Map regex_enum_map = new EnumMap<>(RegexCrypto.class); + + public RegexRegistry() { + regex_enum_map.put(RegexCrypto.CHINESE_NAME, new CHINESENAMECrypto()); + regex_enum_map.put(RegexCrypto.ID_CARD, new IDCARDCrypto()); + regex_enum_map.put(RegexCrypto.FIXED_PHONE, new FIXEDPHONECrypto()); + regex_enum_map.put(RegexCrypto.MOBILE_PHONE, new MOBILEPHONECrypto()); + regex_enum_map.put(RegexCrypto.ADDRESS, new ADDRESSCrypto()); + regex_enum_map.put(RegexCrypto.EMAIL, new EMAILCrypto()); + regex_enum_map.put(RegexCrypto.BANK_CARD, new BANKCARDCrypto()); + regex_enum_map.put(RegexCrypto.CNAPS_CODE, new CNAPSCODECrypto()); + } + + public Crypto getRegex(RegexCrypto crypto) { + return regex_enum_map.get(crypto); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA1Crypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA1Crypto.java new file mode 100644 index 00000000..3e0093ab --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA1Crypto.java @@ -0,0 +1,32 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import java.security.MessageDigest; +import java.util.Base64; + +public class SHA1Crypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("SHA-1"); + md5.update(content.getBytes(CHARSET_UTF8)); + md5.update(SLAT.getBytes(CHARSET_UTF8)); + } catch (Exception ignored) { + } + if (md5 != null) { + return Base64.getEncoder().encodeToString(md5.digest()); + } + return null; + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA256Crypto.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA256Crypto.java new file mode 100644 index 00000000..f408ad64 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/factory/crypto/SHA256Crypto.java @@ -0,0 +1,32 @@ +package com.jiuyv.agile.data.factory.crypto; + +import org.apache.commons.lang3.StringUtils; + +import java.security.MessageDigest; +import java.util.Base64; + +public class SHA256Crypto implements Crypto { + + @Override + public String encrypt(String content) { + if (StringUtils.isBlank(content)) { + return null; + } + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("SHA-256"); + md5.update(content.getBytes(CHARSET_UTF8)); + md5.update(SLAT.getBytes(CHARSET_UTF8)); + } catch (Exception ignored) { + } + if (md5 != null) { + return Base64.getEncoder().encodeToString(md5.digest()); + } + return null; + } + + @Override + public String decrypt(String content) { + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/DbSchemaTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/DbSchemaTypeHandler.java new file mode 100644 index 00000000..487e38ea --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/DbSchemaTypeHandler.java @@ -0,0 +1,62 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.request.DbSchema; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * DbSchema自定义类型 + * + * @author yulei + */ +@MappedTypes(DbSchema.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class DbSchemaTypeHandler extends BaseTypeHandler { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, DbSchema DbSchema, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(DbSchema)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public DbSchema getNullableResult(ResultSet resultSet, String s) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(s),DbSchema.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public DbSchema getNullableResult(ResultSet resultSet, int i) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(i),DbSchema.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public DbSchema getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + try { + return new ObjectMapper().readValue(callableStatement.getString(i),DbSchema.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ExecuteConfigTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ExecuteConfigTypeHandler.java new file mode 100644 index 00000000..befc89ed --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ExecuteConfigTypeHandler.java @@ -0,0 +1,62 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.request.ExecuteConfig; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * ExecuteConfig自定义类型 + * + * @author yulei + */ +@MappedTypes(ExecuteConfig.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class ExecuteConfigTypeHandler extends BaseTypeHandler { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, ExecuteConfig ExecuteConfig, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(ExecuteConfig)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public ExecuteConfig getNullableResult(ResultSet resultSet, String s) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(s),ExecuteConfig.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public ExecuteConfig getNullableResult(ResultSet resultSet, int i) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(i),ExecuteConfig.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public ExecuteConfig getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + try { + return new ObjectMapper().readValue(callableStatement.getString(i),ExecuteConfig.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListFieldRuleTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListFieldRuleTypeHandler.java new file mode 100644 index 00000000..70f5c442 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListFieldRuleTypeHandler.java @@ -0,0 +1,71 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.request.FieldRule; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * List自定义类型 + * + * @author yulei + */ +@MappedTypes(List.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class ListFieldRuleTypeHandler extends BaseTypeHandler> { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, List list, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(list)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public List getNullableResult(ResultSet resultSet, String s) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, FieldRule.class); + return objectMapper.readValue(resultSet.getString(s),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(ResultSet resultSet, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, FieldRule.class); + return objectMapper.readValue(resultSet.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, FieldRule.class); + return objectMapper.readValue(callableStatement.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListReqParamTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListReqParamTypeHandler.java new file mode 100644 index 00000000..e8bda117 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListReqParamTypeHandler.java @@ -0,0 +1,71 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.request.ReqParam; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * List自定义类型 + * + * @author yulei + */ +@MappedTypes(List.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class ListReqParamTypeHandler extends BaseTypeHandler> { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, List list, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(list)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public List getNullableResult(ResultSet resultSet, String s) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ReqParam.class); + return objectMapper.readValue(resultSet.getString(s),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(ResultSet resultSet, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ReqParam.class); + return objectMapper.readValue(resultSet.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ReqParam.class); + return objectMapper.readValue(callableStatement.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListResParamTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListResParamTypeHandler.java new file mode 100644 index 00000000..0f0495ed --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/ListResParamTypeHandler.java @@ -0,0 +1,71 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.response.ResParam; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * List自定义类型 + * + * @author yulei + */ +@MappedTypes(List.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class ListResParamTypeHandler extends BaseTypeHandler> { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, List list, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(list)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public List getNullableResult(ResultSet resultSet, String s) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ResParam.class); + return objectMapper.readValue(resultSet.getString(s),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(ResultSet resultSet, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ResParam.class); + return objectMapper.readValue(resultSet.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + ObjectMapper objectMapper = new ObjectMapper(); + try { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, ResParam.class); + return objectMapper.readValue(callableStatement.getString(i),javaType); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/MappingHandlerMapping.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/MappingHandlerMapping.java new file mode 100644 index 00000000..f6efb3e7 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/MappingHandlerMapping.java @@ -0,0 +1,117 @@ +package com.jiuyv.agile.data.handler; + +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class MappingHandlerMapping { + + private static Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class); + + private static Map mappings = new ConcurrentHashMap<>(); + private RequestMappingHandlerMapping requestMappingHandlerMapping; + private RequestHandler handler; + private Method method; + + { + try { + method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class, Map.class); + } catch (NoSuchMethodException e) { + } + } + + private String ignore = "services"; + private String prefix = "v1.0.0"; + private String separator = "/"; + + public MappingHandlerMapping() {} + + public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) { + this.requestMappingHandlerMapping = requestMappingHandlerMapping; + } + + public void setHandler(RequestHandler handler) { + this.handler = handler; + } + + public static DataApiEntity getMappingApiInfo(HttpServletRequest request) { + NativeWebRequest webRequest = new ServletWebRequest(request); + String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); + return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping)); + } + + public static DataApiEntity getMappingApiInfo(String key) { + return mappings.get(key); + } + + public static String buildMappingKey(String requestMethod, String requestMapping) { + return requestMethod.toUpperCase() + ":" + requestMapping; + } + + /** + * 注册请求映射 + * + * @param api + */ + public void registerMapping(DataApiEntity api) { + String mappingKey = getMappingKey(api); + if (mappings.containsKey(mappingKey)) { + // 取消注册 + mappings.remove(mappingKey); + requestMappingHandlerMapping.unregisterMapping(getRequestMapping(api)); + } + logger.info("注册接口:{}:{}", api.getApiName(),mappingKey); + RequestMappingInfo requestMapping = getRequestMapping(api); + mappings.put(mappingKey, api); + requestMappingHandlerMapping.registerMapping(requestMapping, handler, method); + } + + /** + * 取消注册请求映射 + * + * @param api + */ + public void unregisterMapping(DataApiEntity api) { + String mappingKey = getMappingKey(api); + if (mappings.containsKey(mappingKey)) { + // 取消注册 + mappings.remove(mappingKey); + requestMappingHandlerMapping.unregisterMapping(getRequestMapping(api)); + } + logger.info("取消注册接口:{}:{}", api.getApiName(),mappingKey); + } + + private String getMappingKey(DataApiEntity api) { + return buildMappingKey(api.getReqMethod().toUpperCase(), getRequestPath(api.getApiVersion(), api.getApiUrl())); + } + + private RequestMappingInfo getRequestMapping(DataApiEntity api) { + return RequestMappingInfo.paths(getRequestPath(api.getApiVersion(), api.getApiUrl())).methods(RequestMethod.valueOf(api.getReqMethod().toUpperCase())).build(); + } + + /** + * 调用接口 /services/v1.0.0/user/1 + * @param version + * @param path + * @return + */ + private String getRequestPath(String version, String path) { + if (version != null) { + prefix = version; + } + return separator + ignore + separator + prefix + (path.startsWith(separator) ? path : (separator + path)); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RateLimitTypeHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RateLimitTypeHandler.java new file mode 100644 index 00000000..07a6f04b --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RateLimitTypeHandler.java @@ -0,0 +1,62 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.dto.request.RateLimit; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * RateLimit自定义类型 + * + * @author yulei + */ +@MappedTypes(RateLimit.class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class RateLimitTypeHandler extends BaseTypeHandler { + @Override + public void setNonNullParameter(PreparedStatement preparedStatement, int i, RateLimit rateLimit, JdbcType jdbcType) throws SQLException { + try { + preparedStatement.setString(i,new ObjectMapper().writeValueAsString(rateLimit)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + @Override + public RateLimit getNullableResult(ResultSet resultSet, String s) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(s),RateLimit.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public RateLimit getNullableResult(ResultSet resultSet, int i) throws SQLException { + try { + return new ObjectMapper().readValue(resultSet.getString(i),RateLimit.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public RateLimit getNullableResult(CallableStatement callableStatement, int i) throws SQLException { + try { + return new ObjectMapper().readValue(callableStatement.getString(i),RateLimit.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestHandler.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestHandler.java new file mode 100644 index 00000000..b4313cf1 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestHandler.java @@ -0,0 +1,71 @@ +package com.jiuyv.agile.data.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jiuyv.agile.data.database.PageResult; +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import com.jiuyv.agile.data.service.impl.ApiMappingEngine; +import com.jiuyv.agile.data.web.R; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +public class RequestHandler { + + private static Logger logger = LoggerFactory.getLogger(RequestHandler.class); + + private RequestInterceptor requestInterceptor; + + private ApiMappingEngine apiMappingEngine; + + private ObjectMapper objectMapper; + + public void setRequestInterceptor(RequestInterceptor requestInterceptor) { + this.requestInterceptor = requestInterceptor; + } + + public void setApiMappingEngine(ApiMappingEngine apiMappingEngine) { + this.apiMappingEngine = apiMappingEngine; + } + + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @ResponseBody + public Object invoke(HttpServletRequest request, HttpServletResponse response, + @PathVariable(required = false) Map pathVariables, + @RequestParam(required = false) Map requestParams, + @RequestBody(required = false) Map requestBodys) throws Exception { + DataApiEntity api; + Map params = new HashMap<>(); + if (null != pathVariables && !pathVariables.isEmpty()) { + logger.info("pathVariables:{}", pathVariables.toString()); + params.putAll(pathVariables); + } + if (null != requestParams && !requestParams.isEmpty()) { + logger.info("requestParams:{}", requestParams.toString()); + params.putAll(requestParams); + } + if (null != requestBodys && !requestBodys.isEmpty()) { + logger.info("requestBodys:{}", requestBodys.toString()); + params.putAll(requestBodys); + } + api = MappingHandlerMapping.getMappingApiInfo(request); + // 序列化 + api = objectMapper.readValue(objectMapper.writeValueAsString(api), DataApiEntity.class); + // 执行前置拦截器 + requestInterceptor.preHandle(request, response, api, params); + PageResult> value = apiMappingEngine.execute(api, params); + // 执行后置拦截器 + requestInterceptor.postHandle(request, response, api, params, value); + return R.ok(value); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestInterceptor.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestInterceptor.java new file mode 100644 index 00000000..72c0c5d2 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/handler/RequestInterceptor.java @@ -0,0 +1,76 @@ +package com.jiuyv.agile.data.handler; + +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import com.jiuyv.agile.data.dto.enums.ParamType; +import com.jiuyv.agile.data.utils.IPUtil; +import com.jiuyv.agile.data.utils.MD5Util; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class RequestInterceptor { + + private static Logger logger = LoggerFactory.getLogger(RequestInterceptor.class); + + /** + * 请求之前执行 + * + * @return 当返回对象时,直接将此对象返回到页面,返回null时,继续执行后续操作 + * @throws Exception + */ + public void preHandle(HttpServletRequest request, HttpServletResponse response, DataApiEntity api, Map params) throws Exception { + logger.info("************ApiInterceptor preHandle executed**********"); + String uri = request.getRequestURI(); + logger.info("getRequestURI的值:" + uri); + String ipAddr = IPUtil.getIpAddr(request); + logger.info("ipAddr的值:" + ipAddr); + + // 密钥校验 + String apiKey = request.getHeader("api_key"); + String secretKey = request.getHeader("secret_key"); + if (StringUtils.isBlank(apiKey) || StringUtils.isBlank(secretKey)) { + throw new RuntimeException("api_key或secret_key空"); + } + MD5Util mt = MD5Util.getInstance(); + String apiId = mt.decode(apiKey); + String userId = mt.decode(secretKey); + + // 黑名单校验 + String deny = api.getDeny(); + if (StringUtils.isNotBlank(deny)) { + List denyList = Arrays.asList(deny.split(",")); + if (denyList != null && denyList.size() > 0) { + for (String ip : denyList) { + if(ip.equals(ipAddr)){ + throw new RuntimeException(ip + "已被加入IP黑名单"); + } + } + } + } + + // 参数校验 + if (null != params && !params.isEmpty()) { + api.getReqParams().stream().forEach(param -> { + if (params.containsKey(param.getParamName())) { + // 参数类型是否正确 + ParamType.parse(ParamType.getParamType(param.getParamType()), params.get(param.getParamName())); + } + }); + } + + } + + /** + * 执行完毕之后执行 + * + * @throws Exception + */ + public void postHandle(HttpServletRequest request, HttpServletResponse response, DataApiEntity api, Map params, Object value) throws Exception { + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/ApiMaskDao.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/ApiMaskDao.java new file mode 100644 index 00000000..1af7d942 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/ApiMaskDao.java @@ -0,0 +1,19 @@ +package com.jiuyv.agile.data.model.dao; + +import com.jiuyv.agile.data.dto.request.ApiMaskQuery; +import com.jiuyv.agile.data.model.entity.ApiMaskEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + *

+ * 数据API脱敏信息表 Mapper 接口 + *

+ */ + +@Mapper +public interface ApiMaskDao{ + + List selectList(ApiMaskQuery query); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/DataApiDao.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/DataApiDao.java new file mode 100644 index 00000000..493e9cd4 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/DataApiDao.java @@ -0,0 +1,17 @@ +package com.jiuyv.agile.data.model.dao; + +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 数据API信息表 Mapper 接口 + *

+ */ +@Mapper +public interface DataApiDao { + + DataApiEntity getById(Long id); + + int updateById(DataApiEntity dataApiEntity); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/MetadataSourceDao.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/MetadataSourceDao.java new file mode 100644 index 00000000..2e9b887f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/dao/MetadataSourceDao.java @@ -0,0 +1,15 @@ +package com.jiuyv.agile.data.model.dao; + +import com.jiuyv.agile.data.model.entity.MetadataSourceEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 数据源信息表 Mapper 接口 + *

+ */ +@Mapper +public interface MetadataSourceDao { + + MetadataSourceEntity getById(Long id); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/ApiMaskEntity.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/ApiMaskEntity.java new file mode 100644 index 00000000..83525798 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/ApiMaskEntity.java @@ -0,0 +1,189 @@ +package com.jiuyv.agile.data.model.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.jiuyv.agile.data.dto.request.FieldRule; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + *

+ * 数据API脱敏信息表 + *

+ */ +public class ApiMaskEntity implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主键 + */ + private Long id; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 状态(0不启用,1启用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 数据API + */ + private Long apiId; + + /** + * 脱敏名称 + */ + private String maskName; + + /** + * 脱敏字段规则配置 + */ + private List rules; + + /** + * 创建人所属部门 + */ + private String createDept; + + /** + * 版本号 + */ + private Long version; + + /** + * 验证token + */ + private String recToken; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Long getApiId() { + return apiId; + } + + public void setApiId(Long apiId) { + this.apiId = apiId; + } + + public String getMaskName() { + return maskName; + } + + public void setMaskName(String maskName) { + this.maskName = maskName; + } + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public String getCreateDept() { + return createDept; + } + + public void setCreateDept(String createDept) { + this.createDept = createDept; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getRecToken() { + return recToken; + } + + public void setRecToken(String recToken) { + this.recToken = recToken; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/DataApiEntity.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/DataApiEntity.java new file mode 100644 index 00000000..514ea6f2 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/DataApiEntity.java @@ -0,0 +1,283 @@ +package com.jiuyv.agile.data.model.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.jiuyv.agile.data.dto.request.ExecuteConfig; +import com.jiuyv.agile.data.dto.request.RateLimit; +import com.jiuyv.agile.data.dto.request.ReqParam; +import com.jiuyv.agile.data.dto.response.ResParam; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + *

+ * 数据API信息表 + *

+ */ +public class DataApiEntity implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主键 + */ + private Long id; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 状态(0不启用,1启用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * API名称 + */ + private String apiName; + + /** + * API版本 + */ + private String apiVersion; + + /** + * API路径 + */ + private String apiUrl; + + /** + * 请求类型 + */ + private String reqMethod; + + /** + * 返回格式 + */ + private String resType; + + /** + * IP黑名单多个,隔开 + */ + private String deny; + + /** + * 限流配置 + */ + private RateLimit rateLimit; + + /** + * 执行配置 + */ + private ExecuteConfig executeConfig; + + /** + * 请求参数 + */ + private List reqParams; + + /** + * 返回字段 + */ + private List resParams; + + /** + * 创建人所属部门 + */ + private String createDept; + + /** + * 版本号 + */ + private Long version; + + /** + * 验证token + */ + private String recToken; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getApiName() { + return apiName; + } + + public void setApiName(String apiName) { + this.apiName = apiName; + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + public String getApiUrl() { + return apiUrl; + } + + public void setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + } + + public String getReqMethod() { + return reqMethod; + } + + public void setReqMethod(String reqMethod) { + this.reqMethod = reqMethod; + } + + public String getResType() { + return resType; + } + + public void setResType(String resType) { + this.resType = resType; + } + + public String getDeny() { + return deny; + } + + public void setDeny(String deny) { + this.deny = deny; + } + + public RateLimit getRateLimit() { + return rateLimit; + } + + public void setRateLimit(RateLimit rateLimit) { + this.rateLimit = rateLimit; + } + + public ExecuteConfig getExecuteConfig() { + return executeConfig; + } + + public void setExecuteConfig(ExecuteConfig executeConfig) { + this.executeConfig = executeConfig; + } + + public List getReqParams() { + return reqParams; + } + + public void setReqParams(List reqParams) { + this.reqParams = reqParams; + } + + public List getResParams() { + return resParams; + } + + public void setResParams(List resParams) { + this.resParams = resParams; + } + + public String getCreateDept() { + return createDept; + } + + public void setCreateDept(String createDept) { + this.createDept = createDept; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getRecToken() { + return recToken; + } + + public void setRecToken(String recToken) { + this.recToken = recToken; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/MetadataSourceEntity.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/MetadataSourceEntity.java new file mode 100644 index 00000000..6fe1626e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/model/entity/MetadataSourceEntity.java @@ -0,0 +1,201 @@ +package com.jiuyv.agile.data.model.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.jiuyv.agile.data.dto.request.DbSchema; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 数据源信息表 + *

+ */ +public class MetadataSourceEntity implements Serializable { + + private static final long serialVersionUID=1L; + + /** + * 主键 + */ + private Long id; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 状态(0不启用,1启用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 数据源类型 + */ + private String dbType; + + /** + * 数据源名称 + */ + private String sourceName; + + /** + * 元数据同步(0否,1同步中, 2是) + */ + private String isSync; + + /** + * 数据源连接信息 + */ + private DbSchema dbSchema; + + /** + * 创建人所属部门 + */ + private String createDept; + + /** + * 版本号 + */ + private Long version; + + /** + * 验证token + */ + private String recToken; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public String getSourceName() { + return sourceName; + } + + public void setSourceName(String sourceName) { + this.sourceName = sourceName; + } + + public String getIsSync() { + return isSync; + } + + public void setIsSync(String isSync) { + this.isSync = isSync; + } + + public DbSchema getDbSchema() { + return dbSchema; + } + + public void setDbSchema(DbSchema dbSchema) { + this.dbSchema = dbSchema; + } + + public String getCreateDept() { + return createDept; + } + + public void setCreateDept(String createDept) { + this.createDept = createDept; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getRecToken() { + return recToken; + } + + public void setRecToken(String recToken) { + this.recToken = recToken; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/AgileApiService.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/AgileApiService.java new file mode 100644 index 00000000..a42ac237 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/AgileApiService.java @@ -0,0 +1,13 @@ +package com.jiuyv.agile.data.service; + +/** + * 敏捷API + * + * @author yulei + */ +public interface AgileApiService { + + void releaseDataApi(Long id); + + void cancelDataApi(Long id); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/ApiMaskService.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/ApiMaskService.java new file mode 100644 index 00000000..64f3c245 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/ApiMaskService.java @@ -0,0 +1,13 @@ +package com.jiuyv.agile.data.service; + +import com.jiuyv.agile.data.model.entity.ApiMaskEntity; + +/** + *

+ * 数据API脱敏信息表 服务类 + *

+ */ +public interface ApiMaskService{ + + ApiMaskEntity getApiMaskByApiId(Long apiId); +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/MetadataSourceService.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/MetadataSourceService.java new file mode 100644 index 00000000..6e23fbe1 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/MetadataSourceService.java @@ -0,0 +1,15 @@ +package com.jiuyv.agile.data.service; + +import com.jiuyv.agile.data.model.entity.MetadataSourceEntity; + + +/** + *

+ * 数据源信息表 服务类 + *

+ */ +public interface MetadataSourceService{ + + MetadataSourceEntity getMetadataSourceById(Long id); + +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/AgileApiServiceImpl.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/AgileApiServiceImpl.java new file mode 100644 index 00000000..9c40c854 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/AgileApiServiceImpl.java @@ -0,0 +1,44 @@ +package com.jiuyv.agile.data.service.impl; + +import com.jiuyv.agile.data.common.constants.DataConstant; +import com.jiuyv.agile.data.model.dao.DataApiDao; +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import com.jiuyv.agile.data.handler.MappingHandlerMapping; +import com.jiuyv.agile.data.service.AgileApiService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author yulei + */ +@Service +public class AgileApiServiceImpl implements AgileApiService { + + @Autowired + private DataApiDao dataApiDao; + + @Autowired + private MappingHandlerMapping mappingHandlerMapping; + + @Override + public void releaseDataApi(Long id) { + DataApiEntity dataApiEntity = dataApiDao.getById(id); + if (dataApiEntity == null){ + throw new RuntimeException("无法查询到服务"); + } + mappingHandlerMapping.registerMapping(dataApiEntity); + dataApiEntity.setStatus(DataConstant.ApiState.RELEASE.getKey()); + dataApiDao.updateById(dataApiEntity); + } + + @Override + public void cancelDataApi(Long id) { + DataApiEntity dataApiEntity = dataApiDao.getById(id); + if (dataApiEntity == null){ + throw new RuntimeException("无法查询到服务"); + } + mappingHandlerMapping.unregisterMapping(dataApiEntity); + dataApiEntity.setStatus(DataConstant.ApiState.CANCEL.getKey()); + dataApiDao.updateById(dataApiEntity); + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMappingEngine.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMappingEngine.java new file mode 100644 index 00000000..0423caed --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMappingEngine.java @@ -0,0 +1,101 @@ +package com.jiuyv.agile.data.service.impl; + +import com.jiuyv.agile.data.database.DataSourceFactory; +import com.jiuyv.agile.data.database.DbQuery; +import com.jiuyv.agile.data.database.DbQueryProperty; +import com.jiuyv.agile.data.database.PageResult; +import com.jiuyv.agile.data.dto.request.DbSchema; +import com.jiuyv.agile.data.dto.request.FieldRule; +import com.jiuyv.agile.data.model.entity.ApiMaskEntity; +import com.jiuyv.agile.data.model.entity.DataApiEntity; +import com.jiuyv.agile.data.model.entity.MetadataSourceEntity; +import com.jiuyv.agile.data.factory.AbstractFactory; +import com.jiuyv.agile.data.factory.FactoryProducer; +import com.jiuyv.agile.data.factory.crypto.Crypto; +import com.jiuyv.agile.data.service.ApiMaskService; +import com.jiuyv.agile.data.service.MetadataSourceService; +import com.jiuyv.agile.data.utils.PageUtil; +import com.jiuyv.agile.data.utils.SqlBuilderUtil; +import com.jiuyv.agile.data.utils.ThrowableUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +public class ApiMappingEngine { + + private static Logger logger = LoggerFactory.getLogger(ApiMappingEngine.class); + + @Autowired + private DataSourceFactory dataSourceFactory; + + @Autowired + private MetadataSourceService metadataSourceService; + + @Autowired + private ApiMaskService apiMaskService; + + public PageResult> execute(DataApiEntity dataApi, Map params) { + MetadataSourceEntity dataSource = Optional.ofNullable(metadataSourceService.getMetadataSourceById(dataApi.getExecuteConfig().getSourceId())).orElseThrow(() -> new RuntimeException("API调用查询数据源出错")); + DbSchema dbSchema = dataSource.getDbSchema(); + DbQueryProperty dbQueryProperty = new DbQueryProperty(dataSource.getDbType(), dbSchema.getHost(), + dbSchema.getUsername(), dbSchema.getPassword(), dbSchema.getPort(), dbSchema.getDbName(), dbSchema.getSid()); + DbQuery dbQuery = Optional.ofNullable(dataSourceFactory.createDbQuery(dbQueryProperty)).orElseThrow(() -> new RuntimeException("创建数据查询接口出错")); + // 参数 + Integer pageNum = Integer.parseInt(String.valueOf(params.getOrDefault("pageNum", 1))); + Integer pageSize = Integer.parseInt(String.valueOf(params.getOrDefault("pageSize", 20))); + PageUtil pageUtil = new PageUtil(pageNum, pageSize); + Integer offset = pageUtil.getOffset(); + SqlBuilderUtil.SqlFilterResult sqlFilterResult; + try { + sqlFilterResult = SqlBuilderUtil.getInstance().applyFilters(dataApi.getExecuteConfig().getSqlText(), params); + } catch (Exception e) { + logger.error("全局异常信息ex={}, StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e)); + throw new RuntimeException("API调用动态构造SQL语句出错"); + } + Map acceptedFilters = sqlFilterResult.getAcceptedFilters(); + // 数据脱敏 + List rules = null; + ApiMaskEntity apiMaskEntity = apiMaskService.getApiMaskByApiId(dataApi.getId()); + if (apiMaskEntity != null) { + rules = apiMaskEntity.getRules(); + } + PageResult> pageResult; + try { + pageResult = dbQuery.queryByPage(sqlFilterResult.getSql(), acceptedFilters, offset, pageSize); + } catch (Exception e) { + logger.error("全局异常信息ex={}, StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e)); + throw new RuntimeException("API调用查询结果集出错"); + } + try { + if (rules != null && rules.size() > 0){ + // 并行流处理脱敏 + List finalRules = rules; + pageResult.getData().parallelStream().forEach(m -> { + finalRules.stream().forEach(r -> { + if (m.containsKey(r.getFieldName())) { + Object obj = m.get(r.getFieldName()); + if (null != obj){ + AbstractFactory factory = FactoryProducer.getFactory(r.getCipherType()); + Crypto crypto = factory.getCrypto(r.getCryptType()); + String encrypt = crypto.encrypt(String.valueOf(obj)); + m.put(r.getFieldName(), encrypt); + } + } + }); + }); + } + } catch (Exception e) { + logger.error("全局异常信息ex={}, StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e)); + throw new RuntimeException("API调用数据脱敏出错"); + } + pageResult.setPageNum(pageNum); + pageResult.setPageSize(pageSize); + return pageResult; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMaskServiceImpl.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMaskServiceImpl.java new file mode 100644 index 00000000..dc7ff9a9 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/ApiMaskServiceImpl.java @@ -0,0 +1,33 @@ +package com.jiuyv.agile.data.service.impl; + +import com.jiuyv.agile.data.model.dao.ApiMaskDao; +import com.jiuyv.agile.data.dto.request.ApiMaskQuery; +import com.jiuyv.agile.data.model.entity.ApiMaskEntity; +import com.jiuyv.agile.data.service.ApiMaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + *

+ * 数据API脱敏信息表 服务实现类 + *

+ */ +@Service +public class ApiMaskServiceImpl implements ApiMaskService { + + @Autowired + private ApiMaskDao apiMaskDao; + + @Override + public ApiMaskEntity getApiMaskByApiId(Long apiId) { + ApiMaskQuery query = new ApiMaskQuery(); + query.setApiId(apiId); + List list = apiMaskDao.selectList(query); + if (list != null && list.size() > 0){ + return list.get(0); + } + return null; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/MetadataSourceServiceImpl.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/MetadataSourceServiceImpl.java new file mode 100644 index 00000000..1b8169fe --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/service/impl/MetadataSourceServiceImpl.java @@ -0,0 +1,25 @@ +package com.jiuyv.agile.data.service.impl; + +import com.jiuyv.agile.data.model.dao.MetadataSourceDao; +import com.jiuyv.agile.data.model.entity.MetadataSourceEntity; +import com.jiuyv.agile.data.service.MetadataSourceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * 数据源信息表 服务实现类 + *

+ */ +@Service +public class MetadataSourceServiceImpl implements MetadataSourceService { + + @Autowired + private MetadataSourceDao metadataSourceDao; + + @Override + public MetadataSourceEntity getMetadataSourceById(Long id) { + MetadataSourceEntity metadataSourceEntity = metadataSourceDao.getById(id); + return metadataSourceEntity; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/task/AgileApiTask.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/task/AgileApiTask.java new file mode 100644 index 00000000..9ec181f0 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/task/AgileApiTask.java @@ -0,0 +1,25 @@ +package com.jiuyv.agile.data.task; + +/** + * 敏捷API定时任务 + * + * @author yulei + */ +public class AgileApiTask { + + /** + * 发布接口 + * @return + */ +// @Scheduled(cron = "0/5 * * * * ? ") + public void releaseDataApi(){ + } + + /** + * 注销接口 + * @return + */ +// @Scheduled(cron = "0/5 * * * * ? ") + public void cancelDataApi(){ + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/IPUtil.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/IPUtil.java new file mode 100644 index 00000000..c4c8cf08 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/IPUtil.java @@ -0,0 +1,63 @@ +package com.jiuyv.agile.data.utils; + +import javax.servlet.http.HttpServletRequest; +import java.net.*; +import java.util.Enumeration; + +public class IPUtil { + + public static String getLocalIP() throws SocketException { + String localIP = null; + Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress ip = null; + while (allNetInterfaces.hasMoreElements()) { + NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement(); + Enumeration addresses = netInterface.getInetAddresses(); + while (addresses.hasMoreElements()) { + ip = (InetAddress) addresses.nextElement(); + if (ip != null && ip instanceof Inet4Address) { + localIP = ip.getHostAddress(); + if (!"127.0.0.1".equalsIgnoreCase(localIP)) { + return localIP; + } + } + } + } + return localIP; + } + + /** + * 获取当前网络ip + * @param request + * @return + */ + public static String getIpAddr(HttpServletRequest request) { + String ipAddress = request.getHeader("x-forwarded-for"); + if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = request.getHeader("Proxy-Client-IP"); + } + if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = request.getHeader("WL-Proxy-Client-IP"); + } + if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = request.getRemoteAddr(); + if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) { + //根据网卡取本机配置的IP + InetAddress inet = null; + try { + inet = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + ipAddress = inet != null ? inet.getHostAddress() : null; + } + } + //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 + if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15 + if (ipAddress.contains(",")) { + ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); + } + } + return ipAddress; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/MD5Util.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/MD5Util.java new file mode 100644 index 00000000..daafbe0f --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/MD5Util.java @@ -0,0 +1,123 @@ +package com.jiuyv.agile.data.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.security.Key; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Base64; + +public class MD5Util { + + /** 向量(同时拥有向量和密匙才能解密),此向量必须是8byte,多少都报错 */ + private final byte[] DESIV = new byte[] { 0x22, 0x54, 0x36, 110, 0x40, (byte) 0xac, (byte) 0xad, (byte) 0xdf }; + /** 自定义密钥,个数不能太短,太短报错,过长,它默认只取前N位(N的具体值,大家另行查找资料) */ + private final String deSkey = "datax-cloud"; + /** 加密算法的参数接口 */ + private AlgorithmParameterSpec iv = null; + private Key key = null; + private String charset = "UTF-8"; + + private static volatile MD5Util instance; + + /** + * 构造函数 + * @throws Exception + */ + private MD5Util() throws Exception { + // 设置密钥参数 + DESKeySpec keySpec = new DESKeySpec(deSkey.getBytes(this.charset)); + // 设置向量 + iv = new IvParameterSpec(DESIV); + // 获得密钥工厂 + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + // 得到密钥对象 + key = keyFactory.generateSecret(keySpec); + } + + public static MD5Util getInstance() throws Exception { + if(instance == null) { + synchronized (MD5Util.class) { + if(instance == null) { + instance = new MD5Util(); + } + } + } + return instance; + } + +// public static void main(String[] args) { +// try { +// String value = "1246656415670484994"; +// MD5Util mt = new MD5Util(); +// System.out.println("加密前的字符:" + value); +// System.out.println("加密后的字符:" + mt.encode(value)); +// System.out.println("解密后的字符:" + mt.decode(mt.encode(value))); +// System.out.println("字符串的MD5值:"+ getMD5Value(value)); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } + + /** + * 加密 + * @param data + * @return + * @throws Exception + */ + public String encode(String data) throws Exception { + // 得到加密对象Cipher + Cipher enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + // 设置工作模式为加密模式,给出密钥和向量 + enCipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] pasByte = enCipher.doFinal(data.getBytes(this.charset)); + return Base64.getEncoder().encodeToString(pasByte); + } + + /** + * 解密 + * @param data + * @return + * @throws Exception + */ + public String decode(String data) throws Exception { + Cipher deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + deCipher.init(Cipher.DECRYPT_MODE, key, iv); + //此处注意doFinal()的参数的位数必须是8的倍数,否则会报错(通过encode加密的字符串读出来都是8的倍数位,但写入文件再读出来,就可能因为读取的方式的问题,导致最后此处的doFinal()的参数的位数不是8的倍数) + //此处必须用base64Decoder,若用data。getBytes()则获取的字符串的byte数组的个数极可能不是8的倍数,而且不与上面的BASE64Encoder对应(即使解密不报错也不会得到正确结果) + byte[] pasByte = deCipher.doFinal(Base64.getDecoder().decode(data)); + return new String(pasByte, this.charset); + } + + /** + * 获取MD5的值,可用于对比校验 + * @param sourceStr + * @return + */ + private static String getMD5Value(String sourceStr) { + String result = ""; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(sourceStr.getBytes()); + byte b[] = md.digest(); + int i; + StringBuffer buf = new StringBuffer(""); + for (int offset = 0; offset < b.length; offset++) { + i = b[offset]; + if (i < 0) { + i += 256; + } + if (i < 16) { + buf.append("0"); + } + buf.append(Integer.toHexString(i)); + } + result = buf.toString(); + } catch (NoSuchAlgorithmException e) { + } + return result; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/NamedParameterUtil.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/NamedParameterUtil.java new file mode 100644 index 00000000..de61a4f1 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/NamedParameterUtil.java @@ -0,0 +1,123 @@ +package com.jiuyv.agile.data.utils; + +import org.springframework.util.Assert; + +import java.util.*; + +/** + * 带参数sql处理工具类 + */ +public class NamedParameterUtil { + + private NamedParameterUtil() {} + + /** + * 定义特殊字符(增加最后的自定义的'}') + */ + private static final char[] PARAMETER_SEPARATORS = + new char[] {'"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^', '}'}; + + /** + * 对带参数sql的统计式封装,便于后续肢解拼装 + * @param originalSql + * @return + */ + public static ParsedSql parseSqlStatement(String originalSql) { + Assert.notNull(originalSql, "SQL must not be null"); + ParsedSql parsedSql = new ParsedSql(originalSql); + Set namedParameters = new HashSet(); + char[] sqlchars = originalSql.toCharArray(); + int namedParamCount = 0; + int unNamedParamCount = 0; + int totalParamCount = 0; + int i = 0; + while (i < sqlchars.length) { + char statement = sqlchars[i]; + if (statement == ':') { + int j = i + 1; + while (j < sqlchars.length && !isSeparatorsChar(sqlchars[j])) { + j++; + } + if (j - i > 1) { + String paramName = originalSql.substring(i + 1, j); + if (!namedParameters.contains(paramName)) { + namedParameters.add(paramName); + namedParamCount++; + } + parsedSql.addParamNames(paramName, i, j); + totalParamCount++; + } + i = j - 1; + } else if (statement == '?') { + unNamedParamCount++; + totalParamCount++; + } + i++; + } + parsedSql.setNamedParamCount(namedParamCount); + parsedSql.setUnnamedParamCount(unNamedParamCount); + parsedSql.setTotalParamCount(totalParamCount); + return parsedSql; + } + + /** + * 获得不带参数的sql,即替换参数为? + * @param parsedSql + * @param params + * @return + */ + public static String substituteNamedParams(ParsedSql parsedSql, Map params){ + String original = parsedSql.getOriginalSql(); + StringBuffer actual = new StringBuffer(""); + int lastIndex = 0; + List paramNames = parsedSql.getParamNames(); + for (int i = 0; i < paramNames.size(); i++) { + int[] indexs = parsedSql.getParamIndexs(i); + int startIndex = indexs[0]; + int endIndex = indexs[1]; + String paramName = paramNames.get(i); + actual.append(original.substring(lastIndex, startIndex)); + if (params != null && params.containsKey(paramName)) { + actual.append("?"); + } else{ + actual.append("?"); + } + lastIndex = endIndex; + } + actual.append(original.subSequence(lastIndex, original.length())); + return actual.toString(); + } + + /** + * 获得sql所需参数K,V + * @param parsedSql + * @param params + * @return + */ + public static LinkedHashMap buildValueArray(ParsedSql parsedSql, Map params){ + List paramNames = parsedSql.getParamNames(); + LinkedHashMap acceptedFilters = new LinkedHashMap<>(parsedSql.getTotalParamCount()); + if (parsedSql.getNamedParamCount() > 0 && parsedSql.getUnnamedParamCount() > 0) { + throw new RuntimeException("parameter方式与?方式不能混合!"); + } + for (int i = 0; i < paramNames.size(); i++) { + String keyName = paramNames.get(i); + if (params.containsKey(keyName)) { + acceptedFilters.put(keyName, params.get(keyName)); + } + } + return acceptedFilters; + } + + private static boolean isSeparatorsChar(char statement){ + if (Character.isWhitespace(statement)) { + return true; + } + for (int i = 0; i < PARAMETER_SEPARATORS.length; i++) { + if (statement == PARAMETER_SEPARATORS[i]) { + return true; + } + } + return false; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/PageUtil.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/PageUtil.java new file mode 100644 index 00000000..d0918d24 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/PageUtil.java @@ -0,0 +1,53 @@ +package com.jiuyv.agile.data.utils; + +import java.io.Serializable; + +public class PageUtil implements Serializable { + + private static final long serialVersionUID = 1L; + + private static Integer DEFAULT_MAX_COUNT = 5000; + + // 当前页码 + private Integer pageNum = 1; + // 分页条数 + private Integer pageSize = 20; + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + if (this.pageSize > 0) { + this.pageSize = this.pageSize > DEFAULT_MAX_COUNT ? DEFAULT_MAX_COUNT : this.pageSize; + } else { + this.pageSize = 20; + } + } + + public PageUtil(Integer pageNum, Integer pageSize) { + this.pageNum = pageNum; + this.pageSize = pageSize; + if (this.pageSize > 0) { + this.pageSize = this.pageSize > DEFAULT_MAX_COUNT ? DEFAULT_MAX_COUNT : this.pageSize; + } else { + this.pageSize = 20; + } + } + + public Integer getOffset() { + pageSize = pageSize == null ? 20 : pageSize; + pageNum = pageNum == null ? 1 : pageNum; + int offset = pageNum > 0 ? (pageNum - 1) * pageSize : 0; + return offset; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ParsedSql.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ParsedSql.java new file mode 100644 index 00000000..3bc857c0 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ParsedSql.java @@ -0,0 +1,82 @@ +package com.jiuyv.agile.data.utils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 此类封装NamedParameterSql + */ +public class ParsedSql implements Serializable { + + private static final long serialVersionUID=1L; + + private String originalSql; + //参数名 + private List paramNames = new ArrayList<>(); + //参数在sql中对应的位置 + private List paramIndexs = new ArrayList<>(); + //统计参数个数(不包含重复) + private int namedParamCount; + //统计sql中?的个数 + private int unnamedParamCount; + + private int totalParamCount; + + public ParsedSql(String originalSql){ + this.originalSql = originalSql; + } + + public List getParamNames() { + return paramNames; + } + + public void addParamNames(String paramName,int startIndex,int endIndex) { + paramNames.add(paramName); + paramIndexs.add(new int[]{startIndex,endIndex}); + } + + public int[] getParamIndexs(int position) { + return paramIndexs.get(position); + } + + public String getOriginalSql() { + return originalSql; + } + + public int getNamedParamCount() { + return namedParamCount; + } + + public void setNamedParamCount(int namedParamCount) { + this.namedParamCount = namedParamCount; + } + + public int getUnnamedParamCount() { + return unnamedParamCount; + } + + public void setUnnamedParamCount(int unnamedParamCount) { + this.unnamedParamCount = unnamedParamCount; + } + + public int getTotalParamCount() { + return totalParamCount; + } + + public void setTotalParamCount(int totalParamCount) { + this.totalParamCount = totalParamCount; + } + + @Override + public String toString() { + return "ParsedSql{" + + "originalSql='" + originalSql + '\'' + + ", paramNames=" + paramNames + + ", paramIndexs=" + paramIndexs + + ", namedParamCount=" + namedParamCount + + ", unnamedParamCount=" + unnamedParamCount + + ", totalParamCount=" + totalParamCount + + '}'; + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/SqlBuilderUtil.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/SqlBuilderUtil.java new file mode 100644 index 00000000..1aa33ba5 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/SqlBuilderUtil.java @@ -0,0 +1,295 @@ +package com.jiuyv.agile.data.utils; + +import com.jiuyv.agile.data.dto.request.ReqParam; +import com.jiuyv.agile.data.dto.enums.WhereType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 用于动态构造sql语句 + * ${ segment... } 为一个条件代码块 + * + * String sql = "select * from user where 1=1 + * ${ and username = :username } + * ${ and password = :password } + * ${ and age = :age }" + * + * Map filters = new HashMap(); + * filters.put("username", "yuwei"); + * filters.put("age", "12"); + * filters.put("id", "123"); + * + * SqlFilterResult result = SqlBuilderUtil.applyFilters(sql, filters); + * + * result.getSql()结果 + * select * from user where 1=1 and username=:username and age=:age + * + * result.getAcceptedFilters()结果 + * {username=yuwei} + * {age=12} + */ +public class SqlBuilderUtil { + + private static Logger logger = LoggerFactory.getLogger(SqlBuilderUtil.class); + + private SqlBuilderUtil() {} + + private static volatile SqlBuilderUtil instance; + + public static SqlBuilderUtil getInstance() { + if(instance == null) { + synchronized (SqlBuilderUtil.class) { + if(instance == null) { + instance = new SqlBuilderUtil(); + } + } + } + return instance; + } + + /** + * 空格 + */ + private final String SPACE = " "; + /** + * 冒号占位符 + */ + private final String COLON = ":"; + /** + * 问号占位符 + */ + private final String MARK = "?"; + /** + * where关键字 + */ + private final String WHERE_SQL = "WHERE"; + /** + * AND连接符 + */ + private final String WHERE_AND = "AND"; + /** + * where 1=1条件 + */ + private final String WHERE_INIT = WHERE_SQL + " 1 = 1"; + /** + * 左括号 + */ + private final String LEFT_BRACKET = "("; + /** + * 右括号 + */ + private final String RIGHT_BRACKET = ")"; + /** + * 百分号% + */ + private final String PERCENT_SIGN = "%"; + /** + * 单引号 ' + */ + private final String SINGLE_QUOTE = "'"; + /** + * 条件代码块标记开始 + */ + public final String MARK_KEY_START = "${"; + /** + * 条件代码块标记结束 + */ + public final String MARK_KEY_END = "}"; + + /** + * 拼接命名参数sql + * @param sql + * @param params + * @return + */ + public String buildHql(String sql, List params){ + Assert.notNull(sql, "SQL must not be null"); + return buildHql(new StringBuffer(sql), params); + } + + private String buildHql(StringBuffer sql, List params){ + if(params != null && params.size() > 0){ + return sql.toString(); + } + sql.append(SPACE).append(WHERE_INIT); + for (int i = 0; i < params.size(); i++) { + ReqParam reqParam = params.get(i); + sql.append(SPACE).append(MARK_KEY_START).append(WHERE_AND).append(SPACE).append(reqParam.getParamName()); + if (WhereType.LIKE.getType() == reqParam.getWhereType()) { + // LIKE '%' :username '%' ,:username 两边一定要有空格,如果没有空格,是查询不到数据的 + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()) + .append(SPACE).append(SINGLE_QUOTE).append(PERCENT_SIGN).append(SINGLE_QUOTE).append(SPACE) + .append(COLON).append(reqParam.getParamName()) + .append(SPACE).append(SINGLE_QUOTE).append(PERCENT_SIGN).append(SINGLE_QUOTE).append(MARK_KEY_END); + } else if(WhereType.LIKE_LEFT.getType() == reqParam.getWhereType()) { + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()) + .append(SPACE).append(SINGLE_QUOTE).append(PERCENT_SIGN).append(SINGLE_QUOTE).append(SPACE) + .append(COLON).append(reqParam.getParamName()).append(MARK_KEY_END); + } else if(WhereType.LIKE_RIGHT.getType() == reqParam.getWhereType()) { + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()) + .append(SPACE).append(COLON).append(reqParam.getParamName()) + .append(SPACE).append(SINGLE_QUOTE).append(PERCENT_SIGN).append(SINGLE_QUOTE).append(MARK_KEY_END); + } else if(WhereType.NULL.getType() == reqParam.getWhereType() || WhereType.NOT_NULL.getType() == reqParam.getWhereType()){ + // is null或is not null不需要参数值 + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()).append(MARK_KEY_END); + } else if(WhereType.IN.getType() == reqParam.getWhereType()){ + // in (:ids) + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()) + .append(SPACE).append(LEFT_BRACKET) + .append(COLON).append(reqParam.getParamName()) + .append(RIGHT_BRACKET).append(MARK_KEY_END); + } else { + sql.append(SPACE).append(WhereType.getWhereType(reqParam.getWhereType()).getKey()) + .append(SPACE).append(COLON).append(reqParam.getParamName()).append(MARK_KEY_END); + } + } + return sql.toString(); + } + + /** + * 根据入参动态构造sql语句 + * @param sql + * @param filters + * @return + */ + public SqlFilterResult applyFilters(String sql, Map filters){ + Assert.notNull(sql, "SQL must not be null"); + return applyFilters(new StringBuffer(sql), filters); + } + + private SqlFilterResult applyFilters(StringBuffer sql, Map filters){ + LinkedHashMap acceptedFilters = new LinkedHashMap<>(); + for (int i = 0, end = 0, start = sql.indexOf(MARK_KEY_START); ((start = sql.indexOf(MARK_KEY_START, end)) >= 0); i++) { + end = sql.indexOf(MARK_KEY_END, start); + // 封装该条件代码块中的NamedParameterSql + ParsedSql parsedSql = getSegmentParsedSql(sql, start, end); + if (parsedSql.getParamNames() != null && parsedSql.getParamNames().size() > 0){ + throw new IllegalArgumentException("Not key found in segment=" + sql.substring(start, end + MARK_KEY_END.length())); + } + // 判断输入参数filters中是否存在查询参数 + if (isAcceptedKeys(filters, parsedSql.getParamNames())) { + // 动态构造可执行的sql语句,去掉条件代码块两边的${ }标记符 + if (logger.isDebugEnabled()) { + logger.debug("The filter namedParameters=" + parsedSql.getParamNames() + " is accepted on segment=" + sql.substring(start, end + MARK_KEY_END.length())); + } + // 下面方法2选1可以获取条件代码块 + // select id, name from user where 1 = 1 and id = :id +// String segment = sql.substring(start + MARK_KEY_START.length(), end); + String segment = parsedSql.getOriginalSql(); + // 转换命名参数:为? + // select id, name from user where 1 = 1 and id = ? +// String segment = NamedParameterUtil.substituteNamedParams(parsedSql, filters); + // 获取传参中包含命名参数的数据 + LinkedHashMap linkAcceptedFilters = NamedParameterUtil.buildValueArray(parsedSql, filters); + acceptedFilters.putAll(linkAcceptedFilters); + sql.replace(start, end + MARK_KEY_END.length(), segment); + end = start + segment.length(); + } else { + // 抛弃该条件代码块 + if (logger.isDebugEnabled()) { + logger.debug("The filter namedParameters=" + parsedSql.getParamNames() + " is removed from the query on segment=" + sql.substring(start, end + MARK_KEY_END.length())); + } + sql.replace(start, end + MARK_KEY_END.length(), ""); + end = start; + } + } + return new SqlFilterResult(sql.toString(), acceptedFilters); + } + + /** + * 验证入参,并过滤值为空的入参 + */ + private boolean isAcceptedKeys(Map filters, List keys) { + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + Object value = getProperty(filters, key); + if (!isValuePopulated(value, true)) { + return false; + } + } + return true; + } + + /** + * 封装该条件代码块中的NamedParameterSql + */ + private ParsedSql getSegmentParsedSql(StringBuffer sql, int start, int end) { + String segment = sql.substring(start + MARK_KEY_START.length(), end); + ParsedSql parsedSql = NamedParameterUtil.parseSqlStatement(segment); + return parsedSql; + } + + /** + * 获取参数值 + * @param filters + * @param key + * @return + */ + private Object getProperty(Map filters, String key) { + if (filters == null || filters.isEmpty()) + return null; + return filters.get(key); + } + + /** + * 验证参数值是否空 + * @param value + * @param isRemoveEmpty + * @return + */ + private boolean isValuePopulated(Object value, boolean isRemoveEmpty) { + if (value == null) { + return false; + } + if (isRemoveEmpty) { +// return ObjectUtil.isNotEmpty(value); + return value != null; + } else { + return true; + } + } + + public static class SqlFilterResult implements Serializable { + + private static final long serialVersionUID=1L; + + private String sql; + + private Map acceptedFilters; + + public SqlFilterResult(String sql, Map acceptedFilters) { + this.setSql(sql); + this.setAcceptedFilters(acceptedFilters); + } + + public String getSql() { + return sql; + } + + public void setSql(String sql) { + this.sql = sql; + } + + public Map getAcceptedFilters() { + return acceptedFilters; + } + + public void setAcceptedFilters(Map acceptedFilters) { + this.acceptedFilters = acceptedFilters; + } + + @Override + public String toString() { + return "SqlFilterResult{" + + "sql='" + sql + '\'' + + ", acceptedFilters=" + acceptedFilters + + '}'; + } + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ThrowableUtil.java b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ThrowableUtil.java new file mode 100644 index 00000000..60425650 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/java/com/jiuyv/agile/data/utils/ThrowableUtil.java @@ -0,0 +1,26 @@ +package com.jiuyv.agile.data.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 异常工具 + */ +public class ThrowableUtil { + + /** + * 获取堆栈信息 + * @param throwable + * @return + */ + public static String getStackTrace(Throwable throwable){ + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + try { + throwable.printStackTrace(pw); + return sw.toString(); + } finally { + pw.close(); + } + } +} diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/application.yml b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/application.yml new file mode 100644 index 00000000..eb000ebe --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 8822 +spring: + application: + name: agile-api + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://172.16.12.105:5432/keliubao + username: postgres + password: postgres \ No newline at end of file diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/ApiMaskMapper.xml b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/ApiMaskMapper.xml new file mode 100644 index 00000000..6c451ca6 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/ApiMaskMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + id, + status, + create_by, + create_time, + create_dept, + update_by, + update_time, + api_id, mask_name, remark, + version_num, rec_token + + + + + diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/DataApiMapper.xml b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/DataApiMapper.xml new file mode 100644 index 00000000..eef21d14 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/DataApiMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, + status, + create_by, + create_time, + create_dept, + update_by, + update_time, + api_name, api_version, api_url, remark, req_method, res_type, deny, + version_num, rec_token + + + + id, + status, + create_by, + create_time, + create_dept, + update_by, + update_time, + api_name, api_version, api_url, remark, req_method, res_type, deny, limit_json, config_json, req_json, res_json, + version_num, rec_token + + + + update tbl_data_market_api + + api_name = #{apiName}, + api_version = #{apiVersion}, + api_url = #{apiUrl}, + req_method = #{reqMethod}, + res_type = #{resType}, + deny = #{deny}, + limit_json = #{rateLimit}, + config_json = #{executeConfig}, + req_json = #{reqParams}, + res_json = #{resParams}, + create_dept = #{createDept}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + version_num = #{version}, + rec_token = #{recToken}, + update_time = now() + + where id = #{id} + + + + + diff --git a/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/MetadataSourceMapper.xml b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/MetadataSourceMapper.xml new file mode 100644 index 00000000..dd6f7558 --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/agile-data-service/src/main/resources/mapper/MetadataSourceMapper.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, + status, + create_by, + create_time, + create_dept, + update_by, + update_time, + remark, + db_type, source_name, is_sync, + version_num, rec_token + + + + ${alias}.id, + ${alias}.status, + ${alias}.create_by, + ${alias}.create_time, + ${alias}.create_dept, + ${alias}.update_by, + ${alias}.update_time, + ${alias}.remark, + ${alias}.db_type, ${alias}.source_name, ${alias}.is_sync, ${alias}.db_schema, + ${alias}.version_num, ${alias}.rec_token + + + + + diff --git a/agile-date-service/src/trunck/agile-dataservice/pom.xml b/agile-date-service/src/trunck/agile-dataservice/pom.xml new file mode 100644 index 00000000..108b908e --- /dev/null +++ b/agile-date-service/src/trunck/agile-dataservice/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + pom + + agile-data-api + agile-data-service + + + org.springframework.boot + spring-boot-starter-parent + 2.6.7 + + + com.jiuyv + agile-data + 0.0.1-SNAPSHOT + agile-data + agile-data + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + 3.1.1 + 3.1.0 + 3.1.0 + 3.3 + 6.2.3.Final + 3.1.0 + 1.3.0 + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${openfeign.version} + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis.version} + + + + + + + scm:svn:http://172.16.12.10/svn/sptcc_agile_etl/src/agile-date-service/src/trunck/agile-dataservice + scm:svn:http://172.16.12.10/svn/sptcc_agile_etl/src/agile-date-service/src/trunck/agile-dataservice + + + + + nexus-releases + Internal Releases + http://172.16.12.11:8082/repository/maven-releases/ + + + + nexus-snapshots + Internal Snapshots + http://172.16.12.11:8082/repository/maven-snapshots/ + + + + + + + jiuyv + jiuyv + http://172.16.12.11:8082/repository/maven-public/ + + true + always + + + + jboss + jboss + http://repository.jboss.org/maven2/ + + false + + + + geotools + geotools + http://maven.geotools.fr/repository/ + + false + + + + jahia + jahia + http://maven.jahia.org/maven2/ + + false + + + + vars + vars + http://vars.sourceforge.net/maven2/ + + false + + + + + + jiuyv + jiuyv Plugin Repository + http://172.16.12.11:8082/repository/maven-public/ + + + central + Maven Plugin Repository + http://repo1.maven.org/maven2/ + + + + diff --git a/agile-portal/agile-portal-gateway/pom.xml b/agile-portal/agile-portal-gateway/pom.xml new file mode 100644 index 00000000..2979c1b2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/pom.xml @@ -0,0 +1,324 @@ + + 4.0.0 + + com.jiuyv + agile-portal + 0.0.1 + + agile-portal-gateway + + + + + com.jiuyv.sptcc.portal + agile-portsl-api + 0.0.1 + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${openfeign.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + commons-io + commons-io + ${commons.io.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + commons-collections + commons-collections + ${commons.collections.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + com.github.penggle + kaptcha + ${kaptcha.version} + + + javax.servlet-api + javax.servlet + + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + net.logstash.logback + logstash-logback-encoder + 6.4 + + + + + org.springframework + spring-context-support + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + org.springframework.boot + spring-boot-starter-security + + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + + org.yaml + snakeyaml + + + + + + javax.xml.bind + jaxb-api + + + + + org.apache.commons + commons-pool2 + + + + + + javax.servlet + javax.servlet-api + + + + + com.alibaba + easyexcel + 3.1.1 + + + + + org.aspectj + aspectjweaver + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + + + + org.springframework.boot + spring-boot-starter-cache + + + + com.github.ben-manes.caffeine + caffeine + + + + org.apache.axis + axis + 1.4 + + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + com.anji-plus + spring-boot-starter-captcha + ${ajcaptcha.version} + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + + + + + repackage + + + + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayApplication.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayApplication.java new file mode 100644 index 00000000..ad930b89 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayApplication.java @@ -0,0 +1,35 @@ +package com.jiuyv.sptccc.agile; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * 启动程序 + * + * @author admin + */ +@EnableFeignClients +@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) +public class PortalGatewayApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(PortalGatewayApplication.class); + + public static void main(String[] args) { + SpringApplication.run(PortalGatewayApplication.class, args); + LOGGER.info("(♥◠‿◠)ノ゙ 模块启动成功゙ \n"+ + " ___ ___ ___ \n"+ + " |\\ \\ |\\ \\ / /| \n"+ + " \\ \\ \\ \\ \\ \\/ / / \n"+ + " __ \\ \\ \\ \\ \\ / / \n"+ + " |\\ \\\\_\\ \\ \\/ / / \n"+ + " \\ \\________\\ __/ / / \n"+ + " \\|________| |\\___/ / \n"+ + " \\|___|/ \n" + ); + } +} + diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayServletInitializer.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayServletInitializer.java new file mode 100644 index 00000000..a2bb73ff --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/PortalGatewayServletInitializer.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author admin + */ +public class PortalGatewayServletInitializer extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(PortalGatewayApplication.class); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Anonymous.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Anonymous.java new file mode 100644 index 00000000..a64ae2a8 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author admin + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java new file mode 100644 index 00000000..4410a2da --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * 方法不需要输出日志时使用 + * @author zhouliang + * + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogIgnore { +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java new file mode 100644 index 00000000..6538d3b8 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 返回内容太多时,不需要关注具体内容 + * 只需要知道返回情况时使用 + * @author zhouliang + * + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogSimpleResult { +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/RepeatSubmit.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/RepeatSubmit.java new file mode 100644 index 00000000..5a65f066 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author admin + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + int interval() default 5000; + + /** + * 提示消息 + */ + String message() default "不允许重复提交,请稍候再试"; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java new file mode 100644 index 00000000..26e295f3 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java @@ -0,0 +1,22 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * 日志对敏感字段处理为*(不是过滤,方便确认有没有值) + * 一些固定字段强制处理了,就不需要注解,如password + * @author zhouliang + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SensitiveData { + char defaultMark() default '*';//默认使用* + int firstLength() default 3;//保留前三位 + int endLength() default 3;//保留后三位 +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java new file mode 100644 index 00000000..14ae5049 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java @@ -0,0 +1,15 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 注解:用于标记需要截取内容的属性 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface TruncatedContent { + int length() default 3000; +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java new file mode 100644 index 00000000..b1a7ed25 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java @@ -0,0 +1,87 @@ +package com.jiuyv.sptccc.agile.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author admin + */ +@Component +@ConfigurationProperties(prefix = "conosle") +public class ConsoleConfig { + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getCopyrightYear() { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) { + this.copyrightYear = copyrightYear; + } + + public boolean isDemoEnabled() { + return demoEnabled; + } + + public void setDemoEnabled(boolean demoEnabled) { + this.demoEnabled = demoEnabled; + } + + public static boolean isAddressEnabled() { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) { + setAddressEnabled2(addressEnabled); + } + + public static void setAddressEnabled2(boolean addressEnabled) { + ConsoleConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + setCaptchaType2(captchaType); + } + + public static void setCaptchaType2(String captchaType) { + ConsoleConfig.captchaType = captchaType; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleOprTokenProperties.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleOprTokenProperties.java new file mode 100644 index 00000000..60e569af --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleOprTokenProperties.java @@ -0,0 +1,166 @@ +package com.jiuyv.sptccc.agile.common.config; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.Charsets; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; +import com.jiuyv.sptccc.agile.framework.security.model.OperationTokenSet; +import com.jiuyv.sptccc.agile.framework.security.model.TokenNode; + +/** + * 读取项目自定义相关配置,方便多处使用 + * @author Administrator + * + */ +@Component +@ConfigurationProperties(prefix = "operationtoken") +public class ConsoleOprTokenProperties { + + /** 操作令牌标识(放具体值) */ + private String headerVal="tokenSet"; + + /** 操作令牌标识 */ + private String header="operationToken"; + + /** 操作令牌标识 */ + private String headerKey="operationTokenKey"; + + /** 操作令牌次数 */ + private Integer tokenTimes=100; + + /** 操作令牌跳过的url */ + private List skipUrls; + + + @PostConstruct + public void init() { + Assert.notNull(tokenTimes, "openrationtToken times null"); + } + + + /** + * @return the headerVal + */ + public String getHeaderVal() { + return headerVal; + } + + /** + * @param headerVal the headerVal to set + */ + public void setHeaderVal(String headerVal) { + this.headerVal = headerVal; + } + + /** + * @return the header + */ + public String getHeader() { + return header; + } + + /** + * @param header the header to set + */ + public void setHeader(String header) { + this.header = header; + } + + /** + * @return the headerKey + */ + public String getHeaderKey() { + return headerKey; + } + + /** + * @param headerKey the headerKey to set + */ + public void setHeaderKey(String headerKey) { + this.headerKey = headerKey; + } + + /** + * @return the tokenTimes + */ + public Integer getTokenTimes() { + return tokenTimes; + } + + /** + * @param tokenTimes the tokenTimes to set + */ + public void setTokenTimes(Integer tokenTimes) { + this.tokenTimes = tokenTimes; + } + + + /** + * @return the skipUrls + */ + public List getSkipUrls() { + return skipUrls; + } + + /** + * @param skipUrls the skipUrls to set + */ + public void setSkipUrls(List skipUrls) { + this.skipUrls = skipUrls; + } + + + //加密token + public String encryptToken(String token, String sm4Key) throws Exception { + return Base64.encodeBase64String(Sm4Util.encrypt_ECB_Padding(sm4Key.getBytes(Charsets.UTF_8), token.getBytes(Charsets.UTF_8))); + } + + //解密token + public String decryptToken(String token, String sm4Key) throws Exception { + return Base64.encodeBase64String(Sm4Util.decrypt_ECB_Padding(Hex.decodeHex(sm4Key), Base64.decodeBase64(token))); + } + + public ImmutablePair calculateNewToken(HttpServletRequest request) throws Exception { + // + String sm4key = IdUtils.fastSimpleUUID().substring(0, 16); + String hexSm4Key = Hex.encodeHexString(sm4key.getBytes(Charsets.UTF_8)); + //token + String newToken = IdUtils.fastSimpleUUID().substring(0, 16); + String encryptToken = this.encryptToken(newToken, sm4key); + TokenNode newNode = new TokenNode(encryptToken, newToken, hexSm4Key, sm4key, tokenTimes); + + String tokenSetStr = String.valueOf(request.getSession().getAttribute(this.headerVal)); + OperationTokenSet set = JsonUtil.json2Bean(tokenSetStr, OperationTokenSet.class); + if (null == set) { + set = new OperationTokenSet(newNode, sm4key); + request.getSession().setAttribute(this.headerVal, JsonUtil.toJSONString(set)); + } else { + set.curNode.times = set.curNode.times - 1; + //当前node节点 放入 旧node map + Map oldNodeMap = set.getTokenNodeMap(); + if (null == oldNodeMap) { + oldNodeMap = new ConcurrentHashMap<>(); + } + oldNodeMap.put(set.curNode.encryptToken, set.curNode); + set.setTokenNodeMap(oldNodeMap); + //新node 替换 + set.curNode = newNode; + request.getSession().setAttribute(this.headerVal, JsonUtil.toJSONString(set)); + } + return ImmutablePair.of(newNode, set.curNode.hexSM4Key); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleTokenProperties.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleTokenProperties.java new file mode 100644 index 00000000..25c6bfe1 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleTokenProperties.java @@ -0,0 +1,69 @@ +package com.jiuyv.sptccc.agile.common.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目自定义相关配置,方便多处使用 + * @author Administrator + * + */ +@Component +@ConfigurationProperties(prefix = "token") +public class ConsoleTokenProperties { + + /** 令牌自定义标识 */ + @Value("${token.header}") + private String header; + + /** 令牌秘钥 */ + @Value("${token.secret}") + private String secret; + + /** 令牌有效期(默认30分钟) */ + @Value("${token.expireTime:30}") + private int expireTime; + + /** + * @return the header + */ + public String getHeader() { + return header; + } + + /** + * @param header the header to set + */ + public void setHeader(String header) { + this.header = header; + } + + /** + * @return the secret + */ + public String getSecret() { + return secret; + } + + /** + * @param secret the secret to set + */ + public void setSecret(String secret) { + this.secret = secret; + } + + /** + * @return the expireTime + */ + public int getExpireTime() { + return expireTime; + } + + /** + * @param expireTime the expireTime to set + */ + public void setExpireTime(int expireTime) { + this.expireTime = expireTime; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java new file mode 100644 index 00000000..1f4da315 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java @@ -0,0 +1,15 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 缓存的key 常量 + * + * @author admin + */ +public class CacheConstants +{ + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "";//置为空了,独立缓存不用key,减少空间 + public static final String CAPTCHA_CODE_SIGN_KEY = "SIGN";//秘钥的key +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java new file mode 100644 index 00000000..52f750d7 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java @@ -0,0 +1,43 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 缓存的实例名称 + * 时间和组没法动态控制,只能多个实例分开 + */ +public class CacheNameConstants +{ + private CacheNameConstants() { + throw new IllegalStateException("Utility class"); + } + + /** + * 字典缓存名 + */ + public static final String CACHE_SYS_DICT = "cache_sys_dict"; + + + /** + * 验证码缓存名,登录随机密钥也放里面 + */ + public static final String CACHE_CAPTCHA_CODE = "cache_captcha_code"; + + /** + * 防重提交缓存名5s + */ + public static final String CACHE_REPEAT_SUBMIT_5S = "cache_repeat_submit_5s"; + + /** + * 防重提交缓存名30s + */ + public static final String CACHE_REPEAT_SUBMIT_30S = "cache_repeat_submit_30s"; + + /** + * 数据加密用随机密钥(用户级别) + */ + public static final String CACHE_USER_DATA_SECRET_KEY = "cache_user_data_secret_key"; + + /** + * 1天缓存 + */ + public static final String CACHE_ONEDAY = "cache_oneday"; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java new file mode 100644 index 00000000..9c66818b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java @@ -0,0 +1,109 @@ +package com.jiuyv.sptccc.agile.common.constant; + +import java.util.regex.Pattern; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author admin + */ +public class Constants { + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** 默认的SESSION名称*/ + public static final String SESSION_NAME = "JSESSIONID"; + + /** 变量名:IP*/ + public static final String IP_ADDR_KEY = "ip_addr"; + + + + /** 后缀获取*/ + public static Pattern SUFFIX_RULE=Pattern.compile("\\.([A-Z0-9]+$)",Pattern.CASE_INSENSITIVE); + + //图片的前缀固定 + public final static String IMAGE_URL_PATH="images"; + //文件的前缀固定 + public final static String FILE_URL_PATH="files"; + //文件分类路径写死,不允许随意传。要细分自己加 + public final static String FILE_TYPE_PATHS= "portal,console"; + + + /** FEIGN_CLIENT标志 */ + public final static String FEIGN_CLIENT_FLAG="gateway-source"; + /** FEIGN_CLIENT标志 */ + public final static String FEIGN_CLIENT_VAL="cabcc6d21b22a06d1"; + /** 变量名:用户信息 */ + public static final String USERINFO_KEY = "userInfo"; + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java new file mode 100644 index 00000000..27707d42 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java @@ -0,0 +1,89 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 返回状态码 + * + * @author admin + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java new file mode 100644 index 00000000..c0788d7b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java @@ -0,0 +1,219 @@ +package com.jiuyv.sptccc.agile.common.core.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.core.page.PageDomain; +import com.jiuyv.sptccc.agile.common.core.page.TableDataInfo; +import com.jiuyv.sptccc.agile.common.core.page.TableSupport; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.PageUtils; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.sql.SqlUtil; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; + +/** + * web层通用数据处理 + * + * @author admin + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal((int)new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } + + /** + * 更新时用的,检查params,没有则初始化 + * 放一下旧的recToken和versionNum + * @param params + * @return + */ + public Map checkParams(Map params,String recToken,Long versionNum) + { + if(params==null) { + params=new HashMap<>(); + } + if(StringUtils.isNotBlank(recToken)) { + params.put("recToken", recToken); + } + if(versionNum!=null) { + params.put("versionNum", versionNum); + } + return params; + } + + /** + * 生成一个10位随机码recToken + * @return + */ + public String getNewRecToken() + { + return IdUtils.fastSimpleUUID().substring(0,10); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java new file mode 100644 index 00000000..b49272c2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java @@ -0,0 +1,176 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.util.HashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author admin + */ +public class AjaxResult extends HashMap +{ + protected static Logger logger = LoggerFactory.getLogger(AjaxResult.class); + + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) + { + logger.error("服务报错:"+msg); + if(msg.contains("Exception")){ + return new AjaxResult(HttpStatus.ERROR, "系统内部错误", data); + } + + if(msg.contains("script")){ + return new AjaxResult(HttpStatus.ERROR, "系统参数错误", data); + } + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java new file mode 100644 index 00000000..f1e509ef --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java @@ -0,0 +1,122 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * Entity基类 + * + * @author admin + */ +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** 创建者ID */ + private Long createById; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 创建者名称 */ + private String createBy; + + /** 更新者ID */ + private Long updateById; + + /** 更新者名称 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 搜索值 */ + private String searchValue; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + private Map params; + + public Long getCreateById() { + return createById; + } + + public void setCreateById(Long createById) { + this.createById = createById; + } + + public Long getUpdateById() { + return updateById; + } + + public void setUpdateById(Long updateById) { + this.updateById = updateById; + } + + public String getSearchValue() { + return searchValue == null ? "" : searchValue; + } + + public void setSearchValue(String searchValue) { + this.searchValue = searchValue; + } + + public String getCreateBy() { + return createBy == null ? "" : createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getUpdateBy() { + return updateBy == null ? "" : updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getRemark() { + return remark == null ? "" : remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Map getParams() { + if (params == null) { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) { + this.params = params; + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java new file mode 100644 index 00000000..b775f17d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java @@ -0,0 +1,106 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author admin + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java new file mode 100644 index 00000000..927fd96a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author admin + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java new file mode 100644 index 00000000..15310b9f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java @@ -0,0 +1,61 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Treeselect树结构实体类 + * + * @author admin + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginBody.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginBody.java new file mode 100644 index 00000000..b79e345a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginBody.java @@ -0,0 +1,71 @@ +package com.jiuyv.sptccc.agile.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author admin + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码(这里为手机验证码) + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUser.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUser.java new file mode 100644 index 00000000..4827b22a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUser.java @@ -0,0 +1,283 @@ +package com.jiuyv.sptccc.agile.common.core.domain.model; + +import java.util.Collection; +import java.util.Set; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; + +/** + * 登录用户身份权限 + * + * @author admin + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private TblPortalUserBase user; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public LoginUser() + { + } + + public LoginUser(TblPortalUserBase user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, TblPortalUserBase user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + //需要忽略 + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + //需要忽略 + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + //需要忽略 + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + //需要忽略 + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + //需要忽略 + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public TblPortalUserBase getUser() + { + return user; + } + + public void setUser(TblPortalUserBase user) + { + this.user = user; + } + + @Override + public Collection getAuthorities() + { + return null; + } + + @Override + public String toString() { + return user.getUserName(); + } + @Override + public int hashCode() { + return user.getUserName().hashCode(); + } + @Override + public boolean equals(Object obj) { + if(obj==null) { + return false; + } + return this.toString().equals(obj.toString()); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java new file mode 100644 index 00000000..059cf963 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java @@ -0,0 +1,163 @@ +package com.jiuyv.sptccc.agile.common.core.domain.model; + +import java.io.Serializable; + +/** + * 登录用户基本信息 + * + * @author admin + */ +public class LoginUserSimple implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** 用户账号 */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * @return the userId + */ + public Long getUserId() { + return userId; + } + + /** + * @param userId the userId to set + */ + public void setUserId(Long userId) { + this.userId = userId; + } + + /** + * @return the userName + */ + public String getUserName() { + return userName; + } + + /** + * @param userName the userName to set + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * @return the nickName + */ + public String getNickName() { + return nickName; + } + + /** + * @param nickName the nickName to set + */ + public void setNickName(String nickName) { + this.nickName = nickName; + } + + /** + * @return the deptId + */ + public Long getDeptId() { + return deptId; + } + + /** + * @param deptId the deptId to set + */ + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + /** + * @return the ipaddr + */ + public String getIpaddr() { + return ipaddr; + } + + /** + * @param ipaddr the ipaddr to set + */ + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + /** + * @return the loginLocation + */ + public String getLoginLocation() { + return loginLocation; + } + + /** + * @param loginLocation the loginLocation to set + */ + public void setLoginLocation(String loginLocation) { + this.loginLocation = loginLocation; + } + + /** + * @return the browser + */ + public String getBrowser() { + return browser; + } + + /** + * @param browser the browser to set + */ + public void setBrowser(String browser) { + this.browser = browser; + } + + /** + * @return the os + */ + public String getOs() { + return os; + } + + /** + * @param os the os to set + */ + public void setOs(String os) { + this.os = os; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/RegisterBody.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/RegisterBody.java new file mode 100644 index 00000000..351fee05 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.jiuyv.sptccc.agile.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author admin + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java new file mode 100644 index 00000000..f2ec60a7 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author admin + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java new file mode 100644 index 00000000..e74a24a2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java @@ -0,0 +1,91 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import java.io.Serializable; +import java.util.List; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; + +/** + * 表格分页数据对象 + * + * @author admin + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private int total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + public TableDataInfo(String errormsg) + { + this.code=HttpStatus.ERROR; + this.msg=errormsg; + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java new file mode 100644 index 00000000..4dd19b7b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import com.jiuyv.sptccc.agile.common.core.text.Convert; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author admin + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java new file mode 100644 index 00000000..b83aa9fb --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java @@ -0,0 +1,137 @@ +package com.jiuyv.sptccc.agile.common.core.redis; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.Cache; +import org.springframework.cache.Cache.ValueWrapper; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.framework.config.caffeine.CacheTimestampedValue; + +/** + * 缓存工具类,用的Caffeine 缓存 + * 工具类就不改名了 + * @author admin + **/ +@SuppressWarnings(value = { "unchecked"}) +@Component +public class RedisCache +{ + @Autowired + @Qualifier(value = "commonCacheManager") + private CacheManager cacheManager; + + + /** + * 往指定缓存放入一个键值 + * @param + * @param cacheName + * @param key + * @param value + */ + public void setValueOfCacheName(final String cacheName, final String key, final T value) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.put(key, value); + } + /** + * 往指定缓存放入一个键值(并记录时间) + * @param + * @param cacheName + * @param key + * @param value + */ + public void setTimeValueOfCacheName(final String cacheName, final String key, final T value) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.put(key, new CacheTimestampedValue<>(value)); + } + /** + * 获取指定缓存的某个键值 + * @param + * @param cacheName + * @param key + * @param value + */ + public T getValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + ValueWrapper wval = cache.get(key); + if(wval==null) { + return null; + } + return (T) wval.get(); + } + public T getValueOfCacheName(final String cacheName, final String key,Class clazz) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } +// ParameterizedTypeReference> typeRef = new ParameterizedTypeReference() {}; +// typeRef.getType().getClass() + T wval = cache.get(key, clazz); + if(wval==null) { + return null; + } + return wval; + } + /** + * 获取指定缓存的某个键值(并返回记录时间) + * @param + * @param cacheName + * @param key + * @return + */ + public CacheTimestampedValue getTimeValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + ValueWrapper wval = cache.get(key); + if(wval==null) { + return null; + } + return (CacheTimestampedValue) wval.get(); + } + /** + * 移除指定缓存的某个键值 + * @param cacheName + * @param key + */ + public void removeValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.evict(key); + } + /** + * 清空指定缓存全部的键值 + * @param cacheName + */ + public void clearAllOfCacheName(final String cacheName) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.clear(); + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java new file mode 100644 index 00000000..9545fcd8 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java @@ -0,0 +1,87 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author admin + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java new file mode 100644 index 00000000..7f570c29 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java @@ -0,0 +1,1002 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 类型转换器 + * + * @author admin + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double)value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java new file mode 100644 index 00000000..d9b41e43 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author admin + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java new file mode 100644 index 00000000..605e8a0c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 操作状态 + * + * @author admin + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java new file mode 100644 index 00000000..1b2c350f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 业务操作类型 + * + * @author admin + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java new file mode 100644 index 00000000..028b127e --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.jiuyv.sptccc.agile.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author admin + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java new file mode 100644 index 00000000..8eeee8b4 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java @@ -0,0 +1,7 @@ +package com.jiuyv.sptccc.agile.common.enums; + +public enum OperateTypeEnum { + ADD, + DELETE, + UPDATE; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java new file mode 100644 index 00000000..e8661418 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 操作人类别 + * + * @author admin + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/ParamType.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/ParamType.java new file mode 100644 index 00000000..e63d9ca6 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/ParamType.java @@ -0,0 +1,85 @@ +package com.jiuyv.sptccc.agile.common.enums; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; + +public enum ParamType { + + String("1", "字符串"), + Integer("2", "整型"), + Float("3", "浮点型"), + Date("4", "时间"), + List("5", "集合"); + + private final String key; + + private final String val; + + ParamType(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static Object parse(ParamType paramType, Object obj) { + if (obj == null) { + return null; + } + switch (paramType) { + case String: + try { + return (java.lang.String)obj; + } catch (Exception e) { + throw new ServiceException("参数值[" + obj + "]不是" + String.getVal() + "数据类型]"); + } + case Float: + try { + return new BigDecimal(obj.toString()).doubleValue(); + } catch (Exception e) { + throw new ServiceException("参数值[" + obj + "]不是" + Float.getVal() + "数据类型]"); + } + case Integer: + try { + return (java.lang.Integer)obj; + } catch (Exception e) { + throw new ServiceException("参数值[" + obj + "]不是" + Integer.getVal() + "数据类型]"); + } + case List: + try { + return (java.util.List)obj; + } catch (Exception e) { + throw new ServiceException("参数值[" + obj + "]不是" + List.getVal() + "数据类型]"); + } + case Date: + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return sdf.parse(obj.toString()); + } catch (Exception e) { + try { + return sdf.parse(obj.toString()); + } catch (Exception ex) { + throw new ServiceException("参数值[" + obj + "]不是" + Date.getVal() + "数据类型]"); + } + } + } + return null; + } + + public static ParamType getParamType(String paramType) { + for (ParamType type : ParamType.values()) { + if (type.key.equals(paramType)) { + return type; + } + } + return String; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/UserStatus.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/UserStatus.java new file mode 100644 index 00000000..48c9fcd9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 用户状态 + * + * @author admin + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java new file mode 100644 index 00000000..d8c642cc --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 全局异常 + * + * @author admin + */ +public class GlobalException extends RuntimeException +{ + + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java new file mode 100644 index 00000000..58a61171 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java @@ -0,0 +1,73 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 业务异常 + * + * @author admin + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java new file mode 100644 index 00000000..12b883d3 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 工具类异常 + * + * @author admin + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java new file mode 100644 index 00000000..2e1ca827 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.jiuyv.sptccc.agile.common.exception.base; + +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author admin + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java new file mode 100644 index 00000000..0975083d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +import com.jiuyv.sptccc.agile.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author admin + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 00000000..945fb5f9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author admin + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 00000000..19f5f35a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author admin + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java new file mode 100644 index 00000000..cde9f9ef --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,81 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author admin + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java new file mode 100644 index 00000000..8016ac7b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author admin + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java new file mode 100644 index 00000000..ef244a73 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author admin + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java new file mode 100644 index 00000000..b2f11e32 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +import com.jiuyv.sptccc.agile.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author admin + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java new file mode 100644 index 00000000..099a4e26 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java @@ -0,0 +1,53 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author admin + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 00000000..46e49efa --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import com.jiuyv.sptccc.agile.common.utils.http.HttpHelper; + +/** + * 构建可重复读取inputStream的request + * + * @author admin + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = HttpHelper.getBodyString(request).getBytes("UTF-8"); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java new file mode 100644 index 00000000..773d765f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 防止XSS攻击的过滤器 + * + * @author admin + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || method.matches("GET") || method.matches("DELETE")) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..79ade74a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,112 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author admin + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java new file mode 100644 index 00000000..75d17c23 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java @@ -0,0 +1,152 @@ +package com.jiuyv.sptccc.agile.common.utils; + + + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * 获取Spring Context 中的 bean + */ +@Component +public class AppUtil implements ApplicationContextAware { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AppUtil.class); + + private static ApplicationContext context; + + + @Override + public void setApplicationContext(ApplicationContext _context) + throws BeansException { + setContext(_context); + + } + + private static void setContext(ApplicationContext _context) { + AppUtil.context = _context; + } + + /** + * 获取spring容器上下文。 + * + * @return ApplicationContext + */ + public static ApplicationContext getApplicaitonContext() { + return context; + } + + /** + * 根据beanId获取配置在系统中的对象实例。 + * + * @param beanId + * @return Object + * @throws + * @since 1.0.0 + */ + public static Object getBean(String beanId) { + try { + return context.getBean(beanId); + } catch (Exception ex) { + LOGGER.debug("getBean:" + beanId + "," + ex.getMessage()); + } + return null; + } + + /** + * 获取Spring容器的Bean + * + * @param beanClass + * @return T + * @throws + * @since 1.0.0 + */ + public static T getBean(Class beanClass) { + try { + return context.getBean(beanClass); + } catch (Exception ex) { + LOGGER.debug("getBean:" + beanClass + "," + ex.getMessage()); + } + return null; + } + + + /** + * 获取接口的实现类实例。 + * + * @param clazz + * @return + */ + public static Map getImplInstance(Class clazz){ + return context.getBeansOfType(clazz); + } + + public static List getImplInstanceArray(Class clazz){ + List list = new ArrayList<>(); + + Map map = context.getBeansOfType(clazz); + + for (T t : map.values()) { + list.add(t); + } + return list; + } + + + /** + * 发布事件。 + * + * @param event void + */ + public static void publishEvent(ApplicationEvent event) { + if (context != null) { + context.publishEvent(event); + } + } + + + /** + * 获取当前系统环境
+ * 目前仅支持单一环境配置。请勿配置多个参数 dev sit 之类。 环境配置的判断参考下面代码
+ * doGetActiveProfiles().contains(profile) || (doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile)) + * + * @return + */ + private static String currentProfiles = null; + + public static String getCtxEnvironment() { + if (currentProfiles != null) { + return currentProfiles; + } + + Environment environment = context.getEnvironment(); + String[] activeProfiles = environment.getActiveProfiles(); + + if (activeProfiles.length>0) { + currentProfiles = activeProfiles[0]; + return currentProfiles; + } + + String[] defaultProfiles = environment.getDefaultProfiles(); + if (defaultProfiles.length>0) { + currentProfiles = defaultProfiles[0]; + return defaultProfiles[0]; + } + + throw new ServiceException("查找不到正确的环境属性配置!", 9999); + } + + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java new file mode 100644 index 00000000..6b5197de --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author admin + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java new file mode 100644 index 00000000..d14e82ce --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java @@ -0,0 +1,162 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author admin + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static final String YYYY = "yyyy"; + + public static final String YYYY_MM = "yyyy-MM"; + + public static final String YYYY_MM_DD = "yyyy-MM-dd"; + + public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", + "yyyy.MM.dd HH:mm", "yyyy.MM" }; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60L; + long nh = 1000 * 60 * 60L; + long nm = 1000 * 60L; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java new file mode 100644 index 00000000..67e7a850 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author admin + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java new file mode 100644 index 00000000..b3c54edf --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java @@ -0,0 +1,292 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonGenerator.Feature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jiuyv.sptccc.agile.common.annotation.SensitiveData; +import com.jiuyv.sptccc.agile.common.annotation.TruncatedContent; +import com.jiuyv.sptccc.agile.common.utils.jackson.MaskSensitiveDataSerializerProvider; + +/** + * + * @author zhouliang + * + */ +public abstract class JsonUtil { + + /** + * The Constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); + + private JsonUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * The object mapper. + */ + private static ObjectMapper objectMapper = new ObjectMapper(); + //专门使用一个独立的,处理敏感字段等 + private static ObjectMapper objectMapper2 = new ObjectMapper(); + + static { + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.setSerializationInclusion(Include.NON_NULL); + objectMapper.configure(Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); + objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + + + objectMapper2.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper2.setSerializationInclusion(Include.NON_NULL); + objectMapper2.configure(Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); + objectMapper2.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + // 启用自定义注解功能 + objectMapper2.setAnnotationIntrospector(new JsonUtil.SensitiveDataIntrospector()); + objectMapper2.setSerializerProvider(new MaskSensitiveDataSerializerProvider()); + } + public static ObjectMapper JsonMapper(){ + return objectMapper; + } + public static ObjectMapper JsonMapper2(){ + return objectMapper2; + } + + /** + * 对字段太长的字段进行截取,不需要关注值,只关心有没有 + * @author zhouliang + *, + */ + public static class TruncatedIntrospector extends JacksonAnnotationIntrospector { + private static final long serialVersionUID = 1L; + @Override + public Object findSerializer(Annotated annotated) { + if (annotated.hasAnnotation(TruncatedContent.class)) { + TruncatedContent jsonProperty = annotated.getAnnotation(TruncatedContent.class); + return new TruncatedStringSerializer(jsonProperty.length()); + + } + return super.findSerializer(annotated); + } + } + + public static class TruncatedStringSerializer extends JsonSerializer { + private int MAX_LENGTH = 2000; + public TruncatedStringSerializer(){ + } + public TruncatedStringSerializer(int length) { + this.MAX_LENGTH = length; + } + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + if (value.length() > MAX_LENGTH) { + gen.writeString(value.substring(0, MAX_LENGTH) + "..."); + } else { + gen.writeString(value); + } + } + } + + /** + * 通过注解处理敏感字段,自行在需要的注入 + * @author zhouliang + * + */ + public static class SensitiveDataIntrospector extends JacksonAnnotationIntrospector { + private static final long serialVersionUID = 1L; + @Override + public Object findSerializer(Annotated annotated) { + if (annotated.hasAnnotation(SensitiveData.class)) { + SensitiveData sensitiveDataProperty = annotated.getAnnotation(SensitiveData.class); + return new JsonSerializer() { + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if(value!=null) { + String val=StringUtils.padl("",6, sensitiveDataProperty.defaultMark()); + //大于允许截取 + if(value.length()*2/3>=(sensitiveDataProperty.firstLength()+sensitiveDataProperty.endLength())) { + gen.writeString(value.substring(0,sensitiveDataProperty.firstLength())+ val+ value.substring(value.length()-sensitiveDataProperty.endLength())); + }else { + gen.writeString(val); + } + } + } + }; + } + return super.findSerializer(annotated); + } + } + + + /** + * 转为json字符串 + * + * @param object the object + * @return the string + */ + public static String toJSONString(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return ""; + } + } + + /** + * json字符串转为对象 + * @param + * @param json + * @param clz + * @return + */ + public static T json2Bean(String json, Class clz) { + try { + return objectMapper.readValue(json, clz); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * json字符串转为对象 + * @param + * @param json + * @param clz + * @return + */ + public static T json2Bean(String json, JavaType clz) { + try { + return objectMapper.readValue(json, clz); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + + /** + * 转为对象集合 + * @param + * @param json + * @param clz + * @return + */ + public static List json2List(String json, Class clz) { + try { + JavaType javaType = getCollectionType(ArrayList.class, clz); + return objectMapper.readValue(json, javaType); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return new ArrayList<>(); + } + } + + /** + * 获取泛型的Collection Type + * + * @param collectionClass 泛型的Collection + * @param elementClasses 元素类 + * @return JavaType Java类型 + * @since 1.0 + */ + public static JavaType getCollectionType(Class collectionClass, Class... elementClasses) { + return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + + /** + * json字符转ArrayNode + * @param json + * @return + */ + public static ArrayNode parseArray(String json) { + try { + return (ArrayNode) objectMapper.readTree(json); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * json字符转ObjectNode + * @param json + * @return + */ + public static ObjectNode parseObject(String json) { + try { + return (ObjectNode) objectMapper.readTree(json); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * 创建ArrayNode + * @param json + * @return + */ + public static ArrayNode createArray() { + return objectMapper.createArrayNode(); + } + /** + * 创建ObjectNode + * @param json + * @return + */ + public static ObjectNode createObject() { + return objectMapper.createObjectNode(); + } + /** + * 集合实体对象转ArrayNode + * @param json + * @return + */ + public static ArrayNode toArray(Object obj) { + try { + return objectMapper.valueToTree(obj); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return createArray(); + } + } + /** + * 实体对象转ObjectNode + * @param json + * @return + */ + public static ObjectNode toObject(Object obj) { + try { + return objectMapper.valueToTree(obj); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return createObject(); + } + } + /** 拷贝对象 + * 主要还是是用于Node拷贝 + * */ + public static void copyProperties(K source, V target) { + try { + objectMapper.updateValue(target,source); + } catch (Exception e) { + LOGGER.error("convert failed", e); + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java new file mode 100644 index 00000000..4a7e576e --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.common.utils; + +/** + * 处理并记录日志文件 + * + * @author admin + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java new file mode 100644 index 00000000..9e0a7b04 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java @@ -0,0 +1,41 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.util.Locale; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author admin + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * 输出到日志时指定一个固定编码(一般就英文吧) + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String messageDef(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, Locale.US); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java new file mode 100644 index 00000000..8f4b01cf --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import com.github.pagehelper.PageHelper; +import com.jiuyv.sptccc.agile.common.core.page.PageDomain; +import com.jiuyv.sptccc.agile.common.core.page.TableSupport; +import com.jiuyv.sptccc.agile.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author admin + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java new file mode 100644 index 00000000..a38152fd --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java @@ -0,0 +1,138 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * 反射工具类 + */ +public class ReflectUtil { + private ReflectUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * 判断 beanClass 是否存在 name 字段 + * + * @param beanClass 实体类类型 + * @param name 字段名 + * @return boolean 是否存在 + */ + public static boolean hasField(Class beanClass, String name) { + Field field = ReflectionUtils.findField(beanClass, name); + return field != null; + } + + /** + * 获取 beanClass 中的 name 字段 + * + * @param beanClass 实体类类型 + * @param name 字段名 + * @return Field 字段对象 + * @throws ServiceException 如果不存在该字段,则抛出此异常 + */ + public static Field getField(Class beanClass, String name) throws ServiceException { + Field field = ReflectionUtils.findField(beanClass, name); + if (field == null) { + throw new ServiceException("该类中不存在该字段"); + } + return field; + } + + /** + * 获取 beanClass 中的所有字段 + * + * @param beanClass 实体类类型 + * @return Field[] 字段对象数组 + */ + public static Field[] getFields(Class beanClass) { + return beanClass.getDeclaredFields(); + } + /** + * 获取 beanClass 中的所有字段,包括其父类中的字段 + * + * @param beanClass 实体类类型 + * @return Field[] 字段对象数组 + */ + public static Field[] getAllFields(Class beanClass) { + return getFieldsDirectly(beanClass, true); + } + /** + * 获取 beanClass 中所有的字段,包括其父类中的字段 + * + * @param beanClass 实体类类型 + * @param withSuperClassFields 是否包含父类中的属性 + * @return Field[] 字段对象数组 + * @throws ServiceException 安全检查异常 + */ + public static Field[] getFieldsDirectly(Class beanClass, boolean withSuperClassFields) throws ServiceException { + Assert.notNull(beanClass,"beanClass不能为空"); + if (withSuperClassFields) { + Field[] allFields = null; + Class searchType = beanClass; + Field[] declaredFields; + while (searchType != null) { + declaredFields = searchType.getDeclaredFields(); + if (null == allFields) { + allFields = declaredFields; + }else{ + Field[] mergedArray = Arrays.copyOf(allFields, allFields.length + declaredFields.length); + System.arraycopy(declaredFields, 0, mergedArray, allFields.length, declaredFields.length); + allFields=mergedArray; + } + searchType = searchType.getSuperclass(); + } + return allFields; + } else { + return beanClass.getDeclaredFields(); + } + } + + /** + * 使用set方法赋值,如果没有则不赋值 + * + * @param obj 实体类对象 + * @param field 字段对象 + * @param value 字段值 + */ + public static void setFieldValue(Object obj, String field, Object value) { + Assert.notNull(obj,"obj不能为空"); + Assert.notNull(field,"field不能为空"); + try { + Method method = ReflectionUtils.findMethod(obj.getClass(), "set"+StringUtils.capitalize(field)); + if(method!=null) { + method.invoke(obj, value); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + //忽略 + } + } + /** + * 使用set方法赋值,如果没有则不赋值 + * + * @param obj 实体类对象 + * @param field 字段对象 + * @param value 字段值 + */ + public static void setFieldValue(Object obj, Field field, Object value) { + Assert.notNull(obj,"obj不能为空"); + Assert.notNull(field,"field不能为空"); + try { + Method method = ReflectionUtils.findMethod(field.getDeclaringClass(), "set"+StringUtils.capitalize(field.getName())); + if(method!=null) { + method.invoke(obj, value); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + //忽略 + } + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java new file mode 100644 index 00000000..95caf083 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java @@ -0,0 +1,121 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author admin + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java new file mode 100644 index 00000000..efd26f68 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java @@ -0,0 +1,260 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author admin + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return request.getParameter(name); + } + return null; + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toStr(request.getParameter(name), defaultValue); + } + + return null; + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toInt(request.getParameter(name)); + } + return null; + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toInt(request.getParameter(name), defaultValue); + } + return null; + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toBool(request.getParameter(name)); + } + return Boolean.FALSE; + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toBool(request.getParameter(name), defaultValue); + } + return Boolean.FALSE; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + ServletRequestAttributes requestAttributes = getRequestAttributes(); + if(null != requestAttributes) { + return requestAttributes.getRequest(); + } + return null; + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + ServletRequestAttributes requestAttributes = getRequestAttributes(); + if(null != requestAttributes) { + return requestAttributes.getResponse(); + } + return null; + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + HttpServletRequest request = getRequest(); + if(null != request) { + return request.getSession(); + } + return null; + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + if(null != attributes) { + return (ServletRequestAttributes) attributes; + } + return null; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 写入图片信息 + * @param response + * @param fileType + * @param fileName + * @param fileContent + */ + public static void renderImg(HttpServletResponse response, String fileType, + String fileName, byte[] fileContent) { + response.setContentType(getContentType(fileType)); + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + try { + response.getOutputStream().write(fileContent); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String getContentType(String fileType) { + if("gif".equalsIgnoreCase(fileType)) { + return "image/gif"; + }else if ("jpg".equalsIgnoreCase(fileType)||"jpeg".equalsIgnoreCase(fileType)) { + return "image/jpg"; + }else if ("png".equalsIgnoreCase(fileType)) { + return "image/png"; + }else if ("bmp".equalsIgnoreCase(fileType)) { + return "image/bmp"; + }else { + return "application/octet-stream"; + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java new file mode 100644 index 00000000..6e384863 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java @@ -0,0 +1,613 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author admin + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + 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(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } + /** + * 判断是否为数字开头 + * @param str + * @return + */ + public static boolean isNumStart(String str) { + int chr = str.charAt(0); + if (chr < 48 || chr > 57) { + return false; + } + return true; + } + /** + * string 转 date + */ + public static Date toDate(String str) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String dateStr = str.replace("年","-").replace("月","-").replace("日",""); + Date date = null; + try { + date = simpleDateFormat.parse(dateStr); + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java new file mode 100644 index 00000000..76e46a99 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java @@ -0,0 +1,74 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author admin + */ +public class Threads { + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + + /** + * 停止线程池 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. 如果超时, 则调用shutdownNow, + * 取消在workQueue中Pending的任务,并中断所有阻塞函数. 如果仍然超時,則強制退出. 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + logger.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + logger.error(t.getMessage(), t); + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java new file mode 100644 index 00000000..6ad90e72 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java @@ -0,0 +1,215 @@ +package com.jiuyv.sptccc.agile.common.utils.bean; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * Bean 工具类 + * + * @author admin + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + private static final Logger LOGGER = LoggerFactory.getLogger(BeanUtils.class); + + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } + + + /** + * 拷贝集合 + * @param list + * @param clazz + * @return + */ + public static List copyToList(List list, Class clazz) { + List list1 = new ArrayList<>(); + for (T temp : list) { + V newInstance; + try { + newInstance = clazz.newInstance(); + copyProperties(temp, newInstance); + list1.add(newInstance); + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return list1; + } + } + return list1; + } + + /** + * 拷贝对象 + * @param t + * @param clazz + * @return + */ + public static V copyProperties(T t, Class clazz) { + V newInstance; + try { + newInstance = clazz.newInstance(); + copyProperties(t, newInstance); + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return null; + } + return newInstance; + } + /** + * 拷贝对象 + * @param source + * @param target + * @return + */ + public static void copyProperties(Object source, Object target) throws ServiceException { + org.springframework.beans.BeanUtils.copyProperties(source, target); + } + + /** + * 简单对象转为map + * @param obj + * @return + */ + public static Map beanToMap(Object obj) { + if (obj == null) { + return null; + } + Map map = new HashMap<>(); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor property : propertyDescriptors) { + String key = property.getName(); + // 过滤class属性 + if (!key.equals("class")) { + // 得到property对应的getter方法 + Object value = property.getReadMethod().invoke(obj); + map.put(key, value); + } + } + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return null; + } + return map; + } + + /** + * LIST转LIST + */ + public static List> beanToMap(List objList) { + if (objList == null || objList.isEmpty()) { + return new ArrayList<>(); + } + List> listMap = new ArrayList<>(); + for(Object obj : objList){ + listMap.add(beanToMap(obj)); + } + return listMap; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java new file mode 100644 index 00000000..b7e54c5f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.jiuyv.sptccc.agile.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author admin + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java new file mode 100644 index 00000000..668ef341 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java @@ -0,0 +1,108 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.io.File; +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author admin + */ +public class FileTypeUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 是否是图片 + * + * @param ext + * @return "jpg", "jpeg", "gif", "png", "bmp" 为文件后缀名者为图片 + */ + public static boolean isValidImageExt(String ext) { + ext = ext.toLowerCase(Locale.ENGLISH); + for (String s : MimeTypeUtils.getImageExtension()) { + if (s.equalsIgnoreCase(ext)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java new file mode 100644 index 00000000..3d915ead --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,80 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.util.Arrays; +import java.util.Collections; + +/** + * 媒体类型工具类 + * + * @author admin + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + private static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static String[] getImageExtension() { + return Collections.unmodifiableList(Arrays.asList(IMAGE_EXTENSION)).toArray(new String[IMAGE_EXTENSION.length]); + } + + private static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static String[] getFlashExtension() { + return Collections.unmodifiableList(Arrays.asList(FLASH_EXTENSION)).toArray(new String[FLASH_EXTENSION.length]); + } + + private static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static String[] getMediaExtension() { + return Collections.unmodifiableList(Arrays.asList(MEDIA_EXTENSION)).toArray(new String[MEDIA_EXTENSION.length]); + } + + private static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static String[] getVideoExtension() { + return Collections.unmodifiableList(Arrays.asList(VIDEO_EXTENSION)).toArray(new String[VIDEO_EXTENSION.length]); + } + + private static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + + public static String[] getDefaultAllowedExtension() { + return Collections.unmodifiableList(Arrays.asList(DEFAULT_ALLOWED_EXTENSION)).toArray(new String[DEFAULT_ALLOWED_EXTENSION.length]); + } + + public static String getExtension(String prefix) { + switch (prefix) { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java new file mode 100644 index 00000000..b641c516 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java @@ -0,0 +1,155 @@ +package com.jiuyv.sptccc.agile.common.utils.html; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author admin + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java new file mode 100644 index 00000000..cebbce0e --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.jiuyv.sptccc.agile.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author admin + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/CookieUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/CookieUtil.java new file mode 100644 index 00000000..eafad4c9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/CookieUtil.java @@ -0,0 +1,75 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.util.WebUtils; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * Cookie工具类 + * @author zhouliang + * + */ +public class CookieUtil { + + /** + * 设置Cookie + * @param request + * @param response + * @param key + * @param val + * @param maxage + */ + public static void setCookie(HttpServletRequest request,HttpServletResponse response,String key,String val,Integer maxage) { + Cookie cookie = new Cookie(key, val); + cookie.setPath(getCookiePath(request)); + cookie.setHttpOnly(true); + cookie.setSecure(false); + cookie.setMaxAge(maxage==null?Integer.MAX_VALUE:maxage); + response.addCookie(cookie); + } + /** + * 设置已存在的Cookie生命周期 + * @param request + * @param response + * @param key + * @param maxage + * @return + */ + public static boolean resetCookie(HttpServletRequest request,HttpServletResponse response,String key,Integer maxage) { + Cookie cookie = WebUtils.getCookie(request, key); + if(cookie!=null) { + cookie.setMaxAge(maxage==null?Integer.MAX_VALUE:maxage); + response.addCookie(cookie); + return true; + } + return false; + } + + + public static String getCookiePath(HttpServletRequest request) { + return "/"; + } + /** + * 获取Cookie的值 + * @param request + * @param key + * @return + */ + public static String getCookieVal(HttpServletRequest request,String key) { + String value = null; + Cookie cookie = WebUtils.getCookie(request, key); + if(cookie!=null) { + value=cookie.getValue(); + } + if (StringUtils.isNotEmpty(value) && value.startsWith(Constants.TOKEN_PREFIX)) + { + value = value.replace(Constants.TOKEN_PREFIX, ""); + } + return value; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java new file mode 100644 index 00000000..30e385d2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author admin + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java new file mode 100644 index 00000000..92a252d2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java @@ -0,0 +1,967 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.net.ssl.SSLContext; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.FileEntity; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.uuid.Seq; + + +/** + * 描述 用于发送http请求的工具类 + */ + +public class HttpRequestUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(HttpRequestUtil.class); + + private static PoolingHttpClientConnectionManager connMgr; + private static RequestConfig requestConfig; + private static final int MAX_TIMEOUT = 7000; + + private HttpRequestUtil() { + throw new IllegalStateException("Utility class"); + } + + static { + // 设置连接池 + connMgr = new PoolingHttpClientConnectionManager(); + // 设置连接池大小 + connMgr.setMaxTotal(100); + connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal()); + + RequestConfig.Builder configBuilder = RequestConfig.custom(); + // 设置连接超时 + configBuilder.setConnectTimeout(MAX_TIMEOUT); + // 设置读取超时 + configBuilder.setSocketTimeout(MAX_TIMEOUT); + // 设置从连接池获取连接实例的超时 + configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT); + requestConfig = configBuilder.build(); + } + + /** + * 填充请求头参数至get请求中 + * + * @param httpPost + * @param headers + * @return + */ + private static HttpGet setHeadersToGet(HttpGet httpGet, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpGet.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpGet; + } /** + * 填充请求头参数至Post请求中 + * + * @param httpPost + * @param headers + * @return + */ + private static HttpPost setHeadersToPost(HttpPost httpPost, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpPost.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpPost; + } /** + * 填充请求头参数至Put请求中 + * + * @param httpPut + * @param headers + * @return + */ + private static HttpPut setHeadersToPut(HttpPut httpPut, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpPut.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpPut; + } /** + * 填充请求体参数至Post请求中 + * + * @param httpPost + * @param params + * @return + */ + private static HttpPost setParamsToRequest(HttpPost httpPost, Map params) { + if(params==null) { + params=new HashMap<>(); + } + List pairList = new ArrayList<>(params.size()); + for (Map.Entry entry : params.entrySet()) { + NameValuePair pair = null; + if (entry.getValue() == null) { + pair = new BasicNameValuePair(entry.getKey(), null); + } else { + pair = new BasicNameValuePair(entry.getKey(), entry + .getValue().toString()); + } + pairList.add(pair); + } + httpPost.setEntity(new UrlEncodedFormEntity(pairList, StandardCharsets.UTF_8)); + return httpPost; + } + + /** + * 设置get-url请求参数 + * @param apiUrl + * @param params + * @return + */ + private static String setUrlParams(String apiUrl, Map params) { + StringBuilder param = new StringBuilder(); + int i = 0; + if (params != null && params.size() > 0) { + for (Entry entrySet : params.entrySet()) { + if (i == 0) + param.append("?"); + else + param.append("&"); + param.append(entrySet.getKey()).append("=").append(entrySet.getValue()); + i++; + } + } + apiUrl += param; + return apiUrl; + } + + /** + * 发送 GET 请求(HTTP),不带输入数据 + * + * @param url + * @return + */ + public static HttpResponseResult doGet(String url) { + return doGet(url, new HashMap<>(), new HashMap<>()); + } + + /** + * 发送 GET 请求(HTTP),K-V形式,无请求头参数 + * + * @param url + * @param params + * @return + */ + public static HttpResponseResult doGet(String url, Map params) { + return doGet(url, params, null); + } + + /** + * 发送 GET 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doGet(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig) + .setConnectionManagerShared(true).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + String result = null; + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet = setHeadersToGet(httpGet, headers); + response = httpClient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + result = IOUtils.toString(instream, StandardCharsets.UTF_8); + requestResult.setBody(result); + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + /** + * 发送 GET 请求(HTTP),K-V形式,下载文件,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @param out 存储文件到指定目录或者写入流 + * @return + */ + public static HttpResponseResult doGetFile(String url, Map params, Map headers,Object out) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig) + .setConnectionManagerShared(true).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet = setHeadersToGet(httpGet, headers); + response = httpClient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + if(out instanceof String) {//路径不要指定名称 + // 创建用于存储文件的File对象 + String path = out.toString(); + File file = new File(path); + //文件名称,后面再添加一个id串 + String filename="myfile_"+Seq.getId()+".tar.gz"; + if(!file.exists()) { + if(!file.getName().contains(".")) {//无文件名 + file = new File(path+File.separator+filename); + }else { + filename=file.getName(); + } + }else { + filename=file.getName(); + } + // 创建用于写入文件的FileOutputStream对象 + try (FileOutputStream outstream = new FileOutputStream(file)) { + // 将获取到的数据流写入本地文件中 + IOUtils.copy(instream, outstream); + // 关闭所有打开的资源 + instream.close(); + } + requestResult.setBody(filename); + requestResult.setMessage("文件下载成功"); + }else if(out instanceof HttpServletResponse){ + long contentLength = entity.getContentLength(); + + HttpServletResponse res = (HttpServletResponse) out; + + //文件名称,后面再添加一个id串 + String filename="myfile_"+Seq.getId()+".tar.gz"; + // 设置响应头 + res.setContentType("application/octet-stream"); + res.setContentLength((int)contentLength); + res.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); + // 写入响应流中 + OutputStream outputStream = res.getOutputStream(); + byte[] buffer = new byte[1024*1024]; + int len; + while ((len = instream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + outputStream.flush(); + outputStream.close(); + instream.close(); + } + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 POST 请求(HTTP),不带输入数据 + * + * @param url + * @return + */ + public static HttpResponseResult doPost(String url) { + return doPost(url, new HashMap<>(), new HashMap<>()); + } + + /** + * 发送 POST 请求(HTTP),JSON形式,无请求头参数 + * + * @param url + * @param json json对象 + * @return + */ + public static HttpResponseResult doPost(String url, Object json) { + return doPost(url, json, null); + } + + /** + * 发送 POST 请求(HTTP),K-V形式,无请求头参数 + * + * @param url + * @param params + * @return + */ + public static HttpResponseResult doPost(String url, Map params) { + return doPost(url, params, null); + } + + /** + * 发送 POST 请求(HTTP),K-V形式 ,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPost(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + String httpStr = null; + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + httpPost = setParamsToRequest(httpPost, params); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity entity = response.getEntity(); + if(entity!=null) { + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + } + requestResult.setBody(httpStr); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 POST 请求(HTTP),JSON形式,有请求头参数 + * + * @param url + * @param json json对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPost(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + String httpStr = null; + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPost.setEntity(stringEntity); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity entity = response.getEntity(); + if(entity!=null) { + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + } + requestResult.setBody(httpStr); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL POST 请求(HTTPS),无K-V形式参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doPostSSL(String url) { + return doPostSSL(url, null, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doPostSSL(String url, Map params) { + return doPostSSL(url, params, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpEntity entity = null; + httpPost.setConfig(requestConfig); + + httpPost = setHeadersToPost(httpPost, headers); + httpPost = setParamsToRequest(httpPost, params); + response = httpClient.execute(httpPost); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL POST 请求(HTTPS),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Object json) { + return doPostSSL(url, json, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + HttpEntity entity = null; + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPost.setEntity(stringEntity); + response = httpClient.execute(httpPost); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL GET 请求(HTTPs),无参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doGetSSL(String url) { + return doGetSSL(url, null, null); + } + + /** + * 发送 SSL GET 请求(HTTPs),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doGetSSL(String url, Map params) { + return doGetSSL(url, params, null); + } + + /** + * 发送 SSL GET 请求(HTTPs),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doGetSSL(String url, Map params, Map headers) { + + CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + String result = null; + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet.setConfig(requestConfig); + httpGet = setHeadersToGet(httpGet, headers); + response = httpclient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + result = IOUtils.toString(instream, StandardCharsets.UTF_8); + requestResult.setBody(result); + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } /** + * 发送 PUT 请求(HTTP),无参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult put(String url) { + return put(url, null, null); + } + + /** + * 发送 PUT 请求(HTTP),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult put(String url, Map params) { + return put(url, params, null); + } + + /** + * 发送 PUT 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult put(String url, Map params, Map headers) { + CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpEntity entity = null; + HttpResponseResult requestResult = new HttpResponseResult(); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpPut httpPut = new HttpPut(url); + if (headers != null) { + Set set = headers.keySet(); + for (String item : set) { + String value = headers.get(item); + httpPut.addHeader(item, value); + } + } + if (params != null) { + List paramList = new ArrayList<>(); + for (Map.Entry param : params.entrySet()) { + NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue()); + paramList.add(pair); + } + httpPut.setEntity(new UrlEncodedFormEntity(paramList, StandardCharsets.UTF_8.name())); + } + response = client.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + try { + if (response != null) { + EntityUtils.consume(response.getEntity()); + } + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } /** + * 发送 SSL PUT 请求(HTTP),JSON形式,无请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @return + */ + public static HttpResponseResult doPut(String url, Object json) { + return doPut(url, json, null); + } + + /** + * 发送 PUT 请求(HTTP),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPut(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPut httpPut = new HttpPut(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + HttpEntity entity = null; + httpPut.setConfig(requestConfig); + httpPut = setHeadersToPut(httpPut, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPut.setEntity(stringEntity); + response = httpClient.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + + /** + * 发送 PUT 请求(HTTP),传文件,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @param headers 请求头参数 + * @return in 文件来源:文件路径或输入流 + */ + public static HttpResponseResult doPutFile(String url, Map headers,Object in) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPut httpPut = new HttpPut(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params=null",url); + try { + HttpEntity entity = null; + httpPut.setConfig(requestConfig); + httpPut = setHeadersToPut(httpPut, headers); + + if(in instanceof String) {//路径不要指定名称 + File file = new File(in.toString()); + FileEntity reqEntity = new FileEntity(file, ContentType.APPLICATION_OCTET_STREAM); + httpPut.setEntity(reqEntity); + }else if(in instanceof InputStream){ + InputStreamEntity reqEntity = new InputStreamEntity((InputStream)in, -1, ContentType.APPLICATION_OCTET_STREAM); + httpPut.setEntity(reqEntity); + } + response = httpClient.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + requestResult.setMessage("文件上传成功"); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 DELETE 请求(HTTP),无参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doDelete(String url) { + return doDelete(url, null, null); + } + + /** + * 发送 DELETE 请求(HTTP),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doDelete(String url, Map params) { + return doDelete(url, params, null); + } + + /** + * 发送 DELETE 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doDelete(String url, Map params, Map headers) { + CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpEntity entity = null; + HttpResponseResult requestResult = new HttpResponseResult(); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpDelete httpDelete = new HttpDelete(url); + if (headers != null) { + Set set = headers.keySet(); + for (String item : set) { + String value = headers.get(item); + httpDelete.addHeader(item, value); + } + } + if (params != null) { + URIBuilder uriBuilder = new URIBuilder(url); + for (Entry keySet : params.entrySet()) { + uriBuilder.setParameter(keySet.getKey(), keySet.getValue()); + } + URI uri = uriBuilder.build(); + httpDelete.setURI(uri); + } + response = client.execute(httpDelete); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + try { + if (response != null) { + EntityUtils.consume(response.getEntity()); + } + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 创建SSL安全连接(不知道对不对) + * + * @return + */ +// private static SSLConnectionSocketFactory createSSLConnSocketFactory() { +// SSLConnectionSocketFactory sslsf = null; +// try { +// SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { +// +// public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { +// return true; +// } +// }).build(); +// sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() { +// @Override +// public boolean verify(String arg0, SSLSession arg1) { +// return true; +// } +// }); +// } catch (GeneralSecurityException e) { +// LOGGER.error("【http请求异常】{0}",e); +// } +// return sslsf; +// } + private static SSLConnectionSocketFactory createSSLConnSocketFactory() { + SSLConnectionSocketFactory sslsf = null; + try { + // 创建信任所有证书的 TrustManager + TrustStrategy trustStrategy = (cert, authType) -> true; + // 创建 SSL 上下文,并设置协议和算法 + SSLContext sslContext = SSLContexts.custom() + .setProtocol("TLS") + .setSecureRandom(new SecureRandom()) + .loadTrustMaterial(null, trustStrategy) + .build(); + // 创建 SSLConnectionSocketFactory 对象,用于创建 SSL 连接 + sslsf=new SSLConnectionSocketFactory(sslContext); + } catch (GeneralSecurityException e) { + LOGGER.info("【http请求异常】",e); + } + return sslsf; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java new file mode 100644 index 00000000..c0d95fbf --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java @@ -0,0 +1,53 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +/** + * 描述 + */ + +public class HttpResponseResult { + + private int status; + + private String body;//如果是下载文件,自动生成的会返回文件名称 + + private String message; + + public HttpResponseResult() { + } + + public HttpResponseResult(int statusCode, String body, String message) { + this.status = statusCode; + this.body = body; + this.message = message; + } + + public int getStatus() { + return status; + } + + public void setStatus(int statusCode) { + this.status = statusCode; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + //对于返回body是json时,需要过滤则转换为treenode进行清理,或者不输出 + return "status="+status+", body="+body+", message="+message; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java new file mode 100644 index 00000000..e90c7009 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java @@ -0,0 +1,292 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 通用http发送方法 + * + * @author admin + */ +public class HttpUtils { + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) { + return sendGet(url, param, StandardCharsets.UTF_8.name()); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl; + try { + realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + try(BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType))){ + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result.toString(); + } +// public static String sendGet2(String url, String param, String contentType) { +// StringBuilder result = new StringBuilder(); +// BufferedReader in = null; +// try { +// String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; +// log.info("sendGet - {}", urlNameString); +// URL realUrl = new URL(urlNameString); +// URLConnection connection = realUrl.openConnection(); +// connection.setRequestProperty("accept", "*/*"); +// connection.setRequestProperty("connection", "Keep-Alive"); +// connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); +// connection.connect(); +// in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); +// String line; +// while ((line = in.readLine()) != null) { +// result.append(line); +// } +// log.info("recv - {}", result); +// } catch (ConnectException e) { +// log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); +// } catch (SocketTimeoutException e) { +// log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); +// } catch (IOException e) { +// log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); +// } catch (Exception e) { +// log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); +// } finally { +// try { +// if (in != null) { +// in.close(); +// } +// } catch (Exception ex) { +// log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); +// } +// } +// return result.toString(); +// } + + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) { + StringBuilder result = new StringBuilder(); + log.info("sendPost - {}", url); + URL realUrl; + try { + realUrl = new URL(url); + URLConnection conn; + try { + conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + try( + PrintWriter out = new PrintWriter(conn.getOutputStream()); + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))){ + out.print(param); + out.flush(); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result.toString(); + } + +// public static String sendPost2(String url, String param) { +// PrintWriter out = null; +// BufferedReader in = null; +// StringBuilder result = new StringBuilder(); +// try { +// log.info("sendPost - {}", url); +// URL realUrl = new URL(url); +// URLConnection conn = realUrl.openConnection(); +// conn.setRequestProperty("accept", "*/*"); +// conn.setRequestProperty("connection", "Keep-Alive"); +// conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); +// conn.setRequestProperty("Accept-Charset", "utf-8"); +// conn.setRequestProperty("contentType", "utf-8"); +// conn.setDoOutput(true); +// conn.setDoInput(true); +// out = new PrintWriter(conn.getOutputStream()); +// out.print(param); +// out.flush(); +// in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); +// String line; +// while ((line = in.readLine()) != null) { +// result.append(line); +// } +// log.info("recv - {}", result); +// } catch (ConnectException e) { +// log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); +// } catch (SocketTimeoutException e) { +// log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); +// } catch (IOException e) { +// log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); +// } catch (Exception e) { +// log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); +// } finally { +// try { +// if (out != null) { +// out.close(); +// } +// if (in != null) { +// in.close(); +// } +// } catch (IOException ex) { +// log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); +// } +// } +// return result.toString(); +// } + + public static String sendSSLPost(String url, String param) { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try { + log.info("sendSSLPost - {}", urlNameString); + //原来的SSL不行,先改了 + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) { + if (ret != null && !"".equals(ret.trim())) { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java new file mode 100644 index 00000000..c18fa8ae --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java @@ -0,0 +1,58 @@ +package com.jiuyv.sptccc.agile.common.utils.ip; + +import java.nio.charset.StandardCharsets; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author admin + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (ConsoleConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", StandardCharsets.UTF_8.name()); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + ObjectNode obj = JsonUtil.parseObject(rspStr); + String region = obj.get("pro").asText(); + String city = obj.get("city").asText(); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java new file mode 100644 index 00000000..4b399a52 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java @@ -0,0 +1,228 @@ +package com.jiuyv.sptccc.agile.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author admin + */ +public class IpUtils { + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + 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(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + if(null != addr) { + return internalIp(addr) || "127.0.0.1".equals(ip); + } + return false; + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) { + if (StringUtils.isNull(addr) || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + return false; + case SECTION_5: + if(b1 == SECTION_6) { + return true; + } + return false; + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) { + if (org.apache.commons.lang3.StringUtils.isBlank(text)) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") >= 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (false == isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java new file mode 100644 index 00000000..a55266c8 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java @@ -0,0 +1,157 @@ +package com.jiuyv.sptccc.agile.common.utils.jackson; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.ObjectUtils; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + + +/** + * 敏感字段处理为*(不是过滤,方便确认有没有值) + * 无需注解,固定的可以直接写这里面 + * 如需要特殊处理修改自定义实现 + * @author zhouliang + * + */ +public class MaskSensitiveDataSerializerProvider extends DefaultSerializerProvider { + + private static final long serialVersionUID = 1L; + + //开始或结尾是,就认为是敏感字段​ + private static final String LIKE_FILEDS = "password,email,phone"; + //明确敏感字段 + private static final String STATIC_FILEDS = "idCard,socialCreditCode"; + + private static final Set STATIC_FILED_SET = new HashSet<>( + Arrays.stream(STATIC_FILEDS.split(",")) + .map(String::trim) + .filter(o -> !ObjectUtils.isEmpty(o)) + .collect(Collectors.toSet()) + ); + private static final Set LIKE_FILED_SET = new HashSet<>( + Arrays.stream(LIKE_FILEDS.split(",")) + .map(String::trim) + .filter(o -> !ObjectUtils.isEmpty(o)) + .collect(Collectors.toSet()) + ); + + public MaskSensitiveDataSerializerProvider() { + super(); + } + + public MaskSensitiveDataSerializerProvider(MaskSensitiveDataSerializerProvider src) { + super(src); + } + + protected MaskSensitiveDataSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) { + super(src, config, f); + } + + @Override + public DefaultSerializerProvider copy() { + if (this.getClass() != MaskSensitiveDataSerializerProvider.class) { + return super.copy(); + } + return new MaskSensitiveDataSerializerProvider(this); + } + + @Override + public MaskSensitiveDataSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) { + return new MaskSensitiveDataSerializerProvider(this, config, jsf); + } + + @Override + public JsonSerializer findValueSerializer(Class valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findValueSerializer(valueType, property); + } + + @Override + public JsonSerializer findValueSerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findValueSerializer(valueType, property); + } + + @Override + public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findPrimaryPropertySerializer(valueType, property); + } + + @Override + public JsonSerializer findPrimaryPropertySerializer(Class valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findPrimaryPropertySerializer(valueType, property); + } + + private JsonSerializer findSerializerByBeanProperty(BeanProperty property) { + String propertyName = Optional.ofNullable(property).map(BeanProperty::getName).orElse(""); + if (STATIC_FILED_SET.contains(propertyName)) { + return MaskSerializer.INSTANCE; + }else { + for(String f: LIKE_FILED_SET) { + Pattern p=Pattern.compile("(^"+f+")|("+f+"$)",Pattern.CASE_INSENSITIVE); + Matcher m=p.matcher(propertyName); + if(m.find()) { + return MaskSerializer.INSTANCE; + } + } + } + + return null; + } + + static class MaskSerializer extends JsonSerializer { + + public static final MaskSerializer INSTANCE = new MaskSerializer(); + private MaskSerializer() { + } + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if(value instanceof String) { + String value2=(String) value; + String val=StringUtils.padl("",6, '*'); + //大于允许截取 + if(value2.length()*2/3>=6) { + gen.writeString(value2.substring(0,3)+ val+ value2.substring(value2.length()-3)); + }else { + gen.writeString(val); + } + } + } + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java new file mode 100644 index 00000000..0524715c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,411 @@ +package com.jiuyv.sptccc.agile.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.core.text.Convert; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author admin + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java new file mode 100644 index 00000000..ce73e826 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.jiuyv.sptccc.agile.common.utils.sign; + +/** + * Base64工具类 + * + * @author admin + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(val2 & 0xff) | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | (val3 & 0xff)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(val2 & 0xff) | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | (b4 & 0xff)); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | (b4 & 0xff)); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java new file mode 100644 index 00000000..b1200b43 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java @@ -0,0 +1,46 @@ +package com.jiuyv.sptccc.agile.common.utils.sm4; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.crypto.digests.SM3Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.util.Arrays; + +/** + * Created with IntelliJ IDEA. + * + * @author Shawffer + * @description: + * @date: 2022-07-19 + * @time: 17:35 + */ +public class Sm3Util { + + //如果报错就是以下代码 + static { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + public static byte[] hashPWtoBytes( byte[] rawpassword, String salt) throws NoSuchProviderException, NoSuchAlgorithmException { + String plaintext=new String(rawpassword)+salt; + byte[] plaintextBytes = plaintext.getBytes(); + MessageDigest digest = null; + digest = MessageDigest.getInstance("SM3", BouncyCastleProvider.PROVIDER_NAME); + return digest.digest(plaintextBytes); + } + + + public static String encrypt(String plaintext) throws NoSuchAlgorithmException, NoSuchProviderException { + byte[] plaintextBytes = plaintext.getBytes(); + MessageDigest digest = null; + digest = MessageDigest.getInstance("SM3", BouncyCastleProvider.PROVIDER_NAME); + return Base64.encodeBase64String(digest.digest(plaintextBytes)); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java new file mode 100644 index 00000000..a2b9cbec --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java @@ -0,0 +1,237 @@ +package com.jiuyv.sptccc.agile.common.utils.sm4; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.macs.CBCBlockCipherMac; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.paddings.BlockCipherPadding; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Created with IntelliJ IDEA. + * + * @author Shawffer + * @description: + * @date: 2022-07-19 + * @time: 17:04 + */ +public class Sm4Util { + public static final String ALGORITHM_NAME = "SM4"; + public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding"; + public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding"; + public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding"; + public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding"; + + /** + * SM4算法目前只支持128位(即密钥16字节) + */ + public static final int DEFAULT_KEY_SIZE = 128; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { + return generateKey(DEFAULT_KEY_SIZE); + } + + public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); + kg.init(keySize, new SecureRandom()); + return kg.generateKey().getEncoded(); + } + + public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidKeyException { + Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME); + return doMac("SM4-CMAC", keyObj, data); + } + + public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) { + org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8); + return doMac(mac, key, iv, data); + } + + /** + * 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC + * + * @param key + * @param iv + * @param data + * @return + */ + public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) { + SM4Engine engine = new SM4Engine(); + org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding()); + return doMac(mac, key, iv, data); + } + + /** + * @param key + * @param iv + * @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍 + * @param data + * @return + * @throws Exception + */ + public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception { + SM4Engine engine = new SM4Engine(); + if (padding == null) { + if (data.length % engine.getBlockSize() != 0) { + throw new Exception("if no padding, data length must be multiple of SM4 BlockSize"); + } + } + org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding); + return doMac(mac, key, iv, data); + } + + + private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) { + CipherParameters cipherParameters = new KeyParameter(key); + mac.init(new ParametersWithIV(cipherParameters, iv)); + mac.update(data, 0, data.length); + byte[] result = new byte[mac.getMacSize()]; + mac.doFinal(result, 0); + return result; + } + + private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidKeyException { + Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + mac.init(key); + mac.update(data); + return mac.doFinal(); + } + + private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key) + throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidKeyException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + cipher.init(mode, sm4Key); + return cipher; + } + + private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv) + throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + cipher.init(mode, sm4Key, ivParameterSpec); + return cipher; + } + + public static String encryptEcb(String key, String text) throws Exception { + return Base64.encodeBase64String(Sm4Util.encrypt_ECB_Padding(Hex.decodeHex(key),text.getBytes("UTF-8"))); + } + public static String decryptEcb(String key, String text) throws Exception { + return new String(Sm4Util.decrypt_ECB_Padding(Hex.decodeHex(key), Base64.decodeBase64((text)))); + } + public static String generateKeyHex() throws Exception { + return Hex.encodeHexString(Sm4Util.generateKey(DEFAULT_KEY_SIZE)); + } + + + public static void main(String[] args) throws Exception { + System.out.println(); + String plainText = "3wTFdgqPwEauKeK7JuUrIQ=="; + String sec_Key = "809bf66f0f47f2c4c7183cf5df6e3a92"; + + byte[] tmp = Sm4Util.decrypt_ECB_Padding(Hex.decodeHex(sec_Key), Base64.decodeBase64(plainText)); + System.out.println("A: " + new String(tmp)); + String tmp2 = Sm4Util.decryptEcb(sec_Key, plainText); + System.out.println("B: " + tmp2); + + System.out.println("---------"); + plainText = "123"; + String s1= Sm4Util.encryptEcb(sec_Key, plainText); + System.out.println("C="+s1); + + String s2= Sm4Util.decryptEcb(sec_Key, s1); + System.out.println("D="+s2); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java new file mode 100644 index 00000000..e0361c7c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java @@ -0,0 +1,155 @@ +package com.jiuyv.sptccc.agile.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author admin + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + setBeanFactory(beanFactory); + } + + public static void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + setContext(applicationContext); + } + + public static void setContext(ApplicationContext applicationContext) { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java new file mode 100644 index 00000000..ebdd1b62 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java @@ -0,0 +1,61 @@ +package com.jiuyv.sptccc.agile.common.utils.sql; + +import com.jiuyv.sptccc.agile.common.exception.UtilException; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author admin + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java new file mode 100644 index 00000000..675b1a64 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.jiuyv.sptccc.agile.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author admin + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java new file mode 100644 index 00000000..e6067cb5 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java @@ -0,0 +1,105 @@ +package com.jiuyv.sptccc.agile.common.utils.uuid; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * @author admin 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static String machineCode = "1"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } + + /** + * 产生一个数字传,比如给手机验证码 + * @param length + * @return + */ + public static String randomNumber(int length){ + if(length<4) {//最少4位 + length=4; + } + Random random = new Random(); + StringBuilder bld=new StringBuilder(); + for(int i=0;i +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java new file mode 100644 index 00000000..d49c4f37 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.jiuyv.sptccc.agile.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author admin + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java new file mode 100644 index 00000000..b20b06ee --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java @@ -0,0 +1,36 @@ +package com.jiuyv.sptccc.agile.common.xss; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author admin + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/CommonFeignApi.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/CommonFeignApi.java new file mode 100644 index 00000000..244366e3 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/CommonFeignApi.java @@ -0,0 +1,48 @@ +package com.jiuyv.sptccc.agile.feign.portal; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.dto.common.ResGetFileByteAddDTO; +import com.jiuyv.sptccc.agile.framework.config.FeignConfiguration; + + +/** + * 用户信息 + * @author zhouliang + * @date 2023-04-25 + */ +@FeignClient(name="portal-service", contextId = "DynamicImageFeignApi", url="${gateway.portalUrl}" + ,configuration = FeignConfiguration.class) +public interface CommonFeignApi { + + /** 获取图片*/ + @PostMapping("/"+Constants.IMAGE_URL_PATH+"/get") + R getImage(@RequestParam("uuid") String uuid); + + /** + * 下载文件 + * @param uuid + * @param offset 已下载文件大小 + * @return + */ + @PostMapping("/"+Constants.FILE_URL_PATH+"/download") + ResponseEntity downloadFile(@RequestParam("uuid") String uuid,@RequestParam("offset") long offset); + + /** + * 上传文件 + * @param file 文件 + * @param type 目录分类 + * @param filename 文件名称 + * @return + */ + @PostMapping("/"+Constants.FILE_URL_PATH+"/upload") + R uploadFile(@RequestParam("file") MultipartFile file,@RequestParam("uuid") String type + ,@RequestParam("filename") String filename); +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalLogininforFeignApi.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalLogininforFeignApi.java new file mode 100644 index 00000000..696fd22c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalLogininforFeignApi.java @@ -0,0 +1,25 @@ +package com.jiuyv.sptccc.agile.feign.portal; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.dto.logininfor.ReqPortalLogininforAddDTO; +import com.jiuyv.sptccc.agile.feign.portal.fallback.PortalLogininforFallback; +import com.jiuyv.sptccc.agile.framework.config.FeignConfiguration; + + +/** + * 系统访问记录 + * @author zhouliang + * @date 2023-04-25 + */ +@FeignClient(name="portal-service", contextId = "PortalLogininforFeignApi", url="${gateway.portalUrl}" + , fallbackFactory = PortalLogininforFallback.class,configuration = FeignConfiguration.class) +public interface PortalLogininforFeignApi{ + + @PostMapping("/logininfor/insert") + R insertLogininfor(@RequestBody ReqPortalLogininforAddDTO req); + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalPhoneMsgLogFeignApi.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalPhoneMsgLogFeignApi.java new file mode 100644 index 00000000..4b82625f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalPhoneMsgLogFeignApi.java @@ -0,0 +1,24 @@ +package com.jiuyv.sptccc.agile.feign.portal; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.dto.phoneMsgLog.ReqPortalPhoneMsgSendDTO; +import com.jiuyv.sptccc.agile.feign.portal.fallback.PortalPhoneMsgLogFallback; +import com.jiuyv.sptccc.agile.framework.config.FeignConfiguration; + + +/** + * 短信记录 + * @author zhouliang + * @date 2023-04-25 + */ +@FeignClient(name="portal-service", contextId = "PortalPhoneMsgLogFeignApi", url="${gateway.portalUrl}" +, fallbackFactory = PortalPhoneMsgLogFallback.class, configuration = FeignConfiguration.class) +public interface PortalPhoneMsgLogFeignApi { + + @PostMapping("/phoneMsgLog/send") + R sendPhoneMsg(@RequestBody ReqPortalPhoneMsgSendDTO msgLog); +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalUserFeignApi.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalUserFeignApi.java new file mode 100644 index 00000000..efe40627 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/PortalUserFeignApi.java @@ -0,0 +1,36 @@ +package com.jiuyv.sptccc.agile.feign.portal; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; +import com.jiuyv.sptccc.agile.feign.portal.dto.user.ReqPortalUserResetErrorDTO; +import com.jiuyv.sptccc.agile.feign.portal.fallback.PortalUserFallback; +import com.jiuyv.sptccc.agile.framework.config.FeignConfiguration; + + +/** + * 用户信息 + * @author zhouliang + * @date 2023-04-25 + */ +@FeignClient(name="portal-service", contextId = "PortalUserFeignApi", url="${gateway.portalUrl}" + , fallbackFactory = PortalUserFallback.class,configuration = FeignConfiguration.class) +public interface PortalUserFeignApi { + + @PostMapping("/portalUser/selectUserByUserName") + public R selectUserByUserName(@RequestParam("username")String username); + + /** + * 重置用户登陆状态等信息 + * @param req + * @return + */ + @PutMapping("/portalUser/resetError") + public R resetUserError(@RequestBody ReqPortalUserResetErrorDTO req); + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/TestFeign.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/TestFeign.java new file mode 100644 index 00000000..39de300c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/TestFeign.java @@ -0,0 +1,9 @@ +package com.jiuyv.sptccc.agile.feign.portal; + +import com.jiuyv.sptccc.agile.api.TestFeignApi; +import com.jiuyv.sptccc.agile.framework.config.FeignConfiguration; +import org.springframework.cloud.openfeign.FeignClient; + +@FeignClient(name="portal-service", url="${gateway.portalUrl}",path = "test00") +public interface TestFeign extends TestFeignApi { +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalLogininforBase.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalLogininforBase.java new file mode 100644 index 00000000..b3a78b67 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalLogininforBase.java @@ -0,0 +1,125 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto; + +import java.util.Date; + + +/** + * 系统访问记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalLogininforBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 访问id + */ + private Long infoId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录ip地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态 + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + + + + public Long getInfoId(){ + return infoId; + } + public void setInfoId(Long infoId){ + this.infoId = infoId; + } + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getIpaddr(){ + return ipaddr; + } + public void setIpaddr(String ipaddr){ + this.ipaddr = ipaddr; + } + + public String getLoginLocation(){ + return loginLocation; + } + public void setLoginLocation(String loginLocation){ + this.loginLocation = loginLocation; + } + + public String getBrowser(){ + return browser; + } + public void setBrowser(String browser){ + this.browser = browser; + } + + public String getOs(){ + return os; + } + public void setOs(String os){ + this.os = os; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getMsg(){ + return msg; + } + public void setMsg(String msg){ + this.msg = msg; + } + + public Date getLoginTime(){ + return loginTime; + } + public void setLoginTime(Date loginTime){ + this.loginTime = loginTime; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalOperLogBase.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalOperLogBase.java new file mode 100644 index 00000000..3e9b69ce --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalOperLogBase.java @@ -0,0 +1,209 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto; + +import java.util.Date; + + +/** + * 操作日志记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalOperLogBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 模块标题 + */ + private String title; + + /** + * 业务类型 + */ + private Integer businessType; + + /** + * 方法名称 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别 + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 主机地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态 + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + + + public Long getOperId(){ + return operId; + } + public void setOperId(Long operId){ + this.operId = operId; + } + + public String getTitle(){ + return title; + } + public void setTitle(String title){ + this.title = title; + } + + public Integer getBusinessType(){ + return businessType; + } + public void setBusinessType(Integer businessType){ + this.businessType = businessType; + } + + public String getMethod(){ + return method; + } + public void setMethod(String method){ + this.method = method; + } + + public String getRequestMethod(){ + return requestMethod; + } + public void setRequestMethod(String requestMethod){ + this.requestMethod = requestMethod; + } + + public Integer getOperatorType(){ + return operatorType; + } + public void setOperatorType(Integer operatorType){ + this.operatorType = operatorType; + } + + public String getOperName(){ + return operName; + } + public void setOperName(String operName){ + this.operName = operName; + } + + public String getDeptName(){ + return deptName; + } + public void setDeptName(String deptName){ + this.deptName = deptName; + } + + public String getOperUrl(){ + return operUrl; + } + public void setOperUrl(String operUrl){ + this.operUrl = operUrl; + } + + public String getOperIp(){ + return operIp; + } + public void setOperIp(String operIp){ + this.operIp = operIp; + } + + public String getOperLocation(){ + return operLocation; + } + public void setOperLocation(String operLocation){ + this.operLocation = operLocation; + } + + public String getOperParam(){ + return operParam; + } + public void setOperParam(String operParam){ + this.operParam = operParam; + } + + public String getJsonResult(){ + return jsonResult; + } + public void setJsonResult(String jsonResult){ + this.jsonResult = jsonResult; + } + + public Integer getStatus(){ + return status; + } + public void setStatus(Integer status){ + this.status = status; + } + + public String getErrorMsg(){ + return errorMsg; + } + public void setErrorMsg(String errorMsg){ + this.errorMsg = errorMsg; + } + + public Date getOperTime(){ + return operTime; + } + public void setOperTime(Date operTime){ + this.operTime = operTime; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalUserBase.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalUserBase.java new file mode 100644 index 00000000..7806f9d1 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/TblPortalUserBase.java @@ -0,0 +1,404 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto; + +import java.util.Date; + + +/** + * 用户信息表_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalUserBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 版本号 + */ + private Long versionNum; + + /** + * 随机码 + */ + private String recToken; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型 + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 头像地址 + */ + private String avatar; + + /** + * 密码 + */ + private String password; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 行业类别 + */ + private String industryCategory; + + /** + * 社会统一信用代码 + */ + private String socialCreditCode; + + /** + * 企业行业 + */ + private String enterpriseIndustry; + + /** + * 企业地址 + */ + private String enterpriseAddress; + + /** + * 帐号状态 + */ + private String status; + + /** + * 删除标志 + */ + private String delFlag; + + /** + * 最后登录ip + */ + private String loginIp; + + /**最后一次登录错误时间*/ + private Date lastLoginErrorTime; + + /** 登录错误计数 */ + private Integer loginErrorcount; + + /** 是否锁定(0、未锁定;1、锁定) */ + private Integer isLocked; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + + + public Long getUserId(){ + return userId; + } + public void setUserId(Long userId){ + this.userId = userId; + } + + public Long getVersionNum(){ + return versionNum; + } + public void setVersionNum(Long versionNum){ + this.versionNum = versionNum; + } + + public String getRecToken(){ + return recToken; + } + public void setRecToken(String recToken){ + this.recToken = recToken; + } + + public Long getDeptId(){ + return deptId; + } + public void setDeptId(Long deptId){ + this.deptId = deptId; + } + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getNickName(){ + return nickName; + } + public void setNickName(String nickName){ + this.nickName = nickName; + } + + public String getUserType(){ + return userType; + } + public void setUserType(String userType){ + this.userType = userType; + } + + public String getEmail(){ + return email; + } + public void setEmail(String email){ + this.email = email; + } + + public String getPhonenumber(){ + return phonenumber; + } + public void setPhonenumber(String phonenumber){ + this.phonenumber = phonenumber; + } + + public String getSex(){ + return sex; + } + public void setSex(String sex){ + this.sex = sex; + } + + public String getAvatar(){ + return avatar; + } + public void setAvatar(String avatar){ + this.avatar = avatar; + } + + public String getPassword(){ + return password; + } + public void setPassword(String password){ + this.password = password; + } + + /** + * Get企业名称 + */ + public String getEnterpriseName(){ + return enterpriseName; + } + /** + * Set企业名称 + */ + public void setEnterpriseName(String enterpriseName){ + this.enterpriseName = enterpriseName; + } + + /** + * Get行业类别 + */ + public String getIndustryCategory(){ + return industryCategory; + } + /** + * Set行业类别 + */ + public void setIndustryCategory(String industryCategory){ + this.industryCategory = industryCategory; + } + + /** + * Get社会统一信用代码 + */ + public String getSocialCreditCode(){ + return socialCreditCode; + } + /** + * Set社会统一信用代码 + */ + public void setSocialCreditCode(String socialCreditCode){ + this.socialCreditCode = socialCreditCode; + } + + /** + * Get企业行业 + */ + public String getEnterpriseIndustry(){ + return enterpriseIndustry; + } + /** + * Set企业行业 + */ + public void setEnterpriseIndustry(String enterpriseIndustry){ + this.enterpriseIndustry = enterpriseIndustry; + } + + /** + * Get企业地址 + */ + public String getEnterpriseAddress(){ + return enterpriseAddress; + } + /** + * Set企业地址 + */ + public void setEnterpriseAddress(String enterpriseAddress){ + this.enterpriseAddress = enterpriseAddress; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getDelFlag(){ + return delFlag; + } + public void setDelFlag(String delFlag){ + this.delFlag = delFlag; + } + + public String getLoginIp(){ + return loginIp; + } + public void setLoginIp(String loginIp){ + this.loginIp = loginIp; + } + + public Date getLoginDate(){ + return loginDate; + } + public void setLoginDate(Date loginDate){ + this.loginDate = loginDate; + } + + /** + * @return the lastLoginErrorTime + */ + public Date getLastLoginErrorTime() { + return lastLoginErrorTime; + } + /** + * @param lastLoginErrorTime the lastLoginErrorTime to set + */ + public void setLastLoginErrorTime(Date lastLoginErrorTime) { + this.lastLoginErrorTime = lastLoginErrorTime; + } + /** + * @return the loginErrorcount + */ + public Integer getLoginErrorcount() { + return loginErrorcount; + } + /** + * @param loginErrorcount the loginErrorcount to set + */ + public void setLoginErrorcount(Integer loginErrorcount) { + this.loginErrorcount = loginErrorcount; + } + /** + * @return the isLocked + */ + public Integer getIsLocked() { + return isLocked; + } + /** + * @param isLocked the isLocked to set + */ + public void setIsLocked(Integer isLocked) { + this.isLocked = isLocked; + } + public String getCreateBy(){ + return createBy; + } + public void setCreateBy(String createBy){ + this.createBy = createBy; + } + + public Date getCreateTime(){ + return createTime; + } + public void setCreateTime(Date createTime){ + this.createTime = createTime; + } + + public String getUpdateBy(){ + return updateBy; + } + public void setUpdateBy(String updateBy){ + this.updateBy = updateBy; + } + + public Date getUpdateTime(){ + return updateTime; + } + public void setUpdateTime(Date updateTime){ + this.updateTime = updateTime; + } + + public String getRemark(){ + return remark; + } + public void setRemark(String remark){ + this.remark = remark; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/common/ResGetFileByteAddDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/common/ResGetFileByteAddDTO.java new file mode 100644 index 00000000..986fded0 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/common/ResGetFileByteAddDTO.java @@ -0,0 +1,68 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto.common; + +/** + * 获取完整文件返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ResGetFileByteAddDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 文件名 + */ + private String filename; + + /** + * 文件字节 + */ + private byte[] file; + + /** + * 文件后缀类型 + */ + private String type; + + /** + * @return the filename + */ + public String getFilename() { + return filename; + } + + /** + * @param filename the filename to set + */ + public void setFilename(String filename) { + this.filename = filename; + } + + /** + * @return the file + */ + public byte[] getFile() { + return file; + } + + /** + * @param file the file to set + */ + public void setFile(byte[] file) { + this.file = file; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(String type) { + this.type = type; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/logininfor/ReqPortalLogininforAddDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/logininfor/ReqPortalLogininforAddDTO.java new file mode 100644 index 00000000..39495037 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/logininfor/ReqPortalLogininforAddDTO.java @@ -0,0 +1,95 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto.logininfor; + +/** + * 系统访问记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalLogininforAddDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录ip地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态 + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getIpaddr(){ + return ipaddr; + } + public void setIpaddr(String ipaddr){ + this.ipaddr = ipaddr; + } + + public String getLoginLocation(){ + return loginLocation; + } + public void setLoginLocation(String loginLocation){ + this.loginLocation = loginLocation; + } + + public String getBrowser(){ + return browser; + } + public void setBrowser(String browser){ + this.browser = browser; + } + + public String getOs(){ + return os; + } + public void setOs(String os){ + this.os = os; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getMsg(){ + return msg; + } + public void setMsg(String msg){ + this.msg = msg; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/phoneMsgLog/ReqPortalPhoneMsgSendDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/phoneMsgLog/ReqPortalPhoneMsgSendDTO.java new file mode 100644 index 00000000..bcea6b01 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/phoneMsgLog/ReqPortalPhoneMsgSendDTO.java @@ -0,0 +1,111 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto.phoneMsgLog; + +import java.util.ArrayList; +import java.util.List; + +/** + * 发送短信请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalPhoneMsgSendDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 手机号 + */ + private String phoneNumber; + + /** + * 消息模板编码(可以用模板,统一管理) + */ + private String msgTemplateCode; + + /** + * 消息内容(不用模板,完整消息) + */ + private String msgText; + + /** + * 消息类型 + */ + private String msgType; + + + /** + * 模板参数,按顺序替换的%s + */ + private List msgParams; + + /** + * @return the phoneNumber + */ + public String getPhoneNumber() { + return phoneNumber; + } + + /** + * @param phoneNumber the phoneNumber to set + */ + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + /** + * @return the msgTemplateCode + */ + public String getMsgTemplateCode() { + return msgTemplateCode; + } + + /** + * @param msgTemplateCode the msgTemplateCode to set + */ + public void setMsgTemplateCode(String msgTemplateCode) { + this.msgTemplateCode = msgTemplateCode; + } + + /** + * @return the msgText + */ + public String getMsgText() { + return msgText; + } + + /** + * @param msgText the msgText to set + */ + public void setMsgText(String msgText) { + this.msgText = msgText; + } + + /** + * @return the msgType + */ + public String getMsgType() { + return msgType; + } + + /** + * @param msgType the msgType to set + */ + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + /** + * @return the msgParams + */ + public List getMsgParams() { + return msgParams; + } + + /** + * @param msgParams the msgParams to set + */ + public void setMsgParams(List msgParams) { + this.msgParams = msgParams; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalOperLogAddDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalOperLogAddDTO.java new file mode 100644 index 00000000..ba1fb967 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalOperLogAddDTO.java @@ -0,0 +1,182 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto.user; + +import java.util.Date; + + +/** + * 操作日志记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalOperLogAddDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 模块标题 + */ + private String title; + + /** + * 业务类型 + */ + private Integer businessType; + + /** + * 方法名称 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别 + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 主机地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态 + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + public String getTitle(){ + return title; + } + public void setTitle(String title){ + this.title = title; + } + + public Integer getBusinessType(){ + return businessType; + } + public void setBusinessType(Integer businessType){ + this.businessType = businessType; + } + + public String getMethod(){ + return method; + } + public void setMethod(String method){ + this.method = method; + } + + public String getRequestMethod(){ + return requestMethod; + } + public void setRequestMethod(String requestMethod){ + this.requestMethod = requestMethod; + } + + public Integer getOperatorType(){ + return operatorType; + } + public void setOperatorType(Integer operatorType){ + this.operatorType = operatorType; + } + + public String getOperName(){ + return operName; + } + public void setOperName(String operName){ + this.operName = operName; + } + + public String getDeptName(){ + return deptName; + } + public void setDeptName(String deptName){ + this.deptName = deptName; + } + + public String getOperUrl(){ + return operUrl; + } + public void setOperUrl(String operUrl){ + this.operUrl = operUrl; + } + + public String getOperIp(){ + return operIp; + } + public void setOperIp(String operIp){ + this.operIp = operIp; + } + + public String getOperLocation(){ + return operLocation; + } + public void setOperLocation(String operLocation){ + this.operLocation = operLocation; + } + + public String getOperParam(){ + return operParam; + } + public void setOperParam(String operParam){ + this.operParam = operParam; + } + + public String getJsonResult(){ + return jsonResult; + } + public void setJsonResult(String jsonResult){ + this.jsonResult = jsonResult; + } + + public Integer getStatus(){ + return status; + } + public void setStatus(Integer status){ + this.status = status; + } + + public String getErrorMsg(){ + return errorMsg; + } + public void setErrorMsg(String errorMsg){ + this.errorMsg = errorMsg; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalUserResetErrorDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalUserResetErrorDTO.java new file mode 100644 index 00000000..d4055ade --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/dto/user/ReqPortalUserResetErrorDTO.java @@ -0,0 +1,122 @@ +package com.jiuyv.sptccc.agile.feign.portal.dto.user; + +import java.util.Date; + +/** + * 用户信息表_重置登陆错误请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalUserResetErrorDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 最后登录ip + */ + private String loginIp; + + + /**最后一次登录错误时间*/ + private Date lastLoginErrorTime; + + /** 登录错误计数 */ + private Integer loginErrorcount; + + /** 是否锁定(0、未锁定;1、锁定) */ + private Integer isLocked; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * @return the userId + */ + public Long getUserId() { + return userId; + } + + /** + * @param userId the userId to set + */ + public void setUserId(Long userId) { + this.userId = userId; + } + + /** + * @return the loginIp + */ + public String getLoginIp() { + return loginIp; + } + + /** + * @param loginIp the loginIp to set + */ + public void setLoginIp(String loginIp) { + this.loginIp = loginIp; + } + + /** + * @return the lastLoginErrorTime + */ + public Date getLastLoginErrorTime() { + return lastLoginErrorTime; + } + + /** + * @param lastLoginErrorTime the lastLoginErrorTime to set + */ + public void setLastLoginErrorTime(Date lastLoginErrorTime) { + this.lastLoginErrorTime = lastLoginErrorTime; + } + + /** + * @return the loginErrorcount + */ + public Integer getLoginErrorcount() { + return loginErrorcount; + } + + /** + * @param loginErrorcount the loginErrorcount to set + */ + public void setLoginErrorcount(Integer loginErrorcount) { + this.loginErrorcount = loginErrorcount; + } + + /** + * @return the isLocked + */ + public Integer getIsLocked() { + return isLocked; + } + + /** + * @param isLocked the isLocked to set + */ + public void setIsLocked(Integer isLocked) { + this.isLocked = isLocked; + } + + /** + * @return the loginDate + */ + public Date getLoginDate() { + return loginDate; + } + + /** + * @param loginDate the loginDate to set + */ + public void setLoginDate(Date loginDate) { + this.loginDate = loginDate; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalLogininforFallback.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalLogininforFallback.java new file mode 100644 index 00000000..61b19c8b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalLogininforFallback.java @@ -0,0 +1,31 @@ +package com.jiuyv.sptccc.agile.feign.portal.fallback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.PortalLogininforFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.logininfor.ReqPortalLogininforAddDTO; + + +/** + * 系统访问记录 + * @author zhouliang + * @date 2023-04-25 + */ +public class PortalLogininforFallback implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(PortalLogininforFallback.class); + + @Override + public PortalLogininforFeignApi create(Throwable throwable) + { + return new PortalLogininforFeignApi() + { + @Override + public R insertLogininfor(ReqPortalLogininforAddDTO req) { + return R.fail("记录登录日志失败"); + } + }; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalPhoneMsgLogFallback.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalPhoneMsgLogFallback.java new file mode 100644 index 00000000..7364d968 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalPhoneMsgLogFallback.java @@ -0,0 +1,32 @@ +package com.jiuyv.sptccc.agile.feign.portal.fallback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.PortalPhoneMsgLogFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.phoneMsgLog.ReqPortalPhoneMsgSendDTO; + + + +/** + * 短信信息 + * @author zhouliang + * @date 2023-04-25 + */ +public class PortalPhoneMsgLogFallback implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(PortalPhoneMsgLogFallback.class); + + @Override + public PortalPhoneMsgLogFeignApi create(Throwable throwable) + { + return new PortalPhoneMsgLogFeignApi() + { + @Override + public R sendPhoneMsg(ReqPortalPhoneMsgSendDTO msgLog) { + return R.fail("发送短信失败"); + } + }; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalUserFallback.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalUserFallback.java new file mode 100644 index 00000000..1606e2a5 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/feign/portal/fallback/PortalUserFallback.java @@ -0,0 +1,40 @@ +package com.jiuyv.sptccc.agile.feign.portal.fallback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; + +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.feign.portal.PortalUserFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; +import com.jiuyv.sptccc.agile.feign.portal.dto.user.ReqPortalUserResetErrorDTO; + + + +/** + * 用户信息表 + * @author zhouliang + * @date 2023-04-25 + */ +public class PortalUserFallback implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(PortalUserFallback.class); + + @Override + public PortalUserFeignApi create(Throwable throwable) + { + return new PortalUserFeignApi() + { + + @Override + public R selectUserByUserName(String username) { + return R.fail("获取用户信息失败"); + } + + @Override + public R resetUserError(ReqPortalUserResetErrorDTO req) { + return R.fail("更新用户信息失败"); + } + + }; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java new file mode 100644 index 00000000..38124446 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java @@ -0,0 +1,220 @@ +package com.jiuyv.sptccc.agile.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.enums.HttpMethod; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; + +/** + * 全局请求日志,这个日志就不记录到表里面了 + * @author zhouliang + * + */ +@Aspect +@Component +public class GlobalLogAspect +{ + private static final Logger log = LoggerFactory.getLogger(GlobalLogAspect.class); + + private static final ThreadLocal timeTreadLocal = new ThreadLocal<>(); + + //不输出任何日志 + private static final String LogIgnorePath = "@annotation(com.jiuyv.sptccc.agile.common.annotation.LogIgnore)"; + //返回不输出返回的数据 + private static final String LogSimpleResultPath = "@annotation(com.jiuyv.sptccc.agile.common.annotation.LogSimpleResult)"; + //扫描所有Controller + private static final String ControllerPath = "execution(* *..*.*Controller.*(..))"; + //不输出这个转发的 + private static final String InitBinderPath = "@annotation(org.springframework.web.bind.annotation.InitBinder)"; + + //请求Controller输出日志 + @Pointcut(ControllerPath+"&&!"+LogIgnorePath+"&&!"+InitBinderPath) + public void controllerAspect() { + } + //请求Controller不输出返回内容 + @Pointcut(LogSimpleResultPath) + public void resultSimpleAspect() { + } + + //请求日志记录 + @Before("controllerAspect()") + public void before(JoinPoint joinPoint) { + timeTreadLocal.set(System.currentTimeMillis()); + // 接收请求,记录完整请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //获取请求的request + HttpServletRequest request = attributes.getRequest(); + String methodName = getMethodName(joinPoint); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + String userName="none"; + try { + LoginUser loginuser = SecurityUtils.getLoginUser(); + if(loginuser!=null) { + userName=loginuser.getUserId()+"/"+loginuser.getUser().getUserName()+"/"+loginuser.getUser().getNickName(); + } + }catch (Exception e) { + //不报错 + } + String keyValue = setRequestValue(joinPoint,request.getMethod()); + log.info("{}-req:User={},Ip={},Uri={},Method={},ContentType={}",methodName,userName,ip,request.getRequestURI(),request.getMethod(),request.getContentType()); + log.info("{}-req:Params={}",methodName, keyValue); + } + //后置返回 + @AfterReturning(pointcut = "controllerAspect() && !resultSimpleAspect()",returning = "result") + public void doAfterReturning(JoinPoint joinPoint,Object result) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + String returnstr = null; + if(result!=null) { + try { + returnstr=JsonUtil.JsonMapper2().writeValueAsString(result); + } catch (JsonProcessingException e) { + returnstr="返回转换异常"; + } + } + log.info("{}-res:Success,Time={}s,Data={}",methodName, callTime, returnstr); + timeTreadLocal.remove(); + } + //后置返回(不输出返回内容) + @AfterReturning(pointcut = "controllerAspect() && resultSimpleAspect()",returning = "result") + public void doAfterReturningSimple(JoinPoint joinPoint,Object result) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + log.info("{}-res:Success,Time={}s,Data=omitting content...",methodName, callTime); + timeTreadLocal.remove(); + } + //后置异常返回 + @AfterThrowing(pointcut = "controllerAspect()", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + log.info("{}-res:Failure,Time={}s,Error={}",methodName, callTime,e.getMessage()); + timeTreadLocal.remove(); + } + + /** 获取方法名称*/ + private String getMethodName(JoinPoint joinPoint) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + //获取被拦截的方法 + Method method = methodSignature.getMethod(); + String className = method.getDeclaringClass().getCanonicalName(); + String methodName = className.substring(className.lastIndexOf(".")+1) + "." + method.getName(); + return methodName; + } + + /** + * 获取请求的参数 + */ + private String setRequestValue(JoinPoint joinPoint, String requestMethod) + { + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) + { + String params = argsArrayToString(joinPoint.getArgs()); + return params; + } + else + { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + return paramsMap.toString(); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + Object jsonObj = JsonUtil.JsonMapper2().writeValueAsString(o); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + if(value instanceof MultipartFile) { + return true; + } + } + return false; + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + if(entry.getValue() instanceof MultipartFile) { + return true; + } + } + return false; + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java new file mode 100644 index 00000000..b7cae806 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author admin + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.jiuyv.sptccc.agile.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java new file mode 100644 index 00000000..34741e09 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java @@ -0,0 +1,29 @@ +package com.jiuyv.sptccc.agile.framework.config; + +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; + +@EnableAsync +@Configuration +public class AsyncConfig { + + @Bean("taskExecutor") + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setKeepAliveSeconds(30); + executor.setThreadNamePrefix("datax-async-service-"); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAwaitTerminationSeconds(60); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/CaptchaConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/CaptchaConfig.java new file mode 100644 index 00000000..19b38b6a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author admin + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.jiuyv.sptccc.agile.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FeignConfiguration.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FeignConfiguration.java new file mode 100644 index 00000000..f1c0aa9d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FeignConfiguration.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUserSimple; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; + +import feign.RequestInterceptor; +import feign.RequestTemplate; + + +/** + * 统一放一个header + * @author zhouliang + * + */ +@Configuration +public class FeignConfiguration { + + @Bean + public RequestInterceptor requestInterceptor() { + return new FeignClientInterceptor(); + } + + private static class FeignClientInterceptor implements RequestInterceptor { + @Override + public void apply(RequestTemplate requestTemplate) { + //网关统一增加一个header标志,服务只认这个标志 + requestTemplate.header(Constants.FEIGN_CLIENT_FLAG, Constants.FEIGN_CLIENT_VAL); + try { + //有没有可能会丢失登陆信息 + LoginUser loginUser=SecurityUtils.getLoginUser(); + if(loginUser!=null) { + TblPortalUserBase user = loginUser.getUser(); + LoginUserSimple info=new LoginUserSimple(); + info.setUserId(user.getUserId()); + info.setUserName(user.getUserName()); + info.setNickName(user.getNickName()); + info.setDeptId(user.getDeptId()); + info.setBrowser(loginUser.getBrowser()); + info.setOs(loginUser.getOs()); + info.setIpaddr(loginUser.getIpaddr()); + info.setLoginLocation(loginUser.getLoginLocation()); + requestTemplate.header(Constants.USERINFO_KEY,JsonUtil.toJSONString(info)); + } + }catch (Exception e) { + //没有登陆信息 + } + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java new file mode 100644 index 00000000..2b51f6ce --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java @@ -0,0 +1,59 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.jiuyv.sptccc.agile.common.filter.RepeatableFilter; +import com.jiuyv.sptccc.agile.common.filter.XssFilter; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author admin + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/KaptchaTextCreator.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/KaptchaTextCreator.java new file mode 100644 index 00000000..5f08eec0 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/KaptchaTextCreator.java @@ -0,0 +1,75 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author admin + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = (int) Math.round(Math.random() * 2); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if (!(x == 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else if (randomoperands == 2) + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ResourcesConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ResourcesConfig.java new file mode 100644 index 00000000..6cc434ab --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/ResourcesConfig.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.jiuyv.sptccc.agile.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author admin + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/SecurityConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/SecurityConfig.java new file mode 100644 index 00000000..133e74d2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/SecurityConfig.java @@ -0,0 +1,209 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.framework.config.properties.PermitAllUrlProperties; +import com.jiuyv.sptccc.agile.framework.security.filter.JwtAuthenticationFilter; +import com.jiuyv.sptccc.agile.framework.security.filter.JwtAuthenticationTokenFilter; +import com.jiuyv.sptccc.agile.framework.security.filter.OperationTokenFilter; +import com.jiuyv.sptccc.agile.framework.security.handle.AuthenticationEntryPointImpl; +import com.jiuyv.sptccc.agile.framework.security.handle.LogoutSuccessHandlerImpl; +import com.jiuyv.sptccc.agile.framework.security.handle.SessionRegistryLogoutHandler; +import com.jiuyv.sptccc.agile.framework.security.handle.UserExpiredSessionStrategy; +import com.jiuyv.sptccc.agile.framework.security.handle.UserInvalidSessionStrategy; + +/** + * spring security配置 + * + * @author admin + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + @Autowired + private OperationTokenFilter operationTokenFilter; + @Autowired + private UserInvalidSessionStrategy userInvalidSessionStrategy; + + @Autowired + private UserExpiredSessionStrategy userExpiredSessionStrategy; + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandlerImpl; + + + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + // 注解标记允许匿名访问的url + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + + httpSecurity + // TODO 取消禁用 + .csrf().disable() + .addFilterAfter(operationTokenFilter, FilterSecurityInterceptor.class) + // 认证失败处理类 update by 20221024 扫描后优化 + .exceptionHandling() + .authenticationEntryPoint(unauthorizedHandler) + .and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + .antMatchers("/**login.**","/login","/captchaImage", "/captchaGet","/captchaCheck").anonymous() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/static/**", "/favicon.**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable() + .and().sessionManagement().invalidSessionStrategy(userInvalidSessionStrategy) + .maximumSessions(1)//限制1个用户 + .maxSessionsPreventsLogin(false).expiredSessionStrategy(userExpiredSessionStrategy) + .sessionRegistry(sessionRegistry()) + ; + // 添加Logout filter + httpSecurity.logout() + .deleteCookies(header,Constants.SESSION_NAME).invalidateHttpSession(true) //测试时先注释方便看 + .addLogoutHandler(sessionRegistryLogoutHandler()) + .logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandlerImpl); + // 添加JWT filter + httpSecurity.addFilter(jwtAuthenticationFilter()).addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + @Override + public void configure(WebSecurity web) throws Exception { + //强制跳过所有静态资源,这样权限不管静态资源,不然没法正确提示 + web.ignoring().antMatchers("/static/**"); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } + + /** + * 缓存实现 + * @return + */ + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + /** + * 登出时做一些处理 + * @return + */ + @Bean + public SessionRegistryLogoutHandler sessionRegistryLogoutHandler() { + return new SessionRegistryLogoutHandler(sessionRegistry()); + } + + /** + * 登录实现 + * @return + */ + @Bean + public JwtAuthenticationFilter jwtAuthenticationFilter() { + return new JwtAuthenticationFilter(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java new file mode 100644 index 00000000..c2acf79c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java @@ -0,0 +1,44 @@ +package com.jiuyv.sptccc.agile.framework.config.caffeine; + +import java.io.Serializable; + +/** + * 定义对象记录插入时间,如果需要动态时间的,使用这个封装类 + * @author Administrator + * + * @param + */ +public class CacheTimestampedValue implements Serializable{ + private static final long serialVersionUID = 1L; + + private V value; + private long timestamp; + + public CacheTimestampedValue(V value) { + this.value = value; + this.timestamp = System.currentTimeMillis(); + } + + + public void setValue(V value) { + this.value = value; + } + public V getValue() { + return value; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + public long getTimestamp() { + return timestamp; + } + + /** + * 获取计算当前时间和记录时间秒差额 + * @return + */ + public long getSecondDifference() { + return (System.currentTimeMillis()-timestamp)/1000; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java new file mode 100644 index 00000000..9181526d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java @@ -0,0 +1,66 @@ +package com.jiuyv.sptccc.agile.framework.config.caffeine; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.constant.Constants; + +/** + * caffeine内存 + * @author Administrator + */ +@Configuration +public class CaffeineCacheConfig { + + /** + * 创建一些常规缓存,设置失效时间保证有效的释放内存 + * 由于只能实例上设置时间,勉强用如下方法实现动态时间 + * (动态时间:把失效时间设置略大于动态时间,再用这个类[CacheTimestampedValue]进行存取/计算) + * 比如 菜单、权限 + * @return + */ + @Bean + @Qualifier(value = "commonCacheManager") + public CacheManager commonCacheManager() { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + List caches = new ArrayList<>(); + + //设置死数量也有一些问题,并发数必须<=maximumSize,并发数高了前面的信息就失效了 + //某些场景键值数量自行按需设置 + + + //5s缓存,主要用于防重提交, + caches.add(new CaffeineCache(CacheNameConstants.CACHE_REPEAT_SUBMIT_5S, + Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build())); + //30s缓存,主要用于防重提交(有些方法周期长5s不够的话) + caches.add(new CaffeineCache(CacheNameConstants.CACHE_REPEAT_SUBMIT_30S, + Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build())); + //主要用于验证码 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_CAPTCHA_CODE, + Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build())); + //用户数据加密密钥 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_USER_DATA_SECRET_KEY, + Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build())); + //1天缓存 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_ONEDAY, + Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).maximumSize(10000).build())); + //永久缓存,放置一些系统字典、配置之类的 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_SYS_DICT, + Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.DAYS).maximumSize(2000).build())); + cacheManager.setCaches(caches); + return cacheManager; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/properties/PermitAllUrlProperties.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 00000000..f43547fd --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,79 @@ +package com.jiuyv.sptccc.agile.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition; +import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import com.jiuyv.sptccc.agile.common.annotation.Anonymous; +import org.springframework.web.util.pattern.PathPattern; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author admin + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + mapping.getHandlerMethods().forEach((info,handlerMethod) ->{ + // 获取方法和类上的注解 替代path variable 为 * + if (AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class) == null && + AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class) == null) { + return; + } + PatternsRequestCondition patternsCondition = info.getPatternsCondition(); + if (patternsCondition != null) { + patternsCondition.getPatterns().forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))); + return; + } + PathPatternsRequestCondition pathPatternsCondition = info.getPathPatternsCondition(); + if (pathPatternsCondition != null) { + pathPatternsCondition.getPatterns().forEach(pattern -> + urls.add(RegExUtils.replaceAll(pattern.getPatternString(), PATTERN, ASTERISK))); + } + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/RepeatSubmitInterceptor.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 00000000..f56b6be0 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,58 @@ +package com.jiuyv.sptccc.agile.framework.interceptor; + +import java.lang.reflect.Method; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import com.jiuyv.sptccc.agile.common.annotation.RepeatSubmit; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author admin + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JsonUtil.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/impl/SameUrlDataInterceptor.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 00000000..705a5577 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,67 @@ +package com.jiuyv.sptccc.agile.framework.interceptor.impl; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.annotation.RepeatSubmit; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.filter.RepeatedlyRequestWrapper; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.http.HttpHelper; +import com.jiuyv.sptccc.agile.framework.config.caffeine.CacheTimestampedValue; +import com.jiuyv.sptccc.agile.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author admin + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { + + @Autowired + private RedisCache redisCache; + + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) { + nowParams = JsonUtil.toJSONString(request.getParameterMap()); + } + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(Constants.SESSION_NAME)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = url + submitKey; + int interval=annotation.interval(); + + String cacheNameKey=null; + if(interval>5) { + cacheNameKey=CacheNameConstants.CACHE_REPEAT_SUBMIT_30S; + }else { + cacheNameKey=CacheNameConstants.CACHE_REPEAT_SUBMIT_5S; + } + CacheTimestampedValue sessionObj = redisCache.getTimeValueOfCacheName(cacheNameKey,cacheRepeatKey); + if (sessionObj != null && sessionObj.getSecondDifference() { + + private static final Logger logger = LoggerFactory.getLogger(AuthenticationFailureListener.class); + @Autowired + private PortalUserFeignApi userService; + + @Override + public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent authenticationFailureBadCredentialsEvent) { + Date thisErrorLoginTime = null;// 修改的本次登录错误时间 + Integer islocked = 0;// 获取是否锁定状态 + + String account = authenticationFailureBadCredentialsEvent.getAuthentication().getPrincipal().toString(); + R userRes = userService.selectUserByUserName(account); + if (userRes.getData() != null) { + ReqPortalUserResetErrorDTO user=new ReqPortalUserResetErrorDTO(); + user.setUserId(userRes.getData().getUserId()); + if (user.getIsLocked() == null) { + user.setIsLocked(0); + } else { + islocked = user.getIsLocked(); + } + + if (user.getLoginErrorcount() == null) { + user.setLoginErrorcount(0); + } + + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String datestr = format.format(date); + try { + thisErrorLoginTime = format.parse(datestr); + } catch (ParseException e) { + e.printStackTrace(); + } + + if (islocked == 1) {// 账户被锁定 // 被锁定是登录错误次数一定是5,所以只判断一次 + Date lastLoginErrorTime = null; // 最后一次登录错误时间 + Long timeSlot = 0L; + if (user.getLastLoginErrorTime() == null) { + lastLoginErrorTime = thisErrorLoginTime; + } else { + lastLoginErrorTime = user.getLastLoginErrorTime(); + if(null != thisErrorLoginTime) { + timeSlot = thisErrorLoginTime.getTime() - lastLoginErrorTime.getTime(); + } + } + + if (timeSlot < 1800000) {// 判断最后锁定时间,30分钟之内继续锁定 + logger.error("您的账户已被锁定,请" + (30 - Math.ceil((double) timeSlot / 60000)) + "分钟之后再次尝试"); + throw new BadCredentialsException("您的账户已被锁定,请" + (30 - Math.ceil((double) timeSlot / 60000)) + "分钟之后再次尝试"); + } else {// 判断最后锁定时间,30分钟之后仍是错误,继续锁定30分钟 + user.setLastLoginErrorTime(thisErrorLoginTime); + userService.resetUserError(user); + logger.error("账户或密码错误,您的账户已被锁定,请30分钟之后再次尝试登录"); + throw new BadCredentialsException("账户或密码错误,您的账户已被锁定,请30分钟之后再次尝试登录"); + } + } else if (user.getLoginErrorcount() == 4) {// 账户第五次登录失败 ,此时登录错误次数增加至5,以后错误仍是5,不再递增 + user.setLoginErrorcount(5); + user.setIsLocked(1); + user.setLastLoginErrorTime(thisErrorLoginTime); + userService.resetUserError(user);// 修改用户 + logger.error("您的账户已被锁定,请30分钟之后再次尝试登录"); + throw new BadCredentialsException("您的账户已被锁定,请30分钟之后再次尝试登录"); + } else {// 账户前四次登录失败 + user.setLoginErrorcount(user.getLoginErrorcount() + 1); + user.setLastLoginErrorTime(thisErrorLoginTime); + userService.resetUserError(user);// 修改用户 + logger.error("账户或密码错误,您还有" + (5 - user.getLoginErrorcount()) + "次登录机会"); + throw new BadCredentialsException("账户或密码错误,您还有" + (5 - user.getLoginErrorcount()) + "次登录机会"); + } + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java new file mode 100644 index 00000000..2dcfbb24 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.jiuyv.sptccc.agile.common.utils.Threads; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author admin + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java new file mode 100644 index 00000000..07c681b5 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.jiuyv.sptccc.agile.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author admin + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java new file mode 100644 index 00000000..3d63cc4c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,130 @@ +package com.jiuyv.sptccc.agile.framework.manager.factory; + +import java.util.TimerTask; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.utils.LogUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.AddressUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; +import com.jiuyv.sptccc.agile.feign.portal.PortalLogininforFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.logininfor.ReqPortalLogininforAddDTO; + +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author admin + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + ReqPortalLogininforAddDTO logininfor = new ReqPortalLogininforAddDTO(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(PortalLogininforFeignApi.class).insertLogininfor(logininfor); + } + }; + } + public static TimerTask recordLogininforV2(final HttpServletRequest request, final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(request); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + ReqPortalLogininforAddDTO logininfor = new ReqPortalLogininforAddDTO(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(PortalLogininforFeignApi.class).insertLogininfor(logininfor); + } + }; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadCaptchaException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadCaptchaException.java new file mode 100644 index 00000000..ac08de5c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadCaptchaException.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.framework.security.exception; + +import org.springframework.security.core.AuthenticationException; + +/** + * 验证码错误异常类 + * @author zhouliang + */ +public class BadCaptchaException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a BadCredentialsException with the specified message. + * @param msg the detail message + */ + public BadCaptchaException(String msg) { + super(msg); + } + + /** + * Constructs a BadCredentialsException with the specified message and + * root cause. + * @param msg the detail message + * @param cause root cause + */ + public BadCaptchaException(String msg, Throwable cause) { + super(msg, cause); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadUserSecretKeyException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadUserSecretKeyException.java new file mode 100644 index 00000000..524bf718 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/BadUserSecretKeyException.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.framework.security.exception; + +import org.springframework.security.core.AuthenticationException; + +/** + * 密钥错误异常类 + * @author zhouliang + */ +public class BadUserSecretKeyException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a BadCredentialsException with the specified message. + * @param msg the detail message + */ + public BadUserSecretKeyException(String msg) { + super(msg); + } + + /** + * Constructs a BadCredentialsException with the specified message and + * root cause. + * @param msg the detail message + * @param cause root cause + */ + public BadUserSecretKeyException(String msg, Throwable cause) { + super(msg, cause); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/UserIllegalException.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/UserIllegalException.java new file mode 100644 index 00000000..85afcff9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/exception/UserIllegalException.java @@ -0,0 +1,31 @@ +package com.jiuyv.sptccc.agile.framework.security.exception; + +import org.springframework.security.core.AuthenticationException; + +/** + * 用户登陆信息非法 + * @author zhouliang + * + */ +public class UserIllegalException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a BadCredentialsException with the specified message. + * @param msg the detail message + */ + public UserIllegalException(String msg) { + super(msg); + } + + /** + * Constructs a BadCredentialsException with the specified message and + * root cause. + * @param msg the detail message + * @param cause root cause + */ + public UserIllegalException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFailureHandler.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFailureHandler.java new file mode 100644 index 00000000..41b335e4 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFailureHandler.java @@ -0,0 +1,59 @@ +package com.jiuyv.sptccc.agile.framework.security.filter; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.framework.security.exception.BadCaptchaException; +import com.jiuyv.sptccc.agile.framework.security.exception.BadUserSecretKeyException; +import com.jiuyv.sptccc.agile.framework.security.exception.UserIllegalException; + +/** + * 登录校验异常处理 + * @author zhouliang + * + */ +@Component +public class JwtAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response, + AuthenticationException e)throws IOException, ServletException { + int code=HttpStatus.INTERNAL_SERVER_ERROR.value(); + String msg = MessageUtils.message("user.not.exists"); + //验证码错误 + if (e instanceof BadCaptchaException) { + msg=MessageUtils.message("user.jcaptcha.error"); + } + //秘钥错误 + else if (e instanceof BadUserSecretKeyException) { + msg=MessageUtils.message("user.secret.key.error"); + } + //用户信息不全 + else if (e instanceof UserIllegalException) { + msg=MessageUtils.message("user.login.error"); + } + //用户密码校验不匹配 + else if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) { + msg=e.getMessage(); + if(StringUtils.isBlank(msg)) { + msg=MessageUtils.message("user.password.not.match"); + } + } + ServletUtils.renderString(response, JsonUtil.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFilter.java new file mode 100644 index 00000000..6063a009 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationFilter.java @@ -0,0 +1,144 @@ +package com.jiuyv.sptccc.agile.framework.security.filter; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.session.SessionInformation; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.web.WebAttributes; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.jiuyv.sptccc.agile.common.config.ConsoleOprTokenProperties; +import com.jiuyv.sptccc.agile.common.config.ConsoleTokenProperties; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginBody; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.http.CookieUtil; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; +import com.jiuyv.sptccc.agile.framework.security.exception.UserIllegalException; +import com.jiuyv.sptccc.agile.framework.security.model.TokenNode; +import com.jiuyv.sptccc.agile.framework.web.service.SysLoginService; +import com.jiuyv.sptccc.agile.framework.web.service.TokenService; + + +/** + * 登陆校验等 + * @author zhouliang + * + */ +public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + @Override + @Autowired + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + super.setAuthenticationManager(authenticationManager); + } + + @Override + @Autowired + public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) { + super.setAuthenticationFailureHandler(failureHandler); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException { + LoginBody loginBody=null; + try { + loginBody = JsonUtil.JsonMapper().readValue(request.getReader(), LoginBody.class); + } catch (IOException e) { + loginBody=new LoginBody(); + } + if(StringUtils.isBlank(loginBody.getUsername()) || StringUtils.isBlank(loginBody.getPassword()) + || StringUtils.isBlank(loginBody.getCode()) || StringUtils.isBlank(loginBody.getUuid())) { + throw new UserIllegalException("Illegal login information"); + } + SysLoginService loginService=SpringUtils.getBean(SysLoginService.class); + return loginService.login(this.getAuthenticationManager(),loginBody); + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) + throws IOException, ServletException { + super.unsuccessfulAuthentication(request, response, failed); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, + FilterChain chain, Authentication authResult) + throws IOException, ServletException { + LoginUser loginUser = (LoginUser) authResult.getPrincipal(); + HttpSession session = request.getSession(false); + if (session != null) { + session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); + } + + TokenService tokenService=SpringUtils.getBean(TokenService.class); + RedisCache redisCache=SpringUtils.getBean(RedisCache.class); + SessionRegistry sessionRegistry=SpringUtils.getBean(SessionRegistry.class); + ConsoleTokenProperties consoleTokenProperties=SpringUtils.getBean(ConsoleTokenProperties.class); + ConsoleOprTokenProperties oprTokenProperties=SpringUtils.getBean(ConsoleOprTokenProperties.class); + + List sessionsInfo = sessionRegistry.getAllSessions(authResult.getPrincipal(), true); + //将旧的session失效 + if (!sessionsInfo.isEmpty()) { + for (SessionInformation sessionInformation : sessionsInfo) { + try { +// System.out.println("重复登陆--"+username+"--"+sessionInformation.getSessionId()); + sessionInformation.expireNow(); + }catch (Exception e) { + //跳过 + } + } + } + String jwttoken = tokenService.createToken(request,loginUser); + + SecurityContextHolder.getContext().setAuthentication(authResult); + + //注册一下顺便用 + sessionRegistry.registerNewSession(loginUser.getToken(), authResult.getPrincipal()); + + //保留这个,里面判断ip + CookieUtil.setCookie(request, response, consoleTokenProperties.getHeader(), jwttoken + , consoleTokenProperties.getExpireTime()*60); + try { + //返回数据加密用的秘钥 + String sm4KeyPage = Sm4Util.generateKeyHex();//秘钥的秘钥 + //随机生成密钥 + String dataSmEncryptKey = Sm4Util.generateKeyHex(); + String newDataSmKey= Sm4Util.encryptEcb(sm4KeyPage, dataSmEncryptKey);//数据传输秘钥 + response.setHeader("secretKeyPage", sm4KeyPage); + response.setHeader("dataSecretKey", newDataSmKey); + redisCache.setValueOfCacheName(CacheNameConstants.CACHE_USER_DATA_SECRET_KEY, loginUser.getUserId().toString(), dataSmEncryptKey); + } catch (Exception e) { + logger.error(e); + } + + try { + // 第一次加载 主页 分配TOKEN + ImmutablePair pair = oprTokenProperties.calculateNewToken(request); + //操作token,是否要时间限制 + response.setHeader(oprTokenProperties.getHeader(), pair.left.getEncryptToken()); + response.setHeader(oprTokenProperties.getHeaderKey(), pair.left.getHexSM4Key()); + } catch (Exception e1) { + logger.error(e1); + } + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationTokenFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 00000000..e0b0932b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,112 @@ +package com.jiuyv.sptccc.agile.framework.security.filter; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.jiuyv.sptccc.agile.common.config.ConsoleTokenProperties; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.http.CookieUtil; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; +import com.jiuyv.sptccc.agile.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author admin + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + @Autowired + private ConsoleTokenProperties consoleTokenProperties; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + + String path = request.getRequestURI(); + + //这里主要是统一对url做一些处理 + boolean isAnonymous=false; + boolean isPermitAll=false; + // 获取当前请求的FilterSecurityInterceptor + FilterSecurityInterceptor interceptor = getInterceptor(request); + if(interceptor!=null && interceptor.getSecurityMetadataSource() instanceof ExpressionBasedFilterInvocationSecurityMetadataSource) { + ExpressionBasedFilterInvocationSecurityMetadataSource fms=(ExpressionBasedFilterInvocationSecurityMetadataSource) interceptor.getSecurityMetadataSource(); + FilterInvocation fli=new FilterInvocation(request.getServletPath(),request.getMethod()); + Collection cfgs = fms.getAttributes(fli); + if(cfgs!=null&&!cfgs.isEmpty()) { + if(cfgs.stream().anyMatch( x-> "permitAll".equals(x.toString()))) { + isPermitAll=true; + } + if(cfgs.stream().anyMatch( x-> "anonymous".equals(x.toString()))) { + isAnonymous=true; + } + } + } +// System.out.println("查看一下url属性--"+path+",isAnonymous="+isAnonymous+",isPermitAll="+isPermitAll); + if(!isAnonymous && !isPermitAll) { + //校验当前请求ip和jwt里面的ip是否一致 + //这里从token校验不从缓存校验,可以有效区分非法请求 + String jwttoken=CookieUtil.getCookieVal(request, consoleTokenProperties.getHeader()); + if(StringUtils.isNotEmpty(jwttoken)) { + String ip = (String) tokenService.parseToken(jwttoken).get(Constants.IP_ADDR_KEY); + String currIp = IpUtils.getIpAddr(request); + if(!currIp.equals(ip)) { + logger.error("Request for IP is illegal>>"+ip+"--"+currIp); + throw new ServiceException("Request for IP is illegal"); + } + }else { + //应该不可能有为空,全局携带吧 + Authentication auth =SecurityUtils.getAuthentication(); + if(auth!=null) { + throw new ServiceException("Request for IP is illegal"); + } + } + } + + chain.doFilter(request, response); + } + + + private FilterSecurityInterceptor getInterceptor(HttpServletRequest request) { + FilterSecurityInterceptor result = null; + FilterChainProxy filterChainProxy = SpringUtils.getBean(FilterChainProxy.class); + List filterChains = filterChainProxy.getFilterChains(); + for (SecurityFilterChain chain : filterChains) { + if (chain.matches(request)) { + for (Filter filter : chain.getFilters()) { + if (filter instanceof FilterSecurityInterceptor) { + result = (FilterSecurityInterceptor) filter; + } + } + } + } + return result; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/OperationTokenFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/OperationTokenFilter.java new file mode 100644 index 00000000..595a8169 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/OperationTokenFilter.java @@ -0,0 +1,140 @@ +package com.jiuyv.sptccc.agile.framework.security.filter; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +import javax.annotation.PostConstruct; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import com.jiuyv.sptccc.agile.common.config.ConsoleOprTokenProperties; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.framework.security.model.OperationTokenSet; +import com.jiuyv.sptccc.agile.framework.security.model.TokenNode; + +@Component +public class OperationTokenFilter implements Filter { + public static final Logger LOGGER = LoggerFactory.getLogger(OperationTokenFilter.class); + + @Autowired + private ConsoleOprTokenProperties oprTokenProperties; + + private List expectedSkipUrls = new LinkedList<>(); + + @PostConstruct + public void init() { + oprTokenProperties.getSkipUrls().forEach(url -> expectedSkipUrls.add(Pattern.compile(url))); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + private boolean isMatchCheckUrl(String uri) { + if (uri.contains("login")) { + return false; + } + for (Pattern expectedSkipUrl : expectedSkipUrls) { + if (expectedSkipUrl.matcher(uri).find()) { + return false; + } + } + return true; + } + + /** + * 提取header头 + * token = SM4加密后token + * key = hexSM4Key + *

+ * 查询和上送一致的token ,一致则更新token + * 不一致尝试查询 旧token , 并对旧token次数减少 + * + * @param request + * @param response + * @param chain + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletRequest = (HttpServletRequest) request; + HttpServletResponse servletResponse = (HttpServletResponse) response; + + String uri = servletRequest.getRequestURI(); + Boolean isPostMethod = StringUtils.equalsIgnoreCase("POST", servletRequest.getMethod()); + //uri match + if (isPostMethod && isMatchCheckUrl(uri)) { + String uploadEncryptToken = servletRequest.getHeader(oprTokenProperties.getHeader()); + String hexSM4Key = servletRequest.getHeader(oprTokenProperties.getHeaderKey()); + if (null == uploadEncryptToken) { + LOGGER.error("请求报文header缺失operationToken域,uri:{}", uri); + throw new IOException("请求报文header缺失operationToken域,uri:" + uri); + } + Object tmp = servletRequest.getSession().getAttribute(oprTokenProperties.getHeaderVal()); + Assert.notNull(tmp, "session not find operationTokenSet,please re-login"); + String tokenSetStr = servletRequest.getSession().getAttribute(oprTokenProperties.getHeaderVal()).toString(); + OperationTokenSet set = JsonUtil.json2Bean(tokenSetStr, OperationTokenSet.class); + if (null == set) { + LOGGER.error("会话失效,查询操作员令牌失败,uri:{}", uri); + throw new IOException("会话失效,查询操作员令牌失败,请重新登录"); + } + //sm4 解密 获取 token + try { + String uploadEntryptToken = servletRequest.getHeader(oprTokenProperties.getHeader()); + String newToken; + //本地加密后token 和上送token 比对 + TokenNode curNode = set.getCurNode(); + if (StringUtils.equals(curNode.getEncryptToken(), uploadEncryptToken)) { + //token 一致 生成新token + ImmutablePair pair = oprTokenProperties.calculateNewToken(servletRequest); + newToken = pair.left.getToken(); + LOGGER.info("sessionId:[{}] set new operationToken:[{}]", servletRequest.getSession().getId(), newToken); + } else { + //不一致 + //检查老 token + TokenNode oldNode = set.getTokenNodeMap().get(uploadEntryptToken); + if (null == oldNode) { + LOGGER.error("未查询到匹配的操作令牌,token:{},uri:{}", uploadEntryptToken, uri); + throw new IOException("未查询到匹配的操作令牌,业务拒绝受理"); + } + + //老令牌 使用次数-1 + int newTimes = oldNode.getTimes() - 1; + if (0 == newTimes) { + set.getTokenNodeMap().remove(uploadEntryptToken); + } else { + oldNode.minusTime(); + } + } + servletResponse.setHeader(oprTokenProperties.getHeader(), set.getCurNode().getEncryptToken()); + servletResponse.setHeader(oprTokenProperties.getHeaderKey(), set.getCurNode().getHexSM4Key()); + } catch (Exception e) { + LOGGER.error("检查operationToken失败,uri:{}", uri); + throw new IOException("检查operationToken失败", e); + } + } + chain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } +}; \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/RefererFilter.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/RefererFilter.java new file mode 100644 index 00000000..15ab1183 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/filter/RefererFilter.java @@ -0,0 +1,105 @@ +package com.jiuyv.sptccc.agile.framework.security.filter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; + +@Component +@Configuration +@WebFilter(filterName = "refererFilter", urlPatterns = "/*") +public class RefererFilter implements Filter { + public static final Logger logger = LoggerFactory.getLogger(RefererFilter.class); + + /** + * 忽略的URL + */ + @Value("${security.csrf.excludes}") + private String excludes; + + /** + * 放行指定的url + */ + @Value("${security.csrf.refererUrl}") + private String refererUrl; + + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // TODO Auto-generated method stub + // this.filterConfig = filterConfig; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + boolean enable = true; + logger.info("是否启用Referer 跨站点拦截器>>{}" , enable); + HttpServletRequest request = (HttpServletRequest) servletRequest; + + // 不启用或者已忽略的URL不拦截 + if (!enable || isExcludeUrl(request.getServletPath())) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + String referer = request.getHeader("Referer"); + String serverName = request.getServerName(); + logger.info("Referer过滤器 => 服务器:{} => 当前域名:{}", serverName, referer); + // 做特殊处理,放行特定的referer + List refererUrls = Arrays.asList(refererUrl.split(",")); + logger.info("放行的域名是>>{}" , JsonUtil.toJSONString(refererUrls)); + if(refererUrls.contains(referer)) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + // 判断是否存在外链请求本站 + if (null != referer && referer.indexOf(serverName) < 0) { + logger.error("Referer过滤器 => 服务器:{} => 当前域名:{}", serverName, referer); + servletResponse.setContentType("text/html; charset=utf-8"); + servletResponse.getWriter().write("系统不支持当前域名的访问!"); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + @Override + public void destroy() { + // TODO Auto-generated method stub + // this.filterConfig = null; + } + + /** + * 判断是否为忽略的URL + * + * @param urlPath + * URL路径 + * @return true-忽略,false-过滤 + */ + private boolean isExcludeUrl(String url) { + if (excludes == null || excludes.isEmpty()) { + return false; + } + List urls = Arrays.asList(excludes.split(",")); + return urls.stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(url)) + .anyMatch(Matcher::find); + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/AuthenticationEntryPointImpl.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 00000000..b0a52c17 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,38 @@ +package com.jiuyv.sptccc.agile.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author admin + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + //System.out.println("X1--"+request.getRequestURI()); + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JsonUtil.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/LogoutSuccessHandlerImpl.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 00000000..29dbfd31 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,37 @@ +package com.jiuyv.sptccc.agile.framework.security.handle; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; + +/** + * 自定义退出处理类 返回成功 + * + * @author admin + */ +@Component +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + ServletUtils.renderString(response, JsonUtil.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功"))); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/SessionRegistryLogoutHandler.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/SessionRegistryLogoutHandler.java new file mode 100644 index 00000000..808f3a4b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/SessionRegistryLogoutHandler.java @@ -0,0 +1,75 @@ +package com.jiuyv.sptccc.agile.framework.security.handle; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.session.SessionInformation; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.logout.LogoutHandler; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.framework.manager.AsyncManager; +import com.jiuyv.sptccc.agile.framework.manager.factory.AsyncFactory; + +/** + * 退出时做处理, + * 如果有不自动清理的缓存,可以在这处理 + * @author zhouliang + * + */ +public class SessionRegistryLogoutHandler implements LogoutHandler { + + private final SessionRegistry sessionRegistry; + + public SessionRegistryLogoutHandler(SessionRegistry sessionRegistry) { + this.sessionRegistry = sessionRegistry; + } + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + HttpSession session = request.getSession(false); + String msg="退出成功"; + int code=HttpStatus.SUCCESS; + boolean flag=false; + if (session!=null && authentication != null && authentication.getPrincipal() instanceof UserDetails) { + List sessionsInfo = sessionRegistry.getAllSessions(authentication.getPrincipal(),true); + String msg2=msg; + if(sessionsInfo.size()>1) { + flag=true; + msg2=msg+"!已在另一台机器登录"; + msg= msg+"!"+MessageUtils.message("user.again.login.error"); + code=HttpStatus.FORBIDDEN; + } + try { + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + if (StringUtils.isNotNull(loginUser)) + { +// // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininforV2(request, loginUser.getUsername(), Constants.LOGOUT, msg2)); + } + //request.getSession().invalidate(); + sessionRegistry.removeSessionInformation(session.getId()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + //System.out.println("logout--"+request.getRequestURI()+"--"+msg); +// if(!flag) { +// ServletUtils.renderString(response, JsonUtil.toJSONString(AjaxResult.error(code, msg))); +// } + + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserExpiredSessionStrategy.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserExpiredSessionStrategy.java new file mode 100644 index 00000000..fba2883c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserExpiredSessionStrategy.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.framework.security.handle; + +import java.io.IOException; + +import javax.servlet.ServletException; + +import org.springframework.security.web.session.SessionInformationExpiredEvent; +import org.springframework.security.web.session.SessionInformationExpiredStrategy; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; + +/** + * 旧用户被踢出 + * @author zhouliang + */ +@Component +public class UserExpiredSessionStrategy implements SessionInformationExpiredStrategy { + + @Override + public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { + //System.out.println("X3--"+event.getRequest().getRequestURI()); + ServletUtils.renderString(event.getResponse(), JsonUtil.toJSONString( + AjaxResult.error(HttpStatus.ERROR, MessageUtils.message("user.again.login.error")))); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserInvalidSessionStrategy.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserInvalidSessionStrategy.java new file mode 100644 index 00000000..056de924 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/handle/UserInvalidSessionStrategy.java @@ -0,0 +1,38 @@ +package com.jiuyv.sptccc.agile.framework.security.handle; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.web.session.InvalidSessionStrategy; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.http.CookieUtil; + +/** + * session失效后处理逻辑 + * @author zhouliang + * + */ +@Component +public class UserInvalidSessionStrategy implements InvalidSessionStrategy { + @Override + public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException { + //System.out.println("X2--"+request.getRequestURI()); + // 1.将浏览器的sessionid清除 + CookieUtil.setCookie(request, response, Constants.SESSION_NAME, null, 0); + + ServletUtils.renderString(response, JsonUtil.toJSONString( + AjaxResult.error(HttpStatus.UNAUTHORIZED, MessageUtils.message("user.login.invalid")))); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/OperationTokenSet.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/OperationTokenSet.java new file mode 100644 index 00000000..b6b2dec6 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/OperationTokenSet.java @@ -0,0 +1,45 @@ +package com.jiuyv.sptccc.agile.framework.security.model; + +import java.util.Map; + +public class OperationTokenSet { + public TokenNode curNode; + public String sm4Key; + private Map tokenNodeMap; + + public OperationTokenSet() { + } + + public OperationTokenSet(TokenNode curNode, String sm4Key) { + this.curNode = curNode; + this.sm4Key = sm4Key; + } + + public TokenNode getCurNode() { + return curNode; + } + + public OperationTokenSet setCurNode(TokenNode curNode) { + this.curNode = curNode; + return this; + } + + public String getSm4Key() { + return sm4Key; + } + + public OperationTokenSet setSm4Key(String sm4Key) { + this.sm4Key = sm4Key; + return this; + } + + public Map getTokenNodeMap() { + return tokenNodeMap; + } + + public OperationTokenSet setTokenNodeMap(Map tokenNodeMap) { + this.tokenNodeMap = tokenNodeMap; + return this; + } + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/TokenNode.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/TokenNode.java new file mode 100644 index 00000000..d62a37df --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/security/model/TokenNode.java @@ -0,0 +1,69 @@ +package com.jiuyv.sptccc.agile.framework.security.model; + +public class TokenNode { + public String encryptToken; + public String token; + public String hexSM4Key; + public String SM4Key; + public Integer times; + + public TokenNode() { + } + + public TokenNode(String encryptToken, String token, String hexKey, String key, Integer times) { + this.encryptToken = encryptToken; + this.token = token; + this.hexSM4Key = hexKey; + this.SM4Key = key; + this.times = times; + } + + public String getToken() { + return token; + } + + public Integer getTimes() { + return times; + } + + public String getEncryptToken() { + return encryptToken; + } + + public String getHexSM4Key() { + return hexSM4Key; + } + + public String getSM4Key() { + return SM4Key; + } + + public void minusTime() { + this.times = this.times - 1; + } + + public TokenNode setEncryptToken(String encryptToken) { + this.encryptToken = encryptToken; + return this; + } + + public TokenNode setToken(String token) { + this.token = token; + return this; + } + + public TokenNode setHexSM4Key(String hexSM4Key) { + this.hexSM4Key = hexSM4Key; + return this; + } + + public TokenNode setSM4Key(String SM4Key) { + this.SM4Key = SM4Key; + return this; + } + + public TokenNode setTimes(Integer times) { + this.times = times; + return this; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..5df07472 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,121 @@ +package com.jiuyv.sptccc.agile.framework.web.exception; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author admin + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler({ServiceException.class}) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request, HttpServletResponse response) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { +// log.error(e.getMessage(), e); +// String message = e.getBindingResult().getFieldError().getDefaultMessage(); +// return AjaxResult.error(message); + log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass()); + BindingResult bindingResult = e.getBindingResult(); + + Map errorMap = new HashMap<>(); + bindingResult.getFieldErrors().forEach((fieldError) -> { + errorMap.put(fieldError.getField(), fieldError.getDefaultMessage()); + }); + //return AjaxResult.error("参数校验错误",errorMap); + return AjaxResult.error(JsonUtil.toJSONString(errorMap)); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler({RuntimeException.class}) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request, HttpServletResponse response) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + + return AjaxResult.error("系统忙,请稍后再试"); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/SysLoginService.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/SysLoginService.java new file mode 100644 index 00000000..98111afd --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/SysLoginService.java @@ -0,0 +1,156 @@ +package com.jiuyv.sptccc.agile.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.CacheConstants; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginBody; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import com.jiuyv.sptccc.agile.feign.portal.PortalUserFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.user.ReqPortalUserResetErrorDTO; +import com.jiuyv.sptccc.agile.framework.manager.AsyncManager; +import com.jiuyv.sptccc.agile.framework.manager.factory.AsyncFactory; +import com.jiuyv.sptccc.agile.framework.security.exception.BadCaptchaException; +import com.jiuyv.sptccc.agile.framework.security.exception.BadUserSecretKeyException; + +/** + * 登录校验方法 + * + * @author admin + */ +@Component +public class SysLoginService { + private static final Logger log = LoggerFactory.getLogger(SysLoginService.class); + + @Autowired + private RedisCache redisCache; + + @Autowired + private PortalUserFeignApi userService; + /** + * 验证码标志 + */ + @Value("${conosle.captchaOnOff}") + private boolean captchaOnOff; + + /** + * 登录验证 + * @param authenticationManager + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + * @throws Exception + */ + public Authentication login(AuthenticationManager authenticationManager,LoginBody loginBody) throws AuthenticationException{ + String username=loginBody.getUsername(); + String password=loginBody.getPassword(); + String code=loginBody.getCode(); + String uuid=loginBody.getUuid(); + //处理验证码和秘钥 + String secretKey=validateCaptcha(username, code, uuid);; + // 用户验证 + Authentication authentication = null; + + try { + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + try { + password =Sm4Util.decryptEcb(secretKey, password); + } catch (Exception e) { + } + authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); + + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return authentication; + } catch (AuthenticationException e) { + if (e instanceof BadCredentialsException) {//密码错误 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + }else {//其他异常 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + } + throw e; + } + } + + + /** + * 校验验证码和秘钥 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 秘钥 + */ + public String validateCaptcha(String username, String code, String uuid) throws AuthenticationException{ + //随机秘钥 + String secretVerifyKey = CacheConstants.CAPTCHA_CODE_SIGN_KEY + StringUtils.nvl(uuid, ""); + String secretKey=redisCache.getValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE,secretVerifyKey); + if (secretKey != null) { + redisCache.removeValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE,secretVerifyKey); + } + // 验证码开关 + if (captchaOnOff) { + //验证码验证 + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(username, ""); + String captcha = redisCache.getValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE,verifyKey); + if (captcha != null) { + redisCache.removeValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE,verifyKey); + } + if (captcha == null) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + log.info("{},code{}.", MessageUtils.message("user.jcaptcha.expire"),code); + throw new BadCaptchaException(MessageUtils.message("user.jcaptcha.expire")); + } + if (!code.equalsIgnoreCase(captcha)) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + log.info("{},code{}.", MessageUtils.message("user.jcaptcha.error"),code); + throw new BadCaptchaException(MessageUtils.message("user.jcaptcha.error")); + } + } + + if (secretKey == null) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.secret.key.error"))); + throw new BadUserSecretKeyException(MessageUtils.message("user.secret.key.error")); + } + return secretKey; + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) { + ReqPortalUserResetErrorDTO tblSysUser = new ReqPortalUserResetErrorDTO(); + tblSysUser.setUserId(userId); + tblSysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + tblSysUser.setLoginDate(DateUtils.getNowDate()); + + //密码错次清零及用户状态恢复正常 + tblSysUser.setLoginErrorcount(0); + tblSysUser.setIsLocked(0); + userService.resetUserError(tblSysUser); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/TokenService.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/TokenService.java new file mode 100644 index 00000000..1ab9149d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/TokenService.java @@ -0,0 +1,168 @@ +package com.jiuyv.sptccc.agile.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletContext; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.session.SessionInformation; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.stereotype.Component; +import org.springframework.web.util.WebUtils; + +import com.jiuyv.sptccc.agile.common.constant.CacheConstants; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.AddressUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; + +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author admin + */ +@Component +public class TokenService +{ + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + public String createToken(HttpServletRequest request,LoginUser loginUser) + { + String token = request.getSession().getId(); + loginUser.setToken(token); + setUserAgent(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + claims.put(Constants.IP_ADDR_KEY, loginUser.getIpaddr()); + return createToken(claims); + } + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + return Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + public Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/UserDetailsServiceImpl.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 00000000..144e52a9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,53 @@ +package com.jiuyv.sptccc.agile.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUser; +import com.jiuyv.sptccc.agile.common.enums.UserStatus; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.feign.portal.PortalUserFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; + +/** + * 用户验证处理 + * + * @author admin + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private PortalUserFeignApi userService; + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + R userRes = userService.selectUserByUserName(username); + if(userRes.getCode()!=HttpStatus.SUCCESS) { + throw new AuthenticationServiceException(userRes.getMsg()); + } + TblPortalUserBase user = userRes.getData(); + if(StringUtils.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UsernameNotFoundException("账户或密码错误"); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new UsernameNotFoundException("账户或密码错误"); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UsernameNotFoundException("账户或密码错误"); + } + + return new LoginUser(user.getUserId(), user.getDeptId(), user, null); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/constant/PortalPhoneMsgLogConstants.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/constant/PortalPhoneMsgLogConstants.java new file mode 100644 index 00000000..6f1afaf4 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/constant/PortalPhoneMsgLogConstants.java @@ -0,0 +1,13 @@ +package com.jiuyv.sptccc.agile.portal.constant; + +/** + * 通用常量信息 + * + * @author admin + */ +public class PortalPhoneMsgLogConstants { + + //msg_type枚举 + public static final String MSG_TYPE_1 = "登陆验证"; + public static final String MSG_TYPE_2 = "修改密码"; +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java new file mode 100644 index 00000000..0d220359 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java @@ -0,0 +1,168 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Matcher; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FilenameUtils; +import org.apache.tomcat.util.http.parser.Ranges; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.file.FileTypeUtils; +import com.jiuyv.sptccc.agile.feign.portal.CommonFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.common.ResGetFileByteAddDTO; + +import net.logstash.logback.encoder.org.apache.commons.lang3.StringUtils; + +/** + * 通用请求处理 + * + * @author admin + */ +@RestController +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private CommonFeignApi commonFeignApi; + + + /** + * 图片需要直接页面显示的,是通过虚拟路径 + * @param request + * @param response + * @param model + */ + @GetMapping("/"+Constants.IMAGE_URL_PATH+"/*/*.*") + public void imagePreview(HttpServletRequest request, HttpServletResponse response, ModelMap model) { + String[] paths = request.getRequestURI().split("/"); + if(paths == null || paths.length < 4) { + ServletUtils.renderString(response, JsonUtil.toJSONString(R.fail("Image does not exist"))); + return; + } + Matcher mtype =Constants.SUFFIX_RULE.matcher(request.getRequestURI()); + if(mtype.find() && FileTypeUtils.isValidImageExt(mtype.group())) { + ServletUtils.renderString(response, JsonUtil.toJSONString(R.fail("Image does not exist"))); + return; + } + + //去文件表查询信息 paths[paths.length-1] + String uuid=FilenameUtils.getBaseName(paths[paths.length-1]); + R res = commonFeignApi.getImage(uuid); + ResGetFileByteAddDTO data=res.getData(); + byte[] imgBody=data.getFile(); + if(imgBody!=null){ + ServletUtils.renderImg(response, data.getType(), uuid, imgBody); + }else { + ServletUtils.renderString(response, JsonUtil.toJSONString(R.fail("Image does not exist"))); + } + } + + /** + * 文件下载,通过uuid,防止轻松下载别人文件 + * @param request + * @param response + * @return + */ + @GetMapping("/"+Constants.FILE_URL_PATH+"/{uuid}") + public ResponseEntity downloadFile(HttpServletRequest request, HttpServletResponse response + ,@PathVariable String uuid) { + //下载时文件进度由页面传过来 + String range = request.getHeader(HttpHeaders.RANGE); + String ifrange = request.getHeader(HttpHeaders.IF_RANGE); + long start = 0; + try { + if(StringUtils.isNotBlank(range)) { + String[] parts = range.replace("bytes=", "").split("-"); + start=Integer.parseInt(parts[0]); + } + }catch (Exception e) { + //不报错 + } + ResponseEntity res = commonFeignApi.downloadFile(uuid,start); + Resource body = res.getBody(); + HttpHeaders headers = res.getHeaders(); + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + //response.setHeader(HttpHeaders.LAST_MODIFIED, headers.getFirst(HttpHeaders.LAST_MODIFIED)); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+body.getFilename()); + response.setHeader(HttpHeaders.ACCEPT_RANGES, headers.getFirst(HttpHeaders.ACCEPT_RANGES)); + response.setHeader(HttpHeaders.CONTENT_LENGTH, headers.getFirst(HttpHeaders.CONTENT_LENGTH)); + response.setHeader(HttpHeaders.CONTENT_RANGE, headers.getFirst(HttpHeaders.CONTENT_RANGE)); + StreamingResponseBody responseBody = null; + try(InputStream inputStream = body.getInputStream()) { + responseBody = outputStream -> { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + }; + return new ResponseEntity<>(responseBody, start>0?HttpStatus.PARTIAL_CONTENT:HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(responseBody, HttpStatus.EXPECTATION_FAILED); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/"+Constants.FILE_URL_PATH+"/upload/{type}") + public R uploadFile(MultipartFile file,@PathVariable String type) throws Exception + { + if(!checkUploadPath(type)) { + return R.fail(MessageUtils.message("upload.type.is.valid")); + } + + String filename=file.getOriginalFilename(); + filename=StringUtils.defaultIfBlank(filename, file.getName()); + if(StringUtils.isBlank(filename)) { + return R.fail(MessageUtils.message("upload.filename.is.required")); + } + if(filename.length()>50) { + return R.fail(MessageUtils.message("upload.filename.exceed.length",50)); + } + //HttpStatus.PERMANENT_REDIRECT + //上传时文件进度是服务器来控制的 308 + return commonFeignApi.uploadFile(file, type, filename); + } + /** + * 校验文件分类路径 + * @param type + * @return + */ + private boolean checkUploadPath(String type) { + String[] types=Constants.FILE_TYPE_PATHS.split(","); + for(String x:types) { + if(type.equals(x.trim())) { + return true; + } + } + return false; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/LoginUserController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/LoginUserController.java new file mode 100644 index 00000000..06b35395 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/LoginUserController.java @@ -0,0 +1,69 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.jiuyv.sptccc.agile.common.config.ConsoleOprTokenProperties; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.core.controller.BaseController; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; +import com.jiuyv.sptccc.agile.framework.security.model.TokenNode; + +/** + * 登陆用户信息 + * + * @author admin + */ +@RestController +public class LoginUserController extends BaseController +{ + @Autowired + private RedisCache redisCache; + @Autowired + private ConsoleOprTokenProperties oprTokenProperties; + + /** + * 获取自己的信息 + */ + @GetMapping("/getInfo") + public AjaxResult getInfo(HttpServletRequest request,HttpServletResponse response) + { + TblPortalUserBase user = SecurityUtils.getLoginUser().getUser(); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + + try { + //返回数据加密用的秘钥 + String sm4KeyPage =Sm4Util.generateKeyHex();//秘钥的秘钥 + //随机生成密钥 + String dataSmEncryptKey = Sm4Util.generateKeyHex(); + String newDataSmKey= Sm4Util.encryptEcb(sm4KeyPage, dataSmEncryptKey);//数据传输秘钥 + response.setHeader("secretKeyPage", sm4KeyPage); + response.setHeader("dataSecretKey", newDataSmKey); + redisCache.setValueOfCacheName(CacheNameConstants.CACHE_USER_DATA_SECRET_KEY, user.getUserId().toString(), dataSmEncryptKey); + } catch (Exception e) { + logger.error("{}",e); + } + + + try { + // 第一次加载 主页 分配TOKEN + ImmutablePair pair = oprTokenProperties.calculateNewToken(request); + //操作token,是否要时间限制 + response.setHeader(oprTokenProperties.getHeader(), pair.left.getEncryptToken()); + response.setHeader(oprTokenProperties.getHeaderKey(), pair.left.getHexSM4Key()); + } catch (Exception e1) { + logger.error("{}",e1); + } + return ajax; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/MyCaptchaController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/MyCaptchaController.java new file mode 100644 index 00000000..107cf73a --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/MyCaptchaController.java @@ -0,0 +1,288 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.List; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.anji.captcha.util.StringUtils; +import com.google.code.kaptcha.Producer; +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.constant.CacheConstants; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.common.core.redis.RedisCache; +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.bean.BeanUtils; +import com.jiuyv.sptccc.agile.common.utils.sign.Base64; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.Seq; +import com.jiuyv.sptccc.agile.feign.portal.PortalPhoneMsgLogFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.PortalUserFeignApi; +import com.jiuyv.sptccc.agile.feign.portal.dto.TblPortalUserBase; +import com.jiuyv.sptccc.agile.feign.portal.dto.phoneMsgLog.ReqPortalPhoneMsgSendDTO; +import com.jiuyv.sptccc.agile.portal.constant.PortalPhoneMsgLogConstants; +import com.jiuyv.sptccc.agile.portal.dto.ReqAjCaptchaCheckDTO; +import com.jiuyv.sptccc.agile.portal.dto.ReqAjCaptchaGetDTO; +import com.jiuyv.sptccc.agile.portal.dto.ReqPhoneCodeGetDTO; +import com.jiuyv.sptccc.agile.portal.dto.ResAjCaptchaCheckDTO; +import com.jiuyv.sptccc.agile.portal.dto.ResAjCaptchaGetDTO; +import com.jiuyv.sptccc.agile.portal.dto.ResPhoneCodeGetDTO; + +/** + * 验证码提供类 + * @author zhouliang + * + */ +@RestController +public class MyCaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private CaptchaService captchaService; + + @Autowired + private PortalUserFeignApi userService; + @Autowired + private PortalPhoneMsgLogFeignApi phoneMsgLogService; + + + @Value("${conosle.phoneCaptchaTestOnOff:false}") + private boolean phoneCaptchaTestOnOff; + + /** + * 登录页面获取验证码 + * 重写captcha/get,专用于登陆 + * @param request + * @param response + * @param req + * @return + */ + @PostMapping("/captchaGet") + public R get(HttpServletRequest request,HttpServletResponse response + ,@Validated @RequestBody ReqAjCaptchaGetDTO req) { + String uuid = req.getUuid(); + + CaptchaVO data=new CaptchaVO(); + data.setCaptchaType(req.getCaptchaType()); + data.setClientUid(req.getUuid()); + data.setBrowserInfo(getRemoteId(request)); + ResponseModel responseModel=captchaService.get(data); + if(!responseModel.isSuccess()) { + return R.fail(responseModel.getRepMsg()); + } + + ResAjCaptchaGetDTO resDTO=new ResAjCaptchaGetDTO(); + BeanUtils.copyProperties(responseModel.getRepData(), resDTO); + resDTO.setCaptchaType(req.getCaptchaType()); + return R.ok(resDTO); + } + /** + * 登录页面校验验证码 + * 重写captcha/check,专用于登陆 + * @param request + * @param req + * @return + */ + @PostMapping("/captchaCheck") + public R check(HttpServletRequest request,@Validated @RequestBody ReqAjCaptchaCheckDTO req) { + String uuid = req.getUuid(); + + //如果坐标PointJson加密则在这里解密Sm4Util + CaptchaVO data=new CaptchaVO(); + data.setCaptchaType(req.getCaptchaType()); + data.setPointJson(req.getPointJson()); + data.setToken(req.getToken()); + data.setBrowserInfo(getRemoteId(request)); + ResponseModel responseModel=captchaService.check(data); + if(!responseModel.isSuccess()) { + return R.fail(MessageUtils.message("user.jcaptcha.ponit.error")); + } + + ResAjCaptchaCheckDTO resDTO=new ResAjCaptchaCheckDTO(); + BeanUtils.copyProperties(responseModel.getRepData(), resDTO); + resDTO.setCaptchaType(req.getCaptchaType()); + return R.ok(resDTO); + } + + /** + * 登录获取手机验证码 + * @param request + * @param req + * @return + */ + @PostMapping("/loginCode") + public R getLoginCode(HttpServletRequest request,HttpServletResponse response + ,@Validated @RequestBody ReqPhoneCodeGetDTO req) { + String uuid = req.getUuid(); + + ResponseModel captchaRes = null; + try { + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(req.getCaptchaVerification()); + captchaRes = captchaService.verification(captchaVO); + }catch(Exception e) { + //这里不报错,后面是统一错误 + } + if(captchaRes==null || !captchaRes.isSuccess()) { + return R.fail(MessageUtils.message("user.jcaptcha.ponit.error")); + } + + //如果没有用户则报错 + R userRes = userService.selectUserByUserName(req.getUsername()); + if(HttpStatus.SUCCESS!=userRes.getCode()) { + return R.fail(userRes.getMsg()); + } + TblPortalUserBase user = userRes.getData(); + + //传输加密信息 + String newLoginSecretKey =null;//登录的随机秘钥 + String sm4KeyPage = null;//和前端约定一个密钥,用于传输时对秘钥解密 + try { + //其实动态传没有意义,反而会被截获,解析本身只需要一次 + sm4KeyPage = Sm4Util.generateKeyHex(); + //随机生成密钥 + String loginSm4Key = Sm4Util.generateKeyHex(); + + newLoginSecretKey = Sm4Util.encryptEcb(sm4KeyPage, loginSm4Key);//再加密 + //存下登录用的的加密秘钥 + redisCache.setValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE, CacheConstants.CAPTCHA_CODE_SIGN_KEY+req.getUsername(), loginSm4Key); + } catch (Exception e) { + return R.fail(e.getMessage()); + } + + //把密码加密的方式加密后返回给前端 + response.addHeader("secretKeyPage", sm4KeyPage); + response.addHeader("secretKey", newLoginSecretKey); + + //发送一个手机验证码,肯定要和用户绑定 + String phoneCode = Seq.randomNumber(4); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + req.getUsername(); + String captcha = redisCache.getValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE,verifyKey); + if(StringUtils.isNotBlank(captcha)) { + return R.fail(MessageUtils.message("user.jcaptcha.frequent.requests")); + } + redisCache.setValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE, verifyKey, phoneCode); + if(phoneCaptchaTestOnOff) { + //测试模式,不调用验证码服务 + }else { + //调用验证码服务,通过手机发送,在实际发送的时候处理模板即可 + ReqPortalPhoneMsgSendDTO msgLog=new ReqPortalPhoneMsgSendDTO(); + msgLog.setPhoneNumber(user.getPhonenumber()); + msgLog.setMsgType(PortalPhoneMsgLogConstants.MSG_TYPE_1); + msgLog.setMsgTemplateCode("");//用模板 + List msgParams=msgLog.getMsgParams(); + msgParams.add(phoneCode); + msgLog.setMsgParams(msgParams); + R msgRes = phoneMsgLogService.sendPhoneMsg(msgLog); + if(HttpStatus.SUCCESS!=msgRes.getCode()) { + return R.fail(msgRes.getMsg()); + } + } + + ResPhoneCodeGetDTO resDTO=new ResPhoneCodeGetDTO(); + if(phoneCaptchaTestOnOff) { + //开启了测试 + resDTO.setCode(phoneCode); + } + return R.ok(resDTO); + } + + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletRequest request,HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("captchaOnOff", true); + + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + // 保存验证码信息 + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = ConsoleConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setValueOfCacheName(CacheNameConstants.CACHE_CAPTCHA_CODE, verifyKey, code); + + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + + + return ajax; + } + + + //图形验证码用 + public static final String getRemoteId(HttpServletRequest request) { + String xfwd = request.getHeader("X-Forwarded-For"); + String ip = getRemoteIpFromXfwd(xfwd); + String ua = request.getHeader("user-agent"); + if (StringUtils.isNotBlank(ip)) { + return ip + ua; + } + return request.getRemoteAddr() + ua; + } + //图形验证码用 + private static String getRemoteIpFromXfwd(String xfwd) { + if (StringUtils.isNotBlank(xfwd)) { + String[] ipList = xfwd.split(","); + return StringUtils.trim(ipList[0]); + } + return null; + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java new file mode 100644 index 00000000..ec8a48b0 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.jiuyv.sptccc.agile.common.core.controller.BaseController; + +/** + * 用户信息 + * + * @author admin + */ +@RestController +@RequestMapping("/portalUser") +public class PortalUserController extends BaseController +{ + +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/TestController.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/TestController.java new file mode 100644 index 00000000..9c5c44b2 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/controller/TestController.java @@ -0,0 +1,23 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import com.jiuyv.sptccc.agile.api.TestFeignApi; +import com.jiuyv.sptccc.agile.common.annotation.Anonymous; +import com.jiuyv.sptccc.agile.feign.portal.TestFeign; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("tt") +public class TestController { + + @Autowired + private TestFeign testFeign; + + @RequestMapping("/cc/{id}") + @Anonymous + public String testF(@PathVariable("id") String id) { + return testFeign.test01(); + } +} diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaCheckDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaCheckDTO.java new file mode 100644 index 00000000..221b6e07 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaCheckDTO.java @@ -0,0 +1,121 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import javax.validation.constraints.NotBlank; + +/** + * 检查验证码请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqAjCaptchaCheckDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * @title 唯一标识|Y + * 等价clientUid,只需传一个 + */ + private String uuid; + + /*** + * @title 客户端UI组件id + * 组件初始化时设置一次,UUID + */ + private String clientUid; + + /** + * @title 验证码类型|Y + * 类型:clickWord,blockPuzzle + */ + @NotBlank(message = "验证码类型不能为空") + private String captchaType; + + /** + * 点坐标|Y + */ + @NotBlank(message = "点坐标不能为空") + private String pointJson; + + /** + * 请求的token|Y + */ + @NotBlank(message = "token不能为空") + private String token; + + + /** + * @return the uuid + */ + @NotBlank(message = "唯一ID不能为空") + public String getUuid() { + return uuid; + } + + /** + * @param uuid the uuid to set + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * @return the clientUid + */ + public String getClientUid() { + return clientUid; + } + + /** + * @param clientUid the clientUid to set + */ + public void setClientUid(String clientUid) { + this.clientUid = clientUid; + } + + /** + * @return the captchaType + */ + public String getCaptchaType() { + return captchaType; + } + + + /** + * @param captchaType the captchaType to set + */ + public void setCaptchaType(String captchaType) { + this.captchaType = captchaType; + } + + + /** + * @return the pointJson + */ + public String getPointJson() { + return pointJson; + } + + + /** + * @param pointJson the pointJson to set + */ + public void setPointJson(String pointJson) { + this.pointJson = pointJson; + } + + + /** + * @return the token + */ + public String getToken() { + return token; + } + + + /** + * @param token the token to set + */ + public void setToken(String token) { + this.token = token; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaGetDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaGetDTO.java new file mode 100644 index 00000000..66eedd6f --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqAjCaptchaGetDTO.java @@ -0,0 +1,77 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import javax.validation.constraints.NotBlank; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 获取验证码请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqAjCaptchaGetDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * @title 唯一标识|Y + * 等价clientUid,只需传一个 + */ + private String uuid; + + /** + * @title 验证码类型|Y + * 类型:clickWord,blockPuzzle + */ + @NotBlank(message = "验证码类型不能为空") + private String captchaType; + + /*** + * @title 客户端UI组件id + * 组件初始化时设置一次,UUID + */ + private String clientUid; + + @NotBlank(message = "唯一id不能为空") + public String getUuid() + { + if(StringUtils.isBlank(uuid)) { + return clientUid; + } + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } + + + /** + * @return the captchaType + */ + public String getCaptchaType() { + return captchaType; + } + + /** + * @param captchaType the captchaType to set + */ + public void setCaptchaType(String captchaType) { + this.captchaType = captchaType; + } + + /** + * @return the clientUid + */ + public String getClientUid() { + return clientUid; + } + + /** + * @param clientUid the clientUid to set + */ + public void setClientUid(String clientUid) { + this.clientUid = clientUid; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqPhoneCodeGetDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqPhoneCodeGetDTO.java new file mode 100644 index 00000000..d6ef4d58 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ReqPhoneCodeGetDTO.java @@ -0,0 +1,77 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import javax.validation.constraints.NotBlank; + +import com.jiuyv.sptccc.agile.common.annotation.SensitiveData; +import com.jiuyv.sptccc.agile.common.annotation.TruncatedContent; + +/** + * 获取手机验证码请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPhoneCodeGetDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 唯一标识|Y + */ + @NotBlank(message = "唯一标识不能为空") + private String uuid; + + /** + * 用户名|Y + */ + @NotBlank(message = "登录用户名不能为空") + private String username; + + /** + * 图形验证码坐标|Y + */ + @NotBlank(message = "图形坐标不能为空") + private String captchaVerification; + + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the uuid + */ + public String getUuid() { + return uuid; + } + + /** + * @param uuid the uuid to set + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * @return the captchaVerification + */ + public String getCaptchaVerification() { + return captchaVerification; + } + + /** + * @param captchaVerification the captchaVerification to set + */ + public void setCaptchaVerification(String captchaVerification) { + this.captchaVerification = captchaVerification; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaCheckDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaCheckDTO.java new file mode 100644 index 00000000..fd4f12c6 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaCheckDTO.java @@ -0,0 +1,87 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +/** + * 检查验证码返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ResAjCaptchaCheckDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 验证码类型:(clickWord,blockPuzzle) + */ + private String captchaType; + + /** + * UUID(每次请求的验证码唯一标识) + */ + private String token; + + /** + * 校验结果 + */ + private Boolean result = true; + + /** + * 手机验证码(测试期间会返回给前端) + */ + private String code; + + /** + * @return the captchaType + */ + public String getCaptchaType() { + return captchaType; + } + + /** + * @param captchaType the captchaType to set + */ + public void setCaptchaType(String captchaType) { + this.captchaType = captchaType; + } + + /** + * @return the token + */ + public String getToken() { + return token; + } + + /** + * @param token the token to set + */ + public void setToken(String token) { + this.token = token; + } + + /** + * @return the result + */ + public Boolean getResult() { + return result; + } + + /** + * @param result the result to set + */ + public void setResult(Boolean result) { + this.result = result; + } + + /** + * @return the code + */ + public String getCode() { + return code; + } + + /** + * @param code the code to set + */ + public void setCode(String code) { + this.code = code; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaGetDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaGetDTO.java new file mode 100644 index 00000000..b529d4d9 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResAjCaptchaGetDTO.java @@ -0,0 +1,106 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +/** + * 获取验证码返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ResAjCaptchaGetDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 验证码类型:(clickWord,blockPuzzle) + */ + private String captchaType; + + /** + * 原生图片base64 + */ + private String originalImageBase64; + /** + * 滑块图片base64 + */ + private String jigsawImageBase64; + + /** + * UUID(每次请求的验证码唯一标识) + */ + private String token; + + /** + * 校验结果 + */ + private Boolean result = false; + + + /** + * @return the captchaType + */ + public String getCaptchaType() { + return captchaType; + } + + /** + * @param captchaType the captchaType to set + */ + public void setCaptchaType(String captchaType) { + this.captchaType = captchaType; + } + + /** + * @return the originalImageBase64 + */ + public String getOriginalImageBase64() { + return originalImageBase64; + } + + /** + * @param originalImageBase64 the originalImageBase64 to set + */ + public void setOriginalImageBase64(String originalImageBase64) { + this.originalImageBase64 = originalImageBase64; + } + + /** + * @return the jigsawImageBase64 + */ + public String getJigsawImageBase64() { + return jigsawImageBase64; + } + + /** + * @param jigsawImageBase64 the jigsawImageBase64 to set + */ + public void setJigsawImageBase64(String jigsawImageBase64) { + this.jigsawImageBase64 = jigsawImageBase64; + } + + /** + * @return the token + */ + public String getToken() { + return token; + } + + /** + * @param token the token to set + */ + public void setToken(String token) { + this.token = token; + } + + /** + * @return the result + */ + public Boolean getResult() { + return result; + } + + /** + * @param result the result to set + */ + public void setResult(Boolean result) { + this.result = result; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResPhoneCodeGetDTO.java b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResPhoneCodeGetDTO.java new file mode 100644 index 00000000..b7d3ac3b --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/java/com/jiuyv/sptccc/agile/portal/dto/ResPhoneCodeGetDTO.java @@ -0,0 +1,30 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +/** + * 获取手机验证码返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ResPhoneCodeGetDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 手机验证码(测试期间会返回给前端) + */ + private String code; + + /** + * @return the code + */ + public String getCode() { + return code; + } + + /** + * @param code the code to set + */ + public void setCode(String code) { + this.code = code; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/resources/application-dev.yml b/agile-portal/agile-portal-gateway/src/main/resources/application-dev.yml new file mode 100644 index 00000000..ea66cb1d --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/application-dev.yml @@ -0,0 +1,141 @@ +# 项目相关配置 +conosle: + # 名称 + name: portal-gateway + # 版本 + version: 1.0 + # 版权年份 + copyrightYear: 2022 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数组计算 char 字符验证 + captchaType: math + # 登陆验证码开关 + captchaOnOff: true + # 手机验证码测试开关,true为测试 + phoneCaptchaTestOnOff: true +#路由服务地址 +gateway: + portalUrl: http://127.0.0.1:18082/portal-service + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(单位分钟,应该和session匹配,session默认30分钟) + expireTime: 30 + +operationtoken: + tokenTimes: 100 + skipUrls: + - '/getInfo' + - '/captchaGet' + - '/captchaCheck' + - '/captchaImage' + +# 开发环境配置 +server: + port: 18081 #本地 + servlet: + # 应用的访问路径 + context-path: /portal-gateway + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.jiuyv.sptccc.agile: debug + org.springframework: warn + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: + + +spring: + application: + name: portal-gateway + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + datasource: #数据源配置 + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://172.16.12.105:5432/keliubao + username: postgres + password: postgres + jackson: + date-format: yyyy-MM-dd HH:mm:ss # 常用的时间格式 + default-property-inclusion: non_null # 忽略空对象 + serialization: + fail-on-empty-beans: false # 序列化空对象时不抛出异常 + indent-output: true # 输出格式化为缩进的JSON + write-dates-as-timestamps: false # 日期序列化为时间戳而不是ISO-8601格式 + deserialization: + fail-on-unknown-properties: false # 反序列化时忽略未知的属性 + cache: + type: caffeine + caffeine.spec: maximumSize=2000,expireAfterWrite=1h + +# 信息安全 +security: + csrf: + excludes: /login,/register,/captchaImage,/captchaGet,/captchaCheck + refererUrl: http://127.0.0.1:18080/ + +aj: + captcha: + # 缓存类型,此处为本地缓存 + cache-type: local + # 本地缓存的最大值,超过这个值会清除缓存 + cache-number: 5000 + # 定时清除过期缓存(单位:秒),设置为0代表不执行 + timing-clear: 180 + # 滑动拼图验证码的图片路径 + jigsaw: classpath:images/jigsaw + # 点选文字验证码的图片路径 + pic-click: classpath:images/pic-click + # 验证码类型,默认实例化两种验证码 + type: default + # 水印文字,使用Unicode编码,此处为“我的水印” + water-mark: 魔法水印魔法水印 + # 水印字体,使用Unicode编码,此处为文泉驿正黑 + water-font: WenQuanZhengHei.ttf + # 点选文字验证码的文字字体,使用Unicode编码,此处为文泉驿正黑 + font-type: WenQuanZhengHei.ttf + # 校验滑动拼图允许误差偏移量,单位为像素(默认为5) + slip-offset: 5 + # 是否启用AES加密坐标(true表示启用,false表示禁用) + aes-status: false + # 滑动干扰项,可选值为0、1、2 + interference-options: 0 diff --git a/agile-portal/agile-portal-gateway/src/main/resources/application.yml b/agile-portal/agile-portal-gateway/src/main/resources/application.yml new file mode 100644 index 00000000..08253209 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/application.yml @@ -0,0 +1,19 @@ +# Spring配置 +spring: + profiles: + active: dev + mvc: + view: + static-path-pattern: /static/** + favicon: + enabled: false + thymeleaf: + prefix: classpath:/static/ + suffix: .html + enabled: true + cache: false + encoding: utf-8 + content-type: text/html + check-template-location: false + mode: HTML + \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/resources/banner.txt b/agile-portal/agile-portal-gateway/src/main/resources/banner.txt new file mode 100644 index 00000000..6e5e0f93 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/banner.txt @@ -0,0 +1,6 @@ +Application Version: ${chemical.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/resources/fonts/WenQuanZhengHei.ttf b/agile-portal/agile-portal-gateway/src/main/resources/fonts/WenQuanZhengHei.ttf new file mode 100644 index 00000000..f84e9feb Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/fonts/WenQuanZhengHei.ttf differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/fonts/license.txt b/agile-portal/agile-portal-gateway/src/main/resources/fonts/license.txt new file mode 100644 index 00000000..719f68f0 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/fonts/license.txt @@ -0,0 +1,55 @@ +文泉驿是一个开源汉字字体项目 + +由旅美学者房骞骞(FangQ) + +于2004年10月创建 + +集中力量解决GNU/Linux + +高质量中文字体匮乏的状况 + +目前,文泉驿已经开发并发布了 + +第一个完整覆盖GB18030汉字 + +(包含27000多个汉字) + +的多规格点阵汉字字型文件 + +第一个覆盖GBK字符集的 + +开源矢量字型文件(文泉驿正黑) + +并提供了目前包含字符数目最多的 + +开源字体——GNU Unifont——中 + +绝大多数中日韩文相关的符号 + +这些字型文件已经逐渐成为 + +主流Linux/Unix发行版 + +中文桌面的首选中文字体 + +目前Ubuntu、Fedora、Slackware + +Magic Linux、CDLinux + +使用文泉驿作为默认中文字体 + +Debian、Gentoo、Mandriva + +ArchLinux、Frugalware + +则提供了官方源支持 + +而FreeBSD则在其ports中有提供 + +所以,今天我们所要分享的就是 + +文泉驿正黑体 + +可在Linux/UNIX,Windows + +Mac OS和嵌入式操作系统中使用 \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/resources/i18n/messages.properties b/agile-portal/agile-portal-gateway/src/main/resources/i18n/messages.properties new file mode 100644 index 00000000..4f9f6c7c --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/i18n/messages.properties @@ -0,0 +1,51 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.secret.key.error=用户密钥无效 +user.login.error=用户登陆信息非法 +user.again.login.error=当前用户已在另一台机器登录,您被迫下线 +user.login.invalid=登陆信息过期,请重新登陆 +user.jcaptcha.frequent.requests=请勿频繁获取验证码 +user.jcaptcha.ponit.error=图形验证码已失效 + +user.password.retry.limit.count=密码输入错误,您还有{0}次登录机会 +user.password.retry.limit.exceed=密码输入错误{0}次,用户已被锁定,请10分钟之后再尝试登录 +user.locked=用户已被锁定,请10分钟之后再尝试登录 + +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +upload.filename.is.required=文件名称不能为空 +upload.type.is.valid=上传文件分类非法 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] + +data.not.exist=数据不存在 +data.is.expired=数据已过期,请重新加载 \ No newline at end of file diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg1.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg1.png new file mode 100644 index 00000000..93b70d4c Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg1.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg2.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg2.png new file mode 100644 index 00000000..f60f7b73 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg2.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg3.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg3.png new file mode 100644 index 00000000..13585062 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg3.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg4.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg4.png new file mode 100644 index 00000000..8c05e9c3 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg4.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg5.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg5.png new file mode 100644 index 00000000..bcd00c73 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/original/bg5.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/1.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/1.png new file mode 100644 index 00000000..d6b975b5 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/1.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/2.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/2.png new file mode 100644 index 00000000..b1482d48 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/2.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/3.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/3.png new file mode 100644 index 00000000..cdbb0b18 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/3.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/4.png b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/4.png new file mode 100644 index 00000000..bc69c962 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/jigsaw/slidingBlock/4.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg1.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg1.png new file mode 100644 index 00000000..51573a0c Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg1.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg10.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg10.png new file mode 100644 index 00000000..f633aeed Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg10.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg2.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg2.png new file mode 100644 index 00000000..909dc39e Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg2.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg3.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg3.png new file mode 100644 index 00000000..59bc59c0 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg3.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg4.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg4.png new file mode 100644 index 00000000..c856f4d9 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg4.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg5.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg5.png new file mode 100644 index 00000000..4594fcf6 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg5.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg6.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg6.png new file mode 100644 index 00000000..0f28d820 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg6.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg7.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg7.png new file mode 100644 index 00000000..1e044929 Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg7.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg8.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg8.png new file mode 100644 index 00000000..f11545fd Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg8.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg9.png b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg9.png new file mode 100644 index 00000000..2f3a86da Binary files /dev/null and b/agile-portal/agile-portal-gateway/src/main/resources/images/pic-click/bg9.png differ diff --git a/agile-portal/agile-portal-gateway/src/main/resources/logback-spring.xml b/agile-portal/agile-portal-gateway/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..b7f7baf4 --- /dev/null +++ b/agile-portal/agile-portal-gateway/src/main/resources/logback-spring.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + System.out + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + + + ${LOG_HOME}/${APP_NAME}.log + + + ${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log.zip + + + 100MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + + ${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log + 7 + + + 100MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + + ${JSON_LOG_HOME}/${APP_NAME}.json.%d{yyyy-MM-dd}.%i.log + 7 + + + 100MB + + + + + + + { + "timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", + "severity": "%level", + "service": "${APP_NAME:-}", + "trace": "%X{X-B3-TraceId:-}", + "span": "%X{X-B3-SpanId:-}", + "parent": "%X{X-B3-ParentSpanId:-}", + "exportable": "%X{X-Span-Export:-}", + "pid": "${PID:-}", + "thread": "%thread", + "class": "%logger{40}", + "rest": "%message" + } + + + + + + + + ${JSON_LOG_HOME}/${APP_NAME}.json.log + + + ${JSON_LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.json.log.zip + + + 100MB + + + + + + + { + "timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", + "severity": "%level", + "service": "${APP_NAME:-}", + "trace": "%X{X-B3-TraceId:-}", + "span": "%X{X-B3-SpanId:-}", + "parent": "%X{X-B3-ParentSpanId:-}", + "exportable": "%X{X-Span-Export:-}", + "pid": "${PID:-}", + "thread": "%thread", + "class": "%logger{40}", + "rest": "%message" + } + + + + + + + System.err + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/pom.xml b/agile-portal/agile-portal-service/pom.xml new file mode 100644 index 00000000..14fcc967 --- /dev/null +++ b/agile-portal/agile-portal-service/pom.xml @@ -0,0 +1,261 @@ + + 4.0.0 + + com.jiuyv + agile-portal + 0.0.1 + + agile-portal-service + + + + + org.openjdk.jmh + jmh-core + 1.36 + + + + + org.openjdk.jmh + jmh-generator-annprocess + 1.36 + + + + + com.jiuyv.sptcc.portal + agile-portsl-api + 0.0.1 + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${openfeign.version} + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + commons-io + commons-io + ${commons.io.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + commons-collections + commons-collections + ${commons.collections.version} + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + net.logstash.logback + logstash-logback-encoder + 6.4 + + + + + org.springframework + spring-context-support + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + javax.xml.bind + jaxb-api + + + + + org.apache.commons + commons-pool2 + + + + + + javax.servlet + javax.servlet-api + + + + + org.aspectj + aspectjweaver + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + + + + org.springframework.boot + spring-boot-starter-cache + + + + com.github.ben-manes.caffeine + caffeine + + + + org.postgresql + postgresql + + + + com.jcraft + jsch + 0.1.55 + + + + org.apache.axis + axis + 1.4 + + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + + + + + repackage + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleApplication.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleApplication.java new file mode 100644 index 00000000..570e7b2e --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleApplication.java @@ -0,0 +1,33 @@ +package com.jiuyv.sptccc.agile; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动程序 + * + * @author admin + */ +@EnableScheduling +@SpringBootApplication() +public class PortalConsoleApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(PortalConsoleApplication.class); + + public static void main(String[] args) { + SpringApplication.run(PortalConsoleApplication.class, args); + LOGGER.info("(♥◠‿◠)ノ゙ 模块启动成功゙ \n"+ + " ___ ___ ___ \n"+ + " |\\ \\ |\\ \\ / /| \n"+ + " \\ \\ \\ \\ \\ \\/ / / \n"+ + " __ \\ \\ \\ \\ \\ / / \n"+ + " |\\ \\\\_\\ \\ \\/ / / \n"+ + " \\ \\________\\ __/ / / \n"+ + " \\|________| |\\___/ / \n"+ + " \\|___|/ \n" + ); + } +} + diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleServletInitializer.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleServletInitializer.java new file mode 100644 index 00000000..8343a6d2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/PortalConsoleServletInitializer.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author admin + */ +public class PortalConsoleServletInitializer extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(PortalConsoleApplication.class); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/EnumCheckValue.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/EnumCheckValue.java new file mode 100644 index 00000000..ad2d8587 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/EnumCheckValue.java @@ -0,0 +1,24 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import com.jiuyv.sptccc.agile.common.utils.bean.EnumStringValidator; + +/** + * 注解:用于标记需要截取内容的属性 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = EnumStringValidator.class) +public @interface EnumCheckValue { + Class value(); + String message() default "值不在枚举范围内"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Log.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Log.java new file mode 100644 index 00000000..9b6a6df0 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/Log.java @@ -0,0 +1,47 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.jiuyv.sptccc.agile.common.enums.BusinessType; +import com.jiuyv.sptccc.agile.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author admin + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java new file mode 100644 index 00000000..4410a2da --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogIgnore.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * 方法不需要输出日志时使用 + * @author zhouliang + * + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogIgnore { +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java new file mode 100644 index 00000000..6538d3b8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/LogSimpleResult.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 返回内容太多时,不需要关注具体内容 + * 只需要知道返回情况时使用 + * @author zhouliang + * + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogSimpleResult { +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/NoAuthUser.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/NoAuthUser.java new file mode 100644 index 00000000..36084551 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/NoAuthUser.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * 方法不需要登陆时使用 + * @author zhouliang + * + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NoAuthUser { +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java new file mode 100644 index 00000000..01b4ff52 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/SensitiveData.java @@ -0,0 +1,21 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * 日志对敏感字段处理为*(不是过滤,方便确认有没有值) + * 一些固定字段强制处理了,就不需要注解,如password + * @author zhouliang + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SensitiveData { + char defaultMark() default '*';//默认使用* + int firstLength() default 3;//保留前三位 + int endLength() default 3;//保留后三位 +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java new file mode 100644 index 00000000..14ae5049 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/annotation/TruncatedContent.java @@ -0,0 +1,15 @@ +package com.jiuyv.sptccc.agile.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 注解:用于标记需要截取内容的属性 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface TruncatedContent { + int length() default 3000; +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java new file mode 100644 index 00000000..41034889 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/config/ConsoleConfig.java @@ -0,0 +1,123 @@ +package com.jiuyv.sptccc.agile.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author admin + */ +@Component +@ConfigurationProperties(prefix = "conosle") +public class ConsoleConfig { + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getCopyrightYear() { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) { + this.copyrightYear = copyrightYear; + } + + public boolean isDemoEnabled() { + return demoEnabled; + } + + public void setDemoEnabled(boolean demoEnabled) { + this.demoEnabled = demoEnabled; + } + + public static String getProfile() { + return profile; + } + + public void setProfile(String profile) { + setProfile2(profile); + } + + public static void setProfile2(String profile) { + ConsoleConfig.profile = profile; + } + + public static boolean isAddressEnabled() { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) { + setAddressEnabled2(addressEnabled); + } + + public static void setAddressEnabled2(boolean addressEnabled) { + ConsoleConfig.addressEnabled = addressEnabled; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() { + return getProfile() + "/upload"; + } + + /** + * 获取随附文件上传路径 + */ + public static String getUploadPath2() { + return getProfile(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java new file mode 100644 index 00000000..1f4da315 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheConstants.java @@ -0,0 +1,15 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 缓存的key 常量 + * + * @author admin + */ +public class CacheConstants +{ + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "";//置为空了,独立缓存不用key,减少空间 + public static final String CAPTCHA_CODE_SIGN_KEY = "SIGN";//秘钥的key +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java new file mode 100644 index 00000000..52f750d7 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/CacheNameConstants.java @@ -0,0 +1,43 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 缓存的实例名称 + * 时间和组没法动态控制,只能多个实例分开 + */ +public class CacheNameConstants +{ + private CacheNameConstants() { + throw new IllegalStateException("Utility class"); + } + + /** + * 字典缓存名 + */ + public static final String CACHE_SYS_DICT = "cache_sys_dict"; + + + /** + * 验证码缓存名,登录随机密钥也放里面 + */ + public static final String CACHE_CAPTCHA_CODE = "cache_captcha_code"; + + /** + * 防重提交缓存名5s + */ + public static final String CACHE_REPEAT_SUBMIT_5S = "cache_repeat_submit_5s"; + + /** + * 防重提交缓存名30s + */ + public static final String CACHE_REPEAT_SUBMIT_30S = "cache_repeat_submit_30s"; + + /** + * 数据加密用随机密钥(用户级别) + */ + public static final String CACHE_USER_DATA_SECRET_KEY = "cache_user_data_secret_key"; + + /** + * 1天缓存 + */ + public static final String CACHE_ONEDAY = "cache_oneday"; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java new file mode 100644 index 00000000..0f11a506 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/Constants.java @@ -0,0 +1,86 @@ +package com.jiuyv.sptccc.agile.common.constant; + +import java.util.regex.Pattern; + +/** + * 通用常量信息 + * + * @author admin + */ +public class Constants { + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** 后缀获取*/ + public static Pattern SUFFIX_RULE=Pattern.compile("\\.([A-Z0-9]+$)",Pattern.CASE_INSENSITIVE); + + /** 图片的前缀固定 */ + public final static String IMAGE_URL_PATH="images"; + //文件的前缀固定 + public final static String FILE_URL_PATH="files"; + + /** FEIGN_CLIENT标志 */ + public final static String FEIGN_CLIENT_FLAG="gateway-source"; + /** FEIGN_CLIENT标志 */ + public final static String FEIGN_CLIENT_VAL="cabcc6d21b22a06d1"; + /** 变量名:用户信息 */ + public static final String USERINFO_KEY = "userInfo"; + + //变量名:不要version字段 + public final static String NO_VERSION_FLAG="noVersionFlag"; + + /** 一个固定的密钥,用于配置文件内容加密/解密 */ + public static final String SM4_SECERT_KEY = "a14751855ccb428d982c33dfa3535a57"; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java new file mode 100644 index 00000000..27707d42 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/HttpStatus.java @@ -0,0 +1,89 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 返回状态码 + * + * @author admin + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/UserConstants.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/UserConstants.java new file mode 100644 index 00000000..badff171 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/constant/UserConstants.java @@ -0,0 +1,78 @@ +package com.jiuyv.sptccc.agile.common.constant; + +/** + * 用户常量信息 + * + * @author admin + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验返回结果码 */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/BaseManagerUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/BaseManagerUtils.java new file mode 100644 index 00000000..da9fd277 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/BaseManagerUtils.java @@ -0,0 +1,80 @@ +package com.jiuyv.sptccc.agile.common.core; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Locale; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.core.domain.BaseTime; +import com.jiuyv.sptccc.agile.common.utils.ReflectUtil; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.Seq; +import com.jiuyv.sptccc.agile.system.mapper.ISysTimeBaseMapper;/** + * 某些普遍方法再次封装,减少引入快速使用 + * @author zhouliang + * + */ +public class BaseManagerUtils { + + protected static final Logger LOGGER = LoggerFactory.getLogger(BaseManagerUtils.class); + + private BaseManagerUtils() { + throw new IllegalStateException("Utility class"); + } + + /** 获取系统的时间*/ + public static BaseTime getSystemTime() { + //强行从数据库获取,如果没有也不报错 + BaseTime timeVO = null; + try{ + ISysTimeBaseMapper tmapper=SpringUtils.getBean(ISysTimeBaseMapper.class); + timeVO=tmapper.selectSysCurrentTime(); + }catch (Exception e) { + //正常是不会走到这里面的,只是兼容写法而已 + LOGGER.error("Exception in obtaining database time。Using System Time Substitution"); + timeVO = new BaseTime(); + timeVO.setUtcTime(Instant.now()); + } + timeVO.setDate(Date.from(timeVO.getUtcTime())); + timeVO.setDataTime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + .withLocale(Locale.SIMPLIFIED_CHINESE).withZone(ZoneId.of(timeVO.getTimeZone())).format(timeVO.getUtcTime())); + timeVO.setDateDay(timeVO.getDataTime().substring(0, 10)); + return timeVO; + } + + + /** 更新关键字段*/ + public static void initUpdateInfo(T info, Date time) { + try{ + ReflectUtil.setFieldValue(info, "updateByName", SecurityUtils.getNickname()); + ReflectUtil.setFieldValue(info, "updateBy", SecurityUtils.getUserId()); + ReflectUtil.setFieldValue(info, "updateTime", time); + ReflectUtil.setFieldValue(info, "recToken", IdUtils.fastSimpleUUID().substring(0, 10)); + }catch(Exception e){ + } + } + + /** 创建关键字段*/ + public static void initCreateInfo(T info, Date time) { + try{ + ReflectUtil.setFieldValue(info, "createByName", SecurityUtils.getNickname()); + ReflectUtil.setFieldValue(info, "createBy", SecurityUtils.getUserId()); + ReflectUtil.setFieldValue(info, "createTime", time); + ReflectUtil.setFieldValue(info, "dataStatus", "00"); + BaseManagerUtils.initUpdateInfo(info, time); + }catch(Exception e){ + } + } + + /** 生成id*/ + public static Long getId() { + return Long.valueOf(Seq.getId(Seq.commSeqType)); + } + + } \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java new file mode 100644 index 00000000..cd3e030d --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/controller/BaseController.java @@ -0,0 +1,222 @@ +package com.jiuyv.sptccc.agile.common.core.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.jiuyv.sptccc.agile.common.annotation.NoAuthUser; +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUserSimple; +import com.jiuyv.sptccc.agile.common.core.page.PageDomain; +import com.jiuyv.sptccc.agile.common.core.page.TableDataInfo; +import com.jiuyv.sptccc.agile.common.core.page.TableSupport; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.PageUtils; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.sql.SqlUtil; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; + +/** + * web层通用数据处理 + * + * @author admin + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @NoAuthUser + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal((int)new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUserSimple getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUserName(); + } + + /** + * 更新时用的,检查params,没有则初始化 + * 放一下旧的recToken和versionNum + * @param params + * @return + */ + public Map checkParams(Map params,String recToken,Long versionNum) + { + if(params==null) { + params=new HashMap<>(); + } + if(StringUtils.isNotBlank(recToken)) { + params.put("recToken", recToken); + } + if(versionNum!=null) { + params.put("versionNum", versionNum); + } + return params; + } + + /** + * 生成一个10位随机码recToken + * @return + */ + public String getNewRecToken() + { + return IdUtils.fastSimpleUUID().substring(0,10); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java new file mode 100644 index 00000000..b49272c2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/AjaxResult.java @@ -0,0 +1,176 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.util.HashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author admin + */ +public class AjaxResult extends HashMap +{ + protected static Logger logger = LoggerFactory.getLogger(AjaxResult.class); + + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) + { + logger.error("服务报错:"+msg); + if(msg.contains("Exception")){ + return new AjaxResult(HttpStatus.ERROR, "系统内部错误", data); + } + + if(msg.contains("script")){ + return new AjaxResult(HttpStatus.ERROR, "系统参数错误", data); + } + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java new file mode 100644 index 00000000..f1e509ef --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseEntity.java @@ -0,0 +1,122 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * Entity基类 + * + * @author admin + */ +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** 创建者ID */ + private Long createById; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 创建者名称 */ + private String createBy; + + /** 更新者ID */ + private Long updateById; + + /** 更新者名称 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 搜索值 */ + private String searchValue; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + private Map params; + + public Long getCreateById() { + return createById; + } + + public void setCreateById(Long createById) { + this.createById = createById; + } + + public Long getUpdateById() { + return updateById; + } + + public void setUpdateById(Long updateById) { + this.updateById = updateById; + } + + public String getSearchValue() { + return searchValue == null ? "" : searchValue; + } + + public void setSearchValue(String searchValue) { + this.searchValue = searchValue; + } + + public String getCreateBy() { + return createBy == null ? "" : createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getUpdateBy() { + return updateBy == null ? "" : updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getRemark() { + return remark == null ? "" : remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Map getParams() { + if (params == null) { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) { + this.params = params; + } + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseTime.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseTime.java new file mode 100644 index 00000000..aafabc22 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/BaseTime.java @@ -0,0 +1,81 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.time.Instant; +import java.util.Date; + +/** + * 时间对象 + * @author zhouliang + * + */ +public class BaseTime implements java.io.Serializable { + + /** default Serial Version UID*/ + private static final long serialVersionUID = 1L; + + /** 当前时区 */ + private String timeZone ="+08:00"; + + /** 当前时区 YYYY-MM-DD */ + private String dateDay; + + /** 当前时区 YYYY-MM-DD HH:MM:SS */ + private String dataTime; + + /** 当前时区日期 */ + private Date date; + + /** UTC-0 带时区时间 */ + private Instant utcTime; + + /** UTC-0 带时区时间 */ + private String utcTimeStr; + + public String getTimeZone() { + return timeZone; + } + + public void setTimeZone(String timeZone) { + this.timeZone = timeZone; + } + + public String getDateDay() { + return dateDay; + } + + public void setDateDay(String dateDay) { + this.dateDay = dateDay; + } + + public String getDataTime() { + return dataTime; + } + + public void setDataTime(String dataTime) { + this.dataTime = dataTime; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public Instant getUtcTime() { + return utcTime; + } + + public void setUtcTime(Instant utcTime) { + this.utcTime = utcTime; + } + + public String getUtcTimeStr() { + return utcTimeStr; + } + + public void setUtcTimeStr(String utcTimeStr) { + this.utcTimeStr = utcTimeStr; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java new file mode 100644 index 00000000..df0c9731 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/R.java @@ -0,0 +1,115 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author admin + */ +public class R implements Serializable +{ + private static final Logger log = LoggerFactory.getLogger(R.class); + + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + if(SUCCESS!=code) { + //异常要输出 + log.info("Return Business Exception >> code={}, msg={}",code, msg); + } + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java new file mode 100644 index 00000000..927fd96a --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author admin + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java new file mode 100644 index 00000000..15310b9f --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/TreeSelect.java @@ -0,0 +1,61 @@ +package com.jiuyv.sptccc.agile.common.core.domain; + +import java.io.Serializable; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Treeselect树结构实体类 + * + * @author admin + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java new file mode 100644 index 00000000..059cf963 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/domain/model/LoginUserSimple.java @@ -0,0 +1,163 @@ +package com.jiuyv.sptccc.agile.common.core.domain.model; + +import java.io.Serializable; + +/** + * 登录用户基本信息 + * + * @author admin + */ +public class LoginUserSimple implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** 用户账号 */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * @return the userId + */ + public Long getUserId() { + return userId; + } + + /** + * @param userId the userId to set + */ + public void setUserId(Long userId) { + this.userId = userId; + } + + /** + * @return the userName + */ + public String getUserName() { + return userName; + } + + /** + * @param userName the userName to set + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * @return the nickName + */ + public String getNickName() { + return nickName; + } + + /** + * @param nickName the nickName to set + */ + public void setNickName(String nickName) { + this.nickName = nickName; + } + + /** + * @return the deptId + */ + public Long getDeptId() { + return deptId; + } + + /** + * @param deptId the deptId to set + */ + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + /** + * @return the ipaddr + */ + public String getIpaddr() { + return ipaddr; + } + + /** + * @param ipaddr the ipaddr to set + */ + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + /** + * @return the loginLocation + */ + public String getLoginLocation() { + return loginLocation; + } + + /** + * @param loginLocation the loginLocation to set + */ + public void setLoginLocation(String loginLocation) { + this.loginLocation = loginLocation; + } + + /** + * @return the browser + */ + public String getBrowser() { + return browser; + } + + /** + * @param browser the browser to set + */ + public void setBrowser(String browser) { + this.browser = browser; + } + + /** + * @return the os + */ + public String getOs() { + return os; + } + + /** + * @param os the os to set + */ + public void setOs(String os) { + this.os = os; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java new file mode 100644 index 00000000..f2ec60a7 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author admin + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java new file mode 100644 index 00000000..a2da716e --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableDataInfo.java @@ -0,0 +1,83 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author admin + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private int total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java new file mode 100644 index 00000000..4dd19b7b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.common.core.page; + +import com.jiuyv.sptccc.agile.common.core.text.Convert; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author admin + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java new file mode 100644 index 00000000..bf7e9554 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/redis/RedisCache.java @@ -0,0 +1,135 @@ +package com.jiuyv.sptccc.agile.common.core.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.Cache; +import org.springframework.cache.Cache.ValueWrapper; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.framework.config.caffeine.CacheTimestampedValue; + +/** + * 缓存工具类,用的Caffeine 缓存 + * 工具类就不改名了 + * @author admin + **/ +@SuppressWarnings(value = { "unchecked"}) +@Component +public class RedisCache +{ + @Autowired + @Qualifier(value = "commonCacheManager") + private CacheManager cacheManager; + + + /** + * 往指定缓存放入一个键值 + * @param + * @param cacheName + * @param key + * @param value + */ + public void setValueOfCacheName(final String cacheName, final String key, final T value) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.put(key, value); + } + /** + * 往指定缓存放入一个键值(并记录时间) + * @param + * @param cacheName + * @param key + * @param value + */ + public void setTimeValueOfCacheName(final String cacheName, final String key, final T value) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.put(key, new CacheTimestampedValue<>(value)); + } + /** + * 获取指定缓存的某个键值 + * @param + * @param cacheName + * @param key + * @param value + */ + public T getValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + ValueWrapper wval = cache.get(key); + if(wval==null) { + return null; + } + return (T) wval.get(); + } + public T getValueOfCacheName(final String cacheName, final String key,Class clazz) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } +// ParameterizedTypeReference> typeRef = new ParameterizedTypeReference() {}; +// typeRef.getType().getClass() + T wval = cache.get(key, clazz); + if(wval==null) { + return null; + } + return wval; + } + /** + * 获取指定缓存的某个键值(并返回记录时间) + * @param + * @param cacheName + * @param key + * @return + */ + public CacheTimestampedValue getTimeValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + ValueWrapper wval = cache.get(key); + if(wval==null) { + return null; + } + return (CacheTimestampedValue) wval.get(); + } + /** + * 移除指定缓存的某个键值 + * @param cacheName + * @param key + */ + public void removeValueOfCacheName(final String cacheName, final String key) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.evict(key); + } + /** + * 清空指定缓存全部的键值 + * @param cacheName + */ + public void clearAllOfCacheName(final String cacheName) + { + Cache cache = cacheManager.getCache(cacheName); + if(cache==null) { + throw new ServiceException("Cache does not exist!"); + } + cache.clear(); + } + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java new file mode 100644 index 00000000..9545fcd8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/CharsetKit.java @@ -0,0 +1,87 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author admin + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java new file mode 100644 index 00000000..7f570c29 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/Convert.java @@ -0,0 +1,1002 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 类型转换器 + * + * @author admin + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double)value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java new file mode 100644 index 00000000..d9b41e43 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.jiuyv.sptccc.agile.common.core.text; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author admin + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java new file mode 100644 index 00000000..605e8a0c --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 操作状态 + * + * @author admin + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java new file mode 100644 index 00000000..1b2c350f --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 业务操作类型 + * + * @author admin + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/CipherType.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/CipherType.java new file mode 100644 index 00000000..cf3dc120 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/CipherType.java @@ -0,0 +1,33 @@ +package com.jiuyv.sptccc.agile.common.enums; + +public enum CipherType { + + REGEX("1", "正则替换"), + ALGORITHM("2", "加密算法"); + + private final String key; + + private final String val; + + CipherType(String key, String val) { + this.key = key; + this.val = val; + } + + public String getKey() { + return key; + } + + public String getVal() { + return val; + } + + public static CipherType getCipherType(String cipherType) { + for (CipherType type : CipherType.values()) { + if (type.key.equals(cipherType)) { + return type; + } + } + return REGEX; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java new file mode 100644 index 00000000..00cff613 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/HttpMethod.java @@ -0,0 +1,37 @@ +package com.jiuyv.sptccc.agile.common.enums; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author admin + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/IDictEnum.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/IDictEnum.java new file mode 100644 index 00000000..fb524908 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/IDictEnum.java @@ -0,0 +1,14 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 字段代码通用枚举接口 + * 便于统一方式处理 + * @author zhouliang + * + */ +public interface IDictEnum { + + public String getCode(); + + public String getMsg(); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java new file mode 100644 index 00000000..8eeee8b4 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperateTypeEnum.java @@ -0,0 +1,7 @@ +package com.jiuyv.sptccc.agile.common.enums; + +public enum OperateTypeEnum { + ADD, + DELETE, + UPDATE; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java new file mode 100644 index 00000000..e8661418 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.jiuyv.sptccc.agile.common.enums; + +/** + * 操作人类别 + * + * @author admin + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java new file mode 100644 index 00000000..d8c642cc --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 全局异常 + * + * @author admin + */ +public class GlobalException extends RuntimeException +{ + + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java new file mode 100644 index 00000000..58a61171 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/ServiceException.java @@ -0,0 +1,73 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 业务异常 + * + * @author admin + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java new file mode 100644 index 00000000..12b883d3 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.jiuyv.sptccc.agile.common.exception; + +/** + * 工具类异常 + * + * @author admin + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java new file mode 100644 index 00000000..2e1ca827 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.jiuyv.sptccc.agile.common.exception.base; + +import com.jiuyv.sptccc.agile.common.utils.MessageUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author admin + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java new file mode 100644 index 00000000..0975083d --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +import com.jiuyv.sptccc.agile.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author admin + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 00000000..945fb5f9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author admin + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 00000000..19f5f35a --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author admin + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java new file mode 100644 index 00000000..84b4c313 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,82 @@ +package com.jiuyv.sptccc.agile.common.exception.file; + +import java.util.Arrays; + +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author admin + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/job/TaskException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/job/TaskException.java new file mode 100644 index 00000000..b0dc00a2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.jiuyv.sptccc.agile.common.exception.job; + +/** + * 计划策略异常 + * + * @author admin + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java new file mode 100644 index 00000000..8016ac7b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author admin + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java new file mode 100644 index 00000000..ef244a73 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author admin + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java new file mode 100644 index 00000000..b2f11e32 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +import com.jiuyv.sptccc.agile.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author admin + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserPasswordNotMatchException.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 00000000..7952aaed --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.jiuyv.sptccc.agile.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author admin + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java new file mode 100644 index 00000000..ae4c8287 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatableFilter.java @@ -0,0 +1,55 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author admin + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 00000000..06f5e4b7 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,77 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import com.jiuyv.sptccc.agile.common.utils.http.HttpHelper; + +/** + * 构建可重复读取inputStream的request + * + * @author admin + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = HttpHelper.getBodyString(request).getBytes("UTF-8"); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java new file mode 100644 index 00000000..1a33093f --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssFilter.java @@ -0,0 +1,76 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 防止XSS攻击的过滤器 + * + * @author admin + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || method.matches("GET") || method.matches("DELETE")) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..fc82a477 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,114 @@ +package com.jiuyv.sptccc.agile.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author admin + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java new file mode 100644 index 00000000..75d17c23 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/AppUtil.java @@ -0,0 +1,152 @@ +package com.jiuyv.sptccc.agile.common.utils; + + + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * 获取Spring Context 中的 bean + */ +@Component +public class AppUtil implements ApplicationContextAware { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AppUtil.class); + + private static ApplicationContext context; + + + @Override + public void setApplicationContext(ApplicationContext _context) + throws BeansException { + setContext(_context); + + } + + private static void setContext(ApplicationContext _context) { + AppUtil.context = _context; + } + + /** + * 获取spring容器上下文。 + * + * @return ApplicationContext + */ + public static ApplicationContext getApplicaitonContext() { + return context; + } + + /** + * 根据beanId获取配置在系统中的对象实例。 + * + * @param beanId + * @return Object + * @throws + * @since 1.0.0 + */ + public static Object getBean(String beanId) { + try { + return context.getBean(beanId); + } catch (Exception ex) { + LOGGER.debug("getBean:" + beanId + "," + ex.getMessage()); + } + return null; + } + + /** + * 获取Spring容器的Bean + * + * @param beanClass + * @return T + * @throws + * @since 1.0.0 + */ + public static T getBean(Class beanClass) { + try { + return context.getBean(beanClass); + } catch (Exception ex) { + LOGGER.debug("getBean:" + beanClass + "," + ex.getMessage()); + } + return null; + } + + + /** + * 获取接口的实现类实例。 + * + * @param clazz + * @return + */ + public static Map getImplInstance(Class clazz){ + return context.getBeansOfType(clazz); + } + + public static List getImplInstanceArray(Class clazz){ + List list = new ArrayList<>(); + + Map map = context.getBeansOfType(clazz); + + for (T t : map.values()) { + list.add(t); + } + return list; + } + + + /** + * 发布事件。 + * + * @param event void + */ + public static void publishEvent(ApplicationEvent event) { + if (context != null) { + context.publishEvent(event); + } + } + + + /** + * 获取当前系统环境
+ * 目前仅支持单一环境配置。请勿配置多个参数 dev sit 之类。 环境配置的判断参考下面代码
+ * doGetActiveProfiles().contains(profile) || (doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile)) + * + * @return + */ + private static String currentProfiles = null; + + public static String getCtxEnvironment() { + if (currentProfiles != null) { + return currentProfiles; + } + + Environment environment = context.getEnvironment(); + String[] activeProfiles = environment.getActiveProfiles(); + + if (activeProfiles.length>0) { + currentProfiles = activeProfiles[0]; + return currentProfiles; + } + + String[] defaultProfiles = environment.getDefaultProfiles(); + if (defaultProfiles.length>0) { + currentProfiles = defaultProfiles[0]; + return defaultProfiles[0]; + } + + throw new ServiceException("查找不到正确的环境属性配置!", 9999); + } + + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java new file mode 100644 index 00000000..6b5197de --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author admin + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java new file mode 100644 index 00000000..1b29309b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/DateUtils.java @@ -0,0 +1,163 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; + +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author admin + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static final String YYYY = "yyyy"; + + public static final String YYYY_MM = "yyyy-MM"; + + public static final String YYYY_MM_DD = "yyyy-MM-dd"; + + public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", + "yyyy.MM.dd HH:mm", "yyyy.MM" }; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60L; + long nh = 1000 * 60 * 60L; + long nm = 1000 * 60L; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java new file mode 100644 index 00000000..0dfcd0d0 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ExceptionUtil.java @@ -0,0 +1,40 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author admin + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonPage.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonPage.java new file mode 100644 index 00000000..935d0080 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonPage.java @@ -0,0 +1,54 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.Serializable; +import java.util.List; + +//@Accessors(chain = true) +public class JsonPage implements Serializable { + + private static final long serialVersionUID = 1L; + + private Integer pageNum; + private Integer pageSize; + private Integer total; + private List data; + + public JsonPage(Long pageNum, Long pageSize, Long total, List data) { + this.pageNum = pageNum.intValue(); + this.pageSize = pageSize.intValue(); + this.total = total.intValue(); + this.data = data; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java new file mode 100644 index 00000000..b3c54edf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/JsonUtil.java @@ -0,0 +1,292 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonGenerator.Feature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jiuyv.sptccc.agile.common.annotation.SensitiveData; +import com.jiuyv.sptccc.agile.common.annotation.TruncatedContent; +import com.jiuyv.sptccc.agile.common.utils.jackson.MaskSensitiveDataSerializerProvider; + +/** + * + * @author zhouliang + * + */ +public abstract class JsonUtil { + + /** + * The Constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); + + private JsonUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * The object mapper. + */ + private static ObjectMapper objectMapper = new ObjectMapper(); + //专门使用一个独立的,处理敏感字段等 + private static ObjectMapper objectMapper2 = new ObjectMapper(); + + static { + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.setSerializationInclusion(Include.NON_NULL); + objectMapper.configure(Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); + objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + + + objectMapper2.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper2.setSerializationInclusion(Include.NON_NULL); + objectMapper2.configure(Feature.WRITE_BIGDECIMAL_AS_PLAIN, true); + objectMapper2.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + // 启用自定义注解功能 + objectMapper2.setAnnotationIntrospector(new JsonUtil.SensitiveDataIntrospector()); + objectMapper2.setSerializerProvider(new MaskSensitiveDataSerializerProvider()); + } + public static ObjectMapper JsonMapper(){ + return objectMapper; + } + public static ObjectMapper JsonMapper2(){ + return objectMapper2; + } + + /** + * 对字段太长的字段进行截取,不需要关注值,只关心有没有 + * @author zhouliang + *, + */ + public static class TruncatedIntrospector extends JacksonAnnotationIntrospector { + private static final long serialVersionUID = 1L; + @Override + public Object findSerializer(Annotated annotated) { + if (annotated.hasAnnotation(TruncatedContent.class)) { + TruncatedContent jsonProperty = annotated.getAnnotation(TruncatedContent.class); + return new TruncatedStringSerializer(jsonProperty.length()); + + } + return super.findSerializer(annotated); + } + } + + public static class TruncatedStringSerializer extends JsonSerializer { + private int MAX_LENGTH = 2000; + public TruncatedStringSerializer(){ + } + public TruncatedStringSerializer(int length) { + this.MAX_LENGTH = length; + } + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + if (value.length() > MAX_LENGTH) { + gen.writeString(value.substring(0, MAX_LENGTH) + "..."); + } else { + gen.writeString(value); + } + } + } + + /** + * 通过注解处理敏感字段,自行在需要的注入 + * @author zhouliang + * + */ + public static class SensitiveDataIntrospector extends JacksonAnnotationIntrospector { + private static final long serialVersionUID = 1L; + @Override + public Object findSerializer(Annotated annotated) { + if (annotated.hasAnnotation(SensitiveData.class)) { + SensitiveData sensitiveDataProperty = annotated.getAnnotation(SensitiveData.class); + return new JsonSerializer() { + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if(value!=null) { + String val=StringUtils.padl("",6, sensitiveDataProperty.defaultMark()); + //大于允许截取 + if(value.length()*2/3>=(sensitiveDataProperty.firstLength()+sensitiveDataProperty.endLength())) { + gen.writeString(value.substring(0,sensitiveDataProperty.firstLength())+ val+ value.substring(value.length()-sensitiveDataProperty.endLength())); + }else { + gen.writeString(val); + } + } + } + }; + } + return super.findSerializer(annotated); + } + } + + + /** + * 转为json字符串 + * + * @param object the object + * @return the string + */ + public static String toJSONString(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return ""; + } + } + + /** + * json字符串转为对象 + * @param + * @param json + * @param clz + * @return + */ + public static T json2Bean(String json, Class clz) { + try { + return objectMapper.readValue(json, clz); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * json字符串转为对象 + * @param + * @param json + * @param clz + * @return + */ + public static T json2Bean(String json, JavaType clz) { + try { + return objectMapper.readValue(json, clz); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + + /** + * 转为对象集合 + * @param + * @param json + * @param clz + * @return + */ + public static List json2List(String json, Class clz) { + try { + JavaType javaType = getCollectionType(ArrayList.class, clz); + return objectMapper.readValue(json, javaType); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return new ArrayList<>(); + } + } + + /** + * 获取泛型的Collection Type + * + * @param collectionClass 泛型的Collection + * @param elementClasses 元素类 + * @return JavaType Java类型 + * @since 1.0 + */ + public static JavaType getCollectionType(Class collectionClass, Class... elementClasses) { + return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + + /** + * json字符转ArrayNode + * @param json + * @return + */ + public static ArrayNode parseArray(String json) { + try { + return (ArrayNode) objectMapper.readTree(json); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * json字符转ObjectNode + * @param json + * @return + */ + public static ObjectNode parseObject(String json) { + try { + return (ObjectNode) objectMapper.readTree(json); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return null; + } + } + /** + * 创建ArrayNode + * @param json + * @return + */ + public static ArrayNode createArray() { + return objectMapper.createArrayNode(); + } + /** + * 创建ObjectNode + * @param json + * @return + */ + public static ObjectNode createObject() { + return objectMapper.createObjectNode(); + } + /** + * 集合实体对象转ArrayNode + * @param json + * @return + */ + public static ArrayNode toArray(Object obj) { + try { + return objectMapper.valueToTree(obj); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return createArray(); + } + } + /** + * 实体对象转ObjectNode + * @param json + * @return + */ + public static ObjectNode toObject(Object obj) { + try { + return objectMapper.valueToTree(obj); + } catch (Exception e) { + LOGGER.error("convert failed", e); + return createObject(); + } + } + /** 拷贝对象 + * 主要还是是用于Node拷贝 + * */ + public static void copyProperties(K source, V target) { + try { + objectMapper.updateValue(target,source); + } catch (Exception e) { + LOGGER.error("convert failed", e); + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java new file mode 100644 index 00000000..4a7e576e --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.common.utils; + +/** + * 处理并记录日志文件 + * + * @author admin + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java new file mode 100644 index 00000000..9e0a7b04 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/MessageUtils.java @@ -0,0 +1,41 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.util.Locale; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author admin + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * 输出到日志时指定一个固定编码(一般就英文吧) + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String messageDef(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, Locale.US); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/NumberUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/NumberUtil.java new file mode 100644 index 00000000..c3b29dd9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/NumberUtil.java @@ -0,0 +1,92 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.math.BigDecimal; + +import org.apache.commons.lang3.StringUtils; + +/** + * 数字工具类 + */ +public class NumberUtil { + + private NumberUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * 提供精确的加法运算
+ * 如果传入多个值为null或者空,则返回0 + * + * @param values 多个被加值 + * @return 和 + * @since 4.0.0 + */ + public static BigDecimal add(String... values) { + if (values==null||values.length==0) { + return BigDecimal.ZERO; + } + + String value = values[0]; + BigDecimal result = new BigDecimal(value); + for (int i = 1; i < values.length; i++) { + value = values[i]; + if (StringUtils.isNotBlank(value)) { + result = result.add(new BigDecimal(value)); + } + } + return result; + } + + /** + * 提供精确的乘法运算 + * + * @param v1 被乘数 + * @param v2 乘数 + * @return 积 + * @since 3.0.8 + */ + public static BigDecimal mul(String v1, String v2) { + return mul(new BigDecimal(v1), new BigDecimal(v2)); + } + + /** + * 提供精确的乘法运算
+ * 如果传入多个值为null或者空,则返回0 + * + * @param values 多个被乘值 + * @return 积 + * @since 4.0.0 + */ + public static BigDecimal mul(String... values) { + if (values==null||values.length==0) { + return BigDecimal.ZERO; + } + + BigDecimal result = new BigDecimal(values[0]); + for (int i = 1; i < values.length; i++) { + result = result.multiply(new BigDecimal(values[i])); + } + + return result; + } + + /** + * 提供精确的乘法运算
+ * 如果传入多个值为null或者空,则返回0 + * + * @param values 多个被乘值 + * @return 积 + * @since 4.0.0 + */ + public static BigDecimal mul(BigDecimal... values) { + if (values==null||values.length==0) { + return BigDecimal.ZERO; + } + + BigDecimal result = values[0]; + for (int i = 1; i < values.length; i++) { + result = result.multiply(values[i]); + } + return result; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java new file mode 100644 index 00000000..8f4b01cf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import com.github.pagehelper.PageHelper; +import com.jiuyv.sptccc.agile.common.core.page.PageDomain; +import com.jiuyv.sptccc.agile.common.core.page.TableSupport; +import com.jiuyv.sptccc.agile.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author admin + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java new file mode 100644 index 00000000..a38152fd --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ReflectUtil.java @@ -0,0 +1,138 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * 反射工具类 + */ +public class ReflectUtil { + private ReflectUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * 判断 beanClass 是否存在 name 字段 + * + * @param beanClass 实体类类型 + * @param name 字段名 + * @return boolean 是否存在 + */ + public static boolean hasField(Class beanClass, String name) { + Field field = ReflectionUtils.findField(beanClass, name); + return field != null; + } + + /** + * 获取 beanClass 中的 name 字段 + * + * @param beanClass 实体类类型 + * @param name 字段名 + * @return Field 字段对象 + * @throws ServiceException 如果不存在该字段,则抛出此异常 + */ + public static Field getField(Class beanClass, String name) throws ServiceException { + Field field = ReflectionUtils.findField(beanClass, name); + if (field == null) { + throw new ServiceException("该类中不存在该字段"); + } + return field; + } + + /** + * 获取 beanClass 中的所有字段 + * + * @param beanClass 实体类类型 + * @return Field[] 字段对象数组 + */ + public static Field[] getFields(Class beanClass) { + return beanClass.getDeclaredFields(); + } + /** + * 获取 beanClass 中的所有字段,包括其父类中的字段 + * + * @param beanClass 实体类类型 + * @return Field[] 字段对象数组 + */ + public static Field[] getAllFields(Class beanClass) { + return getFieldsDirectly(beanClass, true); + } + /** + * 获取 beanClass 中所有的字段,包括其父类中的字段 + * + * @param beanClass 实体类类型 + * @param withSuperClassFields 是否包含父类中的属性 + * @return Field[] 字段对象数组 + * @throws ServiceException 安全检查异常 + */ + public static Field[] getFieldsDirectly(Class beanClass, boolean withSuperClassFields) throws ServiceException { + Assert.notNull(beanClass,"beanClass不能为空"); + if (withSuperClassFields) { + Field[] allFields = null; + Class searchType = beanClass; + Field[] declaredFields; + while (searchType != null) { + declaredFields = searchType.getDeclaredFields(); + if (null == allFields) { + allFields = declaredFields; + }else{ + Field[] mergedArray = Arrays.copyOf(allFields, allFields.length + declaredFields.length); + System.arraycopy(declaredFields, 0, mergedArray, allFields.length, declaredFields.length); + allFields=mergedArray; + } + searchType = searchType.getSuperclass(); + } + return allFields; + } else { + return beanClass.getDeclaredFields(); + } + } + + /** + * 使用set方法赋值,如果没有则不赋值 + * + * @param obj 实体类对象 + * @param field 字段对象 + * @param value 字段值 + */ + public static void setFieldValue(Object obj, String field, Object value) { + Assert.notNull(obj,"obj不能为空"); + Assert.notNull(field,"field不能为空"); + try { + Method method = ReflectionUtils.findMethod(obj.getClass(), "set"+StringUtils.capitalize(field)); + if(method!=null) { + method.invoke(obj, value); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + //忽略 + } + } + /** + * 使用set方法赋值,如果没有则不赋值 + * + * @param obj 实体类对象 + * @param field 字段对象 + * @param value 字段值 + */ + public static void setFieldValue(Object obj, Field field, Object value) { + Assert.notNull(obj,"obj不能为空"); + Assert.notNull(field,"field不能为空"); + try { + Method method = ReflectionUtils.findMethod(field.getDeclaringClass(), "set"+StringUtils.capitalize(field.getName())); + if(method!=null) { + method.invoke(obj, value); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + //忽略 + } + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java new file mode 100644 index 00000000..f4362432 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/SecurityUtils.java @@ -0,0 +1,123 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUserSimple; + +/** + * 安全服务工具类 + * 这里没有登陆用户,都是由网关传过来(header) + * + * @author admin + */ +public class SecurityUtils +{ + //这个在切面处理好,方法截取在 + private static ThreadLocal localUser = new ThreadLocal<>(); + + /** + * 用户ID + */ + public static Long getUserId() + { + if(localUser.get() != null) { + return localUser.get().getUserId(); + } + return null; + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + if(localUser.get() != null) { + return localUser.get().getUserName(); + } + return null; + } + /** + * 获取用户姓名 + */ + public static String getNickname() + { + if(localUser.get() != null) { + return localUser.get().getNickName(); + } + return null; + } + + + /** + * 获取部门ID + */ + public static Long getDeptId() + { + if(localUser.get() != null) { + return localUser.get().getDeptId(); + } + return null; + } + + /** + * 获取用户 + */ + public static LoginUserSimple getLoginUser() + { + return localUser.get(); + } + /** + * 从header解析用户,并放入当前线程(切面中处理) + * 有用户则返回true,反之false + */ + public static boolean setLoginUser() { + try + { + //从header取出来,如有必要可以放临时缓存 + String userjson=ServletUtils.getRequest().getHeader(Constants.USERINFO_KEY); + if(StringUtils.isBlank(userjson)) { + return false; + } + LoginUserSimple user=JsonUtil.json2Bean(userjson, LoginUserSimple.class); + localUser.set(user); + return true; + } + catch (Exception e) + { + //这里不报错 + } + return false; + } + /** + * 清理当前用户 + */ + public static void clearLoginUser() { + localUser.remove(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java new file mode 100644 index 00000000..c4ee02d9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ServletUtils.java @@ -0,0 +1,262 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author admin + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return request.getParameter(name); + } + return null; + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toStr(request.getParameter(name), defaultValue); + } + + return null; + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toInt(request.getParameter(name)); + } + return null; + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toInt(request.getParameter(name), defaultValue); + } + return null; + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toBool(request.getParameter(name)); + } + return Boolean.FALSE; + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + HttpServletRequest request = getRequest(); + if(null != request) { + return Convert.toBool(request.getParameter(name), defaultValue); + } + return Boolean.FALSE; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + ServletRequestAttributes requestAttributes = getRequestAttributes(); + if(null != requestAttributes) { + return requestAttributes.getRequest(); + } + return null; + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + ServletRequestAttributes requestAttributes = getRequestAttributes(); + if(null != requestAttributes) { + return requestAttributes.getResponse(); + } + return null; + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + HttpServletRequest request = getRequest(); + if(null != request) { + return request.getSession(); + } + return null; + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + if(null != attributes) { + return (ServletRequestAttributes) attributes; + } + return null; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 写入图片信息 + * @param response + * @param fileType + * @param fileName + * @param fileContent + */ + public static void renderImg(HttpServletResponse response, String fileType, + String fileName, byte[] fileContent) { + response.setContentType(getContentType(fileType)); + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + try { + response.getOutputStream().write(fileContent); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String getContentType(String fileType) { + if("gif".equalsIgnoreCase(fileType)) { + return "image/gif"; + }else if ("jpg".equalsIgnoreCase(fileType)||"jpeg".equalsIgnoreCase(fileType)) { + return "image/jpg"; + }else if ("png".equalsIgnoreCase(fileType)) { + return "image/png"; + }else if ("bmp".equalsIgnoreCase(fileType)) { + return "image/bmp"; + }else { + return "application/octet-stream"; + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java new file mode 100644 index 00000000..de7043b0 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/StringUtils.java @@ -0,0 +1,614 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.util.AntPathMatcher; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author admin + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + 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(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } + /** + * 判断是否为数字开头 + * @param str + * @return + */ + public static boolean isNumStart(String str) { + int chr = str.charAt(0); + if (chr < 48 || chr > 57) { + return false; + } + return true; + } + /** + * string 转 date + */ + public static Date toDate(String str) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String dateStr = str.replace("年","-").replace("月","-").replace("日",""); + Date date = null; + try { + date = simpleDateFormat.parse(dateStr); + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java new file mode 100644 index 00000000..b250a3f9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/Threads.java @@ -0,0 +1,75 @@ +package com.jiuyv.sptccc.agile.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author admin + */ +public class Threads { + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + + /** + * 停止线程池 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. 如果超时, 则调用shutdownNow, + * 取消在workQueue中Pending的任务,并中断所有阻塞函数. 如果仍然超時,則強制退出. 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + logger.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + logger.error(t.getMessage(), t); + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java new file mode 100644 index 00000000..6ad90e72 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanUtils.java @@ -0,0 +1,215 @@ +package com.jiuyv.sptccc.agile.common.utils.bean; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.exception.ServiceException; + +/** + * Bean 工具类 + * + * @author admin + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + private static final Logger LOGGER = LoggerFactory.getLogger(BeanUtils.class); + + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } + + + /** + * 拷贝集合 + * @param list + * @param clazz + * @return + */ + public static List copyToList(List list, Class clazz) { + List list1 = new ArrayList<>(); + for (T temp : list) { + V newInstance; + try { + newInstance = clazz.newInstance(); + copyProperties(temp, newInstance); + list1.add(newInstance); + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return list1; + } + } + return list1; + } + + /** + * 拷贝对象 + * @param t + * @param clazz + * @return + */ + public static V copyProperties(T t, Class clazz) { + V newInstance; + try { + newInstance = clazz.newInstance(); + copyProperties(t, newInstance); + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return null; + } + return newInstance; + } + /** + * 拷贝对象 + * @param source + * @param target + * @return + */ + public static void copyProperties(Object source, Object target) throws ServiceException { + org.springframework.beans.BeanUtils.copyProperties(source, target); + } + + /** + * 简单对象转为map + * @param obj + * @return + */ + public static Map beanToMap(Object obj) { + if (obj == null) { + return null; + } + Map map = new HashMap<>(); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor property : propertyDescriptors) { + String key = property.getName(); + // 过滤class属性 + if (!key.equals("class")) { + // 得到property对应的getter方法 + Object value = property.getReadMethod().invoke(obj); + map.put(key, value); + } + } + } catch (Exception e) { + LOGGER.error("convert failed {}", e); + return null; + } + return map; + } + + /** + * LIST转LIST + */ + public static List> beanToMap(List objList) { + if (objList == null || objList.isEmpty()) { + return new ArrayList<>(); + } + List> listMap = new ArrayList<>(); + for(Object obj : objList){ + listMap.add(beanToMap(obj)); + } + return listMap; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java new file mode 100644 index 00000000..3ac78aff --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/BeanValidators.java @@ -0,0 +1,25 @@ +package com.jiuyv.sptccc.agile.common.utils.bean; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author admin + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/EnumStringValidator.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/EnumStringValidator.java new file mode 100644 index 00000000..ce5e01fb --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/bean/EnumStringValidator.java @@ -0,0 +1,51 @@ +package com.jiuyv.sptccc.agile.common.utils.bean; + +import java.lang.reflect.Method; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import com.jiuyv.sptccc.agile.common.annotation.EnumCheckValue; +import com.jiuyv.sptccc.agile.common.enums.IDictEnum; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 校验string类型的枚举 + * @author Administrator + * + */ + +public class EnumStringValidator implements ConstraintValidator { + private Class enumClass; + + @Override + public void initialize(EnumCheckValue constraintAnnotation) { + enumClass = constraintAnnotation.value(); + } + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isBlank(value)) { + return true; // 允许为空 + } + try { + if(enumClass.isAssignableFrom(IDictEnum.class)) { + String[] codes=value.split(","); + Method method = enumClass.getMethod("values"); + + Object[] objs = new Object[0]; + IDictEnum[] ems = (IDictEnum[])method.invoke(objs); + for(IDictEnum x:ems){ + for(String cx:codes){ + if(cx.equals(x.getCode())){//区分大小写 + return true; + } + } + } + return false; + } + return true; + } catch (Exception e) { + return false; + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java new file mode 100644 index 00000000..7f197579 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileTypeUtils.java @@ -0,0 +1,94 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.io.File; +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author admin + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } + + /** + * 是否是图片 + * + * @param ext + * @return "jpg", "jpeg", "gif", "png", "bmp" 为文件后缀名者为图片 + */ + public static boolean isValidImageExt(String ext) { + ext = ext.toLowerCase(Locale.ENGLISH); + for (String s : MimeTypeUtils.getImageExtension()) { + if (s.equalsIgnoreCase(ext)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUploadUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUploadUtils.java new file mode 100644 index 00000000..4d0508f8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUploadUtils.java @@ -0,0 +1,298 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.exception.file.FileNameLengthLimitExceededException; +import com.jiuyv.sptccc.agile.common.exception.file.FileSizeLimitExceededException; +import com.jiuyv.sptccc.agile.common.exception.file.InvalidExtensionException; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author admin + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = ConsoleConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.getDefaultAllowedExtension()); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.getDefaultAllowedExtension()); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = ConsoleConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.getImageExtension()) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.getFlashExtension()) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.getMediaExtension()) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.getVideoExtension()) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param fileName 上传的文件名称(非必传) + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file, String fileName) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.getDefaultAllowedExtension(),fileName); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 编码文件名UUID形式 + */ + public static final String extractUuidFilename(MultipartFile file) { + return StringUtils.format("{}.{}", IdUtils.fastUUID(), getExtension(file)); + } + + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, String fileName) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + // 若未传入对应的文件名称,则使用默认的文件命名规则 + if (StringUtils.isBlank(fileName)) { + fileName = extractFilename(file); + } + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUtils.java new file mode 100644 index 00000000..12504486 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/FileUtils.java @@ -0,0 +1,293 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.util.StreamUtils; + +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.uuid.IdUtils; + +/** + * 文件处理工具类 + * + * @author admin + */ +public class FileUtils { + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + private FileUtils() { + throw new IllegalStateException("Utility class"); + } + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException { + try(FileInputStream fis = new FileInputStream(new File(filePath))){ + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) { + os.write(b, 0, length); + } + } + } + +// public static void writeBytes2(String filePath, OutputStream os) throws IOException { +// FileInputStream fis = null; +// try { +// File file = new File(filePath); +// if (!file.exists()) { +// throw new FileNotFoundException(filePath); +// } +// fis = new FileInputStream(file); +// byte[] b = new byte[1024]; +// int length; +// while ((length = fis.read(b)) > 0) { +// os.write(b, 0, length); +// } +// } catch (IOException e) { +// throw e; +// } finally { +// if(null != fis) { +// IOUtils.close(fis); +// } +// IOUtils.close(os); +// +// } +// } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException { + return writeBytes(data, ConsoleConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException { + String extension = getFileExtendName(data); + String pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + try(FileOutputStream fos = new FileOutputStream(FileUploadUtils.getAbsoluteFile(uploadDir, pathName))){ + fos.write(data); + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + } +// public static String writeBytes2(byte[] data, String uploadDir) throws IOException { +// FileOutputStream fos = null; +// String pathName = ""; +// try { +// String extension = getFileExtendName(data); +// pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; +// File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); +// fos = new FileOutputStream(file); +// fos.write(data); +// } finally { +// IOUtils.close(fos); +// } +// return FileUploadUtils.getPathFileName(uploadDir, pathName); +// } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) { + boolean delete = file.delete(); + flag = delete; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.getDefaultAllowedExtension(), FileTypeUtils.getFileType(resource))) { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) + throws UnsupportedEncodingException { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } else if (agent.contains("Firefox")) { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } else if (agent.contains("Chrome")) { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } else { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) + throws UnsupportedEncodingException { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";") + .append("filename*=").append("utf-8''").append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) { + strFileExtendName = "gif"; + } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) { + strFileExtendName = "jpg"; + } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) { + strFileExtendName = "bmp"; + } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) { + if (fileName == null) { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) { + if (fileName == null) { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } + + + + /** + * 读取文件为字符串(jar包内的文件) + * + * @param filePath 文件路径 + * @return 文件内容字符串 + */ + public static String readResourceToString(String filePath) throws ServiceException { + try { + return StreamUtils.copyToString(FileUtils.class.getClassLoader().getResourceAsStream(filePath), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new ServiceException("文件不存在"); + } + } + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java new file mode 100644 index 00000000..3d915ead --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,80 @@ +package com.jiuyv.sptccc.agile.common.utils.file; + +import java.util.Arrays; +import java.util.Collections; + +/** + * 媒体类型工具类 + * + * @author admin + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + private static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static String[] getImageExtension() { + return Collections.unmodifiableList(Arrays.asList(IMAGE_EXTENSION)).toArray(new String[IMAGE_EXTENSION.length]); + } + + private static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static String[] getFlashExtension() { + return Collections.unmodifiableList(Arrays.asList(FLASH_EXTENSION)).toArray(new String[FLASH_EXTENSION.length]); + } + + private static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static String[] getMediaExtension() { + return Collections.unmodifiableList(Arrays.asList(MEDIA_EXTENSION)).toArray(new String[MEDIA_EXTENSION.length]); + } + + private static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static String[] getVideoExtension() { + return Collections.unmodifiableList(Arrays.asList(VIDEO_EXTENSION)).toArray(new String[VIDEO_EXTENSION.length]); + } + + private static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + + public static String[] getDefaultAllowedExtension() { + return Collections.unmodifiableList(Arrays.asList(DEFAULT_ALLOWED_EXTENSION)).toArray(new String[DEFAULT_ALLOWED_EXTENSION.length]); + } + + public static String getExtension(String prefix) { + switch (prefix) { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java new file mode 100644 index 00000000..b641c516 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/EscapeUtil.java @@ -0,0 +1,155 @@ +package com.jiuyv.sptccc.agile.common.utils.html; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author admin + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java new file mode 100644 index 00000000..cebbce0e --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.jiuyv.sptccc.agile.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author admin + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java new file mode 100644 index 00000000..f1848bdf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpHelper.java @@ -0,0 +1,57 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +import javax.servlet.ServletRequest; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author admin + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java new file mode 100644 index 00000000..92a252d2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpRequestUtil.java @@ -0,0 +1,967 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.net.ssl.SSLContext; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.FileEntity; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; + +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.uuid.Seq; + + +/** + * 描述 用于发送http请求的工具类 + */ + +public class HttpRequestUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(HttpRequestUtil.class); + + private static PoolingHttpClientConnectionManager connMgr; + private static RequestConfig requestConfig; + private static final int MAX_TIMEOUT = 7000; + + private HttpRequestUtil() { + throw new IllegalStateException("Utility class"); + } + + static { + // 设置连接池 + connMgr = new PoolingHttpClientConnectionManager(); + // 设置连接池大小 + connMgr.setMaxTotal(100); + connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal()); + + RequestConfig.Builder configBuilder = RequestConfig.custom(); + // 设置连接超时 + configBuilder.setConnectTimeout(MAX_TIMEOUT); + // 设置读取超时 + configBuilder.setSocketTimeout(MAX_TIMEOUT); + // 设置从连接池获取连接实例的超时 + configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT); + requestConfig = configBuilder.build(); + } + + /** + * 填充请求头参数至get请求中 + * + * @param httpPost + * @param headers + * @return + */ + private static HttpGet setHeadersToGet(HttpGet httpGet, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpGet.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpGet; + } /** + * 填充请求头参数至Post请求中 + * + * @param httpPost + * @param headers + * @return + */ + private static HttpPost setHeadersToPost(HttpPost httpPost, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpPost.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpPost; + } /** + * 填充请求头参数至Put请求中 + * + * @param httpPut + * @param headers + * @return + */ + private static HttpPut setHeadersToPut(HttpPut httpPut, Map headers) { + if (headers != null && headers.size() != 0) { + for (Entry entrySet : headers.entrySet()) { + httpPut.addHeader(entrySet.getKey(), entrySet.getValue()); + } + } + return httpPut; + } /** + * 填充请求体参数至Post请求中 + * + * @param httpPost + * @param params + * @return + */ + private static HttpPost setParamsToRequest(HttpPost httpPost, Map params) { + if(params==null) { + params=new HashMap<>(); + } + List pairList = new ArrayList<>(params.size()); + for (Map.Entry entry : params.entrySet()) { + NameValuePair pair = null; + if (entry.getValue() == null) { + pair = new BasicNameValuePair(entry.getKey(), null); + } else { + pair = new BasicNameValuePair(entry.getKey(), entry + .getValue().toString()); + } + pairList.add(pair); + } + httpPost.setEntity(new UrlEncodedFormEntity(pairList, StandardCharsets.UTF_8)); + return httpPost; + } + + /** + * 设置get-url请求参数 + * @param apiUrl + * @param params + * @return + */ + private static String setUrlParams(String apiUrl, Map params) { + StringBuilder param = new StringBuilder(); + int i = 0; + if (params != null && params.size() > 0) { + for (Entry entrySet : params.entrySet()) { + if (i == 0) + param.append("?"); + else + param.append("&"); + param.append(entrySet.getKey()).append("=").append(entrySet.getValue()); + i++; + } + } + apiUrl += param; + return apiUrl; + } + + /** + * 发送 GET 请求(HTTP),不带输入数据 + * + * @param url + * @return + */ + public static HttpResponseResult doGet(String url) { + return doGet(url, new HashMap<>(), new HashMap<>()); + } + + /** + * 发送 GET 请求(HTTP),K-V形式,无请求头参数 + * + * @param url + * @param params + * @return + */ + public static HttpResponseResult doGet(String url, Map params) { + return doGet(url, params, null); + } + + /** + * 发送 GET 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doGet(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig) + .setConnectionManagerShared(true).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + String result = null; + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet = setHeadersToGet(httpGet, headers); + response = httpClient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + result = IOUtils.toString(instream, StandardCharsets.UTF_8); + requestResult.setBody(result); + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + /** + * 发送 GET 请求(HTTP),K-V形式,下载文件,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @param out 存储文件到指定目录或者写入流 + * @return + */ + public static HttpResponseResult doGetFile(String url, Map params, Map headers,Object out) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig) + .setConnectionManagerShared(true).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet = setHeadersToGet(httpGet, headers); + response = httpClient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + if(out instanceof String) {//路径不要指定名称 + // 创建用于存储文件的File对象 + String path = out.toString(); + File file = new File(path); + //文件名称,后面再添加一个id串 + String filename="myfile_"+Seq.getId()+".tar.gz"; + if(!file.exists()) { + if(!file.getName().contains(".")) {//无文件名 + file = new File(path+File.separator+filename); + }else { + filename=file.getName(); + } + }else { + filename=file.getName(); + } + // 创建用于写入文件的FileOutputStream对象 + try (FileOutputStream outstream = new FileOutputStream(file)) { + // 将获取到的数据流写入本地文件中 + IOUtils.copy(instream, outstream); + // 关闭所有打开的资源 + instream.close(); + } + requestResult.setBody(filename); + requestResult.setMessage("文件下载成功"); + }else if(out instanceof HttpServletResponse){ + long contentLength = entity.getContentLength(); + + HttpServletResponse res = (HttpServletResponse) out; + + //文件名称,后面再添加一个id串 + String filename="myfile_"+Seq.getId()+".tar.gz"; + // 设置响应头 + res.setContentType("application/octet-stream"); + res.setContentLength((int)contentLength); + res.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); + // 写入响应流中 + OutputStream outputStream = res.getOutputStream(); + byte[] buffer = new byte[1024*1024]; + int len; + while ((len = instream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + outputStream.flush(); + outputStream.close(); + instream.close(); + } + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 POST 请求(HTTP),不带输入数据 + * + * @param url + * @return + */ + public static HttpResponseResult doPost(String url) { + return doPost(url, new HashMap<>(), new HashMap<>()); + } + + /** + * 发送 POST 请求(HTTP),JSON形式,无请求头参数 + * + * @param url + * @param json json对象 + * @return + */ + public static HttpResponseResult doPost(String url, Object json) { + return doPost(url, json, null); + } + + /** + * 发送 POST 请求(HTTP),K-V形式,无请求头参数 + * + * @param url + * @param params + * @return + */ + public static HttpResponseResult doPost(String url, Map params) { + return doPost(url, params, null); + } + + /** + * 发送 POST 请求(HTTP),K-V形式 ,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPost(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + String httpStr = null; + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + httpPost = setParamsToRequest(httpPost, params); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity entity = response.getEntity(); + if(entity!=null) { + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + } + requestResult.setBody(httpStr); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 POST 请求(HTTP),JSON形式,有请求头参数 + * + * @param url + * @param json json对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPost(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + String httpStr = null; + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPost.setEntity(stringEntity); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity entity = response.getEntity(); + if(entity!=null) { + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + } + requestResult.setBody(httpStr); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL POST 请求(HTTPS),无K-V形式参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doPostSSL(String url) { + return doPostSSL(url, null, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doPostSSL(String url, Map params) { + return doPostSSL(url, params, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Map params, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpEntity entity = null; + httpPost.setConfig(requestConfig); + + httpPost = setHeadersToPost(httpPost, headers); + httpPost = setParamsToRequest(httpPost, params); + response = httpClient.execute(httpPost); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL POST 请求(HTTPS),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Object json) { + return doPostSSL(url, json, null); + } + + /** + * 发送 SSL POST 请求(HTTPS),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPostSSL(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPost httpPost = new HttpPost(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + HttpEntity entity = null; + httpPost.setConfig(requestConfig); + httpPost = setHeadersToPost(httpPost, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPost.setEntity(stringEntity); + response = httpClient.execute(httpPost); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 SSL GET 请求(HTTPs),无参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doGetSSL(String url) { + return doGetSSL(url, null, null); + } + + /** + * 发送 SSL GET 请求(HTTPs),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doGetSSL(String url, Map params) { + return doGetSSL(url, params, null); + } + + /** + * 发送 SSL GET 请求(HTTPs),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doGetSSL(String url, Map params, Map headers) { + + CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build(); + HttpGet httpGet = null; + HttpResponse response = null; + HttpResponseResult requestResult = new HttpResponseResult(); + String apiUrl=setUrlParams(url, params); + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + String result = null; + try { + HttpEntity entity = null; + httpGet = new HttpGet(apiUrl); + httpGet.setConfig(requestConfig); + httpGet = setHeadersToGet(httpGet, headers); + response = httpclient.execute(httpGet); + if (response != null) { + entity = response.getEntity(); + } + + if (entity != null) { + InputStream instream = entity.getContent(); + result = IOUtils.toString(instream, StandardCharsets.UTF_8); + requestResult.setBody(result); + } + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } /** + * 发送 PUT 请求(HTTP),无参数,无请求头参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult put(String url) { + return put(url, null, null); + } + + /** + * 发送 PUT 请求(HTTP),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult put(String url, Map params) { + return put(url, params, null); + } + + /** + * 发送 PUT 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult put(String url, Map params, Map headers) { + CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpEntity entity = null; + HttpResponseResult requestResult = new HttpResponseResult(); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpPut httpPut = new HttpPut(url); + if (headers != null) { + Set set = headers.keySet(); + for (String item : set) { + String value = headers.get(item); + httpPut.addHeader(item, value); + } + } + if (params != null) { + List paramList = new ArrayList<>(); + for (Map.Entry param : params.entrySet()) { + NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue()); + paramList.add(pair); + } + httpPut.setEntity(new UrlEncodedFormEntity(paramList, StandardCharsets.UTF_8.name())); + } + response = client.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + try { + if (response != null) { + EntityUtils.consume(response.getEntity()); + } + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } /** + * 发送 SSL PUT 请求(HTTP),JSON形式,无请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @return + */ + public static HttpResponseResult doPut(String url, Object json) { + return doPut(url, json, null); + } + + /** + * 发送 PUT 请求(HTTP),JSON形式,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doPut(String url, Object json, Map headers) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPut httpPut = new HttpPut(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(json)); + try { + HttpEntity entity = null; + httpPut.setConfig(requestConfig); + httpPut = setHeadersToPut(httpPut, headers); + StringEntity stringEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);//解决中文乱码问题 + stringEntity.setContentEncoding(StandardCharsets.UTF_8.name()); + stringEntity.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpPut.setEntity(stringEntity); + response = httpClient.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + + /** + * 发送 PUT 请求(HTTP),传文件,有请求头参数 + * + * @param url API接口URL + * @param json JSON对象 + * @param headers 请求头参数 + * @param headers 请求头参数 + * @return in 文件来源:文件路径或输入流 + */ + public static HttpResponseResult doPutFile(String url, Map headers,Object in) { + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpResponseResult requestResult = new HttpResponseResult(); + HttpPut httpPut = new HttpPut(url); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params=null",url); + try { + HttpEntity entity = null; + httpPut.setConfig(requestConfig); + httpPut = setHeadersToPut(httpPut, headers); + + if(in instanceof String) {//路径不要指定名称 + File file = new File(in.toString()); + FileEntity reqEntity = new FileEntity(file, ContentType.APPLICATION_OCTET_STREAM); + httpPut.setEntity(reqEntity); + }else if(in instanceof InputStream){ + InputStreamEntity reqEntity = new InputStreamEntity((InputStream)in, -1, ContentType.APPLICATION_OCTET_STREAM); + httpPut.setEntity(reqEntity); + } + response = httpClient.execute(httpPut); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + requestResult.setMessage("文件上传成功"); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + if (response != null) { + try { + EntityUtils.consume(response.getEntity()); + } catch (IOException e) { + LOGGER.error("【http请求异常】{0}",e); + } + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 发送 DELETE 请求(HTTP),无参数 + * + * @param url API接口URL + * @return + */ + public static HttpResponseResult doDelete(String url) { + return doDelete(url, null, null); + } + + /** + * 发送 DELETE 请求(HTTP),K-V形式,无请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @return + */ + public static HttpResponseResult doDelete(String url, Map params) { + return doDelete(url, params, null); + } + + /** + * 发送 DELETE 请求(HTTP),K-V形式,有请求头参数 + * + * @param url API接口URL + * @param params 参数map + * @param headers 请求头参数 + * @return + */ + public static HttpResponseResult doDelete(String url, Map params, Map headers) { + CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpEntity entity = null; + HttpResponseResult requestResult = new HttpResponseResult(); + CloseableHttpResponse response = null; + String httpStr = null; + LOGGER.info("【http请求参数内容】url={}, params={}",url,JsonUtil.toJSONString(params)); + try { + HttpDelete httpDelete = new HttpDelete(url); + if (headers != null) { + Set set = headers.keySet(); + for (String item : set) { + String value = headers.get(item); + httpDelete.addHeader(item, value); + } + } + if (params != null) { + URIBuilder uriBuilder = new URIBuilder(url); + for (Entry keySet : params.entrySet()) { + uriBuilder.setParameter(keySet.getKey(), keySet.getValue()); + } + URI uri = uriBuilder.build(); + httpDelete.setURI(uri); + } + response = client.execute(httpDelete); + if (response != null) { + int statusCode = response.getStatusLine().getStatusCode(); + entity = response.getEntity(); + requestResult.setStatus(statusCode); + if (statusCode != HttpStatus.SC_OK || entity == null) { + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + } + + httpStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + requestResult.setBody(httpStr); + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } finally { + try { + if (response != null) { + EntityUtils.consume(response.getEntity()); + } + } catch (Exception e) { + LOGGER.error("【http请求异常】{0}",e); + } + requestResult.setStatus(response!=null?response.getStatusLine().getStatusCode():500); + } + LOGGER.info("【http请求返回内容】{}",requestResult.toString()); + return requestResult; + } + + /** + * 创建SSL安全连接(不知道对不对) + * + * @return + */ +// private static SSLConnectionSocketFactory createSSLConnSocketFactory() { +// SSLConnectionSocketFactory sslsf = null; +// try { +// SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { +// +// public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { +// return true; +// } +// }).build(); +// sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() { +// @Override +// public boolean verify(String arg0, SSLSession arg1) { +// return true; +// } +// }); +// } catch (GeneralSecurityException e) { +// LOGGER.error("【http请求异常】{0}",e); +// } +// return sslsf; +// } + private static SSLConnectionSocketFactory createSSLConnSocketFactory() { + SSLConnectionSocketFactory sslsf = null; + try { + // 创建信任所有证书的 TrustManager + TrustStrategy trustStrategy = (cert, authType) -> true; + // 创建 SSL 上下文,并设置协议和算法 + SSLContext sslContext = SSLContexts.custom() + .setProtocol("TLS") + .setSecureRandom(new SecureRandom()) + .loadTrustMaterial(null, trustStrategy) + .build(); + // 创建 SSLConnectionSocketFactory 对象,用于创建 SSL 连接 + sslsf=new SSLConnectionSocketFactory(sslContext); + } catch (GeneralSecurityException e) { + LOGGER.info("【http请求异常】",e); + } + return sslsf; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java new file mode 100644 index 00000000..c0d95fbf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpResponseResult.java @@ -0,0 +1,53 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +/** + * 描述 + */ + +public class HttpResponseResult { + + private int status; + + private String body;//如果是下载文件,自动生成的会返回文件名称 + + private String message; + + public HttpResponseResult() { + } + + public HttpResponseResult(int statusCode, String body, String message) { + this.status = statusCode; + this.body = body; + this.message = message; + } + + public int getStatus() { + return status; + } + + public void setStatus(int statusCode) { + this.status = statusCode; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + //对于返回body是json时,需要过滤则转换为treenode进行清理,或者不输出 + return "status="+status+", body="+body+", message="+message; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java new file mode 100644 index 00000000..a41fd6e6 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/http/HttpUtils.java @@ -0,0 +1,293 @@ +package com.jiuyv.sptccc.agile.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 通用http发送方法 + * + * @author admin + */ +public class HttpUtils { + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl; + try { + realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + try(BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType))){ + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result.toString(); + } +// public static String sendGet2(String url, String param, String contentType) { +// StringBuilder result = new StringBuilder(); +// BufferedReader in = null; +// try { +// String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; +// log.info("sendGet - {}", urlNameString); +// URL realUrl = new URL(urlNameString); +// URLConnection connection = realUrl.openConnection(); +// connection.setRequestProperty("accept", "*/*"); +// connection.setRequestProperty("connection", "Keep-Alive"); +// connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); +// connection.connect(); +// in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); +// String line; +// while ((line = in.readLine()) != null) { +// result.append(line); +// } +// log.info("recv - {}", result); +// } catch (ConnectException e) { +// log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); +// } catch (SocketTimeoutException e) { +// log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); +// } catch (IOException e) { +// log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); +// } catch (Exception e) { +// log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); +// } finally { +// try { +// if (in != null) { +// in.close(); +// } +// } catch (Exception ex) { +// log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); +// } +// } +// return result.toString(); +// } + + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) { + StringBuilder result = new StringBuilder(); + log.info("sendPost - {}", url); + URL realUrl; + try { + realUrl = new URL(url); + URLConnection conn; + try { + conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + try( + PrintWriter out = new PrintWriter(conn.getOutputStream()); + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))){ + out.print(param); + out.flush(); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result.toString(); + } + +// public static String sendPost2(String url, String param) { +// PrintWriter out = null; +// BufferedReader in = null; +// StringBuilder result = new StringBuilder(); +// try { +// log.info("sendPost - {}", url); +// URL realUrl = new URL(url); +// URLConnection conn = realUrl.openConnection(); +// conn.setRequestProperty("accept", "*/*"); +// conn.setRequestProperty("connection", "Keep-Alive"); +// conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); +// conn.setRequestProperty("Accept-Charset", "utf-8"); +// conn.setRequestProperty("contentType", "utf-8"); +// conn.setDoOutput(true); +// conn.setDoInput(true); +// out = new PrintWriter(conn.getOutputStream()); +// out.print(param); +// out.flush(); +// in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); +// String line; +// while ((line = in.readLine()) != null) { +// result.append(line); +// } +// log.info("recv - {}", result); +// } catch (ConnectException e) { +// log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); +// } catch (SocketTimeoutException e) { +// log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); +// } catch (IOException e) { +// log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); +// } catch (Exception e) { +// log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); +// } finally { +// try { +// if (out != null) { +// out.close(); +// } +// if (in != null) { +// in.close(); +// } +// } catch (IOException ex) { +// log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); +// } +// } +// return result.toString(); +// } + + public static String sendSSLPost(String url, String param) { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try { + log.info("sendSSLPost - {}", urlNameString); + //原来的SSL不行,先改了 + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) { + if (ret != null && !"".equals(ret.trim())) { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java new file mode 100644 index 00000000..c18fa8ae --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/AddressUtils.java @@ -0,0 +1,58 @@ +package com.jiuyv.sptccc.agile.common.utils.ip; + +import java.nio.charset.StandardCharsets; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author admin + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (ConsoleConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", StandardCharsets.UTF_8.name()); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + ObjectNode obj = JsonUtil.parseObject(rspStr); + String region = obj.get("pro").asText(); + String city = obj.get("city").asText(); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java new file mode 100644 index 00000000..fb2cb5d6 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/ip/IpUtils.java @@ -0,0 +1,229 @@ +package com.jiuyv.sptccc.agile.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import javax.servlet.http.HttpServletRequest; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author admin + */ +public class IpUtils { + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + 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(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + if(null != addr) { + return internalIp(addr) || "127.0.0.1".equals(ip); + } + return false; + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) { + if (StringUtils.isNull(addr) || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + return false; + case SECTION_5: + if(b1 == SECTION_6) { + return true; + } + return false; + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) { + if (org.apache.commons.lang3.StringUtils.isBlank(text)) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") >= 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (false == isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java new file mode 100644 index 00000000..a55266c8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/jackson/MaskSensitiveDataSerializerProvider.java @@ -0,0 +1,157 @@ +package com.jiuyv.sptccc.agile.common.utils.jackson; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.ObjectUtils; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import com.fasterxml.jackson.databind.ser.SerializerFactory; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + + +/** + * 敏感字段处理为*(不是过滤,方便确认有没有值) + * 无需注解,固定的可以直接写这里面 + * 如需要特殊处理修改自定义实现 + * @author zhouliang + * + */ +public class MaskSensitiveDataSerializerProvider extends DefaultSerializerProvider { + + private static final long serialVersionUID = 1L; + + //开始或结尾是,就认为是敏感字段​ + private static final String LIKE_FILEDS = "password,email,phone"; + //明确敏感字段 + private static final String STATIC_FILEDS = "idCard,socialCreditCode"; + + private static final Set STATIC_FILED_SET = new HashSet<>( + Arrays.stream(STATIC_FILEDS.split(",")) + .map(String::trim) + .filter(o -> !ObjectUtils.isEmpty(o)) + .collect(Collectors.toSet()) + ); + private static final Set LIKE_FILED_SET = new HashSet<>( + Arrays.stream(LIKE_FILEDS.split(",")) + .map(String::trim) + .filter(o -> !ObjectUtils.isEmpty(o)) + .collect(Collectors.toSet()) + ); + + public MaskSensitiveDataSerializerProvider() { + super(); + } + + public MaskSensitiveDataSerializerProvider(MaskSensitiveDataSerializerProvider src) { + super(src); + } + + protected MaskSensitiveDataSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) { + super(src, config, f); + } + + @Override + public DefaultSerializerProvider copy() { + if (this.getClass() != MaskSensitiveDataSerializerProvider.class) { + return super.copy(); + } + return new MaskSensitiveDataSerializerProvider(this); + } + + @Override + public MaskSensitiveDataSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) { + return new MaskSensitiveDataSerializerProvider(this, config, jsf); + } + + @Override + public JsonSerializer findValueSerializer(Class valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findValueSerializer(valueType, property); + } + + @Override + public JsonSerializer findValueSerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findValueSerializer(valueType, property); + } + + @Override + public JsonSerializer findPrimaryPropertySerializer(JavaType valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findPrimaryPropertySerializer(valueType, property); + } + + @Override + public JsonSerializer findPrimaryPropertySerializer(Class valueType, BeanProperty property) + throws JsonMappingException { + JsonSerializer target = findSerializerByBeanProperty(property); + if (target != null) { + return target; + } + return super.findPrimaryPropertySerializer(valueType, property); + } + + private JsonSerializer findSerializerByBeanProperty(BeanProperty property) { + String propertyName = Optional.ofNullable(property).map(BeanProperty::getName).orElse(""); + if (STATIC_FILED_SET.contains(propertyName)) { + return MaskSerializer.INSTANCE; + }else { + for(String f: LIKE_FILED_SET) { + Pattern p=Pattern.compile("(^"+f+")|("+f+"$)",Pattern.CASE_INSENSITIVE); + Matcher m=p.matcher(propertyName); + if(m.find()) { + return MaskSerializer.INSTANCE; + } + } + } + + return null; + } + + static class MaskSerializer extends JsonSerializer { + + public static final MaskSerializer INSTANCE = new MaskSerializer(); + private MaskSerializer() { + } + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if(value instanceof String) { + String value2=(String) value; + String val=StringUtils.padl("",6, '*'); + //大于允许截取 + if(value2.length()*2/3>=6) { + gen.writeString(value2.substring(0,3)+ val+ value2.substring(value2.length()-3)); + }else { + gen.writeString(val); + } + } + } + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java new file mode 100644 index 00000000..9cb6c689 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,412 @@ +package com.jiuyv.sptccc.agile.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.core.text.Convert; +import com.jiuyv.sptccc.agile.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author admin + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/ISftpProressService.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/ISftpProressService.java new file mode 100644 index 00000000..4be817cb --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/ISftpProressService.java @@ -0,0 +1,21 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp; + +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SftpProgress; + +/** + * sftp传输进度保存接口 + * 为了和实际业务拆分开,通过传入方式解决 + * @author zhouliang + * + */ +public interface ISftpProressService { + + /** + * 保存文件进度。调用progress.isFinish=true时表示文件传输完成 + * 自定义实现逻辑(最好是异步) + * 传输中失败,进度可能是滞后的。不过没有影响,传输时会使用服务器的纠正 + * @return + */ + public void doSaveFileProressing(SftpProgress progress); + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SFTPChannel.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SFTPChannel.java new file mode 100644 index 00000000..caf436a8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SFTPChannel.java @@ -0,0 +1,67 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp; + +import java.util.Properties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SFTPConfig; + +/** + * SFTP构建 + * 自己使用时记得手动关闭 + * @author zhouliang + * + */ +public class SFTPChannel { + Session session = null; + Channel channel = null; + + private static final Logger LOG = LoggerFactory.getLogger(SFTPChannel.class); + + /** + * 服务器连接对象 + * @param sftpDetails + * @param timeout + * @return + * @throws JSchException + */ + public ChannelSftp getChannel(SFTPConfig sftpDetails, int timeout) throws JSchException { + String ftpHost = sftpDetails.getHost(); + int ftpPort = sftpDetails.getPort(); + String ftpUserName = sftpDetails.getUsername(); + String ftpPassword = sftpDetails.getPassword(); + + JSch jsch = new JSch(); // 创建JSch对象 + session = jsch.getSession(ftpUserName, ftpHost, ftpPort); // 根据用户名,主机ip,端口获取一个Session对象 + if (ftpPassword != null) { + session.setPassword(ftpPassword); // 设置密码 + } + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); // 为Session对象设置properties + session.setTimeout(timeout); // 设置timeout时间 + session.connect(); // 通过Session建立链接 + channel = session.openChannel("sftp"); // 打开SFTP通道 + channel.connect(); // 建立SFTP通道的连接 + LOG.debug("Connected successfully to ftpHost = " + ftpHost + ",as ftpUserName = " + ftpUserName); + return (ChannelSftp) channel; + } + + /** + * 关闭连接 + */ + public void closeChannel() { + if (channel != null) { + channel.disconnect(); + } + if (session != null) { + session.disconnect(); + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SftpFileUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SftpFileUtils.java new file mode 100644 index 00000000..ba2acab4 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/SftpFileUtils.java @@ -0,0 +1,261 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.util.StreamUtils; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.SftpATTRS; +import com.jiuyv.sptccc.agile.common.exception.UtilException; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SFTPConfig; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SftpProgress; +import com.jiuyv.sptccc.agile.common.utils.sftp.monitor.SftpComplexProgressMonitor; +import com.jiuyv.sptccc.agile.common.utils.sftp.monitor.SftpSimpleProgressMonitor; + +import net.logstash.logback.encoder.org.apache.commons.lang3.StringUtils; + +/** + * sftp工具类 + * 都是单例连接操作后就会关闭,要多个操作的自己构建SFTPChannel + * @author zhouliang + * + */ +public class SftpFileUtils +{ + private SftpFileUtils() { + throw new IllegalStateException("Utility class"); + } + + private static final Logger LOG = LoggerFactory.getLogger(SftpFileUtils.class); + //sftp等待时间,单位毫秒 + private static final int TIMEOUT = 30000; + //是文件路径 + private static final Pattern FILE_SUFFIX = Pattern.compile("\\.[A-Z0-9]+$",Pattern.CASE_INSENSITIVE); + + /** + * 检查是否文件,不是则报错 + * @param path + * @throws Exception + */ + public static boolean isFile(String path) throws Exception { + if(StringUtils.isBlank(path)) { + return false; + } + Matcher m=FILE_SUFFIX.matcher(path); + return m.find(); + } + + /** + * 获取文件当前的实际size + * @param sftpDetails 配置 + * @param src 文件路径 :例如/home/sysfile/xxx.zip + * @throws Exception + */ + public static Long getFileSize(SFTPConfig sftpDetails,String src) throws Exception { + if(!isFile(src)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + Long size=null; + try { + //自动判断是否存在文件 + SftpATTRS attrs = chSftp.lstat(src); + size=attrs.getSize(); + }catch(Exception e) { + size=null; + } + chSftp.quit(); + return size; + }catch(Exception e) { + LOG.error("uploadSimpleMonitor sftp error :{}",e.getMessage(),e); + throw new UtilException("uploadSimpleMonitor sftp error"); + }finally { + channel.closeChannel(); + } + } + /** + * 获取文件 + * 小文件,主要是图片 + * @param sftpDetails 配置 + * @param src 文件路径 :例如/home/sysfile/xxx.zip + * @throws Exception + */ + public static byte[] getFileBytes(SFTPConfig sftpDetails,String src) throws Exception { + if(!isFile(src)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + InputStream in=chSftp.get(src); + byte[] bytes = StreamUtils.copyToByteArray(in); + chSftp.quit(); + return bytes; + }catch(Exception e) { + LOG.error("uploadSimpleMonitor sftp error :{}",e.getMessage(),e); + throw new UtilException("uploadSimpleMonitor sftp error"); + }finally { + channel.closeChannel(); + } + } + + /** + * 上传文件流,覆盖 + * 小文件上传用,无监控 + * @param sftpDetails 配置 + * @param src 源文件流 + * @param dst 写入路径 :例如/home/sysfile/xxx.zip + * @throws Exception + */ + public static void upload(SFTPConfig sftpDetails,InputStream src, String dst) throws Exception { + if(!isFile(dst)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + chSftp.put(src, dst, ChannelSftp.OVERWRITE); //覆盖 + chSftp.quit(); + }catch(Exception e) { + LOG.error("sftp error :{}",e.getMessage(),e); + throw new UtilException("sftp error"); + }finally { + channel.closeChannel(); + } + } + + /** + * 上传文件流,覆盖 + * 小文件上传用, 简单监控 + * @param sftpDetails 配置 + * @param src 源文件流 + * @param dst 写入路径 :例如/home/sysfile/xxx.zip + * @throws Exception + */ + public static void uploadSimpleMonitor(SFTPConfig sftpDetails,InputStream src, String dst) throws Exception { + if(!isFile(dst)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + chSftp.put(src, dst,new SftpSimpleProgressMonitor(), ChannelSftp.OVERWRITE); //覆盖 + chSftp.quit(); + }catch(Exception e) { + LOG.error("uploadSimpleMonitor sftp error :{}",e.getMessage(),e); + throw new UtilException("uploadSimpleMonitor sftp error"); + }finally { + channel.closeChannel(); + } + } + + /** + * 上传文件流,断点上传(自动判断是否存在) + * 记录进度、复杂监控 + * @param sftpDetails 配置 + * @param src 源文件流 + * @param dst 写入路径 :例如/home/sysfile/xxx.zip + * @param progress 当前文件进度 + * @param progressService 进度处理实现 + * @throws Exception + */ + public static void uploadComplexMonitor(SFTPConfig sftpDetails,InputStream src, String dst,SftpProgress progress + ,ISftpProressService progressService) throws Exception { + if(!isFile(dst)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + try { + //自动判断是否存在文件,防止文件已经删除 + SftpATTRS attrs = chSftp.lstat(dst); + //已上传实际的size + progress.setTransferedSize(attrs.getSize()); +// src.skip(offset);//跳过 + chSftp.put(src, dst, new SftpComplexProgressMonitor(progress,progressService), ChannelSftp.RESUME); //续传 + + }catch(Exception e){ + chSftp.put(src, dst, new SftpComplexProgressMonitor(progress,progressService), ChannelSftp.OVERWRITE); //覆盖 + } + chSftp.quit(); + }catch(Exception e) { + LOG.error("uploadComplexMonitor sftp error :{}",e.getMessage(),e); + throw new UtilException("uploadComplexMonitor sftp error"); + }finally { + channel.closeChannel(); + } + } + + + /** + * 下载文件到响应流,断点下载 + * 记录进度、复杂监控 + * @param sftpDetails 配置 + * @param src 源文件路径:例如/home/sysfile/xxx.zip + * @param out + * @param progress 当前文件进度 + * @param progressService 进度处理实现 + * @throws Exception + */ + public static void downloadComplexMonitor(SFTPConfig sftpDetails,String src, HttpServletResponse response + ,SftpProgress progress,ISftpProressService progressService) throws Exception { + if(!isFile(src)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + //自动判断是否存在文件 + SftpATTRS attrs = chSftp.lstat(src); + progress.setFileSize(attrs.getSize());//文件总大小 + long start=progress.getTransferedSize(); + //response.setHeader(HttpHeaders.LAST_MODIFIED, "");//基于文件修改时间的字符串 + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+progress.getFileName()); + response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); + response.setHeader(HttpHeaders.CONTENT_LENGTH, (attrs.getSize() - start)+""); + response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + (attrs.getSize() - 1) + "/" +attrs.getSize()); + chSftp.get(src,response.getOutputStream(), new SftpComplexProgressMonitor(progress,progressService)); + chSftp.quit(); + }catch(Exception e) { + LOG.error("downloadComplexMonitor sftp error :{}",e.getMessage(),e); + throw new UtilException("downloadComplexMonitor sftp error"); + }finally { + channel.closeChannel(); + } + } + + + /** + * 删除文件 + * @param sftpDetails 配置 + * @param src 文件路径:例如/home/sysfile/xxx.zip + * @throws Exception + */ + public static void deleteFile(SFTPConfig sftpDetails,String src) throws Exception { + if(!isFile(src)) { + throw new UtilException("Not a file path"); + } + SFTPChannel channel = new SFTPChannel(); + try { + ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT); + chSftp.rm(src); + chSftp.quit(); + }catch(Exception e) { + LOG.error("deleteFile sftp error :{}",e.getMessage(),e); + throw new UtilException("deleteFile sftp error"); + }finally { + channel.closeChannel(); + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SFTPConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SFTPConfig.java new file mode 100644 index 00000000..f53682ea --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SFTPConfig.java @@ -0,0 +1,105 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp.model; + +/** + * sftp配置类 + * @author zhouliang + * + */ +public class SFTPConfig implements java.io.Serializable{ + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * 主机ip + */ + private String host; + /** + * 端口 + */ + private int port=22; + /** + * 用户账号 + */ + private String username; + /** + * 密码 + */ + private String password; + + /** + * location + */ + private int location; + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host the host to set + */ + public void setHost(String host) { + this.host = host; + } + + /** + * @return the port + */ + public int getPort() { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(int port) { + this.port = port; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return the location + */ + public int getLocation() { + return location; + } + + /** + * @param location the location to set + */ + public void setLocation(int location) { + this.location = location; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SftpProgress.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SftpProgress.java new file mode 100644 index 00000000..58e13365 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/model/SftpProgress.java @@ -0,0 +1,100 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp.model; + +/** + * SFTP进度对象 + * 这里不关心其他属性 + * @author zhouliang + * + */ +public class SftpProgress { + + /** + * 文件唯一id + * 一般用记录主键id + */ + private String fileId; + + + /** + * 文件总大小 + */ + private long fileSize; + + /** + * 已传输文件大小 + */ + private long transferedSize; + + + /** + * 下载的文件名(原始的,不是uuid) + */ + private String fileName; + + /** + * @return the fileId + */ + public String getFileId() { + return fileId; + } + + /** + * @param fileId the fileId to set + */ + public void setFileId(String fileId) { + this.fileId = fileId; + } + + /** + * @return the fileSize + */ + public long getFileSize() { + return fileSize; + } + + /** + * @param fileSize the fileSize to set + */ + public void setFileSize(long fileSize) { + this.fileSize = fileSize; + } + + /** + * @return the transferedSize + */ + public long getTransferedSize() { + return transferedSize; + } + + /** + * @param transferedSize the transferedSize to set + */ + public void setTransferedSize(long transferedSize) { + this.transferedSize = transferedSize; + } + + + /** + * @return the fileName + */ + public String getFileName() { + return fileName; + } + + /** + * @param fileName the fileName to set + */ + public void setFileName(String fileName) { + this.fileName = fileName; + } + + + /** + * 是否传输完成 + * 如果完成,则肯定相等了 + * @return + */ + public boolean isFinish() { + return this.transferedSize==this.fileSize; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpComplexProgressMonitor.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpComplexProgressMonitor.java new file mode 100644 index 00000000..e1b841f1 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpComplexProgressMonitor.java @@ -0,0 +1,110 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp.monitor; + +import java.text.DecimalFormat; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jcraft.jsch.SftpProgressMonitor; +import com.jiuyv.sptccc.agile.common.utils.sftp.ISftpProressService; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SftpProgress; + +/** + * sftp复杂监控实现 + * 包含了断点续传,便于规避大文件失败重传 + * @author zhouliang + * + */ +public class SftpComplexProgressMonitor implements SftpProgressMonitor { + + private static final Logger LOGGER = LoggerFactory.getLogger(SftpComplexProgressMonitor.class); + + private long progressInterval = 10 * 1000L; // 默认间隔时间,单位毫秒 + + private long transfered; // 记录已传输的数据总大小 + + long lastRecordTime = System.currentTimeMillis(); + + private long fileSize; // 记录文件总大小 + + private SftpProgress progress=null; + + private ISftpProressService progressService=null; + + private boolean flag=false;//是否记录 + + public SftpComplexProgressMonitor(long fileSize) { + this.fileSize = fileSize; + } + public SftpComplexProgressMonitor(long fileSize,long offset) { + this.fileSize = fileSize; + this.transfered = offset;//上次传输位置 + } + public SftpComplexProgressMonitor(SftpProgress progress,ISftpProressService progressService) { + this.progressService=progressService; + this.progress=progress; + this.fileSize = progress.getFileSize(); + this.transfered = progress.getTransferedSize();//上次传输位置 + } + + /** + * @return the progress + */ + public SftpProgress getProgress() { + return progress; + } + + + /** + * 实现了SftpProgressMonitor接口的count方法 + */ + public boolean count(long count) { + transfered = transfered + count; + if (System.currentTimeMillis() - lastRecordTime >= progressInterval) {//已经超过N秒 + lastRecordTime = System.currentTimeMillis(); // 更新上次记录时间 + sendProgressMessage(transfered); + doHandler(transfered); + } + return true; + } + + @Override + public void end() { + sendProgressMessage(transfered); + if(flag) { + doHandler(transfered); + } + LOGGER.info("Transferring done."); + } + + @Override + public void init(int op, String src, String dest, long max) { + LOGGER.info("Transferring begin."); + } + + /** + * 处理进度 + * @param transfered + */ + private void doHandler(long transfered) { + if(progressService!=null) { + flag=true; + progress.setTransferedSize(transfered); + progressService.doSaveFileProressing(progress); + } + } + + /** + * 打印progress信息 + * @param transfered + */ + private void sendProgressMessage(long transfered) { + if (fileSize != 0) { + double d = ((double)transfered * 100)/(double)fileSize; + DecimalFormat df = new DecimalFormat( "#.##"); + LOGGER.info("transferred size: "+transfered + " bytes"+", " + df.format(d) + "%"); + } else { + LOGGER.info("transferred size: "+transfered + " bytes"+ transfered); + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpSimpleProgressMonitor.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpSimpleProgressMonitor.java new file mode 100644 index 00000000..d5e27d61 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sftp/monitor/SftpSimpleProgressMonitor.java @@ -0,0 +1,54 @@ +package com.jiuyv.sptccc.agile.common.utils.sftp.monitor; + +import java.text.DecimalFormat; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jcraft.jsch.SftpProgressMonitor; + + +/** + * sftp简单监控实现 + * 小文件传输监控 + * @author zhouliang + * + */ +public class SftpSimpleProgressMonitor implements SftpProgressMonitor { + private static final Logger LOGGER = LoggerFactory.getLogger(SftpSimpleProgressMonitor.class); + + private long progressInterval = 10 * 1000L; // 默认间隔时间,单位毫秒 + + private long transfered; + + long lastRecordTime = System.currentTimeMillis(); + + @Override + public boolean count(long count) { + transfered = transfered + count; + if (System.currentTimeMillis() - lastRecordTime >= progressInterval) {//已经超过N秒 + sendProgressMessage(transfered); + lastRecordTime = System.currentTimeMillis(); // 更新上次记录时间 + } + return true; + } + + @Override + public void end() { + sendProgressMessage(transfered); + LOGGER.info("Transferring done."); + } + + @Override + public void init(int op, String src, String dest, long max) { + LOGGER.info("Transferring begin."); + } + + /** + * 打印progress信息 + * @param transfered + */ + private void sendProgressMessage(long transfered) { + LOGGER.info("transferred size: "+transfered + " bytes"+ transfered); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java new file mode 100644 index 00000000..ce73e826 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.jiuyv.sptccc.agile.common.utils.sign; + +/** + * Base64工具类 + * + * @author admin + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(val2 & 0xff) | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | (val3 & 0xff)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(val2 & 0xff) | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | (b4 & 0xff)); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | (b4 & 0xff)); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java new file mode 100644 index 00000000..5271a1b8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm3Util.java @@ -0,0 +1,40 @@ +package com.jiuyv.sptccc.agile.common.utils.sm4; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Created with IntelliJ IDEA. + * + * @author Shawffer + * @description: + * @date: 2022-07-19 + * @time: 17:35 + */ +public class Sm3Util { + + //如果报错就是以下代码 + static { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + public static byte[] hashPWtoBytes( byte[] rawpassword, String salt) throws NoSuchProviderException, NoSuchAlgorithmException { + String plaintext=new String(rawpassword)+salt; + byte[] plaintextBytes = plaintext.getBytes(); + MessageDigest digest = null; + digest = MessageDigest.getInstance("SM3", BouncyCastleProvider.PROVIDER_NAME); + return digest.digest(plaintextBytes); + } + + + public static String encrypt(String plaintext) throws NoSuchAlgorithmException, NoSuchProviderException { + byte[] plaintextBytes = plaintext.getBytes(); + MessageDigest digest = null; + digest = MessageDigest.getInstance("SM3", BouncyCastleProvider.PROVIDER_NAME); + return Base64.encodeBase64String(digest.digest(plaintextBytes)); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java new file mode 100644 index 00000000..a2b9cbec --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sm4/Sm4Util.java @@ -0,0 +1,237 @@ +package com.jiuyv.sptccc.agile.common.utils.sm4; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.macs.CBCBlockCipherMac; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.paddings.BlockCipherPadding; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Created with IntelliJ IDEA. + * + * @author Shawffer + * @description: + * @date: 2022-07-19 + * @time: 17:04 + */ +public class Sm4Util { + public static final String ALGORITHM_NAME = "SM4"; + public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding"; + public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding"; + public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding"; + public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding"; + + /** + * SM4算法目前只支持128位(即密钥16字节) + */ + public static final int DEFAULT_KEY_SIZE = 128; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { + return generateKey(DEFAULT_KEY_SIZE); + } + + public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); + kg.init(keySize, new SecureRandom()); + return kg.generateKey().getEncoded(); + } + + public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } + + public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } + + public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidAlgorithmParameterException { + Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(cipherText); + } + + public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidKeyException { + Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME); + return doMac("SM4-CMAC", keyObj, data); + } + + public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) { + org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8); + return doMac(mac, key, iv, data); + } + + /** + * 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC + * + * @param key + * @param iv + * @param data + * @return + */ + public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) { + SM4Engine engine = new SM4Engine(); + org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding()); + return doMac(mac, key, iv, data); + } + + /** + * @param key + * @param iv + * @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍 + * @param data + * @return + * @throws Exception + */ + public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception { + SM4Engine engine = new SM4Engine(); + if (padding == null) { + if (data.length % engine.getBlockSize() != 0) { + throw new Exception("if no padding, data length must be multiple of SM4 BlockSize"); + } + } + org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding); + return doMac(mac, key, iv, data); + } + + + private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) { + CipherParameters cipherParameters = new KeyParameter(key); + mac.init(new ParametersWithIV(cipherParameters, iv)); + mac.update(data, 0, data.length); + byte[] result = new byte[mac.getMacSize()]; + mac.doFinal(result, 0); + return result; + } + + private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidKeyException { + Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + mac.init(key); + mac.update(data); + return mac.doFinal(); + } + + private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key) + throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, + InvalidKeyException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + cipher.init(mode, sm4Key); + return cipher; + } + + private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv) + throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchProviderException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); + Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + cipher.init(mode, sm4Key, ivParameterSpec); + return cipher; + } + + public static String encryptEcb(String key, String text) throws Exception { + return Base64.encodeBase64String(Sm4Util.encrypt_ECB_Padding(Hex.decodeHex(key),text.getBytes("UTF-8"))); + } + public static String decryptEcb(String key, String text) throws Exception { + return new String(Sm4Util.decrypt_ECB_Padding(Hex.decodeHex(key), Base64.decodeBase64((text)))); + } + public static String generateKeyHex() throws Exception { + return Hex.encodeHexString(Sm4Util.generateKey(DEFAULT_KEY_SIZE)); + } + + + public static void main(String[] args) throws Exception { + System.out.println(); + String plainText = "3wTFdgqPwEauKeK7JuUrIQ=="; + String sec_Key = "809bf66f0f47f2c4c7183cf5df6e3a92"; + + byte[] tmp = Sm4Util.decrypt_ECB_Padding(Hex.decodeHex(sec_Key), Base64.decodeBase64(plainText)); + System.out.println("A: " + new String(tmp)); + String tmp2 = Sm4Util.decryptEcb(sec_Key, plainText); + System.out.println("B: " + tmp2); + + System.out.println("---------"); + plainText = "123"; + String s1= Sm4Util.encryptEcb(sec_Key, plainText); + System.out.println("C="+s1); + + String s2= Sm4Util.decryptEcb(sec_Key, s1); + System.out.println("D="+s2); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java new file mode 100644 index 00000000..e0361c7c --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/spring/SpringUtils.java @@ -0,0 +1,155 @@ +package com.jiuyv.sptccc.agile.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author admin + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + setBeanFactory(beanFactory); + } + + public static void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + setContext(applicationContext); + } + + public static void setContext(ApplicationContext applicationContext) { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java new file mode 100644 index 00000000..ebdd1b62 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/sql/SqlUtil.java @@ -0,0 +1,61 @@ +package com.jiuyv.sptccc.agile.common.utils.sql; + +import com.jiuyv.sptccc.agile.common.exception.UtilException; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author admin + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java new file mode 100644 index 00000000..675b1a64 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.jiuyv.sptccc.agile.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author admin + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java new file mode 100644 index 00000000..e6067cb5 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/utils/uuid/Seq.java @@ -0,0 +1,105 @@ +package com.jiuyv.sptccc.agile.common.utils.uuid; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +import com.jiuyv.sptccc.agile.common.utils.DateUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * @author admin 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static String machineCode = "1"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } + + /** + * 产生一个数字传,比如给手机验证码 + * @param length + * @return + */ + public static String randomNumber(int length){ + if(length<4) {//最少4位 + length=4; + } + Random random = new Random(); + StringBuilder bld=new StringBuilder(); + for(int i=0;i +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java new file mode 100644 index 00000000..44034ead --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/Xss.java @@ -0,0 +1,28 @@ +package com.jiuyv.sptccc.agile.common.xss; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +/** + * 自定义xss校验注解 + * + * @author admin + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java new file mode 100644 index 00000000..dccbce82 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/common/xss/XssValidator.java @@ -0,0 +1,36 @@ +package com.jiuyv.sptccc.agile.common.xss; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 自定义xss校验注解实现 + * + * @author admin + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java new file mode 100644 index 00000000..2b9f6f10 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/GlobalLogAspect.java @@ -0,0 +1,220 @@ +package com.jiuyv.sptccc.agile.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUserSimple; +import com.jiuyv.sptccc.agile.common.enums.HttpMethod; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; + +/** + * 全局请求日志,这个日志就不记录到表里面了 + * @author zhouliang + * + */ +@Aspect +@Component +public class GlobalLogAspect +{ + private static final Logger log = LoggerFactory.getLogger(GlobalLogAspect.class); + + private static final ThreadLocal timeTreadLocal = new ThreadLocal<>(); + + //不输出任何日志 + private static final String LogIgnorePath = "@annotation(com.jiuyv.sptccc.agile.common.annotation.LogIgnore)"; + //返回不输出返回的数据 + private static final String LogSimpleResultPath = "@annotation(com.jiuyv.sptccc.agile.common.annotation.LogSimpleResult)"; + //扫描所有Controller + private static final String ControllerPath = "execution(* *..*.*Controller.*(..))"; + //不输出这个转发的 + private static final String InitBinderPath = "@annotation(org.springframework.web.bind.annotation.InitBinder)"; + + //请求Controller输出日志 + @Pointcut(ControllerPath+"&&!"+LogIgnorePath+"&&!"+InitBinderPath) + public void controllerAspect() { + } + //请求Controller不输出返回内容 + @Pointcut(LogSimpleResultPath) + public void resultSimpleAspect() { + } + + //请求日志记录 + @Before("controllerAspect()") + public void before(JoinPoint joinPoint) { + timeTreadLocal.set(System.currentTimeMillis()); + // 接收请求,记录完整请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //获取请求的request + HttpServletRequest request = attributes.getRequest(); + String methodName = getMethodName(joinPoint); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + String userName="none"; + try { + LoginUserSimple loginuser = SecurityUtils.getLoginUser(); + if(loginuser!=null) { + userName=loginuser.getUserId()+"/"+loginuser.getUserName()+"/"+loginuser.getNickName(); + } + }catch (Exception e) { + //不报错 + } + String keyValue = setRequestValue(joinPoint,request.getMethod()); + log.info("{}-req:User={},Ip={},Uri={},Method={},ContentType={}",methodName,userName,ip,request.getRequestURI(),request.getMethod(),request.getContentType()); + log.info("{}-req:Params={}",methodName, keyValue); + } + //后置返回 + @AfterReturning(pointcut = "controllerAspect() && !resultSimpleAspect()",returning = "result") + public void doAfterReturning(JoinPoint joinPoint,Object result) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + String returnstr = null; + if(result!=null) { + try { + returnstr=JsonUtil.JsonMapper2().writeValueAsString(result); + } catch (JsonProcessingException e) { + returnstr="return convert error"; + } + } + log.info("{}-res:Success,Time={}s,Data={}",methodName, callTime, returnstr); + timeTreadLocal.remove(); + } + //后置返回(不输出返回内容) + @AfterReturning(pointcut = "controllerAspect() && resultSimpleAspect()",returning = "result") + public void doAfterReturningSimple(JoinPoint joinPoint,Object result) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + log.info("{}-res:Success,Time={}s,Data=omitting content...",methodName, callTime); + timeTreadLocal.remove(); + } + //后置异常返回 + @AfterThrowing(pointcut = "controllerAspect()", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { + long startTime = timeTreadLocal.get(); + double callTime = (System.currentTimeMillis() - startTime) / 1000.0; + String methodName = getMethodName(joinPoint); + log.info("{}-res:Failure,Time={}s,Error={}",methodName, callTime,e.getMessage()); + timeTreadLocal.remove(); + } + + /** 获取方法名称*/ + private String getMethodName(JoinPoint joinPoint) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + //获取被拦截的方法 + Method method = methodSignature.getMethod(); + String className = method.getDeclaringClass().getCanonicalName(); + String methodName = className.substring(className.lastIndexOf(".")+1) + "." + method.getName(); + return methodName; + } + + /** + * 获取请求的参数 + */ + private String setRequestValue(JoinPoint joinPoint, String requestMethod) + { + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) + { + String params = argsArrayToString(joinPoint.getArgs()); + return params; + } + else + { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + return paramsMap.toString(); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + Object jsonObj = JsonUtil.JsonMapper2().writeValueAsString(o); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + if(value instanceof MultipartFile) { + return true; + } + } + return false; + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + if(entry.getValue() instanceof MultipartFile) { + return true; + } + } + return false; + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/InnerAuthAspect.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/InnerAuthAspect.java new file mode 100644 index 00000000..943de8e7 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/InnerAuthAspect.java @@ -0,0 +1,69 @@ +package com.jiuyv.sptccc.agile.framework.aspectj; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 接口调用验证处理 + * @author zhouliang + * + */ +@Aspect +@Component +public class InnerAuthAspect implements Ordered +{ + //扫描所有Controller + private static final String ControllerPath = "execution(* *..*.*Controller.*(..))"; + //接口无需登陆用户 + private static final String NoAuthPath = "@annotation(com.jiuyv.sptccc.agile.common.annotation.NoAuthUser)"; + + //请求Controller默认必须登陆用户 + @Pointcut(ControllerPath +"&&!"+NoAuthPath) + public void controllerAspect() { + } + + @Around("controllerAspect()") + public Object innerAround(ProceedingJoinPoint point) throws Throwable + { + String source = ServletUtils.getRequest().getHeader(Constants.FEIGN_CLIENT_FLAG); + // 内部请求验证 + if (!StringUtils.equals(Constants.FEIGN_CLIENT_VAL, source)) + { +// throw new ServiceException("非法访问请求"); + } + boolean userflag = SecurityUtils.setLoginUser(); + // 用户信息验证 + if (!userflag) + { +// throw new ServiceException("没有设置用户信息,不允许访问"); + } + + try { + return point.proceed(); + } catch (Exception ex) { + throw ex; + } finally { + //用完马上清理掉 + SecurityUtils.clearLoginUser(); + } + } + + /** + * 确保在权限认证aop执行前执行 + */ + @Override + public int getOrder() + { + return Ordered.HIGHEST_PRECEDENCE + 1; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/LogAspect.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/LogAspect.java new file mode 100644 index 00000000..74cac1c0 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/aspectj/LogAspect.java @@ -0,0 +1,227 @@ +package com.jiuyv.sptccc.agile.framework.aspectj; + +import java.util.Collection; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import com.jiuyv.sptccc.agile.common.annotation.Log; +import com.jiuyv.sptccc.agile.common.core.domain.model.LoginUserSimple; +import com.jiuyv.sptccc.agile.common.enums.BusinessStatus; +import com.jiuyv.sptccc.agile.common.enums.HttpMethod; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.SecurityUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.framework.manager.AsyncManager; +import com.jiuyv.sptccc.agile.framework.manager.factory.AsyncFactory; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalOperLog; + +/** + * 操作日志记录处理 + * + * @author admin + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUserSimple loginUserSimple = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + TblPortalOperLog operLog = new TblPortalOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + operLog.setOperIp(ip); + operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); + if (loginUserSimple != null) + { + operLog.setOperName(loginUserSimple.getUserName()); + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("==前置通知异常=="); + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, TblPortalOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JsonUtil.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, TblPortalOperLog operLog) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) + { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + Object jsonObj = JsonUtil.toJSONString(o); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + if(value instanceof MultipartFile) { + return true; + } + } + return false; + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + if(entry.getValue() instanceof MultipartFile) { + return true; + } + } + return false; + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java new file mode 100644 index 00000000..877c48a7 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ApplicationConfig.java @@ -0,0 +1,31 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.TimeZone; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author admin + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.jiuyv.sptccc.agile.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java new file mode 100644 index 00000000..b86b67b6 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/AsyncConfig.java @@ -0,0 +1,29 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@EnableAsync +@Configuration +public class AsyncConfig { + + @Bean("taskExecutor") + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(20); + executor.setQueueCapacity(100); + executor.setKeepAliveSeconds(30); + executor.setThreadNamePrefix("datax-async-service-"); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAwaitTerminationSeconds(60); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java new file mode 100644 index 00000000..b5f00cb1 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/FilterConfig.java @@ -0,0 +1,61 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.DispatcherType; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.jiuyv.sptccc.agile.common.filter.RepeatableFilter; +import com.jiuyv.sptccc.agile.common.filter.XssFilter; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author admin + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/SftpConfigProperties.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/SftpConfigProperties.java new file mode 100644 index 00000000..fb3c17e8 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/SftpConfigProperties.java @@ -0,0 +1,114 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; + +/** + * sftp配置类 + * @author zhouliang + * + */ +@Component +@ConfigurationProperties(prefix = "filesftp") +public class SftpConfigProperties implements java.io.Serializable{ + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * 主机ip + */ + private String host; + /** + * 端口 + */ + private int port=22; + /** + * 用户账号 + */ + private String username; + /** + * 密码 + */ + private String password; + + /** + * location + */ + private int location; + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host the host to set + */ + public void setHost(String host) { + this.host = host; + } + + /** + * @return the port + */ + public int getPort() { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(int port) { + this.port = port; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @param username the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the password + * @throws Exception + */ + public String getPassword() throws Exception { + return Sm4Util.decryptEcb(Constants.SM4_SECERT_KEY, password); + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return the location + */ + public int getLocation() { + return location; + } + + /** + * @param location the location to set + */ + public void setLocation(int location) { + this.location = location; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ThreadPoolConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ThreadPoolConfig.java new file mode 100644 index 00000000..7d4dee3a --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/ThreadPoolConfig.java @@ -0,0 +1,65 @@ +package com.jiuyv.sptccc.agile.framework.config; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import com.jiuyv.sptccc.agile.common.utils.Threads; + +/** + * 线程池配置 + * + * @author admin + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java new file mode 100644 index 00000000..c2acf79c --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CacheTimestampedValue.java @@ -0,0 +1,44 @@ +package com.jiuyv.sptccc.agile.framework.config.caffeine; + +import java.io.Serializable; + +/** + * 定义对象记录插入时间,如果需要动态时间的,使用这个封装类 + * @author Administrator + * + * @param + */ +public class CacheTimestampedValue implements Serializable{ + private static final long serialVersionUID = 1L; + + private V value; + private long timestamp; + + public CacheTimestampedValue(V value) { + this.value = value; + this.timestamp = System.currentTimeMillis(); + } + + + public void setValue(V value) { + this.value = value; + } + public V getValue() { + return value; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + public long getTimestamp() { + return timestamp; + } + + /** + * 获取计算当前时间和记录时间秒差额 + * @return + */ + public long getSecondDifference() { + return (System.currentTimeMillis()-timestamp)/1000; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java new file mode 100644 index 00000000..591670c9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/config/caffeine/CaffeineCacheConfig.java @@ -0,0 +1,63 @@ +package com.jiuyv.sptccc.agile.framework.config.caffeine; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.jiuyv.sptccc.agile.common.constant.CacheNameConstants; + +/** + * caffeine内存 + * @author Administrator + */ +@Configuration +public class CaffeineCacheConfig { + + /** + * 创建一些常规缓存,设置失效时间保证有效的释放内存 + * 由于只能实例上设置时间,勉强用如下方法实现动态时间 + * (动态时间:把失效时间设置略大于动态时间,再用这个类[CacheTimestampedValue]进行存取/计算) + * 比如 菜单、权限 + * @return + */ + @Bean + @Qualifier(value = "commonCacheManager") + public CacheManager commonCacheManager() { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + List caches = new ArrayList<>(); + + //设置死数量也有一些问题,并发数必须<=maximumSize,并发数高了前面的信息就失效了 + //某些场景键值数量自行按需设置 + + + //5s缓存,主要用于防重提交, + caches.add(new CaffeineCache(CacheNameConstants.CACHE_REPEAT_SUBMIT_5S, + Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build())); + //30s缓存,主要用于防重提交(有些方法周期长5s不够的话) + caches.add(new CaffeineCache(CacheNameConstants.CACHE_REPEAT_SUBMIT_30S, + Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build())); + //主要用于验证码 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_CAPTCHA_CODE, + Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build())); + //用户数据加密密钥 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_USER_DATA_SECRET_KEY, + Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build())); + //1天缓存 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_ONEDAY, + Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).maximumSize(10000).build())); + //永久缓存,放置一些系统字典、配置之类的 + caches.add(new CaffeineCache(CacheNameConstants.CACHE_SYS_DICT, + Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.DAYS).maximumSize(2000).build())); + cacheManager.setCaches(caches); + return cacheManager; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java new file mode 100644 index 00000000..2dcfbb24 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/AsyncManager.java @@ -0,0 +1,56 @@ +package com.jiuyv.sptccc.agile.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.jiuyv.sptccc.agile.common.utils.Threads; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author admin + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java new file mode 100644 index 00000000..39270319 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/ShutdownManager.java @@ -0,0 +1,40 @@ +package com.jiuyv.sptccc.agile.framework.manager; + +import javax.annotation.PreDestroy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author admin + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java new file mode 100644 index 00000000..3beeaa5e --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,152 @@ +package com.jiuyv.sptccc.agile.framework.manager.factory; + +import java.util.TimerTask; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.utils.LogUtils; +import com.jiuyv.sptccc.agile.common.utils.ServletUtils; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.AddressUtils; +import com.jiuyv.sptccc.agile.common.utils.ip.IpUtils; +import com.jiuyv.sptccc.agile.common.utils.spring.SpringUtils; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalLogininfor; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalOperLog; +import com.jiuyv.sptccc.agile.portal.service.IPortalLogininforService; +import com.jiuyv.sptccc.agile.portal.service.IPortalOperLogService; + +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author admin + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + TblPortalLogininfor logininfor = new TblPortalLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(IPortalLogininforService.class).insertLogininfor(logininfor); + } + }; + } + public static TimerTask recordLogininforV2(final HttpServletRequest request, final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(request); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + TblPortalLogininfor logininfor = new TblPortalLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(IPortalLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final TblPortalOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(IPortalOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..a549be30 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,121 @@ +package com.jiuyv.sptccc.agile.framework.web.exception; + +import java.nio.file.AccessDeniedException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.jiuyv.sptccc.agile.common.constant.HttpStatus; +import com.jiuyv.sptccc.agile.common.core.domain.AjaxResult; +import com.jiuyv.sptccc.agile.common.exception.ServiceException; +import com.jiuyv.sptccc.agile.common.utils.JsonUtil; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author admin + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler({ServiceException.class}) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request, HttpServletResponse response) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { +// log.error(e.getMessage(), e); +// String message = e.getBindingResult().getFieldError().getDefaultMessage(); +// return AjaxResult.error(message); + log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass()); + BindingResult bindingResult = e.getBindingResult(); + + Map errorMap = new HashMap<>(); + bindingResult.getFieldErrors().forEach((fieldError) -> { + errorMap.put(fieldError.getField(), fieldError.getDefaultMessage()); + }); + //return AjaxResult.error("参数校验错误",errorMap); + return AjaxResult.error(JsonUtil.toJSONString(errorMap)); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler({RuntimeException.class}) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request, HttpServletResponse response) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + + return AjaxResult.error("系统忙,请稍后再试"); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(301); + + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java new file mode 100644 index 00000000..898f77af --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/CommonController.java @@ -0,0 +1,131 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FilenameUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +import com.jiuyv.sptccc.agile.common.config.ConsoleConfig; +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.common.utils.file.FileTypeUtils; +import com.jiuyv.sptccc.agile.common.utils.sftp.SftpFileUtils; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SFTPConfig; +import com.jiuyv.sptccc.agile.common.utils.sftp.model.SftpProgress; +import com.jiuyv.sptccc.agile.framework.config.SftpConfigProperties; +import com.jiuyv.sptccc.agile.portal.dto.common.ResGetFileByteAddDTO; + +/** + * 通用请求处理 + * + * @author admin + */ +@Controller +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private SftpConfigProperties sftpConfigProperties; + + /** + * 图片页面显示(图片可以限制2M以下吧) + * @param request + * @param uuid + * @return + * @throws Exception + */ + @PostMapping("/"+Constants.IMAGE_URL_PATH+"/get") + @ResponseBody + public R getImage(HttpServletRequest request,String uuid) throws Exception { + ResGetFileByteAddDTO resDTO=new ResGetFileByteAddDTO(); + try { + //去查询文件表,获取文件具体信息 + String type="jpg"; + String filename="16ed81d9f13d4e5cabcc6d21b22a06d1.jpg"; + resDTO.setType(type); + resDTO.setFilename(filename); + //不是图片文件则不能通过该接口访问 + if(FileTypeUtils.isValidImageExt(type)) { + byte[] imgBody=SftpFileUtils.getFileBytes(createSftpCfg(), ConsoleConfig.getProfile()+"/"+filename); + resDTO.setFile(imgBody); + } + } catch (IOException e) { + // 不报错 + } + return R.ok(resDTO); + } + + /** + * 下载文件。比如小于200M的文件就不做断点,免得日志太多 + * @param response + * @param uuid + * @param offset 已下载大小 + * @throws Exception + */ + @PostMapping(value = "/"+Constants.FILE_URL_PATH+"/download") + public void downloadFile(HttpServletResponse response,String uuid,long offset) throws Exception { + //获取到文件记录 + String fileId=""; + String type=""; + uuid="userlibs.tar.gz"; + + SftpProgress p=new SftpProgress(); + p.setFileId(fileId); + p.setTransferedSize(offset); + p.setFileName("userlibs.tar.gz"); + SftpFileUtils.downloadComplexMonitor(createSftpCfg(), ConsoleConfig.getProfile()+type+"/"+uuid + , response, p, null); + } + + /** + * 上传文件。比如小于200M的文件就不做断点,免得日志太多 + * @param response + * @param file + * @param type 分类类型 + * @return + * @throws Exception + */ + @PostMapping(value = "/"+Constants.FILE_URL_PATH+"/upload") + public R uploadFile(HttpServletResponse response, MultipartFile file,String type,String filename) throws Exception { + //创建文件记录 + String fileId=""; + String uuid="userlibs.tar.gz"; + type="/user/202306";//可以追加月份到分类路径 + + //上传 + SftpProgress p=new SftpProgress(); + p.setFileId(fileId); + p.setFileSize(file.getSize()); + p.setFileName(filename); + SftpFileUtils.uploadComplexMonitor(createSftpCfg(), file.getInputStream(), ConsoleConfig.getProfile()+type+"/"+uuid + , p, null); + + //会返回文件的一些信息 + return R.ok(); + } + + /** + * sftp属性 + * @return + * @throws Exception + */ + private SFTPConfig createSftpCfg() throws Exception{ + SFTPConfig cfg=new SFTPConfig(); + cfg.setUsername(sftpConfigProperties.getUsername()); + cfg.setPassword(sftpConfigProperties.getPassword()); + cfg.setPort(sftpConfigProperties.getPort()); + cfg.setHost(sftpConfigProperties.getHost()); + return cfg; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalLogininforController.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalLogininforController.java new file mode 100644 index 00000000..dda5badf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalLogininforController.java @@ -0,0 +1,42 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.jiuyv.sptccc.agile.common.annotation.NoAuthUser; +import com.jiuyv.sptccc.agile.common.core.controller.BaseController; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalLogininfor; +import com.jiuyv.sptccc.agile.portal.dto.logininfor.ReqPortalLogininforAddDTO; +import com.jiuyv.sptccc.agile.portal.service.IPortalLogininforService; + +/** + * 系统访问记录 + * + * @author admin + */ +@RestController +@RequestMapping("/logininfor") +public class PortalLogininforController extends BaseController +{ + @Autowired + private IPortalLogininforService logininforService; + + @NoAuthUser + @PostMapping("/insert") + public void insert(@Validated @RequestBody ReqPortalLogininforAddDTO req) + { + TblPortalLogininfor logininfor=new TblPortalLogininfor(); + logininfor.setUserName(req.getUserName()); + logininfor.setIpaddr(req.getIpaddr()); + logininfor.setLoginLocation(req.getLoginLocation()); + logininfor.setBrowser(req.getBrowser()); + logininfor.setOs(req.getOs()); + logininfor.setStatus(req.getStatus()); + logininfor.setMsg(req.getMsg()); + logininforService.insertLogininfor(logininfor); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java new file mode 100644 index 00000000..7a925087 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/controller/PortalUserController.java @@ -0,0 +1,63 @@ +package com.jiuyv.sptccc.agile.portal.controller; + +import javax.validation.constraints.NotBlank; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.jiuyv.sptccc.agile.common.annotation.NoAuthUser; +import com.jiuyv.sptccc.agile.common.core.controller.BaseController; +import com.jiuyv.sptccc.agile.common.core.domain.R; +import com.jiuyv.sptccc.agile.common.utils.bean.BeanUtils; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser; +import com.jiuyv.sptccc.agile.portal.dto.TblPortalUserBase; +import com.jiuyv.sptccc.agile.portal.dto.user.ReqPortalUserResetErrorDTO; +import com.jiuyv.sptccc.agile.portal.service.IPortalUserService; + + +/** + * 用户信息 + * + * @author admin + */ +@RestController +@RequestMapping("/portalUser") +public class PortalUserController extends BaseController +{ + @Autowired + private IPortalUserService userService; + + /** + * 获取用户 + */ + @NoAuthUser + @PostMapping("/selectUserByUserName") + public R selectUserByUserName(String username) + { + TblPortalUser user = userService.selectUserByUserName(username); + return R.ok(BeanUtils.copyProperties(user, TblPortalUserBase.class)); + } + + /** + * 系统重置登陆状态等信息 + */ + @NoAuthUser + @PutMapping("/resetError") + public R resetError(@Validated @RequestBody ReqPortalUserResetErrorDTO req) + { + TblPortalUser user=new TblPortalUser(); + user.setUserId(req.getUserId()); + user.setLoginIp(req.getLoginIp()); + user.setLastLoginErrorTime(req.getLastLoginErrorTime()); + user.setLoginErrorcount(req.getLoginErrorcount()); + user.setIsLocked(req.getIsLocked()); + user.setLoginDate(req.getLoginDate()); + userService.updateUserProfileNoVersion(user); + return R.ok(); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalLogininfor.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalLogininfor.java new file mode 100644 index 00000000..c2973607 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalLogininfor.java @@ -0,0 +1,132 @@ +package com.jiuyv.sptccc.agile.portal.domain; + +import java.util.Date; + +import com.jiuyv.sptccc.agile.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 tbl_portal_logininfor + * + * @author admin + */ +public class TblPortalLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Long infoId; + + /** 用户账号 */ + private String userName; + + /** 登录状态 0成功 1失败 */ + private String status; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地点 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 提示消息 */ + private String msg; + + /** 访问时间 */ + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalOperLog.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalOperLog.java new file mode 100644 index 00000000..cf272d7f --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalOperLog.java @@ -0,0 +1,236 @@ +package com.jiuyv.sptccc.agile.portal.domain; + +import java.util.Date; + +import com.jiuyv.sptccc.agile.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author admin + */ +public class TblPortalOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + private Long operId; + + /** 操作模块 */ + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + private String method; + + /** 请求方式 */ + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + private Integer operatorType; + + /** 操作人员 */ + private String operName; + + /** 部门名称 */ + private String deptName; + + /** 请求url */ + private String operUrl; + + /** 操作地址 */ + private String operIp; + + /** 操作地点 */ + private String operLocation; + + /** 请求参数 */ + private String operParam; + + /** 返回参数 */ + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + private Integer status; + + /** 错误消息 */ + private String errorMsg; + + /** 操作时间 */ + private Date operTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalUser.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalUser.java new file mode 100644 index 00000000..ebd0e9a2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/domain/TblPortalUser.java @@ -0,0 +1,354 @@ +package com.jiuyv.sptccc.agile.portal.domain; + +import java.util.Date; + +import com.jiuyv.sptccc.agile.common.core.domain.BaseEntity; + +/** + * 用户对象 tbl_portal_user + * + * @author admin + */ +public class TblPortalUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private Long userId; + + /** 版本号 */ + private Long versionNum; + /** token锁 */ + private String recToken; + + /** 部门ID */ + private Long deptId; + + /** 用户账号 */ + private String userName; + + /** 用户昵称 */ + private String nickName; + + /** 用户类型 */ + private String userType; + + /** 用户邮箱 */ + private String email; + + /** 手机号码 */ + private String phonenumber; + + /** 用户性别 */ + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + private String loginIp; + + /** 最后登录时间 */ + private Date loginDate; + + /** 角色ID */ + private Long roleId; + + /**最后一次登录错误时间*/ + private Date lastLoginErrorTime; + + /** 登录错误计数 */ + private Integer loginErrorcount; + + /** 是否锁定(0、未锁定;1、锁定) */ + private Integer isLocked; + + /** + * @return the userId + */ + public Long getUserId() { + return userId; + } + + /** + * @param userId the userId to set + */ + public void setUserId(Long userId) { + this.userId = userId; + } + + /** + * @return the versionNum + */ + public Long getVersionNum() { + return versionNum; + } + + /** + * @param versionNum the versionNum to set + */ + public void setVersionNum(Long versionNum) { + this.versionNum = versionNum; + } + + /** + * @return the recToken + */ + public String getRecToken() { + return recToken; + } + + /** + * @param recToken the recToken to set + */ + public void setRecToken(String recToken) { + this.recToken = recToken; + } + + /** + * @return the deptId + */ + public Long getDeptId() { + return deptId; + } + + /** + * @param deptId the deptId to set + */ + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + /** + * @return the userName + */ + public String getUserName() { + return userName; + } + + /** + * @param userName the userName to set + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * @return the nickName + */ + public String getNickName() { + return nickName; + } + + /** + * @param nickName the nickName to set + */ + public void setNickName(String nickName) { + this.nickName = nickName; + } + + /** + * @return the userType + */ + public String getUserType() { + return userType; + } + + /** + * @param userType the userType to set + */ + public void setUserType(String userType) { + this.userType = userType; + } + + /** + * @return the email + */ + public String getEmail() { + return email; + } + + /** + * @param email the email to set + */ + public void setEmail(String email) { + this.email = email; + } + + /** + * @return the phonenumber + */ + public String getPhonenumber() { + return phonenumber; + } + + /** + * @param phonenumber the phonenumber to set + */ + public void setPhonenumber(String phonenumber) { + this.phonenumber = phonenumber; + } + + /** + * @return the sex + */ + public String getSex() { + return sex; + } + + /** + * @param sex the sex to set + */ + public void setSex(String sex) { + this.sex = sex; + } + + /** + * @return the avatar + */ + public String getAvatar() { + return avatar; + } + + /** + * @param avatar the avatar to set + */ + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * @return the delFlag + */ + public String getDelFlag() { + return delFlag; + } + + /** + * @param delFlag the delFlag to set + */ + public void setDelFlag(String delFlag) { + this.delFlag = delFlag; + } + + /** + * @return the loginIp + */ + public String getLoginIp() { + return loginIp; + } + + /** + * @param loginIp the loginIp to set + */ + public void setLoginIp(String loginIp) { + this.loginIp = loginIp; + } + + /** + * @return the loginDate + */ + public Date getLoginDate() { + return loginDate; + } + + /** + * @param loginDate the loginDate to set + */ + public void setLoginDate(Date loginDate) { + this.loginDate = loginDate; + } + + /** + * @return the roleId + */ + public Long getRoleId() { + return roleId; + } + + /** + * @param roleId the roleId to set + */ + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + /** + * @return the lastLoginErrorTime + */ + public Date getLastLoginErrorTime() { + return lastLoginErrorTime; + } + + /** + * @param lastLoginErrorTime the lastLoginErrorTime to set + */ + public void setLastLoginErrorTime(Date lastLoginErrorTime) { + this.lastLoginErrorTime = lastLoginErrorTime; + } + + /** + * @return the loginErrorcount + */ + public Integer getLoginErrorcount() { + return loginErrorcount; + } + + /** + * @param loginErrorcount the loginErrorcount to set + */ + public void setLoginErrorcount(Integer loginErrorcount) { + this.loginErrorcount = loginErrorcount; + } + + /** + * @return the isLocked + */ + public Integer getIsLocked() { + return isLocked; + } + + /** + * @param isLocked the isLocked to set + */ + public void setIsLocked(Integer isLocked) { + this.isLocked = isLocked; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalLogininforBase.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalLogininforBase.java new file mode 100644 index 00000000..51d81315 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalLogininforBase.java @@ -0,0 +1,125 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import java.util.Date; + + +/** + * 系统访问记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalLogininforBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 访问id + */ + private Long infoId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录ip地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态 + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + + + + public Long getInfoId(){ + return infoId; + } + public void setInfoId(Long infoId){ + this.infoId = infoId; + } + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getIpaddr(){ + return ipaddr; + } + public void setIpaddr(String ipaddr){ + this.ipaddr = ipaddr; + } + + public String getLoginLocation(){ + return loginLocation; + } + public void setLoginLocation(String loginLocation){ + this.loginLocation = loginLocation; + } + + public String getBrowser(){ + return browser; + } + public void setBrowser(String browser){ + this.browser = browser; + } + + public String getOs(){ + return os; + } + public void setOs(String os){ + this.os = os; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getMsg(){ + return msg; + } + public void setMsg(String msg){ + this.msg = msg; + } + + public Date getLoginTime(){ + return loginTime; + } + public void setLoginTime(Date loginTime){ + this.loginTime = loginTime; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalOperLogBase.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalOperLogBase.java new file mode 100644 index 00000000..fffc989b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalOperLogBase.java @@ -0,0 +1,209 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import java.util.Date; + + +/** + * 操作日志记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalOperLogBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 模块标题 + */ + private String title; + + /** + * 业务类型 + */ + private Integer businessType; + + /** + * 方法名称 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别 + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 主机地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态 + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + + + public Long getOperId(){ + return operId; + } + public void setOperId(Long operId){ + this.operId = operId; + } + + public String getTitle(){ + return title; + } + public void setTitle(String title){ + this.title = title; + } + + public Integer getBusinessType(){ + return businessType; + } + public void setBusinessType(Integer businessType){ + this.businessType = businessType; + } + + public String getMethod(){ + return method; + } + public void setMethod(String method){ + this.method = method; + } + + public String getRequestMethod(){ + return requestMethod; + } + public void setRequestMethod(String requestMethod){ + this.requestMethod = requestMethod; + } + + public Integer getOperatorType(){ + return operatorType; + } + public void setOperatorType(Integer operatorType){ + this.operatorType = operatorType; + } + + public String getOperName(){ + return operName; + } + public void setOperName(String operName){ + this.operName = operName; + } + + public String getDeptName(){ + return deptName; + } + public void setDeptName(String deptName){ + this.deptName = deptName; + } + + public String getOperUrl(){ + return operUrl; + } + public void setOperUrl(String operUrl){ + this.operUrl = operUrl; + } + + public String getOperIp(){ + return operIp; + } + public void setOperIp(String operIp){ + this.operIp = operIp; + } + + public String getOperLocation(){ + return operLocation; + } + public void setOperLocation(String operLocation){ + this.operLocation = operLocation; + } + + public String getOperParam(){ + return operParam; + } + public void setOperParam(String operParam){ + this.operParam = operParam; + } + + public String getJsonResult(){ + return jsonResult; + } + public void setJsonResult(String jsonResult){ + this.jsonResult = jsonResult; + } + + public Integer getStatus(){ + return status; + } + public void setStatus(Integer status){ + this.status = status; + } + + public String getErrorMsg(){ + return errorMsg; + } + public void setErrorMsg(String errorMsg){ + this.errorMsg = errorMsg; + } + + public Date getOperTime(){ + return operTime; + } + public void setOperTime(Date operTime){ + this.operTime = operTime; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalUserBase.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalUserBase.java new file mode 100644 index 00000000..27366e25 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/TblPortalUserBase.java @@ -0,0 +1,314 @@ +package com.jiuyv.sptccc.agile.portal.dto; + +import java.util.Date; + + +/** + * 用户信息表_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class TblPortalUserBase implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 版本号 + */ + private Long versionNum; + + /** + * 随机码 + */ + private String recToken; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型 + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 头像地址 + */ + private String avatar; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态 + */ + private String status; + + /** + * 删除标志 + */ + private String delFlag; + + /** + * 最后登录ip + */ + private String loginIp; + + /**最后一次登录错误时间*/ + private Date lastLoginErrorTime; + + /** 登录错误计数 */ + private Integer loginErrorcount; + + /** 是否锁定(0、未锁定;1、锁定) */ + private Integer isLocked; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + + + public Long getUserId(){ + return userId; + } + public void setUserId(Long userId){ + this.userId = userId; + } + + public Long getVersionNum(){ + return versionNum; + } + public void setVersionNum(Long versionNum){ + this.versionNum = versionNum; + } + + public String getRecToken(){ + return recToken; + } + public void setRecToken(String recToken){ + this.recToken = recToken; + } + + public Long getDeptId(){ + return deptId; + } + public void setDeptId(Long deptId){ + this.deptId = deptId; + } + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getNickName(){ + return nickName; + } + public void setNickName(String nickName){ + this.nickName = nickName; + } + + public String getUserType(){ + return userType; + } + public void setUserType(String userType){ + this.userType = userType; + } + + public String getEmail(){ + return email; + } + public void setEmail(String email){ + this.email = email; + } + + public String getPhonenumber(){ + return phonenumber; + } + public void setPhonenumber(String phonenumber){ + this.phonenumber = phonenumber; + } + + public String getSex(){ + return sex; + } + public void setSex(String sex){ + this.sex = sex; + } + + public String getAvatar(){ + return avatar; + } + public void setAvatar(String avatar){ + this.avatar = avatar; + } + + public String getPassword(){ + return password; + } + public void setPassword(String password){ + this.password = password; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getDelFlag(){ + return delFlag; + } + public void setDelFlag(String delFlag){ + this.delFlag = delFlag; + } + + public String getLoginIp(){ + return loginIp; + } + public void setLoginIp(String loginIp){ + this.loginIp = loginIp; + } + + public Date getLoginDate(){ + return loginDate; + } + public void setLoginDate(Date loginDate){ + this.loginDate = loginDate; + } + + /** + * @return the lastLoginErrorTime + */ + public Date getLastLoginErrorTime() { + return lastLoginErrorTime; + } + /** + * @param lastLoginErrorTime the lastLoginErrorTime to set + */ + public void setLastLoginErrorTime(Date lastLoginErrorTime) { + this.lastLoginErrorTime = lastLoginErrorTime; + } + /** + * @return the loginErrorcount + */ + public Integer getLoginErrorcount() { + return loginErrorcount; + } + /** + * @param loginErrorcount the loginErrorcount to set + */ + public void setLoginErrorcount(Integer loginErrorcount) { + this.loginErrorcount = loginErrorcount; + } + /** + * @return the isLocked + */ + public Integer getIsLocked() { + return isLocked; + } + /** + * @param isLocked the isLocked to set + */ + public void setIsLocked(Integer isLocked) { + this.isLocked = isLocked; + } + public String getCreateBy(){ + return createBy; + } + public void setCreateBy(String createBy){ + this.createBy = createBy; + } + + public Date getCreateTime(){ + return createTime; + } + public void setCreateTime(Date createTime){ + this.createTime = createTime; + } + + public String getUpdateBy(){ + return updateBy; + } + public void setUpdateBy(String updateBy){ + this.updateBy = updateBy; + } + + public Date getUpdateTime(){ + return updateTime; + } + public void setUpdateTime(Date updateTime){ + this.updateTime = updateTime; + } + + public String getRemark(){ + return remark; + } + public void setRemark(String remark){ + this.remark = remark; + } + +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/common/ResGetFileByteAddDTO.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/common/ResGetFileByteAddDTO.java new file mode 100644 index 00000000..90b844f4 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/common/ResGetFileByteAddDTO.java @@ -0,0 +1,68 @@ +package com.jiuyv.sptccc.agile.portal.dto.common; + +/** + * 获取完整文件返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ResGetFileByteAddDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 文件名 + */ + private String filename; + + /** + * 文件字节 + */ + private byte[] file; + + /** + * 文件后缀类型 + */ + private String type; + + /** + * @return the filename + */ + public String getFilename() { + return filename; + } + + /** + * @param filename the filename to set + */ + public void setFilename(String filename) { + this.filename = filename; + } + + /** + * @return the file + */ + public byte[] getFile() { + return file; + } + + /** + * @param file the file to set + */ + public void setFile(byte[] file) { + this.file = file; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(String type) { + this.type = type; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/logininfor/ReqPortalLogininforAddDTO.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/logininfor/ReqPortalLogininforAddDTO.java new file mode 100644 index 00000000..ce79f068 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/logininfor/ReqPortalLogininforAddDTO.java @@ -0,0 +1,95 @@ +package com.jiuyv.sptccc.agile.portal.dto.logininfor; + +/** + * 系统访问记录_完整返回体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalLogininforAddDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户账号 + */ + private String userName; + + /** + * 登录ip地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态 + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + public String getUserName(){ + return userName; + } + public void setUserName(String userName){ + this.userName = userName; + } + + public String getIpaddr(){ + return ipaddr; + } + public void setIpaddr(String ipaddr){ + this.ipaddr = ipaddr; + } + + public String getLoginLocation(){ + return loginLocation; + } + public void setLoginLocation(String loginLocation){ + this.loginLocation = loginLocation; + } + + public String getBrowser(){ + return browser; + } + public void setBrowser(String browser){ + this.browser = browser; + } + + public String getOs(){ + return os; + } + public void setOs(String os){ + this.os = os; + } + + public String getStatus(){ + return status; + } + public void setStatus(String status){ + this.status = status; + } + + public String getMsg(){ + return msg; + } + public void setMsg(String msg){ + this.msg = msg; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/user/ReqPortalUserResetErrorDTO.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/user/ReqPortalUserResetErrorDTO.java new file mode 100644 index 00000000..b4d71658 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/dto/user/ReqPortalUserResetErrorDTO.java @@ -0,0 +1,122 @@ +package com.jiuyv.sptccc.agile.portal.dto.user; + +import java.util.Date; + +/** + * 用户信息表_重置登陆错误请求体 + * @author zhouliang + * @date 2023-04-25 + */ +public class ReqPortalUserResetErrorDTO implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 最后登录ip + */ + private String loginIp; + + + /**最后一次登录错误时间*/ + private Date lastLoginErrorTime; + + /** 登录错误计数 */ + private Integer loginErrorcount; + + /** 是否锁定(0、未锁定;1、锁定) */ + private Integer isLocked; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * @return the userId + */ + public Long getUserId() { + return userId; + } + + /** + * @param userId the userId to set + */ + public void setUserId(Long userId) { + this.userId = userId; + } + + /** + * @return the loginIp + */ + public String getLoginIp() { + return loginIp; + } + + /** + * @param loginIp the loginIp to set + */ + public void setLoginIp(String loginIp) { + this.loginIp = loginIp; + } + + /** + * @return the lastLoginErrorTime + */ + public Date getLastLoginErrorTime() { + return lastLoginErrorTime; + } + + /** + * @param lastLoginErrorTime the lastLoginErrorTime to set + */ + public void setLastLoginErrorTime(Date lastLoginErrorTime) { + this.lastLoginErrorTime = lastLoginErrorTime; + } + + /** + * @return the loginErrorcount + */ + public Integer getLoginErrorcount() { + return loginErrorcount; + } + + /** + * @param loginErrorcount the loginErrorcount to set + */ + public void setLoginErrorcount(Integer loginErrorcount) { + this.loginErrorcount = loginErrorcount; + } + + /** + * @return the isLocked + */ + public Integer getIsLocked() { + return isLocked; + } + + /** + * @param isLocked the isLocked to set + */ + public void setIsLocked(Integer isLocked) { + this.isLocked = isLocked; + } + + /** + * @return the loginDate + */ + public Date getLoginDate() { + return loginDate; + } + + /** + * @param loginDate the loginDate to set + */ + public void setLoginDate(Date loginDate) { + this.loginDate = loginDate; + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalLogininforMapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalLogininforMapper.java new file mode 100644 index 00000000..ebd451eb --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalLogininforMapper.java @@ -0,0 +1,20 @@ +package com.jiuyv.sptccc.agile.portal.mapper; + +import java.util.List; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author admin + */ +public interface TblPortalLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(TblPortalLogininfor logininfor); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalOperLogMapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalOperLogMapper.java new file mode 100644 index 00000000..b99b2d23 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalOperLogMapper.java @@ -0,0 +1,20 @@ +package com.jiuyv.sptccc.agile.portal.mapper; + +import java.util.List; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalOperLog; + +/** + * 操作日志 数据层 + * + * @author admin + */ +public interface TblPortalOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(TblPortalOperLog operLog); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalUserMapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalUserMapper.java new file mode 100644 index 00000000..a16c2894 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/mapper/TblPortalUserMapper.java @@ -0,0 +1,79 @@ +package com.jiuyv.sptccc.agile.portal.mapper; + +import org.apache.ibatis.annotations.Param; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser; + +/** + * 用户表 数据层 + * + * @author admin + */ +public interface TblPortalUserMapper +{ + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public TblPortalUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public TblPortalUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(TblPortalUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(TblPortalUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public TblPortalUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public TblPortalUser checkEmailUnique(String email); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalLogininforService.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalLogininforService.java new file mode 100644 index 00000000..c88fcb33 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalLogininforService.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.portal.service; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author admin + */ +public interface IPortalLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(TblPortalLogininfor logininfor); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalOperLogService.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalOperLogService.java new file mode 100644 index 00000000..07732bc0 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalOperLogService.java @@ -0,0 +1,18 @@ +package com.jiuyv.sptccc.agile.portal.service; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalOperLog; + +/** + * 操作日志 服务层 + * + * @author admin + */ +public interface IPortalOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(TblPortalOperLog operLog); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalUserService.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalUserService.java new file mode 100644 index 00000000..5152f98a --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/IPortalUserService.java @@ -0,0 +1,87 @@ +package com.jiuyv.sptccc.agile.portal.service; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser; + +/** + * 业务层 + * + * @author admin + */ +public interface IPortalUserService +{ + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public TblPortalUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public TblPortalUser selectUserById(Long userId); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkPhoneUnique(TblPortalUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkEmailUnique(TblPortalUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(TblPortalUser user); + + /** + * 系统修改用户的登陆时相关内容 + * 非常频繁,不变version + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfileNoVersion(TblPortalUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(TblPortalUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalLogininforServiceImpl.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalLogininforServiceImpl.java new file mode 100644 index 00000000..6f38a3f4 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalLogininforServiceImpl.java @@ -0,0 +1,37 @@ +package com.jiuyv.sptccc.agile.portal.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalLogininfor; +import com.jiuyv.sptccc.agile.portal.mapper.TblPortalLogininforMapper; +import com.jiuyv.sptccc.agile.portal.service.IPortalLogininforService; + + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author admin + */ +@Service +public class PortalLogininforServiceImpl implements IPortalLogininforService +{ + + @Autowired + private TblPortalLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(TblPortalLogininfor logininfor) + { + if(StringUtils.isNotBlank(logininfor.getMsg()) && logininfor.getMsg().length()>1000) { + logininfor.setMsg(logininfor.getMsg().substring(0, 1000)); + } + logininforMapper.insertLogininfor(logininfor); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalOperLogServiceImpl.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalOperLogServiceImpl.java new file mode 100644 index 00000000..b661f9e3 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalOperLogServiceImpl.java @@ -0,0 +1,31 @@ +package com.jiuyv.sptccc.agile.portal.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.jiuyv.sptccc.agile.portal.domain.TblPortalOperLog; +import com.jiuyv.sptccc.agile.portal.mapper.TblPortalOperLogMapper; +import com.jiuyv.sptccc.agile.portal.service.IPortalOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author admin + */ +@Service +public class PortalOperLogServiceImpl implements IPortalOperLogService +{ + @Autowired + private TblPortalOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(TblPortalOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalUserServiceImpl.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalUserServiceImpl.java new file mode 100644 index 00000000..2dddb3cf --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/portal/service/impl/PortalUserServiceImpl.java @@ -0,0 +1,137 @@ +package com.jiuyv.sptccc.agile.portal.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.jiuyv.sptccc.agile.common.constant.Constants; +import com.jiuyv.sptccc.agile.common.constant.UserConstants; +import com.jiuyv.sptccc.agile.common.utils.StringUtils; +import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser; +import com.jiuyv.sptccc.agile.portal.mapper.TblPortalUserMapper; +import com.jiuyv.sptccc.agile.portal.service.IPortalUserService; + +/** + * 用户 业务层处理 + * + * @author admin + */ +@Service +public class PortalUserServiceImpl implements IPortalUserService { + private static final Logger log = LoggerFactory.getLogger(PortalUserServiceImpl.class); + + @Autowired + private TblPortalUserMapper userMapper; + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public TblPortalUser selectUserByUserName(String userName) { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public TblPortalUser selectUserById(Long userId) { + return userMapper.selectUserById(userId); + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(TblPortalUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + TblPortalUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(TblPortalUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + TblPortalUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(TblPortalUser user) { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息,不变version + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfileNoVersion(TblPortalUser user) { + user.getParams().put(Constants.NO_VERSION_FLAG, "1");//有值就行 + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(TblPortalUser user) { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) { + return userMapper.resetUserPwd(userName, password); + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/controller/MyTestController.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/controller/MyTestController.java new file mode 100644 index 00000000..be00027b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/controller/MyTestController.java @@ -0,0 +1,15 @@ +package com.jiuyv.sptccc.agile.system.controller; + +import com.jiuyv.sptccc.agile.api.TestFeignApi; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("test00") +public class MyTestController implements TestFeignApi { + + @Override + public String test01() { + return "我测试用的!!!!"; + } +} diff --git a/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/mapper/ISysTimeBaseMapper.java b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/mapper/ISysTimeBaseMapper.java new file mode 100644 index 00000000..ed9aea31 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/java/com/jiuyv/sptccc/agile/system/mapper/ISysTimeBaseMapper.java @@ -0,0 +1,12 @@ +package com.jiuyv.sptccc.agile.system.mapper; + +import com.jiuyv.sptccc.agile.common.core.domain.BaseTime; + +public interface ISysTimeBaseMapper { + + /** + * 获取系统当前时间-yyyyMMddHHmmss + * @return + */ + BaseTime selectSysCurrentTime(); +} diff --git a/agile-portal/agile-portal-service/src/main/resources/application-dev.yml b/agile-portal/agile-portal-service/src/main/resources/application-dev.yml new file mode 100644 index 00000000..a9dc4033 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/application-dev.yml @@ -0,0 +1,104 @@ +# 项目相关配置 +conosle: + # 名称 + name: portal-service + # 版本 + version: 1.0 + # 版权年份 + copyrightYear: 2022 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/chemical/uploadPath,Linux配置 /home/chemical/uploadPath) + profile: /home/flink/sysfile + # 获取ip地址开关 + addressEnabled: false +filesftp: + host: 172.16.12.108 + port: 22 + username: flink + password: mIVYkELj5T+4MnD5+V542A== + +# 开发环境配置 +server: + port: 18082 #本地 + servlet: + # 应用的访问路径 + context-path: /portal-service + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 200 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.jiuyv.sptccc.agile: debug + org.springframework: warn + +# MyBatis配置 +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.jiuyv.sptccc.agile.** + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + autoRuntimeDialect: true + supportMethodsArguments: true + params: count=countSql + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + + +spring: + application: + name: portal-service + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + datasource: #数据源配置 + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://172.16.12.105:5432/keliubao + username: postgres + password: postgres + jackson: + date-format: yyyy-MM-dd HH:mm:ss # 常用的时间格式 + default-property-inclusion: non_null # 忽略空对象 + serialization: + fail-on-empty-beans: false # 序列化空对象时不抛出异常 + indent-output: true # 输出格式化为缩进的JSON + write-dates-as-timestamps: false # 日期序列化为时间戳而不是ISO-8601格式 + deserialization: + fail-on-unknown-properties: false # 反序列化时忽略未知的属性 + cache: + type: caffeine + caffeine.spec: maximumSize=2000,expireAfterWrite=1h \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/application.yml b/agile-portal/agile-portal-service/src/main/resources/application.yml new file mode 100644 index 00000000..7e977d0f --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/application.yml @@ -0,0 +1,19 @@ +# Spring配置 +spring: + profiles: + active: dev + mvc: + view: + static-path-pattern: /static/** + favicon: + enabled: false + + thymeleaf: + prefix: classpath:/static/ + suffix: .html + enabled: true + cache: false + encoding: utf-8 + content-type: text/html + check-template-location: false + mode: HTML \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/banner.txt b/agile-portal/agile-portal-service/src/main/resources/banner.txt new file mode 100644 index 00000000..6e5e0f93 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/banner.txt @@ -0,0 +1,6 @@ +Application Version: ${chemical.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/i18n/messages.properties b/agile-portal/agile-portal-service/src/main/resources/i18n/messages.properties new file mode 100644 index 00000000..a29291c9 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/i18n/messages.properties @@ -0,0 +1,5 @@ +#错误消息 +not.null=* 必须填写 +length.not.valid=长度必须在{min}到{max}个字符之间 +data.not.exist=数据不存在 +data.is.expired=数据已过期,请重新加载 \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/logback-spring.xml b/agile-portal/agile-portal-service/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..71b9f811 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/logback-spring.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + System.out + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + + + ${LOG_HOME}/${APP_NAME}.log + + + ${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log.zip + + + 100MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + + ${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.log + 7 + + + 100MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + + + + ${JSON_LOG_HOME}/${APP_NAME}.json.%d{yyyy-MM-dd}.%i.log + 7 + + + 100MB + + + + + + + { + "timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", + "severity": "%level", + "service": "${APP_NAME:-}", + "trace": "%X{X-B3-TraceId:-}", + "span": "%X{X-B3-SpanId:-}", + "parent": "%X{X-B3-ParentSpanId:-}", + "exportable": "%X{X-Span-Export:-}", + "pid": "${PID:-}", + "thread": "%thread", + "class": "%logger{40}", + "rest": "%message" + } + + + + + + + + ${JSON_LOG_HOME}/${APP_NAME}.json.log + + + ${JSON_LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.%i.json.log.zip + + + 100MB + + + + + + + { + "timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", + "severity": "%level", + "service": "${APP_NAME:-}", + "trace": "%X{X-B3-TraceId:-}", + "span": "%X{X-B3-SpanId:-}", + "parent": "%X{X-B3-ParentSpanId:-}", + "exportable": "%X{X-Span-Export:-}", + "pid": "${PID:-}", + "thread": "%thread", + "class": "%logger{40}", + "rest": "%message" + } + + + + + + + System.err + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalLogininforMapper.xml b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalLogininforMapper.xml new file mode 100644 index 00000000..3fc4ac52 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalLogininforMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + insert into tbl_portal_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, now()) + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalOperLogMapper.xml b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalOperLogMapper.xml new file mode 100644 index 00000000..cdd2a94b --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalOperLogMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + insert into tbl_portal_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, now()) + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalUserMapper.xml b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalUserMapper.xml new file mode 100644 index 00000000..df115b3c --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/mapper/portal/TblPortalUserMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.version_num,u.rec_token, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + u.last_login_error_time, u.login_error_count, u.is_locked, u.enterprise_name, u.industry_category, u.social_credit_code, u.enterprise_industry, u.enterprise_address + from tbl_portal_user u + + + + + + + + + + + + + + update tbl_portal_user + + + version_num = version_num+1, + + rec_token = #{recToken}, + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + enterprise_name = #{enterpriseName}, + industry_category = #{industryCategory}, + social_credit_code = #{socialCreditCode}, + enterprise_industry = #{enterpriseIndustry}, + enterprise_address = #{enterpriseAddress}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + last_login_error_time = #{lastLoginErrorTime}, + login_error_count = #{loginErrorcount}, + is_locked = #{isLocked}, + remark = #{remark}, + update_time = now() + + where user_id = #{userId} + AND version_num = #{params.versionNum} + AND rec_token = #{params.recToken} + + + + update tbl_portal_user set version_num = version_num+1, avatar = #{avatar} where user_name = #{userName} + + + + update tbl_portal_user set version_num = version_num+1, password = #{password} where user_name = #{userName} + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/mapper/system/SysBaseMapper.xml b/agile-portal/agile-portal-service/src/main/resources/mapper/system/SysBaseMapper.xml new file mode 100644 index 00000000..0d2c5ee2 --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/mapper/system/SysBaseMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-service/src/main/resources/mybatis/mybatis-config.xml b/agile-portal/agile-portal-service/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 00000000..ac37c09c --- /dev/null +++ b/agile-portal/agile-portal-service/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4.java b/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4.java new file mode 100644 index 00000000..60b5698f --- /dev/null +++ b/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4.java @@ -0,0 +1,121 @@ +package com.jiuyv.sptccc.agile; + +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Thread) +public class TestSm4 { + private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final String sec_Key = "809bf66f0f47f2c4c7183cf5df6e3a92"; + private String str1; + private String str10; + private String str100; + private String str1000; + private String str10000; + + @Setup(Level.Iteration) + public void setUp() throws Exception { + this.str1 = generateRandomString(1); + this.str10 = generateRandomString(10); + this.str100 = generateRandomString(100); + this.str1000 = generateRandomString(1000); + this.str10000 = generateRandomString(10000); + } + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder().include(TestSm4.class.getSimpleName()) + //.warmupIterations(2) + //.measurementIterations(4) + //.threads(10) +// .result("Sm4PerformanceTesting.json").resultFormat(ResultFormatType.JSON) + .forks(1) + .build(); + new Runner(options).run(); + + } + + @Benchmark + public String decrypt1() throws Exception { + return Sm4Util.decryptEcb(sec_Key, str1); + } + + @Benchmark + public String decrypt10() throws Exception { + return Sm4Util.decryptEcb(sec_Key, str10); + } + + @Benchmark + public String decrypt100() throws Exception { + return Sm4Util.decryptEcb(sec_Key, str100); + } + + @Benchmark + public String decrypt1000() throws Exception { + return Sm4Util.decryptEcb(sec_Key, str1000); + } + + @Benchmark + public String decrypt10000() throws Exception { + return Sm4Util.decryptEcb(sec_Key, str10000); + } + +// @Benchmark +// public String encrypt1() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str1); +// } +// +// +// @Benchmark +// public String encrypt10() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str10); +// } +// +// +// @Benchmark +// public String encrypt100() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str100); +// } +// +// +// @Benchmark +// public String encrypt1000() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str1000); +// } +// +// +// @Benchmark +// public String encrypt10000() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str10000); +// } + + + + + + public static String generateRandomString(int length) throws Exception { + StringBuilder builder = new StringBuilder(); + Random random = new Random(); + while (builder.length() < length) { + int index = (int) (random.nextFloat() * ALPHA_NUMERIC_STRING.length()); + builder.append(ALPHA_NUMERIC_STRING.charAt(index)); + } +// return builder.toString(); + return Sm4Util.encryptEcb(sec_Key, builder.toString()); + } +} diff --git a/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4AndRAS.java b/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4AndRAS.java new file mode 100644 index 00000000..7c0e354a --- /dev/null +++ b/agile-portal/agile-portal-service/src/test/java/com/jiuyv/sptccc/agile/TestSm4AndRAS.java @@ -0,0 +1,159 @@ +package com.jiuyv.sptccc.agile; + +import com.jiuyv.sptccc.agile.common.utils.sm4.Sm4Util; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +//@BenchmarkMode(Mode.AverageTime) +//@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Thread) +public class TestSm4AndRAS { + private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final String sec_Key = "809bf66f0f47f2c4c7183cf5df6e3a92"; + private String str1; + private String str10; + private String str100; + private String str1000; + private String str10000; + + private static final int KEY_SIZE = 1024; + private static final long TIME_OUT = 1000 << 4; + private KeyPair keyPair; + private Cipher cipher; + + @Setup(Level.Iteration) + public void setUp() throws Exception { + this.str1 = generateRandomString(1); + this.str10 = generateRandomString(10); + this.str100 = generateRandomString(100); + this.str1000 = generateRandomString(1000); + this.str10000 = generateRandomString(10000); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(KEY_SIZE); + this.keyPair = keyPairGenerator.generateKeyPair(); + this.cipher = Cipher.getInstance("RSA"); +// this.cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + this.cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic()); + } + + public static void main(String[] args) throws Exception { +// TestSm4AndRAS andRAS = new TestSm4AndRAS(); +// andRAS.setUp(); +// System.out.println(andRAS.ras100()); + Options options = new OptionsBuilder().include(TestSm4AndRAS.class.getSimpleName()) + //.warmupIterations(2) + //.measurementIterations(4) + //.threads(10) +// .result("Sm4PerformanceTesting.json").resultFormat(ResultFormatType.JSON) + .forks(1) + .build(); + new Runner(options).run(); + + } + + @Benchmark + public String ras10() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + byte[] encryptedMessage = cipher.doFinal(str10.getBytes()); + return Base64.getEncoder().encodeToString(encryptedMessage); + } + + @Benchmark + public String ras100() throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException { + byte[] encryptedMessage = cipher.doFinal(str100.getBytes()); + return Base64.getEncoder().encodeToString(encryptedMessage); + } + +// @Benchmark +// public String decrypt1() throws Exception { +// return Sm4Util.decryptEcb(sec_Key, str1); +// } +// +// @Benchmark +// public String decrypt10() throws Exception { +// return Sm4Util.decryptEcb(sec_Key, str10); +// } +// +// @Benchmark +// public String decrypt100() throws Exception { +// return Sm4Util.decryptEcb(sec_Key, str100); +// } +// +// @Benchmark +// public String decrypt1000() throws Exception { +// return Sm4Util.decryptEcb(sec_Key, str1000); +// } +// +// @Benchmark +// public String decrypt10000() throws Exception { +// return Sm4Util.decryptEcb(sec_Key, str10000); +// } + +// @Benchmark +// public String encrypt1() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str1); +// } +// + + @Benchmark + public String encrypt10() throws Exception { + return Sm4Util.encryptEcb(sec_Key, str10); + } + + + @Benchmark + public String encrypt100() throws Exception { + return Sm4Util.encryptEcb(sec_Key, str100); + } + +// +// @Benchmark +// public String encrypt1000() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str1000); +// } +// +// +// @Benchmark +// public String encrypt10000() throws Exception { +// return Sm4Util.encryptEcb(sec_Key, str10000); +// } + + + + + + public static String generateRandomString(int length) throws Exception { + StringBuilder builder = new StringBuilder(); + Random random = new Random(); + while (builder.length() < length) { + int index = (int) (random.nextFloat() * ALPHA_NUMERIC_STRING.length()); + builder.append(ALPHA_NUMERIC_STRING.charAt(index)); + } + return builder.toString(); +// return Sm4Util.encryptEcb(sec_Key, builder.toString()); + } +} diff --git a/agile-portal/agile-portal-ui/.editorconfig b/agile-portal/agile-portal-ui/.editorconfig new file mode 100644 index 00000000..7034f9bf --- /dev/null +++ b/agile-portal/agile-portal-ui/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/agile-portal/agile-portal-ui/.env.development b/agile-portal/agile-portal-ui/.env.development new file mode 100644 index 00000000..ab1ffa77 --- /dev/null +++ b/agile-portal/agile-portal-ui/.env.development @@ -0,0 +1,11 @@ +# 页面标题 +VUE_APP_TITLE = 久事数字 + +# 开发环境配置 +ENV = 'development' + +# 久事数字/开发环境 +VUE_APP_BASE_API = '/dev-api' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/agile-portal/agile-portal-ui/.env.production b/agile-portal/agile-portal-ui/.env.production new file mode 100644 index 00000000..97c24f27 --- /dev/null +++ b/agile-portal/agile-portal-ui/.env.production @@ -0,0 +1,8 @@ +# 页面标题 +VUE_APP_TITLE = 久事数字 + +# 生产环境配置 +ENV = 'production' + +# 久事数字/生产环境 +VUE_APP_BASE_API = '' diff --git a/agile-portal/agile-portal-ui/.env.staging b/agile-portal/agile-portal-ui/.env.staging new file mode 100644 index 00000000..57114620 --- /dev/null +++ b/agile-portal/agile-portal-ui/.env.staging @@ -0,0 +1,10 @@ +# 页面标题 +VUE_APP_TITLE = 久事数字 + +NODE_ENV = production + +# 测试环境配置 +ENV = 'staging' + +# 久事数字/测试环境 +VUE_APP_BASE_API = '/stage-api' diff --git a/agile-portal/agile-portal-ui/.eslintignore b/agile-portal/agile-portal-ui/.eslintignore new file mode 100644 index 00000000..21cfec76 --- /dev/null +++ b/agile-portal/agile-portal-ui/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue diff --git a/agile-portal/agile-portal-ui/.eslintrc.js b/agile-portal/agile-portal-ui/.eslintrc.js new file mode 100644 index 00000000..82bbdeea --- /dev/null +++ b/agile-portal/agile-portal-ui/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/agile-portal/agile-portal-ui/.gitignore b/agile-portal/agile-portal-ui/.gitignore new file mode 100644 index 00000000..78a752d8 --- /dev/null +++ b/agile-portal/agile-portal-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/agile-portal/agile-portal-ui/babel.config.js b/agile-portal/agile-portal-ui/babel.config.js new file mode 100644 index 00000000..c8267b2d --- /dev/null +++ b/agile-portal/agile-portal-ui/babel.config.js @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + 'plugins': ['dynamic-import-node'] + } + } +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/package.json b/agile-portal/agile-portal-ui/package.json new file mode 100644 index 00000000..57f734e3 --- /dev/null +++ b/agile-portal/agile-portal-ui/package.json @@ -0,0 +1,97 @@ +{ + "name": "jiuyv", + "version": "3.8.3", + "description": "久事数字", + "author": "jiuyv", + "license": "MIT", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging", + "preview": "node build/index.js --preview", + "lint": "eslint --ext .js,.vue src" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "keywords": [ + "vue", + "admin", + "dashboard", + "element-ui", + "boilerplate", + "admin-template", + "management-system" + ], + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "axios": "0.24.0", + "clipboard": "2.0.8", + "core-js": "3.19.1", + "crypto-js": "^4.1.1", + "echarts": "4.9.0", + "echarts-liquidfill": "^2.0.6", + "echarts-wordcloud": "^1.1.3", + "element-ui": "2.15.8", + "file-saver": "2.0.5", + "fuse.js": "6.4.3", + "gm-crypto": "^0.1.8", + "highlight.js": "9.18.5", + "js-beautify": "1.13.0", + "js-cookie": "3.0.1", + "jsencrypt": "3.0.0-rc.1", + "nprogress": "0.2.0", + "quill": "1.3.7", + "screenfull": "5.0.2", + "sortablejs": "1.10.2", + "sql-formatter": "^2.3.3", + "vue": "2.6.12", + "vue-codemirror": "^4.0.6", + "vue-count-to": "1.0.13", + "vue-cropper": "0.5.5", + "vue-meta": "2.4.0", + "vue-router": "3.4.9", + "vuedraggable": "2.24.3", + "vuex": "3.6.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-plugin-eslint": "4.4.6", + "@vue/cli-service": "4.4.6", + "babel-eslint": "10.1.0", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "4.1.0", + "compression-webpack-plugin": "5.0.2", + "connect": "3.6.6", + "eslint": "7.15.0", + "eslint-plugin-vue": "7.2.0", + "html-webpack-plugin": "^4.5.1", + "lint-staged": "10.5.3", + "runjs": "4.4.2", + "sass": "1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "2.1.5", + "svg-sprite-loader": "5.1.1", + "vue-template-compiler": "2.6.12" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/agile-portal/agile-portal-ui/public/favicon.ico b/agile-portal/agile-portal-ui/public/favicon.ico new file mode 100644 index 00000000..37b5fff5 Binary files /dev/null and b/agile-portal/agile-portal-ui/public/favicon.ico differ diff --git a/agile-portal/agile-portal-ui/public/favicon.png b/agile-portal/agile-portal-ui/public/favicon.png new file mode 100644 index 00000000..47fdeda8 Binary files /dev/null and b/agile-portal/agile-portal-ui/public/favicon.png differ diff --git a/agile-portal/agile-portal-ui/public/favicon1.ico b/agile-portal/agile-portal-ui/public/favicon1.ico new file mode 100644 index 00000000..37b5fff5 Binary files /dev/null and b/agile-portal/agile-portal-ui/public/favicon1.ico differ diff --git a/agile-portal/agile-portal-ui/public/html/ie.html b/agile-portal/agile-portal-ui/public/html/ie.html new file mode 100644 index 00000000..052ffcd6 --- /dev/null +++ b/agile-portal/agile-portal-ui/public/html/ie.html @@ -0,0 +1,46 @@ + + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/public/index.html b/agile-portal/agile-portal-ui/public/index.html new file mode 100644 index 00000000..fe9e15f9 --- /dev/null +++ b/agile-portal/agile-portal-ui/public/index.html @@ -0,0 +1,210 @@ + + + + + + + + + <%= webpackConfig.name %> + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + diff --git a/agile-portal/agile-portal-ui/public/robots.txt b/agile-portal/agile-portal-ui/public/robots.txt new file mode 100644 index 00000000..77470cb3 --- /dev/null +++ b/agile-portal/agile-portal-ui/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/App.vue b/agile-portal/agile-portal-ui/src/App.vue new file mode 100644 index 00000000..033f862b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/App.vue @@ -0,0 +1,42 @@ + + + + diff --git a/agile-portal/agile-portal-ui/src/api/business/absolve.js b/agile-portal/agile-portal-ui/src/api/business/absolve.js new file mode 100644 index 00000000..389226f2 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/absolve.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危险货物品数据可免除量列表 +export function listAbsolve(query) { + return request({ + url: '/business/absolve/list', + method: 'get', + params: query + }) +} + +// 查询危险货物品数据可免除量详细 +export function getAbsolve(absolveCode) { + return request({ + url: '/business/absolve/' + absolveCode, + method: 'get' + }) +} + +// 新增危险货物品数据可免除量 +export function addAbsolve(data) { + return request({ + url: '/business/absolve', + method: 'post', + data: data + }) +} + +// 修改危险货物品数据可免除量 +export function updateAbsolve(data) { + return request({ + url: '/business/absolve', + method: 'put', + data: data + }) +} + +// 删除危险货物品数据可免除量 +export function delAbsolve(absolveCode) { + return request({ + url: '/business/absolve/' + absolveCode, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/catalogue.js b/agile-portal/agile-portal-ui/src/api/business/catalogue.js new file mode 100644 index 00000000..aad8f53d --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/catalogue.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危化品目录管理列表 +export function listCatalogue(query) { + return request({ + url: '/business/catalogue/list', + method: 'get', + params: query + }) +} + +// 查询危化品目录管理详细 +export function getCatalogue(id) { + return request({ + url: '/business/catalogue/' + id, + method: 'get' + }) +} + +// 新增危化品目录管理 +export function addCatalogue(data) { + return request({ + url: '/business/catalogue', + method: 'post', + data: data + }) +} + +// 修改危化品目录管理 +export function updateCatalogue(data) { + return request({ + url: '/business/catalogue', + method: 'put', + data: data + }) +} + +// 删除危化品目录管理 +export function delCatalogue(id) { + return request({ + url: '/business/catalogue/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/checkEaInfo.js b/agile-portal/agile-portal-ui/src/api/business/checkEaInfo.js new file mode 100644 index 00000000..35f30420 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/checkEaInfo.js @@ -0,0 +1,50 @@ +import request from '@/utils/request' + +// 查询电子底账列表 +export function listCheckEaInfo(query) { + return request({ + url: '/business/checkEaInfo/list', + method: 'get', + params: query + }) +} + +// 查询电子底账详细 +export function getCheckEaInfo(id) { + return request({ + url: '/business/checkEaInfo/' + id, + method: 'get' + }) +} + +// 新增电子底账 +export function addCheckEaInfo(data) { + return request({ + url: '/business/checkEaInfo', + method: 'post', + data: data + }) +} + +// 修改电子底账 +export function updateCheckEaInfo(data) { + return request({ + url: '/business/checkEaInfo', + method: 'put', + data: data + }) +} + +// 删除电子底账 +export function delCheckEaInfo(id) { + return request({ + url: '/business/checkEaInfo/' + id, + method: 'delete' + }) +} +export function getResultInfo(id) { + return request({ + url: '/business/checkEaInfo/checkAndReviewEaInfo/' + id, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/checkInInfo.js b/agile-portal/agile-portal-ui/src/api/business/checkInInfo.js new file mode 100644 index 00000000..f19f1ea9 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/checkInInfo.js @@ -0,0 +1,66 @@ +import request from '@/utils/request' + +// 查询进口报关列表 +export function listCheckInInfo(query) { + return request({ + url: '/business/checkInInfo/list', + method: 'get', + params: query + }) +} + +// 查询进口报关详细 +export function getCheckInInfo(id) { + return request({ + url: '/business/checkInInfo/' + id, + method: 'get' + }) +} + +// 新增进口报关 +export function addCheckInInfo(data) { + return request({ + url: '/business/checkInInfo', + method: 'post', + data: data + }) +} + +// 修改进口报关 +export function updateCheckInInfo(data) { + return request({ + url: '/business/checkInInfo', + method: 'put', + data: data + }) +} + +// 删除进口报关 +export function delCheckInInfo(id) { + return request({ + url: '/business/checkInInfo/' + id, + method: 'delete' + }) +} +export function getResultInfo(id) { + return request({ + url: '/business/checkInInfo/checkAndReviewInInfo/' + id, + method: 'get' + }) +} +//审单结果查询 +export function reviewCheckInInfo(query) { + return request({ + url: '/business/checkInInfo/reviewInInfo', + method: 'get', + params: query + }) +} +//审单结果查询 +export function flushCheckInInfo(query) { + return request({ + url: '/business/checkInInfo/flushInInfo', + method: 'get', + params: query + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/checkPaInfo.js b/agile-portal/agile-portal-ui/src/api/business/checkPaInfo.js new file mode 100644 index 00000000..d372c9b4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/checkPaInfo.js @@ -0,0 +1,50 @@ +import request from '@/utils/request' + +// 查询包装类型列表 +export function listCheckPaInfo(query) { + return request({ + url: '/business/checkPaInfo/list', + method: 'get', + params: query + }) +} + +// 查询包装类型详细 +export function getCheckPaInfo(id) { + return request({ + url: '/business/checkPaInfo/' + id, + method: 'get' + }) +} + +// 新增包装类型 +export function addCheckPaInfo(data) { + return request({ + url: '/business/checkPaInfo', + method: 'post', + data: data + }) +} + +// 修改包装类型 +export function updateCheckPaInfo(data) { + return request({ + url: '/business/checkPaInfo', + method: 'put', + data: data + }) +} + +// 删除包装类型 +export function delCheckPaInfo(id) { + return request({ + url: '/business/checkPaInfo/' + id, + method: 'delete' + }) +} +export function getResultInfo(id) { + return request({ + url: '/business/checkPaInfo/checkAndReviewPaInfo/' + id, + method: 'get' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/business/checkRule.js b/agile-portal/agile-portal-ui/src/api/business/checkRule.js new file mode 100644 index 00000000..f25a0401 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/checkRule.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询校验规则列表 +export function listCheckRule(query) { + return request({ + url: '/business/checkRule/list', + method: 'get', + params: query + }) +} + +// 查询校验规则详细 +export function getCheckRule(id) { + return request({ + url: '/business/checkRule/' + id, + method: 'get' + }) +} + +// 新增校验规则 +export function addCheckRule(data) { + return request({ + url: '/business/checkRule', + method: 'post', + data: data + }) +} + +// 修改校验规则 +export function updateCheckRule(data) { + return request({ + url: '/business/checkRule', + method: 'put', + data: data + }) +} + +// 删除校验规则 +export function delCheckRule(id) { + return request({ + url: '/business/checkRule/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/dangerGoods.js b/agile-portal/agile-portal-ui/src/api/business/dangerGoods.js new file mode 100644 index 00000000..a8052cc6 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/dangerGoods.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询CIQ危险化学品列表 +export function listDangerGoods(query) { + return request({ + url: '/business/dangerGoods/list', + method: 'get', + params: query + }) +} + +// 查询CIQ危险化学品详细 +export function getDangerGoods(id) { + return request({ + url: '/business/dangerGoods/' + id, + method: 'get' + }) +} + +// 新增CIQ危险化学品 +export function addDangerGoods(data) { + return request({ + url: '/business/dangerGoods', + method: 'post', + data: data + }) +} + +// 修改CIQ危险化学品 +export function updateDangerGoods(data) { + return request({ + url: '/business/dangerGoods', + method: 'put', + data: data + }) +} + +// 删除CIQ危险化学品 +export function delDangerGoods(id) { + return request({ + url: '/business/dangerGoods/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/gasBase.js b/agile-portal/agile-portal-ui/src/api/business/gasBase.js new file mode 100644 index 00000000..5d0d8af8 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/gasBase.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询压缩气体基础数据列表 +export function listGasBase(query) { + return request({ + url: '/business/gasBase/list', + method: 'get', + params: query + }) +} + +// 查询压缩气体基础数据详细 +export function getGasBase(id) { + return request({ + url: '/business/gasBase/' + id, + method: 'get' + }) +} + +// 新增压缩气体基础数据 +export function addGasBase(data) { + return request({ + url: '/business/gasBase', + method: 'post', + data: data + }) +} + +// 修改压缩气体基础数据 +export function updateGasBase(data) { + return request({ + url: '/business/gasBase', + method: 'put', + data: data + }) +} + +// 删除压缩气体基础数据 +export function delGasBase(id) { + return request({ + url: '/business/gasBase/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/goods.js b/agile-portal/agile-portal-ui/src/api/business/goods.js new file mode 100644 index 00000000..1e91318c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/goods.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危险品货物管理列表 +export function listGoods(query) { + return request({ + url: '/business/goods/list', + method: 'get', + params: query + }) +} + +// 查询危险品货物管理详细 +export function getGoods(id) { + return request({ + url: '/business/goods/' + id, + method: 'get' + }) +} + +// 新增危险品货物管理 +export function addGoods(data) { + return request({ + url: '/business/goods', + method: 'post', + data: data + }) +} + +// 修改危险品货物管理 +export function updateGoods(data) { + return request({ + url: '/business/goods', + method: 'put', + data: data + }) +} + +// 删除危险品货物管理 +export function delGoods(id) { + return request({ + url: '/business/goods/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/identify.js b/agile-portal/agile-portal-ui/src/api/business/identify.js new file mode 100644 index 00000000..340b8938 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/identify.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危险品分类鉴定数据列表 +export function listIdentify(query) { + return request({ + url: '/business/identify/list', + method: 'get', + params: query + }) +} + +// 查询危险品分类鉴定数据详细 +export function getIdentify(id) { + return request({ + url: '/business/identify/' + id, + method: 'get' + }) +} + +// 新增危险品分类鉴定数据 +export function addIdentify(data) { + return request({ + url: '/business/identify', + method: 'post', + data: data + }) +} + +// 修改危险品分类鉴定数据 +export function updateIdentify(data) { + return request({ + url: '/business/identify', + method: 'put', + data: data + }) +} + +// 删除危险品分类鉴定数据 +export function delIdentify(id) { + return request({ + url: '/business/identify/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/info.js b/agile-portal/agile-portal-ui/src/api/business/info.js new file mode 100644 index 00000000..3e336728 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/info.js @@ -0,0 +1,61 @@ +import request from '@/utils/request' + +// 查询校验数据列表 +export function listInfo(query) { + return request({ + url: '/business/info/list', + method: 'get', + params: query + }) +} + +// 查询校验数据详细 +export function getInfo(id) { + return request({ + url: '/business/info/' + id, + method: 'get' + }) +} + +// 新增校验数据 +export function addInfo(data) { + return request({ + url: '/business/info', + method: 'post', + data: data + }) +} + +// 修改校验数据 +export function updateInfo(data) { + return request({ + url: '/business/info', + method: 'put', + data: data + }) +} + +// 删除校验数据 +export function delInfo(id) { + return request({ + url: '/business/info/' + id, + method: 'delete' + }) + + +} + +// 校验数据 +export function check(id) { + return request({ + url: '/business/info/check/' + id, + method: 'get' + }) +} + +export function getResultInfo(id) { + return request({ + url: '/business/info/getResultInfo/' + id, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/isolation.js b/agile-portal/agile-portal-ui/src/api/business/isolation.js new file mode 100644 index 00000000..f4cbdd25 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/isolation.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危险货物品数据隔离列表 +export function listIsolation(query) { + return request({ + url: '/business/isolation/list', + method: 'get', + params: query + }) +} + +// 查询危险货物品数据隔离详细 +export function getIsolation(operationCode) { + return request({ + url: '/business/isolation/' + operationCode, + method: 'get' + }) +} + +// 新增危险货物品数据隔离 +export function addIsolation(data) { + return request({ + url: '/business/isolation', + method: 'post', + data: data + }) +} + +// 修改危险货物品数据隔离 +export function updateIsolation(data) { + return request({ + url: '/business/isolation', + method: 'put', + data: data + }) +} + +// 删除危险货物品数据隔离 +export function delIsolation(operationCode) { + return request({ + url: '/business/isolation/' + operationCode, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/operation.js b/agile-portal/agile-portal-ui/src/api/business/operation.js new file mode 100644 index 00000000..43a99e89 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/operation.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询危险货物品数据积载与操作列表 +export function listOperation(query) { + return request({ + url: '/business/operation/list', + method: 'get', + params: query + }) +} + +// 查询危险货物品数据积载与操作详细 +export function getOperation(operationCode) { + return request({ + url: '/business/operation/' + operationCode, + method: 'get' + }) +} + +// 新增危险货物品数据积载与操作 +export function addOperation(data) { + return request({ + url: '/business/operation', + method: 'post', + data: data + }) +} + +// 修改危险货物品数据积载与操作 +export function updateOperation(data) { + return request({ + url: '/business/operation', + method: 'put', + data: data + }) +} + +// 删除危险货物品数据积载与操作 +export function delOperation(operationCode) { + return request({ + url: '/business/operation/' + operationCode, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/organicPeroxide.js b/agile-portal/agile-portal-ui/src/api/business/organicPeroxide.js new file mode 100644 index 00000000..981222e7 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/organicPeroxide.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询有机过氧化物列表 +export function listOrganicPeroxide(query) { + return request({ + url: '/business/organicPeroxide/list', + method: 'get', + params: query + }) +} + +// 查询有机过氧化物详细 +export function getOrganicPeroxide(id) { + return request({ + url: '/business/organicPeroxide/' + id, + method: 'get' + }) +} + +// 新增有机过氧化物 +export function addOrganicPeroxide(data) { + return request({ + url: '/business/organicPeroxide', + method: 'post', + data: data + }) +} + +// 修改有机过氧化物 +export function updateOrganicPeroxide(data) { + return request({ + url: '/business/organicPeroxide', + method: 'put', + data: data + }) +} + +// 删除有机过氧化物 +export function delOrganicPeroxide(id) { + return request({ + url: '/business/organicPeroxide/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/packageBase.js b/agile-portal/agile-portal-ui/src/api/business/packageBase.js new file mode 100644 index 00000000..43c3cb42 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/packageBase.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询固体液体数据管理列表 +export function listPackageBase(query) { + return request({ + url: '/business/packageBase/list', + method: 'get', + params: query + }) +} + +// 查询固体液体数据管理详细 +export function getPackageBase(id) { + return request({ + url: '/business/packageBase/' + id, + method: 'get' + }) +} + +// 新增固体液体数据管理 +export function addPackageBase(data) { + return request({ + url: '/business/packageBase', + method: 'post', + data: data + }) +} + +// 修改固体液体数据管理 +export function updatePackageBase(data) { + return request({ + url: '/business/packageBase', + method: 'put', + data: data + }) +} + +// 删除固体液体数据管理 +export function delPackageBase(id) { + return request({ + url: '/business/packageBase/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/packageRule.js b/agile-portal/agile-portal-ui/src/api/business/packageRule.js new file mode 100644 index 00000000..b656fc27 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/packageRule.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询包装导则管理列表 +export function listPackageRule(query) { + return request({ + url: '/business/packageRule/list', + method: 'get', + params: query + }) +} + +// 查询包装导则管理详细 +export function getPackageRule(id) { + return request({ + url: '/business/packageRule/' + id, + method: 'get' + }) +} + +// 查询包装导则管理详细 +export function getPackageRuleInfo(id) { + return request({ + url: '/business/packageRule/edit/' + id, + method: 'get' + }) +} + +// 新增包装导则管理 +export function addPackageRule(data) { + return request({ + url: '/business/packageRule', + method: 'post', + data: data + }) +} + +// 修改包装导则管理 +export function updatePackageRule(data) { + return request({ + url: '/business/packageRule', + method: 'put', + data: data + }) +} + +// 删除包装导则管理 +export function delPackageRule(id) { + return request({ + url: '/business/packageRule/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/peroxideType52Base.js b/agile-portal/agile-portal-ui/src/api/business/peroxideType52Base.js new file mode 100644 index 00000000..38041bf1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/peroxideType52Base.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询5.2类有机过氧化物数据列表 +export function listPeroxideType52Base(query) { + return request({ + url: '/business/peroxideType52Base/list', + method: 'get', + params: query + }) +} + +// 查询5.2类有机过氧化物数据详细 +export function getPeroxideType52Base(id) { + return request({ + url: '/business/peroxideType52Base/' + id, + method: 'get' + }) +} + +// 新增5.2类有机过氧化物数据 +export function addPeroxideType52Base(data) { + return request({ + url: '/business/peroxideType52Base', + method: 'post', + data: data + }) +} + +// 修改5.2类有机过氧化物数据 +export function updatePeroxideType52Base(data) { + return request({ + url: '/business/peroxideType52Base', + method: 'put', + data: data + }) +} + +// 删除5.2类有机过氧化物数据 +export function delPeroxideType52Base(id) { + return request({ + url: '/business/peroxideType52Base/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/peroxideTypefBase.js b/agile-portal/agile-portal-ui/src/api/business/peroxideTypefBase.js new file mode 100644 index 00000000..29d4bf1a --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/peroxideTypefBase.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询 F型有机过氧化物数据列表 +export function listPeroxideTypefBase(query) { + return request({ + url: '/business/peroxideTypefBase/list', + method: 'get', + params: query + }) +} + +// 查询 F型有机过氧化物数据详细 +export function getPeroxideTypefBase(id) { + return request({ + url: '/business/peroxideTypefBase/' + id, + method: 'get' + }) +} + +// 新增 F型有机过氧化物数据 +export function addPeroxideTypefBase(data) { + return request({ + url: '/business/peroxideTypefBase', + method: 'post', + data: data + }) +} + +// 修改 F型有机过氧化物数据 +export function updatePeroxideTypefBase(data) { + return request({ + url: '/business/peroxideTypefBase', + method: 'put', + data: data + }) +} + +// 删除 F型有机过氧化物数据 +export function delPeroxideTypefBase(id) { + return request({ + url: '/business/peroxideTypefBase/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/ruleGroup.js b/agile-portal/agile-portal-ui/src/api/business/ruleGroup.js new file mode 100644 index 00000000..923539e1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/ruleGroup.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询校验规则组列表 +export function listRuleGroup(query) { + return request({ + url: '/business/ruleGroup/list', + method: 'get', + params: query + }) +} + +// 查询校验规则组详细 +export function getRuleGroup(id) { + return request({ + url: '/business/ruleGroup/' + id, + method: 'get' + }) +} + +// 新增校验规则组 +export function addRuleGroup(data) { + return request({ + url: '/business/ruleGroup', + method: 'post', + data: data + }) +} + +// 修改校验规则组 +export function updateRuleGroup(data) { + return request({ + url: '/business/ruleGroup', + method: 'put', + data: data + }) +} + +// 删除校验规则组 +export function delRuleGroup(id) { + return request({ + url: '/business/ruleGroup/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/business/specialRule.js b/agile-portal/agile-portal-ui/src/api/business/specialRule.js new file mode 100644 index 00000000..e060b670 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/business/specialRule.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询特殊规定管理列表 +export function listSpecialRule(query) { + return request({ + url: '/business/specialRule/list', + method: 'get', + params: query + }) +} + +// 查询特殊规定管理详细 +export function getSpecialRule(id) { + return request({ + url: '/business/specialRule/' + id, + method: 'get' + }) +} + +// 新增特殊规定管理 +export function addSpecialRule(data) { + return request({ + url: '/business/specialRule', + method: 'post', + data: data + }) +} + +// 修改特殊规定管理 +export function updateSpecialRule(data) { + return request({ + url: '/business/specialRule', + method: 'put', + data: data + }) +} + +// 删除特殊规定管理 +export function delSpecialRule(id) { + return request({ + url: '/business/specialRule/' + id, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/delegate.js b/agile-portal/agile-portal-ui/src/api/ccicsh/delegate.js new file mode 100644 index 00000000..69567ad4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/delegate.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询列表 +export function listdelegate(query) { + return request({ + url: '/business/delegate/page/list', + method: 'get', + params: query + }) +} + +// 查询详细 +export function getdelegate(Id) { + return request({ + url: '/business/delegate/' + Id, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/goodsDeclare.js b/agile-portal/agile-portal-ui/src/api/ccicsh/goodsDeclare.js new file mode 100644 index 00000000..4fdfd991 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/goodsDeclare.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询列表 +export function listdelegate(query) { + return request({ + url: '/business/goodsDeclare/page/list', + method: 'get', + params: query + }) +} + +// 查询详细 +export function getdelegate(Id) { + return request({ + url: '/business/goodsDeclare/' + Id, + method: 'get' + }) +} + +// 随附文件 +export function getFileList(data) { + return request({ + url: '/system/dict/data/fileType/', + method: 'post', + data: data + }) +} + +// 随附文件 +export function attachFileList(Id) { + return request({ + url: '/business/attachFile/list?declareId=' + Id, + method: 'get' + }) +} +// 随附文件 +export function downloadFile(query) { + return request({ + url: '/business/attachFile/download', + method: 'get', + params: query, + responseType: 'blob', + }) +} + +// 查询详细 +export function getdelegateList(Id) { + return request({ + url: '/business/delegate/list/' + Id, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/his.js b/agile-portal/agile-portal-ui/src/api/ccicsh/his.js new file mode 100644 index 00000000..e38f5102 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/his.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询随附文件信息列表 +export function listHis(query) { + return request({ + url: '/business/his/list', + method: 'get', + params: query + }) +} + +// 查询随附文件信息详细 +export function getHis(hisId) { + return request({ + url: '/business/his/' + hisId, + method: 'get' + }) +} + +// 新增随附文件信息 +export function addHis(data) { + return request({ + url: '/business/his', + method: 'post', + data: data + }) +} + +// 修改随附文件信息 +export function updateHis(data) { + return request({ + url: '/business/his', + method: 'put', + data: data + }) +} + +// 删除随附文件信息 +export function delHis(hisId) { + return request({ + url: '/business/his/' + hisId, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/info.js b/agile-portal/agile-portal-ui/src/api/ccicsh/info.js new file mode 100644 index 00000000..89fca62b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/info.js @@ -0,0 +1,66 @@ +import request from '@/utils/request' + +// 查询货代公司列表 +export function listInfo(query) { + return request({ + url: '/agent/info/page/list', + method: 'get', + params: query + }) +} +export function listagent(query) { + return request({ + url: '/agent/info/list', + method: 'get', + params: query + }) +} +// 查询货代公司详细 +export function getInfo(id) { + return request({ + url: '/agent/info/' + id, + method: 'get' + }) +} +// 查询货代公司列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/agent/info/list/exclude/' + deptId, + method: 'get' + }) +} +// 新增货代公司 +export function addInfo(data) { + return request({ + url: '/agent/info/add', + method: 'post', + data: data + }) +} + +// 修改货代公司 +export function updateInfo(data) { + return request({ + url: '/agent/info/edit', + method: 'post', + data: data + }) +} + +// 删除货代公司 +export function delInfo(data) { + return request({ + url: '/agent/info/delete', + method: 'post', + data: data + }) +} + +// 启停货代公司 +export function changeStatus(data) { + return request({ + url: '/agent/info/edit/status', + method: 'post', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/logininfor.js b/agile-portal/agile-portal-ui/src/api/ccicsh/logininfor.js new file mode 100644 index 00000000..1f9bb880 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/logininfor.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/business/loginLog/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/business/loginLog/' + infoId, + method: 'delete' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/business/loginLog/removeAll', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/operlog.js b/agile-portal/agile-portal-ui/src/api/ccicsh/operlog.js new file mode 100644 index 00000000..55c50b9c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/business/oprLog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/business/oprLog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/business/oprLog/removeAll', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/ccicsh/user.js b/agile-portal/agile-portal-ui/src/api/ccicsh/user.js new file mode 100644 index 00000000..ac433953 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/ccicsh/user.js @@ -0,0 +1,61 @@ +import request from '@/utils/request' + +// 查询预审平台用户信息列表 +export function listUser(query) { + return request({ + url: '/pq/user/page/list', + method: 'get', + params: query + }) +} + +// 查询预审平台用户信息详细 +export function getUser(id) { + return request({ + url: '/pq/user/' + id, + method: 'get' + }) +} + +// 新增预审平台用户信息 +export function addUser(data) { + return request({ + url: '/pq/user/add', + method: 'post', + data: data + }) +} + +// 修改预审平台用户信息 +export function updateUser(data) { + return request({ + url: '/pq/user/edit', + method: 'post', + data: data + }) +} + +// 删除预审平台用户信息 +export function delUser(data) { + return request({ + url: '/pq/user/delete', + method: 'post', + data: data + }) +} +// 重置密码 +export function resetPwd(data) { + return request({ + url: '/pq/user/resetPwd', + method: 'post', + data: data + }) +} +// 启停 +export function changeStatus(data) { + return request({ + url: '/pq/user/editStatus', + method: 'post', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/login.js b/agile-portal/agile-portal-ui/src/api/login.js new file mode 100644 index 00000000..6ba536e1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/login.js @@ -0,0 +1,61 @@ +import request from '@/utils/request' +import {Decrypt, Encrypt} from '@/utils/secret' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + // data.password = Encrypt(password) + return request({ + url: '/login', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 注册方法 +export function register(data) { + return request({ + url: '/register', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/apilog.js b/agile-portal/agile-portal-ui/src/api/market/apilog.js new file mode 100644 index 00000000..16d3d3c5 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/apilog.js @@ -0,0 +1,30 @@ +import request from '@/utils/request' + +export function pageApiLog(data) { + return request({ + url: '/api/apiLogs/page', + method: 'get', + params: data + }) +} + +export function getApiLog(id) { + return request({ + url: '/api/apiLogs/' + id, + method: 'get' + }) +} + +export function delApiLog(id) { + return request({ + url: '/api/apiLogs/' + id, + method: 'delete' + }) +} + +export function delApiLogs(ids) { + return request({ + url: '/api/apiLogs/batch/' + ids, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/apimapping.js b/agile-portal/agile-portal-ui/src/api/market/apimapping.js new file mode 100644 index 00000000..34d3c93e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/apimapping.js @@ -0,0 +1,19 @@ +import request from '@/utils/request' + +export function getApiCall(url, header, data) { + return request({ + url: '/' + url, + method: 'get', + headers: header, + params: data + }) +} + +export function postApiCall(url, header, data) { + return request({ + url: '/' + url, + method: 'post', + headers: header, + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/apimask.js b/agile-portal/agile-portal-ui/src/api/market/apimask.js new file mode 100644 index 00000000..dca63f28 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/apimask.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function pageApiMask (data) { + return request({ + url: '/market/apiMasks/page', + method: 'get', + params: data + }) +} + +export function getApiMask (id) { + return request({ + url: '/market/apiMasks/' + id, + method: 'get' + }) +} + +export function delApiMask (id) { + return request({ + url: '/market/apiMasks/' + id, + method: 'delete' + }) +} + +export function delApiMasks (ids) { + return request({ + url: '/market/apiMasks/batch/' + ids, + method: 'delete' + }) +} + +export function addApiMask (data) { + return request({ + url: '/market/apiMasks', + method: 'post', + data: data + }) +} + +export function updateApiMask (data) { + return request({ + url: '/market/apiMasks/' + data.id, + method: 'put', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/dataapi.js b/agile-portal/agile-portal-ui/src/api/market/dataapi.js new file mode 100644 index 00000000..c277310f --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/dataapi.js @@ -0,0 +1,98 @@ +import request from '@/utils/request' + +export function listDataApi(data) { + return request({ + url: '/market/dataApis/list', + method: 'get', + params: data + }) +} + +export function pageDataApi(data) { + return request({ + url: '/market/dataApis/page', + method: 'get', + params: data + }) +} + +export function getDataApi(id) { + return request({ + url: '/market/dataApis/' + id, + method: 'get' + }) +} + +export function delDataApi(id) { + return request({ + url: '/market/dataApis/' + id, + method: 'delete' + }) +} + +export function delDataApis(ids) { + return request({ + url: '/market/dataApis/batch/' + ids, + method: 'delete' + }) +} + +export function addDataApi(data) { + return request({ + url: '/market/dataApis', + method: 'post', + data: data + }) +} + +export function updateDataApi(data) { + return request({ + url: '/market/dataApis/' + data.id, + method: 'put', + data: data + }) +} + +export function sqlParse(data) { + return request({ + url: '/market/dataApis/sql/parse', + method: 'post', + data: data + }) +} + +export function copyDataApi(id) { + return request({ + url: '/market/dataApis/' + id + '/copy', + method: 'post' + }) +} + +export function releaseDataApi(id) { + return request({ + url: '/market/dataApis/' + id + '/release', + method: 'post' + }) +} + +export function cancelDataApi(id) { + return request({ + url: '/market/dataApis/' + id + '/cancel', + method: 'post' + }) +} + +export function word(id) { + return request({ + url: '/market/dataApis/word/' + id, + method: 'post', + responseType: 'blob' + }) +} + +export function getDataApiDetail(id) { + return request({ + url: '/market/dataApis/detail/' + id, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/dataservice.js b/agile-portal/agile-portal-ui/src/api/market/dataservice.js new file mode 100644 index 00000000..dba4c5dc --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/dataservice.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +export function pageDataService(data) { + return request({ + url: '/service/services/page', + method: 'get', + params: data + }) +} + +export function getDataService(id) { + return request({ + url: `/service/services/${id}`, + method: 'get' + }) +} + +export function delDataService(id) { + return request({ + url: `/service/services/${id}`, + method: 'delete' + }) +} + +export function delDataServices(ids) { + return request({ + url: `/service/services/batch/${ids}`, + method: 'delete' + }) +} + +export function addDataService(data) { + return request({ + url: '/service/services', + method: 'post', + data: data + }) +} + +export function updateDataService(data) { + return request({ + url: `/service/services/${data.id}`, + method: 'put', + data: data + }) +} + +export function getDataServiceDetail(id) { + return request({ + url: `/service/services/detail/${id}`, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/market/dataservicelog.js b/agile-portal/agile-portal-ui/src/api/market/dataservicelog.js new file mode 100644 index 00000000..dec0da76 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/market/dataservicelog.js @@ -0,0 +1,30 @@ +import request from '@/utils/request' + +export function pageDataServiceLog(data) { + return request({ + url: '/service/serviceLogs/page', + method: 'get', + params: data + }) +} + +export function getDataServiceLog(id) { + return request({ + url: '/service/serviceLogs/' + id, + method: 'get' + }) +} + +export function delDataServiceLog(id) { + return request({ + url: '/service/serviceLogs/' + id, + method: 'delete' + }) +} + +export function delDataServiceLogs(ids) { + return request({ + url: '/service/serviceLogs/batch/' + ids, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/menu.js b/agile-portal/agile-portal-ui/src/api/menu.js new file mode 100644 index 00000000..faef101c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取路由 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/metadata/changerecord.js b/agile-portal/agile-portal-ui/src/api/metadata/changerecord.js new file mode 100644 index 00000000..cbed773c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/changerecord.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +export function pageChangeRecord (data) { + return request({ + url: '/metadata/changeRecords/page', + method: 'get', + params: data + }) +} + +export function getChangeRecord (id) { + return request({ + url: '/metadata/changeRecords/' + id, + method: 'get' + }) +} + +export function delChangeRecord (id) { + return request({ + url: '/metadata/changeRecords/' + id, + method: 'delete' + }) +} + +export function delChangeRecords (ids) { + return request({ + url: '/metadata/changeRecords/batch/' + ids, + method: 'delete' + }) +} + +export function addChangeRecord (data) { + return request({ + url: '/metadata/changeRecords', + method: 'post', + data: data + }) +} + +export function updateChangeRecord (data) { + return request({ + url: '/metadata/changeRecords/' + data.id, + method: 'put', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/metadata/dataauthorize.js b/agile-portal/agile-portal-ui/src/api/metadata/dataauthorize.js new file mode 100644 index 00000000..7beba8c3 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/dataauthorize.js @@ -0,0 +1,24 @@ +import request from '@/utils/request' + +// 刷新参数缓存 +export function refreshAuthorize() { + return request({ + url: '/metadata/authorizes/refresh', + method: 'get' + }) +} + +export function getAuthorizedMetadata(id) { + return request({ + url: '/metadata/authorizes/getAuthorizedMetadata/' + id, + method: 'get' + }) +} + +export function metadataAuthorize(data) { + return request({ + url: '/metadata/authorizes/metadata', + method: 'post', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/metadata/datacolumn.js b/agile-portal/agile-portal-ui/src/api/metadata/datacolumn.js new file mode 100644 index 00000000..7ced1028 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/datacolumn.js @@ -0,0 +1,62 @@ +import request from '@/utils/request' + +export function listDataColumn(data) { + return request({ + url: '/metadata/columns/list', + method: 'get', + params: data + }) +} + +export function pageDataColumn(data) { + return request({ + url: '/metadata/columns/page', + method: 'get', + params: data + }) +} + +export function getDataColumn(id) { + return request({ + url: '/metadata/columns/' + id, + method: 'get' + }) +} + +export function delDataColumn(id) { + return request({ + url: '/metadata/columns/' + id, + method: 'delete' + }) +} + +export function delDataColumns(ids) { + return request({ + url: '/metadata/columns/batch/' + ids, + method: 'delete' + }) +} + +export function addDataColumn(data) { + return request({ + url: '/metadata/columns', + method: 'post', + data: data + }) +} + +export function updateDataColumn(data) { + return request({ + url: '/metadata/columns/' + data.id, + method: 'put', + data: data + }) +} + +export function getDataMetadataTree(level, data) { + return request({ + url: '/metadata/columns/tree/' + level, + method: 'get', + params: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/metadata/datasource.js b/agile-portal/agile-portal-ui/src/api/metadata/datasource.js new file mode 100644 index 00000000..5ecb5618 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/datasource.js @@ -0,0 +1,107 @@ +import request from '@/utils/request' + +// 刷新参数缓存 +export function refreshMetadata () { + return request({ + url: '/metadata/sources/refresh', + method: 'get' + }) +} + +export function listDataSource (data) { + return request({ + url: '/metadata/sources/list', + method: 'get', + params: data + }) +} + +export function pageDataSource (data) { + return request({ + url: '/metadata/sources/page', + method: 'get', + params: data + }) +} + +export function getDataSource (id) { + return request({ + url: '/metadata/sources/' + id, + method: 'get' + }) +} + +export function delDataSource (id) { + return request({ + url: '/metadata/sources/' + id, + method: 'delete' + }) +} + +export function delDataSources (ids) { + return request({ + url: '/metadata/sources/batch/' + ids, + method: 'delete' + }) +} + +export function addDataSource (data) { + return request({ + url: '/metadata/sources', + method: 'post', + data: data + }) +} + +export function updateDataSource (data) { + return request({ + url: '/metadata/sources/' + data.id, + method: 'put', + data: data + }) +} + +export function checkConnection (data) { + return request({ + url: '/metadata/sources/checkConnection', + method: 'post', + data: data + }) +} + +export function queryByPage (data) { + return request({ + url: '/metadata/sources/queryByPage', + method: 'post', + data: data + }) +} + +export function getDbTables (id) { + return request({ + url: '/metadata/sources/' + id + '/tables', + method: 'get' + }) +} + +export function getDbTableColumns (id, tableName) { + return request({ + url: '/metadata/sources/' + id + '/' + tableName + '/columns', + method: 'get' + }) +} + +export function sync (id) { + return request({ + url: '/metadata/sources/sync/' + id, + method: 'post' + }) +} + +export function word (id) { + return request({ + url: '/metadata/sources/word/' + id, + method: 'post', + responseType: 'blob' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/metadata/datatable.js b/agile-portal/agile-portal-ui/src/api/metadata/datatable.js new file mode 100644 index 00000000..a6e1ecf5 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/datatable.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +export function listDataTable (data) { + return request({ + url: '/metadata/tables/list', + method: 'get', + params: data + }) +} + +export function pageDataTable (data) { + return request({ + url: '/metadata/tables/page', + method: 'get', + params: data + }) +} + +export function getDataTable (id) { + return request({ + url: '/metadata/tables/' + id, + method: 'get' + }) +} + +export function delDataTable (id) { + return request({ + url: '/metadata/tables/' + id, + method: 'delete' + }) +} + +export function delDataTables (ids) { + return request({ + url: '/metadata/tables/batch/' + ids, + method: 'delete' + }) +} + +export function addDataTable (data) { + return request({ + url: '/metadata/tables', + method: 'post', + data: data + }) +} + +export function updateDataTable (data) { + return request({ + url: '/metadata/tables/' + data.id, + method: 'put', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/metadata/sqlconsole.js b/agile-portal/agile-portal-ui/src/api/metadata/sqlconsole.js new file mode 100644 index 00000000..c33ae631 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/metadata/sqlconsole.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +export function runSql (data) { + return request({ + url: '/console/sql/run', + method: 'post', + data: data + + }) +} + +export function stopSql (data) { + return request({ + url: '/console/sql/stop', + method: 'post', + data: data + }) +} + +export function exportSql (data) { + return request({ + url: '/console/sql/export', + method: 'post', + data: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/cache.js b/agile-portal/agile-portal-ui/src/api/monitor/cache.js new file mode 100644 index 00000000..72c5f6a3 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/cache.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 查询缓存内容 +export function getCacheValue(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheKey) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }) +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/job.js b/agile-portal/agile-portal-ui/src/api/monitor/job.js new file mode 100644 index 00000000..38155693 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/job.js @@ -0,0 +1,71 @@ +import request from '@/utils/request' + +// 查询定时任务调度列表 +export function listJob(query) { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query + }) +} + +// 查询定时任务调度详细 +export function getJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'get' + }) +} + +// 新增定时任务调度 +export function addJob(data) { + return request({ + url: '/monitor/job', + method: 'post', + data: data + }) +} + +// 修改定时任务调度 +export function updateJob(data) { + return request({ + url: '/monitor/job', + method: 'put', + data: data + }) +} + +// 删除定时任务调度 +export function delJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'delete' + }) +} + +// 任务状态修改 +export function changeJobStatus(jobId, status) { + const data = { + jobId, + status + } + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data: data + }) +} + + +// 定时任务立即执行一次 +export function runJob(jobId, jobGroup) { + const data = { + jobId, + jobGroup + } + return request({ + url: '/monitor/job/run', + method: 'put', + data: data + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/monitor/jobLog.js b/agile-portal/agile-portal-ui/src/api/monitor/jobLog.js new file mode 100644 index 00000000..6e0be616 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/jobLog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询调度日志列表 +export function listJobLog(query) { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 删除调度日志 +export function delJobLog(jobLogId) { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 清空调度日志 +export function cleanJobLog() { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/logininfor.js b/agile-portal/agile-portal-ui/src/api/monitor/logininfor.js new file mode 100644 index 00000000..26a46eb5 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/logininfor.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/online.js b/agile-portal/agile-portal-ui/src/api/monitor/online.js new file mode 100644 index 00000000..bd221378 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询在线用户列表 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/operlog.js b/agile-portal/agile-portal-ui/src/api/monitor/operlog.js new file mode 100644 index 00000000..a04bca84 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/monitor/server.js b/agile-portal/agile-portal-ui/src/api/monitor/server.js new file mode 100644 index 00000000..e1f9ca21 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取服务信息 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/config.js b/agile-portal/agile-portal-ui/src/api/system/config.js new file mode 100644 index 00000000..a404d825 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/config.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询参数列表 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 删除参数配置 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/system/dept.js b/agile-portal/agile-portal-ui/src/api/system/dept.js new file mode 100644 index 00000000..2804676f --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/dept.js @@ -0,0 +1,68 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 查询部门下拉树结构 +export function treeselect() { + return request({ + url: '/system/dept/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询部门树结构 +export function roleDeptTreeselect(roleId) { + return request({ + url: '/system/dept/roleDeptTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/dict/data.js b/agile-portal/agile-portal-ui/src/api/system/dict/data.js new file mode 100644 index 00000000..6c9eb79b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/system/dict/type.js b/agile-portal/agile-portal-ui/src/api/system/dict/type.js new file mode 100644 index 00000000..a7a6e01f --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/dict/type.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/menu.js b/agile-portal/agile-portal-ui/src/api/system/menu.js new file mode 100644 index 00000000..f6415c65 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/notice.js b/agile-portal/agile-portal-ui/src/api/system/notice.js new file mode 100644 index 00000000..c274ea5b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/post.js b/agile-portal/agile-portal-ui/src/api/system/post.js new file mode 100644 index 00000000..1a8e9ca0 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/system/role.js b/agile-portal/agile-portal-ui/src/api/system/role.js new file mode 100644 index 00000000..4b455e13 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/role.js @@ -0,0 +1,111 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 取消用户授权角色 +export function authUserCancel(data) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 授权用户选择 +export function authUserSelectAll(data) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/api/system/user.js b/agile-portal/agile-portal-ui/src/api/system/user.js new file mode 100644 index 00000000..4fd752b4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/system/user.js @@ -0,0 +1,127 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/ruoyi"; + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }) +} + +// 查询授权角色 +export function getAuthRole(userId) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 保存授权角色 +export function updateAuthRole(data) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} diff --git a/agile-portal/agile-portal-ui/src/api/tool/gen.js b/agile-portal/agile-portal-ui/src/api/tool/gen.js new file mode 100644 index 00000000..45069278 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/api/tool/gen.js @@ -0,0 +1,76 @@ +import request from '@/utils/request' + +// 查询生成表数据 +export function listTable(query) { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} +// 查询db数据库列表 +export function listDbTable(query) { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data) { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId) { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName) { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName) { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/agile-portal/agile-portal-ui/src/assets/401_images/401.gif b/agile-portal/agile-portal-ui/src/assets/401_images/401.gif new file mode 100644 index 00000000..cd6e0d94 Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/401_images/401.gif differ diff --git a/agile-portal/agile-portal-ui/src/assets/404_images/404.png b/agile-portal/agile-portal-ui/src/assets/404_images/404.png new file mode 100644 index 00000000..3d8e2305 Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/404_images/404.png differ diff --git a/agile-portal/agile-portal-ui/src/assets/404_images/404_cloud.png b/agile-portal/agile-portal-ui/src/assets/404_images/404_cloud.png new file mode 100644 index 00000000..c6281d09 Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/404_images/404_cloud.png differ diff --git a/agile-portal/agile-portal-ui/src/assets/icons/index.js b/agile-portal/agile-portal-ui/src/assets/icons/index.js new file mode 100644 index 00000000..2c6b309c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/404.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/404.svg new file mode 100644 index 00000000..6df50190 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/bug.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/bug.svg new file mode 100644 index 00000000..05a150dc --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/build.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/build.svg new file mode 100644 index 00000000..97c46886 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/button.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/button.svg new file mode 100644 index 00000000..904fddc8 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/cascader.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/cascader.svg new file mode 100644 index 00000000..e256024f --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/chart.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/chart.svg new file mode 100644 index 00000000..27728fb0 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/checkbox.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/checkbox.svg new file mode 100644 index 00000000..013fd3a2 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/clipboard.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/clipboard.svg new file mode 100644 index 00000000..90923ff6 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/code.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/code.svg new file mode 100644 index 00000000..ed4d23cf --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/color.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/color.svg new file mode 100644 index 00000000..44a81aab --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/component.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/component.svg new file mode 100644 index 00000000..29c34580 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/dashboard.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/dashboard.svg new file mode 100644 index 00000000..5317d370 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/date-range.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/date-range.svg new file mode 100644 index 00000000..fda571e7 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/date.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/date.svg new file mode 100644 index 00000000..52dc73ee --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/dict.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/dict.svg new file mode 100644 index 00000000..48493773 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/documentation.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/documentation.svg new file mode 100644 index 00000000..70431228 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/download.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/download.svg new file mode 100644 index 00000000..c8969513 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/drag.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/drag.svg new file mode 100644 index 00000000..4185d3ce --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/druid.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/druid.svg new file mode 100644 index 00000000..a2b4b4ed --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/edit.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/edit.svg new file mode 100644 index 00000000..d26101f2 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/education.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/education.svg new file mode 100644 index 00000000..7bfb01d1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/email.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/email.svg new file mode 100644 index 00000000..74d25e21 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/example.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/example.svg new file mode 100644 index 00000000..46f42b53 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/excel.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/excel.svg new file mode 100644 index 00000000..74d97b80 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/exit-fullscreen.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 00000000..485c128b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/eye-open.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/eye-open.svg new file mode 100644 index 00000000..88dcc98e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/eye.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/eye.svg new file mode 100644 index 00000000..16ed2d87 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/form.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/form.svg new file mode 100644 index 00000000..dcbaa185 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/fullscreen.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 00000000..0e86b6fa --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/github.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/github.svg new file mode 100644 index 00000000..db0a0d43 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/guide.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/guide.svg new file mode 100644 index 00000000..b2710017 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/icon.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/icon.svg new file mode 100644 index 00000000..82be8eee --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/input.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/input.svg new file mode 100644 index 00000000..ab91381e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/international.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/international.svg new file mode 100644 index 00000000..e9b56eee --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/job.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/job.svg new file mode 100644 index 00000000..2a93a251 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/language.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/language.svg new file mode 100644 index 00000000..0082b577 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/link.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/link.svg new file mode 100644 index 00000000..48197ba4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/list.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/list.svg new file mode 100644 index 00000000..20259edd --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/lock.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/lock.svg new file mode 100644 index 00000000..74fee543 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/log.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/log.svg new file mode 100644 index 00000000..d879d33b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/logininfor.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/logininfor.svg new file mode 100644 index 00000000..267f8447 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/message.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/message.svg new file mode 100644 index 00000000..14ca8172 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/money.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/money.svg new file mode 100644 index 00000000..c1580de1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/monitor.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 00000000..bc308cb0 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/nested.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/nested.svg new file mode 100644 index 00000000..06713a86 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/number.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/number.svg new file mode 100644 index 00000000..ad5ce9af --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/online.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/online.svg new file mode 100644 index 00000000..330a2029 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/password.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/password.svg new file mode 100644 index 00000000..6c64defe --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/pdf.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/pdf.svg new file mode 100644 index 00000000..957aa0cc --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/people.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/people.svg new file mode 100644 index 00000000..2bd54aeb --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/peoples.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/peoples.svg new file mode 100644 index 00000000..aab852e5 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/phone.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/phone.svg new file mode 100644 index 00000000..ab8e8c4e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/post.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/post.svg new file mode 100644 index 00000000..2922c613 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/qq.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/qq.svg new file mode 100644 index 00000000..ee13d4ec --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/question.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/question.svg new file mode 100644 index 00000000..cf75bd4b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/radio.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/radio.svg new file mode 100644 index 00000000..0cde3452 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/rate.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/rate.svg new file mode 100644 index 00000000..aa3b14d7 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/redis-list.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/redis-list.svg new file mode 100644 index 00000000..98a15b2a --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/redis.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/redis.svg new file mode 100644 index 00000000..2f1d62df --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/row.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/row.svg new file mode 100644 index 00000000..07809922 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/search.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/search.svg new file mode 100644 index 00000000..84233dda --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/select.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/select.svg new file mode 100644 index 00000000..d6283828 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/server.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/server.svg new file mode 100644 index 00000000..ca37b001 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/shopping.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/shopping.svg new file mode 100644 index 00000000..87513e7c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/size.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/size.svg new file mode 100644 index 00000000..ddb25b8d --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/skill.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/skill.svg new file mode 100644 index 00000000..a3b73121 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/slider.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/slider.svg new file mode 100644 index 00000000..fbe4f39f --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/star.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/star.svg new file mode 100644 index 00000000..6cf86e66 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/swagger.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/swagger.svg new file mode 100644 index 00000000..05d4e7bc --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/switch.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/switch.svg new file mode 100644 index 00000000..0ba61e38 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/system.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/system.svg new file mode 100644 index 00000000..dba28cf6 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/tab.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/tab.svg new file mode 100644 index 00000000..b4b48e48 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/table.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/table.svg new file mode 100644 index 00000000..0e3dc9de --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/textarea.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/textarea.svg new file mode 100644 index 00000000..2709f292 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/theme.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/theme.svg new file mode 100644 index 00000000..5982a2f7 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/time-range.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/time-range.svg new file mode 100644 index 00000000..13c1202b --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/time.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/time.svg new file mode 100644 index 00000000..b376e32a --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/tool.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/tool.svg new file mode 100644 index 00000000..c813067e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/tree-table.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/tree-table.svg new file mode 100644 index 00000000..8aafdb82 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/tree.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/tree.svg new file mode 100644 index 00000000..dd4b7dd2 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/upload.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/upload.svg new file mode 100644 index 00000000..bae49c0a --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/user.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/user.svg new file mode 100644 index 00000000..0ba0716a --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/validCode.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/validCode.svg new file mode 100644 index 00000000..cfb10214 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/wechat.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/wechat.svg new file mode 100644 index 00000000..c586e551 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svg/zip.svg b/agile-portal/agile-portal-ui/src/assets/icons/svg/zip.svg new file mode 100644 index 00000000..f806fc48 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/icons/svgo.yml b/agile-portal/agile-portal-ui/src/assets/icons/svgo.yml new file mode 100644 index 00000000..d11906ae --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/agile-portal/agile-portal-ui/src/assets/images/dark.svg b/agile-portal/agile-portal-ui/src/assets/images/dark.svg new file mode 100644 index 00000000..f646bd7e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/images/light.svg b/agile-portal/agile-portal-ui/src/assets/images/light.svg new file mode 100644 index 00000000..ab7cc088 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/images/login-background.jpg b/agile-portal/agile-portal-ui/src/assets/images/login-background.jpg new file mode 100644 index 00000000..8a89eb82 Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/images/login-background.jpg differ diff --git a/agile-portal/agile-portal-ui/src/assets/images/profile.jpg b/agile-portal/agile-portal-ui/src/assets/images/profile.jpg new file mode 100644 index 00000000..b3a940b2 Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/images/profile.jpg differ diff --git a/agile-portal/agile-portal-ui/src/assets/logo/logo.png b/agile-portal/agile-portal-ui/src/assets/logo/logo.png new file mode 100644 index 00000000..4a71110a Binary files /dev/null and b/agile-portal/agile-portal-ui/src/assets/logo/logo.png differ diff --git a/agile-portal/agile-portal-ui/src/assets/styles/base.scss b/agile-portal/agile-portal-ui/src/assets/styles/base.scss new file mode 100644 index 00000000..69a9a2ce --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/base.scss @@ -0,0 +1,355 @@ + /** + * 通用css样式布局处理 + */ + + /** 基础通用 **/ + ul, + dl, + dt, + dd { + margin: 0; + padding: 0; + list-style-type: none; + + } + + .h1, + .h2, + .h3, + .h4, + .h5, + .h6, + h1, + h2, + h3, + h4, + h5, + h6 { + margin: 0; + padding: 0; + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; + } + + .el-message-box__status+.el-message-box__message { + word-break: break-word; + } + + .el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; + } + + .el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; + } + + .el-table { + + .el-table__header-wrapper, + .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; + } + } + + .el-table__body-wrapper { + .el-button [class*="el-icon-"]+span { + margin-left: 1px; + } + } + } + + /** 表单布局 **/ + .form-header { + font-size: 15px; + color: #6379bb; + border-bottom: 1px solid #ddd; + margin: 8px 10px 25px 10px; + padding-bottom: 5px + } + + /** 表格布局 **/ + .pagination-container { + position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; + } + + /* tree border */ + .tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #FFFFFF none; + border-radius: 4px; + } + + .pagination-container .el-pagination { + right: 0; + position: absolute; + } + + @media (max-width : 768px) { + .pagination-container .el-pagination>.el-pagination__jump { + display: none !important; + } + + .pagination-container .el-pagination>.el-pagination__sizes { + display: none !important; + } + } + + .el-table .fixed-width .el-button--mini { + padding-left: 0; + padding-right: 0; + width: inherit; + } + + /** 表格更多操作下拉样式 */ + .el-table .el-dropdown-link, + .el-table .el-dropdown-selfdefine { + cursor: pointer; + margin-left: 5px; + } + + .el-table .el-dropdown, + .el-icon-arrow-down { + font-size: 12px; + } + + .el-tree-node__content>.el-checkbox { + margin-right: 8px; + } + + .list-group-striped>.list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; + } + + .list-group { + padding-left: 0px; + list-style: none; + } + + .list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; + } + + .pull-right { + float: right !important; + } + + .el-card__header { + padding: 14px 15px 7px; + min-height: 40px; + } + + .el-card__body { + padding: 15px 20px 20px 20px; + } + + .card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; + } + + /* button color */ + .el-button--cyan.is-active, + .el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; + } + + .el-button--cyan:focus, + .el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; + } + + .el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; + } + + /* text color */ + .text-navy { + color: #1ab394; + } + + .text-primary { + color: inherit; + } + + .text-success { + color: #1c84c6; + } + + .text-info { + color: #23c6c8; + } + + .text-warning { + color: #f8ac59; + } + + .text-danger { + color: #ed5565; + } + + .text-muted { + color: #888888; + } + + /* image */ + .img-circle { + border-radius: 50%; + } + + .img-lg { + width: 80px; + height: 80px; + } + + .avatar-upload-preview { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; + } + + /* 拖拽列样式 */ + .sortable-ghost { + opacity: .8; + color: #fff !important; + background: #42b983 !important; + } + + .top-right-btn { + position: relative; + float: right; + } + + .header-title { + font-size: 18px; + font-weight: 600; + color: #001330; + } + + .el-form-item__label { + color: #001330; + font-weight: normal; + } + + .el-table th.el-table__cell.is-leaf, + .el-table td.el-table__cell { + border-bottom: 1px solid #F0F0F0; + } + + .el-table .el-table__header-wrapper th, + .el-table .el-table__fixed-header-wrapper th { + background-color: #f8f8f9 !important; + color: #2A2A2B; + font-weight: 500; + font-size: 14px; + } + + .el-table--medium .el-table__cell { + padding: 16px 0 !important; + } + + .header-filter { + padding: 10px 0 0 0; + } + + .list-wrapper { + padding-bottom: 20px; + + .table-index { + .el-button--small { + padding: 0 0; + } + } + + } + + + .card-wrapper { + padding-bottom: 40px; + + .btn-list { + padding-bottom: 20px; + } + } + + .el-switch__label { + font-weight: normal; + } + + .el-button--medium { + border-radius: 2px; + } + + .el-input__inner { + border-radius: 2px; + } + + .el-button--mini { + border-radius: 2px; + } + + .el-button--small { + font-size: 14px; + padding: 8px 15px; + } + + .el-breadcrumb__inner a, + .el-breadcrumb__inner.is-link { + color: #fff !important; + } + + .list-dia { + + .el-input--mini, + .el-button--mini { + font-size: 12px; + font-weight: normal; + } + + .el-form-item--mini { + .el-form-item__label { + font-size: 12px; + } + } + + .el-table--mini { + .cell { + font-size: 12px; + } + } + } diff --git a/agile-portal/agile-portal-ui/src/assets/styles/btn.scss b/agile-portal/agile-portal-ui/src/assets/styles/btn.scss new file mode 100644 index 00000000..e6ba1a8e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/element-ui.scss b/agile-portal/agile-portal-ui/src/assets/styles/element-ui.scss new file mode 100644 index 00000000..363092a6 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/element-ui.scss @@ -0,0 +1,92 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/assets/styles/element-variables.scss b/agile-portal/agile-portal-ui/src/assets/styles/element-variables.scss new file mode 100644 index 00000000..1615ff28 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #ffba00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border: 1px solid #dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/index.scss b/agile-portal/agile-portal-ui/src/assets/styles/index.scss new file mode 100644 index 00000000..96095ef6 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/index.scss @@ -0,0 +1,191 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/mixin.scss b/agile-portal/agile-portal-ui/src/assets/styles/mixin.scss new file mode 100644 index 00000000..06fa0612 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/ruoyi.scss b/agile-portal/agile-portal-ui/src/assets/styles/ruoyi.scss new file mode 100644 index 00000000..34c9e84c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/ruoyi.scss @@ -0,0 +1,273 @@ + /** + * 通用css样式布局处理 + * Copyright (c) 2019 ruoyi + */ + + /** 基础通用 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; + } + } + .el-table__body-wrapper { + .el-button [class*="el-icon-"] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size:15px; + color:#6379bb; + border-bottom:1px solid #ddd; + margin:8px 10px 25px 10px; + padding-bottom:5px +} + +/** 表格布局 **/ +.pagination-container { + position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #FFFFFF none; + border-radius:4px; +} + +.pagination-container .el-pagination { + right: 0; + position: absolute; +} + +@media ( max-width : 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-table .fixed-width .el-button--mini { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link { + cursor: pointer; + color: #409EFF; + margin-left: 5px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: absolute; + top: 50%; + transform: translate(50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost{ + opacity: .8; + color: #fff!important; + background: #42b983!important; +} + +.top-right-btn { + position: relative; + float: right; +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/sidebar.scss b/agile-portal/agile-portal-ui/src/assets/styles/sidebar.scss new file mode 100644 index 00000000..ed308b8d --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/sidebar.scss @@ -0,0 +1,227 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + -webkit-transition: width .28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); + box-shadow: 2px 0 6px rgba(0,21,41,.35); + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, .el-submenu__title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-submenu__title { + color: $base-menu-color-active !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-submenu>.el-submenu__title, + & .theme-dark .el-submenu .el-menu-item { + background-color: $base-sub-menu-background !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/transition.scss b/agile-portal/agile-portal-ui/src/assets/styles/transition.scss new file mode 100644 index 00000000..4cb27cc8 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/agile-portal/agile-portal-ui/src/assets/styles/variables.scss b/agile-portal/agile-portal-ui/src/assets/styles/variables.scss new file mode 100644 index 00000000..34484d47 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/assets/styles/variables.scss @@ -0,0 +1,54 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// 默认菜单主题风格 +$base-menu-color:#bfcbd9; +$base-menu-color-active:#f4f4f5; +$base-menu-background:#304156; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#1f2d3d; +$base-sub-menu-hover:#001528; + +// 自定义暗色菜单风格 +/** +$base-menu-color:hsla(0,0%,100%,.65); +$base-menu-color-active:#fff; +$base-menu-background:#001529; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#000c17; +$base-sub-menu-hover:#001528; +*/ + +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color +} diff --git a/agile-portal/agile-portal-ui/src/components/Breadcrumb/index.vue b/agile-portal/agile-portal-ui/src/components/Breadcrumb/index.vue new file mode 100644 index 00000000..1696f547 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Breadcrumb/index.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/day.vue b/agile-portal/agile-portal-ui/src/components/Crontab/day.vue new file mode 100644 index 00000000..fe3eaf0c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/day.vue @@ -0,0 +1,161 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/hour.vue b/agile-portal/agile-portal-ui/src/components/Crontab/hour.vue new file mode 100644 index 00000000..4b1f1fcd --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/hour.vue @@ -0,0 +1,114 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/index.vue b/agile-portal/agile-portal-ui/src/components/Crontab/index.vue new file mode 100644 index 00000000..3963df28 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/index.vue @@ -0,0 +1,430 @@ + + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/min.vue b/agile-portal/agile-portal-ui/src/components/Crontab/min.vue new file mode 100644 index 00000000..43cab900 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/min.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/month.vue b/agile-portal/agile-portal-ui/src/components/Crontab/month.vue new file mode 100644 index 00000000..fd0ac384 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/month.vue @@ -0,0 +1,114 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/result.vue b/agile-portal/agile-portal-ui/src/components/Crontab/result.vue new file mode 100644 index 00000000..aea6e0e4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/result.vue @@ -0,0 +1,559 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/second.vue b/agile-portal/agile-portal-ui/src/components/Crontab/second.vue new file mode 100644 index 00000000..e7b77617 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/second.vue @@ -0,0 +1,117 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/week.vue b/agile-portal/agile-portal-ui/src/components/Crontab/week.vue new file mode 100644 index 00000000..1cec700e --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/week.vue @@ -0,0 +1,202 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/Crontab/year.vue b/agile-portal/agile-portal-ui/src/components/Crontab/year.vue new file mode 100644 index 00000000..5487a6c7 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Crontab/year.vue @@ -0,0 +1,131 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/DictData/index.js b/agile-portal/agile-portal-ui/src/components/DictData/index.js new file mode 100644 index 00000000..c2a0359c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/DictData/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import DataDict from '@/utils/dict' +import { getDicts as getDicts } from '@/api/system/dict/data' + +function install() { + Vue.use(DataDict, { + metas: { + '*': { + labelField: 'dictLabel', + valueField: 'dictValue', + request(dictMeta) { + return getDicts(dictMeta.type).then(res => res.data) + }, + }, + }, + }) +} + +export default { + install, +} \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/components/DictTag/index.vue b/agile-portal/agile-portal-ui/src/components/DictTag/index.vue new file mode 100644 index 00000000..4c196c48 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/DictTag/index.vue @@ -0,0 +1,52 @@ + + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/components/Editor/index.vue b/agile-portal/agile-portal-ui/src/components/Editor/index.vue new file mode 100644 index 00000000..6bb5a18d --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Editor/index.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/FileUpload/index.vue b/agile-portal/agile-portal-ui/src/components/FileUpload/index.vue new file mode 100644 index 00000000..aa2296b9 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/FileUpload/index.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/Hamburger/index.vue b/agile-portal/agile-portal-ui/src/components/Hamburger/index.vue new file mode 100644 index 00000000..368b0021 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/HeaderSearch/index.vue b/agile-portal/agile-portal-ui/src/components/HeaderSearch/index.vue new file mode 100644 index 00000000..c44eff56 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/HeaderSearch/index.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/IconSelect/index.vue b/agile-portal/agile-portal-ui/src/components/IconSelect/index.vue new file mode 100644 index 00000000..b0ec9fa1 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/IconSelect/index.vue @@ -0,0 +1,68 @@ + + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/IconSelect/requireIcons.js b/agile-portal/agile-portal-ui/src/components/IconSelect/requireIcons.js new file mode 100644 index 00000000..99e5c54c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/agile-portal/agile-portal-ui/src/components/ImagePreview/index.vue b/agile-portal/agile-portal-ui/src/components/ImagePreview/index.vue new file mode 100644 index 00000000..743d8d51 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/ImagePreview/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/ImageUpload/index.vue b/agile-portal/agile-portal-ui/src/components/ImageUpload/index.vue new file mode 100644 index 00000000..4068b672 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/ImageUpload/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/Pagination/index.vue b/agile-portal/agile-portal-ui/src/components/Pagination/index.vue new file mode 100644 index 00000000..9ec3e486 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Pagination/index.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/PanThumb/index.vue b/agile-portal/agile-portal-ui/src/components/PanThumb/index.vue new file mode 100644 index 00000000..1bcf4170 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/ParentView/index.vue b/agile-portal/agile-portal-ui/src/components/ParentView/index.vue new file mode 100644 index 00000000..7bf61489 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/agile-portal/agile-portal-ui/src/components/RightPanel/index.vue b/agile-portal/agile-portal-ui/src/components/RightPanel/index.vue new file mode 100644 index 00000000..fbf27eb4 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/RightPanel/index.vue @@ -0,0 +1,149 @@ + + + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/RightToolbar/index.vue b/agile-portal/agile-portal-ui/src/components/RightToolbar/index.vue new file mode 100644 index 00000000..f7663a3c --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/RightToolbar/index.vue @@ -0,0 +1,87 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/RuoYi/Doc/index.vue b/agile-portal/agile-portal-ui/src/components/RuoYi/Doc/index.vue new file mode 100644 index 00000000..75fa8641 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/RuoYi/Doc/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/components/RuoYi/Git/index.vue b/agile-portal/agile-portal-ui/src/components/RuoYi/Git/index.vue new file mode 100644 index 00000000..bdafbaef --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/RuoYi/Git/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/agile-portal/agile-portal-ui/src/components/Screenfull/index.vue b/agile-portal/agile-portal-ui/src/components/Screenfull/index.vue new file mode 100644 index 00000000..d4e539c2 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/Screenfull/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/agile-portal/agile-portal-ui/src/components/SizeSelect/index.vue b/agile-portal/agile-portal-ui/src/components/SizeSelect/index.vue new file mode 100644 index 00000000..069b5de9 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/SizeSelect/index.vue @@ -0,0 +1,56 @@ + + + diff --git a/agile-portal/agile-portal-ui/src/components/SqlEditor/index.vue b/agile-portal/agile-portal-ui/src/components/SqlEditor/index.vue new file mode 100644 index 00000000..e49ef652 --- /dev/null +++ b/agile-portal/agile-portal-ui/src/components/SqlEditor/index.vue @@ -0,0 +1,110 @@ +