Compare commits

...

18 Commits

Author SHA1 Message Date
yangxinyu
240d4725e6 feat:修改资源学习列表count数计算 2025-12-08 09:16:16 +08:00
liu.zixi
0b5716ed7d feat: 课程列表导出,组织名称改为导出全路径 2025-12-07 15:26:25 +08:00
liu.zixi
a1121e8700 fix: 课程列表接口纠错 2025-12-06 17:37:55 +08:00
liu.zixi
5f42139407 fix: 课程列表接口纠错 2025-12-06 17:37:12 +08:00
liu.zixi
3d35519615 fix: 课程列表接口增加返回最后修改时间 2025-12-06 17:25:56 +08:00
liu.zixi
e76833a3c2 fix: 课程列表接口增加返回课程类型 2025-12-06 17:22:07 +08:00
miaowenbo
c3027517c5 fix:修复了资源学习情况列表查询的有没有状态名称模糊查询不一致的问题,现在统一模糊查询
status为空查询字段加上aid,方便前端获取工号部门信息
2025-12-05 17:46:49 +08:00
yangxinyu
ce2524fdcb feat:修改资源学习列表无完成人数无法查到资源的问题 2025-12-04 18:40:18 +08:00
miaowenbo
4f53a268ba fix:学习记录查询/导出接口中,学习时长条件查询重新赋值补全实体类解耦逻辑,避免影响原表数据 2025-12-04 14:38:16 +08:00
yangxinyu
8e1a68d416 feat:修改完成人数统计错误 2025-12-04 14:04:59 +08:00
yangxinyu
4e6d1a6b04 Reapply "Merge branch '251114-feature-course-online' of https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers into 251114-feature-course-online"
This reverts commit f506fe49f2.
2025-12-04 13:33:05 +08:00
yangxinyu
f506fe49f2 Revert "Merge branch '251114-feature-course-online' of https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers into 251114-feature-course-online"
This reverts commit 3c582e9ade, reversing
changes made to 716ba91c2e.
2025-12-04 13:16:35 +08:00
yangxinyu
3c582e9ade Merge branch '251114-feature-course-online' of https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers into 251114-feature-course-online 2025-12-04 13:15:16 +08:00
yangxinyu
716ba91c2e feat资源学习列表新增content_id 2025-12-04 13:14:03 +08:00
miaowenbo
f39562ffc9 fix:分页查询课程学习记录以及导出课程学习记录修改时间条件(初版) 2025-12-03 18:58:04 +08:00
miaowenbo
2b7d0ef35a fix:学习记录导出接口补全缺少的空值校验 2025-12-02 17:41:11 +08:00
liu.zixi
7e21784a74 feat: 数据权限确认 2025-12-01 19:10:37 +08:00
yangxinyu
4b064171e6 feat:增加查询资源学习列表的contentName字段 2025-12-01 17:22:10 +08:00
11 changed files with 295 additions and 184 deletions

View File

