Prechádzať zdrojové kódy

新增wps解析题操作

wangliang 1 rok pred
rodič
commit
f126cd1ccb
21 zmenil súbory, kde vykonal 1372 pridanie a 39 odobranie
  1. 2 1
      server/jkserver.iml
  2. 9 0
      server/pom.xml
  3. 20 3
      server/src/main/java/com/qmth/jkserver/constant/SystemConstant.java
  4. 1 1
      server/src/main/java/com/qmth/jkserver/controller/api/OpenController.java
  5. 620 0
      server/src/main/java/com/qmth/jkserver/controller/api/WpsController.java
  6. 1 1
      server/src/main/java/com/qmth/jkserver/core/inteceptor/ApiInterceptor.java
  7. 1 1
      server/src/main/java/com/qmth/jkserver/core/inteceptor/SystemInterceptor.java
  8. 47 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerQuestionVo.java
  9. 35 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerSectionsVo.java
  10. 61 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerVo.java
  11. 46 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsAttachmentVo.java
  12. 46 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsBlocksVo.java
  13. 104 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsDetailVo.java
  14. 60 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsParamVo.java
  15. 125 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsQuestionVo.java
  16. 36 0
      server/src/main/java/com/qmth/jkserver/dto/wps/WpsSectionsVo.java
  17. 20 24
      server/src/main/java/com/qmth/jkserver/signature/Constants.java
  18. 3 2
      server/src/main/java/com/qmth/jkserver/util/Base64Util.java
  19. 34 4
      server/src/main/java/com/qmth/jkserver/util/HttpUtil.java
  20. 3 2
      server/src/main/java/com/qmth/jkserver/util/ShaUtils.java
  21. 98 0
      server/src/main/java/com/qmth/jkserver/util/WpsUtil.java

+ 2 - 1
server/jkserver.iml

@@ -153,7 +153,6 @@
     <orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
     <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.6.RELEASE" level="project" />
     <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.6.RELEASE" level="project" />
-    <orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.2.6.RELEASE" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.7.0" level="project" />
     <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.0.RELEASE" level="project" />
     <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.0.RELEASE" level="project" />
@@ -211,5 +210,7 @@
     <orderEntry type="library" name="Maven: com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.3" level="project" />
     <orderEntry type="library" name="Maven: com.github.ulisesbocchio:jasypt-spring-boot:3.0.3" level="project" />
     <orderEntry type="library" name="Maven: org.jasypt:jasypt:1.9.3" level="project" />
+    <orderEntry type="library" name="Maven: org.jsoup:jsoup:1.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-test:5.2.6.RELEASE" level="project" />
   </component>
 </module>

+ 9 - 0
server/pom.xml

