Sfoglia il codice sorgente

Merge remote-tracking branch 'remotes/origin/hotfixes_v5.0.5_20250211_updates' into dev_v5.0.6

xiatian 5 mesi fa
parent
commit
748748f12f

+ 520 - 479
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/FileUtil.java

@@ -1,480 +1,521 @@
-/*
- * *************************************************
- * Copyright (c) 2018 QMTH. All Rights Reserved.
- * Created by Deason on 2018-07-12 15:31:10.
- * *************************************************
- */
-
-package cn.com.qmth.examcloud.core.questions.base.converter.utils;
-
-import cn.com.qmth.examcloud.core.questions.base.IoUtils;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
-
-public class FileUtil {
-    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
-
-    /**
-     * 分隔文件
-     *
-     * @param sourcePath 原文件
-     * @param targetPath 目标文件
-     * @param n          跳过的字节数
-     * @return
-     */
-    public static File cutFile(String sourcePath, String targetPath, int n) {
-        File file = new File(sourcePath);
-        File newFile = new File(targetPath);
-
-        try (
-                FileInputStream fis = new FileInputStream(file);
-                InputStream is = new BufferedInputStream(fis);
-                OutputStream os = new FileOutputStream(newFile);
-        ) {
-
-            //从n个字节开始读,注意中文是两个字节
-            fis.skip(n);
-
-            //指定文件位置读取的文件流,存入新文件
-            byte buffer[] = new byte[4 * 1024];
-            int len;
-            while ((len = is.read(buffer)) != -1) {
-                os.write(buffer, 0, len);
-            }
-
-            os.flush();
-            return newFile;
-        } catch (FileNotFoundException e) {
-            log.error(e.getMessage(), e);
-        } catch (IOException e) {
-            log.error(e.getMessage(), e);
-        }
-        return null;
-    }
-
-    /**
-     * 读取文件前面部分N个字节
-     *
-     * @param path       文件路径
-     * @param headerSize 头信息字节数(必须2的倍数)
-     * @param signSize   签名信息字节数
-     * @return
-     */
-    public static String[] readFileHeader(String path, int headerSize, int signSize) {
-        int n = headerSize / 2;
-        String[] codes = new String[n + 1];
-
-        File file = new File(path);
-        try (
-                FileInputStream fis = new FileInputStream(file);
-                DataInputStream ois = new DataInputStream(fis);
-        ) {
-            //分n次读取文件(n * 2)个字节
-            for (int i = 0; i < n; i++) {
-                codes[i] = String.valueOf(ois.readShort());
-            }
-
-            if (signSize > 0) {
-                StringBuilder ss = new StringBuilder();
-                for (int i = 0; i < signSize; i++) {
-                    ss.append((char) ois.readByte());
-                }
-                codes[2] = ss.toString();
-            }
-        } catch (FileNotFoundException e) {
-            log.error(e.getMessage(), e);
-        } catch (IOException e) {
-            log.error(e.getMessage(), e);
-        }
-
-        return codes;
-    }
-
-    /**
-     * 读取文件内容
-     *
-     * @param file
-     * @return
-     */
-    public static String readFileContent(File file) {
-        StringBuilder content = new StringBuilder();
-        InputStreamReader streamReader = null;
-        BufferedReader bufferedReader = null;
-        try {
-            String encoding = "UTF-8";
-            if (file.exists() && file.isFile()) {
-                streamReader = new InputStreamReader(new FileInputStream(file), encoding);
-                bufferedReader = new BufferedReader(streamReader);
-                String line;
-                while ((line = bufferedReader.readLine()) != null) {
-                    content.append(line);
-                }
-            }
-        } catch (Exception e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            IOUtils.closeQuietly(streamReader);
-            IOUtils.closeQuietly(bufferedReader);
-        }
-        return content.toString();
-    }
-
-    /**
-     * 在文件流前面追加头信息和签名信息,并生成新的“.tk”文件
-     */
-    public static boolean appendHeader(File file, short[] headers, String sign) {
-        if (file == null || !file.exists()) {
-            return false;
-        }
-
-        if (!file.isFile()) {
-            return false;
-        }
-
-        FileInputStream fis = null;
-        InputStream is = null;
-        FileOutputStream fos = null;
-        DataOutputStream dos = null;
-        try {
-            //创建临时文件
-            String baseFilePath = file.getAbsolutePath();
-            String targetFilePath = getFilePathName(baseFilePath) + ".tk";
-            File newFile = new File(targetFilePath);
-            fos = new FileOutputStream(newFile);
-            dos = new DataOutputStream(fos);
-
-            //写入头信息
-            for (short s : headers) {
-                dos.writeShort(s);
-            }
-            if (sign != null && !"".equals(sign)) {
-                //写入签名信息
-                dos.write(sign.getBytes("ISO-8859-1"));
-            }
-
-            //在临时文件中追加原始文件内容
-            fis = new FileInputStream(file);
-            is = new BufferedInputStream(fis);
-
-            byte buffer[] = new byte[4 * 1024];
-            int len;
-            while ((len = is.read(buffer)) != -1) {
-                dos.write(buffer, 0, len);
-            }
-            dos.flush();
-            return true;
-        } catch (FileNotFoundException e) {
-            log.error(e.getMessage(), e);
-        } catch (IOException e) {
-            log.error(e.getMessage(), e);
-        } finally {
-            IOUtils.closeQuietly(is);
-            IOUtils.closeQuietly(fis);
-            IOUtils.closeQuietly(dos);
-            IOUtils.closeQuietly(fos);
-        }
-        return false;
-    }
-
-    /**
-     * 生成日期目录路径
-     */
-    public static String generateDateDir() {
-        return "/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/";
-    }
-
-    public static String generateFileName() {
-        return UUID.randomUUID().toString().replaceAll("-", "");
-    }
-
-    public static String generateDateName() {
-        return new SimpleDateFormat("yyMMddHHmmss").format(new Date());
-    }
-
-    /**
-     * 获取文件后缀名(包含".")
-     */
-    public static String getFileSuffix(String fileName) {
-        if (fileName == null) {
-            return "";
-        }
-        int index = fileName.lastIndexOf(".");
-        if (index > -1) {
-            return fileName.substring(index).toLowerCase();
-        }
-        return "";
-    }
-
-    /**
-     * 获取无后缀的文件名
-     *
-     * @param fileName 示例:../xxx/abc.xx
-     * @return 示例:../xxx/abc
-     */
-    public static String getFilePathName(String fileName) {
-        if (fileName != null && fileName.length() > 0) {
-            int index = fileName.lastIndexOf(".");
-            if (index != -1) {
-                return fileName.substring(0, index);
-            }
-        }
-        return "";
-    }
-
-    /**
-     * 创建文件目录
-     */
-    public static boolean makeDirs(String path) {
-        if (path == null || "".equals(path)) {
-            return false;
-        }
-        File folder = new File(path);
-        if (!folder.exists()) {
-            return folder.mkdirs();
-        }
-        return true;
-    }
-
-    /**
-     * 保存字符串到文件中
-     */
-    public static void saveAsFile(String path, String content) {
-        saveAsFile(path, content, null);
-    }
-
-    public static void saveAsFile(String path, String content, String encoding) {
-        if (path == null || content == null) {
-            return;
-        }
-
-        if (encoding == null) {
-            encoding = "UTF-8";
-        }
-
-        File file = new File(path);
-        if (!file.exists()) {
-            if (FileUtil.makeDirs(file.getParent())) {
-                boolean ok = IoUtils.createFile(file);
-                if (!ok) {
-                    throw new RuntimeException("文件创建失败!");
-                }
-            }
-        }
-
-        try (
-                FileOutputStream fos = new FileOutputStream(file);
-                OutputStreamWriter write = new OutputStreamWriter(fos, encoding);
-                BufferedWriter bw = new BufferedWriter(write);
-        ) {
-            bw.write(content);
-            bw.flush();
-            log.info("save as file success. " + path);
-        } catch (IOException e) {
-            log.error("save as file error. " + path);
-        }
-    }
-
-    /**
-     * 解压文件
-     *
-     * @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();
-        }
-
-        List<File> result = new LinkedList<>();
-
-        try (ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));) {
-
-            Enumeration entries = zip.entries();
-            while (entries.hasMoreElements()) {
-                ZipEntry entry = (ZipEntry) entries.nextElement();
-
-                //Linux中需要替换掉路径的反斜杠
-                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
-
-                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 void deleteFolder(String path) {
-
-		File file = new File(path);
-		if (file.exists()) {
-			if (file.isFile()) {
-				deleteFile(path);
-			} else {
-				deleteDirectory(path);
-			}
-		}
-	}
-
-	public static void deleteFile(String path) {
-		File file = new File(path);
-		if (file.isFile() && file.exists()) {
-			file.delete();
-		}
-	}
-
-	public static void deleteDirectory(String path) {
-		if (!path.endsWith(File.separator)) {
-			path = path + File.separator;
-		}
-		File dirFile = new File(path);
-		if (!dirFile.exists() || !dirFile.isDirectory()) {
-			return;
-		}
-		File[] files = dirFile.listFiles();
-		if (files != null) {
-			for (int i = 0; i < files.length; i++) {
-				if (files[i].isFile()) {
-					deleteFile(files[i].getAbsolutePath());
-				} else {
-					deleteDirectory(files[i].getAbsolutePath());
-				}
-			}
-		}
-
-		dirFile.delete();
-	}
+/*
+ * ************************************************* Copyright (c) 2018 QMTH.
+ * All Rights Reserved. Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.utils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.examcloud.core.questions.base.IoUtils;
+
+public class FileUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+    public static String fileToBase64(InputStream is) {
+        byte[] base64Byte;
+        try {
+            base64Byte = new byte[0];
+            byte[] imgByte;
+            imgByte = IOUtils.toByteArray(is);
+            base64Byte = Base64.encodeBase64(imgByte);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return "data:image/jpeg;base64," + new String(base64Byte);
+    }
+
+    /**
+     * 分隔文件
+     *
+     * @param sourcePath
+     *            原文件
+     * @param targetPath
+     *            目标文件
+     * @param n
+     *            跳过的字节数
+     * @return
+     */
+    public static File cutFile(String sourcePath, String targetPath, int n) {
+        File file = new File(sourcePath);
+        File newFile = new File(targetPath);
+
+        try (FileInputStream fis = new FileInputStream(file);
+                InputStream is = new BufferedInputStream(fis);
+                OutputStream os = new FileOutputStream(newFile);) {
+
+            // 从n个字节开始读,注意中文是两个字节
+            fis.skip(n);
+
+            // 指定文件位置读取的文件流,存入新文件
+            byte buffer[] = new byte[4 * 1024];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                os.write(buffer, 0, len);
+            }
+
+            os.flush();
+            return newFile;
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * 读取文件前面部分N个字节
+     *
+     * @param path
+     *            文件路径
+     * @param headerSize
+     *            头信息字节数(必须2的倍数)
+     * @param signSize
+     *            签名信息字节数
+     * @return
+     */
+    public static String[] readFileHeader(String path, int headerSize, int signSize) {
+        int n = headerSize / 2;
+        String[] codes = new String[n + 1];
+
+        File file = new File(path);
+        try (FileInputStream fis = new FileInputStream(file); DataInputStream ois = new DataInputStream(fis);) {
+            // 分n次读取文件(n * 2)个字节
+            for (int i = 0; i < n; i++) {
+                codes[i] = String.valueOf(ois.readShort());
+            }
+
+            if (signSize > 0) {
+                StringBuilder ss = new StringBuilder();
+                for (int i = 0; i < signSize; i++) {
+                    ss.append((char) ois.readByte());
+                }
+                codes[2] = ss.toString();
+            }
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return codes;
+    }
+
+    /**
+     * 读取文件内容
+     *
+     * @param file
+     * @return
+     */
+    public static String readFileContent(File file) {
+        StringBuilder content = new StringBuilder();
+        InputStreamReader streamReader = null;
+        BufferedReader bufferedReader = null;
+        try {
+            String encoding = "UTF-8";
+            if (file.exists() && file.isFile()) {
+                streamReader = new InputStreamReader(new FileInputStream(file), encoding);
+                bufferedReader = new BufferedReader(streamReader);
+                String line;
+                while ((line = bufferedReader.readLine()) != null) {
+                    content.append(line);
+                }
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(streamReader);
+            IOUtils.closeQuietly(bufferedReader);
+        }
+        return content.toString();
+    }
+
+    /**
+     * 在文件流前面追加头信息和签名信息,并生成新的“.tk”文件
+     */
+    public static boolean appendHeader(File file, short[] headers, String sign) {
+        if (file == null || !file.exists()) {
+            return false;
+        }
+
+        if (!file.isFile()) {
+            return false;
+        }
+
+        FileInputStream fis = null;
+        InputStream is = null;
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            // 创建临时文件
+            String baseFilePath = file.getAbsolutePath();
+            String targetFilePath = getFilePathName(baseFilePath) + ".tk";
+            File newFile = new File(targetFilePath);
+            fos = new FileOutputStream(newFile);
+            dos = new DataOutputStream(fos);
+
+            // 写入头信息
+            for (short s : headers) {
+                dos.writeShort(s);
+            }
+            if (sign != null && !"".equals(sign)) {
+                // 写入签名信息
+                dos.write(sign.getBytes("ISO-8859-1"));
+            }
+
+            // 在临时文件中追加原始文件内容
+            fis = new FileInputStream(file);
+            is = new BufferedInputStream(fis);
+
+            byte buffer[] = new byte[4 * 1024];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                dos.write(buffer, 0, len);
+            }
+            dos.flush();
+            return true;
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(is);
+            IOUtils.closeQuietly(fis);
+            IOUtils.closeQuietly(dos);
+            IOUtils.closeQuietly(fos);
+        }
+        return false;
+    }
+
+    /**
+     * 生成日期目录路径
+     */
+    public static String generateDateDir() {
+        return "/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/";
+    }
+
+    public static String generateFileName() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+
+    public static String generateDateName() {
+        return new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+    }
+
+    /**
+     * 获取文件后缀名(包含".")
+     */
+    public static String getFileSuffix(String fileName) {
+        if (fileName == null) {
+            return "";
+        }
+        int index = fileName.lastIndexOf(".");
+        if (index > -1) {
+            return fileName.substring(index).toLowerCase();
+        }
+        return "";
+    }
+
+    /**
+     * 获取无后缀的文件名
+     *
+     * @param fileName
+     *            示例:../xxx/abc.xx
+     * @return 示例:../xxx/abc
+     */
+    public static String getFilePathName(String fileName) {
+        if (fileName != null && fileName.length() > 0) {
+            int index = fileName.lastIndexOf(".");
+            if (index != -1) {
+                return fileName.substring(0, index);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 创建文件目录
+     */
+    public static boolean makeDirs(String path) {
+        if (path == null || "".equals(path)) {
+            return false;
+        }
+        File folder = new File(path);
+        if (!folder.exists()) {
+            return folder.mkdirs();
+        }
+        return true;
+    }
+
+    /**
+     * 保存字符串到文件中
+     */
+    public static void saveAsFile(String path, String content) {
+        saveAsFile(path, content, null);
+    }
+
+    public static void saveAsFile(String path, String content, String encoding) {
+        if (path == null || content == null) {
+            return;
+        }
+
+        if (encoding == null) {
+            encoding = "UTF-8";
+        }
+
+        File file = new File(path);
+        if (!file.exists()) {
+            if (FileUtil.makeDirs(file.getParent())) {
+                boolean ok = IoUtils.createFile(file);
+                if (!ok) {
+                    throw new RuntimeException("文件创建失败!");
+                }
+            }
+        }
+
+        try (FileOutputStream fos = new FileOutputStream(file);
+                OutputStreamWriter write = new OutputStreamWriter(fos, encoding);
+                BufferedWriter bw = new BufferedWriter(write);) {
+            bw.write(content);
+            bw.flush();
+            log.info("save as file success. " + path);
+        } catch (IOException e) {
+            log.error("save as file error. " + path);
+        }
+    }
+
+    /**
+     * 解压文件
+     *
+     * @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();
+        }
+
+        List<File> result = new LinkedList<>();
+
+        try (ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));) {
+
+            Enumeration entries = zip.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+
+                // Linux中需要替换掉路径的反斜杠
+                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
+
+                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 void deleteFolder(String path) {
+
+        File file = new File(path);
+        if (file.exists()) {
+            if (file.isFile()) {
+                deleteFile(path);
+            } else {
+                deleteDirectory(path);
+            }
+        }
+    }
+
+    public static void deleteFile(String path) {
+        File file = new File(path);
+        if (file.isFile() && file.exists()) {
+            file.delete();
+        }
+    }
+
+    public static void deleteDirectory(String path) {
+        if (!path.endsWith(File.separator)) {
+            path = path + File.separator;
+        }
+        File dirFile = new File(path);
+        if (!dirFile.exists() || !dirFile.isDirectory()) {
+            return;
+        }
+        File[] files = dirFile.listFiles();
+        if (files != null) {
+            for (int i = 0; i < files.length; i++) {
+                if (files[i].isFile()) {
+                    deleteFile(files[i].getAbsolutePath());
+                } else {
+                    deleteDirectory(files[i].getAbsolutePath());
+                }
+            }
+        }
+
+        dirFile.delete();
+    }
 }

+ 311 - 321
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/ImportPaperService.java

@@ -162,13 +162,12 @@ public class ImportPaperService {
      * @throws Exception
      */
 
-    public Paper importPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
+    public Paper importPaper(Paper paper, User user, File file, UserDataRule ud) throws Exception {
         paperService.checkPaperNameNew(paper.getName(), user.getRootOrgId().toString());
         if (file.getName().endsWith(DOC_SUFFIX)) {
-            return processImportPaper(paper, user, file,ud);
+            return processImportPaper(paper, user, file, ud);
         } else if (file.getName().endsWith(JSON_SUFFIX)) {
-             return importPaperFromJsonService.processImportPaper(paper, user,
-             file);
+            return importPaperFromJsonService.processImportPaper(paper, user, file);
         } else {
             throw new PaperException("导入文件只支持.docx和.json类型");
         }
@@ -265,169 +264,171 @@ public class ImportPaperService {
      * @throws Exception
      */
 
-    public Paper processImportPaper(Paper paper, User user, File file,UserDataRule ud) throws Exception {
+    public Paper processImportPaper(Paper paper, User user, File file, UserDataRule ud) throws Exception {
         try {
-        	if (ud.assertEmptyQueryResult()) {
-    			throw new StatusException("没有数据权限");
-    		}
+            if (ud.assertEmptyQueryResult()) {
+                throw new StatusException("没有数据权限");
+            }
 
             // 得到前台的课程代码
             String courseNo = paper.getCourseNo();
 
-    		Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
-    		if(ud.assertNeedQueryRefIds()&&!ud.stringRefIds().contains(course.getId())) {
-    			throw new StatusException("没有数据权限");
-    		}
-
-			WordprocessingMLPackage wordMLPackage;
-			WordprocessingMLPackage tmpWordMlPackage;
-			// WordprocessingMLPackage writePkg;
-			ImportPaperCheck importPaperCheck = new ImportPaperCheck();
-			wordMLPackage = WordprocessingMLPackage.load(file);
-			// 初始化图片路径
-			DocxProcessUtil.initPkgImage(wordMLPackage);
-			// 深拷贝临时pkg与最终写入数据库pkg
-			tmpWordMlPackage = DocxProcessUtil.getTmpPackage(wordMLPackage);
-			// 获取word文档中所有段落
-			List<Object> pList = DocxProcessUtil.getAllElementFromObject(wordMLPackage.getMainDocumentPart(), P.class);
-			// 设置试卷
-			initPaper(paper, paper.getName(), user);
-			// 创建空大题类
-			PaperDetail paperDetail = null;
-			// 创建大题集合
-			List<PaperDetail> paperDetails = new ArrayList<>();
-			// 创建小题集合
-			List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
-			// 创建试题集合
-			List<Question> questions = new ArrayList<>();
-			// 大题号
-			int mainQuesNum = 0;
-			// 小题号
-			int subQuesNum = 0;
-			// 大题下的小题序号
-			int errorQuesNum = 0;
-			for (int i = 0; i < pList.size(); i++) {
-				P p = (P) pList.get(i);
-				String pText = DocxProcessUtil.getPText(p);
-				if (StringUtils.isEmpty(pText)) {
-					// 跳过空白段落
-					continue;
-				} else if (isMainQuesHeader(pText)) {
-					// 处理大题头信息
-					processMainQuesHeader(pList, importPaperCheck.getIndex(), importPaperCheck, courseNo);
-					// 校验大题头信息
-					if (!checkQuesHeader(importPaperCheck)) {
-						throw new PaperException(importPaperCheck.getErrorInfo());
-					}
-					// 创建大题类
-					paperDetail = new PaperDetail();
-					// 设置大题类
-					initQuesHeader(paper, paperDetail, paperDetails, ++mainQuesNum, importPaperCheck);
-					// 设置当前索引,防止多余循环
-					i = importPaperCheck.getIndex() - 1;
-					errorQuesNum = 0;
-				} else if (DocxProcessUtil.isNumPr(p)) {
-					// 检测到序列
-					importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + ImportPaperMsg.errMsg_10);
-					throw new PaperException(importPaperCheck.getErrorInfo());
-
-				} else if ((pText.matches("^\\d{1,}\\.[\\s\\S]*") && !pText.startsWith(ImportPaperMsg.left_bracket))
-						|| (isNested(importPaperCheck) && pText.startsWith(ImportPaperMsg.nestedQuestion_start))) {
-					if (paperDetail == null) {
-						throw new PaperException("导入文件格式有误,必须有大题头信息,且以 [ 开头!");
-					}
-					++errorQuesNum;
-					++subQuesNum;
-					// 处理试题
-					// 创建小题类和试题类
-					PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
-					Question question = new Question();
-					// 初始化小题类和试题类
-					initPaperDetail(paper, paperDetail, paperDetailUnit, question, subQuesNum, importPaperCheck);
-					// 处理客观题
-					if (importPaperCheck.getQuesType().equals(ImportPaperMsg.singleSelection)
-							|| importPaperCheck.getQuesType().equals(ImportPaperMsg.multipleSelection)) {
-						// 处理题干
-						processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
-								tmpWordMlPackage, errorQuesNum);
-						// 处理选项
-						processQuesOption(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
-								tmpWordMlPackage, errorQuesNum);
-						// 处理尾信息
-						processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
-								importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
-						// 处理选择题的option--chenken 20170425
-						processSelectOption(question);
-					} else if (importPaperCheck.getQuesType().equals(ImportPaperMsg.nestedQuestion_word)) {
-						// 处理套题
-						processNestedQues(pList, importPaperCheck.getIndex(), question, paperDetailUnit,
-								importPaperCheck, tmpWordMlPackage, paper);
-					} else {
-						// 处理其他题型
-						processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
-								tmpWordMlPackage, errorQuesNum);
-						processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
-								importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
-						// 填空题空格校验
-						if (question.getQuestionType().getName().equals(QuesStructType.FILL_BLANK_QUESTION.getName())) {
-							if (!StringUtils.isBlank(question.getQuesAnswer())) {
-								processFill(question, paperDetailUnit, importPaperCheck, subQuesNum, errorQuesNum);
-							}
-						}
-					}
-					// 设置WordMlPackage二进制数据
-					//                byte[] pkgByte = getZipResource(wordMLPackage, question);
-					//                QuestionPkgPath quesPkgPath = quesPkgPathRepo.save(new QuestionPkgPath(pkgByte));
-					//                question.setQuesPkgPathId(quesPkgPath.getId());
-					//                pkgByte = null;
-					//                quesPkgPath.setQuesPkg(null);
-					// 设置question与Unit集合数据
-					question.setCourse(paper.getCourse());
-					question.setOrgId(user.getRootOrgId().toString());
-					questions.add(question);
-					paperDetailUnits.add(paperDetailUnit);
-					// 设置当前索引,防止多余循环
-					i = importPaperCheck.getIndex() - 1;
-				} else if (paperDetail == null) {
-					throw new PaperException("导入文件格式有误,必须有大题头信息,且以[试题分类]开头!");
-				} else {
-					String errorMsg = pText.length() > 10 ? pText.substring(0, 10) : pText;
-					if (pText.startsWith(ImportPaperMsg.left_bracket)) {
-						throw new PaperException(
-								errorMsg + ",标签格式不正确!正确标签格式:[套题]、[套题数量]、[小题分数]、[答案]、[难度]、[一级属性]、[二级属性]、[公开度]、[小题型]");
-					}
-					throw new PaperException(
-							importPaperCheck.getQuesName() + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_12);
-				}
-				if (!StringUtils.isEmpty(importPaperCheck.getErrorInfo())) {
-					throw new PaperException(importPaperCheck.getErrorInfo());
-				}
-			}
-			if (paperDetails.size() == 0 || paperDetailUnits.size() == 0) {
-				throw new PaperException("导入文件格式有误!");
-			}
-			paper.setPaperDetailCount(mainQuesNum);
-			// 保存导入试卷信息
-			paper = savePaper(paper, paperDetails, paperDetailUnits, questions, importPaperCheck);
-			clearPaper(paperDetails, paperDetailUnits, questions);
-			wordMLPackage = null;
-			tmpWordMlPackage = null;
-		} finally {
-			FileUtils.deleteQuietly(file);
-		}
+            Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
+            if (ud.assertNeedQueryRefIds() && !ud.stringRefIds().contains(course.getId())) {
+                throw new StatusException("没有数据权限");
+            }
+
+            WordprocessingMLPackage wordMLPackage;
+            WordprocessingMLPackage tmpWordMlPackage;
+            // WordprocessingMLPackage writePkg;
+            ImportPaperCheck importPaperCheck = new ImportPaperCheck();
+            wordMLPackage = WordprocessingMLPackage.load(file);
+            // 初始化图片路径
+            DocxProcessUtil.initPkgImage(wordMLPackage);
+            // 深拷贝临时pkg与最终写入数据库pkg
+            tmpWordMlPackage = DocxProcessUtil.getTmpPackage(wordMLPackage);
+            // 获取word文档中所有段落
+            List<Object> pList = DocxProcessUtil.getAllElementFromObject(wordMLPackage.getMainDocumentPart(), P.class);
+            // 设置试卷
+            initPaper(paper, paper.getName(), user);
+            // 创建空大题类
+            PaperDetail paperDetail = null;
+            // 创建大题集合
+            List<PaperDetail> paperDetails = new ArrayList<>();
+            // 创建小题集合
+            List<PaperDetailUnit> paperDetailUnits = new ArrayList<>();
+            // 创建试题集合
+            List<Question> questions = new ArrayList<>();
+            // 大题号
+            int mainQuesNum = 0;
+            // 小题号
+            int subQuesNum = 0;
+            // 大题下的小题序号
+            int errorQuesNum = 0;
+            for (int i = 0; i < pList.size(); i++) {
+                P p = (P) pList.get(i);
+                String pText = DocxProcessUtil.getPText(p);
+                if (StringUtils.isEmpty(pText)) {
+                    // 跳过空白段落
+                    continue;
+                } else if (isMainQuesHeader(pText)) {
+                    // 处理大题头信息
+                    processMainQuesHeader(pList, importPaperCheck.getIndex(), importPaperCheck, courseNo);
+                    // 校验大题头信息
+                    if (!checkQuesHeader(importPaperCheck)) {
+                        throw new PaperException(importPaperCheck.getErrorInfo());
+                    }
+                    // 创建大题类
+                    paperDetail = new PaperDetail();
+                    // 设置大题类
+                    initQuesHeader(paper, paperDetail, paperDetails, ++mainQuesNum, importPaperCheck);
+                    // 设置当前索引,防止多余循环
+                    i = importPaperCheck.getIndex() - 1;
+                    errorQuesNum = 0;
+                } else if (DocxProcessUtil.isNumPr(p)) {
+                    // 检测到序列
+                    importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + ImportPaperMsg.errMsg_10);
+                    throw new PaperException(importPaperCheck.getErrorInfo());
+
+                } else if ((pText.matches("^\\d{1,}\\.[\\s\\S]*") && !pText.startsWith(ImportPaperMsg.left_bracket))
+                        || (isNested(importPaperCheck) && pText.startsWith(ImportPaperMsg.nestedQuestion_start))) {
+                    if (paperDetail == null) {
+                        throw new PaperException("导入文件格式有误,必须有大题头信息,且以 [ 开头!");
+                    }
+                    ++errorQuesNum;
+                    ++subQuesNum;
+                    // 处理试题
+                    // 创建小题类和试题类
+                    PaperDetailUnit paperDetailUnit = new PaperDetailUnit();
+                    Question question = new Question();
+                    // 初始化小题类和试题类
+                    initPaperDetail(paper, paperDetail, paperDetailUnit, question, subQuesNum, importPaperCheck);
+                    // 处理客观题
+                    if (importPaperCheck.getQuesType().equals(ImportPaperMsg.singleSelection)
+                            || importPaperCheck.getQuesType().equals(ImportPaperMsg.multipleSelection)) {
+                        // 处理题干
+                        processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
+                                tmpWordMlPackage, errorQuesNum);
+                        // 处理选项
+                        processQuesOption(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
+                                tmpWordMlPackage, errorQuesNum);
+                        // 处理尾信息
+                        processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
+                                importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
+                        // 处理选择题的option--chenken 20170425
+                        processSelectOption(question);
+                    } else if (importPaperCheck.getQuesType().equals(ImportPaperMsg.nestedQuestion_word)) {
+                        // 处理套题
+                        processNestedQues(pList, importPaperCheck.getIndex(), question, paperDetailUnit,
+                                importPaperCheck, tmpWordMlPackage, paper);
+                    } else {
+                        // 处理其他题型
+                        processQuesBody(pList, importPaperCheck.getIndex(), subQuesNum, question, importPaperCheck,
+                                tmpWordMlPackage, errorQuesNum);
+                        processQuesTail(pList, importPaperCheck.getIndex(), subQuesNum, question, paperDetailUnit,
+                                importPaperCheck, tmpWordMlPackage, false, paper, errorQuesNum);
+                        // 填空题空格校验
+                        if (question.getQuestionType().getName().equals(QuesStructType.FILL_BLANK_QUESTION.getName())) {
+                            if (!StringUtils.isBlank(question.getQuesAnswer())) {
+                                processFill(question, paperDetailUnit, importPaperCheck, subQuesNum, errorQuesNum);
+                            }
+                        }
+                    }
+                    // 设置WordMlPackage二进制数据
+                    // byte[] pkgByte = getZipResource(wordMLPackage, question);
+                    // QuestionPkgPath quesPkgPath = quesPkgPathRepo.save(new
+                    // QuestionPkgPath(pkgByte));
+                    // question.setQuesPkgPathId(quesPkgPath.getId());
+                    // pkgByte = null;
+                    // quesPkgPath.setQuesPkg(null);
+                    // 设置question与Unit集合数据
+                    question.setCourse(paper.getCourse());
+                    question.setOrgId(user.getRootOrgId().toString());
+                    questions.add(question);
+                    paperDetailUnits.add(paperDetailUnit);
+                    // 设置当前索引,防止多余循环
+                    i = importPaperCheck.getIndex() - 1;
+                } else if (paperDetail == null) {
+                    throw new PaperException("导入文件格式有误,必须有大题头信息,且以[试题分类]开头!");
+                } else {
+                    String errorMsg = pText.length() > 10 ? pText.substring(0, 10) : pText;
+                    if (pText.startsWith(ImportPaperMsg.left_bracket)) {
+                        throw new PaperException(
+                                errorMsg + ",标签格式不正确!正确标签格式:[套题]、[套题数量]、[小题分数]、[答案]、[难度]、[一级属性]、[二级属性]、[公开度]、[小题型]");
+                    }
+                    throw new PaperException(
+                            importPaperCheck.getQuesName() + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_12);
+                }
+                if (!StringUtils.isEmpty(importPaperCheck.getErrorInfo())) {
+                    throw new PaperException(importPaperCheck.getErrorInfo());
+                }
+            }
+            if (paperDetails.size() == 0 || paperDetailUnits.size() == 0) {
+                throw new PaperException("导入文件格式有误!");
+            }
+            paper.setPaperDetailCount(mainQuesNum);
+            // 保存导入试卷信息
+            paper = savePaper(paper, paperDetails, paperDetailUnits, questions, importPaperCheck);
+            clearPaper(paperDetails, paperDetailUnits, questions);
+            wordMLPackage = null;
+            tmpWordMlPackage = null;
+        } finally {
+            FileUtils.deleteQuietly(file);
+        }
         return paper;
     }
 
-//    private byte[] getZipResource(WordprocessingMLPackage wordMLPackage, Question question) throws Exception {
-//        List<String> wordXmls = getWordXmlByQuestion(question);
-//        List<Question> subQuestions = question.getSubQuestions();
-//        if (subQuestions != null && subQuestions.size() > 0) {
-//            for (Question subQuestion : subQuestions) {
-//                wordXmls.addAll(getWordXmlByQuestion(subQuestion));
-//            }
-//        }
-//        return DocxProcessUtil.getWordBytesByQuestion(wordMLPackage, wordXmls);
-//    }
+    // private byte[] getZipResource(WordprocessingMLPackage wordMLPackage,
+    // Question question) throws Exception {
+    // List<String> wordXmls = getWordXmlByQuestion(question);
+    // List<Question> subQuestions = question.getSubQuestions();
+    // if (subQuestions != null && subQuestions.size() > 0) {
+    // for (Question subQuestion : subQuestions) {
+    // wordXmls.addAll(getWordXmlByQuestion(subQuestion));
+    // }
+    // }
+    // return DocxProcessUtil.getWordBytesByQuestion(wordMLPackage, wordXmls);
+    // }
 
     public List<String> getWordXmlByQuestion(Question question) {
         List<String> wordXmls = new ArrayList<>();
@@ -742,7 +743,7 @@ public class ImportPaperService {
         // 定义题干wordml和html
         StringBuilder quesBodyWordMl = new StringBuilder();
         StringBuilder quesBodyHtml = new StringBuilder();
-        boolean hasSeq=false;
+        boolean hasSeq = false;
         int i = 0;
         for (i = index; i < pList.size(); i++) {
             P pBody = (P) pList.get(i);
@@ -756,9 +757,9 @@ public class ImportPaperService {
                 throw new PaperException(importPaperCheck.getErrorInfo());
 
             } else if (tmpText.matches("^\\d{1,}\\.[\\s\\S]*")) {
-            	if(hasSeq) {
-            		// 检测到序列
-            		if (subQuesNum == 0) {
+                if (hasSeq) {
+                    // 检测到序列
+                    if (subQuesNum == 0) {
                         if (importPaperCheck.getNestedHeadNumber() != 0) {
                             importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                                     + importPaperCheck.getNestedHeadNumber() + "个题" + ImportPaperMsg.errMsg_14);
@@ -768,17 +769,18 @@ public class ImportPaperService {
                         }
                     } else {
                         if (importPaperCheck.getNestedHeadNumber() != 0) {
-                            importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
-                                    + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_14);
+                            importPaperCheck.setErrorInfo(
+                                    importPaperCheck.getQuesName() + "第" + importPaperCheck.getNestedHeadNumber() + "个题"
+                                            + errorQuesNum + ImportPaperMsg.errMsg_14);
                         } else {
                             importPaperCheck.setErrorInfo(
                                     getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_14);
                         }
                     }
                     throw new PaperException(importPaperCheck.getErrorInfo());
-            	}else {
-            		hasSeq=true;
-            	}
+                } else {
+                    hasSeq = true;
+                }
                 // 题干第一段
                 // 过滤题干标题
                 pBody = DocxProcessUtil.formatP(pBody, QuesUnit.QUES_BODY);
@@ -803,16 +805,16 @@ public class ImportPaperService {
                     importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                             + importPaperCheck.getNestedHeadNumber() + "个题" + ImportPaperMsg.errMsg_01);
                 } else {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
                 }
             } else {
                 if (importPaperCheck.getNestedHeadNumber() != 0) {
                     importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                             + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_01);
                 } else {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_01);
                 }
             }
             throw new PaperException(importPaperCheck.getErrorInfo());
@@ -858,8 +860,8 @@ public class ImportPaperService {
                     importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                             + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + ImportPaperMsg.errMsg_11);
                 } else {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_11);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + ImportPaperMsg.errMsg_11);
                 }
                 throw new PaperException(importPaperCheck.getErrorInfo());
             } else if (tmpText.matches("^[a-zA-Z]\\.[\\s\\S]*")) {
@@ -875,8 +877,8 @@ public class ImportPaperService {
                                 importPaperCheck.getQuesName() + "第" + importPaperCheck.getNestedHeadNumber() + "个题"
                                         + errorQuesNum + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_13);
                     } else {
-                        importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum)
-                                + ":“" + errorMsg + "”" + ImportPaperMsg.errMsg_13);
+                        importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + ":“" + errorMsg
+                                + "”" + ImportPaperMsg.errMsg_13);
                     }
                     throw new PaperException(importPaperCheck.getErrorInfo());
                 }
