Browse Source

first commit

wangwei 5 năm trước cách đây
commit
9eb662c24c
38 tập tin đã thay đổi với 2330 bổ sung0 xóa
  1. 12 0
      .gitignore
  2. 30 0
      assembly.xml
  3. 88 0
      pom.xml
  4. 1 0
      shell/start.args
  5. 29 0
      shell/start.sh
  6. 1 0
      shell/start.vmoptions
  7. 18 0
      shell/stop.sh
  8. 1 0
      shell/version
  9. 51 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/OEApp.java
  10. 85 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/Task.java
  11. 67 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/export_data/ExportData.java
  12. 1 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/export_data/export_data.sql
  13. 174 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/GetStduentAnswerByIdentityNumberService.java
  14. 1 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/package-info.java
  15. 14 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/query_exam_record_data_1.sql
  16. 272 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/GetStduentAnswerDetailService.java
  17. 15 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_record_data_1.sql
  18. 16 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_record_data_2.sql
  19. 8 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_student_info.sql
  20. 137 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/GetStduentOneAnswerService.java
  21. 15 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/query_exam_record_data_id.sql
  22. 8 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/query_exam_student_info.sql
  23. 188 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/ImportData.java
  24. 2 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/delete_data.sql
  25. 12 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/insert_data.sql
  26. 59 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswer.java
  27. 171 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswerService.java
  28. 240 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswerThread.java
  29. 193 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamQuestionEntity.java
  30. 55 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamRecordEntity.java
  31. 62 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamRecordQuestionsEntity.java
  32. 15 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/FixAnswerType.java
  33. 60 0
      src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/QuestionType.java
  34. 56 0
      src/main/resources/application.properties
  35. 1 0
      src/main/resources/classpath.location
  36. 52 0
      src/main/resources/log4j2.xml
  37. 87 0
      src/main/resources/temp.txt
  38. 33 0
      src/main/resources/upyun.xml

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+.project
+.classpath
+.settings
+target/
+.idea/
+*.iml
+*test/
+# Package Files #
+*.jar
+logs/
+
+

+ 30 - 0
assembly.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+	<id>distribution</id>
+	<formats>
+		<format>zip</format>
+	</formats>
+	<fileSets>
+		<fileSet>
+			<directory>${project.basedir}/src/main/resources</directory>
+			<outputDirectory>/config</outputDirectory>
+		</fileSet>
+		<fileSet>
+			<directory>${project.basedir}/shell</directory>
+			<excludes>
+				<exclude>start.args</exclude>
+				<exclude>start.vmoptions</exclude>
+			</excludes>
+			<outputDirectory>/</outputDirectory>
+			<fileMode>0777</fileMode>
+		</fileSet>
+	</fileSets>
+	<dependencySets>
+		<dependencySet>
+			<useProjectArtifact>true</useProjectArtifact>
+			<outputDirectory>lib</outputDirectory>
+			<scope>runtime</scope>
+		</dependencySet>
+	</dependencySets>
+</assembly>

+ 88 - 0
pom.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud</groupId>
+		<artifactId>examcloud-parent</artifactId>
+		<version>2019</version>
+	</parent>
+	<artifactId>data-processing-examcloud-oe</artifactId>
+	<version>2019-SNAPSHOT</version>
+	<packaging>jar</packaging>
+
+	<properties>
+		<!-- 云平台版本 -->
+		<examcloud.version>2019-SNAPSHOT</examcloud.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-web</artifactId>
+			<version>${examcloud.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>org.springframework.cloud</groupId>
+					<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-mongodb</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.springframework.boot</groupId>
+					<artifactId>spring-boot-starter-logging</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>cn.com.qmth.dp.examcloud.oe.OEApp</mainClass>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>./</classpathPrefix>
+						</manifest>
+						<manifestEntries>
+							<Class-Path>../config/</Class-Path>
+						</manifestEntries>
+					</archive>
+					<excludes>
+						<exclude>*.properties</exclude>
+						<exclude>*.xml </exclude>
+						<exclude>classpath.location</exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<finalName>data-processing-examcloud-oe</finalName>
+					<descriptors>
+						<descriptor> assembly.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>install</phase>
+						<goals>
+							<goal>assembly</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+
+</project>

+ 1 - 0
shell/start.args

@@ -0,0 +1 @@
+--spring.profiles.active=test

+ 29 - 0
shell/start.sh

@@ -0,0 +1,29 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="data-processing-examcloud-oe-"$APP_VERSION"-SNAPSHOT.jar"
+
+JAVA_OPTS=`cat $FILE_PATH/start.vmoptions`
+APP_ARGS=`cat $FILE_PATH/start.args`
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "[ERROR] : APP is already running!"
+    exit -1
+fi
+
+echo "java options:"
+echo "$JAVA_OPTS"
+echo "args:"
+echo "$APP_ARGS"
+    
+nohup java $JAVA_OPTS -jar $FILE_PATH/lib/$APP_MAIN_JAR $APP_ARGS >/dev/null 2>&1 &
+
+echo "starting......"
+
+exit 0
+
+
+

+ 1 - 0
shell/start.vmoptions

@@ -0,0 +1 @@
+-server -Xms2g -Xmx2g

+ 18 - 0
shell/stop.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="data-processing-examcloud-oe-"$APP_VERSION"-SNAPSHOT.jar"
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "Runnable jar is $APP_MAIN_JAR."
+    for PID in $PID_LIST 
+    do
+        kill -9 $PID
+    done
+    echo "stopped !"
+fi
+
+exit 0

+ 1 - 0
shell/version

@@ -0,0 +1 @@
+2019

+ 51 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/OEApp.java

@@ -0,0 +1,51 @@
+package cn.com.qmth.dp.examcloud.oe;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+import cn.com.qmth.examcloud.web.upyun.UpyunSiteManager;
+
+/**
+ * 启动类
+ *
+ * @author WANGWEI
+ * @date 2019年3月29日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@SpringBootApplication
+@Configuration
+@EnableAsync
+@ComponentScan(basePackages = {"cn.com.qmth"})
+@EnableAutoConfiguration(exclude = {RedisAutoConfiguration.class})
+public class OEApp {
+
+	static {
+		System.setProperty("hibernate.dialect.storage_engine", "innodb");
+		PropertiesUtil.loadFromResource("application.properties");
+		PropertyHolder.loadFromResource("application.properties");
+	}
+
+	/**
+	 * main
+	 *
+	 * @author WANGWEI
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		SpringApplication.run(OEApp.class, args);
+
+		UpyunSiteManager.init();
+
+		Task task = SpringContextHolder.getBean(Task.class);
+		task.start();
+	}
+
+}

+ 85 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/Task.java

@@ -0,0 +1,85 @@
+package cn.com.qmth.dp.examcloud.oe;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.bson.Document;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+import cn.com.qmth.examcloud.web.upyun.UpYunPathInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunPathEnvironmentInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunService;
+
+/**
+ * 任务
+ *
+ * @author WANGWEI
+ * @date 2019年9月6日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Component
+public class Task {
+
+	private static ExamCloudLog log = ExamCloudLogFactory.getLog(Task.class);
+
+	/**
+	 * 方法注释
+	 *
+	 * @author WANGWEI
+	 */
+	public void start() {
+		try {
+		} catch (Exception e) {
+			log.error("unexpected", e);
+			throw new RuntimeException(e);
+		}
+
+	}
+
+	/**
+	 * 连接测试
+	 *
+	 * @author WANGWEI
+	 */
+	public static void test() {
+
+		UpyunService upyunService = SpringContextHolder.getBean(UpyunService.class);
+		UpyunPathEnvironmentInfo env = new UpyunPathEnvironmentInfo();
+		env.setFileSuffix(".jpg");
+		env.setRootOrgId(String.valueOf(RandomUtils.nextLong()));
+		UpYunPathInfo upYunPathInfo = upyunService.writeFile("test", env,
+				new File("D:/Temp/111111X.jpg"), true);
+		System.out.println("upYunPathInfo: " + JsonUtil.toPrettyJson(upYunPathInfo));
+
+		JdbcTemplate jdbcTemplate = SpringContextHolder.getBean(JdbcTemplate.class);
+		List<Map<String, Object>> list = jdbcTemplate.queryForList("select now() from dual");
+		System.out.println(JsonUtil.toJson(list));
+
+		MongoTemplate mongoTemplate = SpringContextHolder.getBean(MongoTemplate.class);
+		String dbName = mongoTemplate.getDb().getName();
+		System.out.println("mongo.db=" + dbName);
+		MongoCollection<Document> collection = mongoTemplate.getCollection("examRecordQuestions");
+		Document filter = new Document();
+		filter.append("examRecordDataId", 101373L);
+		FindIterable<Document> iterable = collection.find(filter);
+		MongoCursor<Document> iterator = iterable.iterator();
+		while (iterator.hasNext()) {
+			Document next = iterator.next();
+
+			System.out.println("_id=" + next.getObjectId("_id"));
+			System.out.println(JsonUtil.toPrettyJson(next));
+		}
+	}
+}

