Pārlūkot izejas kodu

update content-service/exam

reghao 9 mēneši atpakaļ
vecāks
revīzija
288aff3ec6
17 mainītis faili ar 309 papildinājumiem un 93 dzēšanām
  1. 0 41
      content/content-service/src/main/java/cn/reghao/tnb/content/app/config/web/AccessInterceptor.java
  2. 1 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/config/web/WebConfig.java
  3. 19 12
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/ExamController.java
  4. 48 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/MarkController.java
  5. 18 3
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/PaperController.java
  6. 1 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/db/mapper/ExamUserMapper.java
  7. 20 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/query/GetPaperQuery.java
  8. 5 23
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/query/PaperQuery.java
  9. 3 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/vo/ExamMark.java
  10. 5 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/vo/PaperDetail.java
  11. 1 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamResultService.java
  12. 7 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamService.java
  13. 31 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamUserService.java
  14. 67 11
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/PaperService.java
  15. 74 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/web/AccessInterceptor.java
  16. 6 0
      content/content-service/src/main/resources/mapper/exam/ExamUserMapper.xml
  17. 3 0
      content/content-service/src/main/resources/mapper/exam/PaperMapper.xml

+ 0 - 41
content/content-service/src/main/java/cn/reghao/tnb/content/app/config/web/AccessInterceptor.java

@@ -1,41 +0,0 @@
-package cn.reghao.tnb.content.app.config.web;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.lang.Nullable;
-import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.ModelAndView;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * HTTP 请求拦截器
- *
- * @author reghao
- * @date 2025-07-18 09:18:16
- */
-@Slf4j
-@Component
-public class AccessInterceptor implements HandlerInterceptor {
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        String uri = request.getRequestURI();
-        String method = request.getMethod();
-        String userAgent = request.getHeader("user-agent");
-        String ipv4 = request.getRemoteAddr();
-        //log.info("{} {}", uri, method);
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response,
-                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
-    }
-
-    @Override
-    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
-                                Object handler, @Nullable Exception ex) throws Exception {
-    }
-}

+ 1 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/config/web/WebConfig.java

@@ -1,5 +1,6 @@
 package cn.reghao.tnb.content.app.config.web;
 
+import cn.reghao.tnb.content.app.exam.web.AccessInterceptor;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;

+ 19 - 12
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/ExamController.java

@@ -2,10 +2,13 @@ package cn.reghao.tnb.content.app.exam.controller;
 
 import cn.reghao.jutil.jdk.db.PageList;
 import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.app.exam.model.constant.PaperViewType;
 import cn.reghao.tnb.content.app.exam.model.dto.UserResult;
 import cn.reghao.tnb.content.app.exam.model.query.PaperQuery;
 import cn.reghao.tnb.content.app.exam.model.vo.*;
 import cn.reghao.tnb.content.app.exam.service.ExamService;
+import cn.reghao.tnb.content.app.exam.service.ExamUserService;
 import cn.reghao.tnb.content.app.exam.service.PaperService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
@@ -21,49 +24,53 @@ import java.util.List;
  */
 @Tag(name = "考试接口")
 @RestController
