فهرست منبع

1、新增直播完课答题记录

yys 3 روز پیش
والد
کامیت
5abe326188

+ 56 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveCompletionAnswerRecordController.java

@@ -0,0 +1,56 @@
+package com.fs.live.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.live.param.LiveCompletionAnswerRecordParam;
+import com.fs.live.service.ILiveCompletionAnswerRecordService;
+import com.fs.live.vo.LiveCompletionAnswerRecordListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 直播完课答题记录Controller
+ */
+@RestController
+@RequestMapping("/live/liveCompletionAnswerRecord")
+public class LiveCompletionAnswerRecordController extends BaseController {
+
+    @Autowired
+    private ILiveCompletionAnswerRecordService liveCompletionAnswerRecordService;
+
+    @PreAuthorize("@ss.hasPermi('live:liveCompletionAnswerRecord:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveCompletionAnswerRecordParam param) {
+        startPage();
+        List<LiveCompletionAnswerRecordListVO> list =
+                liveCompletionAnswerRecordService.selectLiveCompletionAnswerRecordList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('live:liveCompletionAnswerRecord:export')")
+    @Log(title = "直播完课答题记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(LiveCompletionAnswerRecordParam param) {
+        List<LiveCompletionAnswerRecordListVO> list =
+                liveCompletionAnswerRecordService.selectLiveCompletionAnswerRecordList(param);
+        ExcelUtil<LiveCompletionAnswerRecordListVO> util =
+                new ExcelUtil<>(LiveCompletionAnswerRecordListVO.class);
+        return util.exportExcel(list, "直播完课答题记录");
+    }
+
+    @PreAuthorize("@ss.hasPermi('live:liveCompletionAnswerRecord:query')")
+    @GetMapping("/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(liveCompletionAnswerRecordService.selectLiveCompletionAnswerRecordById(id));
+    }
+}

+ 47 - 0
fs-service/src/main/java/com/fs/live/domain/LiveCompletionAnswerRecord.java

@@ -0,0 +1,47 @@
+package com.fs.live.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 直播完课答题记录对象 live_completion_answer_record
+ */
+@Data
+@TableName("live_completion_answer_record")
+public class LiveCompletionAnswerRecord implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @Excel(name = "直播ID")
+    private Long liveId;
+
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    @Excel(name = "用户名称")
+    private String userName;
+
+    /** 是否全部正确 0否 1是 */
+    @Excel(name = "是否全部正确", readConverterExp = "0=否,1=是")
+    private Integer isRight;
+
+    /** 用户作答原始 JSON */
+    private String answerJson;
+
+    /** 题目及作答明细 JSON */
+    private String questionJson;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "答题时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+}

+ 20 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveCompletionAnswerRecordMapper.java

@@ -0,0 +1,20 @@
+package com.fs.live.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.live.domain.LiveCompletionAnswerRecord;
+import com.fs.live.param.LiveCompletionAnswerRecordParam;
+import com.fs.live.vo.LiveCompletionAnswerRecordListVO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 直播完课答题记录Mapper
+ */
+@Mapper
+public interface LiveCompletionAnswerRecordMapper extends BaseMapper<LiveCompletionAnswerRecord> {
+
+    List<LiveCompletionAnswerRecordListVO> selectLiveCompletionAnswerRecordList(LiveCompletionAnswerRecordParam param);
+
+    LiveCompletionAnswerRecordListVO selectLiveCompletionAnswerRecordById(Long id);
+}

+ 27 - 0
fs-service/src/main/java/com/fs/live/param/LiveCompletionAnswerRecordParam.java

@@ -0,0 +1,27 @@
+package com.fs.live.param;
+
+import lombok.Data;
+
+/**
+ * 直播完课答题记录查询参数
+ */
+@Data
+public class LiveCompletionAnswerRecordParam {
+
+    private Integer pageNum;
+
+    private Integer pageSize;
+
+    private Long liveId;
+
+    private Long userId;
+
+    private String userName;
+
+    /** 是否全部正确 0否 1是 */
+    private Integer isRight;
+
+    private String beginTime;
+
+    private String endTime;
+}

+ 33 - 0
fs-service/src/main/java/com/fs/live/service/ILiveCompletionAnswerRecordService.java

@@ -0,0 +1,33 @@
+package com.fs.live.service;
+
+import com.fs.live.param.LiveCompletionAnswerRecordParam;
+import com.fs.live.param.LiveCompletionCouponAnswerItem;
+import com.fs.live.vo.LiveCompletionAnswerDetailVO;
+import com.fs.live.vo.LiveCompletionAnswerRecordListVO;
+
+import java.util.List;
+
+/**
+ * 直播完课答题记录Service
+ */
+public interface ILiveCompletionAnswerRecordService {
+
+    /**
+     * 查询完课答题记录列表
+     */
+    List<LiveCompletionAnswerRecordListVO> selectLiveCompletionAnswerRecordList(LiveCompletionAnswerRecordParam param);
+
+    /**
+     * 查询完课答题记录详情
+     */
+    LiveCompletionAnswerRecordListVO selectLiveCompletionAnswerRecordById(Long id);
+
+    /**
+     * 保存完课答题记录(/app/live/completion/coupon/answer 提交时调用)
+     *
+     * @return 记录主键ID
+     */
+    Long saveAnswerRecord(Long liveId, Long userId, boolean allCorrect,
+                          List<LiveCompletionCouponAnswerItem> answers,
+                          List<LiveCompletionAnswerDetailVO> questionDetails);
+}

+ 71 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveCompletionAnswerRecordServiceImpl.java

@@ -0,0 +1,71 @@
+package com.fs.live.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.domain.FsUser;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.live.domain.LiveCompletionAnswerRecord;
+import com.fs.live.mapper.LiveCompletionAnswerRecordMapper;
+import com.fs.live.param.LiveCompletionAnswerRecordParam;
+import com.fs.live.param.LiveCompletionCouponAnswerItem;
+import com.fs.live.service.ILiveCompletionAnswerRecordService;
+import com.fs.live.vo.LiveCompletionAnswerDetailVO;
+import com.fs.live.vo.LiveCompletionAnswerRecordListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 直播完课答题记录Service业务层
+ */
+@Service
+public class LiveCompletionAnswerRecordServiceImpl implements ILiveCompletionAnswerRecordService {
+
+    @Autowired
+    private LiveCompletionAnswerRecordMapper liveCompletionAnswerRecordMapper;
+
+    @Autowired
+    private FsUserMapper fsUserMapper;
+
+    @Override
+    public List<LiveCompletionAnswerRecordListVO> selectLiveCompletionAnswerRecordList(LiveCompletionAnswerRecordParam param) {
+        return liveCompletionAnswerRecordMapper.selectLiveCompletionAnswerRecordList(param);
+    }
+
+    @Override
+    public LiveCompletionAnswerRecordListVO selectLiveCompletionAnswerRecordById(Long id) {
+        return liveCompletionAnswerRecordMapper.selectLiveCompletionAnswerRecordById(id);
+    }
+
+    @Override
+    public Long saveAnswerRecord(Long liveId, Long userId, boolean allCorrect,
+                                 List<LiveCompletionCouponAnswerItem> answers,
+                                 List<LiveCompletionAnswerDetailVO> questionDetails) {
+        LiveCompletionAnswerRecord record = new LiveCompletionAnswerRecord();
+        record.setLiveId(liveId);
+        record.setUserId(userId);
+        record.setUserName(resolveUserName(userId));
+        record.setIsRight(allCorrect ? 1 : 0);
+        record.setAnswerJson(answers == null ? null : JSON.toJSONString(answers));
+        record.setQuestionJson(questionDetails == null ? null : JSON.toJSONString(questionDetails));
+        record.setCreateTime(DateUtils.getNowDate());
+        liveCompletionAnswerRecordMapper.insert(record);
+        return record.getId();
+    }
+
+    private String resolveUserName(Long userId) {
+        if (userId == null) {
+            return null;
+        }
+        FsUser user = fsUserMapper.selectFsUserByUserId(userId);
+        if (user == null) {
+            return null;
+        }
+        if (StringUtils.isNotEmpty(user.getNickName())) {
+            return user.getNickName();
+        }
+        return user.getPhone();
+    }
+}

+ 44 - 7
fs-service/src/main/java/com/fs/live/service/impl/LiveCompletionCouponServiceImpl.java

@@ -11,6 +11,7 @@ import com.fs.live.param.LiveCompletionCouponAnswerItem;
 import com.fs.live.param.LiveCompletionCouponAnswerParam;
 import com.fs.live.param.LiveCompletionCouponClaimParam;
 import com.fs.live.service.*;
+import com.fs.live.vo.LiveCompletionAnswerDetailVO;
 import com.fs.live.vo.LiveCompletionCouponAnswerResult;
 import com.fs.live.vo.LiveCompletionCouponConfigVO;
 import com.fs.live.vo.LiveCompletionCouponInfoVO;
@@ -72,6 +73,9 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
     @Autowired
     private ILiveConsoleOpLogService liveConsoleOpLogService;
 
+    @Autowired
+    private ILiveCompletionAnswerRecordService liveCompletionAnswerRecordService;
+
     @Override
     public LiveCompletionCouponConfigVO parseCompletionCouponConfig(Live live) {
         LiveCompletionCouponConfigVO vo = new LiveCompletionCouponConfigVO();
@@ -213,11 +217,14 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
         }
 
         List<LiveCompletionCouponAnswerItem> normalizedAnswers = normalizeUserAnswers(param.getAnswers());
-        boolean allCorrect = evaluateAnswers(normalizedAnswers, configuredQuestionIds);
-        saveAnswerRecordToday(liveId, userId, allCorrect);
+        AnswerEvaluation evaluation = evaluateAnswersWithDetails(normalizedAnswers, configuredQuestionIds);
+        saveAnswerRecordToday(liveId, userId, evaluation.isAllCorrect());
+        Long recordId = liveCompletionAnswerRecordService.saveAnswerRecord(
+                liveId, userId, evaluation.isAllCorrect(), normalizedAnswers, evaluation.getDetails());
 
         LiveCompletionCouponAnswerResult result = new LiveCompletionCouponAnswerResult();
-        result.setAllCorrect(allCorrect);
+        result.setAllCorrect(evaluation.isAllCorrect());
+        result.setRecordId(recordId);
         return result;
     }
 