+ 67 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/export_data/ExportData.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.dp.examcloud.oe.modules.export_data;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Maps;
+
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.util.ResourceLoader;
+
+@Component
+public class ExportData {
+
+	private static ExamCloudLog log = ExamCloudLogFactory.getLog(ExportData.class);
+
+	private static ExamCloudLog resultlog = ExamCloudLogFactory.getLog("RESULT_LOGGER");
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	public void start() {
+
+		String packageName = this.getClass().getPackage().getName();
+		String packagePath = packageName.replaceAll("\\.", "/");
+		String sql = ResourceLoader.getResource(packagePath + "/export_data.sql");
+
+		try {
+			Connection connection = jdbcTemplate.getDataSource().getConnection();
+
+			PreparedStatement ps = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
+					ResultSet.CONCUR_READ_ONLY);
+			ps.setFetchSize(Integer.MIN_VALUE);
+			ResultSet rs = ps.executeQuery(sql);
+
+			int count = 0;
+			while (rs.next()) {
+				Map<String, Object> map = Maps.newHashMap();
+				ResultSetMetaData metaData = rs.getMetaData();
+				int columnCount = metaData.getColumnCount();
+				for (int i = 0; i < columnCount; i++) {
+					String columnName = metaData.getColumnName(i + 1);
+					Object columnValue = rs.getObject(i + 1);
+					map.put(columnName, columnValue);
+				}
+
+				count++;
+				log.info("[ExportData]. count=" + count);
+				resultlog.info(JsonUtil.toJson(map));
+
+			}
+		} catch (SQLException e) {
+			throw new RuntimeException(e);
+		}
+
+	}
+
+}

+ 1 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/export_data/export_data.sql

@@ -0,0 +1 @@
+SELECT * from student_face_result

+ 174 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/GetStduentAnswerByIdentityNumberService.java

@@ -0,0 +1,174 @@
+package cn.com.qmth.dp.examcloud.oe.modules.get_student_answer_by_identity_number;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.bson.Document;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.mongodb.core.DocumentCallbackHandler;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+import com.mongodb.MongoException;
+
+import cn.com.qmth.examcloud.commons.helpers.ObjectHolder;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.ResourceLoader;
+
+/**
+ * 获取一个考生的作答明细
+ *
+ * @author WANGWEI
+ * @date 2019年7月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Service
+public class GetStduentAnswerByIdentityNumberService {
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	MongoTemplate mongoTemplate;
+
+	private int sqlIndex = 1;
+
+	@Async
+	public void start(Long examId, String courseCode, String identityNumber) throws Exception {
+		String packageName = this.getClass().getPackage().getName();
+		String packagePath = packageName.replaceAll("\\.", "/");
+		String sql = ResourceLoader
+				.getResource(packagePath + "/query_exam_record_data_" + sqlIndex + ".sql");
+		Object[] args = new Object[]{examId, courseCode, identityNumber};
+		List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, args);
+
+		List<Long> examRecordDataIdList = Lists.newArrayList();
+
+		for (Map<String, Object> map : result) {
+			Long examRecordDataId = (Long) map.get("EXAM_RECORD_DATA_ID");
+			String curIdentityNumber = (String) map.get("IDENTITY_NUMBER");
+			String curCourseCode = (String) map.get("CODE");
+
+			if (!curCourseCode.equals(courseCode)) {
+				throw new RuntimeException("unexpected exception. courseCode=" + courseCode);
+			}
+
+			if (!curIdentityNumber.equals(identityNumber)) {
+				throw new RuntimeException("unexpected exception. courseCode=" + courseCode);
+			}
+
+			examRecordDataIdList.add(examRecordDataId);
+		}
+
+		List<Object[]> datas = Lists.newArrayList();
+
+		for (Long cur : examRecordDataIdList) {
+
+			List<Document> answers = getAnswers(cur);
+
+			for (Document doc : answers) {
+				Integer order = doc.getInteger("order");
+				Double studentScore = doc.getDouble("studentScore");
+				String questionId = doc.getString("questionId");
+				String studentAnswer = doc.getString("studentAnswer");
+				String questionType = doc.getString("questionType");
+
+				if (null == order) {
+					throw new RuntimeException("order is null. examRecordDataId=" + cur);
+				}
+
+				if (null == questionType) {
+					throw new RuntimeException(
+							"questionType is null. examRecordDataId=" + cur + "; order=" + order);
+				}
+
+				if (null == studentScore) {
+					// 无作答时,0分
+					if (StringUtils.isBlank(studentAnswer)) {
+						studentScore = 0D;
+					}
+				}
+
+				if (null != studentAnswer && !studentAnswer.contains("<img")) {
+					studentAnswer = Jsoup.clean(studentAnswer, Whitelist.simpleText());
+				}
+
+				if (null != studentAnswer) {
+					if (questionType.equals("SINGLE_CHOICE")
+							|| questionType.equals("MULTIPLE_CHOICE")) {
+						studentAnswer = studentAnswer.replaceAll("0", "A");
+						studentAnswer = studentAnswer.replaceAll("1", "B");
+						studentAnswer = studentAnswer.replaceAll("2", "C");
+						studentAnswer = studentAnswer.replaceAll("3", "D");
+						studentAnswer = studentAnswer.replaceAll("4", "E");
+						studentAnswer = studentAnswer.replaceAll("5", "F");
+						studentAnswer = studentAnswer.replaceAll("6", "G");
+						studentAnswer = studentAnswer.replaceAll("7", "H");
+						studentAnswer = studentAnswer.replaceAll("8", "I");
+					}
+				}
+
+				Object[] row = new Object[]{identityNumber, courseCode, String.valueOf(cur),
+						String.valueOf(order), questionId, studentAnswer,
+						null != studentScore ? String.valueOf(studentScore) : null};
+
+				datas.add(row);
+			}
+
+		}
+
+		String filePath = "D:/Temp/answers-" + examId + "-" + courseCode + "-" + identityNumber
+				+ ".xlsx";
+
+		final String[] EXCEL_HEADER = new String[]{"身份证号码", "课程代码", "考试记录ID", "题号", "题目ID", "答案",
+				"分数"};
+
+		ExcelWriter
+				.write(EXCEL_HEADER,
+						new Class[]{String.class, String.class, String.class, String.class,
+								String.class, String.class, String.class},
+						datas, new File(filePath));
+
+		System.out.println("OVER !   identityNumber=" + identityNumber);
+	}
+
+	/**
+	 * 获取作答
+	 *
+	 * @author WANGWEI
+	 * @param examRecordDataId
+	 * @return
+	 */
+	private List<Document> getAnswers(Long examRecordDataId) {
+		Query query = Query.query(Criteria.where("examRecordDataId").is(examRecordDataId));
+		final ObjectHolder<List<Document>> answersHolder = new ObjectHolder<List<Document>>(null);
+		mongoTemplate.executeQuery(query, "examRecordQuestions", new DocumentCallbackHandler() {
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public void processDocument(Document document)
+					throws MongoException, DataAccessException {
+				List<Document> answers = (List<Document>) document.get("examQuestionEntities");
+				answersHolder.set(answers);
+			}
+		});
+
+		List<Document> answers = answersHolder.get();
+		return answers;
+	}
+
+	public void setSqlIndex(int sqlIndex) {
+		this.sqlIndex = sqlIndex;
+	}
+
+}

+ 1 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/package-info.java

@@ -0,0 +1 @@
+package cn.com.qmth.dp.examcloud.oe.modules.get_student_answer_by_identity_number;

+ 14 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_by_identity_number/query_exam_record_data_1.sql

@@ -0,0 +1,14 @@
+SELECT
+	t1.identity_number,
+	t2.id AS exam_record_data_id,
+	t4.`code` 
+FROM
+	ec_oe_exam_record t1
+	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id
+	LEFT JOIN ec_b_course t4 ON t4.id = t1.course_id 
+WHERE
+	(( t2.is_warn = 0 ) OR ( t2.is_warn = 1 AND t2.is_audit = 1 ) ) 
+	AND t2.exam_record_status != 'EXAM_INVALID' 
+	AND t1.exam_id = ? 
+	AND t4.`code` = ?
+	AND t1.identity_number = ?