@@ -218,6 +218,15 @@
             <artifactId>jasypt-spring-boot-starter</artifactId>
             <version>${jasypt.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.17.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
     </dependencies>
 
 	<!-- 构建 -->

+ 20 - 3
server/src/main/java/com/qmth/jkserver/constant/SystemConstant.java

@@ -80,6 +80,22 @@ public class SystemConstant {
     public static final String SYS_CONFIG_KEY_CHARSETS = "sys.txt.charset";
     public final static String SCHOOL_ID = "schoolId";
     public final static String VERSION = "3.4.0.1";
+    public static final int PAGE_NUMBER_MIN = 1;
+    public static final int PAGE_SIZE_MIN = 10;
+    public static final int PAGE_SIZE_MAX = 500;
+
+    /**
+     * wps
+     */
+    public static final String WPS_DOMAIN = "wps.domain";
+    public static final String WPS_ACCESS_KEY = "wps.access.key";
+    public static final String WPS_SECRET_KEY = "wps.secret.key";
+    public static final String CONTENT_TYPE = "application/json";
+    public static final String QUESTION_LIST = "/api/question/list";
+    public static final String QUESTION_DETAIL = "/api/question/detail";
+    public static final String WPS = "wps";
+    public static final String WPS_DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
+    public static final String WPS_QUESTION = "wps_question";
 
     /**
      * http设置
@@ -92,6 +108,7 @@ public class SystemConstant {
      */
     public static final String METHOD = "post";
     public static final String API_OPEN = "/api/open";
+    public static final String WPS_API_OPEN = "/api/wps";
     public static final String SYSTEM_EXAM_TASK = "/system/exam/task";
 
     /**
@@ -114,9 +131,9 @@ public class SystemConstant {
      */
     public static final String HEADER_AUTHORIZATION = "Authorization";
     public static final String HEADER_TIME = "time";
-    public static final String HEADER_PLATFORM = "platform";
-    public static final String HEADER_DEVICE_ID = "deviceId";
-    public static final String TOKEN = "token";
+//    public static final String HEADER_PLATFORM = "platform";
+//    public static final String HEADER_DEVICE_ID = "deviceId";
+//    public static final String TOKEN = "token";
     public static final String MD5 = "MD5";
     public static final Charset CHARSET = Charset.forName(CHARSET_NAME);
 

+ 1 - 1
server/src/main/java/com/qmth/jkserver/controller/api/OpenController.java

@@ -142,7 +142,7 @@ public class OpenController extends BaseSystemController {
         }
         response.setStatus(HttpStatus.HTTP_OK);
         response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
-        response.setContentType("application/json;charset=" + SystemConstant.CHARSET_NAME);
+        response.setContentType(SystemConstant.CONTENT_TYPE + ";charset=" + SystemConstant.CHARSET_NAME);
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("success", true);
         response.getWriter().print(jsonObject.toJSONString());

+ 620 - 0
server/src/main/java/com/qmth/jkserver/controller/api/WpsController.java

@@ -0,0 +1,620 @@
+package com.qmth.jkserver.controller.api;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.http.HttpStatus;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.ImmutableMap;
+import com.qmth.jkserver.common.UnZipUtils;
+import com.qmth.jkserver.constant.SystemConstant;
+import com.qmth.jkserver.controller.system.BaseSystemController;
+import com.qmth.jkserver.core.annotation.TokenValidate;
+import com.qmth.jkserver.core.exception.JkServerException;
+import com.qmth.jkserver.dto.wps.*;
+import com.qmth.jkserver.enums.TokenPolicy;
+import com.qmth.jkserver.service.CommonCacheService;
+import com.qmth.jkserver.util.FileStoreUtil;
+import com.qmth.jkserver.util.JacksonUtil;
+import com.qmth.jkserver.util.ResultUtil;
+import com.qmth.jkserver.util.WpsUtil;
+import io.swagger.annotations.*;
+import org.apache.commons.io.FileUtils;
+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.select.Elements;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 机考wps对接接口
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/7
+ */
+@Api(tags = "机考wps对接接口管理Controller")
+@RestController
+@RequestMapping("/" + SystemConstant.WPS_API_OPEN)
+public class WpsController extends BaseSystemController {
+    private static final Logger log = LoggerFactory.getLogger(WpsController.class);
+
+    @Resource
+    CommonCacheService commonCacheService;
+
+    @Resource
+    FileStoreUtil fileStoreUtil;
+
+    @ApiOperation(value = "题库列表接口")
+    @RequestMapping(value = "/question/list", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "题库列表接口信息", response = Object.class)})
+    @TokenValidate(policy = TokenPolicy.IGNORE)
+    public Object questionList(@ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                               @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) throws Exception {
+        String result = WpsUtil.exection(new JSONObject(ImmutableMap.of("pageNumber", pageNumber, "pageSize", pageSize)).toJSONString(), SystemConstant.QUESTION_LIST);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        Integer code = (Integer) map.get("code");
+        if (Objects.nonNull(code) && code.intValue() == HttpStatus.HTTP_OK) {
+            Map<String, Object> dataMap = JSONObject.toJavaObject((JSONObject) map.get("data"), Map.class);
+            Integer count = (Integer) dataMap.get("count");
+            if (Objects.nonNull(count) && count.intValue() > 0) {
+                JSONArray jsonArray = (JSONArray) dataMap.get("list");
+                this.delFile();
+                for (int i = 0; i < jsonArray.size(); i++) {
+                    this.reWriteJson(jsonArray.getJSONObject(i));
+                }
+            }
+        }
+        return ResultUtil.ok(result);
+    }
+
+    @ApiOperation(value = "题库明细接口")
+    @RequestMapping(value = "/question/detail", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "题库明细接口信息", response = Object.class)})
+    @TokenValidate(policy = TokenPolicy.IGNORE)
+    public Object questionDetail(@ApiParam(value = "题库id", required = true) @RequestParam String id) throws IOException, NoSuchAlgorithmException {
+        String result = WpsUtil.exection(new JSONObject(Collections.singletonMap("id", id)).toJSONString(), SystemConstant.QUESTION_DETAIL);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        Integer code = (Integer) map.get("code");
+        if (Objects.nonNull(code) && code.intValue() == HttpStatus.HTTP_OK) {
+            this.reWriteJson((JSONObject) map.get("data"));
+        }
+        return ResultUtil.ok(result);
+    }
+
+    @ApiOperation(value = "清除缓存接口")
+    @RequestMapping(value = "/delete/cache", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "清除缓存接口信息", response = Object.class)})
+    @TokenValidate(policy = TokenPolicy.IGNORE)
+    public Object deleteCache(@ApiParam(value = "缓存名称", required = true) @RequestParam String cacheName) throws IOException, NoSuchAlgorithmException {
+        commonCacheService.removeSysConfigCache(cacheName);
+        return ResultUtil.ok(true);
+    }
+
+    @ApiOperation(value = "读取启明试卷接口")
+    @RequestMapping(value = "/read/qm/paper", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "读取启明试卷接口信息", response = Object.class)})
+    @TokenValidate(policy = TokenPolicy.IGNORE)
+    public Object readQmPaper(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
+                              @ApiParam(value = "题库id", required = true) @RequestParam String id) throws IOException, NoSuchAlgorithmException {
+        File zipFile = null;
+        Map<String, Object> jsonMap = this.reWriteJson(null);
+        if (CollectionUtils.isEmpty(jsonMap)) {
+            throw new JkServerException("未找到wps_question.json文件");
+        }
+        JSONObject json = (JSONObject) jsonMap.get(id);
+        Boolean insert = (Boolean) json.get("insert");
+        if (Objects.nonNull(insert) && insert) {
+            return ResultUtil.ok(true);
+        }
+        File rootFile = null;
+        try {
+            String workRoot = null, path = null;
+            if (!fileStoreUtil.workIsOss()) {
+                workRoot = fileStoreUtil.getWorkRoot();
+                path = SystemConstant.WPS + File.separator + DateUtil.format(new Date(), SystemConstant.WPS_DEFAULT_DATE_PATTERN);
+                rootFile = new File(new File(workRoot), path + File.separator + SystemConstant.getNanoId() + SystemConstant.ZIP_PREFIX);
+                if (!rootFile.exists()) {
+                    rootFile.createNewFile();
+                }
+            }
+            IOUtils.copy(file.getInputStream(), new FileOutputStream(rootFile));
+            File zipChildFile = new File(new File(workRoot), path + File.separator + SystemConstant.getNanoId());
+            File zipRootFile = null;
+            List<File> fileList = UnZipUtils.doUnZip(rootFile, zipChildFile, rootFile.getPath());
+            File dirName = null, answerFile = null;
+            Integer mainNumber = null, subNumber = null;
+            for (File f : fileList) {
+                File temp = f.getParentFile();
+                if (Objects.isNull(zipRootFile)) {
+                    while (!Objects.equals(temp.getParent(), zipChildFile.getAbsolutePath())) {
+                        temp = temp.getParentFile();
+                        zipRootFile = temp;
+                    }
+                }
+                if (f.getParent().endsWith("attachment") && Objects.isNull(dirName)) {
+                    dirName = f.getParentFile();
+                } else if (f.getName().endsWith("paper.json")) {
+                    Map<String, Object> map = this.qmPaperReadAndUpdate(f, id, workRoot, path, dirName, jsonMap);
+                    if (!CollectionUtils.isEmpty(map)) {
+                        mainNumber = (Integer) map.get("mainNumber");
+                        subNumber = (Integer) map.get("subNumber");
+                    }
+                } else if (f.getName().endsWith("answer.json")) {
+                    answerFile = this.qmAnswerReadAndUpdate(f, id, jsonMap);
+                }
+            }
+            if (Objects.nonNull(mainNumber) && Objects.nonNull(subNumber) && Objects.nonNull(answerFile)) {
+                this.qmAnswerReloadAndUpdate(answerFile, mainNumber, subNumber);
+            }
+            zipFile = new File(zipChildFile.getAbsolutePath(), zipRootFile.getName() + SystemConstant.ZIP_PREFIX);
+            //重新打包
+            UnZipUtils.zipFile(zipChildFile.getAbsolutePath(), zipFile.getAbsolutePath(), null);
+            log.info("zipFile path:{}", zipFile.getAbsolutePath());
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (Objects.nonNull(rootFile)) {
+                rootFile.delete();
+            }
+        }
+        MultipartFile multipartFile = null;
+        if (Objects.nonNull(zipFile.getAbsolutePath())) {
+            for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
+                multipartFile = new MockMultipartFile("file", file.getName(), "application/zip", new FileInputStream(zipFile));
+                this.readQmPaper(multipartFile, entry.getKey());
+            }
+        }
+        return ResultUtil.ok(true);
+    }
+
+    /**
+     * 启明答案重新修改
+     *
+     * @param answerFile
+     * @param mainNumber
+     * @param subNumber
+     * @throws IOException
+     */
+    protected void qmAnswerReloadAndUpdate(File answerFile, Integer mainNumber, Integer subNumber) throws IOException {
+        byte[] bytes = IOUtils.toByteArray(new FileInputStream(answerFile));
+        JSONObject jsonObject = JSONObject.parseObject(new String(bytes, SystemConstant.CHARSET_NAME));
+        JSONArray jsonArray = jsonObject.getJSONArray("details");//明细
+        JSONArray delJsonArray = new JSONArray(), delJsonOldArray = new JSONArray();
+        WpsAnswerVo wpsAnswerVoNew = new WpsAnswerVo(), wpsAnswerVoOld = null;
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject jsonObject1 = jsonArray.getJSONObject(i);
+            String wpsId = jsonObject1.getString("id");
+            Integer number = jsonObject1.getInteger("number");
+            if (number.intValue() == mainNumber.intValue()) {
+                wpsAnswerVoOld = JSONObject.parseObject(JacksonUtil.parseJson(jsonObject1), WpsAnswerVo.class);
+                delJsonOldArray.add(jsonObject1);
+            }
+            if (Objects.nonNull(wpsId)) {
+                WpsAnswerVo wpsAnswerVo = JSONObject.parseObject(JacksonUtil.parseJson(jsonObject1), WpsAnswerVo.class);
+                wpsAnswerVo.setNumber(mainNumber);
+                wpsAnswerVo.getQuestions().get(0).setNumber(subNumber);
+                delJsonArray.add(jsonObject1);
+                BeanUtils.copyProperties(wpsAnswerVo, wpsAnswerVoNew);
+            }
+        }
+        jsonArray.removeAll(delJsonArray);
+        wpsAnswerVoNew.setId(null);
+        if (Objects.isNull(wpsAnswerVoOld)) {
+            jsonArray.add(JSONObject.parseObject(JacksonUtil.parseJson(wpsAnswerVoNew)));
+        } else {
+            jsonArray.removeAll(delJsonOldArray);
+            wpsAnswerVoOld.getQuestions().addAll(wpsAnswerVoNew.getQuestions());
+            jsonArray.add(JSONObject.parseObject(JacksonUtil.parseJson(wpsAnswerVoOld)));
+        }
+        IOUtils.write(JacksonUtil.parseJson(jsonObject).getBytes(SystemConstant.CHARSET_NAME), new FileOutputStream(answerFile));
+    }
+
+    /**
+     * 启明答案修改
+     *
+     * @param f
+     * @param id
+     * @param jsonMap
+     * @return
+     * @throws IOException
+     */
+    protected File qmAnswerReadAndUpdate(File f, String id, Map<String, Object> jsonMap) throws IOException {
+        File answerFile = null;
+        byte[] bytes = IOUtils.toByteArray(new FileInputStream(f));
+        JSONObject jsonObject = JSONObject.parseObject(new String(bytes, SystemConstant.CHARSET_NAME));
+        JSONArray jsonArray = jsonObject.getJSONArray("details");//明细
+
+        JSONObject json = (JSONObject) jsonMap.get(id);
+        JSONArray object = json.getJSONArray("answer");
+        for (int i = 0; i < object.size(); i++) {
+            JSONObject jsonObject1 = object.getJSONObject(i);
+            List<WpsBlocksVo> wpsBlocksVoList = JSONArray.parseArray(JacksonUtil.parseJson(jsonObject1.getJSONArray("blocks")), WpsBlocksVo.class);
+            WpsSectionsVo wpsSectionsVo = new WpsSectionsVo(wpsBlocksVoList);
+            WpsAnswerSectionsVo wpsAnswerSectionsVo = new WpsAnswerSectionsVo(Arrays.asList(wpsSectionsVo));
+            List<WpsAnswerQuestionVo> wpsAnswerQuestionVoList = new ArrayList<>();
+            wpsAnswerQuestionVoList.add(new WpsAnswerQuestionVo(1, Arrays.asList(wpsAnswerSectionsVo)));
+            WpsAnswerVo wpsAnswerVo = new WpsAnswerVo(id, 1, wpsAnswerQuestionVoList);
+            jsonArray.add(JSONObject.parseObject(JacksonUtil.parseJson(wpsAnswerVo)));
+        }
+        IOUtils.write(JacksonUtil.parseJson(jsonObject).getBytes(SystemConstant.CHARSET_NAME), new FileOutputStream(f));
+        answerFile = f;
+        return answerFile;
+    }
+
+    /**
+     * 启明试卷修改
+     *
+     * @param f
+     * @param id
+     * @param workRoot
+     * @param path
+     * @param dirName
+     * @param jsonMap
+     * @return
+     * @throws IOException
+     */
+    protected Map<String, Object> qmPaperReadAndUpdate(File f, String id, String workRoot, String path, File dirName,
+                                                       Map<String, Object> jsonMap) throws IOException {
+        Integer mainNumber = null, subNumber = null;
+        byte[] bytes = IOUtils.toByteArray(new FileInputStream(f));
+        JSONObject jsonObject = JSONObject.parseObject(new String(bytes, SystemConstant.CHARSET_NAME));
+        BigDecimal totalScore = jsonObject.getBigDecimal("totalScore");//全卷总分
+        Integer detailCount = jsonObject.getInteger("detailCount");//大题数量
+        JSONArray jsonArray = jsonObject.getJSONArray("details");//明细
+        JSONObject wpsJson = null;
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject json = jsonArray.getJSONObject(i);
+            Integer tempMainNumber = json.getInteger("number");
+            mainNumber = Objects.isNull(mainNumber) || mainNumber < tempMainNumber ? tempMainNumber : mainNumber;
+            JSONArray array = json.getJSONArray("questions");
+            for (int y = 0; y < array.size(); y++) {
+                JSONObject arrayJSONObject = array.getJSONObject(y);
+                Integer type = arrayJSONObject.getInteger("type");
+                if (Objects.nonNull(type) && type.intValue() == 200) {
+                    wpsJson = json;
+                    Integer tempSubNumber = arrayJSONObject.getInteger("number");
+                    subNumber = Objects.isNull(subNumber) || subNumber < tempSubNumber ? tempSubNumber : subNumber;
+                }
+            }
+        }
+
+        JSONObject json = (JSONObject) jsonMap.get(id);
+        if (Objects.isNull(wpsJson)) {
+            subNumber = 1;
+            totalScore = this.writeQmPaperStruct(json, id, ++mainNumber, subNumber, workRoot, path, dirName, jsonArray, totalScore, wpsJson);
+            detailCount++;
+        } else {
+            totalScore = this.writeQmPaperStruct(json, id, mainNumber, ++subNumber, workRoot, path, dirName, jsonArray, totalScore, wpsJson);
+        }
+        jsonObject.put("totalScore", totalScore);
+        jsonObject.put("detailCount", detailCount);
+        IOUtils.write(JacksonUtil.parseJson(jsonObject).getBytes(SystemConstant.CHARSET_NAME), new FileOutputStream(f));
+        json.put("insert", true);
+        jsonMap.put(id, json);
+        IOUtils.write(JacksonUtil.parseJson(jsonMap).getBytes(SystemConstant.CHARSET_NAME), new FileOutputStream(this.getWpsQuestionJson()));
+        return ImmutableMap.of("mainNumber", mainNumber, "subNumber", subNumber);
+    }
+
+    /**
+     * 写入qm wps试卷结构
+     *
+     * @param json
+     * @param id
+     * @param mainNumber
+     * @param subNumber
+     * @param workRoot
+     * @param path
+     * @param dirName
+     * @param jsonArray
+     * @param totalScore
+     * @param wpsJson
+     * @return
+     * @throws IOException
+     */
+    protected BigDecimal writeQmPaperStruct(JSONObject json, String id,
+                                            Integer mainNumber, Integer subNumber,
+                                            String workRoot, String path, File dirName,
+                                            JSONArray jsonArray, BigDecimal totalScore, JSONObject wpsJson) throws IOException {
+        BigDecimal score = json.getBigDecimal("score");
+        List<WpsDetailVo> wpsDetailVoList = new ArrayList<>();
+        List<WpsQuestionVo> wpsQuestionVoList = new ArrayList<>();
+        WpsDetailVo wpsDetailVo = null;
+        WpsQuestionVo wpsQuestionVo = new WpsQuestionVo(id, subNumber, score, 200);
+        if (subNumber.intValue() == 1) {
+            wpsDetailVo = new WpsDetailVo(mainNumber, "WPS操作题", null, score, 1);
+        } else {
+            wpsDetailVo = JSONObject.parseObject(JacksonUtil.parseJson(wpsJson), WpsDetailVo.class);
+            wpsDetailVo.setQuestionCount(wpsDetailVo.getQuestionCount() + 1);
+            wpsDetailVo.setTotalScore(wpsDetailVo.getTotalScore().add(score));
+            wpsQuestionVoList = wpsDetailVo.getQuestions();
+            jsonArray.remove(wpsJson);
+        }
+        wpsDetailVoList.add(wpsDetailVo);
+        wpsQuestionVoList.add(wpsQuestionVo);
+
+        JSONObject object = json.getJSONObject("body");
+        JSONArray sections = object.getJSONArray("sections");
+        WpsParamVo wpsParamVo = null;
+        for (int i = 0; i < sections.size(); i++) {
+            JSONObject obj = sections.getJSONObject(i);
+            JSONArray array = obj.getJSONArray("blocks");
+            if (array.size() > 0) {
+                JSONArray delArray = new JSONArray();
+                List<WpsAttachmentVo> wpsOperationFile = new ArrayList<>();
+                for (int j = 0; j < array.size(); j++) {
+                    JSONObject arrayJSONObject = array.getJSONObject(j);
+                    String type = arrayJSONObject.getString("type");
+                    String value = arrayJSONObject.getString("value");
+                    if (Objects.nonNull(type) && Objects.equals(type.trim(), "file")) {
+                        wpsParamVo = JSONObject.parseObject(value, WpsParamVo.class);
+                        List<WpsAttachmentVo> attachmentVoList = wpsParamVo.getWpsOperationFile();
+                        for (WpsAttachmentVo w : attachmentVoList) {
+                            File sourceFile = new File(new File(workRoot), path + File.separator + w.getValue());
+                            File destFile = new File(dirName, w.getValue());
+                            IOUtils.copy(new FileInputStream(sourceFile), new FileOutputStream(destFile));
+                            wpsOperationFile.add(w);
+                        }
+                        delArray.add(arrayJSONObject);
+                    }
+                }
+                array.removeAll(delArray);
+                if (Objects.nonNull(wpsParamVo)) {
+                    wpsParamVo.getWpsOperationFile().clear();
+                    wpsParamVo.setWpsOperationFile(wpsOperationFile);
+                }
+            }
+        }
+        wpsQuestionVo.setBody(object);
+        wpsQuestionVo.setParam(wpsParamVo);
+        wpsDetailVo.setQuestions(wpsQuestionVoList);
+        JSONObject newJson = JSONObject.parseObject(JacksonUtil.parseJson(wpsDetailVo));
+        jsonArray.add(newJson);
+        totalScore = totalScore.add(score);
+        return totalScore;
+    }
+
+    /**
+     * 删除文件
+     *
+     * @throws Exception
+     */
+    protected void delFile() throws Exception {
+        File rootFile = this.createWpsRootPath(null);
+        if (rootFile.exists()) {
+            SystemConstant.deleteFolder(rootFile);
+        }
+    }
+
+    /**
+     * 重写json结构
+     *
+     * @param jsonObject
+     * @return
+     * @throws IOException
+     */
+    protected Map<String, Object> reWriteJson(JSONObject jsonObject) throws IOException {
+        Map<String, Object> jsonMap = null;
+        File rootFile = this.createWpsRootPath(null);
+        String dirName = SystemConstant.WPS_QUESTION + SystemConstant.JSON_PREFIX;
+        File file = new File(rootFile, dirName);
+        jsonMap = new LinkedHashMap<>();
+        if (!file.exists()) {
+            file.createNewFile();
+        } else {
+            byte[] bytes = IOUtils.toByteArray(new FileInputStream(file));
+            jsonMap = JSONArray.parseObject(new String(bytes, SystemConstant.CHARSET_NAME), Map.class);
+        }
+        if (Objects.nonNull(jsonObject)) {
+            this.writeJson(jsonObject, jsonMap);
+            IOUtils.write(JacksonUtil.parseJson(jsonMap).getBytes(SystemConstant.CHARSET_NAME), new FileOutputStream(file));
+        }
+        return jsonMap;
+    }
+
+    /**
+     * 写入json信息
+     *
+     * @param jsonObject
+     * @param jsonMap
+     * @return
+     */
+    protected Map<String, Object> writeJson(JSONObject jsonObject, Map<String, Object> jsonMap) throws IOException {
+        Integer structType = jsonObject.getInteger("structType");
+        if (Objects.nonNull(structType) && structType.intValue() == 7) {
+            JSONObject json = new JSONObject();
+            String id = jsonObject.getString("id");
+            BigDecimal score = jsonObject.getBigDecimal("score");
+            json.put("score", score);
+            json.put("questionCode", jsonObject.get("questionCode"));
+            json.put("level", jsonObject.get("level"));
+            json.put("structType", structType);
+            json.put("questionType", jsonObject.get("questionType"));
+            json.put("type", jsonObject.get("type"));
+            json.put("status", jsonObject.get("status"));
+
+            //解析body
+            JSONObject object = jsonObject.getJSONObject("body");
+            JSONArray jsonArray = object.getJSONArray("sections");
+            for (int i = 0; i < jsonArray.size(); i++) {
+                JSONObject obj = jsonArray.getJSONObject(i);
+                JSONArray array = obj.getJSONArray("blocks");
+                List<WpsSectionsVo> wpsSectionsVoList = new ArrayList<>();
+                List<WpsBlocksVo> wpsBlocksVoList = new ArrayList<>();
+                for (int j = 0; j < array.size(); j++) {
+                    JSONObject arrayJSONObject = array.getJSONObject(j);
+                    String type = arrayJSONObject.getString("type");
+                    String value = arrayJSONObject.getString("value");
+                    if (Objects.nonNull(type) && Objects.equals(type.trim(), "text")) {
+                        Set<String> set = this.replaceHtmlTag(value, "p");
+                        if (CollectionUtils.isEmpty(set)) {
+                            set.addAll(this.replaceHtmlTag(value, "div"));
+                        }
+                        if (CollectionUtils.isEmpty(set)) {
+                            set.addAll(this.replaceHtmlTag(value, "span"));
+                        }
+                        if (!CollectionUtils.isEmpty(set)) {
+                            set.stream().peek(s -> wpsBlocksVoList.add(new WpsBlocksVo("text", s))).collect(Collectors.toSet());
+                        } else {
+                            wpsBlocksVoList.add(new WpsBlocksVo("text", value));
+                        }
+                    } else if (Objects.nonNull(type) && Objects.equals(type.trim(), "file")) {
+                        File rootFile = this.createWpsRootPath(DateUtil.format(new Date(), SystemConstant.WPS_DEFAULT_DATE_PATTERN));
+                        String fileName = arrayJSONObject.getString("fileName");
+                        String[] strs = StringUtils.split(fileName, ".");
+                        File tempFile = new File(rootFile, SystemConstant.getNanoId() + "." + strs[1]);
+                        FileUtils.copyURLToFile(new URL(value), tempFile);
+                        List<WpsAttachmentVo> wpsOperationFile = new ArrayList<>();
+                        wpsOperationFile.add(new WpsAttachmentVo(fileName, tempFile.getName()));
+                        WpsParamVo wpsParamVo = new WpsParamVo(id, score, wpsOperationFile);
+                        arrayJSONObject.put("wpsValue", value);
+                        arrayJSONObject.put("value", wpsParamVo);
+                        wpsBlocksVoList.add(new WpsBlocksVo("file", JacksonUtil.parseJson(wpsParamVo)));
+                    }
+                }
+                wpsSectionsVoList.add(new WpsSectionsVo(wpsBlocksVoList));
+                object.put("sections", JSONArray.parseArray(JacksonUtil.parseJson(wpsSectionsVoList)));
+            }
+            //解析operationDesc
+            String operationDesc = jsonObject.getString("operationDesc");
+            Set<String> set = this.replaceHtmlTag(operationDesc, "p");
+            if (CollectionUtils.isEmpty(set)) {
+                set.addAll(this.replaceHtmlTag(operationDesc, "div"));
+            }
+            if (CollectionUtils.isEmpty(set)) {
+                set.addAll(this.replaceHtmlTag(operationDesc, "span"));
+            }
+            List<WpsBlocksVo> wpsBlocksVoList = new ArrayList<>();
+            List<WpsSectionsVo> wpsSectionsVoList = new ArrayList<>();
+            wpsSectionsVoList.add(new WpsSectionsVo(wpsBlocksVoList));
+            if (!CollectionUtils.isEmpty(set)) {
+                set.stream().peek(s -> wpsBlocksVoList.add(new WpsBlocksVo("text", s))).collect(Collectors.toSet());
+            } else {
+                wpsBlocksVoList.add(new WpsBlocksVo("text", operationDesc));
+            }
+            json.put("answer", JSONArray.parseArray(JacksonUtil.parseJson(wpsSectionsVoList)));
+            json.put("body", object);
+            json.put("answers", jsonObject.get("answers"));
+            json.put("rightAnswers", jsonObject.get("rightAnswers"));
+            json.put("outlineNames", jsonObject.get("outlineNames"));
+            json.put("chapterNames", jsonObject.get("chapterNames"));
+            json.put("pointNames", jsonObject.get("pointNames"));
+            json.put("options", jsonObject.get("options"));
+            json.put("operationDesc", operationDesc);
+            jsonMap.put(id, json);
+        }
+        return jsonMap;
+    }
+
+//    /**
+//     * 过滤html和style
+//     *
+//     * @param content
+//     * @return
+//     */
+//    protected String replaceHtmlTag(String content) {
+//        String regExStyle = "<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式
+//        String regExHtml = "<[^>]+>"; //定义HTML标签的正则表达式
+//        Pattern pStyle = Pattern.compile(regExStyle, Pattern.CASE_INSENSITIVE);
+//        Matcher mStyle = pStyle.matcher(content);
+//        content = mStyle.replaceAll(""); //过滤style标签
+//        Pattern pHtml = Pattern.compile(regExHtml, Pattern.CASE_INSENSITIVE);
+//        Matcher mHtml = pHtml.matcher(content);
+//        content = mHtml.replaceAll(""); //过滤html标签
+//        content = content.replace(" ", "");
+//        content = content.replaceAll("\\s*|\t|\r|\n", "");
+////        content = content.replace("“", "");
+////        content = content.replace("”", "");
+//        content = content.replaceAll(" ", "");
+//        return content.trim(); //返回文本字符串
+//    }
+
+    /**
+     * 获取wps_question.json
+     *
+     * @return
+     */
+    protected File getWpsQuestionJson() {
+        File rootFile = this.createWpsRootPath(null);
+        String dirName = SystemConstant.WPS_QUESTION + SystemConstant.JSON_PREFIX;
+        return new File(rootFile, dirName);
+    }
+
+    /**
+     * 创建wps根目录
+     *
+     * @param date
+     * @return
+     */
+    protected File createWpsRootPath(String date) {
+        File rootFile = null;
+        if (!fileStoreUtil.workIsOss()) {
+            String workRoot = fileStoreUtil.getWorkRoot();
+            String path = Objects.nonNull(date) ? SystemConstant.WPS + File.separator + date : SystemConstant.WPS;
+            rootFile = new File(new File(workRoot), path);
+            if (!rootFile.exists()) {
+                rootFile.mkdirs();
+            }
+        }
+        return rootFile;
+    }
+
+    /**
+     * 替换html标签
+     *
+     * @param content
+     * @param cssTag
+     * @return
+     */
+    protected Set<String> replaceHtmlTag(String content, String cssTag) {
+        Document doc = Jsoup.parse(content);
+        Elements elements = doc.select(cssTag);
+        Set<String> set = new LinkedHashSet<>(elements.size());
+        for (Element e : elements) {
+            if (this.containsChinese(e.text())) {
+                set.add(e.text());
+            }
+        }
+        return set;
+    }
+
+    /**
+     * 是否包含中文
+     *
+     * @param content
+     * @return
+     */
+    protected boolean containsChinese(String content) {
+        // 使用正则表达式匹配中文字符
+        String pattern = "[\u4e00-\u9fa5]";
+        Pattern p = Pattern.compile(pattern);
+        Matcher m = p.matcher(content);
+        // 判断是否包含中文字符
+        return m.find();
+    }
+}

