xiatian 2 年 前
コミット
0e9b3e128f

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+*.class
+
+# Proguard folder generated by ide
+.project
+.classpath
+.settings
+target/
+.idea/
+*.iml
+
+# Log Files
+*.log
+*.class
+
+
+# Package Files #
+*.jar
+*.war
+*.ear
+logs/

+ 199 - 0
pom.xml

@@ -0,0 +1,199 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>yunkai-import</groupId>
+	<artifactId>yunkai-import</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<properties>
+		<java.version>1.8</java.version>
+		<spring-boot.version>2.1.13.RELEASE</spring-boot.version>
+		<druid.version>1.1.14</druid.version>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<examcloud.version>2019-SNAPSHOT</examcloud.version>
+	</properties>
+	<dependencies>
+		<!-- https://mvnrepository.com/artifact/com.ibm.icu/icu4j -->
+		<dependency>
+			<groupId>com.ibm.icu</groupId>
+			<artifactId>icu4j</artifactId>
+			<version>65.1</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+			<version>2.9.10</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>2.9.10</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>2.9.10.3</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-avro</artifactId>
+			<version>2.9.10</version>
+		</dependency>
+		<dependency>
+			<groupId>com.oracle</groupId>
+			<artifactId>ojdbc6</artifactId>
+			<version>11.2.0.3</version>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>8.0.17</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-1.2-api</artifactId>
+			<version>2.3</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi</artifactId>
+			<version>3.17</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-ooxml</artifactId>
+			<version>3.17</version>
+		</dependency>
+		<dependency>
+			<groupId>com.squareup.okhttp3</groupId>
+			<artifactId>okhttp</artifactId>
+			<version>3.11.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>23.0</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.5</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.gson</groupId>
+			<artifactId>gson</artifactId>
+			<version>2.8.5</version>
+		</dependency>
+		<dependency>
+			<groupId>net.freeutils</groupId>
+			<artifactId>jcharset</artifactId>
+			<version>2.0</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+			<version>4.5.10</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+			<version>2.1.13.RELEASE</version>
+		</dependency>
+	</dependencies>
+	<repositories>
+		<repository>
+			<id>aliyun-repos</id>
+			<url>https://maven.aliyun.com/repository/public</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+		<repository>
+			<id>atlassian</id>
+			<name>Central Repository</name>
+			<url>https://packages.atlassian.com/maven-3rdparty/</url>
+			<layout>default</layout>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<testFailureIgnore>true</testFailureIgnore>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+					<compilerArgument>-proc:none</compilerArgument>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<configuration>
+					<attach>true</attach>
+				</configuration>
+				<executions>
+					<execution>
+						<phase>compile</phase>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.6</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>lib/</classpathPrefix>
+							<mainClass>cn.com.qmth.export.Export</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-dependency-plugin</artifactId>
+				<version>2.10</version>
+				<executions>
+					<execution>
+						<id>copy-dependencies</id>
+						<phase>package</phase>
+						<goals>
+							<goal>copy-dependencies</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.directory}/lib</outputDirectory>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 35 - 0
src/main/java/cn/com/qmth/Application.java

@@ -0,0 +1,35 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-16 15:00:21.
+ * *************************************************
+ */
+
+package cn.com.qmth;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@EnableAsync
+@SpringBootApplication
+@ComponentScan(basePackages = {"cn.com.qmth"})
+@EntityScan(basePackages = {"cn.com.qmth"})
+public class Application {
+
+    static {
+        String runtimeLevel = System.getProperty("log.commonLevel");
+        if (null == runtimeLevel) {
+            System.setProperty("log.commonLevel", "INFO");
+        }
+        System.setProperty("hibernate.dialect.storage_engine", "innodb");
+    }
+
+    public static void main(String[] args) {
+        //SpringApplication.run(ApiApplication.class, args);
+    	SpringApplication.run(Application.class, args);
+    }
+
+}

+ 44 - 0
src/main/java/cn/com/qmth/RedisConfig.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-08-02 09:21:53.
+ * *************************************************
+ */
+
+package cn.com.qmth;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * @author: fengdesheng
+ * @since: 2018/8/2
+ */
+@Configuration
+public class RedisConfig {
+
+    @Bean
+    public RedisTemplate<String, Object> strRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        Jackson2JsonRedisSerializer<?> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        jacksonSerializer.setObjectMapper(objectMapper);
+        redisTemplate.setValueSerializer(jacksonSerializer);
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+}

+ 77 - 0
src/main/java/cn/com/qmth/SpringContextHolder.java

@@ -0,0 +1,77 @@
+package cn.com.qmth;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.ResolvableType;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring context holder.
+ *
+ * @author 
+ * @date 2018年7月10日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Component
+public class SpringContextHolder implements ApplicationContextAware {
+
+	private static ApplicationContext ctx = null;
+
+	@Override
+	public void setApplicationContext(ApplicationContext ctx) {
+		SpringContextHolder.ctx = ctx;
+	}
+
+	public static ApplicationContext getApplicationContext() {
+		return ctx;
+	}
+
+	public static Object getBean(String name) {
+		return ctx.getBean(name);
+	}
+
+	public static <T> T getBean(String name, Class<T> requiredType) {
+		return ctx.getBean(name, requiredType);
+	}
+
+	public static <T> T getBean(Class<T> requiredType) {
+		return ctx.getBean(requiredType);
+	}
+
+	public static Object getBean(String name, Object... args) {
+		return ctx.getBean(name, args);
+	}
+
+	public static <T> T getBean(Class<T> requiredType, Object... args) {
+		return ctx.getBean(requiredType, args);
+	}
+
+	public static boolean containsBean(String name) {
+		return ctx.containsBean(name);
+	}
+
+	public static boolean isSingleton(String name) {
+		return ctx.isSingleton(name);
+	}
+
+	public static boolean isPrototype(String name) {
+		return ctx.isPrototype(name);
+	}
+
+	public static boolean isTypeMatch(String name, ResolvableType typeToMatch) {
+		return ctx.isTypeMatch(name, typeToMatch);
+	}
+
+	public static boolean isTypeMatch(String name, Class<?> typeToMatch) {
+		return ctx.isTypeMatch(name, typeToMatch);
+	}
+
+	public static Class<?> getType(String name) {
+		return ctx.getType(name);
+	}
+
+	public static String[] getAliases(String name) {
+		return ctx.getAliases(name);
+	}
+
+}

+ 32 - 0
src/main/java/cn/com/qmth/importpaper/Course.java

@@ -0,0 +1,32 @@
+package cn.com.qmth.importpaper;
+
+public class Course {
+	private Long id;
+	private String code;
+	private String name;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+}

+ 94 - 0
src/main/java/cn/com/qmth/importpaper/DateUtil.java