@@ -908,8 +910,7 @@ public class ImportPaperService {
                 importPaperCheck.setErrorInfo(importPaperCheck.getQuesName() + "第"
                         + importPaperCheck.getNestedHeadNumber() + "个题" + errorQuesNum + "中选项格式不正确或有缺失\n");
             } else {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中选项格式不正确或有缺失\n");
+                importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中选项格式不正确或有缺失\n");
             }
             throw new PaperException(importPaperCheck.getErrorInfo());
         } else {
@@ -1039,9 +1040,8 @@ public class ImportPaperService {
                     importPaperCheck.setErrorInfo(
                             getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + pList.get(i).toString());
                     throw new PaperException(importPaperCheck.getErrorInfo());
-                }else if(question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION){
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
+                } else if (question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                    importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
                 }
             } else {
                 break;
@@ -1054,8 +1054,7 @@ public class ImportPaperService {
                 importPaperCheck.setErrorInfo(paperDetailUnit.getPaperDetail().getName() + "中,第"
                         + paperDetailUnit.getNumber() + "个套题的" + errorQuesNum + "小题中,缺失“[答案]”");
             } else {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中,缺失“[答案]”");
+                importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, errorQuesNum) + "中,缺失“[答案]”");
             }
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
@@ -1063,18 +1062,18 @@ public class ImportPaperService {
         if (StringUtils.isNotEmpty(answerHTML)) {
             if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                     || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-                    ||question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                    || question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                 question.setQuesAnswer(answerHTML.toString().replaceAll(" ", ""));
-            }else {
+            } else {
                 question.setQuesAnswer(answerHTML.toString());
             }
             question.setQuesAnswerWord(answerWordML.toString());
         } else {
             if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
                     || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
-                    ||question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                    || question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
                 question.setQuesAnswer("");
-            }else {
+            } else {
                 question.setQuesAnswer("<p></p>");
             }
             question.setQuesAnswerWord(answerWordML.toString());