@@ -332,9 +339,10 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
     }
 
     /**
-     * 校验是否答完全部题目,并返回是否全部答对(答错不阻断记录)
+     * 校验是否答完全部题目,并返回是否全部答对及每题明细(答错不阻断记录)
      */
-    private boolean evaluateAnswers(List<LiveCompletionCouponAnswerItem> userAnswers, List<Long> configuredQuestionIds) {
+    private AnswerEvaluation evaluateAnswersWithDetails(List<LiveCompletionCouponAnswerItem> userAnswers,
+                                                        List<Long> configuredQuestionIds) {
         Set<Long> submittedIds = userAnswers.stream()
                 .map(LiveCompletionCouponAnswerItem::getQuestionId)
                 .filter(Objects::nonNull)
@@ -350,16 +358,27 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
                 .collect(Collectors.toMap(LiveQuestionBank::getId, q -> q));
 
         boolean allCorrect = true;
+        List<LiveCompletionAnswerDetailVO> details = new ArrayList<>();
         for (LiveCompletionCouponAnswerItem userAnswer : userAnswers) {
             LiveQuestionBank correctAnswer = correctAnswersMap.get(userAnswer.getQuestionId());
             if (correctAnswer == null || correctAnswer.getStatus() == null || correctAnswer.getStatus() == 0) {
                 throw new BaseException("题目不存在或已停用");
             }
-            if (!isAnswerCorrect(userAnswer.getAnswer(), correctAnswer)) {
+            boolean questionCorrect = isAnswerCorrect(userAnswer.getAnswer(), correctAnswer);
+            if (!questionCorrect) {
                 allCorrect = false;
             }
+
+            LiveCompletionAnswerDetailVO detail = new LiveCompletionAnswerDetailVO();
+            detail.setQuestionId(userAnswer.getQuestionId());
+            detail.setTitle(correctAnswer.getTitle());
+            detail.setType(correctAnswer.getType());
+            detail.setUserAnswer(userAnswer.getAnswer());
+            detail.setCorrectAnswer(correctAnswer.getAnswer());
+            detail.setIsRight(questionCorrect ? 1 : 0);
+            details.add(detail);
         }
-        return allCorrect;
+        return new AnswerEvaluation(allCorrect, details);
     }
 
     private boolean isAnswerCorrect(String userAnswerValue, LiveQuestionBank correctAnswer) {
@@ -698,4 +717,22 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
             this.allCorrect = allCorrect;
         }
     }
+
+    private static class AnswerEvaluation {
+        private final boolean allCorrect;
+        private final List<LiveCompletionAnswerDetailVO> details;
+
+        private AnswerEvaluation(boolean allCorrect, List<LiveCompletionAnswerDetailVO> details) {
+            this.allCorrect = allCorrect;
+            this.details = details;
+        }
+
+        public boolean isAllCorrect() {
+            return allCorrect;
+        }
+
+        public List<LiveCompletionAnswerDetailVO> getDetails() {
+            return details;
+        }
+    }
 }

