Merge branch 'refs/heads/release-20250328-master-20251225' into yx-release-20250328-master

This commit is contained in:
liu.zixi
2025-12-24 17:29:15 +08:00
63 changed files with 4874 additions and 569 deletions

1
.gitignore vendored
View File

@@ -48,3 +48,4 @@ nbdist/
!*/build/*.xml
*.pid
doc/

View File

@@ -1,5 +1,6 @@
package com.xboe;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.system.ApplicationPid;
@@ -23,11 +24,13 @@ import java.io.IOException;
@EnableScheduling
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.boe.feign.api.*.remote", "com.xboe.api"})
@Slf4j
public class BoeServerAllApplication {
public static void main(String[] args) {
System.setProperty("jasypt.encryptor.password", "jasypt");
SpringApplication.run(BoeServerAllApplication.class, args);
log.info("== BOE启动成功 ==");
}
@PostConstruct

View File

@@ -34,6 +34,7 @@ public class UrlSecurityFilterImpl implements IUrlSecurityFilter{
noLoginUrls.add("/xboe/m/exam/alone-extend/save");
noLoginUrls.add("/xboe/m/course/manage/test");
noLoginUrls.add("/xboe/m/course/manage/redirectDetail");
}
@Override

View File

@@ -0,0 +1,17 @@
package com.xboe.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 整体系统MySQL数据库schema配置
*/
@ConfigurationProperties(prefix = "mysql.schema")
@Data
public class MySqlSchemaProperties {
/**
* 用户中心数据库schema
*/
private String userCenterSchema;
}

View File