@@ -0,0 +1,94 @@
+package cn.com.qmth.importpaper;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 日期工具
+ * 
+ * @author 
+ */
+public class DateUtil {
+
+	/**
+	 * patterns describing the date and time format
+	 *
+	 * @author 
+	 */
+	public interface DatePatterns {
+		public static final String DEFAULT = "yyyyMMddHHmmss";
+
+		public static final String YYYY = "yyyy";
+
+		public static final String YYYYMM = "yyyyMM";
+
+		public static final String YYYYMMDD = "yyyyMMdd";
+
+		public static final String YYYYMMDDHH = "yyyyMMddHH";
+
+		public static final String YYYYMMDDHHMM = "yyyyMMddHHmm";
+
+		public static final String ISO = "yyyy-MM-dd HH:mm:ss";
+
+		public static final String YYYY_MM = "yyyy-MM";
+
+		public static final String YYYY_MM_DD = "yyyy-MM-dd";
+
+		public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
+
+		public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
+	}
+
+	/**
+	 * get now date.
+	 * 
+	 * @param pattern
+	 * @return
+	 */
+	public static String getNow(String pattern) {
+		return format(new Date(), pattern);
+	}
+
+	/**
+	 * get now ISO date.
+	 * 
+	 * @return
+	 */
+	public static String getNowISO() {
+		return format(new Date(), DatePatterns.ISO);
+	}
+
+	/**
+	 * format date.
+	 * 
+	 * @param date
+	 * @param pattern
+	 * @return
+	 */
+	public static String format(Date date, String pattern) {
+		try {
+			SimpleDateFormat df = new SimpleDateFormat(pattern);
+			return df.format(date);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * parse date.
+	 * 
+	 * @param source
+	 * @param pattern
+	 * @return
+	 */
+	public static Date parse(String source, String pattern) {
+		SimpleDateFormat df = new SimpleDateFormat(pattern);
+		try {
+			return df.parse(source);
+		} catch (ParseException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}

+ 220 - 0
src/main/java/cn/com/qmth/importpaper/FileUtil.java

@@ -0,0 +1,220 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.importpaper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+
+public class FileUtil {
+    private static Logger log = Logger.getLogger(FileUtil.class);
+
+
+    /**
+     * 解压文件
+     *
+     * @param targetDir 解压目录
+     * @param zipFile   待解压的ZIP文件
+     */
+    public static List<File> unZip(File targetDir, File zipFile) {
+        if (targetDir == null) {
+            log.error("解压目录不能为空!");
+            return null;
+        }
+
+        if (zipFile == null) {
+            log.error("待解压的文件不能为空!");
+            return null;
+        }
+
+        if (!zipFile.exists()) {
+            log.error("待解压的文件不存在!" + zipFile.getAbsolutePath());
+            return null;
+        }
+
+        String zipName = zipFile.getName().toLowerCase();
+        if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
+            log.error("待解压的文件格式错误!");
+            return null;
+        }
+
+        if (!targetDir.exists()) {
+            targetDir.mkdir();
+        }
+
+        log.info("UnZip targetDir:" + targetDir.getAbsolutePath());
+        List<File> result = new LinkedList<>();
+
+        try (ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));) {
+
+            @SuppressWarnings("rawtypes")
+			Enumeration entries = zip.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+
+                //Linux中需要替换掉路径的反斜杠
+                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
+                log.info("UnZip:" + entryName);
+
+                String filePath = targetDir.getAbsolutePath() + entryName;
+                File target = new File(filePath);
+                if (entry.isDirectory()) {
+                    target.mkdirs();
+                } else {
+                    File dir = target.getParentFile();
+                    if (!dir.exists()) {
+                        dir.mkdirs();
+                    }
+
+                    try (OutputStream os = new FileOutputStream(target);
+                         InputStream is = zip.getInputStream(entry);) {
+                        IOUtils.copy(is, os);
+                        os.flush();
+                    } catch (IOException e) {
+                        log.error(e.getMessage(), e);
+                    }
+                    result.add(target);
+                }
+            }
+
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 文件压缩
+     *
+     * @param target  目录或文件
+     * @param zipFile 压缩后的ZIP文件
+     */
+    public static boolean doZip(File target, File zipFile) {
+        if (target == null || !target.exists()) {
+            log.error("目录或文件不能为空!");
+            return false;
+        }
+
+        if (zipFile == null) {
+            log.error("待压缩的文件不能为空!");
+            return false;
+        }
+
+        try (
+                OutputStream outStream = new FileOutputStream(zipFile);
+                ZipOutputStream zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));
+        ) {
+            if (!zipFile.exists()) {
+                boolean ok = zipFile.createNewFile();
+                if (!ok) {
+                    log.error("压缩的文件创建失败!");
+                    return false;
+                }
+            }
+
+            if (target.isDirectory()) {
+                File[] files = target.listFiles();
+                if (files.length == 0) {
+                    log.error("文件夹内未找到任何文件!");
+                    return false;
+                }
+
+                for (File file : files) {
+                    doZip(zipOutStream, file, null);
+                }
+            } else {
+                doZip(zipOutStream, target, null);
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return true;
+    }
+
+    private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
+        //log.info("Zip:" + parentDir);
+        if (parentDir == null) {
+            parentDir = "";
+        }
+
+        if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
+            parentDir += File.separator;
+        }
+
+        if (target.isDirectory()) {
+            File[] files = target.listFiles();
+            if (files.length > 0) {
+                for (File file : files) {
+                    doZip(zipOutStream, file, parentDir + target.getName());
+                }
+            } else {
+                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+                zipOutStream.closeEntry();
+            }
+        } else {
+            try (InputStream is = new FileInputStream(target);) {
+                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+                int len;
+                byte[] bytes = new byte[1024];
+                while ((len = is.read(bytes)) > 0) {
+                    zipOutStream.write(bytes, 0, len);
+                }
+            } catch (IOException e) {
+                log.error(e.getMessage(), e);
+            }
+            zipOutStream.closeEntry();
+        }
+    }
+    public static boolean doZip(List<File> source, File zipFile) {
+        if (source == null || source.size()==0) {
+            log.error("待压缩的文件不能为空!");
+            return false;
+        }
+
+        if (zipFile == null) {
+            log.error("压缩后的文件不能为空!");
+            return false;
+        }
+
+        try (
+                OutputStream outStream = new FileOutputStream(zipFile);
+                ZipOutputStream zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));
+        ) {
+            if (!zipFile.exists()) {
+                boolean ok = zipFile.createNewFile();
+                if (!ok) {
+                    log.error("压缩的文件创建失败!");
+                    return false;
+                }
+            }
+            for (File file : source) {
+                doZip(zipOutStream, file, null);
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return true;
+    }
+}

+ 58 - 0
src/main/java/cn/com/qmth/importpaper/FormFilePart.java

@@ -0,0 +1,58 @@
+package cn.com.qmth.importpaper;
+
+import java.io.File;
+
+/**
+ * 表单文件参数
+ *
+ * @author 
+ * @date 2019年5月9日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class FormFilePart {
+
+	private String paramName;
+
+	private String filename;
+
+	private File file;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param paramName
+	 * @param filename
+	 * @param file
+	 */
+	public FormFilePart(String paramName, String filename, File file) {
+		super();
+		this.paramName = paramName;
+		this.filename = filename;
+		this.file = file;
+	}
+
+	public String getParamName() {
+		return paramName;
+	}
+
+	public void setParamName(String paramName) {
+		this.paramName = paramName;
+	}
+
+	public String getFilename() {
+		return filename;
+	}
+
+	public void setFilename(String filename) {
+		this.filename = filename;
+	}
+
+	public File getFile() {
+		return file;
+	}
+
+	public void setFile(File file) {
+		this.file = file;
+	}
+
+}

