西格玛许 hai 4 días
pai
achega
3ef6b13a84

+ 183 - 27
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/controller/MainExamEvaluationController.java

@@ -22,15 +22,18 @@ import org.dromara.demo.domain.dto.KaoshixingRequest;
 import org.dromara.demo.domain.dto.KaoshixingScoreListRequest;
 import org.dromara.demo.domain.dto.KaoshixingUserExamListRequest;
 import org.dromara.demo.service.impl.KaoshixingService;
+import org.dromara.main.domain.MainAbilityConfig;
 import org.dromara.main.domain.bo.MainExamEvaluationBo;
 import org.dromara.main.domain.bo.MainExamEvaluationSyncBo;
 import org.dromara.main.domain.vo.MainExamApplyListVo;
+import org.dromara.main.domain.vo.MainExamResultVo;
 import org.dromara.main.domain.vo.MainExamSyncEmployeeOptionVo;
 import org.dromara.main.domain.vo.MainExamEvaluationVo;
 import org.dromara.main.service.IMainExamEvaluationService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -45,6 +48,7 @@ public class MainExamEvaluationController extends BaseController {
 
     private final IMainExamEvaluationService examEvaluationService;
     private final KaoshixingService kaoshixingService;
+    private final org.dromara.main.mapper.MainExamApplyMapper applyMapper;
 
     /**
      * 查询测评列表
@@ -278,13 +282,7 @@ public class MainExamEvaluationController extends BaseController {
 
     /**
      * 小程序专用:考生静默登录(action_id=203)
-     * 流程:先尝试203静默登录;若用户不存在,自动以201注册并登录。
-     * 成功后返回考试星跳转url,前端用web-view打开即可完成登录进入考试。
-     *
-     * @param params user_id      考生唯一标识(必填,通常用 studentId)
-     *               user_name    考生姓名(首次注册时必填)
-     *               department   部门(首次注册时必填,缺省传 "学员")
-     *               custom_url   登录后跳转地址(可选,*.kaoshixing.com 域名)
+     * 逻辑调整:支持一场测评对应多个考试链接,并从 main_ability_config 获取。
      */
     @PostMapping("/silent-login")
     @Log(title = "考试星静默登录", businessType = BusinessType.OTHER)
@@ -297,37 +295,195 @@ public class MainExamEvaluationController extends BaseController {
         String department = (String) params.getOrDefault("department", "学员");
         String customUrl  = (String) params.get("custom_url");
 
-        KaoshixingAutoLoginRequest req = new KaoshixingAutoLoginRequest();
-        req.setUserId(userId);
-        req.setUserName(userName);
-        req.setDepartment(department);
-        req.setCustomUrl(customUrl);
+        // 获取测评ID
+        Long evaluationId = null;
+        Object evalIdObj = params.get("evaluationId");
+        if (evalIdObj != null && !evalIdObj.toString().isBlank()) {
+            try {
+                evaluationId = Long.valueOf(evalIdObj.toString());
+            } catch (Exception e) {
+                log.warn("无效的测评ID: {}", evalIdObj);
+            }
+        }
 
         try {
-            String resp = kaoshixingService.fetchAutoLogin(req);
-            // 解析考试星返回的url字段
-            com.fasterxml.jackson.databind.ObjectMapper mapper =
-                new com.fasterxml.jackson.databind.ObjectMapper();
-            com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(resp);
-
-            int code = root.path("code").asInt(0);
-            if (code != 10000) {
-                String msg = root.path("msg").asText("登录失败");
-                return R.fail(msg);
+            List<Map<String, String>> exams = new java.util.ArrayList<>();
+            com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
+
+            // 如果传了测评ID,优先从能力配置表获取链接
+            if (evaluationId != null) {
+                MainExamEvaluationVo evaluationVo = examEvaluationService.queryById(evaluationId);
+                if (evaluationVo != null && evaluationVo.getAbilityConfigs() != null) {
+                    for (org.dromara.main.domain.MainAbilityConfig config : evaluationVo.getAbilityConfigs()) {
+                        if (org.dromara.common.core.utils.StringUtils.isNotBlank(config.getThirdExamLink())) {
+                            // 为每个配置的考试生成带Token的链接
+                            KaoshixingAutoLoginRequest req = new KaoshixingAutoLoginRequest();
+                            req.setUserId(userId);
+                            req.setUserName(userName);
+                            req.setDepartment(department);
+                            req.setCustomUrl(config.getThirdExamLink());
+
+                            String resp = kaoshixingService.fetchAutoLogin(req);
+                            com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(resp);
+                            if (root.path("code").asInt() == 10000) {
+                                String url = root.path("url").asText(null);
+                                if (url != null) {
+                                    Map<String, String> examMap = new java.util.HashMap<>();
+                                    examMap.put("name", config.getAbilityName()); // 考试/维度名称
+                                    examMap.put("url", url); // 授权后的链接
+                                    exams.add(examMap);
+                                }
+                            }
+                        }
+                    }
+                }
             }
 
-            String url = root.path("url").asText(null);
-            if (url == null || url.isBlank()) {
-                return R.fail("考试星未返回跳转链接");
+            Map<String, Object> data = new java.util.HashMap<>();
+            // 如果找到了配置的考试链接
+            if (!exams.isEmpty()) {
+                data.put("exams", exams);
+                data.put("url", exams.get(0).get("url")); // 默认提供第一个链接
+                return R.ok(data);
             }
 
-            Map<String, Object> data = new java.util.HashMap<>();
-            data.put("url", url);
+            // 兜底逻辑:如果没配置或查询失败,走原有的跳转逻辑
+            KaoshixingAutoLoginRequest req = new KaoshixingAutoLoginRequest();
+            req.setUserId(userId);
+            req.setUserName(userName);
+            req.setDepartment(department);
+            req.setCustomUrl(customUrl);
+
+            String resp = kaoshixingService.fetchAutoLogin(req);
+            com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(resp);
+            if (root.path("code").asInt() != 10000) {
+                return R.fail(root.path("msg").asText("登录失败"));
+            }
+            data.put("url", root.path("url").asText());
             return R.ok(data);
         } catch (Exception e) {
             log.error("考试星静默登录失败", e);
             return R.fail("登录失败:" + e.getMessage());
         }
     }
+
+
+    @GetMapping("/result/{evaluationId}")
+    public R<MainExamResultVo> getEvaluationResult(@PathVariable Long evaluationId, @RequestParam Long studentId) {
+        // 1. 获取该测评下的所有能力配置
+        MainExamEvaluationVo evaluationVo = examEvaluationService.queryById(evaluationId);
+        List<MainAbilityConfig> configs = evaluationVo.getAbilityConfigs();
+        if (configs == null || configs.isEmpty()) {
+            return R.fail("测评未配置能力项");
+        }
+
+        // 2. 调用考试星 702 接口获取用户的所有考试结果
+        KaoshixingUserExamListRequest req = new KaoshixingUserExamListRequest();
+        req.setUserId(String.valueOf(studentId));
+        req.setPage(1);
+        String resp = kaoshixingService.fetchUserExamList(req);
+
+        // 3. 解析第三方数据并对比
+        MainExamResultVo resultVo = new MainExamResultVo();
+        List<MainExamResultVo.AbilityResult> abilityResults = new java.util.ArrayList<>();
+        List<String> categories = new java.util.ArrayList<>();
+        List<BigDecimal> scores = new java.util.ArrayList<>();
+
+        int finishedCount = 0;
+        boolean isAllPass = true;
+
+        try {
+            com.fasterxml.jackson.databind.JsonNode root = new com.fasterxml.jackson.databind.ObjectMapper().readTree(resp);
+            com.fasterxml.jackson.databind.JsonNode rows = root.path("bizContent").path("rows");
+
+            for (MainAbilityConfig config : configs) {
+                categories.add(config.getAbilityName());
+                boolean found = false;
+
+                // 在第三方记录中查找对应的考试ID
+                for (com.fasterxml.jackson.databind.JsonNode row : rows) {
+                    if (row.path("examInfoId").asLong() == config.getThirdExamInfoId()) {
+                        String status = row.path("status").asText();
+                        if ("checked".equals(status)) {
+                            BigDecimal score = new BigDecimal(row.path("results").asText("0"));
+                            BigDecimal passScore = config.getThirdExamPassMark();
+
+                            MainExamResultVo.AbilityResult ar = new MainExamResultVo.AbilityResult();
+                            ar.setName(config.getAbilityName());
+                            ar.setScore(score);
+                            ar.setPassScore(passScore);
+                            ar.setIsPass(score.compareTo(passScore) >= 0);
+                            ar.setInquireLink(row.path("inquireLink").asText(null));
+
+                            abilityResults.add(ar);
+                            scores.add(score);
+
+                            if (!ar.getIsPass()) isAllPass = false;
+                            finishedCount++;
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (!found) {
+                    scores.add(BigDecimal.ZERO);
+                }
+            }
+
+            boolean allFinished = (finishedCount == configs.size());
+            String finalResult = isAllPass ? "1" : "2";
+
+            resultVo.setAllFinished(allFinished);
+            resultVo.setFinalResult(finalResult);
+            resultVo.setAbilityResults(abilityResults);
+
+            MainExamResultVo.RadarChartData radar = new MainExamResultVo.RadarChartData();
+            radar.setCategories(categories);
+            radar.setSeries(scores);
+            resultVo.setRadarChart(radar);
+
+            // 4. 同步更新 main_exam_apply 记录
+            try {
+                org.dromara.main.domain.MainExamApply apply = applyMapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<org.dromara.main.domain.MainExamApply>()
+                    .eq(org.dromara.main.domain.MainExamApply::getEvaluationId, evaluationId)
+                    .eq(org.dromara.main.domain.MainExamApply::getStudentId, studentId)
+                    .eq(org.dromara.main.domain.MainExamApply::getDelFlag, "0")
+                    .last("LIMIT 1"));
+
+                if (apply != null) {
+                    boolean changed = false;
+                    // 如果全部完成,更新状态为已完成(2)
+                    if (allFinished && !"2".equals(apply.getApplyStatus())) {
+                        apply.setApplyStatus("2");
+                        apply.setFinishedTime(new java.util.Date());
+                        changed = true;
+                    } else if (finishedCount > 0 && "0".equals(apply.getApplyStatus())) {
+                        // 如果有答题记录但没完,更新为进行中(1)
+                        apply.setApplyStatus("1");
+                        changed = true;
+                    }
+
+                    // 更新通过结果
+                    if (allFinished && !finalResult.equals(apply.getFinalResult())) {
+                        apply.setFinalResult(finalResult);
+                        changed = true;
+                    }
+
+                    if (changed) {
+                        applyMapper.updateById(apply);
+                        log.info("同步更新测评申请状态成功,applyId: {}, status: {}, result: {}", apply.getId(), apply.getApplyStatus(), apply.getFinalResult());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("更新测评申请记录失败", e);
+            }
+
+            return R.ok(resultVo);
+        } catch (Exception e) {
+            log.error("解析考试结果失败", e);
+            return R.fail("获取结果失败");
+        }
+    }
 }
 

+ 42 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/domain/MainExamAbilityAttempt.java

@@ -0,0 +1,42 @@
+package org.dromara.main.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 能力考试作答主记录表 main_exam_ability_attempt
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("main_exam_ability_attempt")
+public class MainExamAbilityAttempt extends BaseEntity {
+
+    @TableId(value = "id")
+    private Long id;
+
+    private Long applyId; // 关联 main_exam_apply.id
+    private Long evaluationId;
+    private Long abilityConfigId;
+    private Long thirdExamInfoId;
+    private String thirdExamName;
+    private String thirdUserId;
+    private Integer attemptNo;
+    private String attemptStatus; // 0待开始 1进行中 2已交卷 3已同步
+    private Date startTime;
+    private Date submitTime;
+    private Integer durationSeconds;
+    private BigDecimal totalScore;
+    private BigDecimal passScore;
+    private String finalResult; // 1通过 2未通过
+    private String syncStatus; // 0未同步 1已同步
+
+    @TableLogic
+    private String delFlag;
+}

+ 30 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/domain/vo/MainExamResultVo.java

@@ -0,0 +1,30 @@
+package org.dromara.main.domain.vo;
+
+import lombok.Data;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class MainExamResultVo {
+    private Boolean allFinished; // 是否全部考完
+    private String finalResult;  // 1通过 2未通过
+    private BigDecimal totalScore;
+    private List<AbilityResult> abilityResults;
+    private RadarChartData radarChart;
+
+    @Data
+    public static class AbilityResult {
+        private String name;
+        private BigDecimal score;
+        private BigDecimal passScore;
+        private Boolean isPass;
+        private String inquireLink;
+
+    }
+
+    @Data
+    public static class RadarChartData {
+        private List<String> categories;
+        private List<BigDecimal> series;
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-main/src/main/java/org/dromara/main/mapper/MainExamAbilityAttemptMapper.java

@@ -0,0 +1,7 @@
+package org.dromara.main.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.main.domain.MainExamAbilityAttempt;
+
+public interface MainExamAbilityAttemptMapper extends BaseMapperPlus<MainExamAbilityAttempt, MainExamAbilityAttempt> {
+}