+ 272 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/GetStduentAnswerDetailService.java

@@ -0,0 +1,272 @@
+package cn.com.qmth.dp.examcloud.oe.modules.get_student_answer_detail;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.bson.Document;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.mongodb.core.DocumentCallbackHandler;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.mongodb.MongoException;
+
+import cn.com.qmth.examcloud.commons.helpers.ObjectHolder;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.util.ResourceLoader;
+
+/**
+ * 获取考生作答明细
+ *
+ * @author WANGWEI
+ * @date 2019年7月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Service
+public class GetStduentAnswerDetailService {
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	MongoTemplate mongoTemplate;
+
+	private int sqlIndex = 1;
+
+	private boolean mustHaveScore = true;
+
+	public void start(Long examId, String... courseCodeLIst) throws Exception {
+		for (String courseCode : courseCodeLIst) {
+			start(examId, courseCode);
+		}
+	}
+
+	@Async
+	public void start(Long examId, String courseCode) throws Exception {
+		String packageName = this.getClass().getPackage().getName();
+		String packagePath = packageName.replaceAll("\\.", "/");
+		String sql = ResourceLoader
+				.getResource(packagePath + "/query_exam_record_data_" + sqlIndex + ".sql");
+		Object[] args = new Object[]{examId, courseCode};
+		List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, args);
+
+		Map<String, Double> maxScoreOf = Maps.newHashMap();
+		Map<String, Long> examRecordDataIdOf = Maps.newHashMap();
+		Map<Long, String> identityNumberOf = Maps.newHashMap();
+
+		for (Map<String, Object> map : result) {
+			Long examRecordDataId = (Long) map.get("EXAM_RECORD_DATA_ID");
+			Double score = (Double) map.get("SCORE");
+			String identityNumber = (String) map.get("IDENTITY_NUMBER");
+			String curCourseCode = (String) map.get("CODE");
+
+			if (null == score) {
+				throw new RuntimeException("score is null. examRecordDataId=" + examRecordDataId);
+			}
+
+			if (!curCourseCode.equals(courseCode)) {
+				throw new RuntimeException("unexpected exception. courseCode=" + courseCode);
+			}
+
+			identityNumberOf.put(examRecordDataId, identityNumber);
+
+			Double maxScore = maxScoreOf.get(identityNumber);
+			if (null == maxScore || maxScore <= score) {
+				examRecordDataIdOf.put(identityNumber, examRecordDataId);
+				maxScoreOf.put(identityNumber, score);
+			}
+		}
+
+		List<Long> examRecordDataIdList = new ArrayList<>(examRecordDataIdOf.values());
+
+		List<Object[]> datas = Lists.newArrayList();
+
+		List<Object[]> noStudentScoreList = Lists.newArrayList();
+
+		int total = examRecordDataIdList.size();
+		int count = 0;
+		for (Long cur : examRecordDataIdList) {
+			count++;
+			System.out.println("total: " + total + "; count: " + count);
+
+			List<Document> answers = getAnswers(cur);
+
+			String identityNumber = identityNumberOf.get(cur);
+
+			for (Document doc : answers) {
+				Integer order = doc.getInteger("order");
+				Double studentScore = doc.getDouble("studentScore");
+				Double questionScore = doc.getDouble("questionScore");
+				String questionId = doc.getString("questionId");
+				String studentAnswer = doc.getString("studentAnswer");
+				String correctAnswer = doc.getString("correctAnswer");
+				String questionType = doc.getString("questionType");
+
+				if (null == order) {
+					throw new RuntimeException("order is null. examRecordDataId=" + cur);
+				}
+
+				if (null == questionType) {
+					throw new RuntimeException(
+							"questionType is null. examRecordDataId=" + cur + "; order=" + order);
+				}
+
+				if (null == studentScore) {
+					// 无作答时,0分
+					if (StringUtils.isBlank(studentAnswer)) {
+						studentScore = 0D;
+					}
+					// 计算分数
+					else {
+
+						if (null == questionScore) {
+							throw new RuntimeException("questionScore is null. examRecordDataId="
+									+ cur + ";courseCode=" + courseCode + ";order=" + order);
+						}
+
+						// 客观题
+						if (questionType.equals("SINGLE_CHOICE")
+								|| questionType.equals("MULTIPLE_CHOICE")
+								|| questionType.equals("TRUE_OR_FALSE")) {
+
+							// 无正确答案时
+							if (StringUtils.isBlank(correctAnswer)) {
+								noStudentScoreList.add(new Object[]{cur, courseCode, order});
+								studentScore = null;
+							}
+							// 有正确答案时
+							else {
+								if (correctAnswer.equals(studentAnswer)) {
+									studentScore = questionScore;
+								} else {
+									studentScore = 0D;
+								}
+							}
+
+						}
+						// 主观题
+						else if (questionType.equals("FILL_UP") || questionType.equals("ESSAY")) {
+							noStudentScoreList.add(new Object[]{cur, courseCode, order});
+							studentScore = null;
+						} else {
+							throw new RuntimeException("unknow questionType. questionType="
+									+ questionType + "; examRecordDataId=" + cur + ";courseCode="
+									+ courseCode + ";order=" + order);
+						}
+
+					}
+				}
+
+				if (null != studentAnswer && !studentAnswer.contains("<img")) {
+					studentAnswer = Jsoup.clean(studentAnswer, Whitelist.simpleText());
+				}
+
+				if (null != studentAnswer) {
+					if (questionType.equals("SINGLE_CHOICE")
+							|| questionType.equals("MULTIPLE_CHOICE")) {
+						studentAnswer = studentAnswer.replaceAll("0", "A");
+						studentAnswer = studentAnswer.replaceAll("1", "B");
+						studentAnswer = studentAnswer.replaceAll("2", "C");
+						studentAnswer = studentAnswer.replaceAll("3", "D");
+						studentAnswer = studentAnswer.replaceAll("4", "E");
+						studentAnswer = studentAnswer.replaceAll("5", "F");
+						studentAnswer = studentAnswer.replaceAll("6", "G");
+						studentAnswer = studentAnswer.replaceAll("7", "H");
+						studentAnswer = studentAnswer.replaceAll("8", "I");
+					}
+				}
+
+				Object[] row = new Object[]{identityNumber, courseCode, String.valueOf(order),
+						questionId, studentAnswer, String.valueOf(studentScore)};
+
+				datas.add(row);
+			}
+
+		}
+
+		if (CollectionUtils.isNotEmpty(noStudentScoreList)) {
+			System.out.println("无分数 ============>");
+			Set<Long> examRecordDataIdSet = Sets.newHashSet();
+			for (Object[] objects : noStudentScoreList) {
+				examRecordDataIdSet.add((Long) objects[0]);
+				System.out.println(JsonUtil.toJson(objects));
+			}
+
+			String sql4QueryExamStudentInfo = ResourceLoader
+					.getResource(packagePath + "/query_exam_student_info.sql");
+			String ids = StringUtils.join(examRecordDataIdSet, ",");
+			sql4QueryExamStudentInfo = sql4QueryExamStudentInfo.replace("$$", ids);
+
+			List<Map<String, Object>> studentInfoList = jdbcTemplate
+					.queryForList(sql4QueryExamStudentInfo);
+
+			for (Map<String, Object> curStudent : studentInfoList) {
+				System.out.println(JsonUtil.toJson(curStudent));
+			}
+
+			if (mustHaveScore) {
+				throw new RuntimeException("无分数");
+			}
+		}
+
+		String filePath = "D:/Temp/answers-detail-" + examId + "-" + courseCode + ".xlsx";
+
+		final String[] EXCEL_HEADER = new String[]{"身份证号码", "课程代码", "题号(跟蓝图里面的题号或者题目ID是对应的)",
+				"题目ID", "答案", "分数"};
+
+		ExcelWriter.write(EXCEL_HEADER, new Class[]{String.class, String.class, String.class,
+				String.class, String.class, String.class}, datas, new File(filePath));
+
+		System.out.println("OVER !   courseCode=" + courseCode);
+	}
+
+	/**
+	 * 获取作答
+	 *
+	 * @author WANGWEI
+	 * @param examRecordDataId
+	 * @return
+	 */
+	private List<Document> getAnswers(Long examRecordDataId) {
+		Query query = Query.query(Criteria.where("examRecordDataId").is(examRecordDataId));
+		final ObjectHolder<List<Document>> answersHolder = new ObjectHolder<List<Document>>(null);
+		mongoTemplate.executeQuery(query, "examRecordQuestions", new DocumentCallbackHandler() {
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public void processDocument(Document document)
+					throws MongoException, DataAccessException {
+				List<Document> answers = (List<Document>) document.get("examQuestionEntities");
+				answersHolder.set(answers);
+			}
+		});
+
+		List<Document> answers = answersHolder.get();
+		return answers;
+	}
+
+	public void setSqlIndex(int sqlIndex) {
+		this.sqlIndex = sqlIndex;
+	}
+
+	public void setMustHaveScore(boolean mustHaveScore) {
+		this.mustHaveScore = mustHaveScore;
+	}
+
+}