@@ -1117,8 +1116,7 @@ public class ImportPaperService {
             int subQuesNum, Paper paper, List<QuesProperty> quesProperties) throws Exception {
         // 一级属性为空,二级属性有值
         if (StringUtils.isBlank(firstProperty) && StringUtils.isNotBlank(secondProperty)) {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值为空,请检查");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值为空,请检查");
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
         // 一级属性,二级属性都有值
@@ -1129,15 +1127,14 @@ public class ImportPaperService {
             List<CourseProperty> courseProperties = coursePropertyRepo
                     .findByCourseCodeAndEnable(paper.getCourse().getCode(), true);
             if (courseProperties == null || courseProperties.size() < 1) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构,请先设置课程属性结构");
+                importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构,请先设置课程属性结构");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
 
             for (CourseProperty courseProperty : courseProperties) {
                 // 查询一级属性是否存在
                 Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
-                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
+                        Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
 
                 // 存在一级属性
                 if (proParent != null) {
@@ -1145,7 +1142,7 @@ public class ImportPaperService {
 
                     // 查询二级属性
                     Property proSon = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
-                    		Long.valueOf(paper.getOrgId()), courseProperty.getId(), proParent.getId(), secondProperty);
+                            Long.valueOf(paper.getOrgId()), courseProperty.getId(), proParent.getId(), secondProperty);
 
                     // 存在二级属性
                     if (proSon != null) {
@@ -1162,14 +1159,14 @@ public class ImportPaperService {
             }
 
             if (isFirstEmpty) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
+                importPaperCheck
+                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
 
             if (isSecondEmpty) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
+                importPaperCheck
+                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
         } else if (StringUtils.isNotBlank(firstProperty) && StringUtils.isBlank(secondProperty)) {
@@ -1182,20 +1179,19 @@ public class ImportPaperService {
             List<CourseProperty> courseProperties = coursePropertyRepo
                     .findByCourseCodeAndEnable(paper.getCourse().getCode(), true);
             if (courseProperties == null || courseProperties.size() < 1) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构,请先设置课程属性结构");
+                importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,没有设置课程属性结构,请先设置课程属性结构");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
 
             for (CourseProperty courseProperty : courseProperties) {
                 // 查询一级属性
                 Property proParent = propertyRepo.findByOrgIdAndCoursePropertyIdAndParentIdAndCode(
-                		Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
+                        Long.valueOf(paper.getOrgId()), courseProperty.getId(), Property.ROOT_PARENT_ID, firstProperty);
 
                 // 存在一级属性
                 if (proParent != null) {
                     isFirstEmpty = false;
-                        // 根据一级属性查询二级属性
+                    // 根据一级属性查询二级属性
                     List<Property> sonProperties = propertyRepo.findByParentIdOrderByNumber(proParent.getId());
 
                     // 存在二级属性,跳过
@@ -1213,14 +1209,14 @@ public class ImportPaperService {
                 }
             }
             if (isFirstEmpty) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
+                importPaperCheck
+                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性值与课程属性中一级属性不匹配,请检查");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
 
             if (isSecondEmpty) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
+                importPaperCheck
+                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性值与课程属性中二级属性不匹配,请检查");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
         } else {
@@ -1239,26 +1235,22 @@ public class ImportPaperService {
     private void checkAttributeIsFull(String firstProperty, String secondProperty, Double difficulty, Boolean publicity,
             ImportPaperCheck importPaperCheck, int subQuesNum) throws Exception {
         if (firstProperty == null) {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性缺失,请检查");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题一级属性缺失,请检查");
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
 
         if (secondProperty == null) {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性缺失,请检查");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题二级属性缺失,请检查");
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
 
         if (difficulty == null) {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题难度缺失,请检查");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题难度缺失,请检查");
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
 
         if (publicity == null) {
-                importPaperCheck
-                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题公开性缺失,请检查");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,小题公开性缺失,请检查");
             throw new PaperException(importPaperCheck.getErrorInfo());
         }
     }
@@ -1375,11 +1367,11 @@ public class ImportPaperService {
                 hasQuesType = false;
             } else if (subQuesNum != quesTypeNum) {
                 if (StringUtils.isEmpty(nestedQuesType)) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_02);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_02);
                 } else {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_03);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, quesTypeNum) + ImportPaperMsg.errMsg_03);
                 }
                 throw new PaperException(importPaperCheck.getErrorInfo());
             } else if (isHeader(tmpText)) {
@@ -1497,11 +1489,10 @@ public class ImportPaperService {
      */
     public static String getQuesNumInfo(ImportPaperCheck importPaperCheck, int subQuesNum) {
         if (importPaperCheck.getNestedHeadNumber() == 0) {
-        	return "[大题名称]:" + importPaperCheck.getQuesName() + "第" + subQuesNum + "小题";
+            return "[大题名称]:" + importPaperCheck.getQuesName() + "第" + subQuesNum + "小题";
         } else {
-        	return "[大题名称]:" + importPaperCheck.getQuesName() + "第"
-                    + importPaperCheck.getNestedHeadNumber() + "小题,第" + subQuesNum
-                    + "子题";
+            return "[大题名称]:" + importPaperCheck.getQuesName() + "第" + importPaperCheck.getNestedHeadNumber() + "小题,第"
+                    + subQuesNum + "子题";
         }
     }
 
@@ -1654,9 +1645,9 @@ public class ImportPaperService {
     private void checkSelectAnswer(Question question, String answerWord, ImportPaperCheck importPaperCheck,
             int subQuesNum) throws PaperException {
         if (!StringUtils.isBlank(answerWord)) {
-            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION&&answerWord.contains(",")) {
-                importPaperCheck.setErrorInfo(
-                        getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
+            if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION && answerWord.contains(",")) {
+                importPaperCheck
+                        .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
             String[] pAnswerArray = answerWord.split(",");
@@ -1671,8 +1662,8 @@ public class ImportPaperService {
                 answer = answer.trim();
                 String pattern = "[A-Z]|[a-z]";
                 if (!Pattern.matches(pattern, answer)) {
-                    importPaperCheck.setErrorInfo(
-                            getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
+                    importPaperCheck
+                            .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,答案为:" + answerWord);
                     throw new PaperException(importPaperCheck.getErrorInfo());
                 }
                 if (!optionNumList.contains(answer)) {
@@ -1698,8 +1689,7 @@ public class ImportPaperService {
             if (tmpText.equals("正确") || tmpText.equals("错误")) {
                 return;
             } else {
-                importPaperCheck.setErrorInfo(
-                        getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
+                importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,答案格式不正确,应为:正确或错误");
                 throw new PaperException(importPaperCheck.getErrorInfo());
             }
         }
@@ -1717,8 +1707,7 @@ public class ImportPaperService {
             question.setDifficulty("中");
         } else if (!isInteger(tmpText) || (Double.parseDouble(tmpText) < 1) || (Double.parseDouble(tmpText) > 10)) {
             // 如果不是整数,或者在1到10之间,就报错
-            importPaperCheck
-                    .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题难度只能是1到10之间整数");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题难度只能是1到10之间整数");
             throw new PaperException(importPaperCheck.getErrorInfo());
         } else {
             tempDifficulty = Double.parseDouble(tmpText) / 10;
@@ -1748,8 +1737,7 @@ public class ImportPaperService {
             publicity = true;
         } else if (!tmpText.equals("公开") && !tmpText.equals("非公开")) {
             // 如果不是公开和非公开,就报错
-            importPaperCheck
-                    .setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题公开度只能是公开和非公开");
+            importPaperCheck.setErrorInfo(getQuesNumInfo(importPaperCheck, subQuesNum) + "中,试题公开度只能是公开和非公开");
             throw new PaperException(importPaperCheck.getErrorInfo());
         } else {
             if (tmpText.equals("非公开")) {
@@ -1835,7 +1823,7 @@ public class ImportPaperService {
      */
     private static String getContent(String pText, String replaceContent) {
         String word = replaceContent.replace("[", "").replace("]", "");
-        return pText.replaceAll("\\[" + word + "\\]", "").replaceAll("[::]", "").trim();
+        return pText.replaceFirst("\\[" + word + "\\]", "").replaceFirst("[::]", "").trim();
     }
 
     /**
@@ -1919,11 +1907,13 @@ public class ImportPaperService {
             User user) {
         Question oldQuestion = oldPaperDetailUnit.getQuestion();
         Question newQuestion = BeanCopierUtil.copyProperties(oldQuestion, Question.class);
-//        QuestionPkgPath oldQuesPkgPath = quesPkgPathRepo.findFirstById(oldQuestion.getQuesPkgPathId());
-//        QuestionPkgPath newQuestionPkgPath = BeanCopierUtil.copyProperties(oldQuesPkgPath, QuestionPkgPath.class);
-//        newQuestionPkgPath.setId(null);
-//        newQuestionPkgPath = quesPkgPathRepo.save(newQuestionPkgPath);
-//        newQuestion.setQuesPkgPathId(newQuestionPkgPath.getId());
+        // QuestionPkgPath oldQuesPkgPath =
+        // quesPkgPathRepo.findFirstById(oldQuestion.getQuesPkgPathId());
+        // QuestionPkgPath newQuestionPkgPath =
+        // BeanCopierUtil.copyProperties(oldQuesPkgPath, QuestionPkgPath.class);
+        // newQuestionPkgPath.setId(null);
+        // newQuestionPkgPath = quesPkgPathRepo.save(newQuestionPkgPath);
+        // newQuestion.setQuesPkgPathId(newQuestionPkgPath.getId());
         newQuestion.setCourse(course);
         newQuestion.setId(null);
         newQuestion.setQuesProperties(null);
@@ -1931,86 +1921,87 @@ public class ImportPaperService {
         newQuestion = quesRepo.save(newQuestion);
         // 复制音频
         if (oldQuestion.getHasAudio() != null && oldQuestion.getHasAudio()) {
-//            try {
-                // 查询旧音频
-                List<QuestionAudio> oldQuestionAudios = questionAudioService
-                        .findQuestionAudiosByQuestionId(oldQuestion.getId());
-                // 复制文件下载路径
-                String copyAudioPath = TEMP_FILE_EXP + File.separator + user.getDisplayName() + "_copyAudioPath";
-                // 新建文件夹
-                File copyAudioDir = new File(copyAudioPath);
-                if (!copyAudioDir.exists()) {
-                    copyAudioDir.mkdirs();
+            // try {
+            // 查询旧音频
+            List<QuestionAudio> oldQuestionAudios = questionAudioService
+                    .findQuestionAudiosByQuestionId(oldQuestion.getId());
+            // 复制文件下载路径
+            String copyAudioPath = TEMP_FILE_EXP + File.separator + user.getDisplayName() + "_copyAudioPath";
+            // 新建文件夹
+            File copyAudioDir = new File(copyAudioPath);
+            if (!copyAudioDir.exists()) {
+                copyAudioDir.mkdirs();
+            }
+            for (QuestionAudio oldAudio : oldQuestionAudios) {
+                // 生成随机数,防止文件重名
+                int randomNumber = random.nextInt(1000);
+                // 定义文件下载名称,下载音频文件
+                String newAudioFileName = newQuestion.getId() + "_" + randomNumber + "_" + oldAudio.getFileName();
+
+                // 下载音频
+                File audioFile = new File(copyAudioPath + File.separator + newAudioFileName);
+                try {
+                    String filePath = FssHelper.fixFilePath(oldAudio.getFileUrl());
+                    FssFactory.getInstance().readFile(filePath, audioFile);
+                } catch (Exception e) {
+                    log.error(e.getMessage(), e);
+                    throw new RuntimeException("下载音频失败");
                 }
-                for (QuestionAudio oldAudio : oldQuestionAudios) {
-                    // 生成随机数,防止文件重名
-                    int randomNumber = random.nextInt(1000);
-                    // 定义文件下载名称,下载音频文件
-                    String newAudioFileName = newQuestion.getId() + "_" + randomNumber + "_" + oldAudio.getFileName();
-
-                    // 下载音频
-                    File audioFile = new File(copyAudioPath + File.separator + newAudioFileName);
-                    try {
-                        String filePath = FssHelper.fixFilePath(oldAudio.getFileUrl());
-                        FssFactory.getInstance().readFile(filePath, audioFile);
-                    } catch (Exception e) {
-                        log.error(e.getMessage(), e);
-                        throw new RuntimeException("下载音频失败");
+                // 重新上传新的音频文件
+                String newPath = String.format(FileConstants.RADIO_UPLOAD_PATH, newAudioFileName);
+                FssFileInfo result = FssFactory.getInstance().writeFile(newPath, audioFile, null);
+
+                IoUtils.removeFile(audioFile);
+
+                // 拷贝旧对象
+                QuestionAudio newAudio = new QuestionAudio(newQuestion.getId(), oldAudio.getFileName(),
+                        result.getFileUrl());
+                newAudio.setCreateTime(new Date());
+                newAudio.setCreateUser(user.getDisplayName());
+                questionAudioService.saveQuestionAudio(newAudio, user);
+            }
+            // 删除文件夹
+            IoUtils.removeFile(copyAudioDir);
+
+            // 查询新音频
+            List<QuestionAudio> newQuestionAudios = questionAudioService
+                    .findQuestionAudiosByQuestionId(newQuestion.getId());
+            Map<String, String> newMap = new HashMap<>();
+            for (QuestionAudio audio : newQuestionAudios) {
+                newMap.put(audio.getFileName(), audio.getId());
+            }
+            // 在题干html片段中替换音频存储id
+            String body = newQuestion.getQuesBody();
+            List<String> ids = CommonUtils.getTagANames(body);
+            List<String> names = CommonUtils.getTagANames2(body);
+            Map<String, String> oldBodyMap = new HashMap<>();
+            for (int i = 0; i < ids.size(); i++) {
+                oldBodyMap.put(names.get(i), ids.get(i));
+            }
+            for (String key : oldBodyMap.keySet()) {
+                body = body.replace(oldBodyMap.get(key), newMap.get(key));
+            }
+            newQuestion.setQuesBody(body);
+            // 替换选项中的音频存储id
+            if (newQuestion.getQuesOptions() != null && newQuestion.getQuesOptions().size() > 0) {
+                for (QuesOption option : newQuestion.getQuesOptions()) {
+                    String newOptionBody = option.getOptionBody();
+                    List<String> optionIds = CommonUtils.getTagANames(newOptionBody);
+                    List<String> optionNames = CommonUtils.getTagANames2(newOptionBody);
+                    Map<String, String> oldOptionMap = new HashMap<>();
+                    for (int i = 0; i < optionIds.size(); i++) {
+                        oldOptionMap.put(optionNames.get(i), optionIds.get(i));
                     }
-                    // 重新上传新的音频文件
-                    String newPath = String.format(FileConstants.RADIO_UPLOAD_PATH, newAudioFileName);
-                    FssFileInfo result = FssFactory.getInstance().writeFile(newPath, audioFile, null);
-
-                    IoUtils.removeFile(audioFile);
-
-                    // 拷贝旧对象
-                    QuestionAudio newAudio = new QuestionAudio(newQuestion.getId(), oldAudio.getFileName(),result.getFileUrl());
-                    newAudio.setCreateTime(new Date());
-                    newAudio.setCreateUser(user.getDisplayName());
-                    questionAudioService.saveQuestionAudio(newAudio, user);
-                }
-                // 删除文件夹
-                IoUtils.removeFile(copyAudioDir);
-
-                // 查询新音频
-                List<QuestionAudio> newQuestionAudios = questionAudioService
-                        .findQuestionAudiosByQuestionId(newQuestion.getId());
-                Map<String, String> newMap = new HashMap<>();
-                for (QuestionAudio audio : newQuestionAudios) {
-                    newMap.put(audio.getFileName(), audio.getId());
-                }
-                // 在题干html片段中替换音频存储id
-                String body = newQuestion.getQuesBody();
-                List<String> ids = CommonUtils.getTagANames(body);
-                List<String> names = CommonUtils.getTagANames2(body);
-                Map<String, String> oldBodyMap = new HashMap<>();
-                for (int i = 0; i < ids.size(); i++) {
-                    oldBodyMap.put(names.get(i), ids.get(i));
-                }
-                for (String key : oldBodyMap.keySet()) {
-                    body = body.replace(oldBodyMap.get(key), newMap.get(key));
-                }
-                newQuestion.setQuesBody(body);
-                // 替换选项中的音频存储id
-                if (newQuestion.getQuesOptions() != null && newQuestion.getQuesOptions().size() > 0) {
-                    for (QuesOption option : newQuestion.getQuesOptions()) {
-                        String newOptionBody = option.getOptionBody();
-                        List<String> optionIds = CommonUtils.getTagANames(newOptionBody);
-                        List<String> optionNames = CommonUtils.getTagANames2(newOptionBody);
-                        Map<String, String> oldOptionMap = new HashMap<>();
-                        for (int i = 0; i < optionIds.size(); i++) {
-                            oldOptionMap.put(optionNames.get(i), optionIds.get(i));
-                        }
-                        for (String key : oldOptionMap.keySet()) {
-                            newOptionBody = newOptionBody.replace(oldOptionMap.get(key), newMap.get(key));
-                        }
-                        option.setOptionBody(newOptionBody);
+                    for (String key : oldOptionMap.keySet()) {
+                        newOptionBody = newOptionBody.replace(oldOptionMap.get(key), newMap.get(key));
                     }
+                    option.setOptionBody(newOptionBody);
                 }
-                newQuestion = quesRepo.save(newQuestion);
-//            } catch (IOException e) {
-//                log.error(e.getMessage(), e);
-//            }
+            }
+            newQuestion = quesRepo.save(newQuestion);
+            // } catch (IOException e) {
+            // log.error(e.getMessage(), e);
+            // }
         }
         paperDetailUnit.setQuestion(newQuestion);
     }
@@ -2040,17 +2031,16 @@ public class ImportPaperService {
      */
     public void processFill(Question question, PaperDetailUnit paperDetailUnit, ImportPaperCheck importPaperCheck,
             int subQuesNum, int errorQuesNum) throws PaperException {
-    	if(!question.getQuesBody().contains("###")) {
-    		if (importPaperCheck.getNestedHeadNumber() == 0) {
-                importPaperCheck.setErrorInfo(
-                        "[大题名称]:" + importPaperCheck.getQuesName() + "第" + errorQuesNum + "题中,题干中没有空格");
+        if (!question.getQuesBody().contains("###")) {
+            if (importPaperCheck.getNestedHeadNumber() == 0) {
+                importPaperCheck
+                        .setErrorInfo("[大题名称]:" + importPaperCheck.getQuesName() + "第" + errorQuesNum + "题中,题干中没有空格");
             } else {
                 importPaperCheck.setErrorInfo("[大题名称]:" + importPaperCheck.getQuesName() + "第"
-                        + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + errorQuesNum
-                        + "题中,题干中没有空格");
+                        + importPaperCheck.getNestedHeadNumber() + "个套题中,第" + errorQuesNum + "题中,题干中没有空格");
             }
             throw new PaperException(importPaperCheck.getErrorInfo());
-    	}
+        }
         // 按3个#号截取题干
         String[] quesBody = question.getQuesBody().split("###");
         String[] quesAnwser = question.getQuesAnswer().split("##");

+ 1331 - 1282
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportPaperServiceImpl.java

@@ -1,1283 +1,1332 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.servlet.http.HttpServletResponse;
-
-import cn.com.qmth.examcloud.support.fss.FssFactory;
-import cn.com.qmth.examcloud.support.fss.FssHelper;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-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.stereotype.Service;
-
-import com.google.common.collect.Lists;
-import com.google.gson.Gson;
-
-import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
-import cn.com.qmth.examcloud.core.questions.base.FileDisposeUtil;
-import cn.com.qmth.examcloud.core.questions.base.IdUtils;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
-import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
-import cn.com.qmth.examcloud.core.questions.base.enums.PaperSeqMode;
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
-import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Block;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestQuestion;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Section;
-import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Sections;
-import cn.com.qmth.examcloud.core.questions.service.ExportPaperService;
-import cn.com.qmth.examcloud.core.questions.service.ExportThemisPaperService;
-import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.service.PropertyService;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.DownloadPaperDto;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.QuestionDistributeDto;
-import cn.com.qmth.examcloud.core.questions.service.converter.PrintExamPaperService;
-import cn.com.qmth.examcloud.core.questions.service.export.ExportPaperAbstractService;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
-import cn.com.qmth.examcloud.core.questions.service.util.ExportPaperUtil;
-import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
-import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
-import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
-
-@Service("exportPaperService")
-public class ExportPaperServiceImpl implements ExportPaperService {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ExportPaperServiceImpl.class);
-
-    public static final String TEMP_FILE_EXP = "docxExport/";
-
-    public static final String TEMP_FILE_NAME = "_考试说明.docx";
-
-    public static final String DOCX_SUFFIX = ".docx";
-
-    public static final String ZIP_SUFFIX = ".zip";
-
-    @Autowired
-    private PaperRepo paperRepo;
-
-    @Autowired
-    private PaperService paperService;
-
-    @Autowired
-    private PaperDetailService paperDetailService;
-
-    @Autowired
-    private MongoTemplate mongoTemplate;
-
-    @Autowired
-    private CoursePropertyRepo coursePropertyRepo;
-
-    @Autowired
-    private PropertyService propertyService;
-
-    @Autowired
-    private QuestionAudioRepo questionAudioRepo;
-
-    @Autowired
-    private PrintExamPaperService printExamPaperService;
-
-    @Autowired
-    private PaperDetailUnitRepo paperDetailUnitRepo;
-    
-    @Autowired
-    private ExportThemisPaperService exportThemisPaperService;
-
-//    @Autowired
-//    protected QuesPkgPathRepo quesPkgPathRepo;
-
-//    @Autowired
-//    private SysProperty sysProperty;
-    @Override
-    public void exportPaperFile(String paperId, String exportContentList,
-            HttpServletResponse response, PaperSeqMode seqMode, String examType, String psw, User user) {
-        ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(user.getRootOrgId().toString());
-        // 根据试卷id查询试卷
-        Paper paper = Model.of(paperRepo.findById(paperId));
-
-        String zipFileName =IdUtils.uuid();
-        long startTime;
-        try {
-            File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
-            if (!directory.exists()) {
-                directory.mkdirs();
-            }
-            if (exportContentList.indexOf(ExamFileType.PRINT_EXAM_PACKAGE.name()) > -1) {
-                printExamPaperService.downloadPaper(Lists.newArrayList(paper), directory.getAbsolutePath(), psw);
-            }
-            if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
-                downJson(paper, zipFileName);
-            }
-            if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
-            	List<Paper> papers=new ArrayList<>();
-            	papers.add(paper);
-                downThemisPackage(papers, zipFileName);
-            }
-            try {
-            	DownloadPaperDto dto=new DownloadPaperDto(user.getRootOrgId(), paperId, zipFileName, examType,seqMode);
-                if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
-                    exportPaperAbstractService.downloadPaper(dto);
-                }
-
-                if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
-                    exportPaperAbstractService.downloadPaperAnswer(dto);
-                }
-            } catch (StatusException e) {
-                LOG.error(e.getMessage(), e);
-                throw e;
-            } catch (Exception e) {
-                LOG.error(e.getMessage(), e);
-                throw new StatusException("100001", e.getMessage());
-            }
-            // 下载考试说明 2018-2-27 weiwehai
-            /*
-             * if(examType.equals("offLine") &&
-             * StringUtils.isNotBlank(paper.getExamRemark())){
-             * downExamRemark(paper,zipFileName); }
-             */
-            LOG.debug("开始压缩成zip...");
-            startTime = System.currentTimeMillis();
-            FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
-            FileDisposeUtil.downloadFile(paper.getName() + "_" + paper.getCourse().getCode() + ".zip",
-                    TEMP_FILE_EXP + File.separator + zipFileName + ".zip", response);
-            long endTime = System.currentTimeMillis();
-            LOG.debug("下载zip耗时:" + (endTime - startTime) + "ms");
-        } finally {
-            long endTime = System.currentTimeMillis();
-			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName);
-			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName+ ".zip");
-//            deteleFolder(TEMP_FILE_EXP, zipFileName);
-            long deleteTime = System.currentTimeMillis();
-            LOG.debug("删除文件耗时:" + (deleteTime - endTime) + "ms");
-        }
-        StringBuilder paperInfo=new StringBuilder();
-		paperInfo.append("课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
-		paperInfo.append(" 试卷名称:"+paper.getName());
-		paperInfo.append(getContentName(exportContentList));
-		
-		ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE36.getDesc(),paperInfo.toString()));
-    }
-
-    private String getContentName(String exportContentList) {
-    	StringBuilder sb=new StringBuilder();
-    	sb.append(" 下载内容:");
-    	for(String content:exportContentList.split(",")) {
-    		sb.append(ExamFileType.valueOf(content).getName()+",");
-    	}
-    	sb.deleteCharAt(sb.length()-1);
-    	return sb.toString();
-    }
-    /**
-     * 下载考试说明
-     *
-     * @param zipFileName
-     * @throws Exception
-     */
-//    @SuppressWarnings("unused")
-//    private void downExamRemark(Paper paper, String zipFileName) throws Exception {
-//        // 1.考试说明html转成word
-//        String title = "<p style=\"text-align:center\"><span style=\"font-size:26px\"><span style=\"font-family:宋体\">考&nbsp;试&nbsp;说&nbsp;明</span></span></p>";
-//        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
-//        DocxProcessUtil.html2Docx(wordMLPackage, CommonUtils.formatHtml(title + paper.getExamRemark()));
-//        // 2.导出考试说明word
-//        File file = new File(TEMP_FILE_EXP + File.separator + zipFileName + File.separator + paper.getCourse().getCode()
-//                + TEMP_FILE_NAME);
-//        Docx4J.save(wordMLPackage, file);
-//    }
-
-//    private void deteleFolder(String sourceFilePath, String zipFileName) {
-//        File ComputerTestPaperfoler = new File(sourceFilePath + File.separator + "json");
-//        if (ComputerTestPaperfoler.exists()) {
-//            FileUtils.deleteQuietly(ComputerTestPaperfoler);
-//        }
-//        File zipFolder = new File(sourceFilePath + File.separator + zipFileName);
-//        if (zipFolder.exists()) {
-//            FileUtils.deleteQuietly(zipFolder);
-//        }
-//        File zipFile = new File(sourceFilePath + File.separator + zipFileName + ".zip");
-//        if (zipFile.exists()) {
-//            FileUtils.deleteQuietly(zipFile);
-//        }
-//    }
-
-    private void downJson(Paper paper, String zipFileName) {
-        ComputerTestPaper computerTestPaper = buildComputerTestPapers(paper);
-        String jsonDirectory = TEMP_FILE_EXP + File.separator+zipFileName+File.separator + "json";
-        try {
-	        // 新建文件夹
-	        File dirFile = new File(jsonDirectory);
-	        if (!dirFile.exists()) {
-	            dirFile.mkdirs();
-	        }
-	        makeComputerTestPaperToJsonFile(paper.getCourse().getCode(), computerTestPaper, jsonDirectory);
-	        downloadAudio(computerTestPaper, jsonDirectory);
-	        // 将文件夹打包成zip压缩包放在docxExport下
-	        FileDisposeUtil.fileToZip(jsonDirectory, TEMP_FILE_EXP + File.separator + zipFileName,
-	                paper.getCourse().getCode() + "_" + paper.getName());
-		} finally {
-			FileUtil.deleteDirectory(jsonDirectory);
-		}
-    }
-    
-    private void downThemisPackage(List<Paper> papers, String zipFileName) {
-        String zipDirectory = TEMP_FILE_EXP + File.separator +zipFileName+ File.separator+ "themis_package";
-        // 新建文件夹
-        File dirFile = new File(zipDirectory);
-        if (!dirFile.exists()) {
-        	dirFile.mkdirs();
-        }
-        try {
-        	for(Paper paper:papers) {
-        		ThemisPaperAndAnswer pa = exportThemisPaperService.buildPaperAndAnswer(paper);
-				String paperDirectory = zipDirectory + File.separator + paper.getCourseNo() + File.separator
-						+ paper.getId();
-				File paperDir = new File(paperDirectory);
-				if (!paperDir.exists()) {
-					paperDir.mkdirs();
-				}
-				exportThemisPaperService.makePaperAnswerToJsonFile(pa, paperDirectory);
-				exportThemisPaperService.downloadAudio(pa.getPaper(), paperDirectory);
-        	}
-			// 将文件夹打包成zip压缩包放在docxExport下
-			File zipFile=new File(TEMP_FILE_EXP + File.separator + zipFileName+ File.separator + "在线考试数据包.zip");
-			FileUtil.doZip(dirFile, zipFile);
-		} finally {
-			FileUtil.deleteDirectory(zipDirectory);
-		}
-    }
-
-    private void downloadAudio(ComputerTestPaper computerTestPaper, String jsonDirectory) {
-        // 取到所有大题
-        List<ComputerTestPaperDetail> details = computerTestPaper.getDetails();
-        if (details != null && details.size() > 0) {
-            for (ComputerTestPaperDetail detail : details) {
-                // 取到所有小题集合
-                List<ComputerTestQuestion> questions = detail.getQuestions();
-                if (questions != null && questions.size() > 0) {
-                    for (ComputerTestQuestion question : questions) {
-                        int bodyNum = 1;
-                        // 取到题干
-                        Sections body = question.getBody();
-                        List<Section> sections = body.getSections();
-                        for (Section section : sections) {
-                            List<Block> blocks = section.getBlocks();
-                            if (blocks != null && blocks.size() > 0) {
-                                for (Block block : blocks) {
-                                    if (block.getType().equals("audio")) {
-                                        String id = block.getValue();
-                                        QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
-                                        String audioFileName = questionAudio.getId() + "_" + detail.getNumber() + "_"
-                                                + question.getNumber() + "_1_" + bodyNum + "."
-                                                + questionAudio.getFileSuffixes();
-
-                                        File file = new File(jsonDirectory + File.separator + audioFileName);
-
-                                        String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
-                                        FssFactory.getInstance().readFile(filePath, file);
-
-                                        bodyNum++;
-                                    }
-                                }
-                            }
-                        }
-                        // 取到选项
-                        List<ComputerTestOption> options = question.getOptions();
-                        if (options != null && options.size() > 0) {
-                            for (ComputerTestOption computerTestOption : options) {
-                                int optionNum = 1;
-                                // 获取选项主体
-                                Sections optionBody = computerTestOption.getBody();
-                                List<Section> optionSections = optionBody.getSections();
-                                if (optionSections != null && optionSections.size() > 0) {
-                                    for (Section optionSection : optionSections) {
-                                        List<Block> blocks = optionSection.getBlocks();
-                                        if (blocks != null && blocks.size() > 0) {
-                                            for (Block block : blocks) {
-                                                if (block.getType().equals("audio")) {
-                                                    String id = block.getValue();
-                                                    QuestionAudio questionAudio = Model
-                                                            .of(questionAudioRepo.findById(id));
-                                                    String audioFileName = questionAudio.getId() + "_"
-                                                            + detail.getNumber() + "_" + question.getNumber() + "_2_"
-                                                            + computerTestOption.getNumber() + "_" + optionNum + "."
-                                                            + questionAudio.getFileSuffixes();
-
-                                                    File file = new File(jsonDirectory + File.separator + audioFileName);
-
-                                                    String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
-                                                    FssFactory.getInstance().readFile(filePath, file);
-
-                                                    optionNum++;
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * 构建机考数据包实体类
-     *
-     * @return
-     */
-    public ComputerTestPaper buildComputerTestPapers(Paper paper) {
-        // 得到所有旧对象的大题对象
-        List<PaperDetail> paperDetails = paperService.findPaperDetailsById(paper.getId());
-        // 通过 paper 对象 ,生成新的 ComputerTestPaper 对象
-        ComputerTestPaper computerTestPaper = new ComputerTestPaper(paper, "");
-        List<ComputerTestPaperDetail> details = new ArrayList<>();
-        // 遍历所有旧大题对象,得到小题对象的集合
-        for (PaperDetail paperDetail : paperDetails) {
-            List<PaperDetailUnit> paperDetailUnits = paperDetailService.getUnitsByPaperDetailId(paperDetail.getId());
-            ComputerTestPaperDetail computerTestPaperDetail = new ComputerTestPaperDetail(paperDetail);
-            List<ComputerTestQuestion> questions = new ArrayList<>();
-            // 遍历所有的小题对象
-            for (int i = 0; i < paperDetailUnits.size(); i++) {
-                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(i);
-                // 根据旧的小题对象,生成新的小题对象
-                ComputerTestQuestion computerTestQuestion = new ComputerTestQuestion(paperDetailUnit);
-                // 设置小题题号
-                computerTestQuestion.setNumber(i + 1);
-                // 得到小题题干
-                computerTestQuestion
-                        .setBody(getBodyOrAnswer(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
-                // 得到小题所有选项
-                computerTestQuestion.setOptions(getOption(paperDetailUnit.getQuestion(), computerTestPaper));
-                // 得到小题的答案
-                computerTestQuestion
-                        .setAnswer(getBodyOrAnswer(paperDetailUnit.getQuestion().getQuesAnswer(), computerTestPaper));
-                // 查询小题中的 套题
-                List<Question> subQuestionsList = paperDetailUnit.getQuestion().getSubQuestions();
-                // 判断这个小题中是否有套题
-                if (subQuestionsList != null && subQuestionsList.size() > 0) {
-                    List<ComputerTestQuestion> subQuestions = new ArrayList<>();
-                    // 遍历每个套题
-                    for (int j = 0; j < subQuestionsList.size(); j++) {
-                        Question subQuestion = subQuestionsList.get(j);
-                        ComputerTestQuestion subcomputerTestQuestion = new ComputerTestQuestion(subQuestion);
-                        // 设置套题中小题题号
-                        subcomputerTestQuestion.setNumber(j + 1);
-                        subcomputerTestQuestion.setBody(getBodyOrAnswer(subQuestion.getQuesBody(), computerTestPaper));
-                        subcomputerTestQuestion.setOptions(getOption(subQuestion, computerTestPaper));
-                        subcomputerTestQuestion
-                                .setAnswer(getBodyOrAnswer(subQuestion.getQuesAnswer(), computerTestPaper));
-                        subQuestions.add(subcomputerTestQuestion);
-                    }
-                    computerTestQuestion.setSubQuestions(subQuestions);
-                }
-                questions.add(computerTestQuestion);
-            }
-            computerTestPaperDetail.setQuestions(questions);
-            // paperDetail中的题数(unitCount)可能不准确,这里以questions的实际size为准
-            computerTestPaperDetail.setQuestionCount(questions.size());
-            details.add(computerTestPaperDetail);
-        }
-        computerTestPaper.setDetails(details);
-        return computerTestPaper;
-    }
-
-    private Sections getBodyOrAnswer(String str, ComputerTestPaper computerTestPaper) {
-        Sections body = new Sections();
-        List<Section> sections = new ArrayList<>();
-        // 得到小题题干或者答案行数
-        if (StringUtils.isBlank(str)) {
-            return body;
-        }
-        String[] questionRowStrings = str.split("</p>");
-        for (int i = 0; i < questionRowStrings.length; i++) {
-            List<Block> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
-            if (blocks != null && blocks.size() > 0) {
-                Section section = new Section();
-                // 将小题题干拆分为Block集合
-                section.setBlocks(blocks);
-                sections.add(section);
-            }
-        }
-        body.setSections(sections);
-        return body;
-    }
-
-    private List<ComputerTestOption> getOption(Question question, ComputerTestPaper computerTestPaper) {
-        // 得到小题选项
-        List<QuesOption> quesOptions = question.getQuesOptions();
-        List<ComputerTestOption> options = new ArrayList<>();
-        // 遍历小题选项
-        if (quesOptions != null && quesOptions.size() > 0) {
-            for (QuesOption quesOption : quesOptions) {
-                ComputerTestOption option = new ComputerTestOption();
-                option.setNumber(new Integer(quesOption.getNumber()));
-                option.setCorrect(quesOption.getIsCorrect() == 1 ? true : false);
-                Sections body = new Sections();
-
-                List<Section> sections = new ArrayList<>();
-                // 得到小题选项
-                String optionString = quesOption.getOptionBody();
-                String[] optionStrings = optionString.split("</p>");
-                for (int i = 0; i < optionStrings.length; i++) {
-                    List<Block> blocks = disposeQuestionBodyOrOption(optionStrings[i], computerTestPaper);
-                    if (blocks != null && blocks.size() > 0) {
-                        Section section = new Section();
-                        section.setBlocks(blocks);
-                        sections.add(section);
-                    }
-                }
-                body.setSections(sections);
-                option.setBody(body);
-                options.add(option);
-            }
-        }
-        return options;
-    }
-
-    private List<Block> disposeQuestionBodyOrOption(String questionRow, ComputerTestPaper computerTestPaper) {
-        List<Block> blocks = new ArrayList<>();
-        // 去掉每行里面的<p>,<span>,</span>标签
-        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
-                .replaceAll("</span>", "").replaceAll("</a>", "");
-        String[] questionRowStrings = questionRow.split("<|/>|>");
-        boolean hasAudio = false;
-        for (int i = 0; i < questionRowStrings.length; i++) {
-            Block block = new Block();
-            String rowStr = questionRowStrings[i];
-            // 判断是否有图片
-            if (rowStr.startsWith("img")) {
-                rowStr = "<" + rowStr + ">";
-                Map<String, Object> param = new HashMap<>();
-                // 需要继续做截取,取到Parma
-                block.setType("image");
-                // 获取图片的路径
-                List<String> strSrcList = getImg(rowStr, "src");
-                if (strSrcList.size() > 0) {
-                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
-                    block.setValue(strSrc);
-                }
-                // 获取图片的高度
-                List<String> strHeightList = getImg(rowStr, "height");
-                if (strHeightList.size() > 0) {
-                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
-                    param.put("height", strHeight);
-                }
-                // 获取图片的宽度
-                List<String> strWidthList = getImg(rowStr, "width");
-                if (strHeightList.size() > 0) {
-                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
-                    param.put("width", strWidth);
-                }
-                block.setParam(param);
-                blocks.add(block);
-            } else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
-                rowStr = "<" + rowStr + ">";
-                block.setPlayTime(1);
-                block.setType("audio");
-                block.setValue(CommonUtils.getAttrValue(rowStr, "id"));
-                blocks.add(block);
-                hasAudio = true;
-            } else {
-                block.setType("text");
-                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
-                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
-                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
-                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
-                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
-                if (StringUtils.isNotBlank(rowStr)) {
-                    block.setValue(rowStr);
-                    blocks.add(block);
-                }
-            }
-        }
-        if (hasAudio) {
-            computerTestPaper.setHasVideo(1);
-        }
-        return blocks;
-    }
-
-    /**
-     * 获取图片里面的路径,长度,宽度
-     */
-    private List<String> getImg(String s, String str) {
-        String regex;
-        List<String> list = new ArrayList<>();
-        regex = str + "=\"(.*?)\"";
-        Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
-        Matcher ma = pa.matcher(s);
-        while (ma.find()) {
-            list.add(ma.group());
-        }
-        return list;
-    }
-
-    /**
-     * 将computerTestPaper对象生成JSON文件存放在jsonDirectoryPath中
-     *
-     * @param computerTestPaper
-     * @param jsonDirectoryPath
-     */
-    @SuppressWarnings("deprecation")
-	private void makeComputerTestPaperToJsonFile(String courseCode, ComputerTestPaper computerTestPaper,
-            String jsonDirectoryPath) {
-        // 创建新的JSON文件
-        File file = new File(jsonDirectoryPath + File.separator + courseCode + ".json");
-        // 将对象转成 json对象
-        Gson gson = new Gson();
-        String strJSON = gson.toJson(computerTestPaper);
-
-        strJSON = CommonUtils.replaceUnicodeStr(strJSON);
-        // 生成文件流写入JSON文件
-        FileOutputStream outputStream = null;
-        try {
-            outputStream = new FileOutputStream(file);
-            byte b[] = strJSON.getBytes();
-            outputStream.write(b);
-            outputStream.flush();
-        } catch (FileNotFoundException e) {
-            LOG.error(e.getMessage(), e);
-        } catch (IOException e) {
-            LOG.error(e.getMessage(), e);
-        } finally {
-            IOUtils.closeQuietly(outputStream);
-        }
-    }
-
-    @Override
-    public void exportPaperFiles(List<String> paperIds, String exportContentList,
-            HttpServletResponse response, PaperSeqMode seqMode, String examType, User user) throws Exception {
-        ExportPaperAbstractService exportPaperAbstractService = PaperUtil.getByRootOrgId(user.getRootOrgId().toString());
-        // 根据试卷id查询所有试卷
-        List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
-        String zipFileName = IdUtils.uuid();
-        try {
-			// 创建压缩文件夹
-			File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
-			directory.mkdirs();
-			// 下载试卷
-			if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
-				for (Paper paper : papers) {
-					DownloadPaperDto dto=new DownloadPaperDto(user.getRootOrgId(), paper.getId(), zipFileName, examType,seqMode);
-					exportPaperAbstractService.downloadPaper(dto);
-				}
-			}
-			// 下载答案
-			if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
-				for (Paper paper : papers) {
-					DownloadPaperDto dto=new DownloadPaperDto(user.getRootOrgId(), paper.getId(), zipFileName, examType,seqMode);
-					exportPaperAbstractService.downloadPaperAnswer(dto);
-				}
-			}
-			// 下载机考数据包
-			if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
-				for (Paper paper : papers) {
-					downJson(paper, zipFileName);
-				}
-			}
-			// 下载在线考试数据包
-			if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
-				downThemisPackage(papers, zipFileName);
-			}
-			FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
-			FileDisposeUtil.downloadFile(zipFileName + ".zip", TEMP_FILE_EXP + File.separator + zipFileName + ".zip",
-					response);
-		} finally {
-			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName);
-			FileUtil.deleteFolder(TEMP_FILE_EXP+ File.separator + zipFileName+ ".zip");
-		}
-        StringBuilder paperInfo=new StringBuilder();
-		paperInfo.append("下载数量:"+paperIds.size());
-		paperInfo.append(getContentName(exportContentList));
-		
-		ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE37.getDesc(),paperInfo.toString()));
-    
-    }
-
-    @Override
-    public void downQuestionDistribute(String courseCode, HttpServletResponse response) throws IOException {
-        // 生成导出Excle的list集合
-        List<QuestionDistributeDto> questionDistributes = this.downQuestionDistribute(courseCode);
-
-        final String fileName = courseCode + "试题分布.xlsx";
-        final String filePath = TEMP_FILE_EXP + File.separator + fileName;
-
-        // 生成Excel导出
-        File tempFile = this.writeExcel(questionDistributes, filePath);
-        FileDisposeUtil.downloadFile(fileName, filePath, response);
-        FileUtils.deleteQuietly(tempFile);
-    }
-
-    @Override
-    public void downQuestionDistributes(List<Course> courses) throws IOException {
-        if (CollectionUtils.isEmpty(courses)) {
-            return;
-        }
-
-        for (Course course : courses) {
-            List<QuestionDistributeDto> questionDistributes = this.downQuestionDistribute(course.getCode());
-            final String fileName = course.getCode() + "试题分布.xlsx";
-            final String filePath = TEMP_FILE_EXP + File.separator + fileName;
-            this.writeExcel(questionDistributes, filePath);
-        }
-    }
-
-    private List<QuestionDistributeDto> downQuestionDistribute(String courseCode) {
-        // 1.生成导出Excle的list集合
-        List<QuestionDistributeDto> questionDistributes = new ArrayList<>();
-
-        // 2.根据课程code查询课程属性集合
-        List<CourseProperty> courseProperties = coursePropertyRepo.findByCourseCodeAndEnable(courseCode, true);
-        if (CollectionUtils.isEmpty(courseProperties)) {
-            return questionDistributes;
-        }
-
-        // 3.遍历课程属性集合,根据课程属性查询一级
-        for (CourseProperty courseProperty : courseProperties) {
-            List<Property> parentProperties = propertyService.findParentProperties(courseProperty.getId(),
-                    courseProperty.getOrgId());
-            if (parentProperties != null && parentProperties.size() > 0) {
-                for (Property parentProperty : parentProperties) {
-                    List<Property> sonProperties = propertyService.findSonProperties(parentProperty.getId());
-                    if (sonProperties != null && sonProperties.size() > 0) {
-                        for (Property sonProperty : sonProperties) {
-                            // 单选题集合
-                            List<Question> sinList = questionList(courseCode, courseProperty,
-                                    QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, sonProperty);
-                            // 多选题集合
-                            List<Question> mulList = questionList(courseCode, courseProperty,
-                                    QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, sonProperty);
-                            // 判断题集合
-                            List<Question> bolList = questionList(courseCode, courseProperty,
-                                    QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, sonProperty);
-
-                            List<Question> fillList = null;
-                            // fillList = questionList(courseCode,
-                            // courseProperty,
-                            // QuesStructType.FILL_BLANK_QUESTION,
-                            // parentProperty, sonProperty);
-                            List<Question> textList = null;
-                            // textList = questionList(courseCode,
-                            // courseProperty,
-                            // QuesStructType.TEXT_ANSWER_QUESTION,
-                            // parentProperty, sonProperty);
-
-                            // 计算所有题型数量
-                            Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
-                            QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
-                                    courseProperty.getName(), parentProperty.getName(), sonProperty.getName(), map);
-                            questionDistributes.add(questionDistributeDto);
-                        }
-                    } else {
-                        // 一级属性不为空,二级属性为空
-                        // 单选题集合
-                        List<Question> sinList = questionList(courseCode, courseProperty,
-                                QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, null);
-                        // 多选题集合
-                        List<Question> mulList = questionList(courseCode, courseProperty,
-                                QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, null);
-                        // 判断题集合
-                        List<Question> bolList = questionList(courseCode, courseProperty,
-                                QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, null);
-
-                        List<Question> fillList = null;
-                        // fillList = questionList(courseCode, courseProperty,
-                        // QuesStructType.FILL_BLANK_QUESTION, parentProperty,
-                        // null);
-                        List<Question> textList = null;
-                        // textList = questionList(courseCode, courseProperty,
-                        // QuesStructType.TEXT_ANSWER_QUESTION, parentProperty,
-                        // null);
-
-                        // 计算所有题型数量
-                        Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
-                        QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
-                                courseProperty.getName(), parentProperty.getName(), null, map);
-                        questionDistributes.add(questionDistributeDto);
-                    }
-                }
-            } else {
-                // 一级属性为空
-                QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(courseProperty.getName(), null,
-                        null, null);
-                questionDistributes.add(questionDistributeDto);
-            }
-        }
-
-        return questionDistributes;
-    }
-
-    @SuppressWarnings("resource")
-    private File writeExcel(List<QuestionDistributeDto> questionDistributes, String filePath) throws IOException {
-        // 读取Excel模板
-        InputStream in = this.getClass().getResourceAsStream("/quesDistinct.xlsx");
-        // InputStream in =
-        // this.getClass().getResourceAsStream("/quesDistinctAll.xlsx");
-
-        // 获取第一个工作页
-        Workbook workBook = new XSSFWorkbook(in);
-        Sheet sheet = workBook.getSheetAt(0);
-
-        // 往Excel中写入数据,从第5行开始
-        for (int i = 0; i < questionDistributes.size(); i++) {
-            // 创建一行:从第五行开始,跳过表头
-            Row row = sheet.createRow(i + 4);
-
-            // 获取这行的记录
-            QuestionDistributeDto questionDistributeDto = questionDistributes.get(i);
-
-            // 每列赋值
-            row.createCell(0).setCellValue(questionDistributeDto.getCoursePropertyName());
-            row.createCell(1).setCellValue(questionDistributeDto.getFirstPropertyName());
-            row.createCell(2).setCellValue(questionDistributeDto.getSecondPropertyName());
-            Map<Long, Long> map = questionDistributeDto.getMap();
-            if (map == null) {
-                for (int j = 0; j < 18; j++) {
-                    row.createCell(j + 3).setCellValue(0);
-                }
-            } else {
-                int j = 3;
-                for (Long key : map.keySet()) {
-                    row.createCell(j).setCellValue(map.get(key));
-                    j++;
-                }
-            }
-        }
-
-        LOG.info("[WriteExcel] " + filePath);
-
-        File file = new File(filePath);
-        OutputStream out = new FileOutputStream(file);
-        workBook.write(out);
-        out.close();
-        return file;
-    }
-
-    private Map<Long, Long> countQuesType(List<Question> sinList, List<Question> mulList, List<Question> bolList,
-            List<Question> fillList, List<Question> textList) {
-        Map<Long, Long> map = new TreeMap<>();
-        map = buildMap(sinList, map, 1);
-        map = buildMap(mulList, map, 2);
-        map = buildMap(bolList, map, 3);
-
-        if (fillList != null) {
-            map = buildMap(fillList, map, 4);
-        }
-        if (textList != null) {
-            map = buildMap(textList, map, 5);
-        }
-
-        // 给map的键排序
-        Map<Long, Long> resultMap = sortMapByKey(map);
-        return resultMap;
-    }
-
-    private Map<Long, Long> sortMapByKey(Map<Long, Long> map) {
-        if (map == null || map.isEmpty()) {
-            return null;
-        }
-        Map<Long, Long> sortMap = new TreeMap<Long, Long>(new Comparator<Long>() {
-
-            public int compare(Long l1, Long l2) {
-                return l1.compareTo(l2);
-            }
-        });
-        sortMap.putAll(map);
-        return sortMap;
-    }
-
-    private Map<Long, Long> buildMap(List<Question> quesList, Map<Long, Long> map, int questionType) {
-        // 初始化map
-        for (int i = 1; i < 7; i++) {
-            if (i < 4) {
-                map.put((long) (questionType * 100 + i), 0L);
-            } else {
-                map.put((long) (questionType * 100 + 10 + i - 3), 0L);
-            }
-        }
-        for (Question question : quesList) {
-            if (question.getPublicity()) {
-                // 公开
-                if (question.getDifficulty() != null && question.getDifficulty().equals("难")
-                        || question.getDifficultyDegree() < 0.4 && question.getDifficultyDegree() > 0) {
-                    map = buildMapSum(questionType * 100L + 1L, map);
-                } else if (question.getDifficulty() != null && question.getDifficulty().equals("中")
-                        || question.getDifficultyDegree() < 0.8 && question.getDifficultyDegree() > 0.3) {
-                    map = buildMapSum(questionType * 100L + 2L, map);
-                } else {
-                    map = buildMapSum(questionType * 100L + 3L, map);
-                }
-            } else {
-                // 非公开
-                if (question.getDifficulty() != null && question.getDifficulty().equals("难")
-                        || question.getDifficultyDegree() < 0.4 && question.getDifficultyDegree() > 0) {
-                    map = buildMapSum(questionType * 100L + 11L, map);
-                } else if (question.getDifficulty() != null && question.getDifficulty().equals("中")
-                        || question.getDifficultyDegree() < 0.8 && question.getDifficultyDegree() > 0.3) {
-                    map = buildMapSum(questionType * 100L + 12L, map);
-                } else {
-                    map = buildMapSum(questionType * 100L + 13L, map);
-                }
-            }
-        }
-        return map;
-    }
-
-    private Map<Long, Long> buildMapSum(long key, Map<Long, Long> map) {
-        Long sum = map.get(key);
-        map.put(key, sum + 1);
-        return map;
-    }
-
-    public List<Question> questionList(String courseNo, CourseProperty courseProperty, QuesStructType quesStructType,
-            Property parentProperty, Property sonProperty) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("orgId").is(courseProperty.getOrgId().toString()));
-        query.addCriteria(Criteria.where("course.enable").is("true"));
-        query.addCriteria(Criteria.where("course.code").is(courseNo));
-        query.addCriteria(Criteria.where("questionType").is(quesStructType.name()));
-        query.addCriteria(Criteria.where("quesProperties.coursePropertyName").is(courseProperty.getName()));
-        // 二级属性不为空,那么一级属性也不为空
-        if (sonProperty != null && sonProperty.getId() != null) {
-            query.addCriteria(Criteria.where("quesProperties").elemMatch(Criteria.where("firstProperty.id")
-                    .is(parentProperty.getId()).and("secondProperty.id").is(sonProperty.getId())));
-        } else {
-            if (parentProperty != null && parentProperty.getId() != null) {
-                query.addCriteria(Criteria.where("quesProperties")
-                        .elemMatch(Criteria.where("firstProperty.id").is(parentProperty.getId())));
-            }
-        }
-        List<Question> questionList = this.mongoTemplate.find(query, Question.class);
-        return questionList;
-    }
-
-    public List<Question> questionList2(String courseNo, CourseProperty courseProperty, QuesStructType quesStructType,
-            Property parentProperty, Property sonProperty, List<Question> questions) {
-        List<Question> questionList = new ArrayList<>();
-        String id = courseNo + quesStructType.name() + courseProperty.getName();
-        // 二级属性不为空,那么一级属性也不为空
-        if (sonProperty != null && sonProperty.getId() != null) {
-            id = id + parentProperty.getId() + sonProperty.getId();
-        } else {
-            if (parentProperty != null && parentProperty.getId() != null) {
-                id = id + parentProperty.getId();
-            }
-        }
-        for (Question question : questions) {
-            List<String> idStrings = new ArrayList<>();
-            String id_Q = question.getCourseNo() + question.getQuestionType();
-            List<QuesProperty> quesProperties = question.getQuesProperties();
-            if (quesProperties != null && quesProperties.size() > 0) {
-                for (QuesProperty property : quesProperties) {
-                    String idP = id_Q + property.getCoursePropertyName() + property.getFirstProperty().getId();
-                    if (property.getSecondProperty() != null) {
-                        idP = id_Q + property.getCoursePropertyName() + property.getFirstProperty().getId()
-                                + property.getSecondProperty().getId();
-                    }
-                    idStrings.add(idP);
-                }
-                if (idStrings.contains(id)) {
-                    questionList.add(question);
-                }
-            }
-        }
-        return questionList;
-    }
-
-    @Override
-    public void downOriginalPaper(String paperId, String loginName, HttpServletResponse response) throws Exception {
-//        String zipFileName = loginName + System.currentTimeMillis() + "";
-//        try {
-//            // 生成导出的试卷对象
-//            PaperExp paperExp = paperService.getDownPaperExp(paperId);
-//            File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
-//            if (!directory.exists()) {
-//                directory.mkdirs();
-//            }
-//            String paperfileName = paperExp.getName() + "_" + paperExp.getCourseNo() + "_"
-//                    + ExamFileType.PAPER.getName() + DOCX_SUFFIX;
-//            File file = new File(TEMP_FILE_EXP + File.separator + zipFileName + File.separator + paperfileName);
-//            List<WordprocessingMLPackage> wordPackages = getPkgList(paperId);
-//            DocxProcessUtil.exportWordNew(paperExp, file, ExportPaperAbstractService.ORIGINAL_PAPER);
-//            DocxProcessUtil.processImage(zipFileName + File.separator + paperfileName, wordPackages);
-//            FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
-//            FileDisposeUtil.downloadFile(paperExp.getName() + "_" + paperExp.getCourse().getCode() + ".zip",
-//                    TEMP_FILE_EXP + File.separator + zipFileName + ".zip", response);
-//        } finally {
-//            deteleFolder(TEMP_FILE_EXP, zipFileName);
-//        }
-    }
-
-    /**
-     * 获取当前试卷下所有试题WordPkg
-     *
-     * @param id
-     * @return
-     */
-//    protected List<WordprocessingMLPackage> getPkgList(String id) {
-//        Paper paper = Model.of(paperRepo.findById(id));
-//        List<WordprocessingMLPackage> wordMLPackages = paperDetailUnitRepo.findByPaperOrderByNumber(paper).stream()
-//                .map(PaperDetailUnit::getQuestion).collect(Collectors.toList()).stream()
-//                .map(question -> getPkgObj(question)).collect(Collectors.toList());
-//        return wordMLPackages;
-//    }
-//
-//    private WordprocessingMLPackage getPkgObj(Question question) {
-//        String pkgPathId = question.getQuesPkgPathId();
-//        QuestionPkgPath quesPkg = quesPkgPathRepo.findFirstById(pkgPathId);
-//        if (quesPkg == null) {
-//            byte[] bytes = new byte[0];
-//            return DocxProcessUtil.getPkg(bytes);
-//        }
-//        byte[] pkgByte = quesPkg.getQuesPkg();
-//        return DocxProcessUtil.getPkg(pkgByte);
-//    }
-
-    @Override
-    public void downQuestionDistributeByPapers(String paperIds, HttpServletResponse response) throws IOException {
-        List<QuestionDistributeDto> questionDistributes = new ArrayList<>();
-
-        // 定义课程集合
-        List<String> courseCodes = new ArrayList<>();
-
-        // 定义试题集合
-        List<Question> questions = new ArrayList<>();
-
-        // 查询试卷集合
-        String[] paperIdArray = paperIds.split(",");
-        for (int i = 0; i < paperIdArray.length; i++) {
-            Paper basePaper = Model.of(paperRepo.findById(paperIdArray[i]));
-            courseCodes.add(basePaper.getCourse().getCode());
-            // 将小题全部取出来,只取一次,减少对数据库的查询
-            List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(basePaper);
-            for (PaperDetailUnit unit : allPaperDetailUnits) {
-                if (unit.getQuestionType().equals(QuesStructType.SINGLE_ANSWER_QUESTION)
-                        || unit.getQuestionType().equals(QuesStructType.MULTIPLE_ANSWER_QUESTION)
-                        || unit.getQuestionType().equals(QuesStructType.BOOL_ANSWER_QUESTION)) {
-                    questions.add(unit.getQuestion());
-                }
-            }
-        }
-
-        // 根据课程code查询课程属性集合
-        for (String courseCode : courseCodes) {
-            List<CourseProperty> courseProperties = coursePropertyRepo.findByCourseCodeAndEnable(courseCode, true);
-            // 遍历课程属性集合,根据课程属性查询一级
-            if (courseProperties != null && courseProperties.size() > 0) {
-                for (CourseProperty courseProperty : courseProperties) {
-                    List<Property> parentProperties = propertyService.findParentProperties(courseProperty.getId(),
-                            courseProperty.getOrgId());
-                    if (parentProperties != null && parentProperties.size() > 0) {
-                        for (Property parentProperty : parentProperties) {
-                            List<Property> sonProperties = propertyService.findSonProperties(parentProperty.getId());
-                            if (sonProperties != null && sonProperties.size() > 0) {
-                                for (Property sonProperty : sonProperties) {
-                                    // 单选题集合
-                                    List<Question> sinList = questionList2(courseCode, courseProperty,
-                                            QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, sonProperty,
-                                            questions);
-                                    // 多选题集合
-                                    List<Question> mulList = questionList2(courseCode, courseProperty,
-                                            QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, sonProperty,
-                                            questions);
-                                    // 判断题集合
-                                    List<Question> bolList = questionList2(courseCode, courseProperty,
-                                            QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, sonProperty,
-                                            questions);
-
-                                    List<Question> fillList = null;
-                                    // fillList = questionList2(courseNo,
-                                    // courseProperty,
-                                    // QuesStructType.FILL_BLANK_QUESTION,
-                                    // parentProperty, sonProperty, questions);
-                                    List<Question> textList = null;
-                                    // textList = questionList2(courseNo,
-                                    // courseProperty,
-                                    // QuesStructType.TEXT_ANSWER_QUESTION,
-                                    // parentProperty, sonProperty, questions);
-
-                                    // 计算所有题型数量
-                                    Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
-                                    QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
-                                            courseProperty.getName(), parentProperty.getName(), sonProperty.getName(),
-                                            map);
-                                    questionDistributes.add(questionDistributeDto);
-                                }
-                            } else {
-                                // 一级属性不为空,二级属性为空
-                                // 单选题集合
-                                List<Question> sinList = questionList2(courseCode, courseProperty,
-                                        QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, null, questions);
-                                // 多选题集合
-                                List<Question> mulList = questionList2(courseCode, courseProperty,
-                                        QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, null, questions);
-                                // 判断题集合
-                                List<Question> bolList = questionList2(courseCode, courseProperty,
-                                        QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, null, questions);
-
-                                List<Question> fillList = null;
-                                // fillList = questionList2(courseNo,
-                                // courseProperty,
-                                // QuesStructType.FILL_BLANK_QUESTION,
-                                // parentProperty, null, questions);
-                                List<Question> textList = null;
-                                // textList = questionList2(courseNo,
-                                // courseProperty,
-                                // QuesStructType.TEXT_ANSWER_QUESTION,
-                                // parentProperty, null, questions);
-
-                                // 计算所有题型数量
-                                Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
-                                QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
-                                        courseProperty.getName(), parentProperty.getName(), null, map);
-                                questionDistributes.add(questionDistributeDto);
-                            }
-                        }
-                    } else {
-                        // 一级属性为空
-                        QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
-                                courseProperty.getName(), null, null, null);
-                        questionDistributes.add(questionDistributeDto);
-                    }
-                }
-            }
-
-            final String fileName = System.currentTimeMillis() + "试题分布.xlsx";
-            final String filePath = TEMP_FILE_EXP + File.separator + fileName;
-
-            // 生成Excel导出
-            File tempFile = this.writeExcel(questionDistributes, filePath);
-            FileDisposeUtil.downloadFile(fileName, filePath, response);
-            FileUtils.deleteQuietly(tempFile);
-        }
-    }
-
-    @Override
-    public void downOriginalPaperPlus(String paperId, Long rootOrgId, HttpServletResponse response) throws IOException{
-        String zipFileName = IdUtils.uuid();
-        File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
-        if (!directory.exists()) {
-            directory.mkdirs();
-        }
-        // doc固定源文件目录
-//        File docxDir = new File(JsonExportUtil.getDocxBasePath());
-//        // 将要生成的doc源文件目录
-//        File docxTargetDir = new File(directory.getAbsolutePath() + "/docx/");
-//        docxTargetDir.mkdir();
-        try {
-            // 复制docx基础文件
-//            FileUtils.copyDirectory(docxDir, docxTargetDir);
-            // 生成导出的试卷对象
-            PaperExp paperExp = paperService.getDownPaperExp(paperId);
-            
-            PaperUtil.setQuestionSeq(paperExp.getPaperDetails(), PaperSeqMode.MODE4);
-//            // 处理并生成doc源文件
-//            dispose(docxTargetDir, paperExp);
-//            // 压缩docx源文件
-//            String paperfileName = paperExp.getName() + "_" + paperExp.getCourseNo() + "_"
-//                    + ExamFileType.PAPER.getName();
-//            File zipfile = new File(docxTargetDir.getParentFile().getAbsolutePath() + File.separator + "data"
-//                    + File.separator + paperfileName + ZIP_SUFFIX);
-//            zipfile.getParentFile().mkdir();
-//            File docfile = new File(docxTargetDir.getParentFile().getAbsolutePath() + File.separator + "data"
-//                    + File.separator + File.separator + paperfileName + DOCX_SUFFIX);
-//            FileDisposeUtil.createZip(docxTargetDir.getAbsolutePath(), zipfile.getAbsolutePath());
-//            // 修改压缩包为doc文件
-//            zipfile.renameTo(docfile);
-            File docfile= ExportPaperUtil.createOriginPaperDocFile(paperExp, directory);
-            // doc文件加入下载包中
-            FileDisposeUtil.fileToZip(docfile.getParentFile().getAbsolutePath(),
-                    directory.getAbsolutePath(), zipFileName);
-            // 下载文件
-            FileDisposeUtil.downloadFile(paperExp.getName() + "_" + paperExp.getCourse().getCode() + ".zip",
-                    directory.getAbsolutePath() + File.separator + zipFileName + ".zip", response);
-        } finally {
-            FileUtils.deleteDirectory(directory);
-        }
-
-    }
-
-//    private void dispose(File docxTargetDir, PaperExp paperExp) throws IOException {
-//        ExportTempDataDto dto = new ExportTempDataDto();
-//        if (paperExp.getPaperDetails() != null && paperExp.getPaperDetails().size() > 0) {
-//            for (PaperDetailExp pde : paperExp.getPaperDetails()) {
-//                dto.setMainNum(dto.getMainNum() + 1);
-//                dto.setSubNum(0);
-//                if (pde.getPaperDetailUnits() != null && pde.getPaperDetailUnits().size() > 0) {
-//                    for (PaperDetailUnitExp pdue : pde.getPaperDetailUnits()) {
-//                        Question qes = pdue.getQuestion();
-//                        disposeQuestion(qes, dto);
-//                    }
-//                }
-//            }
-//        }
-//        // content-type
-//        writeContentType(docxTargetDir, dto);
-//        // document
-//        writeDocument(docxTargetDir, paperExp);
-//        // document-rel
-//        writeDocumentRel(docxTargetDir, dto);
-//        // image file
-//        writeImage(docxTargetDir, dto);
-//    }
-//
-//    private void writeImage(File docxTargetDir, ExportTempDataDto dto) throws IOException {
-//        for (SectionElement se : dto.getImages()) {
-//            File file = new File(docxTargetDir.getAbsolutePath() + "/word/media/image" + se.getParams().getIndex() + "."
-//                    + se.getParams().getType());
-//            String base64 = se.getValue();
-//            if (base64.contains("data:image")) {
-//                base64 = base64.substring(base64.indexOf(",") + 1);
-//            }
-//            BASE64Decoder decoder = new BASE64Decoder();
-//            byte[] bytes = decoder.decodeBuffer(base64);
-//            FileUtils.writeByteArrayToFile(file, bytes);
-//        }
-//    }
-//
-//    private void writeDocumentRel(File docxTargetDir, ExportTempDataDto dto) throws IOException {
-//        Map<String, Object> map = new HashMap<String, Object>();
-//        map.put("images", dto.getImages());
-//        String doc = JsonExportUtil.getDocumentRelDoc(map);
-//        File file = new File(docxTargetDir.getAbsolutePath() + "/word/_rels/document.xml.rels");
-//        FileUtils.writeStringToFile(file, doc, "utf-8");
-//    }
-//
-//    private void writeContentType(File docxTargetDir, ExportTempDataDto dto) throws IOException {
-//        Map<String, Object> map = new HashMap<String, Object>();
-//        map.put("images", dto.getTypes());
-//        String doc = JsonExportUtil.getContentTypesDoc(map);
-//        File file = new File(docxTargetDir.getAbsolutePath() + "/[Content_Types].xml");
-//        FileUtils.writeStringToFile(file, doc, "utf-8");
-//    }
-//
-//    private void writeDocument(File docxTargetDir, PaperExp paperExp) throws IOException {
-//        String doc = JsonExportUtil.getDocumentDoc(paperExp);
-//        File file = new File(docxTargetDir.getAbsolutePath() + "/word/document.xml");
-//        FileUtils.writeStringToFile(file, doc, "utf-8");
-//    }
-//
-//    private void disposeQuestion(Question qes, ExportTempDataDto dto) {
-//        if (qes != null) {
-//            List<JSection> slist1 = JsonExportUtil.getSections(qes.getQuesBody());
-//            if (slist1 != null && slist1.size() > 0) {
-//                if (qes.getSubQuestions() == null || qes.getSubQuestions().size() == 0) {// 套题题干不加题号
-//                    dto.setSubNum(dto.getSubNum() + 1);
-//                    SectionElement se = new SectionElement();
-//                    se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
-//                    se.setValue(dto.getSubNum() + ".");
-//                    slist1.get(0).getElements().add(0, se);
-//                }
-//
-//                htmlToDoc(slist1, dto);
-//                qes.setQuesBodyWord(getQuestionDoc(slist1));
-//            }
-//
-//            if (qes.getQuesOptions() != null && qes.getQuesOptions().size() > 0) {
-//                int index = 0;
-//                for (QuesOption qo : qes.getQuesOptions()) {
-//                    List<JSection> slist2 = JsonExportUtil.getSections(qo.getOptionBody());
-//                    if (slist2 != null && slist2.size() > 0) {
-//                        SectionElement se = new SectionElement();
-//                        se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
-//                        se.setValue(JsonExportUtil.QUESOPS.charAt(index) + ".");
-//                        index++;
-//                        slist2.get(0).getElements().add(0, se);
-//                        htmlToDoc(slist2, dto);
-//                        qo.setOptionBodyWord(getQuestionDoc(slist2));
-//                    }
-//
-//                }
-//            }
-//            List<JSection> slist3 = JsonExportUtil.getSections(qes.getQuesAnswer());
-//            if (slist3 != null && slist3.size() > 0) {
-//                SectionElement se = new SectionElement();
-//                se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
-//                se.setValue("[答案]:");
-//                slist3.get(0).getElements().add(0, se);
-//
-//                htmlToDoc(slist3, dto);
-//                qes.setQuesAnswerWord(getQuestionDoc(slist3));
-//            }
-//
-//            if (qes.getSubQuestions() != null && qes.getSubQuestions().size() > 0) {
-//                dto.setSubNum(0);
-//                for (Question sunqes : qes.getSubQuestions()) {
-//                    disposeQuestion(sunqes, dto);// 递归处理套题
-//                }
-//            }
-//        }
-//    }
-//
-//    private void htmlToDoc(List<JSection> slist, ExportTempDataDto dto) {
-//        for (JSection js : slist) {
-//            for (SectionElement se : js.getElements()) {
-//                if (JsonExportUtil.ELEMENT_TYPE_IMG.equals(se.getType())) {
-//                    dto.setIndex(dto.getIndex() + 1);
-//                    se.getParams().setIndex(dto.getIndex());
-//                    se.getParams().setRid(JsonExportUtil.ELEMENT_TYPE_RID + dto.getIndex());
-//                    se.getParams().setType(getImageType(se.getValue()));
-//                    dto.getTypes().add(se.getParams().getType());
-//                    dto.getImages().add(se);
-//                }
-//            }
-//        }
-//    }
-//
-//    private String getImageType(String base64) {
-//        return base64.substring(11, base64.indexOf(";"));
-//    }
-//
-//    private String getQuestionDoc(List<JSection> sections) {
-//        Map<String, Object> map = new HashMap<String, Object>();
-//        map.put("sections", sections);
-//        String doc = JsonExportUtil.getQuestionSectionDoc(map);
-//        return doc;
-//    }
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.examcloud.support.fss.FssFactory;
+import cn.com.qmth.examcloud.support.fss.FssHelper;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+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.stereotype.Service;
+
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+
+import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
+import cn.com.qmth.examcloud.core.questions.base.FileDisposeUtil;
+import cn.com.qmth.examcloud.core.questions.base.IdUtils;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
+import cn.com.qmth.examcloud.core.questions.base.enums.ExamFileType;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperSeqMode;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
+import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Block;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestPaperDetail;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.ComputerTestQuestion;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Section;
+import cn.com.qmth.examcloud.core.questions.dao.entity.computerTestModel.Sections;
+import cn.com.qmth.examcloud.core.questions.service.ExportPaperService;
+import cn.com.qmth.examcloud.core.questions.service.ExportThemisPaperService;
+import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.PropertyService;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.DownloadPaperDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.QuestionDistributeDto;
+import cn.com.qmth.examcloud.core.questions.service.converter.PrintExamPaperService;
+import cn.com.qmth.examcloud.core.questions.service.export.ExportPaperAbstractService;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
+import cn.com.qmth.examcloud.core.questions.service.util.ExportPaperUtil;
+import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+
+@Service("exportPaperService")
+public class ExportPaperServiceImpl implements ExportPaperService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExportPaperServiceImpl.class);
+
+    public static final String TEMP_FILE_EXP = "docxExport/";
+
+    public static final String TEMP_FILE_NAME = "_考试说明.docx";
+
+    public static final String DOCX_SUFFIX = ".docx";
+
+    public static final String ZIP_SUFFIX = ".zip";
+
+    @Autowired
+    private PaperRepo paperRepo;
+
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private PaperDetailService paperDetailService;
+
+    @Autowired
+    private MongoTemplate mongoTemplate;
+
+    @Autowired
+    private CoursePropertyRepo coursePropertyRepo;
+
+    @Autowired
+    private PropertyService propertyService;
+
+    @Autowired
+    private QuestionAudioRepo questionAudioRepo;
+
+    @Autowired
+    private PrintExamPaperService printExamPaperService;
+
+    @Autowired
+    private PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    private ExportThemisPaperService exportThemisPaperService;
+
+    // @Autowired
+    // protected QuesPkgPathRepo quesPkgPathRepo;
+
+    // @Autowired
+    // private SysProperty sysProperty;
+    @Override
+    public void exportPaperFile(String paperId, String exportContentList, HttpServletResponse response,
+            PaperSeqMode seqMode, String examType, String psw, User user) {
+        ExportPaperAbstractService exportPaperAbstractService = PaperUtil
+                .getByRootOrgId(user.getRootOrgId().toString());
+        // 根据试卷id查询试卷
+        Paper paper = Model.of(paperRepo.findById(paperId));
+
+        String zipFileName = IdUtils.uuid();
+        long startTime;
+        try {
+            File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
+            if (!directory.exists()) {
+                directory.mkdirs();
+            }
+            if (exportContentList.indexOf(ExamFileType.PRINT_EXAM_PACKAGE.name()) > -1) {
+                printExamPaperService.downloadPaper(Lists.newArrayList(paper), directory.getAbsolutePath(), psw);
+            }
+            if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
+                downJson(paper, zipFileName);
+            }
+            if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
+                List<Paper> papers = new ArrayList<>();
+                papers.add(paper);
+                downThemisPackage(papers, zipFileName);
+            }
+            try {
+                DownloadPaperDto dto = new DownloadPaperDto(user.getRootOrgId(), paperId, zipFileName, examType,
+                        seqMode);
+                if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
+                    exportPaperAbstractService.downloadPaper(dto);
+                }
+
+                if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
+                    exportPaperAbstractService.downloadPaperAnswer(dto);
+                }
+            } catch (StatusException e) {
+                LOG.error(e.getMessage(), e);
+                throw e;
+            } catch (Exception e) {
+                LOG.error(e.getMessage(), e);
+                throw new StatusException("100001", e.getMessage());
+            }
+            // 下载考试说明 2018-2-27 weiwehai
+            /*
+             * if(examType.equals("offLine") &&
+             * StringUtils.isNotBlank(paper.getExamRemark())){
+             * downExamRemark(paper,zipFileName); }
+             */
+            LOG.debug("开始压缩成zip...");
+            startTime = System.currentTimeMillis();
+            FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
+            FileDisposeUtil.downloadFile(paper.getName() + "_" + paper.getCourse().getCode() + ".zip",
+                    TEMP_FILE_EXP + File.separator + zipFileName + ".zip", response);
+            long endTime = System.currentTimeMillis();
+            LOG.debug("下载zip耗时:" + (endTime - startTime) + "ms");
+        } finally {
+            long endTime = System.currentTimeMillis();
+            FileUtil.deleteFolder(TEMP_FILE_EXP + File.separator + zipFileName);
+            FileUtil.deleteFolder(TEMP_FILE_EXP + File.separator + zipFileName + ".zip");
+            // deteleFolder(TEMP_FILE_EXP, zipFileName);
+            long deleteTime = System.currentTimeMillis();
+            LOG.debug("删除文件耗时:" + (deleteTime - endTime) + "ms");
+        }
+        StringBuilder paperInfo = new StringBuilder();
+        paperInfo.append("课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
+        paperInfo.append(" 试卷名称:" + paper.getName());
+        paperInfo.append(getContentName(exportContentList));
+
+        ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                AdminOperateType.TYPE36.getDesc(), paperInfo.toString()));
+    }
+
+    private String getContentName(String exportContentList) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(" 下载内容:");
+        for (String content : exportContentList.split(",")) {
+            sb.append(ExamFileType.valueOf(content).getName() + ",");
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        return sb.toString();
+    }
+
+    /**
+     * 下载考试说明
+     *
+     * @param zipFileName
+     * @throws Exception
+     */
+    // @SuppressWarnings("unused")
+    // private void downExamRemark(Paper paper, String zipFileName) throws
+    // Exception {
+    // // 1.考试说明html转成word
+    // String title = "<p style=\"text-align:center\"><span
+    // style=\"font-size:26px\"><span
+    // style=\"font-family:宋体\">考&nbsp;试&nbsp;说&nbsp;明</span></span></p>";
+    // WordprocessingMLPackage wordMLPackage =
+    // WordprocessingMLPackage.createPackage();
+    // DocxProcessUtil.html2Docx(wordMLPackage, CommonUtils.formatHtml(title +
+    // paper.getExamRemark()));
+    // // 2.导出考试说明word
+    // File file = new File(TEMP_FILE_EXP + File.separator + zipFileName +
+    // File.separator + paper.getCourse().getCode()
+    // + TEMP_FILE_NAME);
+    // Docx4J.save(wordMLPackage, file);
+    // }
+
+    // private void deteleFolder(String sourceFilePath, String zipFileName) {
+    // File ComputerTestPaperfoler = new File(sourceFilePath + File.separator +
+    // "json");
+    // if (ComputerTestPaperfoler.exists()) {
+    // FileUtils.deleteQuietly(ComputerTestPaperfoler);
+    // }
+    // File zipFolder = new File(sourceFilePath + File.separator + zipFileName);
+    // if (zipFolder.exists()) {
+    // FileUtils.deleteQuietly(zipFolder);
+    // }
+    // File zipFile = new File(sourceFilePath + File.separator + zipFileName +
+    // ".zip");
+    // if (zipFile.exists()) {
+    // FileUtils.deleteQuietly(zipFile);
+    // }
+    // }
+
+    private void downJson(Paper paper, String zipFileName) {
+        ComputerTestPaper computerTestPaper = buildComputerTestPapers(paper);
+        String jsonDirectory = TEMP_FILE_EXP + File.separator + zipFileName + File.separator + "json";
+        try {
+            // 新建文件夹
+            File dirFile = new File(jsonDirectory);
+            if (!dirFile.exists()) {
+                dirFile.mkdirs();
+            }
+            makeComputerTestPaperToJsonFile(paper.getCourse().getCode(), computerTestPaper, jsonDirectory);
+            downloadAudio(computerTestPaper, jsonDirectory);
+            // 将文件夹打包成zip压缩包放在docxExport下
+            FileDisposeUtil.fileToZip(jsonDirectory, TEMP_FILE_EXP + File.separator + zipFileName,
+                    paper.getCourse().getCode() + "_" + paper.getName());
+        } finally {
+            FileUtil.deleteDirectory(jsonDirectory);
+        }
+    }
+
+    private void downThemisPackage(List<Paper> papers, String zipFileName) {
+        String zipDirectory = TEMP_FILE_EXP + File.separator + zipFileName + File.separator + "themis_package";
+        // 新建文件夹
+        File dirFile = new File(zipDirectory);
+        if (!dirFile.exists()) {
+            dirFile.mkdirs();
+        }
+        try {
+            for (Paper paper : papers) {
+                ThemisPaperAndAnswer pa = null;
+                try {
+                    pa = exportThemisPaperService.buildPaperAndAnswer(paper);
+                } catch (StatusException e) {
+                    throw new StatusException("500", "试卷 " + paper.getName() + " " + e.getDesc(), e);
+                }
+                String paperDirectory = zipDirectory + File.separator + paper.getCourseNo() + File.separator
+                        + paper.getId();
+                File paperDir = new File(paperDirectory);
+                if (!paperDir.exists()) {
+                    paperDir.mkdirs();
+                }
+                exportThemisPaperService.makePaperAnswerToJsonFile(pa, paperDirectory);
+                exportThemisPaperService.downloadAudio(pa.getPaper(), paperDirectory);
+            }
+            // 将文件夹打包成zip压缩包放在docxExport下
+            File zipFile = new File(TEMP_FILE_EXP + File.separator + zipFileName + File.separator + "在线考试数据包.zip");
+            FileUtil.doZip(dirFile, zipFile);
+        } finally {
+            FileUtil.deleteDirectory(zipDirectory);
+        }
+    }
+
+    private void downloadAudio(ComputerTestPaper computerTestPaper, String jsonDirectory) {
+        // 取到所有大题
+        List<ComputerTestPaperDetail> details = computerTestPaper.getDetails();
+        if (details != null && details.size() > 0) {
+            for (ComputerTestPaperDetail detail : details) {
+                // 取到所有小题集合
+                List<ComputerTestQuestion> questions = detail.getQuestions();
+                if (questions != null && questions.size() > 0) {
+                    for (ComputerTestQuestion question : questions) {
+                        int bodyNum = 1;
+                        // 取到题干
+                        Sections body = question.getBody();
+                        List<Section> sections = body.getSections();
+                        for (Section section : sections) {
+                            List<Block> blocks = section.getBlocks();
+                            if (blocks != null && blocks.size() > 0) {
+                                for (Block block : blocks) {
+                                    if (block.getType().equals("audio")) {
+                                        String id = block.getValue();
+                                        QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+                                        String audioFileName = questionAudio.getId() + "_" + detail.getNumber() + "_"
+                                                + question.getNumber() + "_1_" + bodyNum + "."
+                                                + questionAudio.getFileSuffixes();
+
+                                        File file = new File(jsonDirectory + File.separator + audioFileName);
+
+                                        String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
+                                        FssFactory.getInstance().readFile(filePath, file);
+
+                                        bodyNum++;
+                                    }
+                                }
+                            }
+                        }
+                        // 取到选项
+                        List<ComputerTestOption> options = question.getOptions();
+                        if (options != null && options.size() > 0) {
+                            for (ComputerTestOption computerTestOption : options) {
+                                int optionNum = 1;
+                                // 获取选项主体
+                                Sections optionBody = computerTestOption.getBody();
+                                List<Section> optionSections = optionBody.getSections();
+                                if (optionSections != null && optionSections.size() > 0) {
+                                    for (Section optionSection : optionSections) {
+                                        List<Block> blocks = optionSection.getBlocks();
+                                        if (blocks != null && blocks.size() > 0) {
+                                            for (Block block : blocks) {
+                                                if (block.getType().equals("audio")) {
+                                                    String id = block.getValue();
+                                                    QuestionAudio questionAudio = Model
+                                                            .of(questionAudioRepo.findById(id));
+                                                    String audioFileName = questionAudio.getId() + "_"
+                                                            + detail.getNumber() + "_" + question.getNumber() + "_2_"
+                                                            + computerTestOption.getNumber() + "_" + optionNum + "."
+                                                            + questionAudio.getFileSuffixes();
+
+                                                    File file = new File(
+                                                            jsonDirectory + File.separator + audioFileName);
+
+                                                    String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
+                                                    FssFactory.getInstance().readFile(filePath, file);
+
+                                                    optionNum++;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 构建机考数据包实体类
+     *
+     * @return
+     */
+    public ComputerTestPaper buildComputerTestPapers(Paper paper) {
+        // 得到所有旧对象的大题对象
+        List<PaperDetail> paperDetails = paperService.findPaperDetailsById(paper.getId());
+        // 通过 paper 对象 ,生成新的 ComputerTestPaper 对象
+        ComputerTestPaper computerTestPaper = new ComputerTestPaper(paper, "");
+        List<ComputerTestPaperDetail> details = new ArrayList<>();
+        // 遍历所有旧大题对象,得到小题对象的集合
+        for (PaperDetail paperDetail : paperDetails) {
+            List<PaperDetailUnit> paperDetailUnits = paperDetailService.getUnitsByPaperDetailId(paperDetail.getId());
+            ComputerTestPaperDetail computerTestPaperDetail = new ComputerTestPaperDetail(paperDetail);
+            List<ComputerTestQuestion> questions = new ArrayList<>();
+            // 遍历所有的小题对象
+            for (int i = 0; i < paperDetailUnits.size(); i++) {
+                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(i);
+                // 根据旧的小题对象,生成新的小题对象
+                ComputerTestQuestion computerTestQuestion = new ComputerTestQuestion(paperDetailUnit);
+                // 设置小题题号
+                computerTestQuestion.setNumber(i + 1);
+                // 得到小题题干
+                computerTestQuestion
+                        .setBody(getBodyOrAnswer(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
+                // 得到小题所有选项
+                computerTestQuestion.setOptions(getOption(paperDetailUnit.getQuestion(), computerTestPaper));
+                // 得到小题的答案
+                computerTestQuestion
+                        .setAnswer(getBodyOrAnswer(paperDetailUnit.getQuestion().getQuesAnswer(), computerTestPaper));
+                // 查询小题中的 套题
+                List<Question> subQuestionsList = paperDetailUnit.getQuestion().getSubQuestions();
+                // 判断这个小题中是否有套题
+                if (subQuestionsList != null && subQuestionsList.size() > 0) {
+                    List<ComputerTestQuestion> subQuestions = new ArrayList<>();
+                    // 遍历每个套题
+                    for (int j = 0; j < subQuestionsList.size(); j++) {
+                        Question subQuestion = subQuestionsList.get(j);
+                        ComputerTestQuestion subcomputerTestQuestion = new ComputerTestQuestion(subQuestion);
+                        // 设置套题中小题题号
+                        subcomputerTestQuestion.setNumber(j + 1);
+                        subcomputerTestQuestion.setBody(getBodyOrAnswer(subQuestion.getQuesBody(), computerTestPaper));
+                        subcomputerTestQuestion.setOptions(getOption(subQuestion, computerTestPaper));
+                        subcomputerTestQuestion
+                                .setAnswer(getBodyOrAnswer(subQuestion.getQuesAnswer(), computerTestPaper));
+                        subQuestions.add(subcomputerTestQuestion);
+                    }
+                    computerTestQuestion.setSubQuestions(subQuestions);
+                }
+                questions.add(computerTestQuestion);
+            }
+            computerTestPaperDetail.setQuestions(questions);
+            // paperDetail中的题数(unitCount)可能不准确,这里以questions的实际size为准
+            computerTestPaperDetail.setQuestionCount(questions.size());
+            details.add(computerTestPaperDetail);
+        }
+        computerTestPaper.setDetails(details);
+        return computerTestPaper;
+    }
+
+    private Sections getBodyOrAnswer(String str, ComputerTestPaper computerTestPaper) {
+        Sections body = new Sections();
+        List<Section> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (StringUtils.isBlank(str)) {
+            return body;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<Block> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
+            if (blocks != null && blocks.size() > 0) {
+                Section section = new Section();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        body.setSections(sections);
+        return body;
+    }
+
+    private List<ComputerTestOption> getOption(Question question, ComputerTestPaper computerTestPaper) {
+        // 得到小题选项
+        List<QuesOption> quesOptions = question.getQuesOptions();
+        List<ComputerTestOption> options = new ArrayList<>();
+        // 遍历小题选项
+        if (quesOptions != null && quesOptions.size() > 0) {
+            for (QuesOption quesOption : quesOptions) {
+                ComputerTestOption option = new ComputerTestOption();
+                option.setNumber(new Integer(quesOption.getNumber()));
+                option.setCorrect(quesOption.getIsCorrect() == 1 ? true : false);
+                Sections body = new Sections();
+
+                List<Section> sections = new ArrayList<>();
+                // 得到小题选项
+                String optionString = quesOption.getOptionBody();
+                String[] optionStrings = optionString.split("</p>");
+                for (int i = 0; i < optionStrings.length; i++) {
+                    List<Block> blocks = disposeQuestionBodyOrOption(optionStrings[i], computerTestPaper);
+                    if (blocks != null && blocks.size() > 0) {
+                        Section section = new Section();
+                        section.setBlocks(blocks);
+                        sections.add(section);
+                    }
+                }
+                body.setSections(sections);
+                option.setBody(body);
+                options.add(option);
+            }
+        }
+        return options;
+    }
+
+    private List<Block> disposeQuestionBodyOrOption(String questionRow, ComputerTestPaper computerTestPaper) {
+        List<Block> blocks = new ArrayList<>();
+        // 去掉每行里面的<p>,<span>,</span>标签
+        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+                .replaceAll("</span>", "").replaceAll("</a>", "");
+        String[] questionRowStrings = questionRow.split("<|/>|>");
+        boolean hasAudio = false;
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            Block block = new Block();
+            String rowStr = questionRowStrings[i];
+            // 判断是否有图片
+            if (rowStr.startsWith("img")) {
+                rowStr = "<" + rowStr + ">";
+                Map<String, Object> param = new HashMap<>();
+                // 需要继续做截取,取到Parma
+                block.setType("image");
+                // 获取图片的路径
+                List<String> strSrcList = getImg(rowStr, "src");
+                if (strSrcList.size() > 0) {
+                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+                    block.setValue(strSrc);
+                }
+                // 获取图片的高度
+                List<String> strHeightList = getImg(rowStr, "height");
+                if (strHeightList.size() > 0) {
+                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+                    param.put("height", strHeight);
+                }
+                // 获取图片的宽度
+                List<String> strWidthList = getImg(rowStr, "width");
+                if (strHeightList.size() > 0) {
+                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+                    param.put("width", strWidth);
+                }
+                block.setParam(param);
+                blocks.add(block);
+            } else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+                rowStr = "<" + rowStr + ">";
+                block.setPlayTime(1);
+                block.setType("audio");
+                block.setValue(CommonUtils.getAttrValue(rowStr, "id"));
+                blocks.add(block);
+                hasAudio = true;
+            } else {
+                block.setType("text");
+                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+                if (StringUtils.isNotBlank(rowStr)) {
+                    block.setValue(rowStr);
+                    blocks.add(block);
+                }
+            }
+        }
+        if (hasAudio) {
+            computerTestPaper.setHasVideo(1);
+        }
+        return blocks;
+    }
+
+    /**
+     * 获取图片里面的路径,长度,宽度
+     */
+    private List<String> getImg(String s, String str) {
+        String regex;
+        List<String> list = new ArrayList<>();
+        regex = str + "=\"(.*?)\"";
+        Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
+        Matcher ma = pa.matcher(s);
+        while (ma.find()) {
+            list.add(ma.group());
+        }
+        return list;
+    }
+
+    /**
+     * 将computerTestPaper对象生成JSON文件存放在jsonDirectoryPath中
+     *
+     * @param computerTestPaper
+     * @param jsonDirectoryPath
+     */
+    @SuppressWarnings("deprecation")
+    private void makeComputerTestPaperToJsonFile(String courseCode, ComputerTestPaper computerTestPaper,
+            String jsonDirectoryPath) {
+        // 创建新的JSON文件
+        File file = new File(jsonDirectoryPath + File.separator + courseCode + ".json");
+        // 将对象转成 json对象
+        Gson gson = new Gson();
+        String strJSON = gson.toJson(computerTestPaper);
+
+        strJSON = CommonUtils.replaceUnicodeStr(strJSON);
+        // 生成文件流写入JSON文件
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = new FileOutputStream(file);
+            byte b[] = strJSON.getBytes();
+            outputStream.write(b);
+            outputStream.flush();
+        } catch (FileNotFoundException e) {
+            LOG.error(e.getMessage(), e);
+        } catch (IOException e) {
+            LOG.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
+    @Override
+    public void exportPaperFiles(List<String> paperIds, String exportContentList, HttpServletResponse response,
+            PaperSeqMode seqMode, String examType, User user) throws Exception {
+        ExportPaperAbstractService exportPaperAbstractService = PaperUtil
+                .getByRootOrgId(user.getRootOrgId().toString());
+        // 根据试卷id查询所有试卷
+        List<Paper> papers = CommonUtils.toList(paperRepo.findByIdIn(paperIds));
+        String zipFileName = IdUtils.uuid();
+        try {
+            // 创建压缩文件夹
+            File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
+            directory.mkdirs();
+            // 下载试卷
+            if (exportContentList.indexOf(ExamFileType.PAPER.name()) > -1) {
+                for (Paper paper : papers) {
+                    DownloadPaperDto dto = new DownloadPaperDto(user.getRootOrgId(), paper.getId(), zipFileName,
+                            examType, seqMode);
+                    exportPaperAbstractService.downloadPaper(dto);
+                }
+            }
+            // 下载答案
+            if (exportContentList.indexOf(ExamFileType.ANSWER.name()) > -1) {
+                for (Paper paper : papers) {
+                    DownloadPaperDto dto = new DownloadPaperDto(user.getRootOrgId(), paper.getId(), zipFileName,
+                            examType, seqMode);
+                    exportPaperAbstractService.downloadPaperAnswer(dto);
+                }
+            }
+            // 下载机考数据包
+            if (exportContentList.indexOf(ExamFileType.COMPUTERTEST_PACKAGE.name()) > -1) {
+                for (Paper paper : papers) {
+                    downJson(paper, zipFileName);
+                }
+            }
+            // 下载在线考试数据包
+            if (exportContentList.indexOf(ExamFileType.THEMIS_PACKAGE.name()) > -1) {
+                downThemisPackage(papers, zipFileName);
+            }
+            FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator + zipFileName, TEMP_FILE_EXP, zipFileName);
+            FileDisposeUtil.downloadFile(zipFileName + ".zip", TEMP_FILE_EXP + File.separator + zipFileName + ".zip",
+                    response);
+        } finally {
+            FileUtil.deleteFolder(TEMP_FILE_EXP + File.separator + zipFileName);
+            FileUtil.deleteFolder(TEMP_FILE_EXP + File.separator + zipFileName + ".zip");
+        }
+        StringBuilder paperInfo = new StringBuilder();
+        paperInfo.append("下载数量:" + paperIds.size());
+        paperInfo.append(getContentName(exportContentList));
+
+        ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                AdminOperateType.TYPE37.getDesc(), paperInfo.toString()));
+
+    }
+
+    @Override
+    public void downQuestionDistribute(String courseCode, HttpServletResponse response) throws IOException {
+        // 生成导出Excle的list集合
+        List<QuestionDistributeDto> questionDistributes = this.downQuestionDistribute(courseCode);
+
+        final String fileName = courseCode + "试题分布.xlsx";
+        final String filePath = TEMP_FILE_EXP + File.separator + fileName;
+
+        // 生成Excel导出
+        File tempFile = this.writeExcel(questionDistributes, filePath);
+        FileDisposeUtil.downloadFile(fileName, filePath, response);
+        FileUtils.deleteQuietly(tempFile);
+    }
+
+    @Override
+    public void downQuestionDistributes(List<Course> courses) throws IOException {
+        if (CollectionUtils.isEmpty(courses)) {
+            return;
+        }
+
+        for (Course course : courses) {
+            List<QuestionDistributeDto> questionDistributes = this.downQuestionDistribute(course.getCode());
+            final String fileName = course.getCode() + "试题分布.xlsx";
+            final String filePath = TEMP_FILE_EXP + File.separator + fileName;
+            this.writeExcel(questionDistributes, filePath);
+        }
+    }
+
+    private List<QuestionDistributeDto> downQuestionDistribute(String courseCode) {
+        // 1.生成导出Excle的list集合
+        List<QuestionDistributeDto> questionDistributes = new ArrayList<>();
+
+        // 2.根据课程code查询课程属性集合
+        List<CourseProperty> courseProperties = coursePropertyRepo.findByCourseCodeAndEnable(courseCode, true);
+        if (CollectionUtils.isEmpty(courseProperties)) {
+            return questionDistributes;
+        }
+
+        // 3.遍历课程属性集合,根据课程属性查询一级
+        for (CourseProperty courseProperty : courseProperties) {
+            List<Property> parentProperties = propertyService.findParentProperties(courseProperty.getId(),
+                    courseProperty.getOrgId());
+            if (parentProperties != null && parentProperties.size() > 0) {
+                for (Property parentProperty : parentProperties) {
+                    List<Property> sonProperties = propertyService.findSonProperties(parentProperty.getId());
+                    if (sonProperties != null && sonProperties.size() > 0) {
+                        for (Property sonProperty : sonProperties) {
+                            // 单选题集合
+                            List<Question> sinList = questionList(courseCode, courseProperty,
+                                    QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, sonProperty);
+                            // 多选题集合
+                            List<Question> mulList = questionList(courseCode, courseProperty,
+                                    QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, sonProperty);
+                            // 判断题集合
+                            List<Question> bolList = questionList(courseCode, courseProperty,
+                                    QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, sonProperty);
+
+                            List<Question> fillList = null;
+                            // fillList = questionList(courseCode,
+                            // courseProperty,
+                            // QuesStructType.FILL_BLANK_QUESTION,
+                            // parentProperty, sonProperty);
+                            List<Question> textList = null;
+                            // textList = questionList(courseCode,
+                            // courseProperty,
+                            // QuesStructType.TEXT_ANSWER_QUESTION,
+                            // parentProperty, sonProperty);
+
+                            // 计算所有题型数量
+                            Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
+                            QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
+                                    courseProperty.getName(), parentProperty.getName(), sonProperty.getName(), map);
+                            questionDistributes.add(questionDistributeDto);
+                        }
+                    } else {
+                        // 一级属性不为空,二级属性为空
+                        // 单选题集合
+                        List<Question> sinList = questionList(courseCode, courseProperty,
+                                QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, null);
+                        // 多选题集合
+                        List<Question> mulList = questionList(courseCode, courseProperty,
+                                QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, null);
+                        // 判断题集合
+                        List<Question> bolList = questionList(courseCode, courseProperty,
+                                QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, null);
+
+                        List<Question> fillList = null;
+                        // fillList = questionList(courseCode, courseProperty,
+                        // QuesStructType.FILL_BLANK_QUESTION, parentProperty,
+                        // null);
+                        List<Question> textList = null;
+                        // textList = questionList(courseCode, courseProperty,
+                        // QuesStructType.TEXT_ANSWER_QUESTION, parentProperty,
+                        // null);
+
+                        // 计算所有题型数量
+                        Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
+                        QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
+                                courseProperty.getName(), parentProperty.getName(), null, map);
+                        questionDistributes.add(questionDistributeDto);
+                    }
+                }
+            } else {
+                // 一级属性为空
+                QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(courseProperty.getName(), null,
+                        null, null);
+                questionDistributes.add(questionDistributeDto);
+            }
+        }
+
+        return questionDistributes;
+    }
+
+    @SuppressWarnings("resource")
+    private File writeExcel(List<QuestionDistributeDto> questionDistributes, String filePath) throws IOException {
+        // 读取Excel模板
+        InputStream in = this.getClass().getResourceAsStream("/quesDistinct.xlsx");
+        // InputStream in =
+        // this.getClass().getResourceAsStream("/quesDistinctAll.xlsx");
+
+        // 获取第一个工作页
+        Workbook workBook = new XSSFWorkbook(in);
+        Sheet sheet = workBook.getSheetAt(0);
+
+        // 往Excel中写入数据,从第5行开始
+        for (int i = 0; i < questionDistributes.size(); i++) {
+            // 创建一行:从第五行开始,跳过表头
+            Row row = sheet.createRow(i + 4);
+
+            // 获取这行的记录
+            QuestionDistributeDto questionDistributeDto = questionDistributes.get(i);
+
+            // 每列赋值
+            row.createCell(0).setCellValue(questionDistributeDto.getCoursePropertyName());
+            row.createCell(1).setCellValue(questionDistributeDto.getFirstPropertyName());
+            row.createCell(2).setCellValue(questionDistributeDto.getSecondPropertyName());
+            Map<Long, Long> map = questionDistributeDto.getMap();
+            if (map == null) {
+                for (int j = 0; j < 18; j++) {
+                    row.createCell(j + 3).setCellValue(0);
+                }
+            } else {
+                int j = 3;
+                for (Long key : map.keySet()) {
+                    row.createCell(j).setCellValue(map.get(key));
+                    j++;
+                }
+            }
+        }
+
+        LOG.info("[WriteExcel] " + filePath);
+
+        File file = new File(filePath);
+        OutputStream out = new FileOutputStream(file);
+        workBook.write(out);
+        out.close();
+        return file;
+    }
+
+    private Map<Long, Long> countQuesType(List<Question> sinList, List<Question> mulList, List<Question> bolList,
+            List<Question> fillList, List<Question> textList) {
+        Map<Long, Long> map = new TreeMap<>();
+        map = buildMap(sinList, map, 1);
+        map = buildMap(mulList, map, 2);
+        map = buildMap(bolList, map, 3);
+
+        if (fillList != null) {
+            map = buildMap(fillList, map, 4);
+        }
+        if (textList != null) {
+            map = buildMap(textList, map, 5);
+        }
+
+        // 给map的键排序
+        Map<Long, Long> resultMap = sortMapByKey(map);
+        return resultMap;
+    }
+
+    private Map<Long, Long> sortMapByKey(Map<Long, Long> map) {
+        if (map == null || map.isEmpty()) {
+            return null;
+        }
+        Map<Long, Long> sortMap = new TreeMap<Long, Long>(new Comparator<Long>() {
+
+            public int compare(Long l1, Long l2) {
+                return l1.compareTo(l2);
+            }
+        });
+        sortMap.putAll(map);
+        return sortMap;
+    }
+
+    private Map<Long, Long> buildMap(List<Question> quesList, Map<Long, Long> map, int questionType) {
+        // 初始化map
+        for (int i = 1; i < 7; i++) {
+            if (i < 4) {
+                map.put((long) (questionType * 100 + i), 0L);
+            } else {
+                map.put((long) (questionType * 100 + 10 + i - 3), 0L);
+            }
+        }
+        for (Question question : quesList) {
+            if (question.getPublicity()) {
+                // 公开
+                if (question.getDifficulty() != null && question.getDifficulty().equals("难")
+                        || question.getDifficultyDegree() < 0.4 && question.getDifficultyDegree() > 0) {
+                    map = buildMapSum(questionType * 100L + 1L, map);
+                } else if (question.getDifficulty() != null && question.getDifficulty().equals("中")
+                        || question.getDifficultyDegree() < 0.8 && question.getDifficultyDegree() > 0.3) {
+                    map = buildMapSum(questionType * 100L + 2L, map);
+                } else {
+                    map = buildMapSum(questionType * 100L + 3L, map);
+                }
+            } else {
+                // 非公开
+                if (question.getDifficulty() != null && question.getDifficulty().equals("难")
+                        || question.getDifficultyDegree() < 0.4 && question.getDifficultyDegree() > 0) {
+                    map = buildMapSum(questionType * 100L + 11L, map);
+                } else if (question.getDifficulty() != null && question.getDifficulty().equals("中")
+                        || question.getDifficultyDegree() < 0.8 && question.getDifficultyDegree() > 0.3) {
+                    map = buildMapSum(questionType * 100L + 12L, map);
+                } else {
+                    map = buildMapSum(questionType * 100L + 13L, map);
+                }
+            }
+        }
+        return map;
+    }
+
+    private Map<Long, Long> buildMapSum(long key, Map<Long, Long> map) {
+        Long sum = map.get(key);
+        map.put(key, sum + 1);
+        return map;
+    }
+
+    public List<Question> questionList(String courseNo, CourseProperty courseProperty, QuesStructType quesStructType,
+            Property parentProperty, Property sonProperty) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("orgId").is(courseProperty.getOrgId().toString()));
+        query.addCriteria(Criteria.where("course.enable").is("true"));
+        query.addCriteria(Criteria.where("course.code").is(courseNo));
+        query.addCriteria(Criteria.where("questionType").is(quesStructType.name()));
+        query.addCriteria(Criteria.where("quesProperties.coursePropertyName").is(courseProperty.getName()));
+        // 二级属性不为空,那么一级属性也不为空
+        if (sonProperty != null && sonProperty.getId() != null) {
+            query.addCriteria(Criteria.where("quesProperties").elemMatch(Criteria.where("firstProperty.id")
+                    .is(parentProperty.getId()).and("secondProperty.id").is(sonProperty.getId())));
+        } else {
+            if (parentProperty != null && parentProperty.getId() != null) {
+                query.addCriteria(Criteria.where("quesProperties")
+                        .elemMatch(Criteria.where("firstProperty.id").is(parentProperty.getId())));
+            }
+        }
+        List<Question> questionList = this.mongoTemplate.find(query, Question.class);
+        return questionList;
+    }
+
+    public List<Question> questionList2(String courseNo, CourseProperty courseProperty, QuesStructType quesStructType,
+            Property parentProperty, Property sonProperty, List<Question> questions) {
+        List<Question> questionList = new ArrayList<>();
+        String id = courseNo + quesStructType.name() + courseProperty.getName();
+        // 二级属性不为空,那么一级属性也不为空
+        if (sonProperty != null && sonProperty.getId() != null) {
+            id = id + parentProperty.getId() + sonProperty.getId();
+        } else {
+            if (parentProperty != null && parentProperty.getId() != null) {
+                id = id + parentProperty.getId();
+            }
+        }
+        for (Question question : questions) {
+            List<String> idStrings = new ArrayList<>();
+            String id_Q = question.getCourseNo() + question.getQuestionType();
+            List<QuesProperty> quesProperties = question.getQuesProperties();
+            if (quesProperties != null && quesProperties.size() > 0) {
+                for (QuesProperty property : quesProperties) {
+                    String idP = id_Q + property.getCoursePropertyName() + property.getFirstProperty().getId();
+                    if (property.getSecondProperty() != null) {
+                        idP = id_Q + property.getCoursePropertyName() + property.getFirstProperty().getId()
+                                + property.getSecondProperty().getId();
+                    }
+                    idStrings.add(idP);
+                }
+                if (idStrings.contains(id)) {
+                    questionList.add(question);
+                }
+            }
+        }
+        return questionList;
+    }
+
+    @Override
+    public void downOriginalPaper(String paperId, String loginName, HttpServletResponse response) throws Exception {
+        // String zipFileName = loginName + System.currentTimeMillis() + "";
+        // try {
+        // // 生成导出的试卷对象
+        // PaperExp paperExp = paperService.getDownPaperExp(paperId);
+        // File directory = new File(TEMP_FILE_EXP + File.separator +
+        // zipFileName);
+        // if (!directory.exists()) {
+        // directory.mkdirs();
+        // }
+        // String paperfileName = paperExp.getName() + "_" +
+        // paperExp.getCourseNo() + "_"
+        // + ExamFileType.PAPER.getName() + DOCX_SUFFIX;
+        // File file = new File(TEMP_FILE_EXP + File.separator + zipFileName +
+        // File.separator + paperfileName);
+        // List<WordprocessingMLPackage> wordPackages = getPkgList(paperId);
+        // DocxProcessUtil.exportWordNew(paperExp, file,
+        // ExportPaperAbstractService.ORIGINAL_PAPER);
+        // DocxProcessUtil.processImage(zipFileName + File.separator +
+        // paperfileName, wordPackages);
+        // FileDisposeUtil.fileToZip(TEMP_FILE_EXP + File.separator +
+        // zipFileName, TEMP_FILE_EXP, zipFileName);
+        // FileDisposeUtil.downloadFile(paperExp.getName() + "_" +
+        // paperExp.getCourse().getCode() + ".zip",
+        // TEMP_FILE_EXP + File.separator + zipFileName + ".zip", response);
+        // } finally {
+        // deteleFolder(TEMP_FILE_EXP, zipFileName);
+        // }
+    }
+
+    /**
+     * 获取当前试卷下所有试题WordPkg
+     *
+     * @param id
+     * @return
+     */
+    // protected List<WordprocessingMLPackage> getPkgList(String id) {
+    // Paper paper = Model.of(paperRepo.findById(id));
+    // List<WordprocessingMLPackage> wordMLPackages =
+    // paperDetailUnitRepo.findByPaperOrderByNumber(paper).stream()
+    // .map(PaperDetailUnit::getQuestion).collect(Collectors.toList()).stream()
+    // .map(question -> getPkgObj(question)).collect(Collectors.toList());
+    // return wordMLPackages;
+    // }
+    //
+    // private WordprocessingMLPackage getPkgObj(Question question) {
+    // String pkgPathId = question.getQuesPkgPathId();
+    // QuestionPkgPath quesPkg = quesPkgPathRepo.findFirstById(pkgPathId);
+    // if (quesPkg == null) {
+    // byte[] bytes = new byte[0];
+    // return DocxProcessUtil.getPkg(bytes);
+    // }
+    // byte[] pkgByte = quesPkg.getQuesPkg();
+    // return DocxProcessUtil.getPkg(pkgByte);
+    // }
+
+    @Override
+    public void downQuestionDistributeByPapers(String paperIds, HttpServletResponse response) throws IOException {
+        List<QuestionDistributeDto> questionDistributes = new ArrayList<>();
+
+        // 定义课程集合
+        List<String> courseCodes = new ArrayList<>();
+
+        // 定义试题集合
+        List<Question> questions = new ArrayList<>();
+
+        // 查询试卷集合
+        String[] paperIdArray = paperIds.split(",");
+        for (int i = 0; i < paperIdArray.length; i++) {
+            Paper basePaper = Model.of(paperRepo.findById(paperIdArray[i]));
+            courseCodes.add(basePaper.getCourse().getCode());
+            // 将小题全部取出来,只取一次,减少对数据库的查询
+            List<PaperDetailUnit> allPaperDetailUnits = paperDetailUnitRepo.findByPaper(basePaper);
+            for (PaperDetailUnit unit : allPaperDetailUnits) {
+                if (unit.getQuestionType().equals(QuesStructType.SINGLE_ANSWER_QUESTION)
+                        || unit.getQuestionType().equals(QuesStructType.MULTIPLE_ANSWER_QUESTION)
+                        || unit.getQuestionType().equals(QuesStructType.BOOL_ANSWER_QUESTION)) {
+                    questions.add(unit.getQuestion());
+                }
+            }
+        }
+
+        // 根据课程code查询课程属性集合
+        for (String courseCode : courseCodes) {
+            List<CourseProperty> courseProperties = coursePropertyRepo.findByCourseCodeAndEnable(courseCode, true);
+            // 遍历课程属性集合,根据课程属性查询一级
+            if (courseProperties != null && courseProperties.size() > 0) {
+                for (CourseProperty courseProperty : courseProperties) {
+                    List<Property> parentProperties = propertyService.findParentProperties(courseProperty.getId(),
+                            courseProperty.getOrgId());
+                    if (parentProperties != null && parentProperties.size() > 0) {
+                        for (Property parentProperty : parentProperties) {
+                            List<Property> sonProperties = propertyService.findSonProperties(parentProperty.getId());
+                            if (sonProperties != null && sonProperties.size() > 0) {
+                                for (Property sonProperty : sonProperties) {
+                                    // 单选题集合
+                                    List<Question> sinList = questionList2(courseCode, courseProperty,
+                                            QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, sonProperty,
+                                            questions);
+                                    // 多选题集合
+                                    List<Question> mulList = questionList2(courseCode, courseProperty,
+                                            QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, sonProperty,
+                                            questions);
+                                    // 判断题集合
+                                    List<Question> bolList = questionList2(courseCode, courseProperty,
+                                            QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, sonProperty,
+                                            questions);
+
+                                    List<Question> fillList = null;
+                                    // fillList = questionList2(courseNo,
+                                    // courseProperty,
+                                    // QuesStructType.FILL_BLANK_QUESTION,
+                                    // parentProperty, sonProperty, questions);
+                                    List<Question> textList = null;
+                                    // textList = questionList2(courseNo,
+                                    // courseProperty,
+                                    // QuesStructType.TEXT_ANSWER_QUESTION,
+                                    // parentProperty, sonProperty, questions);
+
+                                    // 计算所有题型数量
+                                    Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
+                                    QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
+                                            courseProperty.getName(), parentProperty.getName(), sonProperty.getName(),
+                                            map);
+                                    questionDistributes.add(questionDistributeDto);
+                                }
+                            } else {
+                                // 一级属性不为空,二级属性为空
+                                // 单选题集合
+                                List<Question> sinList = questionList2(courseCode, courseProperty,
+                                        QuesStructType.SINGLE_ANSWER_QUESTION, parentProperty, null, questions);
+                                // 多选题集合
+                                List<Question> mulList = questionList2(courseCode, courseProperty,
+                                        QuesStructType.MULTIPLE_ANSWER_QUESTION, parentProperty, null, questions);
+                                // 判断题集合
+                                List<Question> bolList = questionList2(courseCode, courseProperty,
+                                        QuesStructType.BOOL_ANSWER_QUESTION, parentProperty, null, questions);
+
+                                List<Question> fillList = null;
+                                // fillList = questionList2(courseNo,
+                                // courseProperty,
+                                // QuesStructType.FILL_BLANK_QUESTION,
+                                // parentProperty, null, questions);
+                                List<Question> textList = null;
+                                // textList = questionList2(courseNo,
+                                // courseProperty,
+                                // QuesStructType.TEXT_ANSWER_QUESTION,
+                                // parentProperty, null, questions);
+
+                                // 计算所有题型数量
+                                Map<Long, Long> map = countQuesType(sinList, mulList, bolList, fillList, textList);
+                                QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
+                                        courseProperty.getName(), parentProperty.getName(), null, map);
+                                questionDistributes.add(questionDistributeDto);
+                            }
+                        }
+                    } else {
+                        // 一级属性为空
+                        QuestionDistributeDto questionDistributeDto = new QuestionDistributeDto(
+                                courseProperty.getName(), null, null, null);
+                        questionDistributes.add(questionDistributeDto);
+                    }
+                }
+            }
+
+            final String fileName = System.currentTimeMillis() + "试题分布.xlsx";
+            final String filePath = TEMP_FILE_EXP + File.separator + fileName;
+
+            // 生成Excel导出
+            File tempFile = this.writeExcel(questionDistributes, filePath);
+            FileDisposeUtil.downloadFile(fileName, filePath, response);
+            FileUtils.deleteQuietly(tempFile);
+        }
+    }
+
+    @Override
+    public void downOriginalPaperPlus(String paperId, Long rootOrgId, HttpServletResponse response) throws IOException {
+        String zipFileName = IdUtils.uuid();
+        File directory = new File(TEMP_FILE_EXP + File.separator + zipFileName);
+        if (!directory.exists()) {
+            directory.mkdirs();
+        }
+        // doc固定源文件目录
+        // File docxDir = new File(JsonExportUtil.getDocxBasePath());
+        // // 将要生成的doc源文件目录
+        // File docxTargetDir = new File(directory.getAbsolutePath() +
+        // "/docx/");
+        // docxTargetDir.mkdir();
+        try {
+            // 复制docx基础文件
+            // FileUtils.copyDirectory(docxDir, docxTargetDir);
+            // 生成导出的试卷对象
+            PaperExp paperExp = paperService.getDownPaperExp(paperId);
+
+            PaperUtil.setQuestionSeq(paperExp.getPaperDetails(), PaperSeqMode.MODE4);
+            // // 处理并生成doc源文件
+            // dispose(docxTargetDir, paperExp);
+            // // 压缩docx源文件
+            // String paperfileName = paperExp.getName() + "_" +
+            // paperExp.getCourseNo() + "_"
+            // + ExamFileType.PAPER.getName();
+            // File zipfile = new
+            // File(docxTargetDir.getParentFile().getAbsolutePath() +
+            // File.separator + "data"
+            // + File.separator + paperfileName + ZIP_SUFFIX);
+            // zipfile.getParentFile().mkdir();
+            // File docfile = new
+            // File(docxTargetDir.getParentFile().getAbsolutePath() +
+            // File.separator + "data"
+            // + File.separator + File.separator + paperfileName + DOCX_SUFFIX);
+            // FileDisposeUtil.createZip(docxTargetDir.getAbsolutePath(),
+            // zipfile.getAbsolutePath());
+            // // 修改压缩包为doc文件
+            // zipfile.renameTo(docfile);
+            File docfile = ExportPaperUtil.createOriginPaperDocFile(paperExp, directory);
+            // doc文件加入下载包中
+            FileDisposeUtil.fileToZip(docfile.getParentFile().getAbsolutePath(), directory.getAbsolutePath(),
+                    zipFileName);
+            // 下载文件
+            FileDisposeUtil.downloadFile(paperExp.getName() + "_" + paperExp.getCourse().getCode() + ".zip",
+                    directory.getAbsolutePath() + File.separator + zipFileName + ".zip", response);
+        } finally {
+            FileUtils.deleteDirectory(directory);
+        }
+
+    }
+
+    // private void dispose(File docxTargetDir, PaperExp paperExp) throws
+    // IOException {
+    // ExportTempDataDto dto = new ExportTempDataDto();
+    // if (paperExp.getPaperDetails() != null &&
+    // paperExp.getPaperDetails().size() > 0) {
+    // for (PaperDetailExp pde : paperExp.getPaperDetails()) {
+    // dto.setMainNum(dto.getMainNum() + 1);
+    // dto.setSubNum(0);
+    // if (pde.getPaperDetailUnits() != null && pde.getPaperDetailUnits().size()
+    // > 0) {
+    // for (PaperDetailUnitExp pdue : pde.getPaperDetailUnits()) {
+    // Question qes = pdue.getQuestion();
+    // disposeQuestion(qes, dto);
+    // }
+    // }
+    // }
+    // }
+    // // content-type
+    // writeContentType(docxTargetDir, dto);
+    // // document
+    // writeDocument(docxTargetDir, paperExp);
+    // // document-rel
+    // writeDocumentRel(docxTargetDir, dto);
+    // // image file
+    // writeImage(docxTargetDir, dto);
+    // }
+    //
+    // private void writeImage(File docxTargetDir, ExportTempDataDto dto) throws
+    // IOException {
+    // for (SectionElement se : dto.getImages()) {
+    // File file = new File(docxTargetDir.getAbsolutePath() +
+    // "/word/media/image" + se.getParams().getIndex() + "."
+    // + se.getParams().getType());
+    // String base64 = se.getValue();
+    // if (base64.contains("data:image")) {
+    // base64 = base64.substring(base64.indexOf(",") + 1);
+    // }
+    // BASE64Decoder decoder = new BASE64Decoder();
+    // byte[] bytes = decoder.decodeBuffer(base64);
+    // FileUtils.writeByteArrayToFile(file, bytes);
+    // }
+    // }
+    //
+    // private void writeDocumentRel(File docxTargetDir, ExportTempDataDto dto)
+    // throws IOException {
+    // Map<String, Object> map = new HashMap<String, Object>();
+    // map.put("images", dto.getImages());
+    // String doc = JsonExportUtil.getDocumentRelDoc(map);
+    // File file = new File(docxTargetDir.getAbsolutePath() +
+    // "/word/_rels/document.xml.rels");
+    // FileUtils.writeStringToFile(file, doc, "utf-8");
+    // }
+    //
+    // private void writeContentType(File docxTargetDir, ExportTempDataDto dto)
+    // throws IOException {
+    // Map<String, Object> map = new HashMap<String, Object>();
+    // map.put("images", dto.getTypes());
+    // String doc = JsonExportUtil.getContentTypesDoc(map);
+    // File file = new File(docxTargetDir.getAbsolutePath() +
+    // "/[Content_Types].xml");
+    // FileUtils.writeStringToFile(file, doc, "utf-8");
+    // }
+    //
+    // private void writeDocument(File docxTargetDir, PaperExp paperExp) throws
+    // IOException {
+    // String doc = JsonExportUtil.getDocumentDoc(paperExp);
+    // File file = new File(docxTargetDir.getAbsolutePath() +
+    // "/word/document.xml");
+    // FileUtils.writeStringToFile(file, doc, "utf-8");
+    // }
+    //
+    // private void disposeQuestion(Question qes, ExportTempDataDto dto) {
+    // if (qes != null) {
+    // List<JSection> slist1 = JsonExportUtil.getSections(qes.getQuesBody());
+    // if (slist1 != null && slist1.size() > 0) {
+    // if (qes.getSubQuestions() == null || qes.getSubQuestions().size() == 0)
+    // {// 套题题干不加题号
+    // dto.setSubNum(dto.getSubNum() + 1);
+    // SectionElement se = new SectionElement();
+    // se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
+    // se.setValue(dto.getSubNum() + ".");
+    // slist1.get(0).getElements().add(0, se);
+    // }
+    //
+    // htmlToDoc(slist1, dto);
+    // qes.setQuesBodyWord(getQuestionDoc(slist1));
+    // }
+    //
+    // if (qes.getQuesOptions() != null && qes.getQuesOptions().size() > 0) {
+    // int index = 0;
+    // for (QuesOption qo : qes.getQuesOptions()) {
+    // List<JSection> slist2 = JsonExportUtil.getSections(qo.getOptionBody());
+    // if (slist2 != null && slist2.size() > 0) {
+    // SectionElement se = new SectionElement();
+    // se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
+    // se.setValue(JsonExportUtil.QUESOPS.charAt(index) + ".");
+    // index++;
+    // slist2.get(0).getElements().add(0, se);
+    // htmlToDoc(slist2, dto);
+    // qo.setOptionBodyWord(getQuestionDoc(slist2));
+    // }
+    //
+    // }
+    // }
+    // List<JSection> slist3 = JsonExportUtil.getSections(qes.getQuesAnswer());
+    // if (slist3 != null && slist3.size() > 0) {
+    // SectionElement se = new SectionElement();
+    // se.setType(JsonExportUtil.ELEMENT_TYPE_TEXT);
+    // se.setValue("[答案]:");
+    // slist3.get(0).getElements().add(0, se);
+    //
+    // htmlToDoc(slist3, dto);
+    // qes.setQuesAnswerWord(getQuestionDoc(slist3));
+    // }
+    //
+    // if (qes.getSubQuestions() != null && qes.getSubQuestions().size() > 0) {
+    // dto.setSubNum(0);
+    // for (Question sunqes : qes.getSubQuestions()) {
+    // disposeQuestion(sunqes, dto);// 递归处理套题
+    // }
+    // }
+    // }
+    // }
+    //
+    // private void htmlToDoc(List<JSection> slist, ExportTempDataDto dto) {
+    // for (JSection js : slist) {
+    // for (SectionElement se : js.getElements()) {
+    // if (JsonExportUtil.ELEMENT_TYPE_IMG.equals(se.getType())) {
+    // dto.setIndex(dto.getIndex() + 1);
+    // se.getParams().setIndex(dto.getIndex());
+    // se.getParams().setRid(JsonExportUtil.ELEMENT_TYPE_RID + dto.getIndex());
+    // se.getParams().setType(getImageType(se.getValue()));
+    // dto.getTypes().add(se.getParams().getType());
+    // dto.getImages().add(se);
+    // }
+    // }
+    // }
+    // }
+    //
+    // private String getImageType(String base64) {
+    // return base64.substring(11, base64.indexOf(";"));
+    // }
+    //
+    // private String getQuestionDoc(List<JSection> sections) {
+    // Map<String, Object> map = new HashMap<String, Object>();
+    // map.put("sections", sections);
+    // String doc = JsonExportUtil.getQuestionSectionDoc(map);
+    // return doc;
+    // }
 }

+ 878 - 834
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/ExportThemisPaperServiceImpl.java

@@ -1,835 +1,879 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import cn.com.qmth.examcloud.support.fss.FssFactory;
-import cn.com.qmth.examcloud.support.fss.FssHelper;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.jsoup.nodes.Node;
-import org.jsoup.nodes.TextNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-
-import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
-import cn.com.qmth.examcloud.core.questions.service.ExportThemisPaperService;
-import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswer;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerContent;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerDetail;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisBlock;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSubjectiveAnswer;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisOption;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaper;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperDetail;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisQuestion;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSection;
-import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSections;
-
-@Service("exportThemisPaperService")
-public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
-
-	private static final Logger LOG = LoggerFactory.getLogger(ExportThemisPaperServiceImpl.class);
-
-	@Autowired
-	private PaperService paperService;
-
-	@Autowired
-	private PaperDetailService paperDetailService;
-
-	@Autowired
-	private QuestionAudioRepo questionAudioRepo;
-
-	@Override
-	public void downloadAudio(ThemisPaper pa, String paperDirectory) {
-		String attDirectory = paperDirectory + File.separator + "attachment" + File.separator;
-		File attDir = new File(attDirectory);
-		if (!attDir.exists()) {
-			attDir.mkdirs();
-		}
-		int count = 0;
-		// 取到所有大题
-		List<ThemisPaperDetail> details = pa.getDetails();
-		if (details != null && details.size() > 0) {
-			for (ThemisPaperDetail detail : details) {
-				// 取到所有小题集合
-				List<ThemisQuestion> questions = detail.getQuestions();
-				if (questions != null && questions.size() > 0) {
-					for (ThemisQuestion question : questions) {
-//						int bodyNum = 1;
-						// 取到题干
-						ThemisSections body = question.getBody();
-						List<ThemisSection> sections = body.getSections();
-						for (ThemisSection section : sections) {
-							List<ThemisBlock> blocks = section.getBlocks();
-							if (blocks != null && blocks.size() > 0) {
-								for (ThemisBlock block : blocks) {
-									if (block.getType().equals("audio")) {
-										String val=block.getValue().toString();
-										String id = val.substring(0, val.lastIndexOf("."));
-										QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
-										String audioFileName = questionAudio.getId() + "."
-												+ questionAudio.getFileSuffixes();
-										File file = new File(attDirectory + audioFileName);
-
-										String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
-										FssFactory.getInstance().readFile(filePath, file);
-
-										count++;
-//										bodyNum++;
-									}
-								}
-							}
-						}
-						// 取到选项
-						List<ThemisOption> options = question.getOptions();
-						if (options != null && options.size() > 0) {
-							for (ThemisOption computerTestOption : options) {
-//								int optionNum = 1;
-								// 获取选项主体
-								ThemisSections optionBody = computerTestOption.getBody();
-								List<ThemisSection> optionSections = optionBody.getSections();
-								if (optionSections != null && optionSections.size() > 0) {
-									for (ThemisSection optionSection : optionSections) {
-										List<ThemisBlock> blocks = optionSection.getBlocks();
-										if (blocks != null && blocks.size() > 0) {
-											for (ThemisBlock block : blocks) {
-												if (block.getType().equals("audio")) {
-													String val=block.getValue().toString();
-													String id = val.substring(0, val.lastIndexOf("."));
-													QuestionAudio questionAudio = Model
-															.of(questionAudioRepo.findById(id));
-													String audioFileName = questionAudio.getId() + "."
-															+ questionAudio.getFileSuffixes();
-													File file = new File(attDirectory + audioFileName);
-
-													String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
-													FssFactory.getInstance().readFile(filePath, file);
-
-													count++;
-//													optionNum++;
-												}
-											}
-										}
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-		if (count == 0) {
-			FileUtil.deleteFolder(attDirectory);
-		}
-	}
-
-	@Override
-	public void makePaperAnswerToJsonFile(ThemisPaperAndAnswer pa, String jsonDirectoryPath) {
-		makeAnswerToJsonFile(pa.getAnswer(), jsonDirectoryPath);
-		makePaperToJsonFile(pa.getPaper(), jsonDirectoryPath);
-	}
-
-	@SuppressWarnings("deprecation")
-	private void makeAnswerToJsonFile(ThemisAnswer pa, String jsonDirectoryPath) {
-		// 创建新的JSON文件
-		File file = new File(jsonDirectoryPath + File.separator + "answer.json");
-		// 将对象转成 json对象
-		Gson gson = new Gson();
-		String strJSON = gson.toJson(pa);
-
-		strJSON = CommonUtils.replaceUnicodeStr(strJSON);
-		// 生成文件流写入JSON文件
-		FileOutputStream outputStream = null;
-		try {
-			outputStream = new FileOutputStream(file);
-			byte b[] = strJSON.getBytes("UTF-8");
-			outputStream.write(b);
-			outputStream.flush();
-		} catch (FileNotFoundException e) {
-			LOG.error(e.getMessage(), e);
-		} catch (IOException e) {
-			LOG.error(e.getMessage(), e);
-		} finally {
-			IOUtils.closeQuietly(outputStream);
-		}
-	}
-
-	@SuppressWarnings("deprecation")
-	private void makePaperToJsonFile(ThemisPaper pa, String jsonDirectoryPath) {
-		// 创建新的JSON文件
-		File file = new File(jsonDirectoryPath + File.separator + "paper.json");
-		// 将对象转成 json对象
-		Gson gson = new Gson();
-		String strJSON = gson.toJson(pa);
-
-		strJSON = CommonUtils.replaceUnicodeStr(strJSON);
-		// 生成文件流写入JSON文件
-		FileOutputStream outputStream = null;
-		try {
-			outputStream = new FileOutputStream(file);
-			byte b[] = strJSON.getBytes("UTF-8");
-			outputStream.write(b);
-			outputStream.flush();
-		} catch (FileNotFoundException e) {
-			LOG.error(e.getMessage(), e);
-		} catch (IOException e) {
-			LOG.error(e.getMessage(), e);
-		} finally {
-			IOUtils.closeQuietly(outputStream);
-		}
-	}
-
-	@Override
-	public ThemisPaperAndAnswer buildPaperAndAnswer(Paper paper) {
-		// 得到所有旧对象的大题对象
-		List<PaperDetail> paperDetails = paperService.findPaperDetailsById(paper.getId());
-		// 通过 paper 对象 ,生成新的 ComputerTestPaper 对象
-		ThemisPaper computerTestPaper = new ThemisPaper(paper, "");
-		ThemisAnswer themisAnswer = new ThemisAnswer();
-		List<ThemisPaperDetail> details = new ArrayList<>();
-		List<ThemisAnswerDetail> answerdetails = new ArrayList<>();
-		// 遍历所有旧大题对象,得到小题对象的集合
-		for (PaperDetail paperDetail : paperDetails) {
-			List<PaperDetailUnit> paperDetailUnits = paperDetailService.getUnitsByPaperDetailId(paperDetail.getId());
-			ThemisPaperDetail computerTestPaperDetail = new ThemisPaperDetail(paperDetail);
-			ThemisAnswerDetail answerDetail = new ThemisAnswerDetail(paperDetail);
-			List<ThemisQuestion> questions = new ArrayList<>();
-			List<ThemisAnswerContent> answers = new ArrayList<>();
-			// 遍历所有的小题对象
-			for (int i = 0; i < paperDetailUnits.size(); i++) {
-				PaperDetailUnit paperDetailUnit = paperDetailUnits.get(i);
-				// 根据旧的小题对象,生成新的小题对象
-				ThemisQuestion computerTestQuestion = new ThemisQuestion(paperDetailUnit);
-				ThemisAnswerContent themisAnswerContent = new ThemisAnswerContent(paperDetailUnit);
-				// 设置小题题号
-				computerTestQuestion.setNumber(i + 1);
-				themisAnswerContent.setNumber(i + 1);
-				// 得到小题题干
-				if(computerTestQuestion.getStructType()==4) {
-					computerTestQuestion.setBody(getFillBlankBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
-				}else if(computerTestQuestion.getStructType()==6){
-					computerTestQuestion.setBody(getNestedBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
-				}else {
-					computerTestQuestion.setBody(getBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
-				}
-				// 得到小题所有选项
-				computerTestQuestion.setOptions(getOption(paperDetailUnit.getQuestion(), computerTestPaper));
-				// 得到小题的答案
-				if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-						|| paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-					themisAnswerContent.setAnswer(getSelectAnswer(paperDetailUnit.getQuestion()));
-				} else if (paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-					themisAnswerContent.setAnswer(getBoolAnswer(paperDetailUnit.getQuestion()));
-				} else if (paperDetailUnit.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
-					themisAnswerContent.setAnswer(getFillBlankAnswer(paperDetailUnit.getQuestion()));
-				} else {
-					themisAnswerContent
-							.setAnswer(getAnswer(paperDetailUnit.getQuestion().getQuesAnswer(), computerTestPaper));
-				}
-				// 查询小题中的 套题
-				List<Question> subQuestionsList = paperDetailUnit.getQuestion().getSubQuestions();
-				// 判断这个小题中是否有套题
-				if (subQuestionsList != null && subQuestionsList.size() > 0) {
-					List<ThemisQuestion> subQuestions = new ArrayList<>();
-					List<ThemisAnswerContent> subAnswers = new ArrayList<>();
-					// 遍历每个套题
-					for (int j = 0; j < subQuestionsList.size(); j++) {
-						Question subQuestion = subQuestionsList.get(j);
-						ThemisQuestion subcomputerTestQuestion = new ThemisQuestion(subQuestion);
-						subcomputerTestQuestion.setScore(paperDetailUnit.getSubScoreList().get(j));
-						ThemisAnswerContent subthemisAnswerContent = new ThemisAnswerContent(subQuestion);
-						// 设置套题中小题题号
-						subcomputerTestQuestion.setNumber(j + 1);
-						subthemisAnswerContent.setNumber(j + 1);
-						
-						if(subcomputerTestQuestion.getStructType()==4) {
-							subcomputerTestQuestion.setBody(getFillBlankBody(subQuestion.getQuesBody(), computerTestPaper));
-						}else if(computerTestQuestion.getStructType()==6){
-							subcomputerTestQuestion.setBody(getNestedBody(subQuestion.getQuesBody(), computerTestPaper));
-						}else {
-							subcomputerTestQuestion.setBody(getBody(subQuestion.getQuesBody(), computerTestPaper));
-						}
-						subcomputerTestQuestion.setOptions(getOption(subQuestion, computerTestPaper));
-						if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
-								|| subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
-							subthemisAnswerContent.setAnswer(getSelectAnswer(subQuestion));
-						} else if (subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-							subthemisAnswerContent.setAnswer(getBoolAnswer(subQuestion));
-						} else if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
-							subthemisAnswerContent.setAnswer(getFillBlankAnswer(subQuestion));
-						} else {
-							subthemisAnswerContent.setAnswer(getAnswer(subQuestion.getQuesAnswer(), computerTestPaper));
-						}
-						subQuestions.add(subcomputerTestQuestion);
-						subAnswers.add(subthemisAnswerContent);
-					}
-					computerTestQuestion.setSubQuestions(subQuestions);
-					themisAnswerContent.setSubQuestions(subAnswers);
-				}
-				questions.add(computerTestQuestion);
-				answers.add(themisAnswerContent);
-			}
-			computerTestPaperDetail.setQuestions(questions);
-			answerDetail.setQuestions(answers);
-			// paperDetail中的题数(unitCount)可能不准确,这里以questions的实际size为准
-			computerTestPaperDetail.setQuestionCount(questions.size());
-			details.add(computerTestPaperDetail);
-			answerdetails.add(answerDetail);
-		}
-		computerTestPaper.setDetails(details);
-		themisAnswer.setDetails(answerdetails);
-
-		ThemisPaperAndAnswer ret = new ThemisPaperAndAnswer();
-		computerTestPaper.setId(null);
-		ret.setPaper(computerTestPaper);
-		ret.setAnswer(themisAnswer);
-		return ret;
-	}
-	private ThemisSections getBody(String str, ThemisPaper computerTestPaper) {
-		ThemisSections body = new ThemisSections();
-		List<ThemisSection> sections = new ArrayList<>();
-		// 得到小题题干或者答案行数
-		if (StringUtils.isBlank(str)) {
-			return body;
-		}
-		String[] questionRowStrings = str.split("</p>");
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
-			if (blocks != null && blocks.size() > 0) {
-				ThemisSection section = new ThemisSection();
-				// 将小题题干拆分为Block集合
-				section.setBlocks(blocks);
-				sections.add(section);
-			}
-		}
-		body.setSections(sections);
-		return body;
-	}
-	private ThemisSections getFillBlankBody(String str, ThemisPaper computerTestPaper) {
-		ThemisSections body = new ThemisSections();
-		List<ThemisSection> sections = new ArrayList<>();
-		// 得到小题题干或者答案行数
-		if (StringUtils.isBlank(str)) {
-			return body;
-		}
-		String[] questionRowStrings = str.split("</p>");
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			List<ThemisBlock> blocks = disposeFillBlankQuestionBody(questionRowStrings[i], computerTestPaper);
-			if (blocks != null && blocks.size() > 0) {
-				ThemisSection section = new ThemisSection();
-				// 将小题题干拆分为Block集合
-				section.setBlocks(blocks);
-				sections.add(section);
-			}
-		}
-		body.setSections(sections);
-		return body;
-	}
-	
-	private List<ThemisSubjectiveAnswer> getAnswer(String str, ThemisPaper computerTestPaper) {
-		List<ThemisSection> sections = new ArrayList<>();
-		// 得到小题题干或者答案行数
-		if (StringUtils.isBlank(str)) {
-			return null;
-		}
-		String[] questionRowStrings = str.split("</p>");
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
-			if (blocks != null && blocks.size() > 0) {
-				ThemisSection section = new ThemisSection();
-				// 将小题题干拆分为Block集合
-				section.setBlocks(blocks);
-				sections.add(section);
-			}
-		}
-		List<ThemisSubjectiveAnswer> ans=new ArrayList<>();
-		ThemisSubjectiveAnswer an=new ThemisSubjectiveAnswer();
-		ans.add(an);
-		an.setIndex(1);
-		an.setSections(sections);
-		return ans;
-	}
-	private String changeCloze(String text) {
-		StringBuffer buffer = new StringBuffer();
-        String regex = "##(\\d)##";
-
-        Pattern pattern = Pattern.compile(regex);
-        Matcher matcher = pattern.matcher(text);
-        // 使用find()方法查找匹配项
-        while (matcher.find()) {
-        	matcher.appendReplacement(buffer, "__"+matcher.group(1)+"__");
-        }
-        //fixbug
-        matcher.appendTail(buffer);
-        return buffer.toString();
-	}
-	private ThemisSections getNestedBody(String str, ThemisPaper computerTestPaper) {
-		ThemisSections body = new ThemisSections();
-		List<ThemisSection> sections = new ArrayList<>();
-		// 得到小题题干或者答案行数
-		if (StringUtils.isBlank(str)) {
-			return body;
-		}
-		String[] questionRowStrings = str.split("</p>");
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			List<ThemisBlock> blocks = disposeNestedQuestionBody(questionRowStrings[i], computerTestPaper);
-			if (blocks != null && blocks.size() > 0) {
-				ThemisSection section = new ThemisSection();
-				// 将小题题干拆分为Block集合
-				section.setBlocks(blocks);
-				sections.add(section);
-			}
-		}
-		body.setSections(sections);
-		return body;
-	}
-	private List<ThemisBlock> disposeNestedQuestionBody(String questionRow, ThemisPaper computerTestPaper) {
-		List<ThemisBlock> blocks = new ArrayList<>();
-		// 去掉每行里面的<p>,<span>,</span>标签
-		questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
-				.replaceAll("</span>", "").replaceAll("</a>", "");
-		String[] questionRowStrings = questionRow.split("<|/>|>");
-		boolean hasAudio = false;
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			ThemisBlock block = new ThemisBlock();
-			String rowStr = questionRowStrings[i];
-			// 判断是否有图片
-			if (rowStr.startsWith("img")) {
-				rowStr = "<" + rowStr + ">";
-				Map<String, Object> param = new HashMap<>();
-				// 需要继续做截取,取到Parma
-				block.setType("image");
-				// 获取图片的路径
-				List<String> strSrcList = getImg(rowStr, "src");
-				if (strSrcList.size() > 0) {
-					String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
-					block.setValue(strSrc);
-				}
-				// 获取图片的高度
-				List<String> strHeightList = getImg(rowStr, "height");
-				if (strHeightList.size() > 0) {
-					String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strHeight);
-					if(px!=null) {
-						param.put("height", px);
-					}
-				}
-				// 获取图片的宽度
-				List<String> strWidthList = getImg(rowStr, "width");
-				if (strHeightList.size() > 0) {
-					String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strWidth);
-					if(px!=null) {
-						param.put("width", px);
-					}
-				}
-				block.setParam(param);
-				blocks.add(block);
-			} else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
-				rowStr = "<" + rowStr + ">";
-				block.setType("audio");
-				String id=CommonUtils.getAttrValue(rowStr, "id");
-				QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
-				block.setValue(questionAudio.getId() + "."+ questionAudio.getFileSuffixes());
-				blocks.add(block);
-				hasAudio = true;
-			} else {
-				block.setType("text");
-				rowStr = rowStr.replace("&nbsp;", "");// 消除空格
-				rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
-				rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
-				rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
-				rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
-				if (StringUtils.isNotBlank(rowStr)) {
-					block.setValue(changeCloze(rowStr));
-					blocks.add(block);
-				}
-			}
-		}
-		if (hasAudio) {
-			computerTestPaper.setHasAudio(hasAudio);
-		}
-		return blocks;
-	}
-	
-	private List<ThemisBlock> disposeFillBlankQuestionBody(String questionRow, ThemisPaper computerTestPaper) {
-		List<ThemisBlock> blocks = new ArrayList<>();
-		// 去掉每行里面的<p>,<span>,</span>标签
-		questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
-				.replaceAll("</span>", "").replaceAll("</a>", "");
-		String[] questionRowStrings = questionRow.split("<|/>|>");
-		boolean hasAudio = false;
-		int blankIndex=0;
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			ThemisBlock block = new ThemisBlock();
-			String rowStr = questionRowStrings[i];
-			// 判断是否有图片
-			if (rowStr.startsWith("img")) {
-				rowStr = "<" + rowStr + ">";
-				Map<String, Object> param = new HashMap<>();
-				// 需要继续做截取,取到Parma
-				block.setType("image");
-				// 获取图片的路径
-				List<String> strSrcList = getImg(rowStr, "src");
-				if (strSrcList.size() > 0) {
-					String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
-					block.setValue(strSrc);
-				}
-				// 获取图片的高度
-				List<String> strHeightList = getImg(rowStr, "height");
-				if (strHeightList.size() > 0) {
-					String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strHeight);
-					if(px!=null) {
-						param.put("height", px);
-					}
-				}
-				// 获取图片的宽度
-				List<String> strWidthList = getImg(rowStr, "width");
-				if (strHeightList.size() > 0) {
-					String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strWidth);
-					if(px!=null) {
-						param.put("width", px);
-					}
-				}
-				block.setParam(param);
-				blocks.add(block);
-			} else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
-				rowStr = "<" + rowStr + ">";
-				block.setType("audio");
-				String id=CommonUtils.getAttrValue(rowStr, "id");
-				QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
-				block.setValue(questionAudio.getId() + "."+ questionAudio.getFileSuffixes());
-				blocks.add(block);
-				hasAudio = true;
-			} else {
-				rowStr = rowStr.replace("&nbsp;", "");// 消除空格
-				rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
-				rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
-				rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
-				rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
-		        
-				String regex = "###";
-
-		        Pattern pattern = Pattern.compile(regex);
-		        Matcher matcher = pattern.matcher(rowStr);
-		        int indexStart=0;
-		        // 使用find()方法查找匹配项
-		        while (matcher.find()) {
-		        	blankIndex++;
-		            String content=rowStr.substring(indexStart, matcher.start());
-		            if(StringUtils.isNotEmpty(content)) {
-		            	ThemisBlock temblock = new ThemisBlock();
-			            temblock.setType("text");
-			            temblock.setValue(content);
-						blocks.add(temblock);
-		            }
-		            ThemisBlock blankblock = new ThemisBlock();
-		            blankblock.setType("cloze");
-		            blankblock.setValue(blankIndex);
-		            blocks.add(blankblock);
-		            indexStart=matcher.start()+regex.length();
-		        }
-		        if(indexStart<rowStr.length()) {
-		        	// 输出最后一个分隔符之后的字符串
-			        String content=rowStr.substring(indexStart, rowStr.length());
-		            if(StringUtils.isNotEmpty(content)) {
-		            	ThemisBlock temblock = new ThemisBlock();
-			            temblock.setType("text");
-			            temblock.setValue(content);
-						blocks.add(temblock);
-		            }
-		        }
-			}
-		}
-		if (hasAudio) {
-			computerTestPaper.setHasAudio(hasAudio);
-		}
-		return blocks;
-	}
-	
-	private Integer getIntFromString(String s) {
-		if(StringUtils.isBlank(s)) {
-			return null;
-		}
-		s=s.trim();
-		s=s.replace("px", "");
-		try {
-			Integer i=Integer.valueOf(s);
-			return i;
-		} catch (NumberFormatException e) {
-			return null;
-		}
-	}
-	
-
-	private List<ThemisBlock> disposeQuestionBodyOrOption(String questionRow, ThemisPaper computerTestPaper) {
-		List<ThemisBlock> blocks = new ArrayList<>();
-		// 去掉每行里面的<p>,<span>,</span>标签
-		questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
-				.replaceAll("</span>", "").replaceAll("</a>", "");
-		String[] questionRowStrings = questionRow.split("<|/>|>");
-		boolean hasAudio = false;
-		for (int i = 0; i < questionRowStrings.length; i++) {
-			ThemisBlock block = new ThemisBlock();
-			String rowStr = questionRowStrings[i];
-			// 判断是否有图片
-			if (rowStr.startsWith("img")) {
-				rowStr = "<" + rowStr + ">";
-				Map<String, Object> param = new HashMap<>();
-				// 需要继续做截取,取到Parma
-				block.setType("image");
-				// 获取图片的路径
-				List<String> strSrcList = getImg(rowStr, "src");
-				if (strSrcList.size() > 0) {
-					String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
-					block.setValue(strSrc);
-				}
-				// 获取图片的高度
-				List<String> strHeightList = getImg(rowStr, "height");
-				if (strHeightList.size() > 0) {
-					String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strHeight);
-					if(px!=null) {
-						param.put("height", px);
-					}
-				}
-				// 获取图片的宽度
-				List<String> strWidthList = getImg(rowStr, "width");
-				if (strHeightList.size() > 0) {
-					String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
-					Integer px=getIntFromString(strWidth);
-					if(px!=null) {
-						param.put("width", px);
-					}
-				}
-				block.setParam(param);
-				blocks.add(block);
-			} else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
-				rowStr = "<" + rowStr + ">";
-				block.setType("audio");
-				String id=CommonUtils.getAttrValue(rowStr, "id");
-				QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
-				block.setValue(questionAudio.getId() + "."+ questionAudio.getFileSuffixes());
-				blocks.add(block);
-				hasAudio = true;
-			} else {
-				block.setType("text");
-				rowStr = rowStr.replace("&nbsp;", "");// 消除空格
-				rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
-				rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
-				rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
-				rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
-				if (StringUtils.isNotBlank(rowStr)) {
-					block.setValue(rowStr);
-					blocks.add(block);
-				}
-			}
-		}
-		if (hasAudio) {
-			computerTestPaper.setHasAudio(hasAudio);
-		}
-		return blocks;
-	}
-
-	/**
-	 * 获取图片里面的路径,长度,宽度
-	 */
-	private List<String> getImg(String s, String str) {
-		String regex;
-		List<String> list = new ArrayList<>();
-		regex = str + "=\"(.*?)\"";
-		Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
-		Matcher ma = pa.matcher(s);
-		while (ma.find()) {
-			list.add(ma.group());
-		}
-		return list;
-	}
-
-	private List<ThemisOption> getOption(Question question, ThemisPaper computerTestPaper) {
-		// 得到小题选项
-		List<QuesOption> quesOptions = question.getQuesOptions();
-		List<ThemisOption> options = new ArrayList<>();
-		// 遍历小题选项
-		if (quesOptions != null && quesOptions.size() > 0) {
-			for (QuesOption quesOption : quesOptions) {
-				ThemisOption option = new ThemisOption();
-				option.setNumber(new Integer(quesOption.getNumber()));
-				ThemisSections body = new ThemisSections();
-
-				List<ThemisSection> sections = new ArrayList<>();
-				// 得到小题选项
-				String optionString = quesOption.getOptionBody();
-				String[] optionStrings = optionString.split("</p>");
-				for (int i = 0; i < optionStrings.length; i++) {
-					List<ThemisBlock> blocks = disposeQuestionBodyOrOption(optionStrings[i], computerTestPaper);
-					if (blocks != null && blocks.size() > 0) {
-						ThemisSection section = new ThemisSection();
-						section.setBlocks(blocks);
-						sections.add(section);
-					}
-				}
-				body.setSections(sections);
-				option.setBody(body);
-				options.add(option);
-			}
-		}
-		return options;
-	}
-
-	private JsonArray getSelectAnswer(Question question) {
-		// 得到小题选项
-		List<QuesOption> quesOptions = question.getQuesOptions();
-		int index = 0;
-		// 遍历小题选项
-		if (quesOptions != null && quesOptions.size() > 0) {
-			JsonArray ja = new JsonArray();
-			for (QuesOption quesOption : quesOptions) {
-				index++;
-				if (quesOption.getIsCorrect() == 1) {
-					ja.add(index);
-				}
-			}
-			if (ja.size() > 0) {
-				return ja;
-			} else {
-				return null;
-			}
-		}
-		return null;
-	}
-
-	private Boolean getBoolAnswer(Question question) {
-		String as = question.getQuesAnswer();
-		if (StringUtils.isBlank(as)) {
-			return null;
-		}
-		if (as.contains("正确")) {
-			return true;
-		} else {
-			return false;
-		}
-	}
-
-	private void getSectionElement(Node ce, StringBuilder sb) {
-		if (("span".equals(ce.nodeName()) || "p".equals(ce.nodeName())) && ce.childNodeSize() > 0) {
-			for (Node e : ce.childNodes()) {
-				getSectionElement(e, sb);
-			}
-		} else {
-			if (ce instanceof TextNode) {
-				TextNode tn = (TextNode) ce;
-				String text = tn.text();
-				sb.append(text);
-			} else if (ce instanceof Element) {
-				if ("img".equals(ce.nodeName())) {
-					Element el = (Element) ce;
-					sb.append(el.outerHtml());
-				} else {
-					Element el = (Element) ce;
-					sb.append(el.text());
-				}
-			}
-		}
-	}
-
-	private List<ThemisSubjectiveAnswer> getFillBlankAnswer(Question question) {
-		String html = question.getQuesAnswer();
-		if (StringUtils.isBlank(html)) {
-			return null;
-		}
-		StringBuilder sb = new StringBuilder();
-		Document doc = Jsoup.parse(html);
-		Element b = doc.body();
-		for (Node ce : b.childNodes()) {
-			getSectionElement(ce, sb);
-		}
-
-		if (StringUtils.isBlank(sb.toString())) {
-			return null;
-		}
-		List<ThemisSubjectiveAnswer> tas = new ArrayList<>();
-		String[] sbs = sb.toString().split("##");
-		int index = 0;
-		for (String an : sbs) {
-			index++;
-			ThemisSubjectiveAnswer ta = new ThemisSubjectiveAnswer();
-			ta.setIndex(index);
-			List<ThemisSection> sections = new ArrayList<>();
-			ThemisSection sec = new ThemisSection();
-			List<ThemisBlock> blocks = new ArrayList<>();
-			Document subdoc = Jsoup.parse(an);
-			for (Node ce : subdoc.body().childNodes()) {
-				ThemisBlock block = new ThemisBlock();
-				if (ce instanceof TextNode) {
-					TextNode tn = (TextNode) ce;
-					String text = tn.text();
-					block.setType("text");
-					block.setValue(text);
-				} else if (ce instanceof Element) {
-					if ("img".equals(ce.nodeName())) {
-						Element el = (Element) ce;
-						block.setType("image");
-						block.setValue(el.attr("src"));
-						String h = el.attr("height");
-						String w = el.attr("width");
-						if (StringUtils.isNotBlank(w) && StringUtils.isNotBlank(h)) {
-							Map<String, Object> param = new HashMap<String, Object>();
-							Integer hpx=getIntFromString(h);
-							if(hpx!=null) {
-								param.put("height", hpx);
-							}
-							Integer wpx=getIntFromString(w);
-							if(wpx!=null) {
-								param.put("width", wpx);
-							}
-							block.setParam(param);
-						}
-					} else {
-						Element el = (Element) ce;
-						block.setType("text");
-						block.setValue(el.text());
-					}
-				}
-				blocks.add(block);
-			}
-			sec.setBlocks(blocks);
-			sections.add(sec);
-			ta.setSections(sections);
-			tas.add(ta);
-		}
-		return tas;
-	}
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+import org.jsoup.nodes.TextNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.FileUtil;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
+import cn.com.qmth.examcloud.core.questions.service.ExportThemisPaperService;
+import cn.com.qmth.examcloud.core.questions.service.PaperDetailService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswer;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerContent;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisAnswerDetail;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisBlock;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisOption;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaper;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperAndAnswer;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisPaperDetail;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisQuestion;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSection;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSections;
+import cn.com.qmth.examcloud.core.questions.service.themispaper.ThemisSubjectiveAnswer;
+import cn.com.qmth.examcloud.support.fss.FssFactory;
+import cn.com.qmth.examcloud.support.fss.FssHelper;
+
+@Service("exportThemisPaperService")
+public class ExportThemisPaperServiceImpl implements ExportThemisPaperService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExportThemisPaperServiceImpl.class);
+
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private PaperDetailService paperDetailService;
+
+    @Autowired
+    private QuestionAudioRepo questionAudioRepo;
+
+    @Override
+    public void downloadAudio(ThemisPaper pa, String paperDirectory) {
+        String attDirectory = paperDirectory + File.separator + "attachment" + File.separator;
+        File attDir = new File(attDirectory);
+        if (!attDir.exists()) {
+            attDir.mkdirs();
+        }
+        int count = 0;
+        // 取到所有大题
+        List<ThemisPaperDetail> details = pa.getDetails();
+        if (details != null && details.size() > 0) {
+            for (ThemisPaperDetail detail : details) {
+                // 取到所有小题集合
+                List<ThemisQuestion> questions = detail.getQuestions();
+                if (questions != null && questions.size() > 0) {
+                    for (ThemisQuestion question : questions) {
+                        // int bodyNum = 1;
+                        // 取到题干
+                        ThemisSections body = question.getBody();
+                        List<ThemisSection> sections = body.getSections();
+                        for (ThemisSection section : sections) {
+                            List<ThemisBlock> blocks = section.getBlocks();
+                            if (blocks != null && blocks.size() > 0) {
+                                for (ThemisBlock block : blocks) {
+                                    if (block.getType().equals("audio")) {
+                                        String val = block.getValue().toString();
+                                        String id = val.substring(0, val.lastIndexOf("."));
+                                        QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+                                        String audioFileName = questionAudio.getId() + "."
+                                                + questionAudio.getFileSuffixes();
+                                        File file = new File(attDirectory + audioFileName);
+
+                                        String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
+                                        FssFactory.getInstance().readFile(filePath, file);
+
+                                        count++;
+                                        // bodyNum++;
+                                    }
+                                }
+                            }
+                        }
+                        // 取到选项
+                        List<ThemisOption> options = question.getOptions();
+                        if (options != null && options.size() > 0) {
+                            for (ThemisOption computerTestOption : options) {
+                                // int optionNum = 1;
+                                // 获取选项主体
+                                ThemisSections optionBody = computerTestOption.getBody();
+                                List<ThemisSection> optionSections = optionBody.getSections();
+                                if (optionSections != null && optionSections.size() > 0) {
+                                    for (ThemisSection optionSection : optionSections) {
+                                        List<ThemisBlock> blocks = optionSection.getBlocks();
+                                        if (blocks != null && blocks.size() > 0) {
+                                            for (ThemisBlock block : blocks) {
+                                                if (block.getType().equals("audio")) {
+                                                    String val = block.getValue().toString();
+                                                    String id = val.substring(0, val.lastIndexOf("."));
+                                                    QuestionAudio questionAudio = Model
+                                                            .of(questionAudioRepo.findById(id));
+                                                    String audioFileName = questionAudio.getId() + "."
+                                                            + questionAudio.getFileSuffixes();
+                                                    File file = new File(attDirectory + audioFileName);
+
+                                                    String filePath = FssHelper.fixFilePath(questionAudio.getFileUrl());
+                                                    FssFactory.getInstance().readFile(filePath, file);
+
+                                                    count++;
+                                                    // optionNum++;
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (count == 0) {
+            FileUtil.deleteFolder(attDirectory);
+        }
+    }
+
+    @Override
+    public void makePaperAnswerToJsonFile(ThemisPaperAndAnswer pa, String jsonDirectoryPath) {
+        makeAnswerToJsonFile(pa.getAnswer(), jsonDirectoryPath);
+        makePaperToJsonFile(pa.getPaper(), jsonDirectoryPath);
+    }
+
+    @SuppressWarnings("deprecation")
+    private void makeAnswerToJsonFile(ThemisAnswer pa, String jsonDirectoryPath) {
+        // 创建新的JSON文件
+        File file = new File(jsonDirectoryPath + File.separator + "answer.json");
+        // 将对象转成 json对象
+        Gson gson = new Gson();
+        String strJSON = gson.toJson(pa);
+
+        strJSON = CommonUtils.replaceUnicodeStr(strJSON);
+        // 生成文件流写入JSON文件
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = new FileOutputStream(file);
+            byte b[] = strJSON.getBytes("UTF-8");
+            outputStream.write(b);
+            outputStream.flush();
+        } catch (FileNotFoundException e) {
+            LOG.error(e.getMessage(), e);
+        } catch (IOException e) {
+            LOG.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private void makePaperToJsonFile(ThemisPaper pa, String jsonDirectoryPath) {
+        // 创建新的JSON文件
+        File file = new File(jsonDirectoryPath + File.separator + "paper.json");
+        // 将对象转成 json对象
+        Gson gson = new Gson();
+        String strJSON = gson.toJson(pa);
+
+        strJSON = CommonUtils.replaceUnicodeStr(strJSON);
+        // 生成文件流写入JSON文件
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = new FileOutputStream(file);
+            byte b[] = strJSON.getBytes("UTF-8");
+            outputStream.write(b);
+            outputStream.flush();
+        } catch (FileNotFoundException e) {
+            LOG.error(e.getMessage(), e);
+        } catch (IOException e) {
+            LOG.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
+    @Override
+    public ThemisPaperAndAnswer buildPaperAndAnswer(Paper paper) {
+        // 得到所有旧对象的大题对象
+        List<PaperDetail> paperDetails = paperService.findPaperDetailsById(paper.getId());
+        // 通过 paper 对象 ,生成新的 ComputerTestPaper 对象
+        ThemisPaper computerTestPaper = new ThemisPaper(paper, "");
+        ThemisAnswer themisAnswer = new ThemisAnswer();
+        List<ThemisPaperDetail> details = new ArrayList<>();
+        List<ThemisAnswerDetail> answerdetails = new ArrayList<>();
+        // 遍历所有旧大题对象,得到小题对象的集合
+        for (PaperDetail paperDetail : paperDetails) {
+            List<PaperDetailUnit> paperDetailUnits = paperDetailService.getUnitsByPaperDetailId(paperDetail.getId());
+            ThemisPaperDetail computerTestPaperDetail = new ThemisPaperDetail(paperDetail);
+            ThemisAnswerDetail answerDetail = new ThemisAnswerDetail(paperDetail);
+            List<ThemisQuestion> questions = new ArrayList<>();
+            List<ThemisAnswerContent> answers = new ArrayList<>();
+            // 遍历所有的小题对象
+            for (int i = 0; i < paperDetailUnits.size(); i++) {
+                PaperDetailUnit paperDetailUnit = paperDetailUnits.get(i);
+                try {
+                    // 根据旧的小题对象,生成新的小题对象
+                    ThemisQuestion computerTestQuestion = new ThemisQuestion(paperDetailUnit);
+                    ThemisAnswerContent themisAnswerContent = new ThemisAnswerContent(paperDetailUnit);
+                    // 设置小题题号
+                    computerTestQuestion.setNumber(i + 1);
+                    themisAnswerContent.setNumber(i + 1);
+                    // 得到小题题干
+                    if (computerTestQuestion.getStructType() == 4) {
+                        computerTestQuestion.setBody(
+                                getFillBlankBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
+                    } else if (computerTestQuestion.getStructType() == 6) {
+                        computerTestQuestion
+                                .setBody(getNestedBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
+                    } else {
+                        computerTestQuestion
+                                .setBody(getBody(paperDetailUnit.getQuestion().getQuesBody(), computerTestPaper));
+                    }
+                    // 得到小题所有选项
+                    computerTestQuestion.setOptions(getOption(paperDetailUnit.getQuestion(), computerTestPaper));
+                    // 得到小题的答案
+                    if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                            || paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+                        themisAnswerContent.setAnswer(getSelectAnswer(paperDetailUnit.getQuestion()));
+                    } else if (paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                        themisAnswerContent.setAnswer(getBoolAnswer(paperDetailUnit.getQuestion()));
+                    } else if (paperDetailUnit.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+                        themisAnswerContent.setAnswer(getFillBlankAnswer(paperDetailUnit.getQuestion()));
+                    } else {
+                        themisAnswerContent
+                                .setAnswer(getAnswer(paperDetailUnit.getQuestion().getQuesAnswer(), computerTestPaper));
+                    }
+                    // 查询小题中的 套题
+                    List<Question> subQuestionsList = paperDetailUnit.getQuestion().getSubQuestions();
+                    // 判断这个小题中是否有套题
+                    if (subQuestionsList != null && subQuestionsList.size() > 0) {
+                        List<ThemisQuestion> subQuestions = new ArrayList<>();
+                        List<ThemisAnswerContent> subAnswers = new ArrayList<>();
+                        // 遍历每个套题
+                        for (int j = 0; j < subQuestionsList.size(); j++) {
+                            Question subQuestion = subQuestionsList.get(j);
+                            ThemisQuestion subcomputerTestQuestion = new ThemisQuestion(subQuestion);
+                            subcomputerTestQuestion.setScore(paperDetailUnit.getSubScoreList().get(j));
+                            ThemisAnswerContent subthemisAnswerContent = new ThemisAnswerContent(subQuestion);
+                            // 设置套题中小题题号
+                            subcomputerTestQuestion.setNumber(j + 1);
+                            subthemisAnswerContent.setNumber(j + 1);
+
+                            if (subcomputerTestQuestion.getStructType() == 4) {
+                                subcomputerTestQuestion
+                                        .setBody(getFillBlankBody(subQuestion.getQuesBody(), computerTestPaper));
+                            } else if (computerTestQuestion.getStructType() == 6) {
+                                subcomputerTestQuestion
+                                        .setBody(getNestedBody(subQuestion.getQuesBody(), computerTestPaper));
+                            } else {
+                                subcomputerTestQuestion.setBody(getBody(subQuestion.getQuesBody(), computerTestPaper));
+                            }
+                            subcomputerTestQuestion.setOptions(getOption(subQuestion, computerTestPaper));
+                            if (subQuestion.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                                    || subQuestion.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION) {
+                                subthemisAnswerContent.setAnswer(getSelectAnswer(subQuestion));
+                            } else if (subQuestion.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+                                subthemisAnswerContent.setAnswer(getBoolAnswer(subQuestion));
+                            } else if (subQuestion.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+                                subthemisAnswerContent.setAnswer(getFillBlankAnswer(subQuestion));
+                            } else {
+                                subthemisAnswerContent
+                                        .setAnswer(getAnswer(subQuestion.getQuesAnswer(), computerTestPaper));
+                            }
+                            subQuestions.add(subcomputerTestQuestion);
+                            subAnswers.add(subthemisAnswerContent);
+                        }
+                        computerTestQuestion.setSubQuestions(subQuestions);
+                        themisAnswerContent.setSubQuestions(subAnswers);
+                    }
+                    questions.add(computerTestQuestion);
+                    answers.add(themisAnswerContent);
+                } catch (StatusException e) {
+                    throw new StatusException("500",
+                            "第" + paperDetail.getNumber() + "大题,第" + paperDetailUnit.getNumber() + "小题 " + e.getDesc(),
+                            e);
+                }
+            }
+            computerTestPaperDetail.setQuestions(questions);
+            answerDetail.setQuestions(answers);
+            // paperDetail中的题数(unitCount)可能不准确,这里以questions的实际size为准
+            computerTestPaperDetail.setQuestionCount(questions.size());
+            details.add(computerTestPaperDetail);
+            answerdetails.add(answerDetail);
+        }
+        computerTestPaper.setDetails(details);
+        themisAnswer.setDetails(answerdetails);
+
+        ThemisPaperAndAnswer ret = new ThemisPaperAndAnswer();
+        computerTestPaper.setId(null);
+        ret.setPaper(computerTestPaper);
+        ret.setAnswer(themisAnswer);
+        return ret;
+    }
+
+    private ThemisSections getBody(String str, ThemisPaper computerTestPaper) {
+        ThemisSections body = new ThemisSections();
+        List<ThemisSection> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (StringUtils.isBlank(str)) {
+            return body;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
+            if (blocks != null && blocks.size() > 0) {
+                ThemisSection section = new ThemisSection();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        body.setSections(sections);
+        return body;
+    }
+
+    private ThemisSections getFillBlankBody(String str, ThemisPaper computerTestPaper) {
+        ThemisSections body = new ThemisSections();
+        List<ThemisSection> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (StringUtils.isBlank(str)) {
+            return body;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<ThemisBlock> blocks = disposeFillBlankQuestionBody(questionRowStrings[i], computerTestPaper);
+            if (blocks != null && blocks.size() > 0) {
+                ThemisSection section = new ThemisSection();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        body.setSections(sections);
+        return body;
+    }
+
+    private List<ThemisSubjectiveAnswer> getAnswer(String str, ThemisPaper computerTestPaper) {
+        List<ThemisSection> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (StringUtils.isBlank(str)) {
+            return null;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<ThemisBlock> blocks = disposeQuestionBodyOrOption(questionRowStrings[i], computerTestPaper);
+            if (blocks != null && blocks.size() > 0) {
+                ThemisSection section = new ThemisSection();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        List<ThemisSubjectiveAnswer> ans = new ArrayList<>();
+        ThemisSubjectiveAnswer an = new ThemisSubjectiveAnswer();
+        ans.add(an);
+        an.setIndex(1);
+        an.setSections(sections);
+        return ans;
+    }
+
+    private String changeCloze(String text) {
+        StringBuffer buffer = new StringBuffer();
+        String regex = "##(\\d)##";
+
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(text);
+        // 使用find()方法查找匹配项
+        while (matcher.find()) {
+            matcher.appendReplacement(buffer, "__" + matcher.group(1) + "__");
+        }
+        // fixbug
+        matcher.appendTail(buffer);
+        return buffer.toString();
+    }
+
+    private ThemisSections getNestedBody(String str, ThemisPaper computerTestPaper) {
+        ThemisSections body = new ThemisSections();
+        List<ThemisSection> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (StringUtils.isBlank(str)) {
+            return body;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<ThemisBlock> blocks = disposeNestedQuestionBody(questionRowStrings[i], computerTestPaper);
+            if (blocks != null && blocks.size() > 0) {
+                ThemisSection section = new ThemisSection();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        body.setSections(sections);
+        return body;
+    }
+
+    private List<ThemisBlock> disposeNestedQuestionBody(String questionRow, ThemisPaper computerTestPaper) {
+        List<ThemisBlock> blocks = new ArrayList<>();
+        // 去掉每行里面的<p>,<span>,</span>标签
+        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+                .replaceAll("</span>", "").replaceAll("</a>", "");
+        String[] questionRowStrings = questionRow.split("<|/>|>");
+        boolean hasAudio = false;
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            ThemisBlock block = new ThemisBlock();
+            String rowStr = questionRowStrings[i];
+            // 判断是否有图片
+            if (rowStr.startsWith("img")) {
+                rowStr = "<" + rowStr + ">";
+                Map<String, Object> param = new HashMap<>();
+                // 需要继续做截取,取到Parma
+                block.setType("image");
+                // 获取图片的路径
+                List<String> strSrcList = getImg(rowStr, "src");
+                if (strSrcList.size() > 0) {
+                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+                    block.setValue(strSrc);
+                }
+                // 获取图片的高度
+                List<String> strHeightList = getImg(rowStr, "height");
+                if (strHeightList.size() > 0) {
+                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strHeight);
+                    if (px != null) {
+                        param.put("height", px);
+                    }
+                }
+                // 获取图片的宽度
+                List<String> strWidthList = getImg(rowStr, "width");
+                if (strHeightList.size() > 0) {
+                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strWidth);
+                    if (px != null) {
+                        param.put("width", px);
+                    }
+                }
+                block.setParam(param);
+                blocks.add(block);
+            } else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+                rowStr = "<" + rowStr + ">";
+                block.setType("audio");
+                String id = CommonUtils.getAttrValue(rowStr, "id");
+                QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+                block.setValue(questionAudio.getId() + "." + questionAudio.getFileSuffixes());
+                blocks.add(block);
+                hasAudio = true;
+            } else {
+                block.setType("text");
+                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+                if (StringUtils.isNotBlank(rowStr)) {
+                    block.setValue(changeCloze(rowStr));
+                    blocks.add(block);
+                }
+            }
+        }
+        if (hasAudio) {
+            computerTestPaper.setHasAudio(hasAudio);
+        }
+        return blocks;
+    }
+
+    private List<ThemisBlock> disposeFillBlankQuestionBody(String questionRow, ThemisPaper computerTestPaper) {
+        List<ThemisBlock> blocks = new ArrayList<>();
+        // 去掉每行里面的<p>,<span>,</span>标签
+        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+                .replaceAll("</span>", "").replaceAll("</a>", "");
+        String[] questionRowStrings = questionRow.split("<|/>|>");
+        boolean hasAudio = false;
+        int blankIndex = 0;
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            ThemisBlock block = new ThemisBlock();
+            String rowStr = questionRowStrings[i];
+            // 判断是否有图片
+            if (rowStr.startsWith("img")) {
+                rowStr = "<" + rowStr + ">";
+                Map<String, Object> param = new HashMap<>();
+                // 需要继续做截取,取到Parma
+                block.setType("image");
+                // 获取图片的路径
+                List<String> strSrcList = getImg(rowStr, "src");
+                if (strSrcList.size() > 0) {
+                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+                    block.setValue(strSrc);
+                }
+                // 获取图片的高度
+                List<String> strHeightList = getImg(rowStr, "height");
+                if (strHeightList.size() > 0) {
+                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strHeight);
+                    if (px != null) {
+                        param.put("height", px);
+                    }
+                }
+                // 获取图片的宽度
+                List<String> strWidthList = getImg(rowStr, "width");
+                if (strHeightList.size() > 0) {
+                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strWidth);
+                    if (px != null) {
+                        param.put("width", px);
+                    }
+                }
+                block.setParam(param);
+                blocks.add(block);
+            } else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+                rowStr = "<" + rowStr + ">";
+                block.setType("audio");
+                String id = CommonUtils.getAttrValue(rowStr, "id");
+                QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+                block.setValue(questionAudio.getId() + "." + questionAudio.getFileSuffixes());
+                blocks.add(block);
+                hasAudio = true;
+            } else {
+                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+
+                String regex = "###";
+
+                Pattern pattern = Pattern.compile(regex);
+                Matcher matcher = pattern.matcher(rowStr);
+                int indexStart = 0;
+                // 使用find()方法查找匹配项
+                while (matcher.find()) {
+                    blankIndex++;
+                    String content = rowStr.substring(indexStart, matcher.start());
+                    if (StringUtils.isNotEmpty(content)) {
+                        ThemisBlock temblock = new ThemisBlock();
+                        temblock.setType("text");
+                        temblock.setValue(content);
+                        blocks.add(temblock);
+                    }
+                    ThemisBlock blankblock = new ThemisBlock();
+                    blankblock.setType("cloze");
+                    blankblock.setValue(blankIndex);
+                    blocks.add(blankblock);
+                    indexStart = matcher.start() + regex.length();
+                }
+                if (indexStart < rowStr.length()) {
+                    // 输出最后一个分隔符之后的字符串
+                    String content = rowStr.substring(indexStart, rowStr.length());
+                    if (StringUtils.isNotEmpty(content)) {
+                        ThemisBlock temblock = new ThemisBlock();
+                        temblock.setType("text");
+                        temblock.setValue(content);
+                        blocks.add(temblock);
+                    }
+                }
+            }
+        }
+        if (hasAudio) {
+            computerTestPaper.setHasAudio(hasAudio);
+        }
+        return blocks;
+    }
+
+    private Integer getIntFromString(String s) {
+        if (StringUtils.isBlank(s)) {
+            return null;
+        }
+        s = s.trim();
+        s = s.replace("px", "");
+        try {
+            Integer i = Integer.valueOf(s);
+            return i;
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private List<ThemisBlock> disposeQuestionBodyOrOption(String questionRow, ThemisPaper computerTestPaper) {
+        List<ThemisBlock> blocks = new ArrayList<>();
+        // 去掉每行里面的<p>,<span>,</span>标签
+        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+                .replaceAll("</span>", "").replaceAll("</a>", "");
+        String[] questionRowStrings = questionRow.split("<|/>|>");
+        boolean hasAudio = false;
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            ThemisBlock block = new ThemisBlock();
+            String rowStr = questionRowStrings[i];
+            // 判断是否有图片
+            if (rowStr.startsWith("img")) {
+                rowStr = "<" + rowStr + ">";
+                Map<String, Object> param = new HashMap<>();
+                // 需要继续做截取,取到Parma
+                block.setType("image");
+                // 获取图片的路径
+                List<String> strSrcList = getImg(rowStr, "src");
+                if (strSrcList.size() > 0) {
+                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+                    block.setValue(strSrc);
+                }
+                // 获取图片的高度
+                List<String> strHeightList = getImg(rowStr, "height");
+                if (strHeightList.size() > 0) {
+                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strHeight);
+                    if (px != null) {
+                        param.put("height", px);
+                    }
+                }
+                // 获取图片的宽度
+                List<String> strWidthList = getImg(rowStr, "width");
+                if (strHeightList.size() > 0) {
+                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+                    Integer px = getIntFromString(strWidth);
+                    if (px != null) {
+                        param.put("width", px);
+                    }
+                }
+                block.setParam(param);
+                disposeIamgeUrlToBase64(block);
+                blocks.add(block);
+            } else if (rowStr.startsWith("a")) { // 处理音频
+                if (rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+                    rowStr = "<" + rowStr + ">";
+                    block.setType("audio");
+                    String id = CommonUtils.getAttrValue(rowStr, "id");
+                    QuestionAudio questionAudio = Model.of(questionAudioRepo.findById(id));
+                    if (questionAudio == null) {
+                        throw new StatusException("音频标签错误");
+                    }
+                    block.setValue(questionAudio.getId() + "." + questionAudio.getFileSuffixes());
+                    blocks.add(block);
+                    hasAudio = true;
+                } else {
+                    throw new StatusException("音频标签错误");
+                }
+            } else {
+                block.setType("text");
+                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+                if (StringUtils.isNotBlank(rowStr)) {
+                    block.setValue(rowStr);
+                    blocks.add(block);
+                }
+            }
+        }
+        if (hasAudio) {
+            computerTestPaper.setHasAudio(hasAudio);
+        }
+        return blocks;
+    }
+
+    private void disposeIamgeUrlToBase64(ThemisBlock block) {
+        if (!"image".equals(block.getType())) {
+            return;
+        }
+        String src = block.getValue().toString().trim().toLowerCase();
+        if (!src.startsWith("http://") && !src.startsWith("https://")) {
+            return;
+        }
+        try {
+            URL url = new URL(src);
+            InputStream is = url.openStream();
+            block.setValue(FileUtil.fileToBase64(is));
+        } catch (IOException e) {
+            throw new StatusException("500", e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 获取图片里面的路径,长度,宽度
+     */
+    private List<String> getImg(String s, String str) {
+        String regex;
+        List<String> list = new ArrayList<>();
+        regex = str + "=\"(.*?)\"";
+        Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
+        Matcher ma = pa.matcher(s);
+        while (ma.find()) {
+            list.add(ma.group());
+        }
+        return list;
+    }
+
+    private List<ThemisOption> getOption(Question question, ThemisPaper computerTestPaper) {
+        // 得到小题选项
+        List<QuesOption> quesOptions = question.getQuesOptions();
+        List<ThemisOption> options = new ArrayList<>();
+        // 遍历小题选项
+        if (quesOptions != null && quesOptions.size() > 0) {
+            for (QuesOption quesOption : quesOptions) {
+                ThemisOption option = new ThemisOption();
+                option.setNumber(new Integer(quesOption.getNumber()));
+                ThemisSections body = new ThemisSections();
+
+                List<ThemisSection> sections = new ArrayList<>();
+                // 得到小题选项
+                String optionString = quesOption.getOptionBody();
+                String[] optionStrings = optionString.split("</p>");
+                for (int i = 0; i < optionStrings.length; i++) {
+                    List<ThemisBlock> blocks = disposeQuestionBodyOrOption(optionStrings[i], computerTestPaper);
+                    if (blocks != null && blocks.size() > 0) {
+                        ThemisSection section = new ThemisSection();
+                        section.setBlocks(blocks);
+                        sections.add(section);
+                    }
+                }
+                body.setSections(sections);
+                option.setBody(body);
+                options.add(option);
+            }
+        }
+        return options;
+    }
+
+    private JsonArray getSelectAnswer(Question question) {
+        // 得到小题选项
+        List<QuesOption> quesOptions = question.getQuesOptions();
+        int index = 0;
+        // 遍历小题选项
+        if (quesOptions != null && quesOptions.size() > 0) {
+            JsonArray ja = new JsonArray();
+            for (QuesOption quesOption : quesOptions) {
+                index++;
+                if (quesOption.getIsCorrect() == 1) {
+                    ja.add(index);
+                }
+            }
+            if (ja.size() > 0) {
+                return ja;
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    private Boolean getBoolAnswer(Question question) {
+        String as = question.getQuesAnswer();
+        if (StringUtils.isBlank(as)) {
+            return null;
+        }
+        if (as.contains("正确")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void getSectionElement(Node ce, StringBuilder sb) {
+        if (("span".equals(ce.nodeName()) || "p".equals(ce.nodeName())) && ce.childNodeSize() > 0) {
+            for (Node e : ce.childNodes()) {
+                getSectionElement(e, sb);
+            }
+        } else {
+            if (ce instanceof TextNode) {
+                TextNode tn = (TextNode) ce;
+                String text = tn.text();
+                sb.append(text);
+            } else if (ce instanceof Element) {
+                if ("img".equals(ce.nodeName())) {
+                    Element el = (Element) ce;
+                    sb.append(el.outerHtml());
+                } else {
+                    Element el = (Element) ce;
+                    sb.append(el.text());
+                }
+            }
+        }
+    }
+
+    private List<ThemisSubjectiveAnswer> getFillBlankAnswer(Question question) {
+        String html = question.getQuesAnswer();
+        if (StringUtils.isBlank(html)) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        Document doc = Jsoup.parse(html);
+        Element b = doc.body();
+        for (Node ce : b.childNodes()) {
+            getSectionElement(ce, sb);
+        }
+
+        if (StringUtils.isBlank(sb.toString())) {
+            return null;
+        }
+        List<ThemisSubjectiveAnswer> tas = new ArrayList<>();
+        String[] sbs = sb.toString().split("##");
+        int index = 0;
+        for (String an : sbs) {
+            index++;
+            ThemisSubjectiveAnswer ta = new ThemisSubjectiveAnswer();
+            ta.setIndex(index);
+            List<ThemisSection> sections = new ArrayList<>();
+            ThemisSection sec = new ThemisSection();
+            List<ThemisBlock> blocks = new ArrayList<>();
+            Document subdoc = Jsoup.parse(an);
+            for (Node ce : subdoc.body().childNodes()) {
+                ThemisBlock block = new ThemisBlock();
+                if (ce instanceof TextNode) {
+                    TextNode tn = (TextNode) ce;
+                    String text = tn.text();
+                    block.setType("text");
+                    block.setValue(text);
+                } else if (ce instanceof Element) {
+                    if ("img".equals(ce.nodeName())) {
+                        Element el = (Element) ce;
+                        block.setType("image");
+                        block.setValue(el.attr("src"));
+                        String h = el.attr("height");
+                        String w = el.attr("width");
+                        if (StringUtils.isNotBlank(w) && StringUtils.isNotBlank(h)) {
+                            Map<String, Object> param = new HashMap<String, Object>();
+                            Integer hpx = getIntFromString(h);
+                            if (hpx != null) {
+                                param.put("height", hpx);
+                            }
+                            Integer wpx = getIntFromString(w);
+                            if (wpx != null) {
+                                param.put("width", wpx);
+                            }
+                            block.setParam(param);
+                        }
+                    } else {
+                        Element el = (Element) ce;
+                        block.setType("text");
+                        block.setValue(el.text());
+                    }
+                }
+                blocks.add(block);
+            }
+            sec.setBlocks(blocks);
+            sections.add(sec);
+            ta.setSections(sections);
+            tas.add(ta);
+        }
+        return tas;
+    }
 }

+ 563 - 544
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/PaperDetailUnitServiceImpl.java

@@ -1,545 +1,564 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.bson.types.ObjectId;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.data.domain.Sort.Order;
-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.redis.core.RedisTemplate;
-import org.springframework.stereotype.Service;
-
-import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.service.PaperDetailUnitService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.service.QuesService;
-import cn.com.qmth.examcloud.core.questions.service.RandomPaperQuestionService;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitDto;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
-import cn.com.qmth.examcloud.core.questions.service.cache.BasePaperCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigPaperCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.QuestionAnswerCache;
-import cn.com.qmth.examcloud.core.questions.service.cache.QuestionCache;
-import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
-import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
-import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
-
-/**
- * @author chenken
- * @date 2017年9月13日 上午11:29:56
- * @company QMTH
- */
-@Service("paperDetailUnitService")
-public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
-    @Autowired
-    PaperRepo paperRepo;
-    @Autowired
-    private ExtractConfigPaperCache extractConfigPaperCache;
-    @Autowired
-    private BasePaperCache basePaperCache;
-    @Autowired
-    private QuestionCache questionCache;
-    @Autowired
-    private QuestionAnswerCache questionAnswerCache;
-    
-    
-    @Autowired
-    PaperDetailUnitRepo paperDetailUnitRepo;
-
-    @Autowired
-    QuesService quesService;
-
-    @Autowired
-    PaperService paperService;
-
-    @Autowired
-    MongoTemplate mongoTemplate;
-
-    @Autowired
-    RedisTemplate<String, Object> redisTemplate;
-    @Autowired
-    private RandomPaperQuestionService randomPaperQuestionService;
-    
-    /**
-     * 根据Id获得对应的试题对象
-     *
-     * @param id
-     * @return
-     */
-    public Question getQuestionByPaperDetailUnitId(String id) {
-        return Model.of(paperDetailUnitRepo.findById(id)).getQuestion();
-    }
-
-    /**
-     * 按ID查询小题
-     *
-     * @param id
-     * @return
-     */
-    public PaperDetailUnit findById(String id) {
-        return Model.of(paperDetailUnitRepo.findById(id));
-    }
-    private void checkFillBlankQuestion(Question question) {
-    	if(question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
-    		String body=question.getQuesBody();
-    		Document bodyDoc = Jsoup.parse(body);
-    		if(bodyDoc.body().childrenSize()==0) {
-    			throw new StatusException("1000", "题干不能为空");
-    		}
-	        String bodyText=bodyDoc.body().html();
-	        if(StringUtils.isBlank(bodyText)) {
-	        	throw new StatusException("1001", "题干不能为空");
-	        }
-	        if(bodyText.indexOf("###")==-1) {
-	        	throw new StatusException("1002", "题干不能没有空格(###)");
-	        }
-	        String answer=question.getQuesAnswer();
-	        Document answerDoc = Jsoup.parse(answer);
-	        if(answerDoc.body().childrenSize()!=0) {
-		        String answerText=answerDoc.body().html();
-		        if(StringUtils.isNotBlank(answerText)) {
-		        	if(getSubStringCount(bodyText, "###")!=answerText.split("##").length) {
-		        		throw new StatusException("1003", "题干空格(###)数量和答案数量不一致");
-		        	}
-		        }
-	        }
-    	}
-    }
-    private int getSubStringCount(String src,String find){
-    	int o = 0;
-    	int index=-1;
-    	while((index=src.indexOf(find,index))>-1){
-	    	++index;
-	    	++o;
-    	}
-		return o;
-	}
-    
-    @Override
-    public boolean paperInUse(String questionId) {
-    	 Query query = new Query();
-         query.addCriteria(Criteria.where("question.$id").is(new ObjectId(questionId)));
-         query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
-         List<PaperDetailUnitDto> units=this.mongoTemplate.find(query, PaperDetailUnitDto.class,"paperDetailUnit");
-         if(CollectionUtils.isEmpty(units)) {
-        	 return false;
-         }
-         for(PaperDetailUnitDto dto:units) {
-        	 if(dto.getPaper().getInUse()!=null&&dto.getPaper().getInUse()==1) {
-        		 return true;
-        	 }
-         }
-         return false;
-    }
-    /**
-     * 保存小题
-     */
-    public PaperDetailUnit savePaperDetailUnit(PaperDetailUnitExp updateUnit, User user) {
-    	StringBuilder sb=new StringBuilder();
-        PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
-        if(paperInUse(baseUnit.getQuestion().getId())) {
-        	PaperUtil.checkUpdate(updateUnit, baseUnit,"试卷已调用,");
-        }
-        if(randomPaperQuestionService.existQuestion(baseUnit.getQuestion().getId())) {
-        	PaperUtil.checkUpdateOption(updateUnit, baseUnit,"小题已被抽题模板使用,");
-        }
-        Question baseQuestion = baseUnit.getQuestion();
-        Question updateQuestion = updateUnit.getQuestion();
-        checkFillBlankQuestion(updateQuestion);
-        if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
-            if (updateQuestion.getId().equals(baseQuestion.getId())) {
-            	
-            	if(!StringUtils.equals(baseQuestion.getQuesBody(), updateQuestion.getQuesBody())) {
-            		sb.append(" 第"+baseUnit.getNumber()+"小题(套题)题干变动");
-            	}
-                // 更新对象为套题本身
-                quesService.saveQues(updateQuestion);
-                baseUnit.setQuestion(updateQuestion);
-            } else {
-            	if(updateQuestion.getQuesOptions()!=null&&updateQuestion.getQuesOptions().size()>26) {
-            		throw new StatusException("选项数量不能超过26");
-            	}
-                List<Double> subScoreList = baseUnit.getSubScoreList();
-                int size = baseQuestion.getSubQuestions().size();
-                boolean match = false;
-
-                // 判断更新的对象是哪个子题
-                for (int index = 1; index <= size; index++) {
-                    // 检查子题分数,确保没有错误数据
-                    if (subScoreList.size() < index) {
-                        subScoreList.add(0d);
-                    }
-
-                    Question sub = baseQuestion.getSubQuestions().get(index - 1);
-                    if (updateQuestion.getId().equals(sub.getId())) {
-                    	String changInfo=getQuestionChangeInfo(sub, updateQuestion);
-                    	if(changInfo!=null) {
-                    		sb.append("第"+baseUnit.getNumber()+"小题第"+index+"子题变动:"+changInfo);
-                    	}
-                        // 匹配到子题
-                        subScoreList.set(index - 1, updateUnit.getScore());
-                        baseQuestion.getSubQuestions().set(index - 1, updateQuestion);
-
-                        //重新计算套题的难度,公开度
-                        boolean publicity = false;
-                        double totalSum = 0d;
-                        double totalDou = 0d;
-                        for (int i = 0; i < baseQuestion.getSubQuestions().size(); i++) {
-                            Question subQuestion = baseQuestion.getSubQuestions().get(i);
-                            //设置公开度
-                            if (subQuestion.getPublicity()) {
-                                publicity = true;
-                            }
-
-                            if (subQuestion.getDifficultyDegree() == null) {
-                                subQuestion.setDifficultyDegree(0.5);
-                            }
-
-                            totalSum = subQuestion.getDifficultyDegree() * subScoreList.get(i) + totalSum;
-                            totalDou = subScoreList.get(i) + totalDou;
-                        }
-
-                        BigDecimal b;
-                        if (totalDou != 0d) {
-                            b = BigDecimal.valueOf(totalSum / totalDou);
-                        } else {
-                            b = BigDecimal.valueOf(0d);
-                        }
-
-                        Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
-                        baseQuestion.setDifficultyDegree(difficulty);
-                        baseQuestion.setPublicity(publicity);
-                        match = true;
-                    }
-                }
-
-                if (match) {
-                    quesService.saveQues(baseQuestion);
-                    baseUnit.setQuestion(baseQuestion);
-                }
-
-                // 更新套题unit当前总分与子题分数
-                baseUnit.setSubScoreList(subScoreList);
-            }
-        } else {
-        	if(updateQuestion.getQuesOptions()!=null&&updateQuestion.getQuesOptions().size()>26) {
-        		throw new StatusException("选项数量不能超过26");
-        	}
-        	String changInfo=getQuestionChangeInfo(baseQuestion, updateQuestion);
-        	if(changInfo!=null) {
-        		sb.append("第"+baseUnit.getNumber()+"小题变动:"+changInfo);
-        	}
-            quesService.saveQues(updateQuestion);
-            baseUnit.setQuestion(updateQuestion);
-            baseUnit.setScore(updateUnit.getScore());
-        }
-
-        // 同时要跟新小题里面的Question对象
-        paperDetailUnitRepo.save(baseUnit);
-
-        Paper paper = baseUnit.getPaper();
-        Double total=paper.getTotalScore();
-        Integer dc=paper.getPaperDetailCount();
-        Integer uc=paper.getUnitCount();
-        if(PaperType.GENERATE.equals(paper.getPaperType())) {
-        	paper.setAuditStatus(false);
-        }
-        paperService.formatPaper(paper, user);
-
-        String changInfo=PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
-    	if(changInfo!=null) {
-    		sb.append(changInfo);
-    	}
-    	if(sb.length()!=0) {
-    		StringBuilder paperInfo=new StringBuilder();
-    		paperInfo.append(" 课程:"+paper.getCourse().getName()+"("+paper.getCourse().getCode()+")");
-    		paperInfo.append(" 试卷名称:"+paper.getName());
-    		sb.append(paperInfo);
-    		if(PaperType.IMPORT.equals(paper.getPaperType())) {
-    			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE43.getDesc(),sb.toString()));
-    		}else {
-    			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE44.getDesc(),sb.toString()));
-    		}
-    	}
-        if (baseUnit.getQuestion() != null) {
-            //清除缓存
-            this.clearQuestionCache(baseUnit.getQuestion().getId());
-        }
-
-        return baseUnit;
-    }
-    
-    
-    
-    private String getQuestionChangeInfo(Question old,Question now) {
-    	StringBuilder sb=new StringBuilder();
-    	if(old.getScore().doubleValue() !=now.getScore().doubleValue()) {
-    		sb.append(" 分数变动("+old.getScore()+"变为"+now.getScore()+")");
-    	}
-    	if(!StringUtils.equals(old.getQuesBody(), now.getQuesBody())) {
-    		sb.append(" 题干变动");
-    	}
-    	if(optionChange(old, now)) {
-    		sb.append(" 选项变动");
-    	}
-    	if(!StringUtils.equals(old.getQuesAnswer(), now.getQuesAnswer())) {
-    		sb.append(" 答案变动");
-    	}
-    	if(sb.length()==0) {
-    		return null;
-    	}
-    	return sb.toString();
-    }
-    
-    private boolean optionChange(Question old,Question now) {
-    	if(now.getQuesOptions()==null) {
-    		return false;
-    	}
-    	if(old.getQuesOptions().size()!=now.getQuesOptions().size()) {
-    		return true;
-    	}
-    	for(int i=0;i<now.getQuesOptions().size();i++) {
-    		if(!StringUtils.equals(old.getQuesOptions().get(i).getOptionBody(), now.getQuesOptions().get(i).getOptionBody())) {
-    			return true;
-        	}
-    	}
-    	return false;
-    }
-    /**
-     * 删除小题
-     *
-     * @param id
-     * @return
-     */
-    public void deletePaperDetailUnit(String id, User user) {
-    	String changInfo=null;
-        PaperDetailUnit detailUnit = Model.of(paperDetailUnitRepo.findById(id));
-        Paper paper = detailUnit.getPaper();
-        if(paper.getInUse()!=null&&paper.getInUse()==1) {
-			throw new StatusException("500", "试卷已调用");
-		}
-        paperDetailUnitRepo.deleteById(id);
-
-        if (detailUnit.getQuestion() != null) {
-            //清除缓存
-            this.clearQuestionCache(detailUnit.getQuestion().getId());
-        }
-        Double total=paper.getTotalScore();
-        Integer dc=paper.getPaperDetailCount();
-        Integer uc=paper.getUnitCount();
-        paper.setAuditStatus(false);
-        paperService.formatPaper(paper, user);
-        changInfo=PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
-        if(detailUnit!=null) {
-        	StringBuilder sb=new StringBuilder();
-			sb.append("课程:"+detailUnit.getPaper().getCourse().getName()+"("+detailUnit.getPaper().getCourse().getCode()+")");
-			sb.append(" 试卷名称:"+detailUnit.getPaper().getName()); 
-			sb.append(" 第"+detailUnit.getNumber()+"小题 ");
-			sb.append(changInfo);
-        	ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(), AdminOperateType.TYPE45.getDesc(),sb.toString()));
-        }
-    }
-
-    /**
-     * 根据大题查小题
-     *
-     * @param paperDetail
-     * @return
-     */
-    public List<PaperDetailUnit> getUnitsByPaperDetail(PaperDetail paperDetail) {
-        return paperDetailUnitRepo.findByPaperDetailOrderByNumber(paperDetail);
-    }
-
-    /**
-     * 根据大题集合删除小题
-     *
-     * @param paperDetails
-     */
-    public void deleteUnitsByPaperDetails(List<PaperDetail> paperDetails) {
-        List<PaperDetailUnit> units = new ArrayList<>();
-        for (PaperDetail pd : paperDetails) {
-            units.addAll(getUnitsByPaperDetail(pd));
-        }
-        paperDetailUnitRepo.deleteAll(units);
-    }
-
-    public List<PaperDetailUnit> findByQuestionAndPaperTypes(Question question, List<PaperType> paperTypes) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paperType").in(paperTypes));
-        query.addCriteria(Criteria.where("question").is(question));
-        return this.mongoTemplate.find(query, PaperDetailUnit.class);
-    }
-
-    /**
-     * 查询大题下的小题
-     * 按number升序或降序排列,取第一个
-     *
-     * @return
-     */
-    public PaperDetailUnit findTopOrderByNumber(PaperDetail paperDetail, String orderType) {
-        if ("ASC".equals(orderType)) {
-            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberAsc(paperDetail);
-        } else if ("DESC".equals(orderType)) {
-            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberDesc(paperDetail);
-        }
-        return null;
-    }
-
-    /**
-     * 按试卷 查询 小题,并将小题按number,createTime排序
-     *
-     * @param paper
-     * @return
-     */
-    public List<PaperDetailUnit> findByPaperAndSort(Paper paper) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paper").is(paper));
-        query.with(Sort.by(new Order(Direction.ASC, "number")));
-        query.with(Sort.by(new Order(Direction.ASC, "createTime")));
-        return this.mongoTemplate.find(query, PaperDetailUnit.class);
-    }
-
-
-    public List<PaperDetailUnit> findByQuestionsAndPaperTypeOnline(List<String> ids, PaperType paperType) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.id").in(ids));
-        query.addCriteria(Criteria.where("paperType").is(paperType));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public List<PaperDetailUnit> findByQuestionsAndPaperType(List<String> ids, PaperType paperType) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.$id").in(objectIds));
-        query.addCriteria(Criteria.where("paperType").is(paperType));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public List<PaperDetailUnit> findByDDQuestionsAndPaperType(List<String> ids, PaperType paperType, List<Question> questionList) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.$id").in(objectIds));
-        query.addCriteria(Criteria.where("paperType").is("IMPORT"));
-        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-	/*@Override
-	public List<PaperDetailUnit> testFind(List<String> ids,PaperType paperType, List<Question> questionList) {
-		//第一种查询,quesion.id  id
-		DBObject query1 = new BasicDBObject();
-		BasicDBList values = new BasicDBList();
-		for(String id:ids){
-			values.add(id);
-		}
-		query1.put("quesion.id", new BasicDBObject("$in", values));
-		DBCursor dbCursor = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor.hasNext()){
-			DBObject object=dbCursor.next();
-			log.debug("打印游标---------------"+object);
-		}
-
-		//第二种查询,quesion.$id  id
-		//DBObject query2 = new BasicDBObject();
-		BasicDBList values2 = new BasicDBList();
-		for(String id:ids){
-			values2.add(id);
-		}
-		query1.put("quesion.$id", new BasicDBObject("$in", values2));
-		DBCursor dbCursor2 = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor2.hasNext()){
-			DBObject object=dbCursor2.next();
-			log.debug("打印游标2---------------"+object);
-		}
-
-		//第三种查询,quesion.$id  ObjectId
-		//DBObject query3 = new BasicDBObject();
-		BasicDBList values3 = new BasicDBList();
-		for(String id:ids){
-			values3.add(new ObjectId(id));
-		}
-		query1.put("quesion.$id", new BasicDBObject("$in", values3));
-		DBCursor dbCursor3 = mongoTemplate.getCollection("paperDetailUnit").find(query1);
-		while (dbCursor3.hasNext()){
-			DBObject object=dbCursor3.next();
-			log.debug("打印游标3---------------"+object);
-		}
-		return null;
-	}*/
-
-    @Override
-    public List<PaperDetailUnit> findByPaperIds(List<String> ids) {
-        List<ObjectId> objectIds = new ArrayList<>();
-        for (String id : ids) {
-            objectIds.add(new ObjectId(id));
-        }
-        Query query = new Query();
-        query.addCriteria(Criteria.where("paper.$id").in(objectIds));
-        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
-    @Override
-    public void clearQuestionCache(String questionId) {
-        //清理与当前试题相关的缓存
-//        final String patternKey = CACHE_KEY_QUESTION + "*" + questionId;
-//        Set<String> keys = redisTemplate.keys(patternKey);
-//        if (CollectionUtils.isNotEmpty(keys)) {
-//            redisTemplate.delete(keys);
-//        }
-        questionCache.remove(questionId);
-        questionAnswerCache.remove(questionId);
-        //根据questionId清空相关联的paper缓存
-        List<PaperDetailUnit> list=findByQuestionId(questionId);
-        if(list!=null&&list.size()>0) {
-        	for(PaperDetailUnit pd:list) {
-//        		String paperKey = CACHE_KEY_PAPER + "*" + pd.getPaper().getId();
-//                Set<String> paperKeys = redisTemplate.keys(paperKey);
-//                if (CollectionUtils.isNotEmpty(paperKeys)) {
-//                    redisTemplate.delete(paperKeys);
-//                }
-        	    extractConfigPaperCache.remove(pd.getPaper().getId());
-        	    basePaperCache.remove(pd.getPaper().getId());
-        	}
-        }
-    }
-    
-    private List<PaperDetailUnit> findByQuestionId(String questionId) {
-        Query query = new Query();
-        query.addCriteria(Criteria.where("question.id").is(questionId));
-        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
-        return paperDetailUnits;
-    }
-
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.domain.Sort.Order;
+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.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.service.PaperDetailUnitService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.QuesService;
+import cn.com.qmth.examcloud.core.questions.service.RandomPaperQuestionService;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitDto;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
+import cn.com.qmth.examcloud.core.questions.service.cache.BasePaperCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.ExtractConfigPaperCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.QuestionAnswerCache;
+import cn.com.qmth.examcloud.core.questions.service.cache.QuestionCache;
+import cn.com.qmth.examcloud.core.questions.service.util.PaperUtil;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+
+/**
+ * @author chenken
+ * @date 2017年9月13日 上午11:29:56
+ * @company QMTH
+ */
+@Service("paperDetailUnitService")
+public class PaperDetailUnitServiceImpl implements PaperDetailUnitService {
+
+    @Autowired
+    PaperRepo paperRepo;
+
+    @Autowired
+    private ExtractConfigPaperCache extractConfigPaperCache;
+
+    @Autowired
+    private BasePaperCache basePaperCache;
+
+    @Autowired
+    private QuestionCache questionCache;
+
+    @Autowired
+    private QuestionAnswerCache questionAnswerCache;
+
+    @Autowired
+    PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    QuesService quesService;
+
+    @Autowired
+    PaperService paperService;
+
+    @Autowired
+    MongoTemplate mongoTemplate;
+
+    @Autowired
+    RedisTemplate<String, Object> redisTemplate;
+
+    @Autowired
+    private RandomPaperQuestionService randomPaperQuestionService;
+
+    /**
+     * 根据Id获得对应的试题对象
+     *
+     * @param id
+     * @return
+     */
+    public Question getQuestionByPaperDetailUnitId(String id) {
+        return Model.of(paperDetailUnitRepo.findById(id)).getQuestion();
+    }
+
+    /**
+     * 按ID查询小题
+     *
+     * @param id
+     * @return
+     */
+    public PaperDetailUnit findById(String id) {
+        return Model.of(paperDetailUnitRepo.findById(id));
+    }
+
+    private void checkFillBlankQuestion(Question question) {
+        if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+            String body = question.getQuesBody();
+            Document bodyDoc = Jsoup.parse(body);
+            if (bodyDoc.body().childrenSize() == 0) {
+                throw new StatusException("1000", "题干不能为空");
+            }
+            String bodyText = bodyDoc.body().html();
+            if (StringUtils.isBlank(bodyText)) {
+                throw new StatusException("1001", "题干不能为空");
+            }
+            if (bodyText.indexOf("###") == -1) {
+                throw new StatusException("1002", "题干不能没有空格(###)");
+            }
+            String answer = question.getQuesAnswer();
+            Document answerDoc = Jsoup.parse(answer);
+            if (answerDoc.body().childrenSize() != 0) {
+                String answerText = answerDoc.body().html();
+                if (StringUtils.isNotBlank(answerText)) {
+                    if (getSubStringCount(bodyText, "###") != answerText.split("##").length) {
+                        throw new StatusException("1003", "题干空格(###)数量和答案数量不一致");
+                    }
+                }
+            }
+        }
+    }
+
+    private int getSubStringCount(String src, String find) {
+        int o = 0;
+        int index = -1;
+        while ((index = src.indexOf(find, index)) > -1) {
+            ++index;
+            ++o;
+        }
+        return o;
+    }
+
+    @Override
+    public boolean paperInUse(String questionId) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").is(new ObjectId(questionId)));
+        query.addCriteria(Criteria.where("paperType").is(PaperType.GENERATE.name()));
+        List<PaperDetailUnitDto> units = this.mongoTemplate.find(query, PaperDetailUnitDto.class, "paperDetailUnit");
+        if (CollectionUtils.isEmpty(units)) {
+            return false;
+        }
+        for (PaperDetailUnitDto dto : units) {
+            if (dto.getPaper().getInUse() != null && dto.getPaper().getInUse() == 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 保存小题
+     */
+    public PaperDetailUnit savePaperDetailUnit(PaperDetailUnitExp updateUnit, User user) {
+        StringBuilder sb = new StringBuilder();
+        PaperDetailUnit baseUnit = Model.of(paperDetailUnitRepo.findById(updateUnit.getId()));
+        if (paperInUse(baseUnit.getQuestion().getId())) {
+            PaperUtil.checkUpdate(updateUnit, baseUnit, "试卷已调用,");
+        }
+        if (randomPaperQuestionService.existQuestion(baseUnit.getQuestion().getId())) {
+            PaperUtil.checkUpdateOption(updateUnit, baseUnit, "小题已被抽题模板使用,");
+        }
+        Question baseQuestion = baseUnit.getQuestion();
+        Question updateQuestion = updateUnit.getQuestion();
+        checkFillBlankQuestion(updateQuestion);
+        if (baseUnit.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+            if (updateQuestion.getId().equals(baseQuestion.getId())) {
+
+                if (!StringUtils.equals(baseQuestion.getQuesBody(), updateQuestion.getQuesBody())) {
+                    sb.append(" 第" + baseUnit.getNumber() + "小题(套题)题干变动");
+                }
+                // 更新对象为套题本身
+                quesService.saveQues(updateQuestion);
+                baseUnit.setQuestion(updateQuestion);
+            } else {
+                if (updateQuestion.getQuesOptions() != null && updateQuestion.getQuesOptions().size() > 26) {
+                    throw new StatusException("选项数量不能超过26");
+                }
+                List<Double> subScoreList = baseUnit.getSubScoreList();
+                int size = baseQuestion.getSubQuestions().size();
+                boolean match = false;
+
+                // 判断更新的对象是哪个子题
+                for (int index = 1; index <= size; index++) {
+                    // 检查子题分数,确保没有错误数据
+                    if (subScoreList.size() < index) {
+                        subScoreList.add(0d);
+                    }
+
+                    Question sub = baseQuestion.getSubQuestions().get(index - 1);
+                    if (updateQuestion.getId().equals(sub.getId())) {
+                        String changInfo = getQuestionChangeInfo(sub, updateQuestion);
+                        if (changInfo != null) {
+                            sb.append("第" + baseUnit.getNumber() + "小题第" + index + "子题变动:" + changInfo);
+                        }
+                        // 匹配到子题
+                        subScoreList.set(index - 1, updateUnit.getScore());
+                        baseQuestion.getSubQuestions().set(index - 1, updateQuestion);
+
+                        // 重新计算套题的难度,公开度
+                        boolean publicity = false;
+                        double totalSum = 0d;
+                        double totalDou = 0d;
+                        for (int i = 0; i < baseQuestion.getSubQuestions().size(); i++) {
+                            Question subQuestion = baseQuestion.getSubQuestions().get(i);
+                            // 设置公开度
+                            if (subQuestion.getPublicity()) {
+                                publicity = true;
+                            }
+
+                            if (subQuestion.getDifficultyDegree() == null) {
+                                subQuestion.setDifficultyDegree(0.5);
+                            }
+
+                            totalSum = subQuestion.getDifficultyDegree() * subScoreList.get(i) + totalSum;
+                            totalDou = subScoreList.get(i) + totalDou;
+                        }
+
+                        BigDecimal b;
+                        if (totalDou != 0d) {
+                            b = BigDecimal.valueOf(totalSum / totalDou);
+                        } else {
+                            b = BigDecimal.valueOf(0d);
+                        }
+
+                        Double difficulty = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+                        baseQuestion.setDifficultyDegree(difficulty);
+                        baseQuestion.setPublicity(publicity);
+                        match = true;
+                    }
+                }
+
+                if (match) {
+                    quesService.saveQues(baseQuestion);
+                    baseUnit.setQuestion(baseQuestion);
+                }
+
+                // 更新套题unit当前总分与子题分数
+                baseUnit.setSubScoreList(subScoreList);
+            }
+        } else {
+            if (updateQuestion.getQuesOptions() != null && updateQuestion.getQuesOptions().size() > 26) {
+                throw new StatusException("选项数量不能超过26");
+            }
+            String changInfo = getQuestionChangeInfo(baseQuestion, updateQuestion);
+            if (changInfo != null) {
+                sb.append("第" + baseUnit.getNumber() + "小题变动:" + changInfo);
+            }
+            quesService.saveQues(updateQuestion);
+            baseUnit.setQuestion(updateQuestion);
+            baseUnit.setScore(updateUnit.getScore());
+        }
+
+        // 同时要跟新小题里面的Question对象
+        paperDetailUnitRepo.save(baseUnit);
+
+        Paper paper = baseUnit.getPaper();
+        Double total = paper.getTotalScore();
+        Integer dc = paper.getPaperDetailCount();
+        Integer uc = paper.getUnitCount();
+        if (PaperType.GENERATE.equals(paper.getPaperType())) {
+            paper.setAuditStatus(false);
+        }
+        paperService.formatPaper(paper, user);
+
+        String changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
+        if (changInfo != null) {
+            sb.append(changInfo);
+        }
+        if (sb.length() != 0) {
+            StringBuilder paperInfo = new StringBuilder();
+            paperInfo.append(" 课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
+            paperInfo.append(" 试卷名称:" + paper.getName());
+            sb.append(paperInfo);
+            if (PaperType.IMPORT.equals(paper.getPaperType())) {
+                ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                        AdminOperateType.TYPE43.getDesc(), sb.toString()));
+            } else {
+                ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                        AdminOperateType.TYPE44.getDesc(), sb.toString()));
+            }
+        }
+        if (baseUnit.getQuestion() != null) {
+            // 清除缓存
+            this.clearQuestionCache(baseUnit.getQuestion().getId());
+        }
+
+        return baseUnit;
+    }
+
+    private String getQuestionChangeInfo(Question old, Question now) {
+        StringBuilder sb = new StringBuilder();
+        if (old.getScore().doubleValue() != now.getScore().doubleValue()) {
+            sb.append(" 分数变动(" + old.getScore() + "变为" + now.getScore() + ")");
+        }
+        if (!StringUtils.equals(old.getQuesBody(), now.getQuesBody())) {
+            sb.append(" 题干变动");
+        }
+        if (optionChange(old, now)) {
+            sb.append(" 选项变动");
+        }
+        if (!StringUtils.equals(old.getQuesAnswer(), now.getQuesAnswer())) {
+            sb.append(" 答案变动");
+        }
+        if (sb.length() == 0) {
+            return null;
+        }
+        return sb.toString();
+    }
+
+    private boolean optionChange(Question old, Question now) {
+        if (now.getQuesOptions() == null) {
+            return false;
+        }
+        if (old.getQuesOptions().size() != now.getQuesOptions().size()) {
+            return true;
+        }
+        for (int i = 0; i < now.getQuesOptions().size(); i++) {
+            if (!StringUtils.equals(old.getQuesOptions().get(i).getOptionBody(),
+                    now.getQuesOptions().get(i).getOptionBody())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 删除小题
+     *
+     * @param id
+     * @return
+     */
+    public void deletePaperDetailUnit(String id, User user) {
+        String changInfo = null;
+        PaperDetailUnit detailUnit = Model.of(paperDetailUnitRepo.findById(id));
+        Paper paper = detailUnit.getPaper();
+        if (paper.getInUse() != null && paper.getInUse() == 1) {
+            throw new StatusException("500", "试卷已调用");
+        }
+        paperDetailUnitRepo.deleteById(id);
+
+        if (detailUnit.getQuestion() != null) {
+            // 清除缓存
+            this.clearQuestionCache(detailUnit.getQuestion().getId());
+        }
+        Double total = paper.getTotalScore();
+        Integer dc = paper.getPaperDetailCount();
+        Integer uc = paper.getUnitCount();
+        paper.setAuditStatus(false);
+        paperService.formatPaper(paper, user);
+        changInfo = PaperUtil.getPaperChangeInfo(total, dc, uc, paper);
+        if (detailUnit != null) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("课程:" + detailUnit.getPaper().getCourse().getName() + "("
+                    + detailUnit.getPaper().getCourse().getCode() + ")");
+            sb.append(" 试卷名称:" + detailUnit.getPaper().getName());
+            sb.append(" 第" + detailUnit.getNumber() + "小题 ");
+            sb.append(changInfo);
+            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                    AdminOperateType.TYPE45.getDesc(), sb.toString()));
+        }
+    }
+
+    /**
+     * 根据大题查小题
+     *
+     * @param paperDetail
+     * @return
+     */
+    public List<PaperDetailUnit> getUnitsByPaperDetail(PaperDetail paperDetail) {
+        return paperDetailUnitRepo.findByPaperDetailOrderByNumber(paperDetail);
+    }
+
+    /**
+     * 根据大题集合删除小题
+     *
+     * @param paperDetails
+     */
+    public void deleteUnitsByPaperDetails(List<PaperDetail> paperDetails) {
+        List<PaperDetailUnit> units = new ArrayList<>();
+        for (PaperDetail pd : paperDetails) {
+            units.addAll(getUnitsByPaperDetail(pd));
+        }
+        paperDetailUnitRepo.deleteAll(units);
+    }
+
+    public List<PaperDetailUnit> findByQuestionAndPaperTypes(Question question, List<PaperType> paperTypes) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paperType").in(paperTypes));
+        query.addCriteria(Criteria.where("question").is(question));
+        return this.mongoTemplate.find(query, PaperDetailUnit.class);
+    }
+
+    /**
+     * 查询大题下的小题 按number升序或降序排列,取第一个
+     *
+     * @return
+     */
+    public PaperDetailUnit findTopOrderByNumber(PaperDetail paperDetail, String orderType) {
+        if ("ASC".equals(orderType)) {
+            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberAsc(paperDetail);
+        } else if ("DESC".equals(orderType)) {
+            return paperDetailUnitRepo.findTopByPaperDetailOrderByNumberDesc(paperDetail);
+        }
+        return null;
+    }
+
+    /**
+     * 按试卷 查询 小题,并将小题按number,createTime排序
+     *
+     * @param paper
+     * @return
+     */
+    public List<PaperDetailUnit> findByPaperAndSort(Paper paper) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paper").is(paper));
+        // query.with(Sort.by(new Order(Direction.ASC, "number")));
+        // query.with(Sort.by(new Order(Direction.ASC, "createTime")));
+        List<PaperDetailUnit> list = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        Collections.sort(list, new Comparator<PaperDetailUnit>() {
+
+            @Override
+            public int compare(PaperDetailUnit o1, PaperDetailUnit o2) {
+                if (o1.getPaperDetail().getNumber() > o2.getPaperDetail().getNumber()) {
+                    return 1;
+                } else if (o1.getPaperDetail().getNumber() < o2.getPaperDetail().getNumber()) {
+                    return -1;
+                } else {
+                    if (o1.getNumber() > o2.getNumber()) {
+                        return 1;
+                    } else if (o1.getNumber() < o2.getNumber()) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+                }
+            }
+
+        });
+        return list;
+    }
+
+    public List<PaperDetailUnit> findByQuestionsAndPaperTypeOnline(List<String> ids, PaperType paperType) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.id").in(ids));
+        query.addCriteria(Criteria.where("paperType").is(paperType));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public List<PaperDetailUnit> findByQuestionsAndPaperType(List<String> ids, PaperType paperType) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").in(objectIds));
+        query.addCriteria(Criteria.where("paperType").is(paperType));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public List<PaperDetailUnit> findByDDQuestionsAndPaperType(List<String> ids, PaperType paperType,
+            List<Question> questionList) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.$id").in(objectIds));
+        query.addCriteria(Criteria.where("paperType").is("IMPORT"));
+        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    /*
+     * @Override public List<PaperDetailUnit> testFind(List<String>
+     * ids,PaperType paperType, List<Question> questionList) {
+     * //第一种查询,quesion.id id DBObject query1 = new BasicDBObject(); BasicDBList
+     * values = new BasicDBList(); for(String id:ids){ values.add(id); }
+     * query1.put("quesion.id", new BasicDBObject("$in", values)); DBCursor
+     * dbCursor = mongoTemplate.getCollection("paperDetailUnit").find(query1);
+     * while (dbCursor.hasNext()){ DBObject object=dbCursor.next();
+     * log.debug("打印游标---------------"+object); }
+     * 
+     * //第二种查询,quesion.$id id //DBObject query2 = new BasicDBObject();
+     * BasicDBList values2 = new BasicDBList(); for(String id:ids){
+     * values2.add(id); } query1.put("quesion.$id", new BasicDBObject("$in",
+     * values2)); DBCursor dbCursor2 =
+     * mongoTemplate.getCollection("paperDetailUnit").find(query1); while
+     * (dbCursor2.hasNext()){ DBObject object=dbCursor2.next();
+     * log.debug("打印游标2---------------"+object); }
+     * 
+     * //第三种查询,quesion.$id ObjectId //DBObject query3 = new BasicDBObject();
+     * BasicDBList values3 = new BasicDBList(); for(String id:ids){
+     * values3.add(new ObjectId(id)); } query1.put("quesion.$id", new
+     * BasicDBObject("$in", values3)); DBCursor dbCursor3 =
+     * mongoTemplate.getCollection("paperDetailUnit").find(query1); while
+     * (dbCursor3.hasNext()){ DBObject object=dbCursor3.next();
+     * log.debug("打印游标3---------------"+object); } return null; }
+     */
+
+    @Override
+    public List<PaperDetailUnit> findByPaperIds(List<String> ids) {
+        List<ObjectId> objectIds = new ArrayList<>();
+        for (String id : ids) {
+            objectIds.add(new ObjectId(id));
+        }
+        Query query = new Query();
+        query.addCriteria(Criteria.where("paper.$id").in(objectIds));
+        List<PaperDetailUnit> paperDetailUnits = mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
+    @Override
+    public void clearQuestionCache(String questionId) {
+        // 清理与当前试题相关的缓存
+        // final String patternKey = CACHE_KEY_QUESTION + "*" + questionId;
+        // Set<String> keys = redisTemplate.keys(patternKey);
+        // if (CollectionUtils.isNotEmpty(keys)) {
+        // redisTemplate.delete(keys);
+        // }
+        questionCache.remove(questionId);
+        questionAnswerCache.remove(questionId);
+        // 根据questionId清空相关联的paper缓存
+        List<PaperDetailUnit> list = findByQuestionId(questionId);
+        if (list != null && list.size() > 0) {
+            for (PaperDetailUnit pd : list) {
+                // String paperKey = CACHE_KEY_PAPER + "*" +
+                // pd.getPaper().getId();
+                // Set<String> paperKeys = redisTemplate.keys(paperKey);
+                // if (CollectionUtils.isNotEmpty(paperKeys)) {
+                // redisTemplate.delete(paperKeys);
+                // }
+                extractConfigPaperCache.remove(pd.getPaper().getId());
+                basePaperCache.remove(pd.getPaper().getId());
+            }
+        }
+    }
+
+    private List<PaperDetailUnit> findByQuestionId(String questionId) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("question.id").is(questionId));
+        List<PaperDetailUnit> paperDetailUnits = this.mongoTemplate.find(query, PaperDetailUnit.class);
+        return paperDetailUnits;
+    }
+
 }

+ 228 - 221
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/QuestionAudioServiceImpl.java

@@ -1,221 +1,228 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import cn.com.qmth.examcloud.support.fss.FssFactory;
-import cn.com.qmth.examcloud.support.fss.FssHelper;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.Assert;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
-import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
-
-/**
- * @author chenken
- * @date 2017年8月1日 上午11:27:33
- * @company QMTH
- * @description QuestionRadioServiceImpl.java
- */
-@Service("questionAudioService")
-public class QuestionAudioServiceImpl implements QuestionAudioService {
-
-    @Autowired
-    private QuestionAudioRepo questionAudioRepo;
-
-    @Override
-    public void saveQuestionAudio(QuestionAudio questionAudio, User user) {
-        QuestionAudio existQuestionAudio = this.findByQuestionIdAndFileName(questionAudio.getQuestionId(), questionAudio.getFileName());
-        if (existQuestionAudio != null) {
-            questionAudio.setId(existQuestionAudio.getId());
-
-            String filePath = FssHelper.fixFilePath(existQuestionAudio.getFileUrl());
-            FssFactory.getInstance().deleteFile(filePath);
-
-            questionAudioRepo.delete(existQuestionAudio);
-        }
-
-        questionAudio.setCreateTime(new Date());
-        questionAudio.setCreateUser(user.getDisplayName());
-        questionAudioRepo.save(questionAudio);
-    }
-
-    @Override
-    public List<QuestionAudio> findQuestionAudiosByQuestionId(String questionId) {
-        Assert.hasLength(questionId, "questionId不能为空");
-        return questionAudioRepo.findByQuestionId(questionId);
-    }
-
-    @Override
-    public QuestionAudio findByQuestionIdAndFileName(String questionId, String fileName) {
-        if (StringUtils.isBlank(questionId) || StringUtils.isBlank(fileName)) {
-            return null;
-        }
-
-        List<QuestionAudio> audioList = questionAudioRepo.findByQuestionIdAndFileName(questionId, fileName);
-        if (CollectionUtils.isNotEmpty(audioList)) {
-
-            Collections.sort(audioList, (target1, target2) -> {
-                if (target1.getCreateTime() == null || target2.getCreateTime() == null) {
-                    return 0;
-                }
-
-                if (target1.getCreateTime().before(target2.getCreateTime())) {
-                    return 1;
-                }
-
-                return -1;
-            });
-
-            //取最新的一个
-            return audioList.get(0);
-        }
-
-        return null;
-    }
-
-    @Override
-    public void deleteAudioNotInQuestion(Question question) {
-        if (question.getHasAudio() != null && question.getHasAudio()) {
-            List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionId(question.getId());
-            StringBuffer buffer = new StringBuffer();
-            buffer.append(question.getQuesBody());
-            if (question.getQuesOptions() != null && question.getQuesOptions().size() > 0) {
-                for (QuesOption option : question.getQuesOptions()) {
-                    buffer.append(option.getOptionBody());
-                }
-            }
-
-            String questionBodyAndOptionStr = buffer.toString();
-            Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
-            while (audioIterator.hasNext()) {
-                QuestionAudio audio = audioIterator.next();
-                if (!questionBodyAndOptionStr.contains(audio.getFileName())) {
-
-                    String filePath = FssHelper.fixFilePath(audio.getFileUrl());
-                    FssFactory.getInstance().deleteFile(filePath);
-
-                    questionAudioRepo.delete(audio);
-                }
-            }
-        }
-    }
-
-    @Override
-    public QuestionAudio findAudioById(String id) {
-        if (StringUtils.isNotBlank(id)) {
-            return Model.of(questionAudioRepo.findById(id));
-        }
-        return null;
-    }
-
-    @Override
-    public void sortAudio(Question question) {
-        if (question.getHasAudio() != null && question.getHasAudio()) {
-            //音频顺序改变后,更新bodyHtml
-            String newQusBody = getFileNames(question.getQuesBody(), question);
-            question.setQuesBody(newQusBody);
-            //跟新选项中的optionsHtml
-            if (question.getQuesOptions() != null && question.getQuesOptions().size() > 0) {
-                for (QuesOption option : question.getQuesOptions()) {
-                    String newOptionBody = getFileNames(option.getOptionBody(), question);
-                    option.setOptionBody(newOptionBody);
-                }
-            }
-        }
-    }
-
-    private String getFileNames(String str, Question question) {
-        Map<String, String> map = new LinkedHashMap<>();
-        Pattern p = Pattern.compile("<a\\s+[^<>]*\\s+name=\"([^<>\"]*)\"[^<>]*>");
-        Matcher m = p.matcher(str);
-        int i = 1;
-        int y = 1;
-        while (m.find()) {
-            String name = m.group(1);
-            StringBuffer buffer = new StringBuffer();
-            if (name.substring(0, name.indexOf("_") + 1).contains("2")) {
-                buffer.append(name.substring(0, name.lastIndexOf("_") + 1));
-                buffer.append(i);
-                buffer.append("@temp");
-                buffer.append(name.substring(name.lastIndexOf(".")));
-                map.put(name, buffer.toString());
-                i++;
-            } else {
-                buffer.append(name.substring(0, name.lastIndexOf("_") + 1));
-                buffer.append(y);
-                buffer.append("@temp");
-                buffer.append(name.substring(name.lastIndexOf(".")));
-                map.put(name, buffer.toString());
-                y++;
-            }
-        }
-        //循环替换
-        for (String key : map.keySet()) {
-            str = str.replace(key, map.get(key));
-        }
-        //替换并更新数据
-        for (String key : map.keySet()) {
-            String fileName = key;
-            String value = map.get(key).replace("@temp", "");
-            str = str.replace(map.get(key), value);
-            QuestionAudio questionAudio = this.findByQuestionIdAndFileName(question.getId(), fileName);
-            questionAudio.setFileName(value);
-            questionAudioRepo.save(questionAudio);
-        }
-        return str;
-    }
-
-    @Override
-    public void deleteAudio(List<Question> questions) {
-        //筛选有音频的试题
-        List<String> ids = new ArrayList<String>();
-        for (Question question : questions) {
-            if (question.getHasAudio() != null && question.getHasAudio()) {
-                ids.add(question.getId());
-            }
-        }
-        List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionIdIn(ids);
-        Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
-        while (audioIterator.hasNext()) {
-            QuestionAudio audio = audioIterator.next();
-
-            String filePath = FssHelper.fixFilePath(audio.getFileUrl());
-            FssFactory.getInstance().deleteFile(filePath);
-        }
-        questionAudioRepo.deleteAll(questionAudios);
-    }
-    
-    @Override
-    public void deleteAudioByQuestionId(List<String> questionIds) {
-    	if(CollectionUtils.isEmpty(questionIds)) {
-    		return;
-    	}
-        List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionIdIn(questionIds);
-        Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
-        while (audioIterator.hasNext()) {
-            QuestionAudio audio = audioIterator.next();
-
-            String filePath = FssHelper.fixFilePath(audio.getFileUrl());
-            FssFactory.getInstance().deleteFile(filePath);
-        }
-        questionAudioRepo.deleteAll(questionAudios);
-    }
-
-}
-
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import cn.com.qmth.examcloud.support.fss.FssFactory;
+import cn.com.qmth.examcloud.support.fss.FssHelper;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.dao.QuestionAudioRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
+import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
+
+/**
+ * @author chenken
+ * @date 2017年8月1日 上午11:27:33
+ * @company QMTH
+ * @description QuestionRadioServiceImpl.java
+ */
+@Service("questionAudioService")
+public class QuestionAudioServiceImpl implements QuestionAudioService {
+
+    @Autowired
+    private QuestionAudioRepo questionAudioRepo;
+
+    @Override
+    public void saveQuestionAudio(QuestionAudio questionAudio, User user) {
+        QuestionAudio existQuestionAudio = this.findByQuestionIdAndFileName(questionAudio.getQuestionId(),
+                questionAudio.getFileName());
+        if (existQuestionAudio != null) {
+            questionAudio.setId(existQuestionAudio.getId());
+
+            String filePath = FssHelper.fixFilePath(existQuestionAudio.getFileUrl());
+            FssFactory.getInstance().deleteFile(filePath);
+
+            questionAudioRepo.delete(existQuestionAudio);
+        }
+
+        questionAudio.setCreateTime(new Date());
+        questionAudio.setCreateUser(user.getDisplayName());
+        questionAudioRepo.save(questionAudio);
+    }
+
+    @Override
+    public List<QuestionAudio> findQuestionAudiosByQuestionId(String questionId) {
+        Assert.hasLength(questionId, "questionId不能为空");
+        return questionAudioRepo.findByQuestionId(questionId);
+    }
+
+    @Override
+    public QuestionAudio findByQuestionIdAndFileName(String questionId, String fileName) {
+        if (StringUtils.isBlank(questionId) || StringUtils.isBlank(fileName)) {
+            return null;
+        }
+
+        List<QuestionAudio> audioList = questionAudioRepo.findByQuestionIdAndFileName(questionId, fileName);
+        if (CollectionUtils.isNotEmpty(audioList)) {
+
+            Collections.sort(audioList, (target1, target2) -> {
+                if (target1.getCreateTime() == null || target2.getCreateTime() == null) {
+                    return 0;
+                }
+
+                if (target1.getCreateTime().before(target2.getCreateTime())) {
+                    return 1;
+                }
+
+                return -1;
+            });
+
+            // 取最新的一个
+            return audioList.get(0);
+        }
+
+        return null;
+    }
+
+    @Override
+    public void deleteAudioNotInQuestion(Question question) {
+        if (question.getHasAudio() != null && question.getHasAudio()) {
+            List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionId(question.getId());
+            StringBuffer buffer = new StringBuffer();
+            buffer.append(question.getQuesBody());
+            if (question.getQuesOptions() != null && question.getQuesOptions().size() > 0) {
+                for (QuesOption option : question.getQuesOptions()) {
+                    buffer.append(option.getOptionBody());
+                }
+            }
+
+            String questionBodyAndOptionStr = buffer.toString();
+            Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
+            while (audioIterator.hasNext()) {
+                QuestionAudio audio = audioIterator.next();
+                if (!questionBodyAndOptionStr.contains(audio.getFileName())) {
+
+                    String filePath = FssHelper.fixFilePath(audio.getFileUrl());
+                    FssFactory.getInstance().deleteFile(filePath);
+
+                    questionAudioRepo.delete(audio);
+                }
+            }
+        }
+    }
+
+    @Override
+    public QuestionAudio findAudioById(String id) {
+        if (StringUtils.isNotBlank(id)) {
+            return Model.of(questionAudioRepo.findById(id));
+        }
+        return null;
+    }
+
+    @Override
+    public void sortAudio(Question question) {
+        if (question.getHasAudio() != null && question.getHasAudio()) {
+            // 音频顺序改变后,更新bodyHtml
+            String newQusBody = getFileNames(question.getQuesBody(), question);
+            question.setQuesBody(newQusBody);
+            // 跟新选项中的optionsHtml
+            if (question.getQuesOptions() != null && question.getQuesOptions().size() > 0) {
+                for (QuesOption option : question.getQuesOptions()) {
+                    String newOptionBody = getFileNames(option.getOptionBody(), question);
+                    option.setOptionBody(newOptionBody);
+                }
+            }
+        }
+    }
+
+    private String getFileNames(String str, Question question) {
+        Map<String, String> map = new LinkedHashMap<>();
+        Pattern p = Pattern.compile("<a\\s+[^<>]*\\s+name=\"([^<>\"]*)\"[^<>]*>");
+        Matcher m = p.matcher(str);
+        int i = 1;
+        int y = 1;
+        while (m.find()) {
+            String name = m.group(1);
+            if (name.lastIndexOf(".") == -1) {
+                throw new StatusException("音频标签错误");
+            }
+            StringBuffer buffer = new StringBuffer();
+            if (name.substring(0, name.indexOf("_") + 1).contains("2")) {
+                buffer.append(name.substring(0, name.lastIndexOf("_") + 1));
+                buffer.append(i);
+                buffer.append("@temp");
+                buffer.append(name.substring(name.lastIndexOf(".")));
+                map.put(name, buffer.toString());
+                i++;
+            } else {
+                buffer.append(name.substring(0, name.lastIndexOf("_") + 1));
+                buffer.append(y);
+                buffer.append("@temp");
+                buffer.append(name.substring(name.lastIndexOf(".")));
+                map.put(name, buffer.toString());
+                y++;
+            }
+        }
+        // 循环替换
+        for (String key : map.keySet()) {
+            str = str.replace(key, map.get(key));
+        }
+        // 替换并更新数据
+        for (String key : map.keySet()) {
+            String fileName = key;
+            String value = map.get(key).replace("@temp", "");
+            str = str.replace(map.get(key), value);
+            QuestionAudio questionAudio = this.findByQuestionIdAndFileName(question.getId(), fileName);
+            if (questionAudio == null) {
+                throw new StatusException("音频标签错误");
+            }
+            questionAudio.setFileName(value);
+            questionAudioRepo.save(questionAudio);
+        }
+        return str;
+    }
+
+    @Override
+    public void deleteAudio(List<Question> questions) {
+        // 筛选有音频的试题
+        List<String> ids = new ArrayList<String>();
+        for (Question question : questions) {
+            if (question.getHasAudio() != null && question.getHasAudio()) {
+                ids.add(question.getId());
+            }
+        }
+        List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionIdIn(ids);
+        Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
+        while (audioIterator.hasNext()) {
+            QuestionAudio audio = audioIterator.next();
+
+            String filePath = FssHelper.fixFilePath(audio.getFileUrl());
+            FssFactory.getInstance().deleteFile(filePath);
+        }
+        questionAudioRepo.deleteAll(questionAudios);
+    }
+
+    @Override
+    public void deleteAudioByQuestionId(List<String> questionIds) {
+        if (CollectionUtils.isEmpty(questionIds)) {
+            return;
+        }
+        List<QuestionAudio> questionAudios = questionAudioRepo.findByQuestionIdIn(questionIds);
+        Iterator<QuestionAudio> audioIterator = questionAudios.iterator();
+        while (audioIterator.hasNext()) {
+            QuestionAudio audio = audioIterator.next();
+
+            String filePath = FssHelper.fixFilePath(audio.getFileUrl());
+            FssFactory.getInstance().deleteFile(filePath);
+        }
+        questionAudioRepo.deleteAll(questionAudios);
+    }
+
+}

+ 180 - 154
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/themispaper/ThemisQuestion.java

@@ -1,154 +1,180 @@
-package cn.com.qmth.examcloud.core.questions.service.themispaper;
-
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author chenken
- * @date 2017年7月25日 上午9:51:37
- * @company QMTH
- * @description ComputerTestQuestion.java
- */
-public class ThemisQuestion {
-
-    private String id;
-    /**
-     * 小题号或套题内子题序号
-     */
-    private Integer number;
-
-    private Double score;// 小题分数
-    /**
-     * 1-单选,2-多选,3-判断,4-填空,5-问答,6-套题
-     */
-    private Long structType;
-    /**
-     * 是否客观题
-     */
-    private Boolean objective;
-    /**
-     * 子题
-     */
-    private List<ThemisQuestion> subQuestions;
-
-    /**
-     * 题干
-     */
-    private ThemisSections body;
-
-    /**
-     * 选项
-     */
-    private List<ThemisOption> options;
-
-    private Map<String, Object> param;
-
-
-    public ThemisQuestion() {
-    }
-
-    public ThemisQuestion(PaperDetailUnit paperDetailUnit) {
-        this.id = paperDetailUnit.getId();
-        this.number = paperDetailUnit.getNumber();
-        this.score = paperDetailUnit.getScore();
-        this.structType = paperDetailUnit.getQuestionType().getId();
-        if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
-                paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION ||
-                paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-            this.objective = true;
-        } else {
-            this.objective = false;
-        }
-    }
-
-    public ThemisQuestion(Question question) {
-        this.id = question.getId();
-        this.score = question.getScore();
-        this.structType = question.getQuestionType().getId();
-        if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION ||
-                question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION ||
-                question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
-            this.objective = true;
-        } else {
-            this.objective = false;
-        }
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public Integer getNumber() {
-        return number;
-    }
-
-    public void setNumber(Integer number) {
-        this.number = number;
-    }
-
-    public Double getScore() {
-        return score;
-    }
-
-    public void setScore(Double score) {
-        this.score = score;
-    }
-
-    public Long getStructType() {
-        return structType;
-    }
-
-    public void setStructType(Long structType) {
-        this.structType = structType;
-    }
-
-    public Boolean getObjective() {
-        return objective;
-    }
-
-    public void setObjective(Boolean objective) {
-        this.objective = objective;
-    }
-
-    public List<ThemisQuestion> getSubQuestions() {
-        return subQuestions;
-    }
-
-    public void setSubQuestions(List<ThemisQuestion> subQuestions) {
-        this.subQuestions = subQuestions;
-    }
-
-    public ThemisSections getBody() {
-        return body;
-    }
-
-    public void setBody(ThemisSections body) {
-        this.body = body;
-    }
-
-    public List<ThemisOption> getOptions() {
-        return options;
-    }
-
-    public void setOptions(List<ThemisOption> options) {
-        this.options = options;
-    }
-
-    public Map<String, Object> getParam() {
-        return param;
-    }
-
-    public void setParam(Map<String, Object> param) {
-        this.param = param;
-    }
-
-}
-
+package cn.com.qmth.examcloud.core.questions.service.themispaper;
+
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author chenken
+ * @date 2017年7月25日 上午9:51:37
+ * @company QMTH
+ * @description ComputerTestQuestion.java
+ */
+public class ThemisQuestion {
+
+    private String id;
+
+    /**
+     * 小题号或套题内子题序号
+     */
+    private Integer number;
+
+    private Double score;// 小题分数
+
+    /**
+     * 1-单选,2-多选,3-判断,4-填空,5-问答,6-套题
+     */
+    private Long structType;
+
+    // 1-单选,2-多选,3-判断,4-填空,5-问答
+    // 100-阅读理解,101-完形填空,102-段落匹配,103-选词填空
+    // 110-听力理解(主题干带音频的套题结构,子题题型不限,允许子题选择题无题干)
+    // 200-WPS操作题
+    private Long type;
+
+    /**
+     * 是否客观题
+     */
+    private Boolean objective;
+
+    /**
+     * 子题
+     */
+    private List<ThemisQuestion> subQuestions;
+
+    /**
+     * 题干
+     */
+    private ThemisSections body;
+
+    /**
+     * 选项
+     */
+    private List<ThemisOption> options;
+
+    private Map<String, Object> param;
+
+    public ThemisQuestion() {
+    }
+
+    public ThemisQuestion(PaperDetailUnit paperDetailUnit) {
+        this.id = paperDetailUnit.getId();
+        this.number = paperDetailUnit.getNumber();
+        this.score = paperDetailUnit.getScore();
+        this.structType = paperDetailUnit.getQuestionType().getId();
+        if (this.structType == 6) {
+            this.type = 100L;
+        } else {
+            this.type = this.structType;
+        }
+        if (paperDetailUnit.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                || paperDetailUnit.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+                || paperDetailUnit.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+            this.objective = true;
+        } else {
+            this.objective = false;
+        }
+    }
+
+    public ThemisQuestion(Question question) {
+        this.id = question.getId();
+        this.score = question.getScore();
+        this.structType = question.getQuestionType().getId();
+        if (this.structType == 6) {
+            this.type = 100L;
+        } else {
+            this.type = this.structType;
+        }
+        if (question.getQuestionType() == QuesStructType.SINGLE_ANSWER_QUESTION
+                || question.getQuestionType() == QuesStructType.MULTIPLE_ANSWER_QUESTION
+                || question.getQuestionType() == QuesStructType.BOOL_ANSWER_QUESTION) {
+            this.objective = true;
+        } else {
+            this.objective = false;
+        }
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public Long getStructType() {
+        return structType;
+    }
+
+    public void setStructType(Long structType) {
+        this.structType = structType;
+    }
+
+    public Boolean getObjective() {
+        return objective;
+    }
+
+    public void setObjective(Boolean objective) {
+        this.objective = objective;
+    }
+
+    public List<ThemisQuestion> getSubQuestions() {
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<ThemisQuestion> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+
+    public ThemisSections getBody() {
+        return body;
+    }
+
+    public void setBody(ThemisSections body) {
+        this.body = body;
+    }
+
+    public List<ThemisOption> getOptions() {
+        return options;
+    }
+
+    public void setOptions(List<ThemisOption> options) {
+        this.options = options;
+    }
+
+    public Map<String, Object> getParam() {
+        return param;
+    }
+
+    public void setParam(Map<String, Object> param) {
+        this.param = param;
+    }
+
+    public Long getType() {
+        return type;
+    }
+
+    public void setType(Long type) {
+        this.type = type;
+    }
+
+}