@@ -1,14 +1,6 @@
package com.xboe.data.outside;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import com.boe.feign.api.serverall.entity.UserData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xboe.core.SysConstant;
@@ -16,9 +8,17 @@ import com.xboe.core.api.TokenProxy;
import com.xboe.core.utils.OkHttpUtil;
import com.xboe.data.dto.AudienceUser;
import com.xboe.data.dto.UserOrgIds;
import com.xboe.module.usergroup.dao.UserGroupItemDao;
import com.xboe.system.organization.service.IOrganizationService;
import com.xboe.system.user.service.IUserService;
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.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
@Service
@Slf4j
@@ -30,7 +30,16 @@ public class OutSideDataServiceImpl implements IOutSideDataService {
@Autowired
private OkHttpUtil okHttpUtil;
@Autowired
private UserGroupItemDao userGroupItemDao;
@Autowired
private IUserService userService;
@Autowired
private IOrganizationService organizationService;
private String getNodeText(JsonNode jn) {
if(jn!=null) {
String str = jn.asText();
@@ -402,8 +411,7 @@ public class OutSideDataServiceImpl implements IOutSideDataService {
return uids;
}
@Override
@Override
public void updateUser(String aid, String avatar, String sign) {
String token = TokenProxy.getToken(request);

View File

@@ -0,0 +1,34 @@
package com.xboe.enums;
import lombok.Getter;
import java.util.Arrays;
/**
* 课程审核类型枚举
*/
@Getter
public enum CourseAuditTypeEnum {
CREATE(1, "创建课程"),
UPDATE(2, "更新课程"),
DISABLE(3, "停用课程"),
ENABLE(4, "启用课程"),
;
private final int code;
private final String label;
CourseAuditTypeEnum(int code, String label) {
this.code = code;
this.label = label;
}
public static CourseAuditTypeEnum getByCode(int code) {
return Arrays.stream(values()).filter(item -> item.code == code).findFirst().orElse(CREATE);
}
}

View File

@@ -0,0 +1,32 @@
package com.xboe.enums;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
/**
* 课程创建来源
* boe_course.create_from
*/
@Getter
public enum CourseCreateFromEnum {
ADMIN("admin", "管理端"),
TEACHER("teacher", "教师端")
;
private final String code;
private final String label;
CourseCreateFromEnum(String code, String label) {
this.code = code;
this.label = label;
}
public static CourseCreateFromEnum getByCode(String code) {
return Arrays.stream(values()).filter(item -> StringUtils.equals(item.code, code)).findFirst().orElse(ADMIN);
}
}

View File

@@ -0,0 +1,29 @@
package com.xboe.enums;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
@Getter
public enum CourseStatusEnum {
STATUS_NONE(1, "-"),
STATUS_SUBMIT(2, "审核中"),
STATUS_AUDIT_NOPASS(3, "审核驳回"),
STATUS_AUDIT_FINISH(5, "审核通过")
;
private final int code;
private final String label;
CourseStatusEnum(int code, String label) {
this.code = code;
this.label = label;
}
public static CourseStatusEnum getByCode(Integer code) {
return Arrays.stream(values()).filter(item -> Objects.equals(item.code, code)).findFirst().orElse(STATUS_NONE);
}
}

View File

@@ -0,0 +1,35 @@
package com.xboe.enums;
import lombok.Getter;
import java.util.Arrays;
/**
* 资源学习情况筛选状态枚举
* 需要注意的是,这个枚举不对应数据库表字段
*/
@Getter
public enum StudyCourseQueryStatusEnum {
NOT_START(1, "未开始"),
FINISH(2, "已完成"),
STUDYING(3, "学习中"),
NOT_FINISH(4, "未完成"),
;
private final int code;
private final String label;
StudyCourseQueryStatusEnum(int code, String label) {
this.code = code;
this.label = label;
}
public static StudyCourseQueryStatusEnum getByCode(int code) {
return Arrays.stream(values()).filter(e -> code == e.code).findFirst().orElse(FINISH);
}
}

View File

@@ -0,0 +1,54 @@
package com.xboe.module.course.api;
import com.xboe.core.JsonResponse;
import com.xboe.core.api.ApiBaseController;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.WrapperQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Collections;
@RestController
@RequestMapping("/xboe/m/course/manage")
@Slf4j
public class CourseEsApi extends ApiBaseController {
@Autowired(required = false)
private RestHighLevelClient restHighLevelClient;
@GetMapping("/es/sort")
public JsonResponse updateSortWeight() {
UpdateByQueryRequest request = new UpdateByQueryRequest("new_resource_list");
// 使用 Painless 脚本为文档添加 sortWeight 字段
Script script = new Script(
ScriptType.INLINE,
"painless",
"ctx._source.sortWeight = 9999",
Collections.emptyMap()
);
request.setScript(script);
// 可选:只更新那些还没有 sortWeight 字段的文档(避免覆盖已有值)
String query = "{ \"bool\": { \"must_not\": { \"exists\": { \"field\": \"sortWeight\" } } } }";
request.setQuery(new WrapperQueryBuilder(query));
try {
BulkByScrollResponse response = restHighLevelClient.updateByQuery(request, RequestOptions.DEFAULT);
return success(response.getUpdated());
} catch (IOException e) {
log.error("更新索引失败", e);
return error("更新索引失败");
}
}
}

View File

@@ -12,11 +12,7 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.boe.feign.api.serverall.entity.UserData;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.xboe.api.ThirdApi;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.IFieldFilter;
import com.xboe.core.orm.QueryBuilder;
import com.xboe.data.outside.IOutSideDataService;
import com.xboe.module.course.entity.CourseTag;
import com.xboe.module.course.service.*;
@@ -72,7 +68,7 @@ public class CourseFullTextApi extends ApiBaseController{
@Autowired
ICourseTagService courseTagService;
@Resource
IStudyCourseService IStudyCourseService;
IStudyCourseService IStudyCourseService;
@Autowired
ThirdApi thirdApi;

View File

@@ -1,5 +1,6 @@
package com.xboe.module.course.api;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
@@ -7,6 +8,9 @@ import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import cn.hutool.http.Header;
import cn.hutool.http.useragent.UserAgentUtil;
import com.boe.feign.api.infrastructure.entity.CommonSearchVo;
import com.boe.feign.api.infrastructure.entity.Dict;
import com.xboe.api.ThirdApi;
@@ -14,10 +18,13 @@ import com.xboe.api.vo.UserBasicInfoVo;
import com.xboe.module.course.dto.*;
import com.xboe.module.course.entity.*;
import com.xboe.module.course.service.*;
import com.xboe.module.course.vo.CoursePageVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -57,7 +64,10 @@ public class CourseManageApi extends ApiBaseController{
@Resource
private ICourseService courseService;
@Autowired
private ICoursePageService coursePageService;
@Autowired
IOutSideDataService outsideService;
@@ -100,13 +110,92 @@ public class CourseManageApi extends ApiBaseController{
@Resource
CourseFullTextApi courseFullTextApi;
@Value("${boe.pcPageUrl}")
private String pcPageUrl;
@Value("${boe.h5PageUrl}")
private String h5PageUrl;
@Value("${boe.h5LoginUrl}")
private String h5LoginUrl;
@Value("${boe.pcLoginUrl}")
private String pcLoginUrl;
// @PostMapping("/test")
// public JsonResponse<PageList<Course>> findTest(Pagination pager,CourseQueryDto dto){
// //dto.setOrgAid("7003708665807110150");
// PageList<Course> coursePageList = courseService.findPage(pager.getPageIndex(), pager.getPageSize(),dto);
// return success(coursePageList);
// }
/**
* 新-管理端 课程列表
* @return
*/
@PostMapping("/page")
public JsonResponse<PageList<CoursePageVo>> managePage(@RequestBody CoursePageQueryDTO coursePageQueryDTO) {
// 管理端查询时不需要传入当前用户信息
return success(coursePageService.pageQuery(getCurrent(), coursePageQueryDTO));
}
/**
* 新-教师端 我开发的课程
* @param coursePageQueryDTO
* @return
*/
@PostMapping("/develop_page")
public JsonResponse<PageList<CoursePageVo>> teacherPage(@RequestBody CoursePageQueryDTO coursePageQueryDTO) {
// 按照上次修改时间默认排序
coursePageQueryDTO.setOrderField("sysUpdateTime");
coursePageQueryDTO.setOrderAsc(false);
return success(coursePageService.pageQuery(getCurrent(), coursePageQueryDTO));
}
/**
* 当前用户是否在管理端显示置顶相关功能
* @return
*/
@GetMapping("/show-settop")
public JsonResponse<Boolean> showSetTop() {
CurrentUser currentUser = getCurrent();
return success(coursePageService.showSetTop(currentUser));
}
/**
* 新-管理端 置顶课程列表
* @return
*/
@GetMapping("/topList")
public JsonResponse<List<CoursePageVo>> topList() {
return success(coursePageService.topList());
}
/**
* 新-管理端 置顶课程列表排序修改
* 对整个置顶列表进行重排序
* @param changedList
* @return
*/
@PostMapping("/top-sortchange")
public JsonResponse<List<CoursePageVo>> topListSortChange(@RequestBody List<CoursePageVo> changedList) {
ServiceResponse<List<CoursePageVo>> serviceResponse = coursePageService.topListSortChange(changedList);
if (!serviceResponse.isSuccess()) {
return error(serviceResponse.getMessage());
}
return success(serviceResponse.getData());
}
/**
* 新-管理端 课程列表导出
* @param coursePageQueryDTO
* @param response
*/
@GetMapping("/export")
public void manageExport(CoursePageQueryDTO coursePageQueryDTO, HttpServletResponse response) {
coursePageService.exportCourseList(coursePageQueryDTO, response);
}
/**
* 管理列表的查询
* @param pager
@@ -378,8 +467,9 @@ public class CourseManageApi extends ApiBaseController{
fillCourseData(dto.getCourse());
courseService.save(dto);
}else {
// 20251215lzx编辑基本信息不提交状态让前端课程可以继续查看
//修改后重置,重新提交审核,重新发布
dto.getCourse().setPublished(false);
// dto.getCourse().setPublished(false);
dto.getCourse().setStatus(Course.STATUS_NONE);
courseService.update(dto);
}
@@ -636,8 +726,9 @@ public class CourseManageApi extends ApiBaseController{
//设置为提交状态
dto.getCourse().setStatus(Course.STATUS_SUBMIT);
dto.getCourse().setEnabled(true);//设置启用状态问题
dto.getCourse().setPublished(false);//重新提交审核设置为未发布状态
// 20251217lzx 提交审核时不改变状态
// dto.getCourse().setEnabled(true);//设置启用状态问题
// dto.getCourse().setPublished(false);//重新提交审核设置为未发布状态
courseService.submit(dto);
//提交成功发邮件提醒
try {
@@ -954,6 +1045,7 @@ public class CourseManageApi extends ApiBaseController{
*/
@PostMapping("/top")
public JsonResponse<Boolean> setTop(String ids,String title, Boolean top){
// lzx这个ids实际上只有一个id。。。
if(StringUtils.isBlank(ids)){
return badRequest("参数错误");
}
@@ -962,8 +1054,14 @@ public class CourseManageApi extends ApiBaseController{
}
try {
courseService.setTop(ids, top,getCurrent().getAccountId(), title,"");
return success(true);
// courseService.setTop(ids, top,getCurrent().getAccountId(), title,"");
// return success(true);
// 调整置顶逻辑
ServiceResponse<Boolean> serviceResponse = coursePageService.top(ids, top);
if (!serviceResponse.isSuccess()) {
return error(serviceResponse.getMessage());
}
return success(serviceResponse.getData());
} catch (Exception e) {
log.error("课程设置置顶错误",e);
return error("设置置顶失败",e.getMessage(),false);
@@ -1325,4 +1423,40 @@ public class CourseManageApi extends ApiBaseController{
courseService.saveTip(aid);
return success(true);
}
/**
* 扫描二维码重定向
* @param courseId 课程id
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/redirectDetail")
public void redirectDetail(
@NotNull Long courseId,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
boolean isMobile = UserAgentUtil.parse(request.getHeader(Header.USER_AGENT.toString())).isMobile();
String baseUrl = isMobile ? h5PageUrl : pcPageUrl;
// String loginUrl = isMobile ? h5LoginUrl : pcLoginUrl;
//
// CurrentUser currentUser;
// try {
// currentUser = getCurrent();
// } catch (Exception e) {
// log.warn("获取当前用户信息异常跳转至登录页。课程ID: {}", courseId, e);
// response.sendRedirect(loginUrl);
// return;
// }
//
// if (currentUser == null) {
// log.info("用户未登录跳转至登录页。课程ID: {}", courseId);
// response.sendRedirect(loginUrl);
// return;
// }
log.info("跳转到课程详情页课程ID: {}", courseId);
response.sendRedirect(baseUrl + courseId);
}
}

View File

@@ -490,17 +490,21 @@ public class CoursePortalApi extends ApiBaseController{
courseStudyVo.setContentName(s.getName()+"--"+c.getContentName());
courseStudyVo.setStatus(1);
courseStudyVo.setStudyDuration(0);
// 学习时长默认为0
courseStudyVo.setLearningDuration(0);
courseStudyVos.add(courseStudyVo);
}
}
}
// DecimalFormat decimalFormat = new DecimalFormat("#.#");
if(courseStudyVos!=null && !courseStudyVos.isEmpty()){
if (!courseStudyVos.isEmpty()) {
for (StudyCourseItem study:studyItem) {
for (CourseStudyVo cv:courseStudyVos) {
if(study.getContentId().equals(cv.getContentId())){
// 25.11.26标记 这就能硬set么这俩是一个值么
cv.setStudyDuration(study.getProgress());
// 25.11.26新增,添加学习时长字段
// 数据库存储的秒,前端会转为分钟
cv.setLearningDuration(study.getStudyDuration());
//针对考试,文档一类
if(study.getStatus()==null) {
if(study.getProgress()!=null){
@@ -511,16 +515,7 @@ public class CoursePortalApi extends ApiBaseController{
//音视频
}else{
cv.setStatus(study.getStatus());
}
// if(study.getStudyDuration()!=null){
//// Float duration= Float.valueOf(study.getProgress());
//// duration=duration/60;
//
// cv.setStudyDuration();
// }
}
}
}
@@ -528,6 +523,88 @@ public class CoursePortalApi extends ApiBaseController{
return success(courseStudyVos);
}
/**
* 分页获取课程章节学习进度
* 25.11.26新增
*
* @param pager 分页参数
* @param courseId 课程id
* @param aid 用户id
* @return 课程内容学习进度集合
*/
@GetMapping("/detail-study-page")
public JsonResponse<PageList<CourseStudyVo>> detailStudyPage(Pagination pager, String courseId, String aid) {
if (StringUtil.isBlank(courseId)) {
return badRequest("参数异常,未指定课程");
}
if (StringUtil.isBlank(aid)) {
return badRequest("参数异常,未指定用户");
}
PageList<CourseStudyVo> courseStudyVoPage = new PageList<>();
List<CourseStudyVo> courseStudyVos = new ArrayList<>();
// 课程章节
List<CourseSection> sectionlist = sectionService.getByCourseId(courseId);
// 获取课程章节id集合备用
List<String> sectionIdList = sectionlist.stream().map(CourseSection::getId).collect(Collectors.toList());
if (sectionIdList.isEmpty()) {
log.error("【分页获取课程章节学习进度】课程章节为空");
return success(courseStudyVoPage);
}
// 课程内容(分页只查询满足课程章节id条件的数据
PageList<CourseContent> courseContentPageList = contentService.getByCourseIdPage(pager.getPageIndex(), pager.getPageSize(), courseId, sectionIdList);
List<CourseContent> courseContentlist = courseContentPageList.getList();
// 查出课程当前人学习进度
StudyCourse studyCourse = studyCourseService.findByCourseIdAndAid(courseId, aid);
// 查出对应章节学习进度
List<StudyCourseItem> studyItem = studyCourseService.findStudyItem(studyCourse.getId(), aid);
// 先遍历课程章节
// 然后遍历课程内容
for (CourseContent courseContent : courseContentlist) {
CourseStudyVo courseStudyVo = new CourseStudyVo();
courseStudyVo.setContentId(courseContent.getId());
for (CourseSection s : sectionlist) {
if (courseContent.getCsectionId().equals(s.getId())) {
courseStudyVo.setContentName(s.getName() + "--" + courseContent.getContentName());
}
}
courseStudyVo.setStatus(1);
courseStudyVo.setStudyDuration(0);
// 学习时长默认为0
courseStudyVo.setLearningDuration(0);
courseStudyVos.add(courseStudyVo);
}
if (!courseStudyVos.isEmpty()) {
for (StudyCourseItem study : studyItem) {
for (CourseStudyVo cv : courseStudyVos) {
if (study.getContentId().equals(cv.getContentId())) {
// 25.11.26标记 这就能硬set么这俩是一个值么
cv.setStudyDuration(study.getProgress());
// 25.11.26新增,添加学习时长字段
// 数据库存储的秒,前端会转为分钟
cv.setLearningDuration(study.getStudyDuration());
//针对考试,文档一类
if (study.getStatus() == null) {
if (study.getProgress() != null) {
if (study.getProgress() == 100) {
cv.setStatus(9);
}
}
//音视频
} else {
cv.setStatus(study.getStatus());
}
}
}
}
}
// 拼接为分页格式
courseStudyVoPage.setList(courseStudyVos);
courseStudyVoPage.setCount(courseContentPageList.getCount());
courseStudyVoPage.setPageSize(courseContentPageList.getPageSize());
return success(courseStudyVoPage);
}
/**
* 根据课程id查出对应的教师id
* */

View File

@@ -1,11 +1,17 @@
package com.xboe.module.course.dao;
import java.math.BigInteger;
import java.sql.Timestamp;
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;
import com.xboe.module.course.vo.CoursePageVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import com.xboe.common.OrderCondition;
@@ -17,8 +23,14 @@ import com.xboe.core.orm.QueryBuilder;
import com.xboe.module.course.dto.RankingDto;
import com.xboe.module.course.entity.Course;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Repository
public class CourseDao extends BaseDao<Course> {
@PersistenceContext
private EntityManager entityManager;
/**
* 课程分页 搜索查询
* */
@@ -70,4 +82,444 @@ public class CourseDao extends BaseDao<Course> {
}
return rs;
}
/**
* 课程查询
* 使用原生sql查询
* 入参在sql语句中用:name的方式传入
*
* @param queryDTO 页面的查询入参
* @param isSystemAdmin 是否是系统管理员
* @param orgIds 组织id列表
* @param currentAccountId 当前用户id
* @param pageQuery 是否分页查询
* @return 课程列表
*/
public List<CoursePageVo> queryCourse(CoursePageQueryDTO queryDTO,
boolean isSystemAdmin, List<String> orgIds, String currentAccountId,
boolean pageQuery, String userCenterSchema) {
// select字段
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,");
builder.append("c.sys_type3 AS sysType3,");
builder.append("c.org_id AS orgId,");
builder.append("org.org_name AS orgName,");
builder.append("org.org_name_path AS orgFullName,");
builder.append("c.sys_create_by AS sysCreateBy,");
builder.append("c.create_from AS createFrom,");
builder.append("c.sys_create_time AS sysCreateTime,");
builder.append("c.for_users AS forUsers,");
builder.append("c.status,");
builder.append("c.published,");
builder.append("c.publish_time AS publishTime,");
builder.append("COALESCE(stu.studys, 0) AS studys,");
builder.append("COALESCE(grd.score, 0.0) AS score,");
builder.append("COALESCE(cc.duration_sum, 0) AS courseDuration,");
builder.append("c.enabled,");
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.sys_update_time AS sysUpdateTime");
// 拼接FROM及查询条件语句
appendFrom(builder, queryDTO, isSystemAdmin, orgIds, currentAccountId, userCenterSchema);
// 排序语句
appendOrder(builder, queryDTO);
Query query = entityManager.createNativeQuery(builder.toString());
setQueryParams(query, queryDTO, isSystemAdmin, orgIds, currentAccountId, pageQuery);
List<Object[]> resultList = query.getResultList();
List<CoursePageVo> coursePageVos = new ArrayList<>();
for (Object[] row : resultList) {
CoursePageVo vo = new CoursePageVo();
// 防止BigInteger为null的情况
BigInteger id = (BigInteger) row[0];
if (id != null) {
vo.setId(id.toString());
}
vo.setName((String) row[1]);
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[12];
if (sysCreateTimestamp != null) {
vo.setSysCreateTime(sysCreateTimestamp.toLocalDateTime());
}
vo.setForUsers((String) row[13]);
vo.setStatus((Integer) row[14]);
vo.setPublished((Boolean) row[15]);
// 增加对Timestamp和LocalDateTime的兼容性防止Timestamp为null的情况
Timestamp publishTimestamp = (Timestamp) row[16];
if (publishTimestamp != null) {
vo.setPublishTime(publishTimestamp.toLocalDateTime());
}
// 防止Number为null的情况
Number studysNum = (Number) row[17];
if (studysNum != null) {
vo.setStudys(studysNum.intValue());
} else {
vo.setStudys(0);
}
Number scoreNum = (Number) row[18];
if (scoreNum != null) {
vo.setScore(scoreNum.floatValue());
} else {
vo.setScore(0.0f);
}
Number durationNum = (Number) row[19];
if (durationNum != null) {
vo.setCourseDuration(durationNum.longValue());
} else {
vo.setCourseDuration(0L);
}
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;
}
public long countCourse(CoursePageQueryDTO queryDTO,
boolean isSystemAdmin, List<String> orgIds, String currentAccountId, String userCenterSchema) {
// select count
StringBuilder builder = new StringBuilder("select count(*)");
// 拼接FROM及查询条件语句
appendFrom(builder, queryDTO, isSystemAdmin, orgIds, currentAccountId, userCenterSchema);
// 排序语句
appendOrder(builder, queryDTO);
Query query = entityManager.createNativeQuery(builder.toString());
setQueryParams(query, queryDTO, isSystemAdmin, orgIds, currentAccountId, false);
Number count = (Number) query.getSingleResult();
return count.longValue();
}
/**
* 查询课程名称
* @param courseName
* @return
*/
public List<String> queryCourseNames(String courseName) {
String sql = "SELECT name FROM boe_course WHERE deleted = 0 AND (name = :courseName OR name LIKE CONCAT(:courseName,'(%)'))";
Query query = entityManager.createNativeQuery(sql);
query.setParameter("courseName", courseName);
return (List<String>) query.getResultList();
}
/**
* 拼接FROM及查询条件语句
*
* @param builder
* @param queryDTO
* @param isSystemAdmin
* @param orgIds
* @param currentAccountId
*/
private void appendFrom(StringBuilder builder, CoursePageQueryDTO queryDTO,
boolean isSystemAdmin, List<String> orgIds, String currentAccountId, String userCenterSchema) {
// 开头判断课程培训时间的两个参数是否不为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");
// 学习人数聚合(满足时间条件的学习记录,且学习记录有效)
builder.append(System.lineSeparator());
builder.append("LEFT JOIN (SELECT course_id, COUNT(*) AS studys FROM boe_study_course WHERE status > 1");
if (filterLearningTime) {
builder.append(" AND (add_time >= :learningTimeStart AND add_time <= :learningTimeEnd) OR (finish_time >= :learningTimeStart AND finish_time <= :learningTimeEnd)");
}
builder.append(" GROUP BY course_id) stu ON c.id = stu.course_id");
// 评分聚合(在时间区间内的有效打分)
builder.append(System.lineSeparator());
builder.append("LEFT JOIN (SELECT course_id, AVG(scores) AS score FROM boe_grade");
if (filterLearningTime) {
builder.append(" WHERE sys_create_time >= :learningTimeStart AND sys_create_time <= :learningTimeEnd");
}
builder.append(" GROUP BY course_id) grd ON c.id = grd.course_id");
// 课件聚合
builder.append(System.lineSeparator());
builder.append("LEFT JOIN (SELECT course_id, SUM(duration) AS duration_sum FROM boe_course_content WHERE deleted = 0 GROUP BY course_id) cc ON c.id = cc.course_id");
// 组织机构聚合
builder.append(System.lineSeparator());
builder.append("LEFT JOIN ").append(userCenterSchema)
.append(".organization org ON c.org_id = org.organization_id AND org.deleted = 0");
// 教师联查 - 移除原来的条件性JOIN改用EXISTS子查询方式避免数据重复
// 注意由于上方已通过GROUP_CONCAT聚合教师姓名此处不再需要JOIN教师表
/*
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
builder.append(System.lineSeparator());
builder.append("LEFT JOIN boe_course_teacher ct ON c.id = ct.course_id");
}
*/
// 排序字段是否为sysType
if (StringUtils.equals(queryDTO.getOrderField(), "sysType")) {
builder.append(System.lineSeparator()).append("LEFT JOIN boe_sys_type st1 ON c.sys_type1 = st1.id");
builder.append(System.lineSeparator()).append("LEFT JOIN boe_sys_type st2 ON c.sys_type2 = st2.id");
builder.append(System.lineSeparator()).append("LEFT JOIN boe_sys_type st3 ON c.sys_type3 = st3.id");
}
// where条件
// 第一个条件deleted = 0
builder.append(System.lineSeparator());
builder.append("WHERE c.deleted = 0");
// 数据权限筛选:系统管理员可查看所有课程,非系统管理员只能看到自己创建的课程或所属组织的课程
if (!isSystemAdmin) {
if (orgIds != null && !orgIds.isEmpty() && StringUtils.isNotBlank(currentAccountId)) {
builder.append(System.lineSeparator());
builder.append("AND (c.sys_create_aid = :currentAccountId OR c.org_id IN (:orgIds))");
} else if (orgIds != null && !orgIds.isEmpty()) {
builder.append(System.lineSeparator());
builder.append("AND c.org_id IN (:orgIds)");
} else if (StringUtils.isNotBlank(currentAccountId)) {
builder.append(System.lineSeparator());
builder.append("AND c.sys_create_aid = :currentAccountId");
}
}
// 简单查询条件(展开前)
if (StringUtils.isNotBlank(queryDTO.getName())) {
builder.append(System.lineSeparator());
builder.append("AND c.name LIKE CONCAT('%', :name, '%')");
}
if (StringUtils.isNotBlank(queryDTO.getSysType1())) {
builder.append(System.lineSeparator());
builder.append("AND c.sys_type1 = :sysType1");
}
if (StringUtils.isNotBlank(queryDTO.getSysType2())) {
builder.append(System.lineSeparator());
builder.append("AND c.sys_type2 = :sysType2");
}
if (StringUtils.isNotBlank(queryDTO.getSysType3())) {
builder.append(System.lineSeparator());
builder.append("AND c.sys_type3 = :sysType3");
}
if (StringUtils.isNotBlank(queryDTO.getStatus())) {
builder.append(System.lineSeparator());
builder.append("AND c.status = :status");
}
if (queryDTO.getPublish() != null) {
builder.append(System.lineSeparator());
builder.append("AND c.published = :publish");
}
// 时间筛选逻辑:只有当两个时间参数都提供时才启用学习记录存在性校验
// 注释掉原有的WHERE条件中的时间筛选逻辑因为时间筛选应只影响聚合字段的计算不应过滤课程记录
/*if (filterLearningTime) {
builder.append(System.lineSeparator());
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))");
}*/
// 授课教师筛选 - 使用EXISTS子查询替代JOIN以避免数据重复
// 注意由于上方已通过GROUP_CONCAT聚合教师姓名此处仅用于教师筛选条件
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
builder.append(System.lineSeparator());
// 判断teacherId是一个还是多个
if (queryDTO.getTeacherId().contains(",")) {
builder.append("AND EXISTS (SELECT 1 FROM boe_course_teacher ct WHERE ct.course_id = c.id AND ct.teacher_id IN (:teacherIdList))");
} else {
builder.append("AND EXISTS (SELECT 1 FROM boe_course_teacher ct WHERE ct.course_id = c.id AND ct.teacher_id = :teacherId)");
}
}
// 展开后条件
if (queryDTO.getEnabled() != null) {
builder.append(System.lineSeparator());
builder.append("AND c.enabled = :enabled");
}
if (queryDTO.getOpenCourse() != null) {
builder.append(System.lineSeparator());
// 兼容null数据
if (queryDTO.getOpenCourse() == 0) {
builder.append("AND (c.open_course IS NULL or c.open_course = 0)");
} else {
builder.append("AND c.open_course = 1");
}
}
if (StringUtils.isNotBlank(queryDTO.getOrgId())) {
builder.append(System.lineSeparator());
builder.append("AND c.org_id = :orgId");
}
if (StringUtils.isNotBlank(queryDTO.getCreateUserId())) {
builder.append(System.lineSeparator());
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());
builder.append("AND c.create_from = :createFrom");
}
}
/**
* 拼接ORDER BY语句
* @param builder
* @param queryDTO
*/
private void appendOrder(StringBuilder builder, CoursePageQueryDTO queryDTO) {
builder.append(System.lineSeparator());
builder.append("ORDER BY ");
// 页面选择的排序字段处理
String orderField = queryDTO.getOrderField();
// 小驼峰转MySQL字段
String orderFieldSql = camelToUnderline(orderField);
Boolean orderAsc = queryDTO.getOrderAsc();
if (StringUtils.isNotBlank(orderField)) {
// 排序逻辑
String orderAscStr = orderAsc == null || orderAsc ? "ASC" : "DESC";
// 多字段排序: sysType resOwner
if (StringUtils.equals(orderField, "sysType")) {
builder.append("CONVERT(st1.name USING gbk) ").append(orderAscStr).append(", CONVERT(st2.name USING gbk) ").append(orderAscStr).append(", CONVERT(st3.name USING gbk) ").append(orderAscStr);
// for (int i = 1; i <= 3; i++) {
// builder.append("CONVERT(st1.name USING gbk) ").append(orderAscStr).append(", CONVERT(st2.name USING gbk) ").append(orderAscStr).append(", CONVERT(st3.name USING gbk) ").append(orderAscStr);
// builder.append("c.").append(orderFieldSql).append(i).append(" ").append(orderAscStr).append(", ");
// }
} else if (StringUtils.equals(orderField, "orgName")) {
builder.append("CONVERT(org.org_name USING gbk) ").append(orderAscStr);
} else if (StringUtils.equals(orderField, "studys")) {
builder.append("COALESCE(stu.studys, 0) ").append(orderAscStr);
} else if (StringUtils.equals(orderField, "score")) {
builder.append("COALESCE(grd.score, 0) ").append(orderAscStr);
} else if (StringUtils.equals(orderField, "courseDuration")) {
builder.append("COALESCE(cc.duration_sum, 0) ").append(orderAscStr);
} else if (StringUtils.equals(orderField, "name") || StringUtils.equals(orderField, "sysCreateBy")) {
builder.append("CONVERT(c.").append(orderFieldSql).append(" USING gbk) ").append(orderAscStr);
} else {
builder.append("c.").append(orderFieldSql).append(" ").append(orderAscStr);
}
if (!StringUtils.equals(orderField, "sysCreateTime")) {
builder.append(", c.sys_create_time DESC");
}
} else {
builder.append("c.sys_create_time DESC");
}
}
private void setQueryParams(Query query,
CoursePageQueryDTO queryDTO,
boolean isSystemAdmin, List<String> orgIds, String currentAccountId,
boolean pageQuery) {
boolean filterLearningTime = queryDTO.getLearningTimeStart() != null && queryDTO.getLearningTimeEnd() != null;
// 数据权限
if (!isSystemAdmin) {
if (orgIds != null && !orgIds.isEmpty()) {
query.setParameter("orgIds", orgIds);
}
if (StringUtils.isNotBlank(currentAccountId)) {
query.setParameter("currentAccountId", currentAccountId);
}
}
if (StringUtils.isNotBlank(queryDTO.getName())) {
query.setParameter("name", queryDTO.getName());
}
if (StringUtils.isNotBlank(queryDTO.getSysType1())) {
query.setParameter("sysType1", queryDTO.getSysType1());
}
if (StringUtils.isNotBlank(queryDTO.getSysType2())) {
query.setParameter("sysType2", queryDTO.getSysType2());
}
if (StringUtils.isNotBlank(queryDTO.getSysType3())) {
query.setParameter("sysType3", queryDTO.getSysType3());
}
if (StringUtils.isNotBlank(queryDTO.getStatus())) {
query.setParameter("status", queryDTO.getStatus());
}
if (queryDTO.getPublish() != null) {
query.setParameter("publish", queryDTO.getPublish());
}
if (filterLearningTime) {
query.setParameter("learningTimeStart", queryDTO.getLearningTimeStart().withHour(0).withMinute(0).withSecond(0));
query.setParameter("learningTimeEnd", queryDTO.getLearningTimeEnd().withHour(23).withMinute(59).withSecond(59));
}
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());
}
// 这部分在where条件消化掉了故去掉
// if (queryDTO.getOpenCourse() != null) {
// query.setParameter("openCourse", queryDTO.getOpenCourse());
// }
if (StringUtils.isNotBlank(queryDTO.getOrgId())) {
query.setParameter("orgId", queryDTO.getOrgId());
}
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());
}
if (pageQuery) {
// 设置OFFSET和LIMIT
query.setFirstResult((queryDTO.getPageIndex() - 1) * queryDTO.getPageSize());
query.setMaxResults(queryDTO.getPageSize());
}
}
/**
* 将小驼峰命名转换为下划线命名
* @param camelCase 小驼峰命名字符串
* @return 下划线命名字符串
*/
public String camelToUnderline(String camelCase) {
if (StringUtils.isBlank(camelCase)) {
return camelCase;
}
StringBuilder result = new StringBuilder();
result.append(Character.toLowerCase(camelCase.charAt(0)));
for (int i = 1; i < camelCase.length(); i++) {
char ch = camelCase.charAt(i);
if (Character.isUpperCase(ch)) {
result.append("_");
result.append(Character.toLowerCase(ch));
} else {
result.append(ch);
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,97 @@
package com.xboe.module.course.dto;
import lombok.Data;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
@Data
public class CoursePageQueryDTO {
/**课程名称*/
private String name;
/**资源归属一级*/
private String resOwner1;
/**资源归属二级*/
private String resOwner2;
/**资源归属三级*/
private String resOwner3;
/**
* 资源归属机构id
*/
private String orgId;
/**是否发布,无就是全部*/
private Boolean publish;
/**创建人*/
private String createUser;
/**
* 创建人id
*/
private String createUserId;
/**课程分类的一级*/
private String sysType1;
/**课程分类的二级*/
private String sysType2;
/**课程分类的三级*/
private String sysType3;
/**授课教师*/
private String teacherName;
/**
* 授课教师id
*/
private String teacherId;
/**培训时间筛选类型*/
private String learningTimeType;
/**培训时间-左区间*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime learningTimeStart;
/**培训时间-右区间*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime learningTimeEnd;
/**审核状态*/
private String status;
/**启用状态*/
private Boolean enabled;
/**是否公开课*/
private Integer openCourse;
/**创建来源*/
private String createFrom;
/**页码*/
private Integer pageIndex;
/**每页数量*/
private Integer pageSize;
/**排序字段*/
private String orderField;
/**排序顺序*/
private Boolean orderAsc;
/**
* 是否是新建在线课程页面
* false时代表是在管理端的“在线管理”、“我开发的课程”
* true时代表是在学习路径图/项目管理 中,创建在线课程的选择页面
*/
private Boolean isCreateCourse;
}

View File

@@ -0,0 +1,52 @@
package com.xboe.module.course.dto;
/**
* 服务层返回
* @param <T>
*/
public class ServiceResponse<T> {
private boolean success;
private String message;
private T data;
public static <V> ServiceResponse<V> success(V data) {
ServiceResponse<V> response = new ServiceResponse<>();
response.setSuccess(true);
response.setData(data);
return response;
}
public static <V> ServiceResponse<V> failure(String message) {
ServiceResponse<V> response = new ServiceResponse<>();
response.setSuccess(false);
response.setMessage(message);
return response;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@@ -455,6 +455,26 @@ public class Course extends BaseEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime auditTime;
/**
* 课程时长(秒)
*/
@Column(name = "course_duration")
private Long courseDuration;
/**
* 排序权重
*/
@Column(name = "sort_weight")
private Integer sortWeight;
/**
* 创建来源
* teacher-教师端
* admin-管理员端
*/
@Column(name = "create_from")
private String createFrom;
public Course(String id,String name,String summary,String coverImg,String sysCreateAid,String sysCreateBy,Integer type,LocalDateTime publishTime){
super.setId(id);
this.name=name;

View File

@@ -1,5 +1,10 @@
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;
@@ -99,7 +104,14 @@ public class CourseContent extends BaseEntity {
@Transient
List<BoeaiSubtitleRsp> boeaiSubtitleRspList;
/**
* 展示名称(章名+节名,如果没有章节,则为课程名+节名)
* 25.12.15新增
*/
@Transient
private String displayName;
public CourseContent() {
}

View File

@@ -141,6 +141,26 @@ public class CourseHRBPAudit extends IdEntity {
@Column(name = "last_remark")
private String lastRemark;
/**
* 审核类型
* 1-创建 2-编辑 3-停用 4-启用
*/
@Column(name = "audit_type")
private Integer auditType;
/**
* 审核结果
* 1-通过, 0-驳回
*/
@Column(name = "audit_result")
private Integer auditResult;
/**
* BPM审核id
*/
@Column(name = "audit_id")
private String auditId;
@Transient
private String courseName;

View File

@@ -27,6 +27,11 @@ public class CourseToCourseFullText {
cft.setDevice(c.getDevice());
cft.setIsTop(c.getIsTop()? 1:0);
if (c.getSortWeight() != null) {
cft.setSortWeight(c.getSortWeight());
} else {
cft.setSortWeight(9999);
}
cft.setKeywords(c.getKeywords());
//DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//Long second = c.getPublishTime().toEpochSecond(ZoneOffset.of("+8"));

View File

@@ -1,7 +1,6 @@
package com.xboe.module.course.service;
import java.util.List;
import com.xboe.common.PageList;
import com.xboe.module.course.dto.CourseContentDto;
import com.xboe.module.course.dto.SortItem;
import com.xboe.module.course.entity.CourseAssess;
@@ -9,6 +8,8 @@ import com.xboe.module.course.entity.CourseContent;
import com.xboe.module.course.entity.CourseExam;
import com.xboe.module.course.entity.CourseHomeWork;
import java.util.List;
/**
* 课程内容,当前是分着处理,之后看是否与课程服务合并在一起
* */
@@ -54,7 +55,27 @@ public interface ICourseContentService{
* @return
*/
List<CourseContent> getByCourseId(String courseId);
/**
* 根据id集合得到内容
*
* @param ids id集合
* @return 课程内容集合
*/
List<CourseContent> getByIds(List<String> ids);
/**
* 根据课程id、章节id得到课程所有目录即章节分页顺序按orderIndex 从小到大的顺序
* 25.11.26新增
*
* @param pageIndex 页码
* @param pageSize 每页数量
* @param courseId 课程id
* @param sectionIdList 章节id集合
* @return 课程章节信息集合分页
*/
PageList<CourseContent> getByCourseIdPage(int pageIndex, int pageSize, String courseId, List<String> sectionIdList);
/**
* 更新内容顺序
* @param courseId

View File

@@ -0,0 +1,59 @@
package com.xboe.module.course.service;
import com.xboe.common.PageList;
import com.xboe.core.CurrentUser;
import com.xboe.module.course.dto.CoursePageQueryDTO;
import com.xboe.module.course.dto.ServiceResponse;
import com.xboe.module.course.vo.CoursePageVo;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface ICoursePageService {
/**
* 分页查询
* @param currentUser 教师端传入此选项
* @param coursePageQueryDTO
* @return
*/
PageList<CoursePageVo> pageQuery(CurrentUser currentUser, CoursePageQueryDTO coursePageQueryDTO);
/**
* 当前用户是否展示置顶相关功能
* @param currentUser
* @return
*/
boolean showSetTop(CurrentUser currentUser);
/**
* 置顶列表
* @return
*/
List<CoursePageVo> topList();
/**
* 置顶/取消置顶
* @param courseId
* @param top true代表执行置顶操作false代表执行取消置顶操作
* @return
*/
@Transactional
ServiceResponse<Boolean> top(String courseId, boolean top);
/**
* 置顶列表排序修改
* @param topList
* @return
*/
@Transactional
ServiceResponse<List<CoursePageVo>> topListSortChange(List<CoursePageVo> topList);
/**
* 导出课程列表
* @param coursePageQueryDTO
* @param response
*/
void exportCourseList(CoursePageQueryDTO coursePageQueryDTO, HttpServletResponse response);
}

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

@@ -166,6 +166,7 @@ public class CourseAuditServiceImpl implements ICourseAuditService{
//更新审核记录状态
courseHRBPAuditDao.updateMultiFieldById(auditId,
UpdateBuilder.create("status", pass? CourseHRBPAudit.STATUS_PASS:CourseHRBPAudit.STATUS_NOPASS),
UpdateBuilder.create("auditResult", pass ? 1 : 0),
UpdateBuilder.create("auditRemark", remark),
UpdateBuilder.create("auditUser", name),
UpdateBuilder.create("auditAid", aid),

View File

@@ -5,11 +5,12 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.core.cache.IXaskCache;
import com.xboe.core.cache.XaskCacheProvider;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.IFieldFilter;
import com.xboe.core.orm.UpdateBuilder;
import com.xboe.module.course.dao.*;
import com.xboe.module.course.dto.CourseContentDto;
@@ -141,6 +142,35 @@ 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新增
*
* @param pageIndex 页码
* @param pageSize 每页数量
* @param courseId 课程id
* @param sectionIdList 章节id集合
* @return 课程章节信息集合分页
*/
@Override
public PageList<CourseContent> getByCourseIdPage(int pageIndex, int pageSize, String courseId, List<String> sectionIdList) {
List<IFieldFilter> filters = new ArrayList<>();
filters.add(FieldFilters.eq("courseId", courseId));
filters.add(FieldFilters.eq("deleted", false));
if (sectionIdList != null && !sectionIdList.isEmpty()) {
filters.add(FieldFilters.in("csectionId", sectionIdList));
}
return ccDao.findPage(pageIndex, pageSize, OrderCondition.asc("sortIndex"), filters.toArray(new IFieldFilter[0]));
}
@Override
public CourseHomeWork getHomework(String ccid) {
CourseHomeWork hw=homeworkDao.findOne(FieldFilters.eq("contentId", ccid));

View File

@@ -0,0 +1,524 @@
package com.xboe.module.course.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.common.exception.AppException;
import com.xboe.config.MySqlSchemaProperties;
import com.xboe.core.CurrentUser;
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.data.dto.UserOrgIds;
import com.xboe.data.outside.IOutSideDataService;
import com.xboe.enums.CourseCreateFromEnum;
import com.xboe.enums.CourseStatusEnum;
import com.xboe.module.course.dao.CourseDao;
import com.xboe.module.course.dao.CourseTeacherDao;
import com.xboe.module.course.dto.BoeaiCourseDto;
import com.xboe.module.course.dto.CoursePageQueryDTO;
import com.xboe.module.course.dto.ServiceResponse;
import com.xboe.module.course.entity.Course;
import com.xboe.module.course.entity.CourseTeacher;
import com.xboe.module.course.service.ICourseFullTextSearch;
import com.xboe.module.course.service.ICoursePageService;
import com.xboe.module.course.utils.HttpUtils;
import com.xboe.module.course.vo.CoursePageVo;
import com.xboe.module.excel.ExportsExcelSenderUtil;
import com.xboe.module.type.entity.Type;
import com.xboe.module.type.service.ITypeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@EnableConfigurationProperties({MySqlSchemaProperties.class})
@Service
@Slf4j
public class CoursePageServiceImpl implements ICoursePageService {
@Value("${kjb.aicoreUrl}")
private String aicoreUrl;
@Autowired
private MySqlSchemaProperties mySqlSchemaProperties;
@Resource
private CourseDao courseDao;
@Resource
private CourseTeacherDao courseTeacherDao;
@Autowired
private IOutSideDataService outSideDataService;
@Resource
private ITypeService typeService;
@Autowired(required = false)
private ICourseFullTextSearch fullTextSearch;
@Resource
private PublishCourseUtil publishUtil;
@Override
public PageList<CoursePageVo> pageQuery(CurrentUser currentUser, CoursePageQueryDTO coursePageQueryDTO) {
/*
* 1. 前置权限过滤
* 权限说明:管理员端 可查看本人创建的课程及被授权的课程,其他课程不可见,被赋予查看权的用户可直接引用可查看的课程;
* 教师端 当前用户创建的课程及具有管理权、查看权的课程清单
*/
UserOrgIds userOrgIds = outSideDataService.getOrgIds();
List<String> orgIds = userOrgIds.getIds();
boolean isSystemAdmin = userOrgIds.getPermissions().containsKey(UserOrgIds.IsSystemAdminKey)
&& userOrgIds.getPermissions().get(UserOrgIds.IsSystemAdminKey);
String currentAccountId = currentUser == null ? null : currentUser.getAccountId();
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());
// 整理数据
if (voList != null && !voList.isEmpty()) {
for (CoursePageVo vo : voList) {
// status为null的给默认值
if (vo.getStatus() == null) {
vo.setStatus(Course.STATUS_NONE);
}
// createFrom为空的
if (StringUtils.isBlank(vo.getCreateFrom())) {
vo.setCreateFrom(CourseCreateFromEnum.ADMIN.getCode());
}
// openCourse
if (vo.getOpenCourse() == null) {
vo.setOpenCourse(0);
}
// published
if (vo.getPublished() == null) {
vo.setPublished(false);
}
// enabled
if (vo.getEnabled() == null) {
vo.setEnabled(true);
}
// isTop
if (vo.getIsTop() == null) {
vo.setIsTop(false);
}
}
// AI课程相关
handleAiInfoFromKJB(voList);
}
PageList<CoursePageVo> result = new PageList<>();
result.setCount((int) total);
result.setPageSize(coursePageQueryDTO.getPageSize());
result.setList(voList);
return result;
}
@Override
public boolean showSetTop(CurrentUser currentUser) {
UserOrgIds userOrgIds = outSideDataService.getOrgIds();
return userOrgIds.getPermissions().containsKey(UserOrgIds.IsSystemAdminKey)
&& userOrgIds.getPermissions().get(UserOrgIds.IsSystemAdminKey);
}
@Override
public List<CoursePageVo> topList() {
// 构建查询条件
List<Course> courseList = courseDao.findList(FieldFilters.eq("deleted", false),
FieldFilters.eq("isTop", true));
// 如果有值,查询教师数据
List<CourseTeacher> courseTeacherList;
if (!courseList.isEmpty()) {
List<String> courseIds = courseList.stream()
.map(Course::getId)
.collect(Collectors.toList());
courseTeacherList = getCourseTeacherList(courseIds);
} else {
courseTeacherList = new ArrayList<>();
}
return courseList.stream()
.map(c -> convertToVo(c, courseTeacherList))
.sorted(Comparator.comparing(CoursePageVo::getSortWeight, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(CoursePageVo::getTopTime, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
}
@Override
public ServiceResponse<Boolean> top(String courseId, boolean top) {
// 1. 查询课程数据
Course course = courseDao.get(courseId);
if (top) {
// 2. 执行置顶操作
// 2.1 目前课程是否已置顶
if (course.getIsTop() != null && course.getIsTop()) {
// 错误提示:已置顶
return ServiceResponse.failure("已置顶");
}
// 2.2 查看置顶列表数量
List<CoursePageVo> topList = topList();
int topCount = topList.size();
if (topCount >= 10) {
return ServiceResponse.failure("最多只能置顶10个课程");
}
// 2.3 设置置顶
CoursePageVo thisVo = new CoursePageVo();
thisVo.setId(courseId);
thisVo.setIsTop(true);
thisVo.setSortWeight(0);
List<CoursePageVo> newTopList = new ArrayList<>();
newTopList.add(thisVo);
for (int i = 0; i < topList.size(); i++) {
CoursePageVo vo = topList.get(i);
vo.setSortWeight(i + 1);
newTopList.add(vo);
}
topListSortChange(newTopList);
} else {
Map<String, Object> fieldMap = new HashMap<>();
// 3. 取消置顶
// 3.1 课程是否已置顶
if (course.getIsTop() == null || !course.getIsTop()) {
return ServiceResponse.failure("未置顶");
}
// 3.2 取消置顶
course.setIsTop(false);
course.setSortWeight(9999);
courseDao.updateMultiFieldById(courseId,
UpdateBuilder.create("isTop", top),
UpdateBuilder.create("topTime", null),
UpdateBuilder.create("sortWeight", 9999));
fieldMap.put("isTop", 0);
fieldMap.put("sortWeight", 9999);
// ES同步
if (this.fullTextSearch != null) {
publishUtil.updateFieldByDocId(course.getFullTextId(), fieldMap);
}
}
return ServiceResponse.success(true);
}
@Override
public ServiceResponse<List<CoursePageVo>> topListSortChange(List<CoursePageVo> topList) {
// 1. 按sortWeight升序排序
topList.sort(Comparator.comparingInt(CoursePageVo::getSortWeight));
// 2. 更新
LocalDateTime now = LocalDateTime.now();
for (int i = 0, len = topList.size(); i < len; i++) {
CoursePageVo vo = topList.get(i);
String id = vo.getId();
Map<String, Object> fieldMap = new HashMap<>();
courseDao.updateMultiFieldById(id,
UpdateBuilder.create("isTop", true),
UpdateBuilder.create("topTime", now),
UpdateBuilder.create("sortWeight", i));
fieldMap.put("isTop", 1);
fieldMap.put("sortWeight", i);
if (this.fullTextSearch != null) {
Object fullId = courseDao.findField("fullTextId", FieldFilters.eq("id", id));
if (fullId != null) {
publishUtil.updateFieldByDocId((String) fullId, fieldMap);
}
}
}
return ServiceResponse.success(topList);
}
@Override
public void exportCourseList(CoursePageQueryDTO coursePageQueryDTO, HttpServletResponse response) {
/*
* 1. 前置权限过滤
* 权限说明:管理员端 可查看本人创建的课程及被授权的课程,其他课程不可见,被赋予查看权的用户可直接引用可查看的课程;
* 教师端 当前用户创建的课程及具有管理权、查看权的课程清单
*/
UserOrgIds userOrgIds = outSideDataService.getOrgIds();
List<String> orgIds = userOrgIds.getIds();
boolean isSystemAdmin = userOrgIds.getPermissions().containsKey(UserOrgIds.IsSystemAdminKey)
&& userOrgIds.getPermissions().get(UserOrgIds.IsSystemAdminKey);
List<CoursePageVo> courseList = courseDao.queryCourse(coursePageQueryDTO, isSystemAdmin, orgIds, null, false, mySqlSchemaProperties.getUserCenterSchema());
// 导出
LinkedHashMap<String, String> exportMap = new LinkedHashMap<>();
exportMap.put("课程名称", "name");
exportMap.put("课程分类", "sysType");
exportMap.put("授课教师", "teacherName");
exportMap.put("课程时长", "courseDuration");
exportMap.put("学习人数", "studys");
exportMap.put("课程评分", "score");
exportMap.put("审核状态", "status");
exportMap.put("发布状态", "published");
exportMap.put("启停用状态", "enabled");
exportMap.put("公开课", "openCourse");
exportMap.put("资源归属", "orgName");
exportMap.put("创建人", "sysCreateBy");
exportMap.put("创建来源", "createFrom");
exportMap.put("创建时间", "sysCreateTime");
List<Map<String, Object>> dataList = new ArrayList<>();
if (courseList != null && !courseList.isEmpty()) {
// 查询sysType
Type sysTypeParam = new Type();
sysTypeParam.setSysResType(1);
sysTypeParam.setStatus(1);
List<Type> typeList = typeService.findList(sysTypeParam);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
for (CoursePageVo coursePageVo : courseList) {
Map<String, Object> map = new HashMap<>();
map.put("name", coursePageVo.getName());
// 课程分类
StringBuilder sysTypeBuilder = new StringBuilder();
typeList.stream()
.filter(t -> StringUtils.equals(coursePageVo.getSysType1(), t.getId()))
.findFirst().ifPresent(t -> sysTypeBuilder.append(t.getName()));
if (StringUtils.isNotBlank(coursePageVo.getSysType2())) {
typeList.stream()
.filter(t -> StringUtils.equals(coursePageVo.getSysType2(), t.getId()))
.findFirst().ifPresent(t -> sysTypeBuilder.append("/").append(t.getName()));
}
if (StringUtils.isNotBlank(coursePageVo.getSysType3())) {
typeList.stream()
.filter(t -> StringUtils.equals(coursePageVo.getSysType3(), t.getId()))
.findFirst().ifPresent(t -> sysTypeBuilder.append("/").append(t.getName()));
}
map.put("sysType", sysTypeBuilder.toString());
// map.put("sysType", coursePageVo.getSysType1());
map.put("teacherName", coursePageVo.getTeacherName());
// 课程时长:秒转分
long minutes = Math.round((double) coursePageVo.getCourseDuration() / 60);
map.put("courseDuration", minutes);
map.put("studys", coursePageVo.getStudys());
// 课程评分显示1位小数四舍五入
map.put("score", coursePageVo.getScore() == null ? "0.0" : String.format("%.1f", Math.round(coursePageVo.getScore() * 10.0) / 10.0));
map.put("status", CourseStatusEnum.getByCode(coursePageVo.getStatus()).getLabel());
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.getOrgFullName()); // 修改为组织全名
map.put("sysCreateBy", coursePageVo.getSysCreateBy());
map.put("createFrom", CourseCreateFromEnum.getByCode(coursePageVo.getCreateFrom()).getLabel());
map.put("sysCreateTime", coursePageVo.getSysCreateTime() == null ? "" : formatter.format(coursePageVo.getSysCreateTime()));
dataList.add(map);
}
}
// output
try (OutputStream out = response.getOutputStream()) {
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment;filename=course.xlsx");
ExportsExcelSenderUtil.exportDynamic(exportMap, dataList, out, null);
} catch (Exception e) {
throw new AppException("导出课程列表发生异常", e);
}
}
/**
* 生成过滤条件
*
* @param dto
* @return
*/
private List<IFieldFilter> createFilters(CoursePageQueryDTO dto) {
List<IFieldFilter> filters = new ArrayList<>();
if (StringUtils.isNotBlank(dto.getName())) {
filters.add(FieldFilters.like("c.name", dto.getName()));
}
if (StringUtils.isNotBlank(dto.getCreateUser())) {
filters.add(FieldFilters.like("c.sysCreateBy", dto.getCreateUser()));
}
if (StringUtils.isNotBlank(dto.getResOwner3())) {
filters.add(FieldFilters.eq("c.resOwner3", dto.getResOwner3()));
}
if (StringUtils.isNotBlank(dto.getResOwner2())) {
filters.add(FieldFilters.eq("c.resOwner2", dto.getResOwner2()));
}
if (StringUtils.isNotBlank(dto.getResOwner1())) {
filters.add(FieldFilters.eq("c.resOwner1", dto.getResOwner1()));
}
if (dto.getPublish() != null) {
filters.add(FieldFilters.eq("c.published", dto.getPublish()));
}
// 状态查询
if (StringUtils.isNotBlank(dto.getStatus())) {
filters.add(FieldFilters.eq("c.status", Integer.valueOf(dto.getStatus())));
}
// 系统分类查询
if (StringUtils.isNotBlank(dto.getSysType3())) {
filters.add(FieldFilters.eq("c.sysType3", dto.getSysType3()));
}
if (StringUtils.isNotBlank(dto.getSysType2())) {
filters.add(FieldFilters.eq("c.sysType2", dto.getSysType2()));
}
if (StringUtils.isNotBlank(dto.getSysType1())) {
filters.add(FieldFilters.eq("c.sysType1", dto.getSysType1()));
}
// 是否启用
if (dto.getEnabled() != null) {
filters.add(FieldFilters.eq("c.enabled", dto.getEnabled()));
}
// 时间范围查询(待定)
return filters;
}
private List<CourseTeacher> getCourseTeacherList(List<String> courseIds) {
return courseTeacherDao.findList(OrderCondition.desc("courseId"), FieldFilters.in("courseId", courseIds));
}
/**
* 将Course转换为CoursePageVo
* @param course
* @return
*/
private CoursePageVo convertToVo(Course course, List<CourseTeacher> courseTeacherList) {
CoursePageVo vo = new CoursePageVo();
vo.setId(course.getId());
vo.setName(course.getName());
vo.setCoverImg(course.getCoverImg());
vo.setSysType1(course.getSysType1());
vo.setSysType2(course.getSysType2());
vo.setSysType3(course.getSysType3());
vo.setResOwner1(course.getResOwner1());
vo.setResOwner2(course.getResOwner2());
vo.setResOwner3(course.getResOwner3());
vo.setSysCreateBy(course.getSysCreateBy());
vo.setCreateFrom(course.getCreateFrom());
vo.setSysCreateTime(course.getSysCreateTime());
vo.setSysUpdateTime(course.getSysUpdateTime());
vo.setForUsers(course.getForUsers());
vo.setStatus(course.getStatus());
// auditType 需要从其他地方获取,这里暂时设置为默认值
vo.setAuditType(1);
vo.setPublished(course.getPublished());
vo.setPublishTime(course.getPublishTime());
vo.setStudys(course.getStudys());
vo.setScore(course.getScore());
// courseDuration 需要计算,这里暂时设置为默认值
vo.setCourseDuration(course.getCourseDuration());
vo.setEnabled(course.getEnabled());
vo.setSortWeight(course.getSortWeight());
vo.setTopTime(course.getTopTime());
vo.setIsTop(course.getIsTop());
// 获取教师名称
List<String> teacherNames = courseTeacherList.stream()
.filter(ct -> StringUtils.equals(ct.getCourseId(), course.getId()))
.map(CourseTeacher::getTeacherName)
.collect(Collectors.toList());
if (!teacherNames.isEmpty()) {
vo.setTeacherName(String.join("", teacherNames));
}
return vo;
}
private void handleOrder(QueryBuilder query, String orderField, Boolean orderAsc) {
if (StringUtils.isNotBlank(orderField)) {
boolean isAsc = orderAsc == null || orderAsc;
// 1. 多字段排序
if (StringUtils.equals(orderField, "sysType")) {
if (isAsc) {
query.addOrder(OrderCondition.asc("c.sysType1"));
query.addOrder(OrderCondition.asc("c.sysType2"));
query.addOrder(OrderCondition.asc("c.sysType3"));
} else {
query.addOrder(OrderCondition.desc("c.sysType1"));
query.addOrder(OrderCondition.desc("c.sysType2"));
query.addOrder(OrderCondition.desc("c.sysType3"));
}
} else if (StringUtils.equals(orderField, "resOwner")) {
if (isAsc) {
query.addOrder(OrderCondition.asc("c.resOwner1"));
query.addOrder(OrderCondition.asc("c.resOwner2"));
query.addOrder(OrderCondition.asc("c.resOwner3"));
} else {
query.addOrder(OrderCondition.desc("c.resOwner1"));
query.addOrder(OrderCondition.desc("c.resOwner2"));
query.addOrder(OrderCondition.desc("c.resOwner3"));
}
} else {
if (isAsc) {
query.addOrder(OrderCondition.asc("c." + orderField));
} else {
query.addOrder(OrderCondition.desc("c." + orderField));
}
}
} else {
OrderCondition isTop = OrderCondition.desc("c.isTop");
query.addOrder(isTop);
OrderCondition sortWeightOc = OrderCondition.asc("c.sortWeight");
query.addOrder(sortWeightOc);
OrderCondition createTimeOc = OrderCondition.desc("c.sysCreateTime");
query.addOrder(createTimeOc);
}
}
private void handleAiInfoFromKJB(List<CoursePageVo> courseList) {
// 获取ai课程信息
String url = aicoreUrl + "/aiVideo/getCourseList";
// 入参
JSONObject jsonObject = new JSONObject();
List<String> courseIds = courseList.stream()
.map(CoursePageVo::getId)
.collect(Collectors.toList());
jsonObject.put("courseIds", courseIds);
// 请求
String result = HttpUtils.sendMessage(jsonObject.toJSONString(), url);
log.info("---KJB --getCourseList 接口返回结果result: {}", result);
JSONObject jsonResult = JSONObject.parseObject(result);
String data = jsonResult.getString("rows");
List<BoeaiCourseDto> boeaiCourseDto = JSON.parseArray(data, BoeaiCourseDto.class);
// 反解析
if (boeaiCourseDto != null && !boeaiCourseDto.isEmpty()) {
for (BoeaiCourseDto boeaiCourse : boeaiCourseDto) {
String courseId = boeaiCourse.getCourseId();
courseList.stream()
.filter(course -> StringUtils.equals(course.getId(), courseId))
.findFirst().ifPresent(course -> {
course.setSummaryContent(boeaiCourseDto.get(0).getSummaryContent());
course.setSummaryStatus(boeaiCourseDto.get(0).getSummaryStatus());
course.setAiSet(boeaiCourseDto.get(0).getAiSet());
course.setAiAbstract(boeaiCourseDto.get(0).getAiAbstract());
course.setAiDraft(boeaiCourseDto.get(0).getAiDraft());
course.setAiTranslate(boeaiCourseDto.get(0).getAiTranslate());
course.setLanguageStatus(boeaiCourseDto.get(0).getLanguageStatus());
course.setLanguageCode(boeaiCourseDto.get(0).getLanguageCode() == null ? new ArrayList<>() :Arrays.asList(boeaiCourseDto.get(0).getLanguageCode().split(",")));
});
}
}
}
}

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

@@ -29,12 +29,14 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xboe.api.ThirdApi;
import com.xboe.core.orm.*;
import com.xboe.enums.CourseAuditTypeEnum;
import com.xboe.module.course.dao.*;
import com.xboe.module.course.dto.*;
import com.xboe.module.course.entity.*;
import com.xboe.module.course.dao.*;
import com.xboe.module.course.dto.CourseTagRelationDto;
import com.xboe.module.course.entity.*;
import com.xboe.module.course.service.ICourseHRBPAuditService;
import com.xboe.module.course.service.ICourseTagService;
import com.xboe.module.course.utils.HttpUtils;
import com.xboe.school.study.dao.StudyCourseDao;
@@ -110,6 +112,9 @@ public class CourseServiceImpl implements ICourseService {
@Resource
private ICourseGradeService courseGradeService;
@Resource
private ICourseHRBPAuditService courseHRBPAuditService;
@Resource
private IAccountService accountService;
@@ -1079,6 +1084,8 @@ public class CourseServiceImpl implements ICourseService {
c.setSysVersion(courseDao.getVersion(c.getId()));
}
log.info("-------- 课程保存 update ------- tag = {} " , c.getTags());
// 更改状态
c.setStatus(Course.STATUS_NONE);
courseDao.update(c);
c.setSysVersion(courseDao.getVersion(c.getId()));
full.getCourse().setSysVersion(c.getSysVersion());
@@ -1104,16 +1111,21 @@ public class CourseServiceImpl implements ICourseService {
}
//更新后需要删除发布
if (StringUtils.isNotBlank(c.getFullTextId())) {
publishUtil.removeByDocId(c.getFullTextId());
}
// if (StringUtils.isNotBlank(c.getFullTextId())) {
// publishUtil.removeByDocId(c.getFullTextId());
//
// }
}
@Override
public void submit(CourseFullDto full) throws Exception {
String courseId = full.getCourse().getId();
// 查看课程中现有的审核记录
CourseHRBPAudit query = new CourseHRBPAudit();
query.setCourseId(courseId);
query.setStatus(CourseHRBPAudit.STATUS_PASS);
List<CourseHRBPAudit> auditList = courseHRBPAuditService.findList(query);
Course c = full.getCourse();//当前的课程信息
if (c.getVisible() == null) {
c.setVisible(true);
@@ -1167,6 +1179,11 @@ public class CourseServiceImpl implements ICourseService {
hrbpAudit.setAuditRemark("");
hrbpAudit.setForward(CourseHRBPAudit.FORWARD_NONE);
hrbpAudit.setStatus(CourseHRBPAudit.STATUS_NONE);
if (auditList == null || auditList.isEmpty()) {
hrbpAudit.setAuditType(CourseAuditTypeEnum.CREATE.getCode());
} else {
hrbpAudit.setAuditType(CourseAuditTypeEnum.UPDATE.getCode());
}
courseHRBPAuditDao.save(hrbpAudit);
}
@@ -1229,15 +1246,24 @@ public class CourseServiceImpl implements ICourseService {
@Override
public void submit(CourseHRBPAudit hrbpAudit) throws Exception {
String courseId = hrbpAudit.getCourseId();
// 查看课程中现有的审核记录
CourseHRBPAudit query = new CourseHRBPAudit();
query.setCourseId(courseId);
query.setStatus(CourseHRBPAudit.STATUS_PASS);
List<CourseHRBPAudit> auditList = courseHRBPAuditService.findList(query);
CourseAuditTypeEnum auditTypeEnum = (auditList == null || auditList.isEmpty()) ? CourseAuditTypeEnum.CREATE : CourseAuditTypeEnum.UPDATE;
courseDao.updateMultiFieldById(hrbpAudit.getCourseId(), UpdateBuilder.create("status", Course.STATUS_SUBMIT),
UpdateBuilder.create("published", false), UpdateBuilder.create("publishTime", LocalDateTime.now()));
// courseDao.updateMultiFieldById(courseId, UpdateBuilder.create("status", Course.STATUS_SUBMIT),
// UpdateBuilder.create("published", false), UpdateBuilder.create("publishTime", LocalDateTime.now()));
courseDao.updateMultiFieldById(courseId, UpdateBuilder.create("status", Course.STATUS_SUBMIT));
//保存审核信息
hrbpAudit.setAddTime(LocalDateTime.now());
hrbpAudit.setAuditRemark("");
hrbpAudit.setForward(CourseHRBPAudit.FORWARD_NONE);
hrbpAudit.setStatus(CourseHRBPAudit.STATUS_NONE);
hrbpAudit.setAuditType(auditTypeEnum.getCode());
courseHRBPAuditDao.save(hrbpAudit);
}
@@ -1338,6 +1364,7 @@ public class CourseServiceImpl implements ICourseService {
if (from != null) {
courseHRBPAuditDao.updateMultiFieldById(auditId,
UpdateBuilder.create("status", pass ? CourseHRBPAudit.STATUS_PASS : CourseHRBPAudit.STATUS_NOPASS),
UpdateBuilder.create("auditResult", pass ? 1 : 0),
UpdateBuilder.create("auditRemark", remark),
UpdateBuilder.create("auditUser", name),
UpdateBuilder.create("auditAid", aid),
@@ -1347,6 +1374,7 @@ public class CourseServiceImpl implements ICourseService {
} else {
courseHRBPAuditDao.updateMultiFieldById(auditId,
UpdateBuilder.create("status", pass ? CourseHRBPAudit.STATUS_PASS : CourseHRBPAudit.STATUS_NOPASS),
UpdateBuilder.create("auditResult", pass ? 1 : 0),
UpdateBuilder.create("auditRemark", remark),
UpdateBuilder.create("auditUser", name),
UpdateBuilder.create("auditAid", aid),
@@ -1919,7 +1947,10 @@ public class CourseServiceImpl implements ICourseService {
@Override
public void revokeSubmit(String id) {
//courseDao.updateFieldById(id, "status",Course.STATUS_NONE);
courseDao.updateMultiFieldById(id, UpdateBuilder.create("published", false), UpdateBuilder.create("status", Course.STATUS_NONE));
// 撤回后不修改发布状态
courseDao.updateMultiFieldById(id,
// UpdateBuilder.create("published", false),
UpdateBuilder.create("status", Course.STATUS_NONE));
//如果此课程有未审核的处理就应该主去掉
courseHRBPAuditDao.deleteByFilter(FieldFilters.eq("courseId", id), FieldFilters.eq("status", CourseHRBPAudit.STATUS_NONE));
}
@@ -1932,6 +1963,29 @@ public class CourseServiceImpl implements ICourseService {
return course.getName();
}
/**
* 生成新的课程名称
* @param originalName
* @param existingNames
* @return
*/
private String generateNewCourseName(String originalName, List<String> existingNames) {
int maxNum = 0;
String baseName = originalName.replaceAll("\\s*\\(\\d+\\)\\s*$", "").trim();
for (String name : existingNames) {
// 匹配 "baseName (数字)"
if (name.startsWith(baseName)) {
String suffix = name.substring(baseName.length()).trim();
if (suffix.matches("^\\(\\d+\\)$")) {
int num = Integer.parseInt(suffix.substring(1, suffix.length() - 1));
maxNum = Math.max(maxNum, num);
}
}
}
return baseName + "(" + (maxNum + 1) + ")";
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public String copyCourse(String id, String refId, String refType, String aid, String aname) {
@@ -1952,6 +2006,8 @@ public class CourseServiceImpl implements ICourseService {
String newId = IDGenerator.generate();
String courseName = this.courseName(id);
List<String> cpCourseNameList = courseDao.queryCourseNames(courseName);
String newCourseName = generateNewCourseName(courseName, cpCourseNameList);
LocalDateTime time = LocalDateTime.now();
String mess = null;
if (courseName.length() < 96) {
@@ -1965,7 +2021,7 @@ public class CourseServiceImpl implements ICourseService {
"order_study,status)" +
"select '" + newId + "',org_id,'" + id + "','" + refId + "','" + refType + "'," + visible + ",'" + aid + "','" + aname + "','" + time + "',0,'" + aname + "'," +
"'" + time + "',1,comments,cover_img,dead_time,device,enable_remark,enabled," +
"erasable,0,for_scene,for_users,0,keywords,'" + courseName + "(1)" + "',open_object," +
"erasable,0,for_scene,for_users,0,keywords,'" + newCourseName + "',open_object," +
"overview,pass_formula,0,publish_time,0,res_owner1,res_owner2," +
"res_owner3,score,score_formula,0,source,study_time,0,summary," +
"sys_type1,sys_type2,sys_type3,tags,top_time,trample_count,type,value,0," +

View File

@@ -2,6 +2,7 @@ package com.xboe.module.course.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
@@ -122,6 +123,14 @@ public class PublishCourseUtil {
log.error("更新全文索引字段错误",e);
}
}
public void updateFieldByDocId(String docId, Map<String, Object> fieldMap) {
try {
fullTextSearch.updateFields(ICourseFullTextSearch.DEFAULT_INDEX_NAME, fieldMap, docId);
}catch(Exception e) {
log.error("更新全文索引字段错误",e);
}
}
public void removeByDocId(String docId){
if(fullTextSearch==null) {

View File

@@ -0,0 +1,238 @@
package com.xboe.module.course.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Transient;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class CoursePageVo {
/**
* 课程ID
*/
private String id;
/**
* 课程名称
*/
private String name;
/**
* 课程类型
*/
private Integer type;
/**
* 课程封面图片地址
*/
private String coverImg;
/**
* 课程分类一级
*/
private String sysType1;
/**
* 课程分类二级
*/
private String sysType2;
/**
* 课程分类三级
*/
private String sysType3;
/**
* 资源归属一级
*/
private String resOwner1;
/**
* 资源归属二级
*/
private String resOwner2;
/**
* 资源归属三级
*/
private String resOwner3;
/**
* 机构ID
*/
private String orgId;
/**
* 机构名称
*/
private String orgName;
/**
* 机构全名
*/
private String orgFullName;
/**
* 创建人
*/
private String sysCreateBy;
/**
* 创建来源
* teacher-教师端
* admin-管理员端
*/
private String createFrom;
/**
* 创建时间
*/
@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;
/**
* 目标人群
*/
private String forUsers;
/**
* 审核状态
* 1-无审核状态
* 2-审核中
* 3-审核驳回
* 5-审核通过
*/
private Integer status;
/**
* 课程审核类型
* 1-课程创建
* 2-课程编辑
* 3-课程启用
* 4-课程停用
* 5-撤回申请
*/
private Integer auditType;
/**
* 发布状态
* false-未发布
* true-已发布
*/
private Boolean published;
/**
* 发布时间
*/
private LocalDateTime publishTime;
/**
* 学习人数
*/
private Integer studys;
/**
* 课程评分
*/
private Float score;
/**
* 课程时长(秒)
*/
private Long courseDuration;
/**
* 启用状态
* false-停用
* true-启用
*/
private Boolean enabled;
/**
* 授课教师
*/
private String teacherName;
/**
* 是否公开课
*/
private Integer openCourse;
/**
* 是否置顶
*/
private Boolean isTop;
/**
* 置顶时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime topTime;
/**
* 排序权重
*/
private Integer sortWeight;
/**
* 是否权限课程
* 默认为true
* TODO 在线课优化二期会对此字段进行其他的赋值操作
*/
private Boolean isPermission = true;
//region AI课程项目组相关字段
/**
* 0关闭1打开
*/
private Integer aiSet;
/**
* 摘要 0:上架1下架
*/
private Integer aiAbstract;
/**
* 文稿 0:上架1下架
*/
private Integer aiDraft;
/**
* 翻译 0:上架1下架
*/
private Integer aiTranslate;
/**
* 语言 0:关闭1打开
*/
private Integer languageStatus;
/**
* 语言 zh-CN,en-US,ja-JP,ko-KR
*/
private List<String> languageCode;
/**
* 摘要
*/
private String summaryContent;
/**
* 摘要状态 0:下架1上架
*/
private Integer summaryStatus;
//endregion
}

View File

@@ -8,13 +8,32 @@ import lombok.Data;
@Data
public class CourseStudyVo {
/**
* 内容id
*/
private String contentId;
/**
* 内容名称
*/
private String contentName;
/**
* 学习状态( 1-未学习2-学习中9-学习完成)
*/
private Integer status;
/**
* 学习时长
* 25.11.26新增
* studyDuration字段被占用了换一个
*/
private Integer learningDuration;
/**
* 学习进度(理论上来说这个字段应该是学习时长,但是原接口如此,暂时保留此字段)
*/
private Integer studyDuration;
}

View File

@@ -1,31 +1,21 @@
package com.xboe.module.excel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 导出excel工具类
@@ -34,148 +24,6 @@ import org.springframework.stereotype.Component;
@Component
public class ExportsExcelSenderUtil {
// @Resource
// ITeacherService teacherService;
// @Resource
// ICourseService courseService;
//
//
// private HSSFWorkbook workbook;
//
// /**
// * 教师导出
// * */
// public void teacherExports(Integer pageIndex, Integer pageSize, Teacher entity, OrderCondition order){
//// 创建excel对象
// try {
// workbook = new HSSFWorkbook();
//// 创建工作表 参数 表名
// HSSFSheet sheet = workbook.createSheet("教师信息");
//// 创建标题行
// HSSFRow row = sheet.createRow(0);
// String[] title = {"姓名","部门", "创建时间", "修改时间", "授课时长", "在职状态"};
//// 单元格对象 标题行
// HSSFCell cell = null;
// CellStyle cellStyle = this.cellStyle(workbook);
// for (int i = 0; i < title.length; i++) {
// cell = row.createCell(i);
// cell.setCellValue(title[i]);
// cell.setCellStyle(cellStyle);
// sheet.setColumnWidth(i,20*256);
// }
//// 数据行
// PageList<Teacher> pageList = teacherService.query(pageIndex, pageSize, entity, order);
// List<Teacher> list = pageList.getList();
// for (int i = 0; i < list.size(); i++) {
//// 遍历一次创建一行
// HSSFRow row1 = sheet.createRow(i + 1);
//// 给行内单元格放对应数据
// row1.createCell(0).setCellValue(list.get(i).getName());
// row1.createCell(1).setCellValue(list.get(i).getDepartId());
// row1.createCell(2).setCellValue(String.valueOf(list.get(i).getSysCreateTime()));
// row1.createCell(3).setCellValue(String.valueOf(list.get(i).getSysUpdateTime()));
// row1.createCell(4).setCellValue(list.get(i).getTeaching());
// if(list.get(i).getWaitStatus().equals("0")){
// row1.createCell(5).setCellValue("成功");
// }else{
// row1.createCell(5).setCellValue("失败");
// }
//
// }
// String configValue = SysConstant.getConfigValue(BaseConstant.CONFIG_UPLOAD_FILES_TEMPPATH);
// File file = new File(configValue + "/teacher.xls");
// FileOutputStream fileOutputStream = new FileOutputStream(file);
// workbook.write(fileOutputStream);
// workbook.close();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// /**
// * 课程导出
// * */
// public void courseExports(Pagination pager, CourseQueryDto dto){
// try {
// workbook = new HSSFWorkbook();
//// 创建工作表 参数 表名
// HSSFSheet sheet = workbook.createSheet("课程信息");
//// 创建标题行
// HSSFRow row = sheet.createRow(0);
// String[] title = {"序号","名称", "内容分类", "资源归属", "授课方式", "状态","是否发布"};
//// 单元格对象 标题行
// HSSFCell cell = null;
// CellStyle cellStyle = this.cellStyle(workbook);
// for (int i = 0; i < title.length; i++) {
// cell = row.createCell(i);
// cell.setCellValue(title[i]);
// cell.setCellStyle(cellStyle);
// sheet.setColumnWidth(i,20*256);
// }
//// 数据行
// PageList<Course> page = courseService.findPage(pager.getPageIndex(), pager.getPageSize(), dto);
// List<Course> list = page.getList();
// for (int i = 0; i < list.size(); i++) {
//// 遍历一次创建一行
// HSSFRow row1 = sheet.createRow(i + 1);
//// 给行内单元格放对应数据
// row1.createCell(0).setCellValue(i+1);
// row1.createCell(1).setCellValue(list.get(i).getName());
// row1.createCell(2).setCellValue(list.get(i).getSysType1()+"/"+list.get(i).getSysType2()+"/"+list.get(i).getSysType3());
// row1.createCell(3).setCellValue(list.get(i).getResOwner1()+"/"+list.get(i).getResOwner2()+"/"+list.get(i).getResOwner3());
// if(list.get(i).getType()==20){
// row1.createCell(4).setCellValue("在线课(录播)");
// }
// if(list.get(i).getType()==10){
// row1.createCell(4).setCellValue("微课");
// }
// if(list.get(i).getStatus()==1){
// row1.createCell(5).setCellValue("未提交");
// }
// if(list.get(i).getStatus()==2){
// row1.createCell(5).setCellValue("已提交");
// }
// if(list.get(i).getStatus()==3){
// row1.createCell(5).setCellValue("审核未通过");
// }
// if(list.get(i).getStatus()==5){
// row1.createCell(5).setCellValue("审核完成");
// }
// if(list.get(i).getPublished()){
// row1.createCell(6).setCellValue("已发布");
// }else{
// row1.createCell(6).setCellValue("未发布");
// }
//
// }
// String configValue = SysConstant.getConfigValue(BaseConstant.CONFIG_UPLOAD_FILES_TEMPPATH);
// File file = new File(configValue + "/course.xls");
// FileOutputStream fileOutputStream = new FileOutputStream(file);
// workbook.write(fileOutputStream);
// workbook.close();
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// }
//
// /**
// * 标题行样式
// * */
// private CellStyle cellStyle(HSSFWorkbook workbook){
//
//// 构建字体
// HSSFFont font = workbook.createFont();
//// 加粗
// font.setBold(true);
//// 字号
// font.setFontHeightInPoints((short)10);
//// 创建样式对象
// CellStyle cellStyle = workbook.createCellStyle();
// cellStyle.setFont(font);
//// 设置文字居中
// cellStyle.setAlignment(HorizontalAlignment.CENTER);
// return cellStyle;
// }
/**
* 判断是否为空
* @param obj
@@ -323,10 +171,106 @@ public class ExportsExcelSenderUtil {
}
/**
* 导出动态列Excel
*
* @param kvMap 自定义标题属性,key:标题 value:属性
* @param dataList 数据列表每个元素是一个Mapkey为属性名value为值
* @param out 输出流
* @param dateFormat 日期格式 默认为yyyy-MM-dd HH:mm:ss
*/
public static void exportDynamic(LinkedHashMap<String, String> kvMap, List<Map<String, Object>> dataList, OutputStream out, String dateFormat) {
HSSFWorkbook wb = null;
try {
dateFormat = !ExportsExcelSenderUtil.isEmpty(dateFormat) ? dateFormat : "yyyy-MM-dd HH:mm:ss";
wb = new HSSFWorkbook();
// 表头样式
HSSFCellStyle titleStyle = wb.createCellStyle();
titleStyle.setBorderTop(BorderStyle.THIN);
titleStyle.setBorderLeft(BorderStyle.THIN);
titleStyle.setBorderBottom(BorderStyle.THIN);
titleStyle.setBorderRight(BorderStyle.THIN);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setWrapText(true);
// 设置字体样式
HSSFFont titleFont = wb.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 15);
titleStyle.setFont(titleFont);
// 内容样式
HSSFCellStyle contentStyle = wb.createCellStyle();
contentStyle.setBorderTop(BorderStyle.THIN);
contentStyle.setBorderLeft(BorderStyle.THIN);
contentStyle.setBorderBottom(BorderStyle.THIN);
contentStyle.setBorderRight(BorderStyle.THIN);
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
contentStyle.setAlignment(HorizontalAlignment.CENTER);
contentStyle.setWrapText(true);
// 创建工作表
HSSFSheet sheet = wb.createSheet();
HSSFRow headerRow = sheet.createRow(0);
headerRow.setHeightInPoints(33);
// 设置表头
Set<String> headerKeys = kvMap.keySet();
int columnIndex = 0;
for (String header : headerKeys) {
sheet.setColumnWidth(columnIndex, 60 * 80);
HSSFCell headerCell = headerRow.createCell(columnIndex);
headerCell.setCellValue(header);
headerCell.setCellStyle(titleStyle);
columnIndex++;
}
// 填充数据
for (int i = 0; i < dataList.size(); i++) {
Map<String, Object> rowData = dataList.get(i);
HSSFRow row = sheet.createRow(i + 1);
row.setHeightInPoints(33);
columnIndex = 0;
for (String header : headerKeys) {
HSSFCell cell = row.createCell(columnIndex);
String propertyName = kvMap.get(header); // 获取属性名
Object cellValue = rowData.get(propertyName);
if (cellValue instanceof LocalDateTime) {
LocalDateTime date = (LocalDateTime) cellValue;
cellValue = DateTimeFormatter.ofPattern(dateFormat).format(date);
} else if (cellValue instanceof Date) {
Date date = (Date) cellValue;
cellValue = new SimpleDateFormat(dateFormat).format(date);
}
String valueStr = !ExportsExcelSenderUtil.isEmpty(cellValue) ? cellValue.toString() : "";
cell.setCellValue(valueStr);
cell.setCellStyle(contentStyle);
columnIndex++;
}
}
try {
wb.write(out);
wb.close();
} catch (IOException e) {
// 日志记录
}
} catch (Exception e) {
try {
if (wb != null) {
wb.write(out);
wb.close();
}
} catch (IOException e1) {
// 日志记录
}
throw e;
}
}
}

View File

@@ -1,11 +1,9 @@
package com.xboe.module.interaction.api;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import com.xboe.common.utils.StringUtil;
import com.xboe.core.CurrentUser;
import com.xboe.module.interaction.dto.*;
import com.xboe.module.qa.entity.Question;
import com.xboe.module.qa.service.IQuestionService;
@@ -25,8 +23,6 @@ import com.xboe.module.interaction.service.IFavoritesService;
import lombok.extern.slf4j.Slf4j;
import javax.persistence.Cacheable;
/**
* 收藏处理
*
@@ -44,7 +40,7 @@ public class FavoritesApi extends ApiBaseController {
ICourseTeacherService ctService;
@Autowired
IStudyCourseService studyCourseService;
IStudyCourseService studyCourseService;
@Autowired
IQuestionService questionService;

View File

@@ -1,20 +1,6 @@
package com.xboe.school.study.api;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.boe.feign.api.usercenter.reps.AudienceMemberVo;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.common.Pagination;
@@ -24,10 +10,19 @@ import com.xboe.core.CurrentUser;
import com.xboe.core.JsonResponse;
import com.xboe.core.api.ApiBaseController;
import com.xboe.school.study.dto.BatchSignup;
import com.xboe.school.study.dto.SignupAudienceDTO;
import com.xboe.school.study.entity.StudySignup;
import com.xboe.school.study.service.IStudySignupService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 课程报名, 本控制器只有一个报名处理,没有其它的处理
@@ -63,17 +58,26 @@ public class StudySignupApi extends ApiBaseController{
return error("报名失败,请与管理员联系",e.getMessage());
}
}
/**
* 分页查询报名人员
* @param pager 分页
* @param courseId 课程id
* @param name 姓名
* @param signType 报名方式
* @return 返回课程报名表的集合
*/
@PostMapping("/pagelist")
public JsonResponse<PageList<StudySignup>> findPage(Pagination pager,String courseId,String name,Integer status){
public JsonResponse<PageList<StudySignup>> findPage(Pagination pager, String courseId, String name, Integer signType,String aid) {
// if(StringUtils.isBlank(courseId)){
// return error("无课程信息");
// }
StudySignup ss=new StudySignup();
ss.setCourseId(courseId);
ss.setName(name);
ss.setStatus(status);
//25.12.11 新增aid筛选
ss.setAid(aid);
//2025.11.28 新增signType筛选
ss.setSignType(signType);
//CurrentUser cuser=getCurrent();
try {
PageList<StudySignup> list =signupService.findPage(pager.getPageIndex(),pager.getPageSize(), ss, OrderCondition.desc("id"));
@@ -250,4 +254,14 @@ public class StudySignupApi extends ApiBaseController{
return error("删除报名失败,请与管理员联系",e.getMessage());
}
}
/**
* 获取报名人员
* @param audienceDto
* @return
*/
@PostMapping("/audience/memberList")
public JsonResponse<PageList<AudienceMemberVo>> audienceMemberPage(@RequestBody SignupAudienceDTO audienceDto) {
return success(signupService.audienceMemberPage(audienceDto));
}
}

View File

@@ -0,0 +1,9 @@
package com.xboe.school.study.dao;
import com.xboe.core.orm.BaseDao;
import com.xboe.school.study.entity.StudySignup;
import org.springframework.stereotype.Repository;
@Repository
public class CourseSignDao extends BaseDao<StudySignup> {
}

View File

@@ -0,0 +1,163 @@
package com.xboe.school.study.dao;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.xboe.core.orm.BaseDao;
import com.xboe.module.course.entity.CourseContent;
import com.xboe.module.course.service.ICourseContentService;
import com.xboe.school.study.dto.CourseFinishCountDto;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudySignup;
import com.xboe.school.study.service.IStudyService;
import com.xboe.school.study.service.IStudySignupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Repository
public class CourseStatDao extends BaseDao<StudyCourse> {
@Lazy
@Autowired
IStudyService studyService;
@Autowired
IStudySignupService signupService;
@Autowired
ICourseContentService courseContentService;
/**
* 查询资源列表和完成条数
*
* @param courseId 课程ID
* @param contentName 资源名称
* @return 课程完成人数统计DTO集合
*/
public List<CourseFinishCountDto> findFinishCountList(String courseId, String contentName) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ")
// 资源名
.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_course_content cc ")
// 2025.11.27新增:连表 boe_course_content
.append("LEFT JOIN boe_study_course_item c ON cc.id = c.content_id AND c.status = 9 ");
// 25.12.19新增逻辑:要求管理页面-报名记录,删除报名记录后,学习记录和资源学习情况需要删除,并可以恢复
// 实现方案是仅查询已报名aid的学习记录信息
// 查询报名记录
StudySignup studySignup = new StudySignup();
studySignup.setCourseId(courseId);
List<StudySignup> signRecordList = signupService.findList(studySignup, null);
if (signRecordList != null && !signRecordList.isEmpty()) {
String aidList = signRecordList.stream().map(StudySignup::getAid).collect(Collectors.joining(","));
if (StringUtils.isNotBlank(aidList)) {
sql.append("AND c.aid IN (").append(aidList).append(") ");
} else {
// 报名人数为空,视为无人报名
sql.append("AND 1=0 ");
}
} else {
// 报名人数为空,视为无人报名
sql.append("AND 1=0 ");
}
sql.append("WHERE 1=1 ")
.append("AND cc.deleted=0 ");
List<Object> params = new ArrayList<>();
// courseId非空则过滤参数化防注入
if (StringUtils.isNotBlank(courseId)) {
sql.append("AND cc.course_id = ? ");
params.add(courseId);
}
sql.append("GROUP BY cc.id, cc.content_name, cc.content_type ").append("ORDER BY cc.id ASC ");
// 执行SQL得到Object[]列表([0]courseName, [1]finishCount
List<Object[]> resultList = sqlFindList(sql.toString(), params.toArray());
// 手动封装DTO避免null值导致前端报错
List<CourseFinishCountDto> dtoList = new ArrayList<>();
for (Object[] objs : resultList) {
CourseFinishCountDto dto = new CourseFinishCountDto();
dto.setContentName(objs[0] != null ? (String) objs[0] : "");
dto.setContentId(((BigInteger) objs[1]).toString());
dto.setFinishCount(objs[2] != null ? ((Number) objs[2]).intValue() : 0);
dto.setContentType((Integer) objs[3]);
dtoList.add(dto);
}
// 25.12.16新增:修改课程名称的展示逻辑,修改为章名称+节名称
// 默认取sectionName-contentName如果sectionName不存在就用courseName-contentName
List<String> contentIds = dtoList.stream()
.map(CourseFinishCountDto::getContentId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!contentIds.isEmpty()) {
List<CourseContent> courseContents = courseContentService.getByIds(contentIds);
if (courseContents != null && !courseContents.isEmpty()) {
studyService.setContentDisplayName(courseContents);
// 将courseContents转成contentId为key的Map
Map<String, CourseContent> contentMap = courseContents.stream()
.collect(Collectors.toMap(CourseContent::getId, cc -> cc));
// 遍历dtoList通过contentId匹配展示名称
dtoList.forEach(dto -> {
CourseContent courseContent = contentMap.get(dto.getContentId());
// 兜底避免contentId不存在时NPE
if (courseContent != null) {
dto.setContentName(courseContent.getDisplayName());
} else {
dto.setContentName("");
}
});
}
}
// 25.12.17修改,因为资源名称修改为章名称+节名称,这里从分页修改为手动分页,并对修改后的名称进行模糊查询
if (StringUtils.isNotBlank(contentName)) {
dtoList = dtoList.stream().filter(dto -> dto.getContentName().contains(contentName)).collect(Collectors.toList());
}
return dtoList;
}
/**
* 查询课程完成人数
*
* @param courseId 课程ID
* @param contentName 资源名称
* @return 课程完成人数
*/
public int findFinishCountTotal(String courseId, String contentName) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ")
// 总条数=去重后的人数
.append("COUNT(DISTINCT c.id) ")
.append("FROM boe_course_content c ")
.append("WHERE 1=1 ")
.append("AND c.deleted=0 ");
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

@@ -1,10 +1,175 @@
package com.xboe.school.study.dao;
import com.xboe.enums.StudyCourseQueryStatusEnum;
import com.xboe.module.course.vo.CoursePageVo;
import org.springframework.stereotype.Repository;
import com.xboe.core.orm.BaseDao;
import com.xboe.school.study.entity.StudyCourseItem;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Repository
public class StudyCourseItemDao extends BaseDao<StudyCourseItem> {
@PersistenceContext
private EntityManager entityManager;
/**
* 查询学习记录
* @param courseId 课程id
* @param contentId 内容id
* @param userIds 用户id
* @param status 状态
* @param pageQuery 是否分页
* @param pageIndex 页码
* @param pageSize 每页数量
* @return
*/
public List<StudyCourseItem> queryList(String courseId, String contentId, List<String> userIds, Integer status,
boolean pageQuery, int pageIndex, int pageSize) {
StringBuilder builder = new StringBuilder("SELECT ");
builder.append("bsc.id AS study_id,");
builder.append("bsc.course_id,");
builder.append("bsc.aid,");
builder.append("bsc.aname,");
builder.append("bsci.id AS id,");
builder.append("bsci.content_id AS content_id,");
builder.append("bsci.finish_time,");
builder.append("COALESCE(bsci.progress, 0) AS progress,");
builder.append("COALESCE(bsci.status, 1) AS status,");
builder.append("bsci.score");
// from及后面的语句
appendFrom(builder, userIds, status);
Query query = entityManager.createNativeQuery(builder.toString());
setQueryParam(query, courseId, contentId, userIds, pageQuery, pageIndex, pageSize);
List<Object[]> resultList = query.getResultList();
List<StudyCourseItem> itemList = new ArrayList<>();
for (Object[] row : resultList) {
StudyCourseItem item = new StudyCourseItem();
// 防止BigInteger为null的情况
BigInteger studyId = (BigInteger) row[0];
if (studyId != null) {
item.setStudyId(studyId.toString());
}
BigInteger thisCourseId = (BigInteger) row[1];
if (thisCourseId != null) {
item.setCourseId(thisCourseId.toString());
}
BigInteger aid = (BigInteger) row[2];
if (aid != null) {
item.setAid(aid.toString());
}
item.setAname((String) row[3]);
BigInteger id = (BigInteger) row[4];
if (id != null) {
item.setId(id.toString());
}
BigInteger thisContentId = (BigInteger) row[5];
if (thisContentId != null) {
item.setContentId(thisContentId.toString());
}
Timestamp finishTimestamp = (Timestamp) row[6];
if (finishTimestamp != null) {
item.setFinishTime(finishTimestamp.toLocalDateTime());
}
BigInteger progress = (BigInteger) row[7];
if (progress != null) {
item.setProgress(progress.intValue());
}
BigInteger thisStatus = (BigInteger) row[8];
if (thisStatus != null) {
item.setStatus(thisStatus.intValue());
}
Number score = (Number) row[9];
if (score != null) {
item.setScore(score.floatValue());
}
itemList.add(item);
}
return itemList;
}
public long countList(String courseId, String contentId, List<String> userIds, Integer status) {
StringBuilder builder = new StringBuilder("SELECT COUNT(*)");
// from及后面的语句
appendFrom(builder, userIds, status);
Query query = entityManager.createNativeQuery(builder.toString());
setQueryParam(query, courseId, contentId, userIds, false, 0, 0);
Number count = (Number) query.getSingleResult();
return count.longValue();
}
private void appendFrom(StringBuilder builder, List<String> userIds, Integer status) {
builder.append(" FROM boe_study_course bsc");
// left join语句
builder.append(System.lineSeparator());
builder.append("LEFT JOIN (");
builder.append(System.lineSeparator());
builder.append("SELECT i.id, i.content_id, i.study_id, i.finish_time, i.progress, i.status, i.score FROM boe_study_course_item i WHERE i.content_id = :contentId");
builder.append(System.lineSeparator());
builder.append(") bsci ON bsc.id = bsci.study_id");
// where条件
// courseId
builder.append(System.lineSeparator());
builder.append("WHERE bsc.course_id = :courseId");
// 只查询报过名的数据
builder.append(System.lineSeparator());
builder.append("AND EXISTS (");
builder.append(System.lineSeparator());
builder.append("SELECT 1 FROM boe_study_signup ss WHERE ss.course_id = :courseId AND ss.aid = bsc.aid");
builder.append(System.lineSeparator());
builder.append(")");
// 用户
if (userIds != null && !userIds.isEmpty()) {
builder.append(System.lineSeparator());
builder.append("AND bsc.aid IN (:userIds)");
}
// 状态
if (status != null) {
builder.append(System.lineSeparator());
StudyCourseQueryStatusEnum queryStatusEnum = StudyCourseQueryStatusEnum.getByCode(status);
switch (queryStatusEnum) {
case NOT_START:
builder.append("AND (bsci.status = 1 OR bsci.status is null)");
break;
case STUDYING:
builder.append("AND bsci.status = 2");
break;
case NOT_FINISH:
builder.append("AND (bsci.status = 1 OR bsci.status = 2 OR bsci.status is null)");
break;
case FINISH:
default:
builder.append("AND bsci.status = 9");
break;
}
}
// 排序
builder.append(System.lineSeparator());
builder.append("ORDER BY bsc.id DESC");
}
private void setQueryParam(Query query, String courseId, String contentId, List<String> userIds,
boolean pageQuery, int pageIndex, int pageSize) {
query.setParameter("courseId", courseId);
query.setParameter("contentId", contentId);
if (userIds != null && !userIds.isEmpty()) {
query.setParameter("userIds", userIds);
}
if (pageQuery) {
// 设置OFFSET和LIMIT
query.setFirstResult((pageIndex - 1) * pageSize);
query.setMaxResults(pageSize);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.xboe.school.study.dto;
import lombok.Data;
import java.math.BigInteger;
/**
* 课程完成人数统计DTO
*/
@Data
public class CourseFinishCountDto {
/**
* 资源名称
*/
private String contentName;
/**
* 内容ID
*/
private String contentId;
/**
* 完成人数(数据库 count 统计得出)
*/
private Integer finishCount;
/**
* 内容类型
* 枚举值参考BoedxContentType
*/
private Integer contentType;
}

View File

@@ -0,0 +1,60 @@
package com.xboe.school.study.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 课程报名记录查询筛选DTO
*/
@Data
public class CourseSignDto {
/**
* 课程ID
*/
private String courseId;
/**
* 可选筛选条件(前端筛选时传值,不筛选则留空)
* 姓名筛选(模糊查询)
*/
private String name;
/**
* 25.12.18新增 通过aid模糊查询
*/
private String aid;
/**
* 报名状态1.自主报名 2.手动加入)
*/
private Integer signType;
/**
* 报名状态显示1.自主报名 2.手动加入)
*/
private String signTypeName;
/**
* 课程名称
*/
private String courseName;
/**
* 工号
*/
private String userNo;
/**
* 部门
*/
private String departName;
/**
* 报名时间
*/
private LocalDateTime signTime;
}

View File

@@ -0,0 +1,19 @@
package com.xboe.school.study.dto;
import com.boe.feign.api.usercenter.entity.AudienceDto;
/**
* 课程内受众的查询入参
*/
public class SignupAudienceDTO extends AudienceDto {
private String courseId;
public String getCourseId() {
return courseId;
}
public void setCourseId(String courseId) {
this.courseId = courseId;
}
}

View File

@@ -1,18 +1,16 @@
package com.xboe.school.study.entity;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.IdEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.IdEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/*
* 课程学习表,课程图片通过对应图片的api获取,这里不做存储。
@@ -83,8 +81,9 @@ public class StudyCourse extends IdEntity{
/*
* 开始学习时间,报名和学习是一体的,此字段为后续报名学习不致的情况,当前这种情况没有
* */
@Deprecated
* 25.11.21修改:经生产数据查证本字段实际未废弃,因此继续使用
*/
// @Deprecated
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "start_time")
private LocalDateTime startTime;
@@ -105,8 +104,9 @@ public class StudyCourse extends IdEntity{
/*
* 学习总时间,秒,没有小数的情况,所以直接使用整数类型
* 这一项计算时间,在二期中已经不在使用,学习时长已经移到统计服务中,单独的课程不再记录
* 25.11.21修改:经生产数据查证本字段实际未废弃,因此继续使用
*/
@Deprecated
// @Deprecated
@Column(name = "total_duration")
private Integer totalDuration;
@@ -142,9 +142,57 @@ public class StudyCourse extends IdEntity{
* */
@Column(name = "remark",length = 200)
private String remark;
/**
* 机构信息,多级使用/分隔
* 25.11.24新增
*/
@Transient
private String orgInfo;
@Transient
private String courseImage;
/**
* 工号
* 25.11.24新增
*/
@Transient
private String userNo;
/**
* 部门
* 25.11.24新增
*/
@Transient
private String departName;
/**
* 根据考试设置的最新/最高成绩
* 25.11.24新增
*/
@Transient
private String bestScore;
/**
* 查询开始学习时间yyyy-MM-dd
* 25.11.24新增
*/
@Transient
private String queryStartTime;
/**
* 查询完成学习时间yyyy-MM-dd
* 25.11.24新增
*/
@Transient
private String queryFinishTime;
/**
* 用于筛选报名aid
*/
@Transient
private String signUpAid;
}

View File

@@ -1,19 +1,18 @@
package com.xboe.school.study.entity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.IdEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.IdEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/*
* 课程学习记录表,相当于课程学习表的子表
@@ -138,4 +137,26 @@ public class StudyCourseItem extends IdEntity {
@Transient
private BigDecimal progressVideo;
/**
* 展示名称(章名+节名,如果没有章节,则为课程名+节名)
* 25.12.15新增
*/
@Transient
private String displayName;
/**
* 考试记录集合
* 仅在资源学习情况分页查询-考试信息接口使用
* 25.11.25新增
*/
@Transient
private List<StudyExam> studyExams;
/**
* 评估记录集合
* 仅在资源学习情况分页查询-评估信息接口使用
* 25.11.28新增
*/
@Transient
private List<StudyAssess> studyAssesses;
}

View File

@@ -1,18 +1,17 @@
package com.xboe.school.study.entity;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xboe.core.SysConstant;
import com.xboe.core.orm.IdEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.time.LocalDateTime;
/**
* 课程作业提交记录表
* */
@@ -107,4 +106,13 @@ public class StudyHomeWork extends IdEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "end_time")
private LocalDateTime endTime;
/**
* 作业完成状态
* 确认需求根据studyCourseItem表的状态判断9则为完成其他值为未完成
* 25.12.17新增
*/
@Transient
private Integer status;
}

View File

@@ -0,0 +1,16 @@
package com.xboe.school.study.service;
import com.xboe.school.study.dto.CourseSignDto;
import com.xboe.school.study.entity.StudySignup;
import java.util.List;
public interface ICourseSignService {
/**
* 根据筛选条件查询课程报名记录
*/
List<StudySignup> querySignRecords(CourseSignDto queryDto);
}

View File

@@ -28,4 +28,13 @@ public interface IStudyAssessService {
* @return
*/
List<StudyAssess> getByStudyIdAndContentId(String studyId,String contentId);
/**
* 根据课程id和内容id得到评估的内容
* @param courseId 课程id
* @param contentId 内容
* @return
*/
List<StudyAssess> getByCourseIdAndContentId(String courseId,String contentId);
}

View File

@@ -1,17 +1,14 @@
package com.xboe.school.study.service;
import java.util.List;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.school.study.dto.CourseStudyItem;
import com.xboe.school.study.dto.StudyCourseNameDto;
import com.xboe.school.study.dto.StudyCourseQuery;
import com.xboe.school.study.dto.StudyCourseSimple;
import com.xboe.school.study.dto.*;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudySignup;
import java.util.List;
public interface IStudyCourseService {
/**
@@ -46,15 +43,36 @@ public interface IStudyCourseService {
PageList<StudyCourse> findByES(int pageIndex,int pageSize) throws Exception;
/**
* 分页查询课程学习记录,用户的课程学习记录
* @param pageIndex
* @param pageSize
* @param sc
* @param oc
* @return
* 列表查询课程学习记录,用户的课程学习记录
* 25.11.20新增
*
* @param sc 课程学习表查询字段
* @param oc 排序字段
* @param isFinish 是否已完成
* @return 学习记录信息集合
*/
List<StudyCourse> findList(StudyCourse sc, OrderCondition oc, Boolean isFinish);
/**
* 分页查询课程学习记录
* @param pageIndex 页码
* @param pageSize 每页数量
* @param sc 课程学习记录查询参数
* @param oc 排序字段
* @param isFinish 是否已完成
* @return 课程学习记录分页集合
*/
PageList<StudyCourse> findPage(int pageIndex,int pageSize,StudyCourse sc,OrderCondition oc,Boolean isFinish);
/**
* 分页查询课程的资源名称以及资源学习完成人数
* @param pageIndex 页码
* @param pageSize 每页数据条数
* @param courseId 课程id
* @param contentName 资源名称
*/
PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId, String contentName);
/**
* 热度榜
* */
@@ -87,6 +105,13 @@ public interface IStudyCourseService {
*/
List<StudyCourse> findByIds(List<String> ids);
/**
* 根据课程id集合查询StudyCourse集合
* @param courseIds 课程id集合
* @return StudyCourse集合
*/
List<StudyCourse> findByCourseIds(List<String> courseIds);
/**
* 查询课程内容学习记录
* */
@@ -105,4 +130,15 @@ public interface IStudyCourseService {
List<CourseStudyItem> findByCourseAndUsers(String courseId,List<String> aids) throws Exception;
List<StudyCourse> getCourseId(String userId);
/**
* 根据课程id得到对应课程学习记录包括考试记录
* 这个接口从study_course_item表获取这个表的score考试分数字段保存或更新时是根据考试设置最新/最高分的
* 25.11.24新增
*
* @param courseId 课程id
* @return 考试记录集合
*/
List<StudyCourseItem> getStudyCourseItemByCourseId(String courseId);
}

View File

@@ -1,9 +1,9 @@
package com.xboe.school.study.service;
import java.util.List;
import com.xboe.school.study.entity.StudyExam;
import java.util.List;
public interface IStudyExamService {
/**
@@ -30,5 +30,16 @@ public interface IStudyExamService {
*/
List<StudyExam> getByStudyIdAndContentId(String studyId,String contentId);
/**
* 根据课程id或者内容id得到对应的考试记录
* 课程id必填内容id选填
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 考试记录集合
*/
List<StudyExam> getByCourseIdAndContentId(String courseId, String contentId);
void correctStstus(String courseId);
}

View File

@@ -1,10 +1,9 @@
package com.xboe.school.study.service;
import java.util.Collection;
import java.util.List;
import com.xboe.school.study.entity.StudyHomeWork;
import java.util.List;
public interface IStudyHomeWorkService {
/**
@@ -12,17 +11,17 @@ public interface IStudyHomeWorkService {
* @param exam
*/
void save(StudyHomeWork homework,String token);
StudyHomeWork get(String id);
/**
* 根据学习内容id得到答卷信息
* @param studyItemId
* @return
*/
List<StudyHomeWork> getByStudyItem(String studyItemId);
/**
* 根据学习id和内容id查出作业列表
* @param studyId
@@ -32,4 +31,15 @@ public interface IStudyHomeWorkService {
List<StudyHomeWork> getByStudyIdAndContentId(String studyId,String contentId);
List<StudyHomeWork>getByStudnetNameAndContentId(List<String> studentName, String contentId);
/**
* 根据课程id得到对应的作业记录
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 作业记录集合
*/
List<StudyHomeWork> getByCourseIdAndContentId(String courseId, String contentId);
}

View File

@@ -1,14 +1,15 @@
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;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import com.xboe.common.PageList;
import com.xboe.school.study.dto.StudyContentDto;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudyTime;
/**
* 学习情况处理,比较综合一个处理类
* @author seastar
@@ -85,18 +86,51 @@ public interface IStudyService {
* @return
*/
List<StudyCourseItem> findByStudyId(String studyId);
/**
* 查询学习内容记录
* 资源学习情况分页查询,此接口用于管理端处理
* 25.11.25修改,修改资源学习情况的查询字段,修改为管理端和教师端均可以查询,并添加部门和工号查询逻辑
*
* @param ids 资源学习id集合
* @param pageIndex 页码
* @param pageSize 每页数量
* @param courseId 课程id
* @param contentId 内容id
* @param name 用户名称
* @param status 用户学习状态1-未开始2-已完成3-进行中)
* @return 资源学习情况分页集合
*/
PageList<StudyCourseItem> findItemPage(int pageIndex, int pageSize, List<String> ids, String contentId, String courseId, String name, Integer status,String aid);
/**
* 资源学习情况学习人员的分页查询
* @param pageIndex
* @param pageSize
* @param contentId
* @param courseId
* @param name
* @param status
* @param userIdList
* @return
*/
PageList<StudyCourseItem> findItemPage(int pageIndex, int pageSize, String contentId, String courseId, String name, Integer status);
PageList<StudyCourseItem> itemPage(int pageIndex, int pageSize, String contentId, String courseId, Integer status, List<String> userIdList);
/**
* 获取资源学习情况列表
* @param contentId
* @param courseId
* @param status
* @param userIdList
* @return
*/
List<StudyCourseItem> itemList(String contentId, String courseId, Integer status, List<String> userIdList);
/**
* 为courseContents列表设置展示名称章名+节名 或 课程名+节名)
* 25.12.15新增
*
* @param courseContents 课程内容集合
*/
void setContentDisplayName(List<CourseContent> courseContents);
List<StudyCourseItem> getList(String courseId, String contentId, String name, Integer status);

View File

@@ -1,14 +1,17 @@
package com.xboe.school.study.service;
import java.util.List;
import com.boe.feign.api.usercenter.entity.AudienceDto;
import com.boe.feign.api.usercenter.reps.AudienceMemberVo;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.common.beans.KeyValue;
import com.xboe.school.study.dto.SignupAudienceDTO;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudySignup;
import java.util.List;
/**
* 课程学习报名
*/
@@ -78,7 +81,16 @@ public interface IStudySignupService {
* @param remark
*/
void audit(String id,String aid,String user,boolean pass,String remark);
/**
* 报名列表查询
*
* @param ss 报名查询条件
* @param oc 排序条件
* @return 报名信息集合
*/
List<StudySignup> findList(StudySignup ss, OrderCondition oc);
/**
* 报名分页查询
* @param pageIndex
@@ -88,4 +100,11 @@ public interface IStudySignupService {
* @return
*/
PageList<StudySignup> findPage(int pageIndex,int pageSize,StudySignup ss,OrderCondition oc);
/**
* 查询课程的报名受众成员
* @param audienceDto
* @return
*/
PageList<AudienceMemberVo> audienceMemberPage(SignupAudienceDTO audienceDto);
}

View File

@@ -0,0 +1,56 @@
package com.xboe.school.study.service.impl;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.xboe.common.OrderDirection;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.QueryBuilder;
import com.xboe.school.study.dao.CourseSignDao;
import com.xboe.school.study.dto.CourseSignDto;
import com.xboe.school.study.entity.StudySignup;
import com.xboe.school.study.service.ICourseSignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CourseSignServiceImpl implements ICourseSignService {
@Autowired
CourseSignDao coursesignDao;
/**
* 根据筛选条件查询课程报名记录
*
* @param queryDto 查询条件载体
* @return 课程报名记录
*/
@Override
public List<StudySignup> querySignRecords(CourseSignDto queryDto) {
QueryBuilder query = QueryBuilder.from(StudySignup.class);
if (queryDto != null) {
// 先判断查询条件载体是否为空,避免空指针异常
// 1. 姓名模糊查询(字符串类型:用 isNotBlank 判断非空+非空白)
if (StringUtils.isNotBlank(queryDto.getName())) {
query.addFilter(FieldFilters.like("name", queryDto.getName()));
}
// 2. 课程ID精确查询字符串类型ID通常是精确匹配
if (StringUtils.isNotBlank(queryDto.getCourseId())) {
query.addFilter(FieldFilters.eq("courseId", queryDto.getCourseId()));
}
//3. 课程类型精确查询
if (queryDto.getSignType() != null) {
query.addFilter(FieldFilters.eq("signType", queryDto.getSignType()));
}
//4. aid查询
if (StringUtils.isNotBlank(queryDto.getAid())) {
String aidstr = queryDto.getAid();
query.addFilter(FieldFilters.in("aid", aidstr.split( ",")));
}
}
query.addOrder("id", OrderDirection.DESC);
return coursesignDao.findList(query.builder());
}
}

View File

@@ -86,4 +86,15 @@ public class StudyAssessServiceImpl implements IStudyAssessService{
return dao.findList(FieldFilters.eq("studyId", studyId),FieldFilters.eq("contentId", contentId));
}
/**
* 根据课程id和内容id得到评估的内容
* @param courseId 课程id
* @param contentId 内容
* @return
*/
@Override
public List<StudyAssess> getByCourseIdAndContentId(String courseId,String contentId){
return dao.findList(FieldFilters.eq("courseId", courseId),FieldFilters.eq("contentId", contentId));
}
}

View File

@@ -1,18 +1,5 @@
package com.xboe.school.study.service.impl;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.core.orm.FieldFilters;
@@ -20,29 +7,39 @@ 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;
import com.xboe.module.course.dao.CourseDao;
import com.xboe.module.course.entity.Course;
import com.xboe.module.course.service.ICourseStudySearch;
import com.xboe.school.study.dao.StudyAssessDao;
import com.xboe.school.study.dao.StudyCourseDao;
import com.xboe.school.study.dao.StudyCourseItemDao;
import com.xboe.school.study.dao.StudyExamDao;
import com.xboe.school.study.dao.StudyHomeWorkDao;
import com.xboe.school.study.dao.StudyScoreDao;
import com.xboe.school.study.dao.StudySignupDao;
import com.xboe.school.study.dao.StudyTimeDao;
import com.xboe.school.study.dto.CourseStudyItem;
import com.xboe.school.study.dto.StudyCourseNameDto;
import com.xboe.school.study.dto.StudyCourseQuery;
import com.xboe.school.study.dto.StudyCourseSimple;
import com.xboe.school.study.dao.*;
import com.xboe.school.study.dto.*;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudySignup;
import com.xboe.school.study.service.IStudyCourseService;
import com.xboe.school.study.service.IStudySignupService;
import com.xboe.system.organization.entity.Organization;
import com.xboe.system.organization.service.IOrganizationService;
import com.xboe.system.user.entity.User;
import com.xboe.system.user.service.IUserService;
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.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -55,6 +52,19 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
@Autowired
IStudySignupService signupService;
@Autowired
IOutSideDataService outsideService;
@Autowired
IUserService userService;
@Autowired
IOrganizationService organizationService;
@Autowired
@Lazy
IStudyCourseService studyCourseService;
@Autowired
StudyCourseDao studyCourseDao;
@@ -82,12 +92,22 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
@Autowired
StudyTimeDao studyTimeDao;
@Autowired
CourseStatDao courseStatDao;
@Autowired
CourseDao courseDao;
@Autowired(required = false)
ICourseStudySearch esSearch;//对ES的处理
/**
* 用于避免JPA查询后修改entity实体字段自动更新到数据库
*/
@Autowired
private EntityManager entityManager;
@Override
public StudyCourse findByCourseIdAndAid(String courseId, String aid) {
//加上排序,如果是多条学习记录,只会取最新的一条
@@ -101,6 +121,16 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
return null;
}
/**
* 分页查询课程学习记录
*
* @param pageIndex 页码
* @param pageSize 每页数量
* @param sc 课程学习记录查询参数
* @param oc 排序字段
* @param isFinish 是否已完成
* @return 课程学习记录分页集合
*/
@Override
public PageList<StudyCourse> findPage(int pageIndex, int pageSize, StudyCourse sc, OrderCondition oc,Boolean isFinish) {
QueryBuilder query=QueryBuilder.from(StudyCourse.class);
@@ -129,20 +159,151 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
if(sc.getStartTime()!=null){
query.addFilter(FieldFilters.eq("startTime",sc.getStartTime()));
}
//25.12.11新增添加根据用户aid通过逗号分隔查询
if(StringUtils.isNotBlank(sc.getAid())) {
query.addFilter(FieldFilters.eq("aid", sc.getAid()));
String userIdStr=sc.getAid();
query.addFilter(FieldFilters.in("aid", userIdStr.split(",")));
}
//25.12.11新增添加根据用户aid通过逗号分隔查询
if (StringUtils.isNotBlank(sc.getSignUpAid())) {
String signUpAid = sc.getSignUpAid();
query.addFilter(FieldFilters.in("aid", signUpAid.split(",")));
}
}
if(isFinish!=null) {
if(isFinish) {
query.addFilter(FieldFilters.eq("status",9));
}else {
query.addFilter(FieldFilters.lt("status",9));
// 原有查询是否结束逻辑
if (isFinish != null) {
if (isFinish) {
query.addFilter(FieldFilters.eq("status", 9));
} else {
query.addFilter(FieldFilters.lt("status", 9));
}
}
return studyCourseDao.findPage(query.builder());
PageList<StudyCourse> studyCoursePageList = studyCourseDao.findPage(query.builder());
// 25.11.24新增:添加工号和部门字段,并增加学习时长枚举筛选
// 根据当前查询数据的aid补全用户工号和部门字段
List<StudyCourse> studyCourses = studyCoursePageList.getList();
List<String> userIds = studyCourses.stream().map(StudyCourse::getAid).collect(Collectors.toList());
// 这块和StudyCourseApi的export方法一致但是因为两种查询方式返回对象不同暂时没有整合为独立方法复用
if (!userIds.isEmpty()) {
// 调用用户中心接口
List<UserSimpleVo> userSimpleVos = outsideService.findByIds(userIds);
if (userSimpleVos != null && !userSimpleVos.isEmpty()) {
for (UserSimpleVo userSimpleVo : userSimpleVos) {
// 填充部门字段
for (StudyCourse studyCourse1 : studyCourses) {
if (userSimpleVo.getAid().equals(studyCourse1.getAid())) {
studyCourse1.setOrgInfo(userSimpleVo.getOrgInfo());
// 取code为工号
studyCourse1.setUserNo(userSimpleVo.getCode());
}
}
}
} else {
log.error("【分页查询课程学习记录】用户信息查询失败查询boe人员表作为兜底方案");
// 和需求沟通后查询用户中心失败情况查询boe的人员表作为兜底方案如果仍然查询不到则不填充继续导出其他字段
for (String userId : userIds) {
// 用户信息也是redis获取的
User userInfo = userService.get(userId);
log.info("【分页查询课程学习记录】查询boe人员表用户id{}", userId);
if (userInfo != null) {
// 填充部门字段
for (StudyCourse studyCourse1 : studyCourses) {
if (Objects.equals(userId, studyCourse1.getAid())) {
log.info("【分页查询课程学习记录】查询boe人员表机构id{}", userInfo.getDepartId());
// 获取部门信息参考现有机构表获取namePath字段
Organization organization = organizationService.get(userInfo.getDepartId());
String departName = organization != null ? organization.getNamePath() : "";
studyCourse1.setOrgInfo(departName);
// 取userNo为工号
studyCourse1.setUserNo(userInfo.getUserNo());
}
}
} else {
log.error("【分页查询课程学习记录】用户信息查询boe人员表失败用户id{}", userId);
}
}
}
}
// 25.12.3新增,添加基于时间的查询条件,规则如下:
if (sc != null && StringUtils.isNotBlank(sc.getCourseId())) {
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);
// 如果有查询时间范围,则计算筛选后的学习时长
// 25.12.16修改,无论有没有筛选时间范围,都计算总学习时长
if (itemsByStudyId.containsKey(studyCourse.getId())) {
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
int filteredDuration = 0;
for (StudyCourseItem item : items) {
boolean inRange = true;
// 如果筛选了学习时间,判断结束时间是否在筛选范围内,未筛选学习时间则默认全部求和
if (StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
// 解析查询时间范围
LocalDate startDate = LocalDate.parse(queryStartTime);
LocalDate endDate = LocalDate.parse(queryFinishTime);
// 以结束时间筛选
LocalDateTime finishTime = item.getFinishTime();
LocalDate finishDate = finishTime.toLocalDate();
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, String contentName) {
// 1. 调用Dao层查当前页数据传入偏移量、每页条数、courseId条件
List<CourseFinishCountDto> dtoList = courseStatDao.findFinishCountList(courseId, contentName);
// 2. 调用Dao层查总条数对应PageList的count字段用int类型和PageList一致
int totalCount = dtoList.size();
// 3. 从dtoList中取出当前页数据
int fromIndex = (pageIndex - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, totalCount);
List<CourseFinishCountDto> pageData = dtoList.subList(fromIndex, toIndex);
// 4. 按PageList构造函数创建对象只传list和count
PageList<CourseFinishCountDto> pageList = new PageList<>(pageData, totalCount);
// 5. 设置pageSize覆盖默认10确保总页数计算正确
pageList.setPageSize(pageSize);
// 6. 返回totalPage会在前端调用getTotalPages()时自动计算
return pageList;
}
@Override
public List<StudyCourseNameDto> studyCounts(int num) {
// QueryBuilder builder = QueryBuilder.from(StudyCourse.class);
@@ -674,6 +835,105 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
return rs;
}
@Override
public List<StudyCourse> findList(StudyCourse sc, OrderCondition oc, Boolean isFinish) {
QueryBuilder query = QueryBuilder.from(StudyCourse.class);
if (oc == null) {
oc = OrderCondition.desc("id");
}
query.addOrder(oc);
if (sc != null) {
if (StringUtils.isNotBlank(sc.getCourseName())) {
query.addFilter(FieldFilters.like("courseName", sc.getCourseName()));
}
if (StringUtils.isNotBlank(sc.getAname())) {
query.addFilter(FieldFilters.like("aname", sc.getAname()));
}
if (sc.getStatus() != null) {
query.addFilter(FieldFilters.eq("status", sc.getStatus()));
}
if (sc.getCourseType() != null) {
query.addFilter(FieldFilters.eq("courseType", sc.getCourseType()));
}
if (StringUtils.isNotBlank(sc.getCourseId())) {
query.addFilter(FieldFilters.eq("courseId", sc.getCourseId()));
}
if (sc.getStartTime() != null) {
query.addFilter(FieldFilters.eq("startTime", sc.getStartTime()));
}
if (StringUtils.isNotBlank(sc.getAid())) {
String aidStr = sc.getAid();
query.addFilter(FieldFilters.in("aid", aidStr.split( ",")));
}
//25.12.11新增添加根据用户aid通过逗号分隔查询
if (StringUtils.isNotBlank(sc.getSignUpAid())) {
String signUpAid = sc.getSignUpAid();
query.addFilter(FieldFilters.in("aid", signUpAid.split(",")));
}
}
if (isFinish != null) {
if (isFinish) {
query.addFilter(FieldFilters.eq("status", 9));
} else {
query.addFilter(FieldFilters.lt("status", 9));
}
}
// 25.12.3新增,添加基于时间的查询条件,规则如下:
List<StudyCourse> studyCourses = studyCourseDao.findList(query.builder());
if (sc != null && StringUtils.isNotBlank(sc.getCourseId())) {
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);
// 如果有查询时间范围,则计算筛选后的学习时长
// 25.12.16修改,无论有没有筛选时间范围,都计算总学习时长
if (itemsByStudyId.containsKey(studyCourse.getId())) {
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
int filteredDuration = 0;
for (StudyCourseItem item : items) {
boolean inRange = true;
// 如果筛选了学习时间,判断结束时间是否在筛选范围内,未筛选学习时间则默认全部求和
if (StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
// 解析查询时间范围
LocalDate startDate = LocalDate.parse(queryStartTime);
LocalDate endDate = LocalDate.parse(queryFinishTime);
// 以结束时间筛选
LocalDateTime finishTime = item.getFinishTime();
LocalDate finishDate = finishTime.toLocalDate();
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
public List<CourseStudyItem> findByCourseAndUsers(String courseId, List<String> aids) throws Exception {
@@ -719,4 +979,28 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
return studyCourseDao.findList(FieldFilters.in("id", ids));
}
/**
* 根据课程id集合查询StudyCourse集合
*
* @param courseIds 课程id集合
* @return StudyCourse集合
*/
@Override
public List<StudyCourse> findByCourseIds(List<String> courseIds) {
return studyCourseDao.findList(FieldFilters.in("courseId", courseIds));
}
/**
* 根据课程id得到对应考试记录
* 这个接口从study_course_item表获取这个表的score分数字段更新时是根据考试设置最新/最高分的
* 25.11.24新增
*
* @param courseId 课程id
* @return 考试记录集合
*/
@Override
public List<StudyCourseItem> getStudyCourseItemByCourseId(String courseId) {
return studyCourseItemDao.findList(FieldFilters.eq("courseId", 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;
@@ -22,7 +23,6 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
@@ -159,6 +159,27 @@ public class StudyExamServiceImpl implements IStudyExamService{
return dao.findList(OrderCondition.desc("id"),FieldFilters.eq("studyId", studyId),FieldFilters.eq("contentId", contentId));
}
/**
* 根据课程id或者内容id得到对应的考试记录
* 课程id必填内容id选填
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 考试记录集合
*/
@Override
public List<StudyExam> getByCourseIdAndContentId(String courseId, String contentId) {
if (StringUtils.isEmpty(courseId)) {
throw new IllegalArgumentException("课程id不能为空");
}
IFieldFilter filter = FieldFilters.eq("courseId", courseId);
if (StringUtils.isNotEmpty(contentId)) {
filter = FieldFilters.and(filter, FieldFilters.eq("contentId", contentId));
}
return dao.findList(OrderCondition.desc("id"), filter);
}
@Override
@Transactional
public void correctStstus(String courseId) {

View File

@@ -1,15 +1,8 @@
package com.xboe.school.study.service.impl;
import java.time.LocalDateTime;
import java.util.List;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xboe.common.OrderCondition;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.IFieldFilter;
import com.xboe.module.course.dao.CourseContentDao;
import com.xboe.school.study.dao.StudyCourseDao;
import com.xboe.school.study.dao.StudyCourseItemDao;
@@ -17,6 +10,14 @@ import com.xboe.school.study.dao.StudyHomeWorkDao;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudyHomeWork;
import com.xboe.school.study.service.IStudyHomeWorkService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class StudyHomeWorkServiceImpl implements IStudyHomeWorkService{
@@ -95,4 +96,24 @@ public class StudyHomeWorkServiceImpl implements IStudyHomeWorkService{
return dao.findList(FieldFilters.in("student_name", studentName),FieldFilters.eq("contentId", contentId));
}
/**
* 根据课程id得到对应的作业记录
* 25.11.20新增
*
* @param courseId 课程id
* @param contentId 内容id
* @return 作业记录集合
*/
@Override
public List<StudyHomeWork> getByCourseIdAndContentId(String courseId, String contentId) {
if (StringUtils.isEmpty(courseId)) {
throw new IllegalArgumentException("课程id不能为空");
}
IFieldFilter filter = FieldFilters.eq("courseId", courseId);
if (StringUtils.isNotEmpty(contentId)) {
filter = FieldFilters.and(filter, FieldFilters.eq("contentId", contentId));
}
return dao.findList(OrderCondition.desc("id"), filter);
}
}

View File

@@ -25,17 +25,35 @@ 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.module.course.vo.CoursePageVo;
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;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@Service
public class StudyServiceImpl implements IStudyService{
@@ -51,6 +69,16 @@ public class StudyServiceImpl implements IStudyService{
@Autowired
UserDao userDao;
@Autowired
IStudyCourseService studyCourseService;
@Autowired
ICourseContentService courseContentService;
@Autowired
ICourseSectionService courseSectionService;
@Autowired
StringRedisTemplate redisTemplate;
@@ -229,14 +257,30 @@ public class StudyServiceImpl implements IStudyService{
return list;
}
/**
* 资源学习情况分页查询,此接口用于管理端处理
* 25.11.25修改,修改资源学习情况的查询字段,修改为管理端和教师端均可以查询,并添加部门和工号查询逻辑
*
* @param ids 资源学习id集合
* @param pageIndex 页码
* @param pageSize 每页数量
* @param courseId 课程id
* @param contentId 内容id
* @param name 用户名称
* @param status 用户学习状态1-未开始2-已完成3-进行中)
* @return 资源学习情况分页集合
*/
@Override
public PageList<StudyCourseItem> findItemPage(int pageIndex, int pageSize, String contentId, String courseId,String name,Integer status) {
public PageList<StudyCourseItem> findItemPage(int pageIndex, int pageSize, List<String> ids, String contentId, String courseId,String name,Integer status,String aid) {
QueryBuilder query = QueryBuilder.from(StudyCourseItem.class);
query.setPageIndex(pageIndex);
query.setPageSize(pageSize);
OrderCondition oc=OrderCondition.desc("id");
query.addOrder(oc);
if (ids != null && !ids.isEmpty()) {
query.addFilter(FieldFilters.in("id",ids));
}
if(StringUtils.isNotBlank(contentId)) {
query.addFilter(FieldFilters.eq("contentId",contentId));
}
@@ -244,76 +288,68 @@ 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));
}
// 25.12.16新增 aid筛选
if(StringUtils.isNotBlank(aid)){
String aidList=aid;
query.addFilter(FieldFilters.in("aid",aidList.split( ",")));
}
int pageIndex2 = (pageIndex-1)*10;
if(status!=null) {
if(status==3) {
// 进行中
query.addFilter(FieldFilters.eq("status", 2));
return scItemDao.findPage(query.builder());
}else if(status==2){
// 已完成
query.addFilter(FieldFilters.eq("status",9));
return scItemDao.findPage(query.builder());
}else if (status == 1) {
String sql = "select bsc.id,bsc.course_id,bsc.course_name,bsc.aname,item.content_id,0 as progress,1 as status from boe_study_course bsc " +
" left join boe_study_course_item item on bsc.course_id = item.course_id and bsc.id = item.study_id" +
" where bsc.course_id = '"+courseId+"' and bsc.aname like '%"+name+"%' and bsc.id not in(" +
" select item.study_id from boe_study_course_item item " +
" where item.course_id = '" + courseId + "' and item.content_id = '"+ contentId+"' and item.aname like '%"+name+"%' group by item.study_id" +
" ) group by bsc.id limit "+ pageIndex2+","+ pageSize+";";
String sql2 = "select count(*) as total from (select bsc.id,bsc.course_id,bsc.course_name,bsc.aname,item.content_id,0 as progress,1 as status from boe_study_course bsc " +
" left join boe_study_course_item item on bsc.course_id = item.course_id and bsc.id = item.study_id" +
" where bsc.course_id = '"+courseId+"' and bsc.aname like '%"+name+"%' and bsc.id not in(" +
" select item.study_id from boe_study_course_item item " +
" where item.course_id = '" + courseId + "' and item.content_id = '"+ contentId+"' and item.aname like '%"+name+"%' group by item.study_id" +
" ) group by bsc.id) as total";
log.info("资源完成情况未开始sql"+sql);
List<Object[]> list = scDao.sqlFindList(sql);
log.info("资源完成情况人数"+list);
int totalCount = scDao.sqlCount(sql2);
List<StudyCourseItem> item = new ArrayList<>();
for (Object[] objs : list) {
StudyCourseItem sc = new StudyCourseItem();
sc.setProgress(Integer.valueOf(objs[5].toString()));
sc.setStatus(Integer.valueOf(objs[6].toString()));
sc.setAname(objs[3].toString());
item.add(sc);
}
log.info("资源完成情况人员"+item);
PageList<StudyCourseItem> pageList = new PageList<>(item);
pageList.setCount(totalCount);
pageList.setPageSize(pageSize);
pageList.setList(item);
return pageList;
// 25.11.27标注这里是查询没有item联表数据的主表数据等同于没有学习情况的资源
// 25.12.1修改找技术帮忙查看了前端代码没有status == 1的调用此处应查询状态为未开始的数据重写此处逻辑
// 未开始结合现有数据这里筛选状态为1及为null的数据
query.addFilter(FieldFilters.or(FieldFilters.eq("status", 1), FieldFilters.isNull("status")));
return scItemDao.findPage(query.builder());
} else if (status == 4) {
// 25.12.16修改,添加筛选已完成数据之外的情况
query.addFilter(FieldFilters.ne("status", 9));
return scItemDao.findPage(query.builder());
}
}
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 " +
"from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "' and aname like '%"+name+"%') a " +
"left join " +
"(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, 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 + "' and item.content_id = '" + contentId + "' and item.aname like '%"+name+"%' group by bsc.id) b " +
// 25.12.16新增提前处理aid数组供后续SQL拼接使用
String aidSqlFilter = "";
// 存储aid的SQL筛选片段
if(StringUtils.isNotBlank(aid)){
String[] aidArray = aid.split(",");
// 拼接aid的in筛选
aidSqlFilter = "and item.aid in ('" + String.join("','", aidArray) + "') ";
}
// 未传输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,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 + "'") +
//aidSqlFilter拼接aid筛选条件
(StringUtils.isBlank(name) ? "" : "and item.aname like '%" + name +"%'") + aidSqlFilter+" group by bsc.id) b " +
"on a.course_id = b.course_id and a.id = b.id " +
"group by a.id limit "+ pageIndex2+","+ pageSize+";";
String sql2 = "select count(*) as total from (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 " +
"from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "' and aname like '%"+name+"%') a " +
"left join " +
"(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, item.finish_time, item.progress, item.status " +
"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 + "' and item.content_id = '" + contentId + "' and item.aname like '%"+name+"%' group by bsc.id) b " +
String sql2 = "select count(*) as total from (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 " + "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.finish_time, item.progress, item.status " + "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 + "'") +
//aidSqlFilter拼接aid筛选条件
(StringUtils.isBlank(name) ? "" : "and item.aname like '%" + name +"%'") +aidSqlFilter+
" group by bsc.id) b " +
"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{}", sql);
log.info("数量查询sql{}", sql2);
List<Object[]> list = scDao.sqlFindList(sql);
log.info("资源完成情况人数"+list);
int totalCount = scDao.sqlCount(sql2);
List<StudyCourseItem> item = new ArrayList<>();
for (Object[] objs : list) {
StudyCourseItem sc = new StudyCourseItem();
// 25.11.27新增如果有id添加item表id供后续联表使用
if (objs[8] != null) {
sc.setId(String.valueOf(objs[8]));
}
if (!"0".equals(objs[4].toString())) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime finishTime = LocalDateTime.parse(objs[4].toString(), formatter);
@@ -325,15 +361,142 @@ public class StudyServiceImpl implements IStudyService{
if(objs[7] != null){
sc.setScore(Float.valueOf(objs[7].toString()));
}
// 25.12.5新增补全aid查询
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);
}
log.info("资源完成情况人员"+item);
// 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;
}
@Override
public PageList<StudyCourseItem> itemPage(int pageIndex, int pageSize, String contentId, String courseId, Integer status, List<String> userIdList) {
long count = scItemDao.countList(courseId, contentId, userIdList, status);
List<StudyCourseItem> list = scItemDao.queryList(courseId, contentId, userIdList, status, true, pageIndex, pageSize);
List<String> contentIds = list.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 : list) {
studyCourseItem.setDisplayName(contentList.stream().filter(content -> content.getId().equals(studyCourseItem.getContentId())).findFirst().map(CourseContent::getDisplayName).orElse(null));
}
}
}
PageList<StudyCourseItem> result = new PageList<>();
result.setCount((int) count);
result.setPageSize(pageSize);
result.setList(list);
return result;
}
@Override
public List<StudyCourseItem> itemList(String contentId, String courseId, Integer status, List<String> userIdList) {
List<StudyCourseItem> list = scItemDao.queryList(courseId, contentId, userIdList, status, false, 0, 0);
List<String> contentIds = list.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 : list) {
studyCourseItem.setDisplayName(contentList.stream().filter(content -> content.getId().equals(studyCourseItem.getContentId())).findFirst().map(CourseContent::getDisplayName).orElse(null));
}
}
}
return list;
}
/**
* 为 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.findByCourseIds(new ArrayList<>(courseIds));
if (courses != null) {
courseMap = courses.stream().collect(Collectors.toMap(StudyCourse::getCourseId, 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

View File

@@ -1,22 +1,7 @@
package com.xboe.school.study.service.impl;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;
import javax.transaction.Transactional;
import com.xboe.module.course.dao.CourseContentDao;
import com.xboe.module.course.dto.CourseContentDto;
import com.xboe.module.course.entity.CourseContent;
import com.xboe.school.study.dao.StudyCourseItemDao;
import com.xboe.school.study.entity.StudyCourseItem;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.boe.feign.api.usercenter.remote.AudienceRemoteClient;
import com.boe.feign.api.usercenter.reps.AudienceMemberVo;
import com.xboe.common.OrderCondition;
import com.xboe.common.PageList;
import com.xboe.common.beans.KeyValue;
@@ -24,16 +9,29 @@ 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.module.course.dao.CourseContentDao;
import com.xboe.module.course.dao.CourseDao;
import com.xboe.module.course.dto.CourseStudyDto;
import com.xboe.module.course.service.ICourseStudySearch;
import com.xboe.school.study.dao.StudyCourseDao;
import com.xboe.school.study.dao.StudyCourseItemDao;
import com.xboe.school.study.dao.StudySignupDao;
import com.xboe.school.study.dto.SignupAudienceDTO;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.entity.StudyCourseItem;
import com.xboe.school.study.entity.StudySignup;
import com.xboe.school.study.service.IStudySignupService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -58,13 +56,17 @@ public class StudySignupServiceImpl implements IStudySignupService{
@Autowired
CourseDao courseDao;
@Autowired
private AudienceRemoteClient audienceRemoteClient;
@Override
public void selfSignup(StudySignup signup) {
signup.setSignType(StudySignup.SIGNTYPE_SELF);
StudyCourse sc=studyCourseDao.findByCourseIdAndAid(signup.getAid(), signup.getCourseId());
if (sc == null){
this.addSignup(signup);
}
// StudyCourse sc=studyCourseDao.findByCourseIdAndAid(signup.getAid(), signup.getCourseId());
// if (sc == null){
// this.addSignup(signup);
// }
this.addSignup(signup);
//更新课程学习人数
courseDao.updateMultiFieldById(signup.getCourseId(),UpdateBuilder.create("studys", "studys+1",FieldUpdateType.EXPRESSION));
}
@@ -83,56 +85,66 @@ public class StudySignupServiceImpl implements IStudySignupService{
}
signupDao.save(signup);
//添加到课程学习
LocalDateTime now=LocalDateTime.now();
StudyCourse sc=new StudyCourse();
sc.setAddTime(now);
sc.setAid(signup.getAid());
sc.setAname(signup.getName());
sc.setCourseId(signup.getCourseId());
sc.setCourseName(signup.getCourseName());
sc.setCourseType(signup.getCourseType());
sc.setStartTime(now);
sc.setFinishTime(null);
sc.setLastScore(0f);
sc.setProgress(0f);
sc.setSource(signup.getAuditType());//来源方式,和报名类型一致
sc.setStatus(StudyCourse.STATUS_NOSTUDY);
sc.setStatusTime(now);
sc.setLastTime(sc.getAddTime());
sc.setTotalDuration(0);
studyCourseDao.save(sc);
signup.setStudyId(sc.getId());
//发布到ES
if(esSearch!=null) {
CourseStudyDto dto=new CourseStudyDto();
dto.setId(sc.getId());
dto.setAccountId(sc.getAid());
dto.setCourseId(sc.getCourseId());
dto.setCourseImage(sc.getCourseImage());
dto.setCourseName(sc.getCourseName());
dto.setCourseType(sc.getCourseType());
dto.setProgress(sc.getProgress()==null? 0:sc.getProgress().intValue());
dto.setSource(2);//固定值,新系统
//下面模拟添加线下课程报名,用于本地测试
// 判断是否存在旧的学习记录
StudyCourse sc = studyCourseDao.findByCourseIdAndAid(signup.getAid(), signup.getCourseId());
if (sc != null) {
log.info("用户【{}】已存在学习记录,不再重复添加", signup.getName());
sc = studyCourseDao.findByCourseIdAndAid(signup.getAid(), signup.getCourseId());
} else {
LocalDateTime now=LocalDateTime.now();
sc=new StudyCourse();
sc.setAddTime(now);
sc.setAid(signup.getAid());
sc.setAname(signup.getName());
sc.setCourseId(signup.getCourseId());
sc.setCourseName(signup.getCourseName());
sc.setCourseType(signup.getCourseType());
sc.setStartTime(now);
sc.setFinishTime(null);
sc.setLastScore(0f);
sc.setProgress(0f);
sc.setSource(signup.getAuditType());//来源方式,和报名类型一致
sc.setStatus(StudyCourse.STATUS_NOSTUDY);
sc.setStatusTime(now);
sc.setLastTime(sc.getAddTime());
sc.setTotalDuration(0);
studyCourseDao.save(sc);
signup.setStudyId(sc.getId());
//发布到ES
if(esSearch!=null) {
CourseStudyDto dto=new CourseStudyDto();
dto.setId(sc.getId());
dto.setAccountId(sc.getAid());
dto.setCourseId(sc.getCourseId());
dto.setCourseImage(sc.getCourseImage());
dto.setCourseName(sc.getCourseName());
dto.setCourseType(sc.getCourseType());
dto.setProgress(sc.getProgress()==null? 0:sc.getProgress().intValue());
dto.setSource(2);//固定值,新系统
//下面模拟添加线下课程报名,用于本地测试
// dto.setCourseType(30);
// dto.setSource(3);//来源在线课
// dto.setSource(3);//来源在线课
// dto.setApplyStatus(50);
// dto.setCourseAddress("这里是上课地址");
// dto.setAttendCourseTime(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
//如果开始时间没有,就使用添加时间
if(sc.getStartTime()!=null) {
dto.setStartTime(sc.getStartTime().toEpochSecond(ZoneOffset.of("+8")));
}else {
dto.setStartTime(sc.getAddTime().toEpochSecond(ZoneOffset.of("+8")));
}
dto.setStatus(sc.getStatus());
try {
esSearch.publishOrUpdate(dto, null);
} catch (IOException e) {
log.error("ES发布报名课程学习【"+sc.getAname()+","+sc.getCourseName()+"】失败",e);
//如果开始时间没有,就使用添加时间
if(sc.getStartTime()!=null) {
dto.setStartTime(sc.getStartTime().toEpochSecond(ZoneOffset.of("+8")));
}else {
dto.setStartTime(sc.getAddTime().toEpochSecond(ZoneOffset.of("+8")));
}
dto.setStatus(sc.getStatus());
try {
esSearch.publishOrUpdate(dto, null);
} catch (IOException e) {
log.error("ES发布报名课程学习【"+sc.getAname()+","+sc.getCourseName()+"】失败",e);
}
}
}
//更新课程学习人数
// 不再重复添加
// courseDao.updateMultiFieldById(signup.getCourseId(),UpdateBuilder.create("studys", "studys+1",FieldUpdateType.EXPRESSION));
return sc;
//courseDao.updateMultiFieldById(signup.getCourseId(),UpdateBuilder.create("studys", "studys+1",FieldUpdateType.EXPRESSION));
@@ -203,6 +215,49 @@ public class StudySignupServiceImpl implements IStudySignupService{
}
/**
* 报名列表查询
*
* @param ss 报名查询条件
* @param oc 排序条件
* @return 报名信息集合
*/
@Override
public List<StudySignup> findList(StudySignup ss, OrderCondition oc) {
QueryBuilder query = QueryBuilder.from(StudySignup.class);
if (oc == null) {
oc = OrderCondition.desc("id");
}
query.addOrder(oc);
if (ss != null) {
if (StringUtils.isNotBlank(ss.getName())) {
query.addFilter(FieldFilters.like("name", ss.getName()));
}
if (StringUtils.isNotBlank(ss.getCourseId())) {
query.addFilter(FieldFilters.eq("courseId", ss.getCourseId()));
}
if (StringUtils.isNotBlank(ss.getCourseName())) {
query.addFilter(FieldFilters.like("courseName", ss.getCourseName()));
}
if (ss.getStatus() != null) {
query.addFilter(FieldFilters.eq("status", ss.getStatus()));
}
if (ss.getCourseType() != null) {
query.addFilter(FieldFilters.eq("courseType", ss.getCourseType()));
}
//25.12.11新增添加根据用户aid通过逗号分隔查询
if (StringUtils.isNotBlank(ss.getAid())) {
String userIdStr = ss.getAid();
query.addFilter(FieldFilters.in("aid", userIdStr.split(",")));
}
// 25.11.26新增添加根据报名方式查询1自主报名2管理代报
if (ss.getSignType() != null) {
query.addFilter(FieldFilters.eq("signType", ss.getSignType()));
}
}
return signupDao.findList(query.builder());
}
@Override
public PageList<StudySignup> findPage(int pageIndex, int pageSize, StudySignup ss, OrderCondition oc) {
QueryBuilder query=QueryBuilder.from(StudySignup.class);
@@ -229,13 +284,37 @@ public class StudySignupServiceImpl implements IStudySignupService{
if(ss.getCourseType()!=null) {
query.addFilter(FieldFilters.eq("courseType", ss.getCourseType()));
}
if(StringUtils.isNotBlank(ss.getAid())) {
query.addFilter(FieldFilters.eq("aid", ss.getAid()));
//25.12.11新增添加根据用户aid通过逗号分隔查询
if(StringUtils.isNotBlank(ss.getAid())){
String userIdStr = ss.getAid();
query.addFilter(FieldFilters.in("aid",userIdStr.split(",")));
}
// 25.11.26新增添加根据报名方式查询1自主报名2管理代报
if (ss.getSignType() != null) {
query.addFilter(FieldFilters.eq("signType", ss.getSignType()));
}
}
return signupDao.findPage(query.builder());
}
@Override
public PageList<AudienceMemberVo> audienceMemberPage(SignupAudienceDTO audienceDto) {
// 1. 查询课程报名记录
String courseId = audienceDto.getCourseId();
QueryBuilder query = QueryBuilder.from(StudySignup.class);
query.addFilter(FieldFilters.eq("courseId", courseId));
List<StudySignup> list = signupDao.findList(query.builder());
// 2. 查看是否有已报名的用户
if (list != null && !list.isEmpty()) {
List<String> exclusionUserIds = list.stream()
.map(StudySignup::getAid)
.collect(Collectors.toList());
audienceDto.setExclusionMemberIds(exclusionUserIds);
}
return audienceRemoteClient.memberList(audienceDto);
}
@Override
public StudySignup getByCidAndAid(String cid, String aid) {
List<StudySignup> list=signupDao.findList(1,

View File

@@ -60,6 +60,7 @@ public class SysUploaderApi extends ApiBaseController{
fileTypeSet.add("pptx");
fileTypeSet.add("pdf");
fileTypeSet.add("zip");
fileTypeSet.add("txt");
}
@RequestMapping(value = "/file/upload", method = RequestMethod.POST, produces = "application/json")

View File

@@ -1,5 +1,10 @@
boe:
domain: http://192.168.0.253
domain-name: https://u-pre.boe.com
pcPageUrl: ${boe.domain-name}/pc/course/studyindex?id=
h5PageUrl: ${boe.domain-name}/mobile/pages/study/courseStudy?id=
pcLoginUrl: ${boe.domain-name}/web/
h5LoginUrl: ${boe.domain-name}/m/loginuser
spring:
servlet:
multipart:
@@ -118,4 +123,8 @@ aop-log-record:
password: admin
elasticsearch:
host: 192.168.0.253
port: 9200
port: 9200
mysql:
schema:
user-center-schema: userbasic

View File

@@ -134,7 +134,11 @@ jasypt:
boe:
domain: http://10.251.132.75
domain-name: https://u-pre.boe.com
pcPageUrl: ${boe.domain-name}/pc/course/studyindex?id=
h5PageUrl: ${boe.domain-name}/mobile/pages/study/courseStudy?id=
pcLoginUrl: ${boe.domain-name}/web/
h5LoginUrl: ${boe.domain-name}/m/loginuser
ok:
http:
connect-timeout: 30
@@ -196,4 +200,8 @@ aop-log-record:
password: admin
elasticsearch:
host: 10.251.129.21
port: 9200
port: 9200
mysql:
schema:
user-center-schema: user_basic

View File

@@ -345,6 +345,18 @@ aop-log-record:
host: 10.232.27.40
port: 9200
boe:
domain: http://192.168.0.253
domain-name: https://u.boe.com
pcPageUrl: ${boe.domain-name}/pc/course/studyindex?id=
h5PageUrl: ${boe.domain-name}/mobile/pages/study/courseStudy?id=
pcLoginUrl: ${boe.domain-name}/web/
h5LoginUrl: ${boe.domain-name}/m/loginuser
mysql:
schema:
user-center-schema: user_basic
kjb:
aicoreUrl: http://10.232.28.95:8080
videoUrlPrefix: https://u.boe.com/upload

View File

@@ -159,11 +159,15 @@ jasypt:
boe:
domain: http://10.251.186.27
domain-name: https://u-pre.boe.com
pcPageUrl: ${boe.domain-name}/pc/course/studyindex?id=
h5PageUrl: ${boe.domain-name}/mobile/pages/study/courseStudy?id=
pcLoginUrl: ${boe.domain-name}/web/
h5LoginUrl: ${boe.domain-name}/m/loginuser
kjb:
aicoreUrl: http://10.251.186.27:8088
videoUrlPrefix: https://u-pre.boe.com/upload
ok:
http:
connect-timeout: 30
@@ -227,4 +231,8 @@ aop-log-record:
host: 10.251.129.25
port: 9200
user: elastic
password: Boe@es123
password: Boe@es123
mysql:
schema:
user-center-schema: userbasic