西格玛许 hai 3 días
pai
achega
0c34a83b27

+ 36 - 0
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/KaoshixingService.java

@@ -47,6 +47,8 @@ public class KaoshixingService {
     private static final String ACTION_ID_LOGIN = "201";
     // 查询考生信息 action_id
     private static final String ACTION_ID_QUERY_USER = "209";
+    // 查询考生考试结果信息 action_id (10000)
+    private static final String ACTION_ID_EXAM_RESULT = "10000";
     // JWT过期时间(5分钟,确保在高延迟网络下依然有效)
     private static final long EXPIRATION_SECONDS = 300L;
 
@@ -253,6 +255,40 @@ public class KaoshixingService {
         return sendPostRequest(requestUrl, requestBody);
     }
 
+    /**
+     * 查询考生考试结果信息(action_id=10000)
+     * 用于获取学员在指定考试的详细答题记录(支持多次作答)
+     */
+    public String fetchExamResult(String userId, Long examId, Integer page) {
+        if (isBlank(userId)) {
+            throw new IllegalArgumentException("user_id不能为空");
+        }
+        if (page == null) {
+            page = 1;
+        }
+
+        String jwtInfo = generateJwt(FIXED_APP_KEY, ACTION_ID_EXAM_RESULT);
+        log.info("查询考生考试结果接口JWT:{}", jwtInfo);
+
+        String requestUrl = String.format("%s%s/?jwt=%s", BASE_API_URL, FIXED_APP_ID, jwtInfo);
+        log.info("查询考生考试结果接口请求地址:{}", requestUrl);
+
+        try {
+            Map<String, Object> payload = new HashMap<>();
+            payload.put("userId", userId);
+            payload.put("page", page);
+            if (examId != null) {
+                payload.put("examId", examId);
+            }
+            String requestBody = objectMapper.writeValueAsString(payload);
+            log.info("查询考生考试结果接口请求体:{}", requestBody);
+            return sendPostRequest(requestUrl, requestBody);
+        } catch (Exception e) {
+            log.error("构造考试结果请求体失败", e);
+            throw new RuntimeException("构造请求体失败:" + e.getMessage());
+        }
+    }
+
     /**
      * 在线学习内容列表(action_id=605)
      */

+ 75 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/controller/MainExamEvaluationController.java

@@ -485,5 +485,80 @@ public class MainExamEvaluationController extends BaseController {
             return R.fail("获取结果失败");
         }
     }
+
+    /**
+     * 获取学员在某测评下的详细考试结果(对接考试星 action_id=10000)
+     * 返回每个能力维度的多次作答记录详情
+     */
+    @GetMapping("/detail-result/{evaluationId}")
+    public R<Map<String, Object>> getDetailResult(@PathVariable Long evaluationId, @RequestParam Long studentId) {
+        try {
+            // 1. 获取测评信息及能力配置
+            MainExamEvaluationVo evaluationVo = examEvaluationService.queryById(evaluationId);
+            if (evaluationVo == null) {
+                return R.fail("测评不存在");
+            }
+            List<MainAbilityConfig> configs = evaluationVo.getAbilityConfigs();
+            if (configs == null || configs.isEmpty()) {
+                return R.fail("测评未配置能力项");
+            }
+
+            String userId = String.valueOf(studentId);
+            com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+
+            // 2. 遍历每个能力配置,调用考试星10000接口获取详细结果
+            List<Map<String, Object>> abilityDetails = new java.util.ArrayList<>();
+
+            for (MainAbilityConfig config : configs) {
+                Map<String, Object> abilityMap = new java.util.HashMap<>();
+                abilityMap.put("abilityName", config.getAbilityName());
+                abilityMap.put("examInfoId", config.getThirdExamInfoId());
+                abilityMap.put("examName", config.getThirdExamName());
+                abilityMap.put("passMark", config.getThirdExamPassMark());
+                abilityMap.put("totalScore", config.getThirdExamTotalScore());
+
+                // 调用考试星10000接口获取该学员在此考试中的作答记录
+                String resultJson = kaoshixingService.fetchExamResult(userId, config.getThirdExamInfoId(), 1);
+
+                com.fasterxml.jackson.databind.JsonNode rootNode = mapper.readTree(resultJson);
+                com.fasterxml.jackson.databind.JsonNode bizContent = rootNode.path("bizContent");
+
+                List<Map<String, Object>> records = new java.util.ArrayList<>();
+                com.fasterxml.jackson.databind.JsonNode rowsNode = bizContent.path("rows");
+                if (rowsNode != null && rowsNode.isArray()) {
+                    for (com.fasterxml.jackson.databind.JsonNode row : rowsNode) {
+                        Map<String, Object> record = new java.util.HashMap<>();
+                        record.put("userId", row.path("userId").asText(""));
+                        record.put("examId", row.path("examId").asInt(0));
+                        record.put("examName", row.path("examName").asText(""));
+                        record.put("examStartTime", row.path("examStartTime").asText(""));
+                        record.put("examEndTime", row.path("examEndTime").asText(""));
+                        record.put("examTime", row.path("examTime").asInt(0)); // 分钟
+                        record.put("startTime", row.path("startTime").asText(""));
+                        record.put("commitTime", row.path("commitTime").asText(null));
+                        record.put("score", row.path("score").asText("0"));
+                        record.put("isPass", row.path("isPass").asInt(-1)); // 1=及格, 0=不及格
+                        record.put("times", row.path("times").asInt(0)); // 作答次数
+                        record.put("examResultsId", row.path("examResultsId").asInt(0));
+                        record.put("examStyleName", row.path("examStyleName").asText(""));
+                        records.add(record);
+                    }
+                }
+
+                abilityMap.put("records", records);
+                abilityMap.put("total", bizContent.path("total").asInt(0));
+                abilityDetails.add(abilityMap);
+            }
+
+            Map<String, Object> resultMap = new java.util.HashMap<>();
+            resultMap.put("evaluationName", evaluationVo.getEvaluationName());
+            resultMap.put("abilityDetails", abilityDetails);
+
+            return R.ok(resultMap);
+        } catch (Exception e) {
+            log.error("获取考试详情结果失败", e);
+            return R.fail("获取详情失败:" + e.getMessage());
+        }
+    }
 }
 

