feat:添加资源学习情况导出-考试信息接口,支持导出单个课程单次考试的所有学生信息及考试信息

This commit is contained in:
miaowenbo
2025-11-27 13:45:36 +08:00
parent c8c093ffd9
commit beae204174
3 changed files with 153 additions and 8 deletions

View File

@@ -42,6 +42,7 @@ import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -336,7 +337,7 @@ public class StudyCourseApi extends ApiBaseController{
exportMap.put("学习进度", "学习进度");
// 2.查询课程的所有考试答卷信息拼接动态表头XXX考试成绩
// 注意:这里的考试信息每个学生每门课程只有一条,实际数据可能多次考试,因此根据考试配置中的(最高分/最新数据)获取对应列表数据即可(本次新增接口)
List<StudyExam> studyExams = studyExamService.getByCourseId(courseId);
List<StudyExam> studyExams = studyExamService.getByCourseIdAndContentId(courseId, null);
// courseExam和studyExam表都有testName字段查看现有逻辑后选择studyExam表的testName字段作为考试名称
// 因为需求没提因此查询按默认id排序
// 查询学习记录接口,学习记录默认存储获取最高/最新的分数,备用
@@ -1233,13 +1234,13 @@ public class StudyCourseApi extends ApiBaseController{
}
try {
// 查询当前课程的考试信息
List<StudyExam> studyExams = studyExamService.getByCourseId(courseId);
List<StudyExam> studyExams = studyExamService.getByCourseIdAndContentId(courseId, null);
// 空值校验
if (studyExams == null || studyExams.isEmpty()) {
return success(new PageList<>());
}
List<String> studyCourseItemIds = studyExams.stream().map(StudyExam::getStudyItemId).collect(Collectors.toList());
// 分页查询资源学习信息
// 分页查询资源学习信息(只查询有考试信息的部分)
PageList<StudyCourseItem> rs = studyService.findItemPage(pager.getPageIndex(), pager.getPageSize(), studyCourseItemIds, contentId, courseId, name, status);
// 拼接考试信息
List<StudyCourseItem> studyCourseItems = rs.getList();
@@ -1256,6 +1257,137 @@ public class StudyCourseApi extends ApiBaseController{
}
}
/**
* 资源学习情况导出-考试信息
* 导出单个考试信息
*
* @param courseId 课程id
* @param contentId 内容id
*/
@RequestMapping(value = "/contents-exam-export", method = {RequestMethod.GET, RequestMethod.POST})
public void findPageExam(String courseId, String contentId, HttpServletResponse response) {
if (StringUtils.isBlank(courseId)) {
log.error("【资源学习情况导出-考试信息】课程id不能为空");
return;
}
if (StringUtils.isBlank(contentId)) {
log.error("【资源学习情况导出-考试信息】内容id不能为空");
return;
}
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
LinkedHashMap<String, String> exportMap = new LinkedHashMap<>();
// 1.拼接固定表头(课程名称、资源名称、姓名、工号、部门、考试状态、考试成绩、完成时间)
// 获取逻辑课程名称从studyCourse表获取资源名称从courseContent表获取姓名、工号、部门从用户接口获取考试状态、考试成绩、完成时间从studyExam表获取
exportMap.put("课程名称", "课程名称");
exportMap.put("资源名称", "资源名称");
exportMap.put("姓名", "姓名");
exportMap.put("工号", "工号");
exportMap.put("部门", "部门");
exportMap.put("考试状态", "考试状态");
exportMap.put("考试成绩", "考试成绩");
exportMap.put("完成时间", "完成时间");
// 查询课程名称
StudyCourse studyCourse = new StudyCourse();
studyCourse.setCourseId(courseId);
List<StudyCourse> studyCourses = service.findList(studyCourse, null, null);
String courseName;
if (studyCourses != null && !studyCourses.isEmpty()) {
courseName = studyCourses.get(0).getCourseName();
// 查询资源名称
List<CourseContent> courseContents = contentService.getByCourseId(courseId);
String contentName = "";
if (courseContents != null && !courseContents.isEmpty()) {
for (CourseContent cc : courseContents) {
if (contentId.equals(cc.getId())) {
contentName = cc.getContentName();
break;
}
}
}
// 查询特定考试信息(这里同一个学员,同一次考试可以导出多次考试信息)
List<StudyExam> studyExams = studyExamService.getByCourseIdAndContentId(courseId, contentId);
// 通过studyExams中的人员id集合(去重),调用用户中心接口获取人员信息,填充部门字段
Set<String> userIds = studyExams.stream().map(StudyExam::getStudentId).filter(Objects::nonNull).collect(Collectors.toSet());
List<UserSimpleVo> userList = new ArrayList<>();
if (!userIds.isEmpty()) {
// 调用用户中心接口
List<UserSimpleVo> userSimpleVos = outsideService.findByIds(new ArrayList<>(userIds));
if (userSimpleVos != null && !userSimpleVos.isEmpty()) {
userList.addAll(userSimpleVos);
} else {
log.error("【资源学习情况导出-考试信息】用户信息查询失败查询boe人员表作为兜底方案");
// 查询boe的人员表作为兜底方案
for (String userId : userIds) {
// 用户信息也是redis获取的
User userInfo = userService.get(userId);
if (userInfo != null) {
UserSimpleVo userSimpleVo = new UserSimpleVo();
userSimpleVo.setAid(userInfo.getId());
userSimpleVo.setCode(userInfo.getUserNo());
// 获取部门信息
String departName = organizationService.getName(userInfo.getDepartId());
userSimpleVo.setOrgInfo(StringUtils.isNotBlank(departName) ? departName : "");
userList.add(userSimpleVo);
log.info("【资源学习情况导出-考试信息】查询boe人员表用户id{}", userId);
} else {
log.error("【资源学习情况导出-考试信息】用户信息查询boe人员表失败用户id{}", userId);
}
}
}
}
// 将考试信息与用户信息拼接为map
String finalContentName = contentName;
List<Map<String, Object>> dataList = studyExams.stream().map(exam -> {
Map<String, Object> map = new HashMap<>();
// 拼接基本信息
map.put("课程名称", courseName);
map.put("资源名称", finalContentName);
map.put("姓名", exam.getStudentName());
// 工号和部门信息需要从用户信息中获取
String userNo = "";
String orgInfo = "";
for (UserSimpleVo user : userList) {
if (exam.getStudentId().equals(user.getAid())) {
userNo = user.getCode();
orgInfo = user.getOrgInfo();
break;
}
}
map.put("工号", userNo);
map.put("部门", orgInfo);
// 考试状态需要转换(根据成绩和及格线判断)
String examStatus = "";
if (exam.getScore() != null && exam.getPassLine() != null) {
examStatus = exam.getScore() >= exam.getPassLine() ? "通过" : "未通过";
}
map.put("考试状态", examStatus);
// 考试成绩
map.put("考试成绩", exam.getScore());
// 完成时间
map.put("完成时间", exam.getEndTime());
return map;
}).collect(Collectors.toList());
// 4.拼接消息类型和响应头信息
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment;filename=ExamRecord.xlsx");
// 5.调用动态列excel导出接口
ExportsExcelSenderUtil.exportDynamic(exportMap, dataList, outputStream, "yyyy-MM-dd HH:mm:ss");
}
} catch (Exception e) {
log.error("【资源学习情况导出-考试信息】导出资源学习情况错误:{}", e.getMessage());
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
log.error("【资源学习情况导出-考试信息】关闭输出流失败:{}", e.getMessage());
}
}
}
}
@RequestMapping(value="/study-course-content",method = {RequestMethod.GET,RequestMethod.POST})
public JsonResponse<StudyCourseItem> findStudyCourseItem(String studyId,String contentId){
if(StringUtils.isBlank(studyId)){

View File

@@ -31,13 +31,15 @@ public interface IStudyExamService {
List<StudyExam> getByStudyIdAndContentId(String studyId,String contentId);
/**
* 根据课程id得到对应的考试记录
* 根据课程id或者内容id得到对应的考试记录
* 课程id必填内容id选填
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 考试记录集合
*/
List<StudyExam> getByCourseId(String courseId);
List<StudyExam> getByCourseIdAndContentId(String courseId, String contentId);
void correctStstus(String courseId);
}

View File

@@ -2,6 +2,7 @@ package com.xboe.school.study.service.impl;
import com.xboe.common.OrderCondition;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.IFieldFilter;
import com.xboe.core.orm.QueryBuilder;
import com.xboe.core.orm.UpdateBuilder;
import com.xboe.module.course.dao.CourseContentDao;
@@ -23,6 +24,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@@ -159,15 +161,24 @@ public class StudyExamServiceImpl implements IStudyExamService{
}
/**
* 根据课程id得到对应的考试记录
* 根据课程id或者内容id得到对应的考试记录
* 课程id必填内容id选填
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 考试记录集合
*/
@Override
public List<StudyExam> getByCourseId(String courseId) {
return dao.findList(OrderCondition.desc("id"), FieldFilters.eq("courseId", courseId));
public List<StudyExam> getByCourseIdAndContentId(String courseId, String contentId) {
if (courseId == null) {
throw new IllegalArgumentException("courseId must not be null");
}
IFieldFilter filter = FieldFilters.eq("courseId", courseId);
if (contentId != null) {
filter = FieldFilters.and(filter, FieldFilters.eq("contentId", contentId));
}
return dao.findList(OrderCondition.desc("id"), filter);
}
@Override