+ 15 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_record_data_1.sql

@@ -0,0 +1,15 @@
+SELECT
+	t1.identity_number,
+	t2.id AS exam_record_data_id,
+	t3.total_score as score,
+	t4.`code` 
+FROM
+	ec_oe_exam_record t1
+	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id
+	LEFT JOIN ec_oe_exam_score t3 ON t3.exam_record_data_id = t2.id
+	LEFT JOIN ec_b_course t4 ON t4.id = t1.course_id 
+WHERE
+	(( t2.is_warn = 0 ) OR ( t2.is_warn = 1 AND t2.is_audit = 1 ) ) 
+	AND t2.exam_record_status != 'EXAM_INVALID' 
+	AND t1.exam_id = ? 
+	AND t4.`code` = ?

+ 16 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_record_data_2.sql

@@ -0,0 +1,16 @@
+SELECT
+	t1.identity_number,
+	t2.id AS exam_record_data_id,
+	t3.total_score as score,
+	t4.`code` 
+FROM
+	ec_oe_exam_record t1
+	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id
+	LEFT JOIN ec_oe_exam_score t3 ON t3.exam_record_data_id = t2.id
+	LEFT JOIN ec_b_course t4 ON t4.id = t1.course_id 
+	LEFT JOIN ec_m_student_paper t5 ON t5.exam_record_data_id = t2.id
+WHERE
+	t5.id is not null
+	AND t5.student_paper_status != 0 	
+	AND t1.exam_id = ? 
+	AND t4.`code` = ?

+ 8 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_answer_detail/query_exam_student_info.sql

@@ -0,0 +1,8 @@
+SELECT t1.student_name, t1.identity_number,
+( SELECT x.`code` FROM ec_b_course x WHERE x.id = t1.course_id ) AS course_code 
+FROM
+	ec_oe_exam_record t1,
+	ec_oe_exam_record_data t2 
+WHERE
+	t2.exam_record_id = t1.id 
+	AND t2.id IN ($$)

+ 137 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/GetStduentOneAnswerService.java

@@ -0,0 +1,137 @@
+package cn.com.qmth.dp.examcloud.oe.modules.get_student_one_question_answer;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.bson.Document;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.mongodb.core.DocumentCallbackHandler;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+import com.mongodb.MongoException;
+
+import cn.com.qmth.examcloud.commons.helpers.ObjectHolder;
+import cn.com.qmth.examcloud.commons.helpers.poi.ExcelWriter;
+import cn.com.qmth.examcloud.commons.util.ResourceLoader;
+
+/**
+ * 获取考生作答明细
+ *
+ * @author WANGWEI
+ * @date 2019年7月11日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+@Service
+public class GetStduentOneAnswerService {
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	MongoTemplate mongoTemplate;
+
+	public void start(Long examId, int questionOrder, String... courseCodeLIst) throws Exception {
+		for (String courseCode : courseCodeLIst) {
+			start(examId, questionOrder, courseCode);
+		}
+	}
+
+	@Async
+	public void start(Long examId, int questionOrder, String courseCode) throws Exception {
+		String packageName = this.getClass().getPackage().getName();
+		String packagePath = packageName.replaceAll("\\.", "/");
+		String sql = ResourceLoader.getResource(packagePath + "/query_exam_record_data_id.sql");
+		Object[] args = new Object[]{examId, courseCode};
+		List<Map<String, Object>> result = jdbcTemplate.queryForList(sql, args);
+
+		List<Object[]> datas = Lists.newArrayList();
+		int total = result.size();
+		int count = 0;
+
+		for (Map<String, Object> map : result) {
+			Long examRecordDataId = (Long) map.get("EXAM_RECORD_DATA_ID");
+			String studentName = (String) map.get("STUDENT_NAME");
+			String studentCode = (String) map.get("STUDENT_CODE");
+			String curCourseCode = (String) map.get("COURSE_CODE");
+			if (!curCourseCode.equals(courseCode)) {
+				throw new RuntimeException("unexpected exception. courseCode=" + courseCode);
+			}
+
+			count++;
+			System.out.println("total: " + total + "; count: " + count);
+
+			List<Document> answers = getAnswers(examRecordDataId);
+
+			for (Document doc : answers) {
+				Integer order = doc.getInteger("order");
+				String studentAnswer = doc.getString("studentAnswer");
+
+				if (null == order) {
+					throw new RuntimeException(
+							"order is null. examRecordDataId=" + examRecordDataId);
+				}
+				if (!order.equals(questionOrder)) {
+					continue;
+				}
+
+				if (null != studentAnswer && !studentAnswer.contains("<img")) {
+					studentAnswer = Jsoup.clean(studentAnswer, Whitelist.simpleText());
+				}
+
+				Object[] row = new Object[]{String.valueOf(examId), courseCode, studentName,
+						studentCode, studentAnswer};
+
+				datas.add(row);
+				break;
+			}
+
+		}
+
+		String filePath = "D:/Temp/one-answer-" + examId + "-" + courseCode + ".xlsx";
+
+		final String[] EXCEL_HEADER = new String[]{"考试批次ID", "课程代码", "学生姓名", "学号",
+				"第" + questionOrder + "题作答"};
+
+		ExcelWriter.write(EXCEL_HEADER,
+				new Class[]{String.class, String.class, String.class, String.class, String.class},
+				datas, new File(filePath));
+
+		System.out.println("OVER !   courseCode=" + courseCode);
+	}
+
+	/**
+	 * 获取作答
+	 *
+	 * @author WANGWEI
+	 * @param examRecordDataId
+	 * @return
+	 */
+	private List<Document> getAnswers(Long examRecordDataId) {
+		Query query = Query.query(Criteria.where("examRecordDataId").is(examRecordDataId));
+		final ObjectHolder<List<Document>> answersHolder = new ObjectHolder<List<Document>>(null);
+		mongoTemplate.executeQuery(query, "examRecordQuestions", new DocumentCallbackHandler() {
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public void processDocument(Document document)
+					throws MongoException, DataAccessException {
+				List<Document> answers = (List<Document>) document.get("examQuestionEntities");
+				answersHolder.set(answers);
+			}
+		});
+
+		List<Document> answers = answersHolder.get();
+		return answers;
+	}
+
+}

+ 15 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/query_exam_record_data_id.sql

@@ -0,0 +1,15 @@
+SELECT
+	t2.id AS exam_record_data_id,
+	t1.exam_id,
+	t5.`name` AS student_name,
+	t1.student_code,
+	t1.identity_number,
+	t4.`code` AS course_code 
+FROM
+	ec_oe_exam_record t1
+	LEFT JOIN ec_oe_exam_record_data t2 ON t1.id = t2.exam_record_id
+	LEFT JOIN ec_b_course t4 ON t4.id = t1.course_id
+	LEFT JOIN ec_b_student t5 ON t5.id = t1.student_id 
+WHERE
+	t1.exam_id = ? 
+	AND t4.`code` = ?

+ 8 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/get_student_one_question_answer/query_exam_student_info.sql

@@ -0,0 +1,8 @@
+SELECT t1.student_name, t1.identity_number,
+( SELECT x.`code` FROM ec_b_course x WHERE x.id = t1.course_id ) AS course_code 
+FROM
+	ec_oe_exam_record t1,
+	ec_oe_exam_record_data t2 
+WHERE
+	t2.exam_record_id = t1.id 
+	AND t2.id IN ($$)

+ 188 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/ImportData.java