+ 0 - 33
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/service/impl/MainExamEvaluationServiceImpl.java

@@ -27,9 +27,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Base64;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.Date;
@@ -55,9 +53,6 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
     @Override
     public MainExamEvaluationVo queryById(Long id) {
         MainExamEvaluationVo vo = baseMapper.selectVoByIdWithImages(id);
-        if (vo != null) {
-            encodeDetailFieldInVo(vo);
-        }
         return vo;
     }
 
@@ -65,7 +60,6 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
     public TableDataInfo<MainExamEvaluationVo> queryPageList(MainExamEvaluationBo bo, PageQuery pageQuery) {
         QueryWrapper<MainExamEvaluation> lqw = buildQueryWrapper(bo);
         Page<MainExamEvaluationVo> result = baseMapper.selectVoPageWithImages(pageQuery.build(), lqw);
-        result.getRecords().forEach(this::encodeDetailFieldInVo);
         return TableDataInfo.build(result);
     }
 
@@ -73,7 +67,6 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
     public List<MainExamEvaluationVo> queryList(MainExamEvaluationBo bo) {
         QueryWrapper<MainExamEvaluation> lqw = buildQueryWrapper(bo);
         List<MainExamEvaluationVo> list = baseMapper.selectVoList(lqw);
-        list.forEach(this::encodeDetailFieldInVo);
         return list;
     }
 
@@ -127,7 +120,6 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(MainExamEvaluationBo bo) {
-        decodeDetailFieldInBo(bo);
         MainExamEvaluation add = BeanUtil.toBean(bo, MainExamEvaluation.class);
         if (StringUtils.isBlank(add.getTenantId()) && add.getPositionId() != null) {
             MainPosition position = mainPositionMapper.selectById(add.getPositionId());
@@ -148,7 +140,6 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean updateByBo(MainExamEvaluationBo bo) {
-        decodeDetailFieldInBo(bo);
         MainExamEvaluation update = BeanUtil.toBean(bo, MainExamEvaluation.class);
         validEntityBeforeSave(update);
 
@@ -432,28 +423,4 @@ public class MainExamEvaluationServiceImpl implements IMainExamEvaluationService
         }
         return tenantId;
     }
-
-    private void decodeDetailFieldInBo(MainExamEvaluationBo bo) {
-        if (StringUtils.isNotBlank(bo.getDetail())) {
-            try {
-                // 现在的逻辑:前端强制 Base64 编码,后端强制解码,不再猜测
-                String decoded = new String(Base64.getDecoder().decode(bo.getDetail()), StandardCharsets.UTF_8);
-                bo.setDetail(decoded);
-            } catch (Exception e) {
-                // 如果解码失败,说明传来的不是 Base64(可能是旧数据或异常),记录日志并保持原样
-                log.error("详情字段解码异常,内容可能是原始 HTML。内容长度: {}", bo.getDetail().length());
-            }
-        }
-    }
-
-    private void encodeDetailFieldInVo(MainExamEvaluationVo vo) {
-        if (StringUtils.isNotBlank(vo.getDetail())) {
-            try {
-                String encoded = Base64.getEncoder().encodeToString(vo.getDetail().getBytes(StandardCharsets.UTF_8));
-                vo.setDetail(encoded);
-            } catch (Exception e) {
-                log.warn("详情字段Base64编码失败,使用原始值", e);
-            }
-        }
-    }
 }