@@ -131,6 +131,16 @@ public class CourseManageApi extends ApiBaseController{
return success(coursePageService.pageQuery(null, coursePageQueryDTO));
}
/**
* 新-教师端 我开发的课程
* @param coursePageQueryDTO
* @return
*/
@PostMapping("/develop_page")
public JsonResponse<PageList<CoursePageVo>> teacherPage(@RequestBody CoursePageQueryDTO coursePageQueryDTO) {
return success(coursePageService.pageQuery(getCurrent(), coursePageQueryDTO));
}
/**
* 当前用户是否在管理端显示置顶相关功能
* @return

View File

@@ -6,6 +6,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.xboe.module.course.dto.CoursePageQueryDTO;
@@ -101,6 +102,7 @@ public class CourseDao extends BaseDao<Course> {
StringBuilder builder = new StringBuilder("select ");
builder.append("c.id,");
builder.append("c.name,");
builder.append("c.type,");
builder.append("c.cover_img AS coverImg,");
builder.append("c.sys_type1 AS sysType1,");
builder.append("c.sys_type2 AS sysType2,");
@@ -122,7 +124,8 @@ public class CourseDao extends BaseDao<Course> {
builder.append("c.open_course AS openCourse,");
builder.append("c.is_top AS isTop,");
builder.append("COALESCE(tch.teacher_names, '') AS teacherName,");
builder.append("c.sort_weight AS sortWeight");
builder.append("c.sort_weight AS sortWeight,");
builder.append("c.sys_update_time AS sysUpdateTime");
// 拼接FROM及查询条件语句
appendFrom(builder, queryDTO, isSystemAdmin, orgIds, currentAccountId, userCenterSchema);
// 排序语句
@@ -142,59 +145,65 @@ public class CourseDao extends BaseDao<Course> {
}
vo.setName((String) row[1]);
vo.setCoverImg((String) row[2]);
vo.setSysType1((String) row[3]);
vo.setSysType2((String) row[4]);
vo.setSysType3((String) row[5]);
vo.setOrgId((String) row[6]);
vo.setOrgName((String) row[7]);
vo.setOrgFullName((String) row[8]);
vo.setSysCreateBy((String) row[9]);
vo.setCreateFrom((String) row[10]);
vo.setType((Integer) row[2]);
vo.setCoverImg((String) row[3]);
vo.setSysType1((String) row[4]);
vo.setSysType2((String) row[5]);
vo.setSysType3((String) row[6]);
vo.setOrgId((String) row[7]);
vo.setOrgName((String) row[8]);
vo.setOrgFullName((String) row[9]);
vo.setSysCreateBy((String) row[10]);
vo.setCreateFrom((String) row[11]);
// 增加对Timestamp和LocalDateTime的兼容性防止Timestamp为null的情况
Timestamp sysCreateTimestamp = (Timestamp) row[11];
Timestamp sysCreateTimestamp = (Timestamp) row[12];
if (sysCreateTimestamp != null) {
vo.setSysCreateTime(sysCreateTimestamp.toLocalDateTime());
}
vo.setForUsers((String) row[12]);
vo.setStatus((Integer) row[13]);
vo.setPublished((Boolean) row[14]);
vo.setForUsers((String) row[13]);
vo.setStatus((Integer) row[14]);
vo.setPublished((Boolean) row[15]);
// 增加对Timestamp和LocalDateTime的兼容性防止Timestamp为null的情况
Timestamp publishTimestamp = (Timestamp) row[15];
Timestamp publishTimestamp = (Timestamp) row[16];
if (publishTimestamp != null) {
vo.setPublishTime(publishTimestamp.toLocalDateTime());
}
// 防止Number为null的情况
Number studysNum = (Number) row[16];
Number studysNum = (Number) row[17];
if (studysNum != null) {
vo.setStudys(studysNum.intValue());
} else {
vo.setStudys(0);
}
Number scoreNum = (Number) row[17];
Number scoreNum = (Number) row[18];
if (scoreNum != null) {
vo.setScore(scoreNum.floatValue());
} else {
vo.setScore(0.0f);
}
Number durationNum = (Number) row[18];
Number durationNum = (Number) row[19];
if (durationNum != null) {
vo.setCourseDuration(durationNum.longValue());
} else {
vo.setCourseDuration(0L);
}
vo.setEnabled((Boolean) row[19]);
vo.setOpenCourse((Integer) row[20]);
vo.setIsTop((Boolean) row[21]);
vo.setTeacherName((String) row[22]);
vo.setSortWeight((Integer) row[23]);
vo.setEnabled((Boolean) row[20]);
vo.setOpenCourse((Integer) row[21]);
vo.setIsTop((Boolean) row[22]);
vo.setTeacherName((String) row[23]);
vo.setSortWeight((Integer) row[24]);
// 增加对Timestamp和LocalDateTime的兼容性防止Timestamp为null的情况
Timestamp sysUpdateTimestamp = (Timestamp) row[25];
if (sysUpdateTimestamp != null) {
vo.setSysUpdateTime(sysUpdateTimestamp.toLocalDateTime());
}
coursePageVos.add(vo);
}
return coursePageVos;
@@ -229,7 +238,7 @@ public class CourseDao extends BaseDao<Course> {
// 开头判断课程培训时间的两个参数是否不为null
boolean filterLearningTime = queryDTO.getLearningTimeStart() != null && queryDTO.getLearningTimeEnd() != null;
builder.append(" FROM boe_course c");
// 聚合教师姓名(仅未删除的教师)
// 聚合教师姓名
builder.append(System.lineSeparator());
builder.append("LEFT JOIN (SELECT course_id, GROUP_CONCAT(teacher_name ORDER BY id SEPARATOR ',') AS teacher_names FROM boe_course_teacher GROUP BY course_id) tch ON c.id = tch.course_id");
// 学习人数聚合(满足时间条件的学习记录,且学习记录有效)
@@ -253,6 +262,9 @@ public class CourseDao extends BaseDao<Course> {
builder.append(System.lineSeparator());
builder.append("LEFT JOIN ").append(userCenterSchema)
.append(".organization org ON c.org_id = org.organization_id AND org.deleted = 0");
// 教师联查
builder.append(System.lineSeparator());
builder.append("LEFT JOIN boe_course_teacher ct ON c.id = ct.course_id");
// where条件
// 第一个条件deleted = 0
@@ -302,9 +314,14 @@ public class CourseDao extends BaseDao<Course> {
builder.append("AND (:learningTimeStart IS NULL OR :learningTimeEnd IS NULL OR EXISTS (SELECT 1 FROM boe_study_course sc WHERE sc.course_id = c.id AND sc.finish_time IS NOT NULL AND sc.add_time >= :learningTimeStart AND sc.finish_time <= :learningTimeEnd))");
}
// 授课教师
if (StringUtils.isNotBlank(queryDTO.getTeacherName())) {
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
builder.append(System.lineSeparator());
builder.append("AND tch.teacher_names LIKE CONCAT('%', :teacherName, '%')");
// 判断teacherId是一个还是多个
if (queryDTO.getTeacherId().contains(",")) {
builder.append("AND ct.teacher_id IN (:teacherIdList)");
} else {
builder.append("AND ct.teacher_id = :teacherId");
}
}
// 展开后条件
if (queryDTO.getEnabled() != null) {
@@ -319,9 +336,13 @@ public class CourseDao extends BaseDao<Course> {
builder.append(System.lineSeparator());
builder.append("AND c.org_id = :orgId");
}
if (StringUtils.isNotBlank(queryDTO.getCreateUser())) {
if (StringUtils.isNotBlank(queryDTO.getCreateUserId())) {
builder.append(System.lineSeparator());
builder.append("AND c.sys_create_by LIKE CONCAT('%', :createUser, '%')");
if (queryDTO.getCreateUserId().contains(",")) {
builder.append("AND c.sys_create_aid IN (:createUserIdList)");
} else {
builder.append("AND c.sys_create_aid = :createUserId");
}
}
if (StringUtils.isNotBlank(queryDTO.getCreateFrom())) {
builder.append(System.lineSeparator());
@@ -402,8 +423,14 @@ public class CourseDao extends BaseDao<Course> {
query.setParameter("learningTimeStart", queryDTO.getLearningTimeStart());
query.setParameter("learningTimeEnd", queryDTO.getLearningTimeEnd());
}
if (StringUtils.isNotBlank(queryDTO.getTeacherName())) {
query.setParameter("teacherName", queryDTO.getTeacherName());
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
String teacherIdStr = queryDTO.getTeacherId();
if (teacherIdStr.contains(",")) {
List<String> teacherIdList = Arrays.asList(teacherIdStr.split(","));
query.setParameter("teacherIdList", teacherIdList);
} else {
query.setParameter("teacherId", queryDTO.getTeacherId());
}
}
if (queryDTO.getEnabled() != null) {
query.setParameter("enabled", queryDTO.getEnabled());
@@ -414,8 +441,14 @@ public class CourseDao extends BaseDao<Course> {
if (StringUtils.isNotBlank(queryDTO.getResOwner1())) {
query.setParameter("orgId", queryDTO.getResOwner1());
}
if (StringUtils.isNotBlank(queryDTO.getCreateUser())) {
query.setParameter("createUser", queryDTO.getCreateUser());
if (StringUtils.isNotBlank(queryDTO.getCreateUserId())) {
String createUserIdStr = queryDTO.getCreateUserId();
if (createUserIdStr.contains(",")) {
List<String> createUserIdList = Arrays.asList(createUserIdStr.split(","));
query.setParameter("createUserIdList", createUserIdList);
} else {
query.setParameter("createUserId", queryDTO.getCreateUserId());
}
}
if (StringUtils.isNotBlank(queryDTO.getCreateFrom())) {
query.setParameter("createFrom", queryDTO.getCreateFrom());

View File

@@ -31,6 +31,11 @@ public class CoursePageQueryDTO {
/**创建人*/
private String createUser;
/**
* 创建人id
*/
private String createUserId;
/**课程分类的一级*/
private String sysType1;
@@ -43,6 +48,11 @@ public class CoursePageQueryDTO {
/**授课教师*/
private String teacherName;
/**
* 授课教师id
*/
private String teacherId;
/**培训时间筛选类型*/
private String learningTimeType;
@@ -77,4 +87,11 @@ public class CoursePageQueryDTO {
/**排序顺序*/
private Boolean orderAsc;
/**
* 是否是新建在线课程页面
* false时代表是在管理端的“在线管理”、“我开发的课程”
* true时代表是在学习路径图/项目管理 中,创建在线课程的选择页面
*/
private Boolean isCreateCourse;
}

View File

@@ -82,65 +82,9 @@ public class CoursePageServiceImpl implements ICoursePageService {
String currentAccountId = currentUser == null ? null : currentUser.getAccountId();
// 第一版废弃
// // 构建查询条件
// List<IFieldFilter> filters = new ArrayList<>();
//
// // 自动添加过滤已删除
// filters.add(FieldFilters.eq("c.deleted", false));
//
// // 添加权限过滤条件
// if (!isSystemAdmin) {
// // 非系统管理员需要进行权限过滤
// List<IFieldFilter> permissionFilters = new ArrayList<>();
//
// // 添加组织权限过滤
// if (orgIds != null && !orgIds.isEmpty()) {
// permissionFilters.add(FieldFilters.in("c.orgId", orgIds));
// }
//
// // 添加创建人过滤(可以看到自己创建的课程)
// if (StringUtils.isNotBlank(currentAccountId)) {
// permissionFilters.add(FieldFilters.eq("c.sysCreateAid", currentAccountId));
// }
//
// // 如果有权限条件则添加OR条件
// if (!permissionFilters.isEmpty()) {
// filters.add(FieldFilters.or(permissionFilters));
// }
// }
// filters.addAll(createFilters(coursePageQueryDTO));
//
// QueryBuilder query = QueryBuilder.from(Course.class.getSimpleName() + " c").addFilters(filters);
// // 处理排序
// handleOrder(query, coursePageQueryDTO.getOrderField(), coursePageQueryDTO.getOrderAsc());
//
// // 设置分页参数
// int pageIndex = coursePageQueryDTO.getPageIndex() != null ? coursePageQueryDTO.getPageIndex() : 0;
// int pageSize = coursePageQueryDTO.getPageSize() != null ? coursePageQueryDTO.getPageSize() : 10;
// query.setPageIndex(pageIndex);
// query.setPageSize(pageSize);
//
// // 执行查询
// PageList<Course> coursePageList = courseDao.findPage(query.builder());
//
// // 转换为CoursePageVo
// PageList<CoursePageVo> result = new PageList<>();
// result.setCount(coursePageList.getCount());
// result.setPageSize(coursePageList.getPageSize());
//
// // 子查询根据课程id查询课程下的教师
// List<String> courseIds = coursePageList.getList().stream()
// .map(Course::getId)
// .collect(Collectors.toList());
// List<CourseTeacher> courseTeacherList = getCourseTeacherList(courseIds);
// List<CoursePageVo> voList = coursePageList.getList().stream()
// .map(c -> convertToVo(c, courseTeacherList))
// .collect(Collectors.toList());
// result.setList(voList);
//
// return result;
// 第二版
List<String> readIds = userOrgIds.getReadIds();
orgIds.addAll(readIds);
long total = courseDao.countCourse(coursePageQueryDTO, isSystemAdmin, orgIds, currentAccountId, mySqlSchemaProperties.getUserCenterSchema());
List<CoursePageVo> voList = courseDao.queryCourse(coursePageQueryDTO, isSystemAdmin, orgIds, currentAccountId, true, mySqlSchemaProperties.getUserCenterSchema());
PageList<CoursePageVo> result = new PageList<>();
@@ -306,7 +250,7 @@ public class CoursePageServiceImpl implements ICoursePageService {
map.put("published", coursePageVo.getPublished() == null || !coursePageVo.getPublished() ? "未发布" : "已发布");
map.put("enabled", coursePageVo.getEnabled() == null || coursePageVo.getEnabled() ? "停用" : "启用");
map.put("openCourse", coursePageVo.getOpenCourse() == null || coursePageVo.getOpenCourse() == 0 ? "" : "");
map.put("orgName", coursePageVo.getOrgName());
map.put("orgName", coursePageVo.getOrgFullName()); // 修改为组织全名
map.put("sysCreateBy", coursePageVo.getSysCreateBy());
map.put("createFrom", CourseCreateFromEnum.getByCode(coursePageVo.getCreateFrom()).getLabel());
map.put("sysCreateTime", formatter.format(coursePageVo.getSysCreateTime()));

View File

@@ -18,6 +18,11 @@ public class CoursePageVo {
* 课程名称
*/
private String name;
/**
* 课程类型
*/
private Integer type;
/**
* 课程封面图片地址
@@ -87,6 +92,13 @@ public class CoursePageVo {
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime sysCreateTime;
/**
* 最后修改时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime sysUpdateTime;
/**
* 目标人群

View File

@@ -133,15 +133,15 @@ public class StudyCourseApi extends ApiBaseController{
*
* @param pager 分页参数
* @param courseId 25.11.25新增
*
* @param contentName 资源名称
*/
@RequestMapping(value = "/pagelist-resource", method = {RequestMethod.GET, RequestMethod.POST})
public JsonResponse<PageList<CourseFinishCountDto>> findPageResource(Pagination pager, String courseId) {
public JsonResponse<PageList<CourseFinishCountDto>> findPageResource(Pagination pager, String courseId, String contentName) {
if (StringUtil.isBlank(courseId)) {
return error("课程id不能为空");
}
try {
PageList<CourseFinishCountDto> courseFinishCountDTOs = service.findPageResource(pager.getPageIndex(), pager.getPageSize(), courseId);
PageList<CourseFinishCountDto> courseFinishCountDTOs = service.findPageResource(pager.getPageIndex(), pager.getPageSize(), courseId, contentName);
return success(courseFinishCountDTOs);
} catch (Exception e) {
log.error("查询课程学习记录错误:{}", e.getMessage());

View File

@@ -6,6 +6,7 @@ import com.xboe.school.study.dto.CourseFinishCountDto;
import com.xboe.school.study.entity.StudyCourse;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
@@ -18,33 +19,41 @@ public class CourseStatDao extends BaseDao<StudyCourse> {
* @param startIndex 分页开始索引
* @param pageSize 分页大小
* @param courseId 课程ID
* @param contentName 资源名称
* @return 课程完成人数统计DTO集合
*/
public List<CourseFinishCountDto> findFinishCountPage(int startIndex, int pageSize, String courseId) {
public List<CourseFinishCountDto> findFinishCountPage(int startIndex, int pageSize, String courseId, String contentName) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ")
// 课程名和DTO字段对应
.append("c.content_name AS contentName, ")
// 资源名
.append("cc.content_name AS contentName, ")
// 资源ID
.append("cc.id AS contentId, ")
// 完成人数(去重统计)
.append("COUNT(DISTINCT c.aid) AS finishCount, ")
//2025.11.27新增:资源类型
.append("cc.content_type AS contentType ")
// 你的课程表名
.append("FROM boe_study_course_item c ")
.append("FROM boe_course_content cc ")
// 2025.11.27新增:连表 boe_course_content
.append("LEFT JOIN boe_course_content cc ON cc.id = c.content_id ")
.append("WHERE 1=1 ")
// 条件:已完成
.append("AND c.status = 9 ");
.append("LEFT JOIN boe_study_course_item c ON cc.id = c.content_id AND c.status = 9 ")
.append("WHERE 1=1 ");
List<Object> params = new ArrayList<>();
// courseId非空则过滤参数化防注入
if (StringUtils.isNotBlank(courseId)) {
sql.append("AND c.course_id = ? ");
sql.append("AND cc.course_id = ? ");
params.add(courseId);
}
// 25.12.1修改 新增contentName模糊查询粗略匹配
if (StringUtils.isNotBlank(contentName)) {
// 实现“包含contentName”的模糊查询
sql.append("AND cc.content_name LIKE ? ");
// 通配符%拼接在参数上防注入表示“前后任意字符包含contentName”
params.add("%" + contentName + "%");
}
// 分组+排序+分页聚合函数必须分组排序参考第一个代码的desc id
sql.append("GROUP BY c.content_id, c.content_name, cc.content_type ")
.append("ORDER BY c.content_id DESC ")
sql.append("GROUP BY cc.id, cc.content_name, cc.content_type ")
.append("ORDER BY cc.id DESC ")
// MySQL分页偏移量每页条数
.append("LIMIT ?, ?");
// 补充分页参数顺序startIndex → pageSize
@@ -57,29 +66,41 @@ public class CourseStatDao extends BaseDao<StudyCourse> {
for (Object[] objs : resultList) {
CourseFinishCountDto dto = new CourseFinishCountDto();
dto.setContentName(objs[0] != null ? (String) objs[0] : "");
dto.setFinishCount(objs[1] != null ? ((Number) objs[1]).intValue() : 0);
dto.setContentType((Integer) objs[2]);
dto.setContentId(((BigInteger) objs[1]).toString());
dto.setFinishCount(objs[2] != null ? ((Number) objs[2]).intValue() : 0);
dto.setContentType((Integer) objs[3]);
dtoList.add(dto);
}
return dtoList;
}
// 查总条数返回int类型匹配PageList的count字段
public int findFinishCountTotal(String courseId) {
/**
* 查询课程完成人数
*
* @param courseId 课程ID
* @param contentName 资源名称
* @return 课程完成人数
*/
public int findFinishCountTotal(String courseId, String contentName) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ")
// 总条数=去重后的人数
.append("COUNT(DISTINCT c.content_id) ")
.append("FROM boe_study_course_item c ")
.append("WHERE 1=1 ")
// 条件:已完成
.append("AND c.status = 9 ");
.append("COUNT(DISTINCT c.id) ")
.append("FROM boe_course_content c ")
.append("WHERE 1=1 ");
List<Object> params = new ArrayList<>();
// courseId非空则过滤参数化防注入
if (StringUtils.isNotBlank(courseId)) {
sql.append("AND c.course_id = ? ");
params.add(courseId);
}
// 25.12.1修改 新增contentName模糊查询粗略匹配
if (StringUtils.isNotBlank(contentName)) {
// 实现“包含contentName”的模糊查询
sql.append("AND c.content_name LIKE ? ");
// 通配符%拼接在参数上防注入表示“前后任意字符包含contentName”
params.add("%" + contentName + "%");
}
// 用sqlCount替代sqlFindObject直接返回int类型结果
// sqlCount会执行SQL并返回COUNT的结果无需手动转换Object
return this.sqlCount(sql.toString(), params.toArray());

View File

@@ -2,6 +2,8 @@ package com.xboe.school.study.dto;
import lombok.Data;
import java.math.BigInteger;
/**
* 课程完成人数统计DTO
*/
@@ -13,6 +15,11 @@ public class CourseFinishCountDto {
*/
private String contentName;
/**
* 内容ID
*/
private String contentId;
/**
* 完成人数(数据库 count 统计得出)
*/
@@ -24,4 +31,5 @@ public class CourseFinishCountDto {
*/
private Integer contentType;
}

View File

@@ -69,8 +69,9 @@ public interface IStudyCourseService {
* @param pageIndex 页码
* @param pageSize 每页数据条数
* @param courseId 课程id
* @param contentName 资源名称
*/
PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId);
PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId, String contentName);
/**
* 热度榜

View File

@@ -2,7 +2,10 @@ package com.xboe.school.study.service.impl;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.core.orm.*;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.FieldUpdateType;
import com.xboe.core.orm.QueryBuilder;
import com.xboe.core.orm.UpdateBuilder;
import com.xboe.core.utils.ConvertUtil;
import com.xboe.data.outside.IOutSideDataService;
import com.xboe.module.course.dao.CourseContentDao;
@@ -24,15 +27,17 @@ import com.xboe.system.user.vo.UserSimpleVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -56,6 +61,10 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
@Autowired
IOrganizationService organizationService;
@Autowired
@Lazy
IStudyCourseService studyCourseService;
@Autowired
StudyCourseDao studyCourseDao;
@@ -92,6 +101,13 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
@Autowired(required = false)
ICourseStudySearch esSearch;//对ES的处理
/**
* 用于避免JPA查询后修改entity实体字段自动更新到数据库
*/
@Autowired
private EntityManager entityManager;
@Override
public StudyCourse findByCourseIdAndAid(String courseId, String aid) {
//加上排序,如果是多条学习记录,只会取最新的一条
@@ -146,36 +162,6 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
if(StringUtils.isNotBlank(sc.getAid())) {
query.addFilter(FieldFilters.eq("aid", sc.getAid()));
}
// 25.11.25新增:添加基于时间的查询条件
if (StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
// 开始结束时间均传入的情况,实现筛选逻辑
// (startTime >= 查询开始时间 AND startTime <= 查询结束时间) OR (finishTime >= 查询开始时间 AND finishTime <= 查询结束时间)
// 这样兼容查询结束时间为空值的情况,因为学员课程未结束时没有结束时间
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
// 筛选开始时间
IFieldFilter startTimeInRange = FieldFilters.and(FieldFilters.ge("startTime", startDateTime), FieldFilters.le("startTime", finishDateTime));
// 筛选结束时间
IFieldFilter finishTimeInRange = FieldFilters.and(FieldFilters.ge("finishTime", startDateTime), FieldFilters.le("finishTime", finishDateTime));
// OR条件查询
query.addFilter(FieldFilters.or(startTimeInRange, finishTimeInRange));
} else {
// 只输出单个参数情况
// 筛选开始时间
if (StringUtils.isNotBlank(sc.getQueryStartTime())) {
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
LocalDateTime startDateTime = startDate.atStartOfDay();
query.addFilter(FieldFilters.ge("startTime", startDateTime));
}
// 筛选结束时间
if (StringUtils.isNotBlank(sc.getQueryFinishTime())) {
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
query.addFilter(FieldFilters.le("finishTime", finishDateTime));
}
}
}
// 原有查询是否结束逻辑
if (isFinish != null) {
@@ -231,17 +217,73 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
}
}
}
// 25.12.3新增,添加基于时间的查询条件,规则如下:
if (sc != null && StringUtils.isNotBlank(sc.getCourseId()) && StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
List<StudyCourseItem> studyCourseItemList = studyCourseService.getStudyCourseItemByCourseId(sc.getCourseId());
if (studyCourseItemList != null && !studyCourseItemList.isEmpty()) {
// 将学习内容按照studyId进行分组便于后续计算每个studyId下的总学习时长
Map<String, List<StudyCourseItem>> itemsByStudyId = studyCourseItemList.stream().filter(item -> item.getFinishTime() != null).collect(Collectors.groupingBy(StudyCourseItem::getStudyId));
// 获取查询的起止时间
String queryStartTime = sc.getQueryStartTime();
String queryFinishTime = sc.getQueryFinishTime();
for (StudyCourse studyCourse : studyCourses) {
// 实体类解耦
entityManager.detach(studyCourse);
// 解析查询时间范围
LocalDate startDate = LocalDate.parse(queryStartTime);
LocalDate endDate = LocalDate.parse(queryFinishTime);
// 如果有查询时间范围,则计算筛选后的学习时长
if (itemsByStudyId.containsKey(studyCourse.getId())) {
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
int filteredDuration = 0;
for (StudyCourseItem item : items) {
LocalDateTime finishTime = item.getFinishTime();
LocalDate finishDate = finishTime.toLocalDate();
// 判断结束时间是否在筛选范围内
boolean inRange = true;
if (finishDate.isBefore(startDate)) {
inRange = false;
}
if (finishDate.isAfter(endDate)) {
inRange = false;
}
// 如果在范围内,则累加学习时长
if (inRange) {
Integer duration = item.getStudyDuration();
if (duration != null) {
filteredDuration += duration;
}
}
}
// 更新学习时长
studyCourse.setTotalDuration(filteredDuration);
} else {
// 查询不到item的情况默认置空
studyCourse.setTotalDuration(0);
}
}
}
}
return studyCoursePageList;
}
/**
* 分页查询课程的资源名称以及资源学习完成人数
*
* @param pageIndex 页码
* @param pageSize 每页数据条数
* @param courseId 课程id
* @param contentName 资源名称
*/
@Override
public PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId) {
public PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId, String contentName) {
// 1. 手动计算分页偏移量(数据库分页必需)
// pageIndex<1时设为0避免数据库LIMIT偏移量为负数
int startIndex = (pageIndex < 1) ? 0 : (pageIndex - 1) * pageSize;
// 2. 调用Dao层查当前页数据传入偏移量、每页条数、courseId条件
List<CourseFinishCountDto> dtoList = courseStatDao.findFinishCountPage(startIndex, pageSize, courseId);
List<CourseFinishCountDto> dtoList = courseStatDao.findFinishCountPage(startIndex, pageSize, courseId,contentName);
// 3. 调用Dao层查总条数对应PageList的count字段用int类型和PageList一致
int totalCount = courseStatDao.findFinishCountTotal(courseId);
int totalCount = courseStatDao.findFinishCountTotal(courseId,contentName);
// 4. 按PageList构造函数创建对象只传list和count
PageList<CourseFinishCountDto> pageList = new PageList<>(dtoList, totalCount);
// 5. 设置pageSize覆盖默认10确保总页数计算正确
@@ -809,36 +851,6 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
if (StringUtils.isNotBlank(sc.getAid())) {
query.addFilter(FieldFilters.eq("aid", sc.getAid()));
}
// 25.11.25新增:添加基于时间的查询条件
if (sc.getQueryStartTime() != null && sc.getQueryFinishTime() != null) {
// 开始结束时间均传入的情况,实现筛选逻辑
// (startTime >= 查询开始时间 AND startTime <= 查询结束时间) OR (finishTime >= 查询开始时间 AND finishTime <= 查询结束时间)
// 这样兼容查询结束时间为空值的情况,因为学员课程未结束时没有结束时间
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
// 筛选开始时间
IFieldFilter startTimeInRange = FieldFilters.and(FieldFilters.ge("startTime", startDateTime), FieldFilters.le("startTime", finishDateTime));
// 筛选结束时间
IFieldFilter finishTimeInRange = FieldFilters.and(FieldFilters.ge("finishTime", startDateTime), FieldFilters.le("finishTime", finishDateTime));
// OR条件查询
query.addFilter(FieldFilters.or(startTimeInRange, finishTimeInRange));
} else {
// 只输出单个参数情况
// 筛选开始时间
if (sc.getQueryStartTime() != null) {
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
LocalDateTime startDateTime = startDate.atStartOfDay();
query.addFilter(FieldFilters.ge("startTime", startDateTime));
}
// 筛选结束时间
if (sc.getQueryFinishTime() != null) {
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
query.addFilter(FieldFilters.le("finishTime", finishDateTime));
}
}
}
if (isFinish != null) {
if (isFinish) {
@@ -847,7 +859,55 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
query.addFilter(FieldFilters.lt("status", 9));
}
}
return studyCourseDao.findList(query.builder());
// 25.12.3新增,添加基于时间的查询条件,规则如下:
List<StudyCourse> studyCourses = studyCourseDao.findList(query.builder());
if (sc != null && StringUtils.isNotBlank(sc.getCourseId()) && StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
List<StudyCourseItem> studyCourseItemList = studyCourseService.getStudyCourseItemByCourseId(sc.getCourseId());
if (studyCourseItemList != null && !studyCourseItemList.isEmpty()) {
// 将学习内容按照studyId进行分组便于后续计算每个studyId下的总学习时长
Map<String, List<StudyCourseItem>> itemsByStudyId = studyCourseItemList.stream().filter(item -> item.getFinishTime() != null).collect(Collectors.groupingBy(StudyCourseItem::getStudyId));
// 获取查询的起止时间
String queryStartTime = sc.getQueryStartTime();
String queryFinishTime = sc.getQueryFinishTime();
for (StudyCourse studyCourse : studyCourses) {
// 实体类解耦
entityManager.detach(studyCourse);
// 解析查询时间范围
LocalDate startDate = LocalDate.parse(queryStartTime);
LocalDate endDate = LocalDate.parse(queryFinishTime);
// 如果有查询时间范围,则计算筛选后的学习时长
if (itemsByStudyId.containsKey(studyCourse.getId())) {
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
int filteredDuration = 0;
for (StudyCourseItem item : items) {
LocalDateTime finishTime = item.getFinishTime();
LocalDate finishDate = finishTime.toLocalDate();
// 判断结束时间是否在筛选范围内
boolean inRange = true;
if (finishDate.isBefore(startDate)) {
inRange = false;
}
if (finishDate.isAfter(endDate)) {
inRange = false;
}
// 如果在范围内,则累加学习时长
if (inRange) {
Integer duration = item.getStudyDuration();
if (duration != null) {
filteredDuration += duration;
}
}
}
// 更新学习时长
studyCourse.setTotalDuration(filteredDuration);
} else {
// 查询不到item的情况默认置空
studyCourse.setTotalDuration(0);
}
}
}
}
return studyCourses;
}
@Override

View File

@@ -251,7 +251,7 @@ public class StudyServiceImpl implements IStudyService{
query.addFilter(FieldFilters.eq("courseId",courseId));
}
if(StringUtils.isNotBlank(name)) {
query.addFilter(FieldFilters.eq("aname", name));
query.addFilter(FieldFilters.like("aname", name));
}
int pageIndex2 = (pageIndex-1)*10;
if(status!=null) {
@@ -272,7 +272,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 " + "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 " + "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 " + "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 + "'" +
(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 " +
@@ -284,6 +284,7 @@ public class StudyServiceImpl implements IStudyService{
"on a.course_id = b.course_id and a.id = b.id " +
"group by a.id) as total";
log.info("资源完成情况sql{}", sql);
log.info("数量查询sql{}", sql2);
List<Object[]> list = scDao.sqlFindList(sql);
int totalCount = scDao.sqlCount(sql2);
List<StudyCourseItem> item = new ArrayList<>();
@@ -304,6 +305,10 @@ public class StudyServiceImpl implements IStudyService{
if(objs[7] != null){
sc.setScore(Float.valueOf(objs[7].toString()));
}
// 25.12.5新增补全aid查询
if (objs[8] != null) {
sc.setAid(String.valueOf(objs[8].toString()));
}
item.add(sc);
}
PageList<StudyCourseItem> pageList = new PageList<>(item);