+ 1 - 1
server/src/main/java/com/qmth/jkserver/core/inteceptor/ApiInterceptor.java

@@ -104,7 +104,7 @@ public class ApiInterceptor extends HandlerInterceptorAdapter {
             logger.debug("响应信息:" + returnResult);
             response.setStatus(200);
             response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
-            response.setContentType("application/json;charset=" + SystemConstant.CHARSET_NAME);
+            response.setContentType(SystemConstant.CONTENT_TYPE + ";charset=" + SystemConstant.CHARSET_NAME);
             response.getWriter().write(returnResult);
         } catch (Exception e) {
         }

+ 1 - 1
server/src/main/java/com/qmth/jkserver/core/inteceptor/SystemInterceptor.java

@@ -95,7 +95,7 @@ public class SystemInterceptor extends HandlerInterceptorAdapter {
             ObjectMapper mapper = new ObjectMapper();
             response.setStatus(200);
             response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
-            response.setContentType("application/json;charset=" + SystemConstant.CHARSET_NAME);
+            response.setContentType(SystemConstant.CONTENT_TYPE + ";charset=" + SystemConstant.CHARSET_NAME);
             response.getWriter().write(mapper.writeValueAsString(result));
         } catch (Exception e) {
         }

+ 47 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerQuestionVo.java