@@ -0,0 +1,188 @@
+package cn.com.qmth.dp.examcloud.oe.modules.import_data;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import cn.com.qmth.examcloud.commons.helpers.Counter;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.DBUtil;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.commons.util.DateUtil.DatePatterns;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.ResourceLoader;
+import cn.com.qmth.examcloud.commons.util.Util;
+
+@Component
+public class ImportData {
+
+	private static ExamCloudLog log = ExamCloudLogFactory.getLog(ImportData.class);
+
+	public void start(String dirPath) {
+
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				Counter counter = new Counter(0, Long.MAX_VALUE);
+				while (true) {
+					File dir = new File(dirPath);
+
+					File[] fileArr = dir.listFiles();
+
+					for (File file : fileArr) {
+						processFile(counter, file);
+					}
+
+					Util.sleep(60);
+				}
+
+			}
+		}).start();
+	}
+
+	private void processFile(Counter counter, File file) {
+
+		String packageName = this.getClass().getPackage().getName();
+		String packagePath = packageName.replaceAll("\\.", "/");
+		String insertSql = ResourceLoader.getResource(packagePath + "/insert_data.sql");
+		String deleteSql = ResourceLoader.getResource(packagePath + "/delete_data.sql");
+
+		BufferedReader br = null;
+		InputStreamReader reader = null;
+
+		boolean hasError = false;
+		try {
+			reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
+			br = new BufferedReader(reader);
+
+			Connection ds2Conn = DBUtil.getConnection("ds2");
+
+			String line = null;
+			while (null != (line = br.readLine())) {
+				Object[] deleteParams = null;
+				Object[] columValues = null;
+				try {
+					columValues = toColumValues(line);
+					deleteParams = getDeleteParams(columValues);
+				} catch (Exception e) {
+					log.error("[DataImport]. ErrorLine: " + line, e);
+					continue;
+				}
+
+				try {
+					delete(ds2Conn, deleteSql, deleteParams);
+				} catch (Exception e) {
+					hasError = true;
+					log.error("[DataImport]. DeleteError: " + line, e);
+				}
+
+				try {
+					insert(ds2Conn, insertSql, columValues);
+					counter.next();
+					log.info("[DataImport]. count=" + counter.get());
+				} catch (Exception e) {
+					hasError = true;
+					log.error("[DataImport]. InsertError: " + line, e);
+				}
+
+			}
+
+		} catch (Exception e) {
+			hasError = true;
+			log.error("[DataImport]. unexpected", e);
+		} finally {
+			IOUtils.closeQuietly(br);
+			IOUtils.closeQuietly(reader);
+		}
+
+		if (!hasError) {
+			try {
+				FileUtils.forceDelete(file);
+			} catch (IOException e) {
+				log.error("[DataImport]. fail to delete file. path="
+						+ PathUtil.getCanonicalPath(file), e);
+			}
+		}
+
+	}
+
+	private void insert(Connection ds2Conn, String insertSql, Object[] columValues)
+			throws SQLException {
+
+		while (!ds2Conn.isValid(1)) {
+			ds2Conn = DBUtil.getConnection("ds2");
+		}
+
+		PreparedStatement ps2 = null;
+		try {
+			ps2 = ds2Conn.prepareStatement(insertSql);
+			for (int i = 0; i < columValues.length; i++) {
+				ps2.setObject(i + 1, columValues[i]);
+			}
+			ps2.execute();
+		} finally {
+			DBUtil.close(ps2);
+		}
+
+	}
+
+	private void delete(Connection ds2Conn, String deleteSql, Object[] params) throws SQLException {
+
+		while (!ds2Conn.isValid(1)) {
+			ds2Conn = DBUtil.getConnection("ds2");
+		}
+
+		PreparedStatement ps2 = null;
+		try {
+			ps2 = ds2Conn.prepareStatement(deleteSql);
+			for (int i = 0; i < params.length; i++) {
+				ps2.setObject(i + 1, params[i]);
+			}
+			ps2.execute();
+		} finally {
+			DBUtil.close(ps2);
+		}
+
+	}
+
+	private Object[] toColumValues(String str) {
+		JsonObject jsonObj = JsonParser.parseString(str).getAsJsonObject();
+
+		Object[] arr = new Object[9];
+
+		arr[0] = jsonObj.get("id").getAsLong();
+		arr[1] = jsonObj.get("root_org_id").getAsLong();
+		arr[2] = jsonObj.get("exam_student_id").getAsLong();
+		arr[3] = jsonObj.get("exam_record_data_id").getAsLong();
+		arr[4] = jsonObj.get("photo_path").getAsString();
+		arr[5] = jsonObj.get("file_url").getAsString();
+		arr[6] = jsonObj.get("face_compare_result").getAsString();
+		arr[7] = DateUtil.parse(jsonObj.get("creation_time").getAsString(),
+				DatePatterns.CHINA_DEFAULT);
+		if (!jsonObj.get("used_time").isJsonNull()) {
+			arr[8] = jsonObj.get("used_time").getAsLong();
+		}
+
+		return arr;
+	}
+
+	private Object[] getDeleteParams(Object[] columValues) {
+		Object[] arr = new Object[1];
+		arr[0] = columValues[0];
+		return arr;
+	}
+
+}

+ 2 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/delete_data.sql

@@ -0,0 +1,2 @@
+delete from `student_face_result_2` 
+where  id = ?

+ 12 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/import_data/insert_data.sql

@@ -0,0 +1,12 @@
+INSERT INTO `student_face_result_2` 
+( `id`,
+`root_org_id`, 
+`exam_student_id`,
+`exam_record_data_id`,
+`photo_path`,
+`file_url`,
+`face_compare_result`, 
+`creation_time`,
+`used_time` )
+VALUES
+	( ?, ?, ?, ?, ?, ?, ?, ?, ? )

+ 59 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswer.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer;
+
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity.FixAnswerType;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
+/**
+ * 执行图片处理逻辑
+ */
+@Service
+public class FixStudentAnswer {
+
+	private static final Logger log = LoggerFactory.getLogger(FixStudentAnswer.class);
+
+	/**
+	 * 修复图片
+	 */
+	public void startFixPicAnswer(Long examId, String courseCode, int order) {
+		FixStudentAnswerService service = SpringContextHolder
+				.getBean(FixStudentAnswerService.class);
+		String srcPicFileDirectory = "D:/Temp/srcPic";
+		try {
+			service.fixStudentAnswer(srcPicFileDirectory, examId, courseCode, order,
+					FixAnswerType.PICTURE);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			log.error("FileNotFoundException", e);
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+			log.error("UnsupportedEncodingException", e);
+		} catch (Exception e) {
+			log.error("UnkonwnException ", e);
+		}
+	}
+
+	public void startFixAudioAnswer(Long examId, String courseCode, int order) {
+		FixStudentAnswerService service = SpringContextHolder
+				.getBean(FixStudentAnswerService.class);
+		String srcPicFileDirectory = "D:/Temp/srcAudio";
+		try {
+			service.fixStudentAnswer(srcPicFileDirectory, examId, courseCode, order,
+					FixAnswerType.AUDIO);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			log.error("FileNotFoundException", e);
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+			log.error("UnsupportedEncodingException", e);
+		} catch (Exception e) {
+			log.error("UnkonwnException ", e);
+		}
+	}
+}

+ 171 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswerService.java