+ 24 - 0
fs-service/src/main/java/com/fs/live/vo/LiveCompletionAnswerDetailVO.java

@@ -0,0 +1,24 @@
+package com.fs.live.vo;
+
+import lombok.Data;
+
+/**
+ * 完课答题单题明细(落库快照)
+ */
+@Data
+public class LiveCompletionAnswerDetailVO {
+
+    private Long questionId;
+
+    private String title;
+
+    /** 题型 1单选 2多选 */
+    private Long type;
+
+    private String userAnswer;
+
+    private String correctAnswer;
+
+    /** 本题是否答对 0否 1是 */
+    private Integer isRight;
+}

+ 39 - 0
fs-service/src/main/java/com/fs/live/vo/LiveCompletionAnswerRecordListVO.java

@@ -0,0 +1,39 @@
+package com.fs.live.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 直播完课答题记录列表 VO
+ */
+@Data
+public class LiveCompletionAnswerRecordListVO {
+
+    private Long id;
+
+    @Excel(name = "直播ID")
+    private Long liveId;
+
+    @Excel(name = "直播名称")
+    private String liveName;
+
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    @Excel(name = "用户名称")
+    private String userName;
+
+    @Excel(name = "是否全部正确", readConverterExp = "0=否,1=是")
+    private Integer isRight;
+
+    private String answerJson;
+
+    private String questionJson;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "答题时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/live/vo/LiveCompletionCouponAnswerResult.java

@@ -10,4 +10,7 @@ public class LiveCompletionCouponAnswerResult {
 
     /** 是否全部答对 */
     private boolean allCorrect;
+
+    /** 答题记录ID(写入 live_completion_answer_record) */
+    private Long recordId;
 }

+ 62 - 0
fs-service/src/main/resources/mapper/live/LiveCompletionAnswerRecordMapper.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.live.mapper.LiveCompletionAnswerRecordMapper">
+
+    <select id="selectLiveCompletionAnswerRecordList"
+            parameterType="com.fs.live.param.LiveCompletionAnswerRecordParam"
+            resultType="com.fs.live.vo.LiveCompletionAnswerRecordListVO">
+        SELECT
+            r.id,
+            r.live_id AS liveId,
+            l.live_name AS liveName,
+            r.user_id AS userId,
+            r.user_name AS userName,
+            r.is_right AS isRight,
+            r.answer_json AS answerJson,
+            r.question_json AS questionJson,
+            r.create_time AS createTime
+        FROM live_completion_answer_record r
+        LEFT JOIN live l ON l.live_id = r.live_id
+        <where>
+            <if test="liveId != null">
+                AND r.live_id = #{liveId}
+            </if>
+            <if test="userId != null">
+                AND r.user_id = #{userId}
+            </if>
+            <if test="userName != null and userName != ''">
+                AND r.user_name LIKE CONCAT('%', #{userName}, '%')
+            </if>
+            <if test="isRight != null">
+                AND r.is_right = #{isRight}
+            </if>
+            <if test="beginTime != null and beginTime != ''">
+                AND r.create_time &gt;= #{beginTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND r.create_time &lt;= #{endTime}
+            </if>
+        </where>
+        ORDER BY r.create_time DESC
+    </select>
+
+    <select id="selectLiveCompletionAnswerRecordById" parameterType="Long"
+            resultType="com.fs.live.vo.LiveCompletionAnswerRecordListVO">
+        SELECT
+            r.id,
+            r.live_id AS liveId,
+            l.live_name AS liveName,
+            r.user_id AS userId,
+            r.user_name AS userName,
+            r.is_right AS isRight,
+            r.answer_json AS answerJson,
+            r.question_json AS questionJson,
+            r.create_time AS createTime
+        FROM live_completion_answer_record r
+        LEFT JOIN live l ON l.live_id = r.live_id
+        WHERE r.id = #{id}
+    </select>
+
+</mapper>

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/live/LiveCompletionCouponController.java

@@ -34,7 +34,7 @@ public class LiveCompletionCouponController extends AppBaseController {
     }
 
     /**
-     * 提交今日问题(仅记录答题结果,不发券)
+     * 提交今日问题(校验答题结果并写入 live_completion_answer_record,不发券)
      */
     @PostMapping("/answer")
     @RepeatSubmit