@@ -0,0 +1,47 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description: wps答案题目vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsAnswerQuestionVo implements Serializable {
+
+    @ApiModelProperty(value = "大题号")
+    Integer number;
+
+    @ApiModelProperty(value = "答案集合")
+    List<WpsAnswerSectionsVo> answer;
+
+    public WpsAnswerQuestionVo() {
+
+    }
+
+    public WpsAnswerQuestionVo(Integer number, List<WpsAnswerSectionsVo> answer) {
+        this.number = number;
+        this.answer = answer;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public List<WpsAnswerSectionsVo> getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(List<WpsAnswerSectionsVo> answer) {
+        this.answer = answer;
+    }
+}

+ 35 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerSectionsVo.java

@@ -0,0 +1,35 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description: wps答案section vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsAnswerSectionsVo implements Serializable {
+
+    @ApiModelProperty(value = "sections集合")
+    List<WpsSectionsVo> sections;
+
+    public WpsAnswerSectionsVo() {
+
+    }
+
+    public WpsAnswerSectionsVo(List<WpsSectionsVo> sections) {
+        this.sections = sections;
+    }
+
+    public List<WpsSectionsVo> getSections() {
+        return sections;
+    }
+
+    public void setSections(List<WpsSectionsVo> sections) {
+        this.sections = sections;
+    }
+}

+ 61 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsAnswerVo.java

@@ -0,0 +1,61 @@
+package com.qmth.jkserver.dto.wps;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description: wps答案vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+@JsonInclude(value = JsonInclude.Include.NON_NULL)
+public class WpsAnswerVo implements Serializable {
+
+    @ApiModelProperty(value = "wps题号id")
+    String id;
+
+    @ApiModelProperty(value = "小题号")
+    Integer number;
+
+    @ApiModelProperty(value = "题目集合")
+    List<WpsAnswerQuestionVo> questions;
+
+    public WpsAnswerVo() {
+
+    }
+
+    public WpsAnswerVo(String id, Integer number, List<WpsAnswerQuestionVo> questions) {
+        this.id = id;
+        this.number = number;
+        this.questions = questions;
+    }
+
+    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 List<WpsAnswerQuestionVo> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<WpsAnswerQuestionVo> questions) {
+        this.questions = questions;
+    }
+}