+ 115 - 0
src/main/java/cn/com/qmth/importpaper/HttpMethod.java

@@ -0,0 +1,115 @@
+package cn.com.qmth.importpaper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * HTTP method
+ *
+ * @author 
+ * @date 2019年4月10日
+ * @Copyright (c) 2018-2020  [QQ:522080330] All Rights Reserved.
+ */
+public class HttpMethod {
+
+	public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS");
+
+	public static final HttpMethod GET = new HttpMethod("GET");
+
+	public static final HttpMethod HEAD = new HttpMethod("HEAD");
+
+	public static final HttpMethod POST = new HttpMethod("POST");
+
+	public static final HttpMethod PUT = new HttpMethod("PUT");
+
+	public static final HttpMethod PATCH = new HttpMethod("PATCH");
+
+	public static final HttpMethod DELETE = new HttpMethod("DELETE");
+
+	public static final HttpMethod TRACE = new HttpMethod("TRACE");
+
+	public static final HttpMethod CONNECT = new HttpMethod("CONNECT");
+
+	private static final Map<String, HttpMethod> METHOD_MAP = new HashMap<String, HttpMethod>();
+
+	static {
+		METHOD_MAP.put(OPTIONS.toString(), OPTIONS);
+		METHOD_MAP.put(GET.toString(), GET);
+		METHOD_MAP.put(HEAD.toString(), HEAD);
+		METHOD_MAP.put(POST.toString(), POST);
+		METHOD_MAP.put(PUT.toString(), PUT);
+		METHOD_MAP.put(PATCH.toString(), PATCH);
+		METHOD_MAP.put(DELETE.toString(), DELETE);
+		METHOD_MAP.put(TRACE.toString(), TRACE);
+		METHOD_MAP.put(CONNECT.toString(), CONNECT);
+	}
+
+	private final String name;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param name
+	 */
+	private HttpMethod(String name) {
+		if (name == null) {
+			throw new NullPointerException("name");
+		}
+
+		name = name.trim();
+		if (name.length() == 0) {
+			throw new IllegalArgumentException("empty name");
+		}
+
+		for (int i = 0; i < name.length(); i++) {
+			if (Character.isISOControl(name.charAt(i)) || Character.isWhitespace(name.charAt(i))) {
+				throw new IllegalArgumentException("invalid character in name");
+			}
+		}
+
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public static HttpMethod valueOf(String name) {
+		if (name == null) {
+			throw new NullPointerException("name");
+		}
+
+		name = name.trim();
+		if (name.length() == 0) {
+			throw new IllegalArgumentException("empty name");
+		}
+
+		HttpMethod result = METHOD_MAP.get(name);
+		if (result != null) {
+			return result;
+		} else {
+			throw new IllegalArgumentException("undefined name");
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		return getName().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (!(o instanceof HttpMethod)) {
+			return false;
+		}
+
+		HttpMethod that = (HttpMethod) o;
+		return getName().equals(that.getName());
+	}
+
+	@Override
+	public String toString() {
+		return getName();
+	}
+
+}

+ 163 - 0
src/main/java/cn/com/qmth/importpaper/ImportPaperByCourse.java

@@ -0,0 +1,163 @@
+package cn.com.qmth.importpaper;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import okhttp3.Response;
+
+public class ImportPaperByCourse {
+
+	private static Logger logger = LogManager.getLogger(ImportPaperByCourse.class);
+	
+	private static String paperSuff="(211)";
+
+	private static String sourceDir = "e:\\quesDir";
+//	private static String sourceDir = "e:\\courseDir";
+//	private static String sourceDir = "D:\\2020ddimport\\ques\\excelDir";
+
+	private static String sourceQuesDir = sourceDir + "\\quesDir\\";
+
+//	private final static String url = "https://ecs-test.ea100.com.cn/api/ecs_ques/importDdCollegePaper";
+	private final static String url = "https://www.exam-cloud.cn/api/ecs_ques/importDdCollegePaper";
+//	private final static String url = "https://org0.qmth.com.cn/api/ecs_ques/importDdCollegePaper";
+
+	private final static String rootOrgId = "1627";
+	
+//	private final static String rootOrgId = "0";
+	
+	private final static String key = "U_C_1627_545839";
+
+	private final static String token = "4aa4703852a544f4b1eb23c854419057";
+
+	public static void main(String[] args) {
+		logger.debug("**********************导入开始");
+		Date start = new Date();
+		int errnum = 0;
+		StringBuilder sb = new StringBuilder();
+		
+		int num = 0;
+		String fname = null;
+		File dir = new File(sourceDir);
+		for (File f : dir.listFiles()) {
+			fname = f.getName();
+			if (!f.isDirectory() && fname.endsWith(".xlsx")) {
+				num++;
+				Date n = new Date();
+				try {
+					importCoursePaper(f);
+					Date et = new Date();
+					logger.debug("导入成功(" + num + "):" + fname + " 耗时:" + (et.getTime() - n.getTime()));
+				} catch (Exception e) {
+					errnum++;
+					sb.append(fname).append(",");
+					Date et = new Date();
+					logger.error(
+							"导入失败(" + num + "):" + fname + " 耗时:" + (et.getTime() - n.getTime()) + " " + e.getMessage(),
+							e);
+				}
+			}
+		}
+
+//        try {
+//            importCoursePaper(new File("D:\\2020ddimport\\ques\\excelDir\\F29020021.xlsx"));
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+
+		Date end = new Date();
+		logger.debug("**********************导入结束  耗时:" + (end.getTime() - start.getTime()));
+
+		logger.debug("**********************导入出错数:" + errnum);
+		logger.debug("**********************导入出错课程:" + sb.toString());
+	}
+
+	private static void importCoursePaper(File file) throws Exception {
+		File zfile = new File(sourceDir + "\\" + (file.getName().substring(0, file.getName().lastIndexOf(".")))+ ".zip");
+		try {
+			if (zfile.exists()) {
+				zfile.delete();
+			}
+			createZip(file, zfile);
+			submitZip(zfile);
+		} catch(NoPaperException e) {
+			logger.debug("忽略:" + e.getDesc() );
+		} finally {
+			if (zfile.exists()) {
+				zfile.delete();
+			}
+		}
+	}
+
+	private static void createZip(File file, File zfile) throws InvalidFormatException, IOException{
+
+		XSSFWorkbook wb = null;
+		try {
+			wb = new XSSFWorkbook(file);
+			int num = wb.getNumberOfSheets();
+			if (num == 1) {
+				throw new NoPaperException("1000","没有试卷信息:" + file.getName());
+			}
+			Set<String> ques = new HashSet<String>();
+			for (int i = 1; i < num; i++) {
+				XSSFSheet sheet = wb.getSheetAt(i);
+				int rows = sheet.getLastRowNum();
+				for (int j = 1; j <= rows; j++) {
+					XSSFRow row = sheet.getRow(j);
+					ques.add(row.getCell(0).getStringCellValue());
+				}
+			}
+			List<File> files = new ArrayList<File>();
+			for (String s : ques) {
+				File f = new File(sourceQuesDir + s + ".txt");
+				files.add(f);
+			}
+			files.add(file);
+			FileUtil.doZip(files, zfile);
+		} finally {
+			if (wb != null) {
+				wb.close();
+			}
+		}
+	}
+
+	private static void submitZip(File zfile) throws Exception {
+		Map<String, String> params = Maps.newHashMap();
+		params.put("rootOrgId", rootOrgId);
+		params.put("courseCode", zfile.getName().split("\\.")[0]);
+		if(paperSuff!=null) {
+			params.put("suff", paperSuff);
+		}
+		Map<String, String> headers = Maps.newHashMap();
+		headers.put("key", key);
+		headers.put("token", token);
+		List<FormFilePart> fileList = Lists.newArrayList();
+		fileList.add(new FormFilePart("dataFile", zfile.getName(), zfile));
+		Response resp = null;
+		try {
+			OKHttpUtil.initOkHttpClient();
+			resp = OKHttpUtil.call(HttpMethod.POST, url, headers, params, fileList);
+			if (resp.code() != 200) {
+				throw new Exception(zfile.getName() + ":body:" + resp.body().string());
+			}
+		} finally {
+			IOUtils.closeQuietly(resp);
+		}
+	}
+}

+ 145 - 0
src/main/java/cn/com/qmth/importpaper/ImportPropByCourse.java

@@ -0,0 +1,145 @@
+package cn.com.qmth.importpaper;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import okhttp3.Response;
+
+public class ImportPropByCourse {
+
+	private static Logger logger = LogManager.getLogger(ImportPropByCourse.class);
+	private static String batch = "230517";
+	private static String sourceDir = "d:/yunkai/";
+
+	private final static String url = "http://localhost:8008/api/ecs_ques/importYunkaiProp";
+	private final static String clearurl = "http://localhost:8008/api/ecs_ques/importYunkaiProp/clear";
+
+	private final static String rootOrgId = "149";
+
+	private final static String key = "U_C_149_170";
+
+	private final static String token = "a599299bea744990aca83c7e0140086c";
+
+	public static void main(String[] args) {
+		logger.debug("**********************导入开始");
+		clear();
+		Date start = new Date();
+		int errnum = 0;
+		StringBuilder sb = new StringBuilder();
+
+		int num = 0;
+		List<Course> cs = readSubject();
+		for (Course c : cs) {
+			Date n = new Date();
+			num++;
+			File file = new File(sourceDir + "/prop/" + c.getId() + ".json");
+			try {
+				if (file.exists()) {
+					submit(c, file);
+					Date et = new Date();
+					logger.debug("导入成功(" + num + "):" + file.getName() + " 耗时:" + (et.getTime() - n.getTime()));
+				}
+			} catch (Exception e) {
+				errnum++;
+				sb.append(file.getName()).append(",");
+				Date et = new Date();
+				logger.error(
+						"导入失败(" + num + "):" + file.getName() + " 耗时:" + (et.getTime() - n.getTime()) + " " + e.getMessage(), e);
+			}
+		}
+
+		Date end = new Date();
+		logger.debug("**********************导入结束  耗时:" + (end.getTime() - start.getTime()));
+
+		logger.debug("**********************导入出错数:" + errnum);
+		logger.debug("**********************导入出错课程:" + sb.toString());
+	}
+
+	private static List<Course> readSubject() {
+		List<Course> list = new ArrayList<>();
+		XSSFWorkbook wb = null;
+		try {
+			wb = new XSSFWorkbook(sourceDir + "subject.xlsx");
+			XSSFSheet sheet = wb.getSheetAt(0);
+			int rows = sheet.getLastRowNum();
+			for (int i = 1; i <= rows; i++) {
+				Course c = new Course();
+				XSSFRow row = sheet.getRow(i);
+				String id = row.getCell(0).getStringCellValue().trim();
+				String name = row.getCell(1).getStringCellValue().trim();
+				String code = row.getCell(2).getStringCellValue().trim();
+				c.setId(Long.valueOf(id));
+				c.setCode(code);
+				c.setName(name);
+				list.add(c);
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} finally {
+			if (wb != null) {
+				try {
+					wb.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+		return list;
+	}
+
+	private static void clear() {
+		Map<String, String> params = Maps.newHashMap();
+		params.put("rootOrgId", rootOrgId);
+		params.put("batch", batch);
+		Map<String, String> headers = Maps.newHashMap();
+		headers.put("key", key);
+		headers.put("token", token);
+		Response resp = null;
+		try {
+			OKHttpUtil.initOkHttpClient();
+			resp = OKHttpUtil.call(HttpMethod.POST, clearurl, headers, params);
+			if (resp.code() != 200) {
+				throw new RuntimeException("body:" + resp.body().string());
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} finally {
+			IOUtils.closeQuietly(resp);
+		}
+	}
+
+	private static void submit(Course c, File zfile) throws Exception {
+		Map<String, String> params = Maps.newHashMap();
+		params.put("rootOrgId", rootOrgId);
+		params.put("batch", batch);
+		params.put("courseCode", c.getCode());
+		Map<String, String> headers = Maps.newHashMap();
+		headers.put("key", key);
+		headers.put("token", token);
+		List<FormFilePart> fileList = Lists.newArrayList();
+		fileList.add(new FormFilePart("dataFile", zfile.getName(), zfile));
+		Response resp = null;
+		try {
+			OKHttpUtil.initOkHttpClient();
+			resp = OKHttpUtil.call(HttpMethod.POST, url, headers, params, fileList);
+			if (resp.code() != 200) {
+				throw new Exception(zfile.getName() + ":body:" + resp.body().string());
+			}
+		} finally {
+			IOUtils.closeQuietly(resp);
+		}
+	}
+}

+ 53 - 0
src/main/java/cn/com/qmth/importpaper/JsonUtil.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.importpaper;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import cn.com.qmth.importpaper.DateUtil.DatePatterns;
+
+/**
+ * 类注释
+ *
+ * @author 
+ */
+public class JsonUtil {
+	/**
+	 * 方法注释
+	 *
+	 * @author 
+	 * @param obj
+	 * @return
+	 */
+	public static String toJson(Object obj) {
+		Gson gson = new GsonBuilder().disableHtmlEscaping().setDateFormat(DatePatterns.ISO)
+				.create();
+		return gson.toJson(obj);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author 
+	 * @param obj
+	 * @return
+	 */
+	public static String toPrettyJson(Object obj) {
+		Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting()
+				.setDateFormat(DatePatterns.ISO).create();
+		return gson.toJson(obj);
+	}
+
+	/**
+	 * 方法注释
+	 *
+	 * @author 
+	 * @param json
+	 * @param c
+	 * @return
+	 */
+	public static <T> T fromJson(String json, Class<T> c) {
+		Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+		return gson.fromJson(json, c);
+	}
+
+}

+ 64 - 0
src/main/java/cn/com/qmth/importpaper/NoPaperException.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.importpaper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class NoPaperException extends RuntimeException{
+	
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2190219930300660360L;
+
+	/**
+	 * 状态码
+	 */
+	private String code;
+
+	/**
+	 * 状态描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 */
+	public NoPaperException(String code, String desc) {
+		super("[code: " + code + "; desc: " + desc + "]");
+		this.code = code;
+		this.desc = desc;
+	}
+
+	/**
+	 * 构造函数
+	 */
+	public NoPaperException(String code, String desc, Throwable cause) {
+		super("[code: " + code + "; desc: " + desc + "]", cause);
+		this.code = code;
+		this.desc = desc;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+
+	/**
+	 * @return
+	 */
+	public String toJson() {
+		Map<String, Object> map = new HashMap<String, Object>();
+		map.put("code", code);
+		map.put("desc", desc);
+		return JsonUtil.toJson(map);
+	}
+
+	@Override
+	public String toString() {
+		return toJson();
+	}
+}

+ 298 - 0
src/main/java/cn/com/qmth/importpaper/OKHttpUtil.java

@@ -0,0 +1,298 @@
+package cn.com.qmth.importpaper;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import okhttp3.FormBody;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Request.Builder;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+/**
+ * OKHttp
+ *
+ * @author 
+ * @date 2018年9月6日
+ * @Copyright (c) 2018-2020  [QQ:522080330] All Rights Reserved.
+ */
+public class OKHttpUtil {
+
+
+	public static final class MediaTypes {
+
+		public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+	}
+
+	/**
+	 * 请求体构建器
+	 *
+	 * @author 
+	 * @date 2019年4月10日
+	 * @Copyright (c) 2018-2020  [QQ:522080330] All Rights Reserved.
+	 */
+	public static interface RequestBodyBuilder {
+		RequestBody build();
+	}
+
+	/**
+	 * json请求体构建器
+	 *
+	 * @author 
+	 * @date 2019年4月10日
+	 * @Copyright (c) 2018-2020  [QQ:522080330] All Rights Reserved.
+	 */
+	public static final class JsonBodyBuilder implements RequestBodyBuilder {
+
+		private String json;
+
+		public JsonBodyBuilder(String json) {
+			super();
+			this.json = json;
+		}
+
+		@Override
+		public RequestBody build() {
+			return RequestBody.create(MediaTypes.JSON, json);
+		}
+
+		@Override
+		public String toString() {
+			return json;
+		}
+	}
+
+	private static OkHttpClient okHttpClient;
+
+	public static void initOkHttpClient() {
+		okHttpClient = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS)
+				.readTimeout(800, TimeUnit.SECONDS).writeTimeout(800, TimeUnit.SECONDS).hostnameVerifier(new HostnameVerifier() {
+					
+					@Override
+					public boolean verify(String hostname, SSLSession session) {
+						return true;
+					}
+				}).build();
+	}
+
+	public static OkHttpClient getOkHttpClient() {
+		return okHttpClient;
+	}
+
+	/**
+	 * 发送请求 (带json请求体)
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @param headers
+	 * @param jsonBody
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+			String jsonBody) {
+		return call(httpMethod, url, headers, new JsonBodyBuilder(jsonBody));
+	}
+
+	/**
+	 * 发送请求 (带请求体)
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @param headers
+	 * @param requestBodyBuilder
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+			RequestBodyBuilder requestBodyBuilder) {
+
+
+		Builder builder = null;
+		if (httpMethod.equals(HttpMethod.POST)) {
+			builder = new Request.Builder().url(url).post(requestBodyBuilder.build());
+		} else if (httpMethod.equals(HttpMethod.PUT)) {
+			builder = new Request.Builder().url(url).put(requestBodyBuilder.build());
+		} else if (httpMethod.equals(HttpMethod.DELETE)) {
+			builder = new Request.Builder().url(url).delete(requestBodyBuilder.build());
+		}
+
+		if (null != headers && 0 != headers.size()) {
+			for (Entry<String, String> entry : headers.entrySet()) {
+				builder.addHeader(entry.getKey(), entry.getValue());
+			}
+		}
+
+		Request request = builder.build();
+
+		Response response = null;
+		try {
+			response = okHttpClient.newCall(request).execute();
+			return response;
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 发送请求
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url) {
+		return call(httpMethod, url, null);
+	}
+
+	/**
+	 * 发送请求
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @param headers
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers) {
+
+
+		Builder builder = null;
+		if (httpMethod.equals(HttpMethod.GET)) {
+			builder = new Request.Builder().url(url);
+		} else if (httpMethod.equals(HttpMethod.POST)) {
+			builder = new Request.Builder().url(url).post(new FormBody.Builder().build());
+		} else if (httpMethod.equals(HttpMethod.PUT)) {
+			builder = new Request.Builder().url(url).put(new FormBody.Builder().build());
+		} else if (httpMethod.equals(HttpMethod.DELETE)) {
+			builder = new Request.Builder().url(url).delete(new FormBody.Builder().build());
+		}
+
+		if (null != headers && 0 != headers.size()) {
+			for (Entry<String, String> entry : headers.entrySet()) {
+				builder.addHeader(entry.getKey(), entry.getValue());
+			}
+		}
+
+		Request request = builder.build();
+
+		Response response = null;
+		try {
+			response = okHttpClient.newCall(request).execute();
+			return response;
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 发送请求 (表单)
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @param headers
+	 * @param params
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+			Map<String, String> params) {
+
+		okhttp3.FormBody.Builder formBody = new FormBody.Builder();
+
+		if (null != params && 0 != params.size()) {
+			for (Entry<String, String> entry : params.entrySet()) {
+				formBody.add(entry.getKey(), entry.getValue());
+			}
+		}
+
+		Builder builder = null;
+		if (httpMethod.equals(HttpMethod.POST)) {
+			builder = new Request.Builder().url(url).post(formBody.build());
+		} else if (httpMethod.equals(HttpMethod.PUT)) {
+			builder = new Request.Builder().url(url).put(formBody.build());
+		} else if (httpMethod.equals(HttpMethod.DELETE)) {
+			builder = new Request.Builder().url(url).delete(formBody.build());
+		}
+
+		if (null != headers && 0 != headers.size()) {
+			for (Entry<String, String> entry : headers.entrySet()) {
+				builder.addHeader(entry.getKey(), entry.getValue());
+			}
+		}
+
+		Request request = builder.build();
+
+		Response response = null;
+		try {
+			response = okHttpClient.newCall(request).execute();
+			return response;
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 发送请求 (包含文件表单)
+	 *
+	 * @author 
+	 * @param httpMethod
+	 * @param url
+	 * @param headers
+	 * @param params
+	 * @param formFilePartList
+	 * @return
+	 */
+	public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+			Map<String, String> params, List<FormFilePart> formFilePartList) {
+
+
+		okhttp3.MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder()
+				.setType(MultipartBody.ALTERNATIVE);
+
+		if (null != params) {
+			for (Entry<String, String> entry : params.entrySet()) {
+				multipartBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
+			}
+		}
+
+		if (CollectionUtils.isNotEmpty(formFilePartList)) {
+			MediaType type = MediaType.parse("application/octet-stream");
+			for (FormFilePart part : formFilePartList) {
+				RequestBody fileBody = RequestBody.create(type, part.getFile());
+				multipartBodyBuilder.addFormDataPart(part.getParamName(), part.getFilename(),
+						fileBody);
+			}
+		}
+
+		Builder builder = new Request.Builder().url(url).post(multipartBodyBuilder.build());
+		if (null != headers && 0 != headers.size()) {
+			for (Entry<String, String> entry : headers.entrySet()) {
+				builder.addHeader(entry.getKey(), entry.getValue());
+			}
+		}
+
+		Request request = builder.build();
+
+		Response response = null;
+		try {
+			response = okHttpClient.newCall(request).execute();
+			return response;
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}

+ 79 - 0
src/main/java/cn/com/qmth/importpaper/ReWriteExcel.java

@@ -0,0 +1,79 @@
+package cn.com.qmth.importpaper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+public class ReWriteExcel {
+	private static Logger logger = LogManager.getLogger(ReWriteExcel.class);
+	private static String sourceDir = "C:\\Users\\chenken\\Desktop\\temp\\excelDir";
+	private static String targetDir = "C:\\Users\\chenken\\Desktop\\temp\\targetDir\\";
+	private static String suff="(2019)";
+
+	public static void main(String[] args) {
+		logger.debug("**********************处理开始");
+		Date start=new Date();
+		int errnum=0;
+		int num=0;
+		StringBuilder sb=new StringBuilder();
+		String fname = null;
+		File dir = new File(sourceDir);
+		for (File f : dir.listFiles()) {
+			fname = f.getName();
+			if (!f.isDirectory() && fname.endsWith(".xlsx")) {
+				num++;
+				Date n=new Date();
+				try {
+					write(f);
+					Date et=new Date();
+					logger.debug("处理成功("+num+"):" + fname+" 耗时:"+(et.getTime()-n.getTime()));
+				} catch (Exception e) {
+					errnum++;
+					sb.append(fname).append(",");
+					Date et=new Date();
+					logger.error("处理失败("+num+"):" + fname+" 耗时:"+(et.getTime()-n.getTime())+" "+e.getMessage(), e);
+				}
+			}
+		}
+		
+		Date end=new Date();
+		logger.debug("**********************处理结束  耗时:"+(end.getTime()-start.getTime()));
+		
+		logger.debug("**********************处理出错数:"+errnum);
+		logger.debug("**********************处理出错课程:"+sb.toString());
+	}
+
+
+	private static void write(File file) throws Exception {
+
+		XSSFWorkbook wb = null;
+		OutputStream os=null;
+		try {
+			wb = new XSSFWorkbook(file);
+			XSSFSheet sheet = wb.getSheetAt(0);
+			int rows = sheet.getLastRowNum();
+			for (int j = 1; j <= rows; j++) {
+				XSSFRow row = sheet.getRow(j);
+				String s=row.getCell(1).getStringCellValue();
+				row.getCell(1).setCellValue(s+suff);
+			}
+			os = new FileOutputStream(targetDir+file.getName());
+			wb.write(os);
+		} finally {
+			if (wb != null) {
+				wb.close();
+			}
+			if (os != null) {
+				os.close();
+			}
+		}
+	}
+
+}

+ 104 - 0
src/main/java/cn/com/qmth/multithread/Basket.java

@@ -0,0 +1,104 @@
+package cn.com.qmth.multithread;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.Logger;
+
+import cn.com.qmth.importpaper.FileUtil;
+
+
+
+public  class  Basket {
+    private static Logger logger = Logger.getLogger(Basket.class);
+	/**
+	 * 数据阻塞队列
+	 */
+	private BlockingQueue<Object> queue;
+	
+	/**
+	 * 多线程计数器,子线程都结束后主线程才继续执行
+	 */
+	private CountDownLatch endGate;
+	
+	/**
+	 * 消费者数量
+	 */
+	private int consumerCount;
+	
+	
+	/**
+	 * 判断线程执行是否有出错,生产者、消费者出错都需要修改此值为true
+	 */
+	private boolean isExcuteError = false;
+	
+	
+	public Basket(int consumerCount) {
+		this.consumerCount=consumerCount;
+		queue = new ArrayBlockingQueue<Object>(consumerCount*2);
+		endGate = new CountDownLatch(consumerCount);
+	}
+
+	/**
+	 * 生产数据,不采用put方法防止消费线程全部异常后生产线程阻塞
+	 * @param value
+	 * @throws InterruptedException
+	 */
+	protected void offer(final Object value) throws InterruptedException {
+		if(isExcuteError) {
+			logger.error("**********************offer isExcuteError threadId:"+Thread.currentThread().getId());
+			throw new StatusException("1000001","线程异常");
+		}else {
+			boolean ret=queue.offer(value, 1, TimeUnit.MINUTES);
+			if(!ret) {
+				logger.info("**********************offer time out threadId:"+Thread.currentThread().getId()+value);
+				this.offer(value);
+			}
+		}
+	}
+	/**
+	 * 消费数据,不采用take方法防止生产线程全部异常后消费线程阻塞
+	 * @return
+	 * @throws InterruptedException
+	 */
+	protected Object consume() throws InterruptedException {
+		if(isExcuteError) {
+			logger.error("**********************poll isExcuteError  threadId:"+Thread.currentThread().getId());
+			return new EndObject();
+		}else {
+			Object ob=queue.poll(1, TimeUnit.MINUTES);
+			if(ob==null) {
+				logger.info("**********************poll time out  threadId:"+Thread.currentThread().getId());
+				return this.consume();
+			}else {
+				return ob;
+			}
+		}
+	}
+	
+	protected void await() throws InterruptedException {
+		endGate.await();
+	}
+	protected void countDown() {
+		endGate.countDown();
+	}
+
+	protected boolean isExcuteError() {
+		return isExcuteError;
+	}
+
+	protected void setExcuteError(boolean isExcuteError) {
+		this.isExcuteError = isExcuteError;
+	}
+
+	protected int getConsumerCount() {
+		return consumerCount;
+	}
+
+	protected void setConsumerCount(int consumerCount) {
+		this.consumerCount = consumerCount;
+	}
+
+}

+ 62 - 0
src/main/java/cn/com/qmth/multithread/Consumer.java

@@ -0,0 +1,62 @@
+package cn.com.qmth.multithread;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+
+public abstract class Consumer<T>  extends Thread{
+	private static final Logger logger = Logger.getLogger(Consumer.class);
+	private Basket basket;
+	
+	private String traceId;
+	
+	public Consumer() {
+	}
+	@Override
+	public void run() {
+		ThreadContext.put("TRACE_ID", traceId);
+		logger.info("*******************Consumer:"+Thread.currentThread().getId()+" start");
+		try {
+			while (true) {
+				//先判断是否有异常结束
+				if(basket.isExcuteError()) {
+					break;
+				}
+				//取消费数据
+				Object o= basket.consume();
+				//判断消费数据是否是结束
+				if(o instanceof EndObject) {
+					break;
+				}
+				@SuppressWarnings("unchecked")
+				T t=(T)o;
+				logger.info("*******************Consumer:"+Thread.currentThread().getId()+" consume");
+				//消费数据实现
+				consume(t);
+			}
+		} catch (Exception e) {
+			basket.setExcuteError(true);
+			logger.info("消费线程处理出错",e);
+		}finally {
+			basket.countDown();
+			logger.info("*******************Consumer:"+Thread.currentThread().getId()+" stop");
+			ThreadContext.clearAll();
+		}
+	}
+	public abstract void consume(T t);
+	public Basket getBasket() {
+		return basket;
+	}
+	public void setBasket(Basket basket) {
+		this.basket = basket;
+	}
+	public String getTraceId() {
+		return traceId;
+	}
+	public void setTraceId(String traceId) {
+		this.traceId = traceId;
+	}
+	public abstract void disposeOnEnd(File re,File unre);
+	
+}

+ 10 - 0
src/main/java/cn/com/qmth/multithread/EndObject.java

@@ -0,0 +1,10 @@
+package cn.com.qmth.multithread;
+
+/**
+ * 消费结束标识对象
+ * @author xiatian
+ *
+ */
+public class EndObject {
+
+}

+ 30 - 0
src/main/java/cn/com/qmth/multithread/Kv.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.multithread;
+
+
+public class Kv {
+    private String key;
+    private String value;
+    
+    public Kv(String key, String value) {
+        super();
+        this.key = key;
+        this.value = value;
+    }
+
+    public String getKey() {
+        return key;
+    }
+    
+    public void setKey(String key) {
+        this.key = key;
+    }
+    
+    public String getValue() {
+        return value;
+    }
+    
+    public void setValue(String value) {
+        this.value = value;
+    }
+    
+}

+ 138 - 0
src/main/java/cn/com/qmth/multithread/Producer.java

@@ -0,0 +1,138 @@
+package cn.com.qmth.multithread;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+
+public abstract class Producer {
+	private static final Logger logger = Logger.getLogger(Producer.class);
+	private Basket basket;
+	
+	/**
+	 * 业务参数
+	 */
+	private Map<String, Object> param;
+	
+	/**
+	 * 消费线程class
+	 */
+	private Class<? extends Consumer<?>> consumer;
+	
+	public List<Consumer> consumers=new ArrayList<Consumer>();
+	/**
+	 * 	处理开始方法
+	 * @param consumer 消费线程class
+	 * @param consumerCount 消费线程数
+	 * @param param 生产者业务参数
+	 * @throws InstantiationException
+	 * @throws IllegalAccessException
+	 */
+	public void startDispose(Class<? extends Consumer<?>> consumer, int consumerCount,Map<String, Object> param)
+			throws InstantiationException, IllegalAccessException {
+		Basket basket = new Basket(consumerCount);
+		this.basket = basket;
+		this.consumer = consumer;
+		this.param=param;
+		//启动消费者
+		startConsumer();
+		//开始处理
+		dispose();
+	}
+
+	private void dispose() {
+		try {
+			logger.info("*******************Producer:开始处理");
+			// 生产数据
+			produce(param);
+			logger.info("*******************Producer:生产结束");
+			// 发送生产结束信息
+			endConsumer();
+			logger.info("*******************Producer:成功发送生产结束信息");
+			// 等待子线程结束
+			logger.info("*******************Producer:等待消费线程结束");
+			await();
+			logger.info("*******************Producer:消费线程已结束");
+			// 判断子线程是否正常结束
+			if (basket.isExcuteError()) {
+				throw new StatusException("1000001", "处理失败,线程异常");
+			}
+			logger.info("*******************Producer:结束处理");
+		} catch (StatusException e) {
+			// 获取异常时发送异常结束信息
+			endConsumerAsError();
+			throw e;
+		} catch (Exception e) {
+			// 获取异常时发送异常结束信息
+			endConsumerAsError();
+			throw new StatusException("1000002", "处理失败", e);
+		}
+	}
+
+	/**
+	 * 启动消费者
+	 * 
+	 * @param consumer
+	 * @throws InstantiationException
+	 * @throws IllegalAccessException
+	 */
+	private void startConsumer() throws InstantiationException, IllegalAccessException {
+		int count = basket.getConsumerCount();
+		for (int i = 0; i < count; i++) {
+			Consumer<?> co = (Consumer<?>) consumer.newInstance();
+			co.setBasket(basket);
+			co.setTraceId(ThreadContext.get("TRACE_ID"));
+			consumers.add(co);
+			co.start();
+		}
+
+	}
+
+	/**
+	 * 出异常后修改标识
+	 * 
+	 */
+	private void endConsumerAsError() {
+		basket.setExcuteError(true);
+	}
+
+	/**
+	 * 正常结束消费者
+	 * 
+	 * @throws InterruptedException
+	 */
+	private void endConsumer() throws InterruptedException {
+		int count = basket.getConsumerCount();
+		EndObject eo = new EndObject();
+		for (int i = 0; i < count; i++) {
+			basket.offer(eo);
+		}
+
+	}
+
+	/**
+	 * 生产数据
+	 * 
+	 * @param ob
+	 * @throws InterruptedException
+	 */
+	protected void offer(Object ob) throws InterruptedException {
+		synchronized (basket) {
+			basket.offer(ob);
+		}
+	}
+
+	/**
+	 * 等待所有消费者结束
+	 * 
+	 * @throws InterruptedException
+	 */
+	private void await() throws InterruptedException {
+		basket.await();
+	}
+
+	protected abstract void produce(Map<String, Object> param) throws Exception;
+
+}

+ 80 - 0
src/main/java/cn/com/qmth/multithread/StatusException.java

@@ -0,0 +1,80 @@
+package cn.com.qmth.multithread;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.com.qmth.importpaper.JsonUtil;
+
+/**
+ * 状态异常类<br>
+ *
+ * @author WANG
+ */
+public class StatusException extends RuntimeException {
+	private static final long serialVersionUID = 5003047488500388819L;
+
+	/**
+	 * 追踪ID
+	 */
+	private String traceId;
+
+	/**
+	 * 状态码
+	 */
+	private String code;
+
+	/**
+	 * 状态描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 */
+	public StatusException(String code, String desc) {
+		super("[code: " + code + "; desc: " + desc + "]");
+		this.code = code;
+		this.desc = desc;
+	}
+
+	/**
+	 * 构造函数
+	 */
+	public StatusException(String code, String desc, Throwable cause) {
+		super("[code: " + code + "; desc: " + desc + "]", cause);
+		this.code = code;
+		this.desc = desc;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+
+	public String getTraceId() {
+		return traceId;
+	}
+
+	public void setTraceId(String traceId) {
+		this.traceId = traceId;
+	}
+
+	/**
+	 * @return
+	 */
+	public String toJson() {
+		Map<String, Object> map = new HashMap<String, Object>();
+		map.put("code", code);
+		map.put("desc", desc);
+		return JsonUtil.toJson(map);
+	}
+
+	@Override
+	public String toString() {
+		return toJson();
+	}
+
+}

+ 124 - 0
src/main/resources/application.properties

@@ -0,0 +1,124 @@
+management.endpoints.web.exposure.include=*
+
+spring.jpa.hibernate.ddl-auto=update
+
+spring.main.allow-bean-definition-overriding=true
+
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+eureka.instance.lease-renewal-interval-in-seconds=3
+eureka.instance.lease-expiration-duration-in-seconds=6
+eureka.instance.prefer-ip-address=true
+eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
+eureka.client.healthcheck.enabled=true
+eureka.client.registry-fetch-interval-seconds=3
+eureka.client.instance-info-replication-interval-seconds=5
+eureka.client.initial-instance-info-replication-interval-seconds=5
+
+eureka.client.serviceUrl.defaultZone=http://192.168.1.210:1111/eureka/
+
+#datasource
+dsurl.host=qmth-db4.mysql.rds.aliyuncs.com
+dsurl.port=3306
+dsurl.database=exam_cloud_dev
+spring.datasource.username=exam_cloud_dev
+spring.datasource.password=Examcloud123
+
+spring.datasource.url=jdbc:mysql://${dsurl.host}:${dsurl.port}/${dsurl.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+
+#druid
+spring.datasource.druid.initial-size=5
+spring.datasource.druid.min-idle=5
+spring.datasource.druid.max-active=10
+spring.datasource.druid.max-wait=500
+spring.datasource.druid.validation-query=SELECT 1
+spring.datasource.druid.validation-query-timeout=2000
+spring.datasource.druid.test-on-borrow=false
+spring.datasource.druid.test-on-return=false
+spring.datasource.druid.test-while-idle=true
+spring.datasource.druid.time-between-eviction-runs-millis=10000
+spring.datasource.druid.min-evictable-idle-time-millis=120000
+spring.datasource.druid.filters=config,wall,stat
+spring.datasource.druid.connectionProperties=druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true;config.decrypt=false
+spring.datasource.druid.web-stat-filter.enabled=true
+spring.datasource.druid.web-stat-filter.url-pattern=/*
+spring.datasource.druid.web-stat-filter.exclusions=/druid/*,*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico
+spring.datasource.druid.web-stat-filter.session-stat-enable=true
+spring.datasource.druid.web-stat-filter.session-stat-max-count=10
+spring.datasource.druid.stat-view-servlet.enabled=true
+spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
+spring.datasource.druid.stat-view-servlet.reset-enable=true
+#spring.datasource.druid.stat-view-servlet.login-username=admin
+#spring.datasource.druid.stat-view-servlet.login-password=admin
+
+spring.redis.host=qmth-redis3.redis.rds.aliyuncs.com
+spring.redis.port=6379
+spring.redis.password=jkhm45-UO8rFc-q2Eb90
+spring.redis.database=1
+
+#log
+examcloud.web.log.normalResponseLogEnable=true
+examcloud.web.log.responseLogJsonMaxSize=1000
+#examcloud.inet.preferredNetworks=^192\.168
+
+#upyun 1
+$upyun.site.1.bucketName=exam-cloud-test
+$upyun.site.1.userName=examcloudtest
+$upyun.site.1.domain=https://ecs-test-static-upyun.qmth.com.cn
+$upyun.site.1.domain.backup=https://ecs-test-static-upyun.qmth.com.cn
+$$.$upyun.site.1.password=e5629881b17cabf9d90ef9299489a588f13c7d76c583c43a24eac5a3c58fd33b2adbd9477b88e9db81c8c89bc20a802c
+
+$aliyun.site.1.bucket=examcloud-test
+$aliyun.site.1.ossEndpoint=https://oss-cn-shenzhen.aliyuncs.com
+$aliyun.site.1.accessKeyId=LTAI4FboXLCJzrjVo5dUoXaU
+$aliyun.site.1.accessKeySecret=O0my6eSAl1Ic62WvxEf3WlMXox1LNX
+$aliyun.site.1.domain=https://ecs-test-static.qmth.com.cn
+
+$aliyun.site.2.bucket=examcloud-test
+$aliyun.site.2.ossEndpoint=https://oss-cn-shenzhen.aliyuncs.com
+$aliyun.site.2.accessKeyId=LTAI4FboXLCJzrjVo5dUoXaU
+$aliyun.site.2.accessKeySecret=O0my6eSAl1Ic62WvxEf3WlMXox1LNX
+$aliyun.site.2.domain=https://ecs-test-static.qmth.com.cn
+
+$upyun.site.num=1
+$aliyun.site.num=2
+
+#facepp
+$$.$facepp.apiKey=eaa307ecbaf419538af50e72696814fb4f9c996af95a274af700a6c5e5f7dcf3bfa78d4dbf5eff682b2682df70531c3f
+$$.$facepp.apiSecret=1a66778cd89df65f5d9edcbdd31b8b9147e7ceff43cec6acf27389e9661429752a6cfc20895bdc1c059e9181a99b9b3c
+$$.$facepp.faceid.api_key=27f41b5fcdc5f29a6f35ab39ff85f0aae8f0d43ebb4978ab63d90ab6fb1b1a932d4858c8b993c23dec575e72aa68c983
+$$.$facepp.faceid.api_secret=18bd284cd1f23aa7f0d6ba4102be9dddfea64a59192d987d6b62a2f29cc3ec19895f4cea0e3c51ef4768bd0f68f9f60e
+
+#mongodb
+mguri.username=exam-cloud-dev
+mguri.password=Examcloud123
+mguri.hostAndPortGroup=dds-wz972dde5d2d78e433270.mongodb.rds.aliyuncs.com:3717
+mguri.database=admin
+mguri.maxPoolSize=50
+
+spring.data.mongodb.uri=mongodb://${mguri.username}:${mguri.password}@${mguri.hostAndPortGroup}/${mguri.database}?maxPoolSize=${mguri.maxPoolSize}&maxIdleTimeMS=6000
+
+#baidu
+$$.$baidu.apiKey=197558fa1110c3534e0fe4dd96764ae135b81479296efcdc912954daebc37071
+$$.$baidu.secretKey=6ef631b0775a4a4ebe70cd5b7dde00e4174f8e490e728769fd814ed71ca979ae8f7a2a51bf62481dae4048d75b15ecbc
+
+#tomcat
+server.tomcat.uri-encoding=UTF-8
+server.tomcat.max-threads=10
+server.tomcat.min-spare-threads=1
+server.tomcat.accept-count=10
+server.tomcat.max-connections=100
+
+spring.mvc.async.request-timeout=1000000000
+
+examcloud.api.flowLimited.allowedRate=10
+examcloud.api.flowLimited.minCallRate=10
+examcloud.api.permitsPerSecond=8000
+
+# mq
+$report.enable=false
+#$report.mq-type=kafka
+#$kafka-bootstrap-servers=192.168.10.39:9092

+ 32 - 0
src/main/resources/log4j2.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN" monitorInterval="30">
+	<Appenders>
+		<!-- 控制台 日志 -->
+		<Console name="Console" target="SYSTEM_OUT">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
+		</Console>
+		<!-- debug 日志 -->
+		<RollingFile name="DEBUG_APPERDER" fileName="./logs/debug/debug.log" filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
+			<Policies>
+				<SizeBasedTriggeringPolicy size="100 MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+			</DefaultRolloverStrategy>
+		</RollingFile>
+	</Appenders>
+
+	<Loggers>
+		<Logger name="cn.com.qmth" level="debug" additivity="false">
+			<AppenderRef ref="DEBUG_APPERDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+
+		<Root level="INFO">
+			<AppenderRef ref="Console" />
+			<AppenderRef ref="DEBUG_APPERDER" />
+		</Root>
+	</Loggers>
+
+</Configuration>