Prechádzať zdrojové kódy

feat(answer): 实现用户问卷去重查询功能

- 在 AnswerMapper 中添加自定义 SQL 查询方法,支持按用户和问卷去重
- 新增 selectDistinctUserQuestionnaires 接口与实现,用于分页获取去重结果-优化 WebQuestionGatewayImpl 中的查询逻辑,使用数据库层面去重替代内存处理
- 提升查询性能,避免大量数据加载到内存中进行去重操作-保持原有分页和条件查询功能,确保接口兼容性- 修复分页总数计算问题,使用数据库返回的真实总记录数
hxd 3 týždňov pred
rodič
commit
617b741198

+ 57 - 76
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebQuestionGatewayImpl.java

@@ -294,85 +294,66 @@ public class WebQuestionGatewayImpl implements WebQuestionGateway {
         answerService.saveBatch(answers);
     }
 
-    @Override
-    public PageRecord<UserQuestionnaireListDTO> queryUserQuestionnaireList(UserQuestionnaireListParam query) {
-
-        // 1. 查用户ID(如果传了手机号才查)
-        Long userId = null;
-        if (StringUtils.isNotBlank(query.getUserPhone())) {
-            UserInfo user = userService.getOne(
-                    new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, query.getUserPhone())
-            );
-            if (user == null) return emptyPage(query);
-            userId = user.getUserId();
-            log.info("用户ID: {}", userId);
-        }
-
-        // 2. 查询答案表,按时间倒序
-        LambdaQueryWrapper<Answer> wrapper = new LambdaQueryWrapper<Answer>()
-                .eq(userId != null, Answer::getUserId, userId)
-                .eq(query.getQuestionnaireId() != null, Answer::getQuestionnaireId, query.getQuestionnaireId())
-                .orderByDesc(Answer::getCreateTime);
+@Override
+public PageRecord<UserQuestionnaireListDTO> queryUserQuestionnaireList(UserQuestionnaireListParam query) {
 
-        List<Answer> allAnswers = answerService.list(wrapper);
-        log.info("答案表: {}", allAnswers);
-
-        // 3. 每个用户每份问卷只保留一条记录
-        Map<String, Answer> oneAnswerPerUserQuestionnaire = new LinkedHashMap<>();
-        for (Answer ans : allAnswers) {
-            String key = ans.getUserId() + "-" + ans.getQuestionnaireId();
-            oneAnswerPerUserQuestionnaire.putIfAbsent(key, ans);
-        }
-        log.info("去重后答案表: {}", oneAnswerPerUserQuestionnaire);
-
-        // 4. 分页处理
-        List<Answer> pageAnswers = new ArrayList<>(oneAnswerPerUserQuestionnaire.values());
-        int total = pageAnswers.size();
-        int fromIndex = (query.getPageNo() - 1) * query.getPageSize();
-        int toIndex = Math.min(fromIndex + query.getPageSize(), total);
-        if (fromIndex >= total) return emptyPage(query);
-        pageAnswers = pageAnswers.subList(fromIndex, toIndex);
-
-        // 5. 查询问卷信息
-        Set<Long> questionnaireIds = pageAnswers.stream()
-                .map(Answer::getQuestionnaireId).collect(Collectors.toSet());
-        Map<Long, Questionnaire> questionnaireMap = questionnaireService.listByIds(questionnaireIds)
-                .stream().collect(Collectors.toMap(Questionnaire::getQuestionnaireId, q -> q));
-        log.info("问卷信息: {}", questionnaireMap);
-
-        // 5.1 查询用户信息(所有答题用户)
-        Set<Long> userIds = pageAnswers.stream().map(Answer::getUserId).collect(Collectors.toSet());
-        Map<Long, UserInfo> userMap = userService.listByIds(userIds)
-                .stream().collect(Collectors.toMap(UserInfo::getUserId, u -> u));
-        log.info("用户信息: {}", userMap);
-
-        // 6. 组装 DTO
-        List<UserQuestionnaireListDTO> dtoList = pageAnswers.stream().map(ans -> {
-            UserQuestionnaireListDTO dto = new UserQuestionnaireListDTO();
-            dto.setQuestionnaireId(ans.getQuestionnaireId());
-            dto.setQuestionnaireTitle(
-                    questionnaireMap.get(ans.getQuestionnaireId()) != null
-                            ? questionnaireMap.get(ans.getQuestionnaireId()).getTitle()
-                            : null
-            );
-            UserInfo ansUser = userMap.get(ans.getUserId());
-            dto.setUserPhone(ansUser != null ? ansUser.getPhone() : null);
-            dto.setAnswerTime(ans.getCreateTime());
-            return dto;
-        }).collect(Collectors.toList());
-        log.info("DTO: {}", dtoList);
+    // 1. 查用户ID(如果传了手机号才查)
+    Long userId = null;
+    if (StringUtils.isNotBlank(query.getUserPhone())) {
+        UserInfo user = userService.getOne(
+                new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, query.getUserPhone())
+        );
+        if (user == null) return emptyPage(query);
+        userId = user.getUserId();
+        log.info("用户ID: {}", userId);
+    }
 