+ 46 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsAttachmentVo.java

@@ -0,0 +1,46 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * @Description: wps附件vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsAttachmentVo implements Serializable {
+
+    @ApiModelProperty(value = "wps题库文件名")
+    String name;
+
+    @ApiModelProperty(value = "wps题库文件存储名")
+    String value;
+
+    public WpsAttachmentVo() {
+
+    }
+
+    public WpsAttachmentVo(String name, String value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}

+ 46 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsBlocksVo.java

@@ -0,0 +1,46 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * @Description: wps文本块vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsBlocksVo implements Serializable {
+
+    @ApiModelProperty(value = "wps类型")
+    String type;
+
+    @ApiModelProperty(value = "wps值")
+    String value;
+
+    public WpsBlocksVo() {
+
+    }
+
+    public WpsBlocksVo(String type, String value) {
+        this.type = type;
+        this.value = value;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}

+ 104 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsDetailVo.java

@@ -0,0 +1,104 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @Description: wps题目详情vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsDetailVo implements Serializable {
+
+    @ApiModelProperty(value = "大题序号")
+    Integer number;
+
+    @ApiModelProperty(value = "大题名称")
+    String name;
+
+    @ApiModelProperty(value = "大题说明")
+    String description;
+
+    @ApiModelProperty(value = "大题总分")
+    BigDecimal totalScore;
+
+    @ApiModelProperty(value = "小题数量")
+    Integer questionCount;
+
+    @ApiModelProperty(value = "小题集合")
+    List<WpsQuestionVo> questions;
+
+    public WpsDetailVo() {
+
+    }
+
+    public WpsDetailVo(Integer number, String name, String description, BigDecimal totalScore, Integer questionCount) {
+        this.number = number;
+        this.name = name;
+        this.description = description;
+        this.totalScore = totalScore;
+        this.questionCount = questionCount;
+    }
+
+    public WpsDetailVo(Integer number, String name, String description, BigDecimal totalScore, Integer questionCount, List<WpsQuestionVo> questions) {
+        this.number = number;
+        this.name = name;
+        this.description = description;
+        this.totalScore = totalScore;
+        this.questionCount = questionCount;
+        this.questions = questions;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public BigDecimal getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(BigDecimal totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getQuestionCount() {
+        return questionCount;
+    }
+
+    public void setQuestionCount(Integer questionCount) {
+        this.questionCount = questionCount;
+    }
+
+    public List<WpsQuestionVo> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<WpsQuestionVo> questions) {
+        this.questions = questions;
+    }
+}

+ 60 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsParamVo.java

@@ -0,0 +1,60 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @Description: wps参数vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsParamVo implements Serializable {
+
+    @ApiModelProperty(value = "wps题库id")
+    String wpsQuestionId;
+
+    @ApiModelProperty(value = "wps题库原始分数")
+    BigDecimal wpsQuestionScore;
+
+    @ApiModelProperty(value = "wps题库原始文件")
+    List<WpsAttachmentVo> wpsOperationFile;
+
+    public WpsParamVo() {
+
+    }
+
+    public WpsParamVo(String wpsQuestionId, BigDecimal wpsQuestionScore, List<WpsAttachmentVo> wpsOperationFile) {
+        this.wpsQuestionId = wpsQuestionId;
+        this.wpsQuestionScore = wpsQuestionScore;
+        this.wpsOperationFile = wpsOperationFile;
+    }
+
+    public String getWpsQuestionId() {
+        return wpsQuestionId;
+    }
+
+    public void setWpsQuestionId(String wpsQuestionId) {
+        this.wpsQuestionId = wpsQuestionId;
+    }
+
+    public BigDecimal getWpsQuestionScore() {
+        return wpsQuestionScore;
+    }
+
+    public void setWpsQuestionScore(BigDecimal wpsQuestionScore) {
+        this.wpsQuestionScore = wpsQuestionScore;
+    }
+
+    public List<WpsAttachmentVo> getWpsOperationFile() {
+        return wpsOperationFile;
+    }
+
+    public void setWpsOperationFile(List<WpsAttachmentVo> wpsOperationFile) {
+        this.wpsOperationFile = wpsOperationFile;
+    }
+}

+ 125 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsQuestionVo.java

@@ -0,0 +1,125 @@
+package com.qmth.jkserver.dto.wps;
+
+import com.alibaba.fastjson.JSONObject;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @Description: wps题目vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsQuestionVo implements Serializable {
+
+    @ApiModelProperty(value = "小题id")
+    String id;
+
+    @ApiModelProperty(value = "小题序号")
+    Integer number;
+
+    @ApiModelProperty(value = "小题满分")
+    BigDecimal score;
+
+    @ApiModelProperty(value = "结构类型")
+    Integer structType;
+
+    @ApiModelProperty(value = "新的题型编码")
+    Integer type;
+
+    @ApiModelProperty(value = "是否客观题")
+    boolean objective;
+
+    @ApiModelProperty(value = "小题题干")
+    JSONObject body;
+
+    @ApiModelProperty(value = "试题扩展属性")
+    WpsParamVo param;
+
+    public WpsQuestionVo() {
+
+    }
+
+    public WpsQuestionVo(String id, Integer number, BigDecimal score, Integer type) {
+        this.id = id;
+        this.number = number;
+        this.score = score;
+        this.type = type;
+    }
+
+    public WpsQuestionVo(String id, Integer number, BigDecimal score, Integer type, JSONObject body, WpsParamVo param) {
+        this.id = id;
+        this.number = number;
+        this.score = score;
+        this.type = type;
+        this.body = body;
+        this.param = param;
+    }
+
+    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 BigDecimal getScore() {
+        return score;
+    }
+
+    public void setScore(BigDecimal score) {
+        this.score = score;
+    }
+
+    public Integer getStructType() {
+        return structType;
+    }
+
+    public void setStructType(Integer structType) {
+        this.structType = structType;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public boolean isObjective() {
+        return objective;
+    }
+
+    public void setObjective(boolean objective) {
+        this.objective = objective;
+    }
+
+    public JSONObject getBody() {
+        return body;
+    }
+
+    public void setBody(JSONObject body) {
+        this.body = body;
+    }
+
+    public WpsParamVo getParam() {
+        return param;
+    }
+
+    public void setParam(WpsParamVo param) {
+        this.param = param;
+    }
+}

+ 36 - 0
server/src/main/java/com/qmth/jkserver/dto/wps/WpsSectionsVo.java

@@ -0,0 +1,36 @@
+package com.qmth.jkserver.dto.wps;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description: wps sections vo
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2024/5/8
+ */
+public class WpsSectionsVo implements Serializable {
+
+    @ApiModelProperty(value = "wps文本块集合")
+    List<WpsBlocksVo> blocks;
+
+    public WpsSectionsVo() {
+
+    }
+
+    public WpsSectionsVo(List<WpsBlocksVo> blocks) {
+        this.blocks = blocks;
+    }
+
+
+    public List<WpsBlocksVo> getBlocks() {
+        return blocks;
+    }
+
+    public void setBlocks(List<WpsBlocksVo> blocks) {
+        this.blocks = blocks;
+    }
+}

+ 20 - 24
server/src/main/java/com/qmth/jkserver/signature/Constants.java

@@ -11,39 +11,35 @@ import java.nio.charset.Charset;
 */ 
 public interface Constants {
 
-    public static final int SIGNATURE_EXPIRE_SECONDS = 15;
-
-    public static final int SIGNATURE_AHEAD_SECONDS = 5;
-
-    public static final int VERIFY_TOKEN_EXPIRE_SECONDS = 30;
-
-    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
-
-    public static final String CHARSET_NAME = "UTF-8";
-
-    public static final Charset CHARSET = Charset.forName(CHARSET_NAME);
+//    public static final int SIGNATURE_EXPIRE_SECONDS = 15;
+//
+//    public static final int SIGNATURE_AHEAD_SECONDS = 5;
+//
+//    public static final int VERIFY_TOKEN_EXPIRE_SECONDS = 30;
+//
+//    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
 
     public static final String HEADER_AUTHORIZATION = "Authorization";
 
     public static final String HEADER_TIME = "time";
 
-    public static final String HEADER_PLATFORM = "platform";
-
-    public static final String HEADER_DEVICE_ID = "deviceId";
-
-    public static final String HEADER_RECORD_ID = "recordId";
-
-    public static final String HEADER_SOURCE = "source";
-
-    public static final String HEADER_USER_ID = "userId";
+//    public static final String HEADER_PLATFORM = "platform";
+//
+//    public static final String HEADER_DEVICE_ID = "deviceId";
+//
+//    public static final String HEADER_RECORD_ID = "recordId";
+//
+//    public static final String HEADER_SOURCE = "source";
+//
+//    public static final String HEADER_USER_ID = "userId";
 
     /**
      * aes相关
      */
-    public static final String AES = "AES";
-    public static final String AES_MODE_PKCS5 = "AES/CBC/PKCS5Padding";//用这个模式,规则必须为16位
-    public static final String AES_MODE_PKCS7 = "AES/CBC/PKCS7Padding";//用这个模式,规则必须为16位
-    public static final String AES_RULE = "1234567890123456";//aes密钥
+//    public static final String AES = "AES";
+//    public static final String AES_MODE_PKCS5 = "AES/CBC/PKCS5Padding";//用这个模式,规则必须为16位
+//    public static final String AES_MODE_PKCS7 = "AES/CBC/PKCS7Padding";//用这个模式,规则必须为16位
+//    public static final String AES_RULE = "1234567890123456";//aes密钥
 
     public static final String MD5 = "MD5";
 

+ 3 - 2
server/src/main/java/com/qmth/jkserver/util/Base64Util.java

@@ -1,5 +1,6 @@
 package com.qmth.jkserver.util;
 
+import com.qmth.jkserver.constant.SystemConstant;
 import com.qmth.jkserver.signature.Constants;
 
 import java.util.Base64;
@@ -14,10 +15,10 @@ import java.util.Base64;
 public class Base64Util implements Constants {
 
     public static String encode(byte[] input) {
-        return new String(Base64.getEncoder().encode(input), CHARSET);
+        return new String(Base64.getEncoder().encode(input), SystemConstant.CHARSET);
     }
 
     public static byte[] decode(String input) {
-        return Base64.getDecoder().decode(input.getBytes(CHARSET));
+        return Base64.getDecoder().decode(input.getBytes(SystemConstant.CHARSET));
     }
 }

+ 34 - 4
server/src/main/java/com/qmth/jkserver/util/HttpUtil.java

@@ -255,19 +255,49 @@ public class HttpUtil {
                 .setConnectionRequestTimeout(SystemConstant.CONNECT_TIME_OUT)// 请求超时时间
                 .setSocketTimeout(SystemConstant.SOCKET_CONNECT_TIME_OUT)// 数据读取超时时间
                 .build();
-
         // 构建post请求
         HttpPost post = new HttpPost(url);
         post.setConfig(requestConfig);
         post.setHeader(SystemConstant.HEADER_AUTHORIZATION, secret);
         post.setHeader(SystemConstant.HEADER_TIME, String.valueOf(timestamp));
-        post.setHeader(HTTP.CONTENT_TYPE, "application/json; charset=" + SystemConstant.CHARSET_NAME);
-        post.setHeader("Accept", "application/json");
+        post.setHeader(HTTP.CONTENT_TYPE, SystemConstant.CONTENT_TYPE + "; charset=" + SystemConstant.CHARSET_NAME);
+        post.setHeader("Accept", SystemConstant.CONTENT_TYPE);
 
         String encoderJson = URLEncoder.encode(json, SystemConstant.CHARSET_NAME);
         StringEntity se = new StringEntity(encoderJson);
         se.setContentType("text/json");
-        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
+        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, SystemConstant.CONTENT_TYPE));
+        post.setEntity(se);
+        // 执行请求,获取响应
+        return getRespString(post);
+    }
+
+    /**
+     * post json
+     *
+     * @param url
+     * @param json
+     * @param headers
+     * @return
+     * @throws IOException
+     */
+    public static String postJson(String url, String json, Map<String, String> headers) throws IOException {
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(SystemConstant.CONNECT_TIME_OUT)// 连接主机服务超时时间
+                .setConnectionRequestTimeout(SystemConstant.CONNECT_TIME_OUT)// 请求超时时间
+                .setSocketTimeout(SystemConstant.SOCKET_CONNECT_TIME_OUT)// 数据读取超时时间
+                .build();
+        // 构建post请求
+        HttpPost post = new HttpPost(url);
+        post.setConfig(requestConfig);
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            post.setHeader(entry.getKey(), entry.getValue());
+        }
+
+//        String encoderJson = URLEncoder.encode(json, SystemConstant.CHARSET_NAME);
+//        StringEntity se = new StringEntity(encoderJson);
+        StringEntity se = new StringEntity(json);
+        se.setContentType("text/json");
+        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, SystemConstant.CONTENT_TYPE));
         post.setEntity(se);
         // 执行请求,获取响应
         return getRespString(post);

+ 3 - 2
server/src/main/java/com/qmth/jkserver/util/ShaUtils.java

@@ -1,5 +1,6 @@
 package com.qmth.jkserver.util;
 
+import com.qmth.jkserver.constant.SystemConstant;
 import com.qmth.jkserver.signature.Constants;
 
 import java.security.MessageDigest;
@@ -15,7 +16,7 @@ public class ShaUtils implements Constants {
 
     public static byte[] sha1(String input) {
         try {
-            return MessageDigest.getInstance("SHA1").digest(input.getBytes(CHARSET));
+            return MessageDigest.getInstance("SHA1").digest(input.getBytes(SystemConstant.CHARSET));
         } catch (Exception e) {
             return null;
         }
@@ -23,7 +24,7 @@ public class ShaUtils implements Constants {
 
     public static byte[] sha256(String input) {
         try {
-            return MessageDigest.getInstance("SHA256").digest(input.getBytes(CHARSET));
+            return MessageDigest.getInstance("SHA256").digest(input.getBytes(SystemConstant.CHARSET));
         } catch (Exception e) {
             return null;
         }

+ 98 - 0
server/src/main/java/com/qmth/jkserver/util/WpsUtil.java

@@ -0,0 +1,98 @@
+package com.qmth.jkserver.util;
+
+import com.qmth.jkserver.constant.SpringContextHolder;
+import com.qmth.jkserver.constant.SystemConstant;
+import com.qmth.jkserver.model.SysConfig;
+import com.qmth.jkserver.service.CommonCacheService;
+import org.apache.http.protocol.HTTP;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class WpsUtil {
+    private static final Logger log = LoggerFactory.getLogger(WpsUtil.class);
+
+    private static String getGMTDateString() {
+        SimpleDateFormat format = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss ", Locale.US);
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        return format.format(new Date()) + " GMT";
+    }
+
+    private static String sha1(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return null;
+        }
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-1");
+            digest.update(str.getBytes(StandardCharsets.UTF_8));
+            byte[] messageDigest = digest.digest();
+            return bytesToString(messageDigest);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private static String bytesToString(byte[] bytes) {
+        StringBuilder hexString = new StringBuilder();
+        byte[] var2 = bytes;
+        int var3 = bytes.length;
+
+        for (int var4 = 0; var4 < var3; ++var4) {
+            byte aByte = var2[var4];
+            String shaHex = Integer.toHexString(255 & aByte);
+            if (shaHex.length() == 1) {
+                hexString.append(0);
+            }
+            hexString.append(shaHex);
+        }
+        return hexString.toString();
+    }
+
+    /**
+     * 执行数据方法
+     *
+     * @param content
+     * @param api
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static String exection(String content, String api) throws NoSuchAlgorithmException, IOException {
+        Objects.requireNonNull(content, "内容不能为空");
+        CommonCacheService commonCacheService = SpringContextHolder.getBean(CommonCacheService.class);
+
+        SysConfig sysConfigWpsDomain = commonCacheService.addSysConfigCache(SystemConstant.WPS_DOMAIN);
+        Objects.requireNonNull(sysConfigWpsDomain, "wps域名不能为空");
+        Objects.requireNonNull(sysConfigWpsDomain.getConfigValue(), "wps域名值不能为空");
+
+        SysConfig sysConfigWpsAccessKey = commonCacheService.addSysConfigCache(SystemConstant.WPS_ACCESS_KEY);
+        Objects.requireNonNull(sysConfigWpsAccessKey, "wps access key不能为空");
+        Objects.requireNonNull(sysConfigWpsAccessKey.getConfigValue(), "wps access key值不能为空");
+
+        SysConfig sysConfigWpsSecretKey = commonCacheService.addSysConfigCache(SystemConstant.WPS_SECRET_KEY);
+        Objects.requireNonNull(sysConfigWpsSecretKey, "wps secret key不能为空");
+        Objects.requireNonNull(sysConfigWpsSecretKey.getConfigValue(), "wps secret key值不能为空");
+
+        Map<String, String> headers = new HashMap<>();
+        String contentMd5 = MD5Util.encoder(content);
+        headers.put("Content-Md5", contentMd5);
+        String date = getGMTDateString();
+        String signature = String.format(Locale.US, "WPS-3:%s:%s", sysConfigWpsAccessKey.getConfigValue(), sha1(sysConfigWpsSecretKey.getConfigValue() + contentMd5 + api + SystemConstant.CONTENT_TYPE + date));
+        headers.put("X-Auth", signature);
+        headers.put("DATE", date);
+        headers.put(HTTP.CONTENT_TYPE, SystemConstant.CONTENT_TYPE);
+
+        String result = HttpUtil.postJson(sysConfigWpsDomain.getConfigValue() + api, content, headers);
+        if (Objects.nonNull(result) && !Objects.equals(result.trim(), "")) {
+            log.info("result:{}", JacksonUtil.parseJson(result));
+        }
+        return result;
+    }
+}