-@RequestMapping("/api/content/exam")
+@RequestMapping("/api/content/exam/eval")
 public class ExamController {
+    private final ExamUserService examUserService;
     private final ExamService examService;
     private final PaperService paperService;
 
-    public ExamController(ExamService examService, PaperService paperService) {
+    public ExamController(ExamUserService examUserService, ExamService examService, PaperService paperService) {
+        this.examUserService = examUserService;
         this.examService = examService;
         this.paperService = paperService;
     }
 
     @Operation(summary = "获取考试列表", description = "N")
-    @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
-    @Deprecated
+    @GetMapping(value = "/list", produces = MediaType.APPLICATION_JSON_VALUE)
     public String getExams(PaperQuery paperQuery) {
+        paperQuery.setScope(examUserService.getRole());
         PageList<PaperView> pageList1 = paperService.getExamPapers(paperQuery);
         return WebResult.success(pageList1);
     }
 
     @Operation(summary = "获取考试使用的试卷", description = "N")
     @GetMapping(value = "/start/{paperId}", produces = MediaType.APPLICATION_JSON_VALUE)
-    @Deprecated
-    public String startExam(@PathVariable("paperId") int paperId) {
-        return WebResult.success();
+    public String getExamPaperView(@PathVariable("paperId") int paperId) {
+        int viewType = PaperViewType.PaperExam.getCode();
+        long userId = UserContext.getUser();
+
+        PaperDetail paperDetail = paperService.getPaperDetail(paperId, viewType, userId);
+        return paperDetail != null ? WebResult.success(paperDetail) : WebResult.notFound();
     }
 
     @Operation(summary = "获取试卷中的试题", description = "N")
     @GetMapping(value = "/paper/{paperId}/question", produces = MediaType.APPLICATION_JSON_VALUE)
-    @Deprecated
     public String getExamQuestions1(@PathVariable("paperId") Integer paperId) {
         List<QuestionInfo> list1 = paperService.getPaperQuestions(paperId);
         return WebResult.success(list1);
     }
 
-    @Operation(summary = "暂存用户提交的答案", description = "N")
+    @Operation(summary = "暂存用户提交的试卷答案", description = "N")
     @PostMapping(value = "/cache", produces = MediaType.APPLICATION_JSON_VALUE)
-    public String cacheExamPaper1(@Validated @RequestBody UserResult userResult) {
+    public String cacheExamPaper(@Validated @RequestBody UserResult userResult) {
         paperService.cacheUserAnswers(userResult);
         return WebResult.success();
     }
 
-    @Operation(summary = "用户提交试答案", description = "N")
+    @Operation(summary = "用户提交试答案", description = "N")
     @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
-    public String submitExamPaper1(@Validated @RequestBody UserResult userResult) {
+    public String submitExamPaper(@Validated @RequestBody UserResult userResult) {
         int resultId = examService.submitExamPaper(userResult);
         return WebResult.success(resultId);
     }

+ 48 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/MarkController.java

@@ -0,0 +1,48 @@
+package cn.reghao.tnb.content.app.exam.controller;
+
+import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.app.exam.model.constant.PaperViewType;
+import cn.reghao.tnb.content.app.exam.model.dto.UserResult;
+import cn.reghao.tnb.content.app.exam.model.vo.PaperDetail;
+import cn.reghao.tnb.content.app.exam.service.ExamResultService;
+import cn.reghao.tnb.content.app.exam.service.PaperService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author reghao
+ * @date 2025-08-11 17:14:46
+ */
+@Tag(name = "阅卷接口")
+@RestController
+@RequestMapping("/api/content/exam/mark")
+public class MarkController {
+    private final PaperService paperService;
+    private final ExamResultService examResultService;
+
+    public MarkController(PaperService paperService, ExamResultService examResultService) {
+        this.paperService = paperService;
+        this.examResultService = examResultService;
+    }
+
+    @Operation(summary = "获取阅卷视图", description = "N")
+    @GetMapping(value = "/view", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getMarkPaperView(int paperId) {
+        int viewType = PaperViewType.PaperMark.getCode();
+        long userId = UserContext.getUser();
+
+        PaperDetail paperDetail = paperService.getPaperDetail(paperId, viewType, userId);
+        return paperDetail != null ? WebResult.success(paperDetail) : WebResult.notFound();
+    }
+
+    @Operation(summary = "提交阅卷结果", description = "N")
+    @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String submitExamMark(@RequestBody @Validated UserResult userResult) {
+        examResultService.markExamResult(userResult);
+        return WebResult.success();
+    }
+}

+ 18 - 3
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/controller/PaperController.java

@@ -3,8 +3,10 @@ package cn.reghao.tnb.content.app.exam.controller;
 import cn.reghao.jutil.jdk.db.PageList;
 import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.common.auth.AuthUser;
 import cn.reghao.tnb.common.db.KeyValue;
 import cn.reghao.tnb.content.app.exam.model.dto.PaperAddDto;
+import cn.reghao.tnb.content.app.exam.model.query.GetPaperQuery;
 import cn.reghao.tnb.content.app.exam.model.query.PaperQuery;
 import cn.reghao.tnb.content.app.exam.model.vo.PaperDetail;
 import cn.reghao.tnb.content.app.exam.model.vo.PaperView;
@@ -12,6 +14,7 @@ import cn.reghao.tnb.content.app.exam.service.PaperService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
@@ -31,6 +34,7 @@ public class PaperController {
         this.paperService = paperService;
     }
 
+    @AuthUser
     @Operation(summary = "创建试卷", description = "N")
     @PostMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
     public String addPaper(@RequestBody @Valid PaperAddDto paperAddDto) {
@@ -38,6 +42,7 @@ public class PaperController {
         return WebResult.result(result);
     }
 
+    @AuthUser
     @Operation(summary = "删除试卷", description = "N")
     @DeleteMapping(value = "/{paperId}", produces = MediaType.APPLICATION_JSON_VALUE)
     public String deletePaper(@PathVariable("paperId") Integer paperId) {
@@ -45,6 +50,7 @@ public class PaperController {
         return WebResult.result(result);
     }
 
+    @AuthUser
     @Operation(summary = "获取试卷列表", description = "N")
     @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
     public String getPapers(PaperQuery paperQuery) {
@@ -52,10 +58,18 @@ public class PaperController {
         return WebResult.success(pageList);
     }
 
+    @AuthUser
     @Operation(summary = "获取试卷详情", description = "N")
-    @GetMapping(value = "/{paperId}", produces = MediaType.APPLICATION_JSON_VALUE)
-    public String getPaper(@PathVariable("paperId") Integer paperId, @RequestParam("viewType") int viewType) {
-        PaperDetail paperDetail = paperService.getPaperDetail(paperId, viewType);
+    @GetMapping(value = "/detail", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getPaper(@Validated GetPaperQuery getPaperQuery) {
+        int paperId = getPaperQuery.getPaperId();
+        int viewType = getPaperQuery.getViewType();
+        Long userId = getPaperQuery.getUserId();
+        if (userId == null) {
+            userId = -1L;
+        }
+
+        PaperDetail paperDetail = paperService.getPaperDetail(paperId, viewType, userId);
         if (paperDetail == null) {
             return WebResult.notFound();
         }
@@ -63,6 +77,7 @@ public class PaperController {
         return WebResult.success(paperDetail);
     }
 
+    @AuthUser
     @Operation(summary = "获取试卷 KV 列表", description = "N")
     @GetMapping(value = "/kv", produces = MediaType.APPLICATION_JSON_VALUE)
     public String getPaperKeyValue() {

+ 1 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/db/mapper/ExamUserMapper.java

@@ -14,5 +14,6 @@ import java.util.List;
 @Mapper
 public interface ExamUserMapper extends BaseMapper<ExamUser> {
     List<ExamUser> findByPaperId(int paperId);
+    ExamUser findByUserId(long userId);
     ExamUser findExamUser(@Param("paperId") int paperId, @Param("userId") long userId);
 }

+ 20 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/query/GetPaperQuery.java

@@ -0,0 +1,20 @@
+package cn.reghao.tnb.content.app.exam.model.query;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author reghao
+ * @date 2025-08-11 14:33:52
+ */
+@Setter
+@Getter
+public class GetPaperQuery {
+    @NotNull
+    private Integer paperId;
+    @NotNull
+    private Integer viewType;
+    private Long userId;
+}

+ 5 - 23
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/query/PaperQuery.java

@@ -1,7 +1,6 @@
 package cn.reghao.tnb.content.app.exam.model.query;
 
 import cn.reghao.jutil.jdk.db.Page;
-import cn.reghao.tnb.common.auth.UserContext;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -14,24 +13,19 @@ import lombok.Setter;
 public class PaperQuery {
     private Integer pageNumber;
     private Integer pageSize;
-    private Long loginUser;
     private Integer subjectId;
-    private Integer type;
-    private Integer level;
+    private Integer scope;
 
     public PaperQuery() {
         this.pageNumber = 1;
         this.pageSize = 10;
-        this.loginUser = UserContext.getUser();
     }
 
     private PaperQuery(Builder builder) {
         this.pageNumber = builder.pageNumber;
         this.pageSize = builder.pageSize;
-        this.loginUser = builder.loginUser;
         this.subjectId = builder.subjectId;
-        this.type = builder.type;
-        this.level = builder.level;
+        this.scope = builder.scope;
     }
 
     public Page getPage() {
@@ -41,10 +35,8 @@ public class PaperQuery {
     public static final class Builder {
         private Integer pageNumber;
         private Integer pageSize;
-        private Long loginUser;
         private Integer subjectId;
-        private Integer type;
-        private Integer level;
+        private Integer scope;
 
         public Builder() {
             this.pageNumber = 1;
@@ -61,23 +53,13 @@ public class PaperQuery {
             return this;
         }
 
-        public Builder loginUser(long loginUser) {
-            this.loginUser = loginUser;
-            return this;
-        }
-
         public Builder subjectId(int subjectId) {
             this.subjectId = subjectId;
             return this;
         }
 
-        public Builder level(int level) {
-            this.level = level;
-            return this;
-        }
-
-        public Builder type(int type) {
-            this.type = type;
+        public Builder scope(int scope) {
+            this.scope = scope;
             return this;
         }
 

+ 3 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/vo/ExamMark.java

@@ -11,13 +11,15 @@ public class ExamMark {
     private int totalScore;
     private String examTime;
     private String student;
+    private long userId;
 
-    public ExamMark(ExamResults examResults, String student) {
+    public ExamMark(ExamResults examResults, String student, long userId) {
         this.resultId = examResults.getResultId();
         this.paperId = examResults.getPaperId();
         this.name = examResults.getName();
         this.totalScore = examResults.getTotalScore();
         this.examTime = examResults.getExamTime();
         this.student = student;
+        this.userId = userId;
     }
 }

+ 5 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/model/vo/PaperDetail.java

@@ -31,4 +31,9 @@ public class PaperDetail {
         this.questionMap = questionMap;
         this.userResult = userResult;
     }
+
+    public PaperDetail(int paperId, int status) {
+        this.paperId = paperId;
+        this.status = status;
+    }
 }

+ 1 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamResultService.java

@@ -60,7 +60,7 @@ public class ExamResultService {
                     if (accountInfo != null) {
                         student = accountInfo.getScreenName();
                     }
-                    return new ExamMark(examResults, student);
+                    return new ExamMark(examResults, student, userId);
                 })
                 .collect(Collectors.toList());
         return PageList.pageList(page, total, list1);

+ 7 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamService.java

@@ -154,7 +154,13 @@ public class ExamService {
                 paperAnswer.setScore(0);
             } else if (type == QuestionType.QUESTION4.getCode()) {
                 // 填空题
-                answer = (String) userAnswer.getSubmitAnswer()[0];
+                StringBuilder sb = new StringBuilder();
+                for (Object obj : userAnswer.getSubmitAnswer()) {
+                    sb.append(obj).append(",");
+                }
+                answer = sb.toString();
+                int idx = answer.lastIndexOf(",");
+                answer = answer.substring(0, idx);
             } else if (type == QuestionType.QUESTION5.getCode()) {
                 // 问答题
                 answer = (String) userAnswer.getSubmitAnswer()[0];

+ 31 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/ExamUserService.java

@@ -0,0 +1,31 @@
+package cn.reghao.tnb.content.app.exam.service;
+
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.app.exam.db.mapper.ExamUserMapper;
+import cn.reghao.tnb.content.app.exam.model.po.ExamUser;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author reghao
+ * @date 2025-08-11 15:14:56
+ */
+@Service
+public class ExamUserService {
+    private final ExamUserMapper examUserMapper;
+
+    public ExamUserService(ExamUserMapper examUserMapper) {
+        this.examUserMapper = examUserMapper;
+    }
+
+    public boolean isAdminUser() {
+        long loginUser = UserContext.getUser();
+        ExamUser examUser = examUserMapper.findByUserId(loginUser);
+        return examUser != null && examUser.getRole() == 1;
+    }
+
+    public int getRole() {
+        long loginUser = UserContext.getUser();
+        ExamUser examUser = examUserMapper.findByUserId(loginUser);
+        return examUser != null ? examUser.getRole() : -1;
+    }
+}

+ 67 - 11
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/service/PaperService.java

@@ -109,7 +109,7 @@ public class PaperService {
     }
 
     public PageList<PaperView> getExamPapers(PaperQuery paperQuery) {
-        long loginUser = paperQuery.getLoginUser();
+        long loginUser = UserContext.getUser();
         int total = paperMapper.countByCriteria(paperQuery);
         Page page = paperQuery.getPage();
         List<Paper> list = paperMapper.findPaperByPage(page, paperQuery);
@@ -187,32 +187,71 @@ public class PaperService {
         return questionInfo;
     }
 
-    public PaperDetail getPaperDetail(int paperId, int viewType) {
+    public PaperDetail getPaperDetail(int paperId, int viewType, long userId) {
         long loginUser = UserContext.getUser();
         ExamUser examUser = examUserMapper.findExamUser(paperId, loginUser);
         if (examUser == null) {
-            return null;
+            // 用户没有访问试卷的权限
+            return new PaperDetail(paperId, 1);
         }
 
-        int paperStatus = 1;
         Paper paper = paperMapper.findById(paperId);
-        int markType = paper.getMarkType();
         int role = examUser.getRole();
 
         Map<Long, QuestionAnswer> paperAnswers;
         if (viewType == PaperViewType.PaperPreview.getCode()) {
+            if (role > 1) {
+                // 用户只有做题的权限
+                return new PaperDetail(paperId, 3);
+            }
+
             paperAnswers = getPaperAnswersFromCorrect(paperId);
         } else if (viewType == PaperViewType.PaperExam.getCode()) {
+            if (role == 1) {
+                // 用户只有管理的权限
+                return new PaperDetail(paperId, 2);
+            }
+
             paperAnswers = getPaperAnswersFromCache(paperId, loginUser);
         } else if (viewType == PaperViewType.PaperMark.getCode()) {
-            paperAnswers = getPaperAnswersFromResult(paperId, loginUser);
+            int markType = paper.getMarkType();
+            if (markType == 2) {
+                // 自己批改
+                if (role > 1) {
+                    // 试卷由用户自己批改
+                    return new PaperDetail(paperId, 4);
+                }
+            } else if (markType == 3) {
+                // 他人批改
+                if (role > 1) {
+                    // 用户只有做题的权限
+                    return new PaperDetail(paperId, 3);
+                }
+            }
+
+            // 批改试卷不需要客观题
+            paperAnswers = getPaperAnswersFromResult(paperId, loginUser, false);
         } else if (viewType == PaperViewType.PaperResult.getCode()) {
-            paperAnswers = getPaperAnswersFromResult(paperId, loginUser);
+            if (role == 1) {
+                // 用户只有管理的权限
+                return new PaperDetail(paperId, 2);
+            }
+
+            paperAnswers = getPaperAnswersFromResult(paperId, loginUser, true);
         } else {
-            paperAnswers = getEmptyPaperAnswers(paperId);
+            // 试卷访问类型 viewType 未知
+            return new PaperDetail(paperId, 5);
         }
 
+        int paperStatus = 6;
+        Set<Integer> questionTypes = paperAnswers.values().stream()
+                .collect(Collectors.groupingBy(QuestionAnswer::getQuestionType))
+                .keySet();
         Map<Integer, List<QuestionInfo>> questionMap = getPaperQuestions(paperId).stream()
+                .filter(questionInfo -> {
+                    int questionType = questionInfo.getQuestionType();
+                    return questionTypes.contains(questionType);
+                })
                 .collect(Collectors.groupingBy(QuestionInfo::getQuestionType));
         UserResult userResult = new UserResult(paperId, paperAnswers);
         return new PaperDetail(paper, paperStatus, questionMap, userResult);
@@ -290,7 +329,7 @@ public class PaperService {
         return map;
     }
 
-    private Map<Long, QuestionAnswer> getPaperAnswersFromResult(int paperId, long userId) {
+    private Map<Long, QuestionAnswer> getPaperAnswersFromResult(int paperId, long userId, boolean hasObjective) {
         PaperResult paperResult = getPaperResult(paperId, userId);
         if (paperResult == null) {
             log.error("userId:{} 的试卷 paperId:{} 结果不存在", userId, paperId);
@@ -298,7 +337,7 @@ public class PaperService {
         }
 
         int resultId = paperResult.getId();
-        return getPaperAnswersFromResult0(paperId, resultId);
+        return getPaperAnswersFromResult0(paperId, resultId, hasObjective);
     }
 
     private PaperResult getPaperResult(int paperId, long userId) {
@@ -311,11 +350,16 @@ public class PaperService {
         return list.isEmpty() ? null : list.get(0);
     }
 
-    private Map<Long, QuestionAnswer> getPaperAnswersFromResult0(int paperId, int resultId) {
+    private Map<Long, QuestionAnswer> getPaperAnswersFromResult0(int paperId, int resultId, boolean hasObjective) {
         Map<Long, QuestionAnswer> map = new HashMap<>();
         List<PaperAnswer> paperAnswerList = paperAnswerMapper.findByResultId(resultId);
         paperAnswerList.forEach(paperAnswer -> {
             long questionId = paperAnswer.getQuestionId();
+            int questionType = paperAnswer.getQuestionType();
+            if (questionType <= QuestionType.QUESTION3.getCode() && !hasObjective) {
+                return;
+            }
+
             PaperQuestion paperQuestion = paperQuestionMapper.findPaperQuestion(paperId, questionId);
             int correctScore = paperQuestion.getScore();
             QuestionAnswer questionAnswer = new QuestionAnswer(paperAnswer, correctScore);
@@ -343,10 +387,22 @@ public class PaperService {
         String submitAnswer = paperAnswer.getAnswer();
         List<Object> submitAnswerList;
         if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION3.getCode()) {
+            // 单选题和判断题前端使用 radio, 需要 int 类型来表示选中的 radio
             submitAnswerList = List.of(Integer.parseInt(submitAnswer));
         } else if (type == QuestionType.QUESTION2.getCode()) {
+            // 多选题前端使用 checkbox, 使用 string[] 来表示选中的 checkbox
             String[] answerArr = submitAnswer.split(",");
             submitAnswerList = List.of(answerArr);
+        } else if (type == QuestionType.QUESTION4.getCode()) {
+            // 填空题
+            String[] answerArr = submitAnswer.split(",");
+            submitAnswerList = List.of(answerArr);
+        } else if (type == QuestionType.QUESTION5.getCode()) {
+            // 问答题
+            submitAnswerList = List.of(submitAnswer);
+        } else if (type == QuestionType.QUESTION6.getCode()) {
+            // 组合题
+            submitAnswerList = List.of(submitAnswer);
         } else {
             submitAnswerList = List.of(submitAnswer);
         }

+ 74 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/exam/web/AccessInterceptor.java

@@ -0,0 +1,74 @@
+package cn.reghao.tnb.content.app.exam.web;
+
+import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.content.app.exam.service.ExamService;
+import cn.reghao.tnb.content.app.exam.service.ExamUserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+
+/**
+ * HTTP 请求拦截器
+ *
+ * @author reghao
+ * @date 2025-07-18 09:18:16
+ */
+@Slf4j
+@Component
+public class AccessInterceptor implements HandlerInterceptor {
+    private final ExamUserService examUserService;
+
+    public AccessInterceptor(ExamUserService examUserService) {
+        this.examUserService = examUserService;
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        String uri = request.getRequestURI();
+        String method = request.getMethod();
+        String userAgent = request.getHeader("user-agent");
+        String ipv4 = request.getRemoteAddr();
+        if (uri.startsWith("/api/content/exam/question")
+                || uri.startsWith("/api/content/exam/paper")) {
+            if (!examUserService.isAdminUser()) {
+                String retJson = WebResult.failWithMsg("非管理员");
+                response.setStatus(HttpServletResponse.SC_OK);
+                response.setContentType("application/json; charset=utf-8");
+                PrintWriter printWriter = response.getWriter();
+                printWriter.write(retJson);
+                return false;
+            }
+        }
+
+        if (uri.startsWith("/api/content/exam/eval")) {
+            if (examUserService.isAdminUser()) {
+                String retJson = WebResult.failWithMsg("非普通用户");
+                response.setStatus(HttpServletResponse.SC_OK);
+                response.setContentType("application/json; charset=utf-8");
+                PrintWriter printWriter = response.getWriter();
+                printWriter.write(retJson);
+                return false;
+            }
+        }
+
+        //log.info("{} {}", uri, method);
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response,
+                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+                                Object handler, @Nullable Exception ex) throws Exception {
+    }
+}

+ 6 - 0
content/content-service/src/main/resources/mapper/exam/ExamUserMapper.xml

@@ -22,6 +22,12 @@
         from exam_user
         where paper_id=#{paperId}
     </select>
+    <select id="findByUserId" resultType="cn.reghao.tnb.content.app.exam.model.po.ExamUser">
+        select *
+        from exam_user
+        where user_id=#{userId}
+        limit 1
+    </select>
     <select id="findExamUser" resultType="cn.reghao.tnb.content.app.exam.model.po.ExamUser">
         select *
         from exam_user

+ 3 - 0
content/content-service/src/main/resources/mapper/exam/PaperMapper.xml

@@ -39,5 +39,8 @@
         <if test="paperQuery.subjectId != null">
             and subject_id=#{paperQuery.subjectId}
         </if>
+        <if test="paperQuery.scope != null">
+            and scope=#{paperQuery.scope}
+        </if>
     </select>
 </mapper>