1.【FCJDFDXTXS-120】导出学习记录学习完成时间为空时,学习结束时间应为学生最后一次学习时间
2.【FCJDFDXTXS-115、138、140】修改课程名称的展示逻辑,修改为章名称+节名称,详细逻辑如下:默认取sectionName-contentName,如果sectionName不存在,就用courseName-contentName
This commit is contained in:
miaowenbo
2025-12-15 20:45:09 +08:00
parent 4e0afbb19c
commit eb684e5fed
9 changed files with 197 additions and 30 deletions

View File

@@ -1,16 +1,15 @@
package com.xboe.module.course.entity;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 课程内容表
* */
@@ -94,6 +93,13 @@ public class CourseContent extends BaseEntity {
@Transient
private Integer status;
/**
* 展示名称(章名+节名,如果没有章节,则为课程名+节名)
* 25.12.15新增
*/
@Transient
private String displayName;
public CourseContent() {
}

View File

@@ -56,6 +56,14 @@ public interface ICourseContentService{
*/
List<CourseContent> getByCourseId(String courseId);
/**
* 根据id集合得到内容
*
* @param ids id集合
* @return 课程内容集合
*/
List<CourseContent> getByIds(List<String> ids);
/**
* 根据课程id、章节id得到课程所有目录即章节分页顺序按orderIndex 从小到大的顺序
* 25.11.26新增

View File

@@ -1,9 +1,9 @@
package com.xboe.module.course.service;
import java.util.List;
import com.xboe.module.course.entity.CourseSection;
import java.util.List;
/**
* 课程章节目录
* */
@@ -27,4 +27,13 @@ public interface ICourseSectionService {
*/
List<CourseSection> getByCourseId(String courseId);
/**
* 根据id批量查询课程章节目录
*
* @param ids id集合
* @return 课程章节目录集合
*/
List<CourseSection> getByIds(List<String> ids);
}

View File

@@ -142,6 +142,14 @@ public class CourseContentServiceImpl implements ICourseContentService {
return list;
}
@Override
public List<CourseContent> getByIds(List<String> ids) {
if (ids == null || ids.isEmpty()) {
return new ArrayList<>();
}
return ccDao.findList(OrderCondition.asc("sortIndex"), FieldFilters.in("id", ids), FieldFilters.eq("deleted", false));
}
/**
* 根据课程id、章节id得到课程所有目录即章节分页顺序按orderIndex 从小到大的顺序
* 25.11.26新增

View File

@@ -1,17 +1,16 @@
package com.xboe.module.course.service.impl;
import java.util.List;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import com.xboe.common.OrderCondition;
import com.xboe.core.orm.FieldFilters;
import com.xboe.module.course.dao.CourseSectionDao;
import com.xboe.module.course.entity.CourseSection;
import com.xboe.module.course.service.ICourseSectionService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@@ -35,6 +34,20 @@ public class CourseSectionServiceImpl implements ICourseSectionService {
return courseSectionDao.findList(OrderCondition.asc("orderIndex"), FieldFilters.eq("courseId", courseId));
}
/**
* 根据id批量查询课程章节目录
*
* @param ids id集合
* @return 课程章节目录集合
*/
@Override
public List<CourseSection> getByIds(List<String> ids) {
if (ids == null || ids.isEmpty()) {
return new ArrayList<>();
}
return courseSectionDao.findList(OrderCondition.asc("orderIndex"), FieldFilters.in("id", ids));
}
// @Override
// public void copyCourseSection(String id) {
// String sql="insert into boe_course_section(course_id,description,name," +

View File

@@ -452,7 +452,8 @@ public class StudyCourseApi extends ApiBaseController{
// 这个开始时间已经弃置了,不过先用再说(有值)
map.put("学习开始时间", studyCourse1.getStartTime());
// 结束时间为空的,说明还没学习结束(有值)
map.put("学习结束时间", studyCourse1.getFinishTime());
// 25.12.15修改,需求调整:如果结束时间为空,改为获取最后一次学习时间
map.put("学习结束时间", studyCourse1.getFinishTime() == null ? studyCourse1.getLastTime() : studyCourse1.getFinishTime());
// 学习时长(保留两位小数):
map.put("学习时长", studyCourse1.getTotalDuration() == null ? null : String.format("%.2f", studyCourse1.getTotalDuration() / 60.0));
// 学习状态需要转换
@@ -1356,11 +1357,14 @@ public class StudyCourseApi extends ApiBaseController{
courseName = studyCourses.get(0).getCourseName();
// 查询资源名称
List<CourseContent> courseContents = contentService.getByCourseId(courseId);
String contentName = "";
String displayName = "";
if (courseContents != null && !courseContents.isEmpty()) {
// 25.12.15新增,修改课程名称的展示逻辑,修改为章名称+节名称
// 详细逻辑如下默认取sectionName-contentName如果sectionName不存在就用courseName-contentName
studyService.setContentDisplayName(courseContents);
for (CourseContent cc : courseContents) {
if (contentId.equals(cc.getId())) {
contentName = cc.getContentName();
displayName = cc.getDisplayName();
break;
}
}
@@ -1398,7 +1402,7 @@ public class StudyCourseApi extends ApiBaseController{
}
}
// 将考试信息与用户信息拼接为map
String finalContentName = contentName;
String finalContentName = displayName;
List<Map<String, Object>> dataList = studyExams.stream().map(exam -> {
Map<String, Object> map = new HashMap<>();
// 拼接基本信息
@@ -1494,11 +1498,14 @@ public class StudyCourseApi extends ApiBaseController{
}
// 查询资源名称
List<CourseContent> courseContents = contentService.getByCourseId(courseId);
String contentName = "";
String displayName = "";
if (courseContents != null && !courseContents.isEmpty()) {
for (CourseContent cc : courseContents) {
// 25.12.15新增,修改课程名称的展示逻辑,修改为章名称+节名称
// 详细逻辑如下默认取sectionName-contentName如果sectionName不存在就用courseName-contentName
studyService.setContentDisplayName(courseContents);
if (contentId.equals(cc.getId())) {
contentName = cc.getContentName();
displayName = cc.getDisplayName();
break;
}
}
@@ -1536,7 +1543,7 @@ public class StudyCourseApi extends ApiBaseController{
}
}
// 3.将作业信息与用户信息拼接为map
String finalContentName = contentName;
String finalContentName = displayName;
List<Map<String, Object>> dataList = studyHomeWorks.stream().map(hw -> {
Map<String, Object> map = new HashMap<>();
// 拼接基本信息

View File

@@ -137,6 +137,13 @@ public class StudyCourseItem extends IdEntity {
@Transient
private BigDecimal progressVideo;
/**
* 展示名称(章名+节名,如果没有章节,则为课程名+节名)
* 25.12.15新增
*/
@Transient
private String displayName;
/**
* 考试记录集合
* 仅在资源学习情况分页查询-考试信息接口使用

View File

@@ -1,6 +1,7 @@
package com.xboe.school.study.service;
import com.xboe.common.PageList;
import com.xboe.module.course.entity.CourseContent;
import com.xboe.school.study.dto.StudyContentDto;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudyTime;
@@ -101,6 +102,14 @@ public interface IStudyService {
*/
PageList<StudyCourseItem> findItemPage(int pageIndex, int pageSize, List<String> ids, String contentId, String courseId, String name, Integer status);
/**
* 为courseContents列表设置展示名称章名+节名 或 课程名+节名)
* 25.12.15新增
*
* @param courseContents 课程内容集合
*/
void setContentDisplayName(List<CourseContent> courseContents);
List<StudyCourseItem> getList(String courseId, String contentId, String name, Integer status);
void updateStudyCourseItemLastTime(String studyContentId, int lastStudyTime, LocalDateTime timestamp);

View File

@@ -8,12 +8,17 @@ import com.xboe.core.orm.QueryBuilder;
import com.xboe.core.orm.UpdateBuilder;
import com.xboe.module.course.entity.Course;
import com.xboe.module.course.entity.CourseContent;
import com.xboe.module.course.entity.CourseSection;
import com.xboe.module.course.service.ICourseContentService;
import com.xboe.module.course.service.ICourseSectionService;
import com.xboe.school.study.dao.StudyCourseDao;
import com.xboe.school.study.dao.StudyCourseItemDao;
import com.xboe.school.study.dao.StudyTimeDao;
import com.xboe.school.study.dto.StudyContentDto;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudyTime;
import com.xboe.school.study.service.IStudyCourseService;
import com.xboe.school.study.service.IStudyService;
import com.xboe.standard.enums.BoedxContentType;
import com.xboe.system.user.dao.UserDao;
@@ -27,10 +32,10 @@ import javax.transaction.Transactional;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@Service
public class StudyServiceImpl implements IStudyService{
@@ -46,6 +51,16 @@ public class StudyServiceImpl implements IStudyService{
@Autowired
UserDao userDao;
@Autowired
IStudyCourseService studyCourseService;
@Autowired
ICourseContentService courseContentService;
@Autowired
ICourseSectionService courseSectionService;
@Autowired
StringRedisTemplate redisTemplate;
@@ -272,7 +287,7 @@ public class StudyServiceImpl implements IStudyService{
}
}
// 未传输status的情况查询所有资源学习情况数据
String sql = "select a.id, a.course_id, a.course_name, a.aname, " + "IFNULL(b.finish_time, '0') as finish_time, IFNULL(b.progress, 0) as progress, IFNULL(b.status, 1) as status " + ",b.score,b.item_id,b.aid " + "from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "'" + (StringUtils.isBlank(name) ? "" : "and aname like '%" + name + "%'") + ") a " + "inner join " + "(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, item.id as item_id,item.finish_time, item.progress, item.status,MAX(item.score) score,item.aid " + "from boe_study_course bsc left join boe_study_course_item item on item.course_id = bsc.course_id and item.study_id = bsc.id " + "where bsc.course_id = '" + courseId + "'" +
String sql = "select a.id, a.course_id, a.course_name, a.aname, " + "IFNULL(b.finish_time, '0') as finish_time, IFNULL(b.progress, 0) as progress, IFNULL(b.status, 1) as status " + ",b.score,b.item_id,b.aid,b.content_id " + "from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "'" + (StringUtils.isBlank(name) ? "" : "and aname like '%" + name + "%'") + ") a " + "inner join " + "(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, item.id as item_id,item.finish_time, item.progress, item.status,MAX(item.score) score,item.aid,item.content_id " + "from boe_study_course bsc left join boe_study_course_item item on item.course_id = bsc.course_id and item.study_id = bsc.id " + "where bsc.course_id = '" + courseId + "'" +
(StringUtils.isBlank(contentId) ? "" : "and item.content_id = '" + contentId + "'") +
(StringUtils.isBlank(name) ? "" : "and item.aname like '%" + name +"%'") + " group by bsc.id) b " +
"on a.course_id = b.course_id and a.id = b.id " +
@@ -306,17 +321,102 @@ public class StudyServiceImpl implements IStudyService{
sc.setScore(Float.valueOf(objs[7].toString()));
}
// 25.12.5新增补全aid查询
if (objs[8] != null) {
sc.setAid(String.valueOf(objs[8].toString()));
if (objs[9] != null) {
sc.setAid(String.valueOf(objs[9].toString()));
}
// 25.12.15新增补全contentId查询
if (objs[10] != null) {
sc.setContentId(String.valueOf(objs[10].toString()));
}
item.add(sc);
}
// 25.12.15新增,修改课程名称的展示逻辑,修改为章名称+节名称
// 详细逻辑如下默认取sectionName-contentName如果sectionName不存在就用courseName-contentName
List<String> contentIds = item.stream().map(StudyCourseItem::getContentId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
if (!contentIds.isEmpty()) {
List<CourseContent> contentList = courseContentService.getByIds(contentIds);
if (contentList != null) {
setContentDisplayName(contentList);
// 根据contentId将展示名称映射到item
for (StudyCourseItem studyCourseItem : item) {
studyCourseItem.setDisplayName(contentList.stream().filter(content -> content.getId().equals(studyCourseItem.getContentId())).findFirst().map(CourseContent::getDisplayName).orElse(null));
}
}
}
// 查询当前
PageList<StudyCourseItem> pageList = new PageList<>(item);
pageList.setCount(totalCount);
pageList.setPageSize(pageSize);
pageList.setList(item);
return pageList;
}
/**
* 为 CourseContent 列表设置展示名称(章名+节名 或 课程名+节名)
* 基于 25.12.15 的展示逻辑:优先使用 章名+节名,章不存在则退化为 课程名+节名
*
* @param courseContents 节内容列表(即“节”)
*/
@Override
public void setContentDisplayName(List<CourseContent> courseContents) {
if (courseContents == null || courseContents.isEmpty()) {
return;
}
// 1. 提取唯一 sectionId 和 courseId
Set<String> sectionIds = new HashSet<>();
Set<String> courseIds = new HashSet<>();
for (CourseContent content : courseContents) {
if (content.getCsectionId() != null) {
sectionIds.add(content.getCsectionId());
}
if (content.getCourseId() != null) {
courseIds.add(content.getCourseId());
}
}
// 2. 批量查询 CourseSection
Map<String, CourseSection> sectionMap = new HashMap<>();
if (!sectionIds.isEmpty()) {
List<CourseSection> sections = courseSectionService.getByIds(new ArrayList<>(sectionIds));
if (sections != null) {
sectionMap = sections.stream().collect(Collectors.toMap(CourseSection::getId, Function.identity(), (e1, e2) -> e1));
}
}
// 3. 批量查询 StudyCourse
Map<String, StudyCourse> courseMap = new HashMap<>();
if (!courseIds.isEmpty()) {
List<StudyCourse> courses = studyCourseService.findByIds(new ArrayList<>(courseIds));
if (courses != null) {
courseMap = courses.stream().collect(Collectors.toMap(StudyCourse::getId, Function.identity(), (e1, e2) -> e1));
}
}
// 4. 为每个 CourseContent 设置 displayName
for (CourseContent courseContent : courseContents) {
String sectionName = null;
String courseName = "空课程名称";
String contentName = (courseContent.getContentName() != null) ? courseContent.getContentName() : "空内容名称";
// 尝试获取章名
if (courseContent.getCsectionId() != null) {
CourseSection section = sectionMap.get(courseContent.getCsectionId());
if (section != null && section.getName() != null) {
sectionName = section.getName();
}
}
// 设置 displayName优先章+节,否则课程+节
String displayName;
if (sectionName != null) {
displayName = sectionName + "-" + contentName;
} else {
// 获取课程名
if (courseContent.getCourseId() != null) {
StudyCourse course = courseMap.get(courseContent.getCourseId());
if (course != null && course.getCourseName() != null) {
courseName = course.getCourseName();
}
}
displayName = courseName + "-" + contentName;
}
courseContent.setDisplayName(displayName);
}
}
@Override