@@ -0,0 +1,171 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.RegExUtils;
+import org.assertj.core.util.Sets;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity.FixAnswerType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.helpers.Counter;
+import cn.com.qmth.examcloud.commons.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.util.Util;
+import cn.com.qmth.examcloud.web.upyun.UpyunService;
+
+/**
+ * @Description 修复学生作答
+ * @Author THINKPAD
+ * @Date 2019/7/8 10:16
+ * @Version 1.0
+ */
+@Service
+public class FixStudentAnswerService {
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	@Autowired
+	MongoTemplate mongoTemplate;
+
+	@Autowired
+	UpyunService upyunService;
+
+	private static Counter counter = new Counter(0L, 1000000L);
+
+	public void fixStudentAnswer(String filePath, Long examId, String courseCode, Integer order,
+			FixAnswerType fixAnswerType)
+			throws FileNotFoundException, UnsupportedEncodingException {
+		if (fixAnswerType != FixAnswerType.AUDIO && fixAnswerType != FixAnswerType.PICTURE) {
+			throw new StatusException("100001", "修改的文件类型格式不正确");
+		}
+		// 错误提示
+		List<Map<String, Object>> failList = Lists.newArrayList();
+		File file = new File(filePath);
+
+		if (file.isDirectory()) {
+			String[] list = file.list();
+			// 循环查库
+			// 获取身份证键值对信息
+			Map<String, List<Map<String, Object>>> identityMap = getIdentityMap(filePath, list);
+
+			Set<String> keys = identityMap.keySet();
+			Set<String> tempKeys = Sets.newHashSet();
+			int index = 0;
+			int threadSize = 10;
+			int batchSize = keys.size() / threadSize + 1;
+			for (String key : keys) {
+				index++;
+				tempKeys.add(key);
+				if (index == batchSize) {
+					new FixStudentAnswerThread(jdbcTemplate, mongoTemplate, tempKeys, examId,
+							courseCode, failList, identityMap, order, fixAnswerType, upyunService,
+							counter).start();
+					index = 0;
+					tempKeys = Sets.newHashSet();
+				}
+			}
+
+			if (CollectionUtils.isNotEmpty(tempKeys)) {
+				new FixStudentAnswerThread(jdbcTemplate, mongoTemplate, tempKeys, examId,
+						courseCode, failList, identityMap, order, fixAnswerType, upyunService,
+						counter).start();
+			}
+
+		}
+
+		while (true) {
+			Util.sleep(5);
+			System.out.println("count: " + counter.get());
+			System.out.println("failList: \n" + JsonUtil.toPrettyJson(failList));
+		}
+
+	}
+
+	/**
+	 * 获取身份证和文件地址的对应关系
+	 * 
+	 * @param list
+	 * @return
+	 */
+	private Map<String, List<Map<String, Object>>> getIdentityMap(String filePath, String[] list) {
+		// 所有的图片
+		List<Map<String, Object>> fileMapList = new ArrayList<>();
+		for (String fileName : list) {
+			String fullFilePath = filePath + File.separator + fileName;
+			fileName = RegExUtils.replaceAll(fileName, "\\s+", "");
+			// 如果有-,则证明有多张图片
+			if (fileName.indexOf("-") != -1) {
+				String identityNumber = fileName.substring(0, fileName.indexOf("-"));
+				Map<String, Object> map = new HashMap<>();
+				map.put("identityNumber", identityNumber);
+				map.put("filePath", fullFilePath);
+				// 排序号
+				int sortOrder = 0;
+				if (fileName.indexOf("_") != -1) {
+					try {
+						sortOrder = Integer.parseInt(fileName.substring(fileName.indexOf("-") + 1,
+								fileName.indexOf("_")));
+					} catch (NumberFormatException e) {
+						e.printStackTrace();
+					}
+				} else {
+					try {
+						sortOrder = Integer.parseInt(fileName.substring(fileName.indexOf("-") + 1,
+								fileName.indexOf(".")));
+					} catch (NumberFormatException e) {
+						e.printStackTrace();
+					}
+				}
+				map.put("order", sortOrder);
+				fileMapList.add(map);
+			} else {
+				String identityNumber = "";
+				if (fileName.indexOf("_") > -1) {
+					identityNumber = fileName.substring(0, fileName.indexOf("_"));
+				} else {
+					identityNumber = fileName.substring(0, fileName.indexOf("."));
+				}
+				Map<String, Object> map = new HashMap<>();
+				map.put("identityNumber", identityNumber);
+				map.put("filePath", fullFilePath);
+				map.put("order", 0);
+				fileMapList.add(map);
+			}
+		}
+		Map<String, List<Map<String, Object>>> resultMap = new HashMap<>();
+		for (Map<String, Object> fm : fileMapList) {
+			List<Map<String, Object>> mapList = fileMapList.stream()
+					.filter(p -> p.get("identityNumber").equals(fm.get("identityNumber")))
+					.collect(Collectors.toList());
+			// 对文件进行排序
+			Collections.sort(mapList, new Comparator<Map<String, Object>>() {
+				public int compare(Map<String, Object> o1, Map<String, Object> o2) {
+					int map1value = Integer.parseInt(o1.get("order").toString());
+					int map2value = Integer.parseInt(o2.get("order").toString());
+					return map1value - map2value;
+				}
+			});
+			if (resultMap.get(fm.get("identityNumber")) == null) {
+				resultMap.put(fm.get("identityNumber").toString(), mapList);
+			}
+		}
+		return resultMap;
+	}
+}

+ 240 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/FixStudentAnswerThread.java

@@ -0,0 +1,240 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import com.mongodb.client.result.UpdateResult;
+
+import cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity.ExamRecordEntity;
+import cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity.FixAnswerType;
+import cn.com.qmth.examcloud.commons.helpers.Counter;
+import cn.com.qmth.examcloud.web.upyun.UpYunPathInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunPathEnvironmentInfo;
+import cn.com.qmth.examcloud.web.upyun.UpyunService;
+
+/**
+ * @Description TODO
+ * @Author THINKPAD
+ * @Date 2019/7/8 19:51
+ * @Version 1.0
+ */
+public class FixStudentAnswerThread extends Thread {
+	private Set<String> identityKeys;
+
+	JdbcTemplate jdbcTemplate;
+
+	MongoTemplate mongoTemplate;
+
+	Long examId;
+
+	String courseCode;
+
+	List<Map<String, Object>> failList;
+
+	Map<String, List<Map<String, Object>>> identityMap;
+
+	Integer order;
+
+	FixAnswerType fixAnswerType;
+
+	UpyunService upyunService;
+
+	Counter counter;
+
+	public FixStudentAnswerThread(JdbcTemplate jdbcTemplate, MongoTemplate mongoTemplate,
+			Set<String> identityKeys, Long examId, String courseCode,
+			List<Map<String, Object>> failList, Map<String, List<Map<String, Object>>> identityMap,
+			Integer order, FixAnswerType fixAnswerType, UpyunService upyunService,
+			Counter counter) {
+		this.identityKeys = identityKeys;
+		this.jdbcTemplate = jdbcTemplate;
+		this.mongoTemplate = mongoTemplate;
+		this.examId = examId;
+		this.courseCode = courseCode;
+		this.failList = failList;
+		this.identityMap = identityMap;
+		this.order = order;
+		this.fixAnswerType = fixAnswerType;
+		this.upyunService = upyunService;
+		this.counter = counter;
+	}
+
+	@Override
+	public void run() {
+		// 改成多线程
+		for (String identityNumber : identityKeys) {
+			List<ExamRecordEntity> recordList = getExamRecordList(examId, courseCode,
+					identityNumber);
+			// 学生的考试记录(一个学生可能有多次考试)
+			List<ExamRecordEntity> examRecords = getExamRecordListByIdentityNumber(identityNumber,
+					recordList);
+			if (examRecords == null || examRecords.isEmpty()) {
+				logErr(identityNumber, "找不到身份证号为:" + identityNumber + "的考试记录数据", failList);
+				continue;
+			}
+			for (ExamRecordEntity examRecord : examRecords) {
+				// 考试记录id
+				Long examRecordDataId = examRecord.getExamRecordDataId();
+				// 学生id
+				Long studnetId = examRecord.getStudentId();
+				// 如果没有找到考试记录id
+				try {
+					String newAnswer = "";
+					// 第一步,上传又拍云(多个文件的话,一个个追加)
+					// 当前学生的文件集合
+					List<Map<String, Object>> fileByIdnetityNumberList = identityMap
+							.get(identityNumber);
+					for (Map<String, Object> m : fileByIdnetityNumberList) {
+						this.counter.next();
+						UpyunPathEnvironmentInfo env = new UpyunPathEnvironmentInfo();
+						env.setUserId(studnetId.toString());
+						env.setExt1(examRecordDataId.toString());
+						env.setExt2(order.toString());
+						env.setFileSuffix(getFileSuffix(fixAnswerType));
+
+						String tempFilePath = m.get("filePath").toString();
+						String fullFilePath = tempFilePath;
+						System.out.println("正在上传文件:" + fullFilePath);
+						UpYunPathInfo upYunPathInfo = upyunService.writeFile(
+								getSiteId(fixAnswerType), env, new File(fullFilePath), true);
+						// 上传U盘云的完整地址
+						String upYunFileUrl = upYunPathInfo.getUrl();
+
+						newAnswer += buildFileAnswer(fixAnswerType, upYunFileUrl) + ",";
+					}
+					if (newAnswer.indexOf(",") > -1) {
+						newAnswer = newAnswer.substring(0, newAnswer.lastIndexOf(","));
+					}
+					// 第二步,更新答案
+					long res = updateAnswer(examRecordDataId, order, newAnswer);
+					if (res < 1) {
+						logErr(identityNumber, "更新答案失败", failList);
+						System.out.println("[Fail]:身份证号:" + identityNumber + ",考试记录id:"
+								+ examRecordDataId + ",答案更新失败");
+					} else {
+						System.out.println("[Success]:身份证号:" + identityNumber + ",考试记录id:"
+								+ examRecordDataId + ",答案更新成功");
+					}
+				} catch (Exception e) {
+					logErr(identityNumber, e.getMessage(), failList);
+					continue;
+				}
+			}
+		}
+	}
+
+	private String getFileSuffix(FixAnswerType fixAnswerType) {
+		if (fixAnswerType == FixAnswerType.PICTURE) {
+			return ".jpg";
+		} else {
+			return ".mp3";
+		}
+	}
+
+	public List<ExamRecordEntity> getExamRecordList(Long examId, String courseCode,
+			String identityNumber) {
+		String strSql = "select erd.id as examRecordDataId,er.exam_id as examId, "
+				+ "er.identity_number as identityNumber,c.`code` as courseCode,er.student_id as studentId "
+				+ "from ec_oe_exam_record er "
+				+ "inner join ec_oe_exam_record_data erd on er.id=erd.exam_record_id "
+				+ "inner join ec_b_course c on er.course_id=c.id " + "where er.exam_id=" + examId
+				+ " and c.`code`='" + courseCode + "'" + " and er.identity_number='"
+				+ identityNumber + "'";
+
+		java.util.List<Map<String, Object>> mapList = jdbcTemplate.queryForList(strSql);
+		java.util.List<ExamRecordEntity> resultList = new ArrayList<ExamRecordEntity>();
+		for (Map<String, Object> map : mapList) {
+			ExamRecordEntity entity = new ExamRecordEntity();
+			if (map.get("courseCode") != null) {
+				entity.setCourseCode(map.get("courseCode").toString());
+			}
+			if (map.get("examId") != null) {
+				entity.setExamId(Long.parseLong(map.get("examId").toString()));
+			}
+			if (map.get("examRecordDataId") != null) {
+				entity.setExamRecordDataId(Long.parseLong(map.get("examRecordDataId").toString()));
+			}
+			if (map.get("identityNumber") != null) {
+				entity.setIdentityNumber(map.get("identityNumber").toString());
+			}
+			if (map.get("studentId") != null) {
+				entity.setStudentId(Long.parseLong(map.get("studentId").toString()));
+			}
+			resultList.add(entity);
+		}
+		return resultList;
+	}
+
+	// 根据身份证号获取考试记录id(同一个学生可能有多次考试)
+	private List<ExamRecordEntity> getExamRecordListByIdentityNumber(String identityNumber,
+			List<ExamRecordEntity> examRecordList) {
+		List<ExamRecordEntity> resultList = examRecordList.stream()
+				.filter(record -> record.getIdentityNumber().equals(identityNumber))
+				.collect(Collectors.toList());
+		return resultList;
+	}
+
+	private String getSiteId(FixAnswerType fixAnswerType) {
+		if (fixAnswerType == FixAnswerType.PICTURE) {
+			return "photoAnswer";
+		} else {
+			return "audioAnswer";
+		}
+	}
+
+	/**
+	 * 更新指定试题的答案
+	 *
+	 * @param examRecordDataId
+	 * @param order
+	 * @param newAnswer
+	 * @return
+	 */
+	public long updateAnswer(Long examRecordDataId, Integer order, String newAnswer) {
+		// 查询相应的题目
+		Query query = Query.query(Criteria.where("examRecordDataId").is(examRecordDataId)
+				.and("examQuestionEntities.order").is(order));
+		Update update = Update.update("examQuestionEntities.$.studentAnswer", newAnswer);
+		UpdateResult upResult = mongoTemplate.updateFirst(query, update, "examRecordQuestions");
+		return upResult.getMatchedCount();
+	}
+
+	/**
+	 * 构建文件格式
+	 *
+	 * @param fileUrl
+	 * @return
+	 */
+	private String buildFileAnswer(FixAnswerType fixAnswerType, String fileUrl) {
+		if (fixAnswerType == FixAnswerType.PICTURE) {
+			StringBuilder sb = new StringBuilder();
+
+			sb.append("<div class='photo-answers-block'>");
+			sb.append("<a href='" + fileUrl + "' target='_blank' >");
+			sb.append("<img class='photo-answer' src='" + fileUrl + "!/both/200x200' />");
+			sb.append("</a>");
+			sb.append("</div>");
+			return sb.toString();
+		} else {
+			return fileUrl;
+		}
+	}
+
+	private void logErr(String identityNumber, String errMsg, List<Map<String, Object>> failList) {
+		Map<String, Object> errMap = new HashMap<>();
+		errMap.put("identityNumber", identityNumber);
+		errMap.put("err", errMsg);
+		failList.add(errMap);
+	}
+}