-        // 7. 封装分页结果
-        PageRecord<UserQuestionnaireListDTO> result = new PageRecord<>();
-        result.setRows(dtoList);
-        result.setTotal((long) total);
-        result.setPageNum(query.getPageNo());
-        result.setPageSize(query.getPageSize());
-        result.setOutTotalPageNum(true);
-        result.setTotalPageNum((total + query.getPageSize() - 1) / query.getPageSize());
+    // 2. 使用自定义SQL进行去重分页查询
+    Page<Answer> pageRequest = new Page<>(query.getPageNo(), query.getPageSize());
+    Page<Answer> pageResult = answerService.selectDistinctUserQuestionnaires(pageRequest, userId, query.getQuestionnaireId());
+    List<Answer> pageAnswers = pageResult.getRecords();
+
+    // 3. 查询问卷信息
+    Set<Long> questionnaireIds = pageAnswers.stream()
+            .map(Answer::getQuestionnaireId).collect(Collectors.toSet());
+    Map<Long, Questionnaire> questionnaireMap = questionnaireService.listByIds(questionnaireIds)
+            .stream().collect(Collectors.toMap(Questionnaire::getQuestionnaireId, q -> q));
+    log.info("问卷信息: {}", questionnaireMap);
+
+    // 3.1 查询用户信息(所有答题用户)
+    Set<Long> userIds = pageAnswers.stream().map(Answer::getUserId).collect(Collectors.toSet());
+    Map<Long, UserInfo> userMap = userService.listByIds(userIds)
+            .stream().collect(Collectors.toMap(UserInfo::getUserId, u -> u));
+    log.info("用户信息: {}", userMap);
+
+    // 4. 组装 DTO
+    List<UserQuestionnaireListDTO> dtoList = pageAnswers.stream().map(ans -> {
+        UserQuestionnaireListDTO dto = new UserQuestionnaireListDTO();
+        dto.setQuestionnaireId(ans.getQuestionnaireId());
+        dto.setQuestionnaireTitle(
+                questionnaireMap.get(ans.getQuestionnaireId()) != null
+                        ? questionnaireMap.get(ans.getQuestionnaireId()).getTitle()
+                        : null
+        );
+        UserInfo ansUser = userMap.get(ans.getUserId());
+        dto.setUserPhone(ansUser != null ? ansUser.getPhone() : null);
+        dto.setAnswerTime(ans.getCreateTime());
+        return dto;
+    }).collect(Collectors.toList());
+    log.info("DTO: {}", dtoList);
+
+    // 5. 封装分页结果
+    PageRecord<UserQuestionnaireListDTO> result = new PageRecord<>();
+    result.setRows(dtoList);
+    result.setTotal(pageResult.getTotal());
+    result.setPageNum(query.getPageNo());
+    result.setPageSize(query.getPageSize());
+    result.setOutTotalPageNum(true);
+    result.setTotalPageNum((int) ((pageResult.getTotal() + query.getPageSize() - 1) / query.getPageSize()));
+
+    return result;
+}
 
-        return result;
-    }
 
     @Override
     public UserQuestionnaireDetailsDTO queryUserQuestionnaireDetails(UserQuestionnaireDetailsParam param) {

+ 23 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/AnswerMapper.java

@@ -1,8 +1,11 @@
 package com.hfln.portal.infrastructure.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.hfln.portal.infrastructure.po.Answer;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 
 /**
@@ -10,4 +13,24 @@ import org.apache.ibatis.annotations.Mapper;
  */
 @Mapper
 public interface AnswerMapper extends BaseMapper<Answer> {
+
+
+    /**
+     * 查询用户问卷列表(去重)
+     * @param page 分页参数
+     * @param userId 用户ID
+     * @param questionnaireId 问卷ID
+     * @return 分页结果
+     */
+    @Select("<script>" +
+            "SELECT * FROM (" +
+            "  SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id, questionnaire_id ORDER BY create_time DESC) as rn" +
+            "  FROM answer" +
+            "  WHERE 1=1" +
+            "  <if test='userId != null'>AND user_id = #{userId}</if>" +
+            "  <if test='questionnaireId != null'>AND questionnaire_id = #{questionnaireId}</if>" +
+            ") t WHERE rn = 1" +
+            " ORDER BY create_time DESC" +
+            "</script>")
+    Page<Answer> selectDistinctUserQuestionnaires(Page<Answer> page, @Param("userId") Long userId, @Param("questionnaireId") Long questionnaireId);
 }

+ 11 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/AnswerService.java

@@ -1,7 +1,18 @@
 package com.hfln.portal.infrastructure.service;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.hfln.portal.infrastructure.po.Answer;
 
 public interface AnswerService extends IService<Answer> {
+
+    /**
+ * 查询用户问卷列表(去重)
+ * @param page 分页参数
+ * @param userId 用户ID
+ * @param questionnaireId 问卷ID
+ * @return 分页结果
+ */
+Page<Answer> selectDistinctUserQuestionnaires(Page<Answer> page, Long userId, Long questionnaireId);
+
 }

+ 7 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/AnswerServiceImpl.java

@@ -1,6 +1,7 @@
 package com.hfln.portal.infrastructure.service.impl;
 
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.hfln.portal.infrastructure.mapper.AnswerMapper;
 import com.hfln.portal.infrastructure.po.Answer;
@@ -11,4 +12,10 @@ import org.springframework.stereotype.Service;
 @Service
 @Slf4j
 public class AnswerServiceImpl extends ServiceImpl<AnswerMapper, Answer> implements AnswerService {
+
+    @Override
+    public Page<Answer> selectDistinctUserQuestionnaires(Page<Answer> page, Long userId, Long questionnaireId) {
+        return this.baseMapper.selectDistinctUserQuestionnaires(page, userId, questionnaireId);
+    }
+
 }