+ 193 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamQuestionEntity.java

@@ -0,0 +1,193 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity;
+
+import java.io.Serializable;
+
+/**
+ * @author chenken
+ * @date 2018/8/17 10:18
+ * @company QMTH
+ * @description 考生单题作答记录
+ */
+//@Document(collection="examRecordQuestion")
+public class ExamQuestionEntity implements Serializable{
+
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -6141069483774400912L;
+	
+//	@Id
+	private String id;
+	/**
+	 * 考试记录Data Id
+	 */
+    private Long examRecordDataId;
+    /**
+     * 大题号
+     */
+    private Integer mainNumber;
+    /**
+     * 原题ID
+     */
+    private String questionId;
+    /**
+     * 顺序
+     */
+    private Integer order;
+    /**
+     * 小题分数
+     */
+    private Double questionScore;
+    /**
+     * 小题类型
+     */
+    private QuestionType questionType;
+    /**
+     * 标准答案
+     */
+    private String correctAnswer;
+    /**
+     * 考生作答
+     */
+    private String studentAnswer;
+    /**
+     * 学生小题得分
+     */
+    private Double studentScore;
+    /**
+     * 是否作答
+     */
+    private Boolean isAnswer;
+    /**
+     * 是否标记
+     */
+    private Boolean isSign;
+    
+    /**
+	 * 选项排序值
+	 */
+	private Integer[] optionPermutation;
+	
+	/**
+	 * 音频播放次数
+	 */
+	private String audioPlayTimes;
+    
+	public ExamQuestionEntity() {}
+	
+	public String getId() {
+		return id;
+	}
+	public void setId(String id) {
+		this.id = id;
+	}
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+	public Integer getMainNumber() {
+		return mainNumber;
+	}
+	/**
+	 * 设置 大题号
+	 * @param mainNumber
+	 */
+	public void setMainNumber(Integer mainNumber) {
+		this.mainNumber = mainNumber;
+	}
+	public String getQuestionId() {
+		return questionId;
+	}
+	/**
+	 * 设置题库试题ID
+	 * @param questionId
+	 */
+	public void setQuestionId(String questionId) {
+		this.questionId = questionId;
+	}
+	public Integer getOrder() {
+		return order;
+	}
+	/**
+	 * 设置小题号
+	 * @param order
+	 */
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+	public String getStudentAnswer() {
+		return studentAnswer;
+	}
+	public void setStudentAnswer(String studentAnswer) {
+		this.studentAnswer = studentAnswer;
+	}
+	public Double getStudentScore() {
+		return studentScore;
+	}
+	/**
+	 * 设置考生得分
+	 * @param studentScore
+	 */
+	public void setStudentScore(Double studentScore) {
+		this.studentScore = studentScore;
+	}
+	public Double getQuestionScore() {
+		return questionScore;
+	}
+	/**
+	 * 设置试题分数
+	 * @param questionScore
+	 */
+	public void setQuestionScore(Double questionScore) {
+		this.questionScore = questionScore;
+	}
+	public QuestionType getQuestionType() {
+		return questionType;
+	}
+	/**
+	 * 设置题型
+	 * @param questionType
+	 */
+	public void setQuestionType(QuestionType questionType) {
+		this.questionType = questionType;
+	}
+	public Boolean getIsAnswer() {
+		return isAnswer;
+	}
+	public void setIsAnswer(Boolean isAnswer) {
+		this.isAnswer = isAnswer;
+	}
+	public Boolean getIsSign() {
+		return isSign;
+	}
+	public void setIsSign(Boolean isSign) {
+		this.isSign = isSign;
+	}
+
+	public String getCorrectAnswer() {
+		return correctAnswer;
+	}
+
+	public void setCorrectAnswer(String correctAnswer) {
+		this.correctAnswer = correctAnswer;
+	}
+
+	public Integer[] getOptionPermutation() {
+		return optionPermutation;
+	}
+
+	public void setOptionPermutation(Integer[] optionPermutation) {
+		this.optionPermutation = optionPermutation;
+	}
+
+	public String getAudioPlayTimes() {
+		return audioPlayTimes;
+	}
+
+	public void setAudioPlayTimes(String audioPlayTimes) {
+		this.audioPlayTimes = audioPlayTimes;
+	}
+	
+}

+ 55 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamRecordEntity.java

@@ -0,0 +1,55 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity;
+
+/**
+ * @Description TODO
+ * @Author THINKPAD
+ * @Date 2019/7/8 11:33
+ * @Version 1.0
+ */
+public class ExamRecordEntity {
+    private Long examId;
+    private String courseCode;
+    private Long examRecordDataId;
+    private String identityNumber;
+    private Long studentId;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+}

+ 62 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/ExamRecordQuestionsEntity.java

@@ -0,0 +1,62 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity;
+
+//import org.springframework.data.mongodb.core.mapping.Document;
+
+//import javax.persistence.Id;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author chenken
+ * @date 2018年9月3日 上午10:46:06
+ * @company QMTH
+ * @description 考试作答试题集合
+ */
+//@Document(collection = "examRecordQuestions")
+public class ExamRecordQuestionsEntity implements Serializable {
+    private static final long serialVersionUID = -1688201571728312142L;
+
+//    @Id
+    private String id;
+
+    private Long examRecordDataId;
+
+    private Date creationTime;
+
+    private List<ExamQuestionEntity> examQuestionEntities;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public Date getCreationTime() {
+        return creationTime;
+    }
+
+    public void setCreationTime(Date creationTime) {
+        this.creationTime = creationTime;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+    public List<ExamQuestionEntity> getExamQuestionEntities() {
+        return examQuestionEntities;
+    }
+
+    public void setExamQuestionEntities(
+            List<ExamQuestionEntity> examQuestionEntities) {
+        this.examQuestionEntities = examQuestionEntities;
+    }
+
+}

+ 15 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/FixAnswerType.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity;
+
+/**
+ * 修复答案的类型
+ */
+public enum FixAnswerType {
+    /**
+     * 图片
+     */
+    PICTURE,
+    /**
+     * 音频
+     */
+    AUDIO
+}

+ 60 - 0
src/main/java/cn/com/qmth/dp/examcloud/oe/modules/update_student_answer/entity/QuestionType.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.dp.examcloud.oe.modules.update_student_answer.entity;
+
+/**
+ * 题型
+ *
+ * @author WANGWEI
+ * @date 2018年8月15日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public enum QuestionType {
+
+	/**
+	 * 单选题
+	 */
+	SINGLE_CHOICE("单选题"),
+
+	/**
+	 * 多选题
+	 */
+	MULTIPLE_CHOICE("多选题"),
+
+	/**
+	 * 填空题
+	 */
+	FILL_UP("填空题"),
+
+	/**
+	 * 问答题
+	 */
+	ESSAY("问答题"),
+
+	/**
+	 * 判断题
+	 */
+	TRUE_OR_FALSE("判断题");
+
+	// ===========================================================================
+
+	/**
+	 * 描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param desc
+	 */
+	private QuestionType(String desc) {
+		this.desc = desc;
+	}
+
+	/**
+	 * @return the desc
+	 */
+	public String getDesc() {
+		return desc;
+	}
+
+}

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

@@ -0,0 +1,56 @@
+spring.application.name=DATA-PROCESSING-OE
+server.port=2008
+
+spring.jpa.hibernate.ddl-auto=validate
+spring.main.allow-bean-definition-overriding=true
+
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+#datasource
+dsurl.host=192.168.10.30
+dsurl.port=3306
+dsurl.database=exam_cloud_test
+spring.datasource.username=exam_cloud_test
+spring.datasource.password=exam_cloud_test
+
+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.max-active=15
+spring.datasource.druid.min-idle=5
+spring.datasource.druid.max-wait=2000
+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
+
+#mongodb
+#mguri.username=exam_cloud
+#mguri.password=XXX
+mguri.hostAndPortGroup=192.168.10.30:27017
+mguri.database=examcloud-core-oe
+#mguri.maxPoolSize=10
+
+#spring.data.mongodb.uri=mongodb://${mguri.username}:${mguri.password}@${mguri.hostAndPortGroup}/${mguri.database}?replicaSet=mgset-2892097&maxPoolSize=${mguri.maxPoolSize}&maxIdleTimeMS=6000
+spring.data.mongodb.uri=mongodb://${mguri.hostAndPortGroup}/${mguri.database}
+
+spring.data.mongodb.grid-fs-database=examcloud-core-oe
+spring.data.mongodb.database=examcloud-core-oe
+
+#upyun
+$upyun.site.1.bucketName=exam-cloud-test
+$upyun.site.1.userName=examcloud
+$upyun.site.1.domain=https://ecs-test-static.qmth.com.cn 
+$upyun.site.1.password=ecs87863577!@#
+
+
+
+
+
+
+

+ 1 - 0
src/main/resources/classpath.location

@@ -0,0 +1 @@
+classpath 定位文件

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

@@ -0,0 +1,52 @@
+<?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 | %m | %l%n" />
+		</Console>
+		<!-- debug 日志 -->
+		<RollingFile name="DEBUG_APPENDER" 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>
+				<TimeBasedTriggeringPolicy interval="1" />
+				<SizeBasedTriggeringPolicy size="100 MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+				<Delete basePath="./logs/debug" maxDepth="1">
+					<IfFileName glob="debug-*.log">
+						<IfAccumulatedFileSize exceeds="2 GB" />
+					</IfFileName>
+				</Delete>
+			</DefaultRolloverStrategy>
+		</RollingFile>
+		<!-- result -->
+		<RollingFile name="RESULT_APPENDER" filePattern="./logs/result/result-%i.txt">
+			<PatternLayout pattern="%m%n" />
+			<Policies>
+				<SizeBasedTriggeringPolicy size="512 MB" />
+			</Policies>
+		</RollingFile>
+	</Appenders>
+
+	<Loggers>
+
+		<Logger name="cn.com.qmth" level="INFO" additivity="false">
+			<AppenderRef ref="DEBUG_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="RESULT_LOGGER" level="DEBUG" additivity="false">
+			<AppenderRef ref="RESULT_APPENDER" />
+		</Logger>
+
+		<Logger name="org.quartz.core" level="ERROR" />
+
+		<Root level="ERROR">
+			<AppenderRef ref="Console" />
+			<AppenderRef ref="DEBUG_APPENDER" />
+		</Root>
+
+	</Loggers>
+
+</Configuration>

+ 87 - 0
src/main/resources/temp.txt

@@ -0,0 +1,87 @@
+INSERT INTO ec_oe_exam_record_data (
+  id,
+  root_org_id,
+  org_id,
+  exam_id,
+  exam_type,
+  course_id,
+  course_level,
+  exam_student_id,
+  student_id,
+  student_code,
+  student_name,
+  identity_number,
+  base_paper_id,
+  paper_struct_id,
+  paper_type,
+  exam_record_questions_id,
+  exam_record_status,
+  start_time,
+  used_exam_time,
+  is_illegality,
+  is_warn,
+  is_audit,
+  is_reexamine,
+  is_continued,
+  continued_count,
+  is_exceed,
+  is_all_objective_paper,
+  clean_time,
+  end_time,
+  exam_order,
+  baidu_face_liveness_success_percent,
+  face_failed_count,
+  face_landmark_val,
+  face_stranger_count,
+  face_success_count,
+  face_success_percent,
+  face_total_count,
+  face_verify_result,
+  info_collector,
+  creation_time,
+  update_time
+)
+VALUES
+	(
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	?,
+	? 
+	)

+ 33 - 0
src/main/resources/upyun.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<sites>
+
+	<site>
+		<!--ID -->
+		<id>test</id>
+		<!--名称 -->
+		<name>测试照片</name>
+		<!--又拍云账户ID标识 -->
+		<upyunId>1</upyunId>
+		<!--文件大小限制(IO流限制) -->
+		<maxSize>1M</maxSize>
+		<!--又拍云存储路径 -->
+		<path>/test/${rootOrgId}/${timeMillis}${fileSuffix}</path>
+	</site>
+
+	<site>
+		<id>photoAnswer</id>
+		<name>学生作答照片</name>
+		<upyunId>1</upyunId>
+		<maxSize>1M</maxSize>
+		<path>/oe-answer-file/${userId}/${ext1}/${ext2}/${userId}_${ext1}_${ext2}_${timeMillis}${fileSuffix}</path>
+	</site>
+
+	<site>
+		<id>audioAnswer</id>
+		<name>学生作答音频</name>
+		<upyunId>1</upyunId>
+		<maxSize>1M</maxSize>
+		<path>/oe-answer-file/${userId}/${ext1}/${ext2}/${userId}_${ext1}_${ext2}_${timeMillis}${fileSuffix}</path>
+	</site>
+
+</sites>